@zlink-systems/framework 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/contracts/Dispatch/ZLinkDispatchOptions.d.ts +1 -0
- package/dist/contracts/Streams/IZLinkSession.d.ts +2 -1
- package/dist/runtime/actors/actor-context.js +4 -4
- package/dist/runtime/actors/actor-join-deferred-scope.d.ts +0 -1
- package/dist/runtime/actors/actor-join-deferred-scope.js +0 -18
- package/dist/runtime/actors/deferred-join-accepted-journal.js +2 -6
- package/dist/runtime/backend/contracts/index.d.ts +2 -0
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +4 -0
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +24 -8
- package/dist/runtime/channels/channel-dispatch-pipeline.js +2 -2
- package/dist/runtime/channels/channel-transports.js +3 -9
- package/dist/runtime/channels/dispatch-error-reporter.d.ts +1 -0
- package/dist/runtime/channels/dispatch-error-reporter.js +6 -38
- package/dist/runtime/diagnostics/dispatch-error-details.d.ts +6 -0
- package/dist/runtime/diagnostics/dispatch-error-details.js +55 -0
- package/dist/runtime/diagnostics/flow-context.d.ts +2 -1
- package/dist/runtime/diagnostics/flow-context.js +2 -2
- package/dist/runtime/diagnostics/message-flow.js +11 -3
- package/dist/runtime/foundation/actor-join-recovery-codec.js +0 -4
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +4 -2
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +34 -9
- package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +1 -0
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +5 -0
- package/dist/runtime/foundation/service-stateful-runtime.js +52 -2
- package/dist/runtime/foundation/service-topology-registry.d.ts +1 -0
- package/dist/runtime/foundation/service-topology-registry.js +12 -4
- package/dist/runtime/host/index.js +7 -3
- package/dist/runtime/locations/in-memory-location-store.js +5 -0
- package/dist/runtime/locations/location-store-repository.js +3 -1
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +20 -18
- package/dist/runtime/spots/index.js +9 -3
- package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +2 -1
- package/dist/runtime/spots/spot-actor-packet-dispatch.js +6 -1
- package/dist/runtime/spots/spot-actor-packet-drain.js +2 -0
- package/dist/runtime/spots/spot-node-runtime-manager.js +5 -1
- package/dist/runtime/spots/spot-route-packet-dispatch.js +1 -1
- package/dist/runtime/spots/spot-routed-bound-session-dispatch.js +1 -1
- package/dist/runtime/spots/spot-subscription-dispatch.js +1 -1
- package/dist/runtime/spots/spot-timer.js +1 -1
- package/dist/runtime/streams/actor-session-binding-registry.d.ts +21 -8
- package/dist/runtime/streams/actor-session-binding-registry.js +71 -42
- package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +1 -0
- package/dist/runtime/streams/bound-actor-relay-sender.js +2 -1
- package/dist/runtime/streams/bound-session-response-target.js +8 -1
- package/dist/runtime/streams/bound-session-service.d.ts +1 -0
- package/dist/runtime/streams/bound-session-service.js +6 -3
- package/dist/runtime/streams/index.js +8 -1
- package/dist/runtime/streams/managed-stream.d.ts +3 -0
- package/dist/runtime/streams/managed-stream.js +20 -1
- package/dist/runtime/streams/protocol.d.ts +5 -1
- package/dist/runtime/streams/protocol.js +41 -4
- package/dist/runtime/streams/session-actor-coordinator.js +3 -4
- package/dist/runtime/streams/session-context.d.ts +9 -2
- package/dist/runtime/streams/session-context.js +20 -4
- package/dist/runtime/streams/session-local-actors.d.ts +3 -1
- package/dist/runtime/streams/session-local-actors.js +17 -2
- package/dist/runtime/streams/stream-frame-factory.d.ts +1 -1
- package/dist/runtime/streams/stream-frame-factory.js +3 -2
- package/dist/runtime/streams/stream-session-runtime.js +42 -12
- package/package.json +3 -3
- package/src/contracts/Dispatch/ZLinkDispatchOptions.ts +1 -0
- package/src/contracts/Streams/IZLinkSession.ts +2 -1
- package/src/runtime/actors/actor-context.ts +4 -4
- package/src/runtime/actors/actor-join-deferred-scope.ts +0 -32
- package/src/runtime/actors/deferred-join-accepted-journal.ts +2 -6
- package/src/runtime/backend/contracts/index.ts +5 -0
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +43 -11
- package/src/runtime/channels/channel-dispatch-pipeline.ts +4 -2
- package/src/runtime/channels/channel-transports.ts +3 -9
- package/src/runtime/channels/dispatch-error-reporter.ts +7 -45
- package/src/runtime/diagnostics/dispatch-error-details.ts +62 -0
- package/src/runtime/diagnostics/flow-context.ts +6 -4
- package/src/runtime/diagnostics/message-flow.ts +12 -3
- package/src/runtime/foundation/actor-join-recovery-codec.ts +0 -4
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +54 -13
- package/src/runtime/foundation/service-session-binding-ingress-port.ts +1 -0
- package/src/runtime/foundation/service-stateful-runtime.ts +71 -5
- package/src/runtime/foundation/service-topology-registry.ts +18 -5
- package/src/runtime/host/index.ts +11 -4
- package/src/runtime/locations/in-memory-location-store.ts +7 -0
- package/src/runtime/locations/location-store-repository.ts +4 -1
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +18 -17
- package/src/runtime/spots/index.ts +22 -2
- package/src/runtime/spots/spot-actor-packet-dispatch.ts +20 -3
- package/src/runtime/spots/spot-actor-packet-drain.ts +2 -0
- package/src/runtime/spots/spot-node-runtime-manager.ts +5 -1
- package/src/runtime/spots/spot-route-packet-dispatch.ts +2 -1
- package/src/runtime/spots/spot-routed-bound-session-dispatch.ts +2 -1
- package/src/runtime/spots/spot-subscription-dispatch.ts +2 -1
- package/src/runtime/spots/spot-timer.ts +6 -1
- package/src/runtime/streams/actor-session-binding-registry.ts +150 -63
- package/src/runtime/streams/actor-session-binding-runtime-owner.ts +1 -0
- package/src/runtime/streams/bound-actor-relay-sender.ts +10 -2
- package/src/runtime/streams/bound-session-response-target.ts +10 -1
- package/src/runtime/streams/bound-session-service.ts +13 -3
- package/src/runtime/streams/index.ts +8 -1
- package/src/runtime/streams/managed-stream.ts +34 -12
- package/src/runtime/streams/protocol.ts +44 -5
- package/src/runtime/streams/session-actor-coordinator.ts +12 -4
- package/src/runtime/streams/session-context.ts +31 -4
- package/src/runtime/streams/session-local-actors.ts +19 -3
- package/src/runtime/streams/stream-frame-factory.ts +4 -2
- package/src/runtime/streams/stream-session-runtime.ts +58 -13
|
@@ -303,9 +303,10 @@ class RawServiceMeshRuntime {
|
|
|
303
303
|
return this.send(targetNodeRoutingId, [(0, service_wire_m6a_codec_1.encodeNodeSendHeader)(), this.applicationFrame(payload)]);
|
|
304
304
|
}
|
|
305
305
|
async sendToChannel(channelName, payload) {
|
|
306
|
-
const
|
|
307
|
-
if (
|
|
308
|
-
return
|
|
306
|
+
const selection = this.selectChannelTarget(channelName);
|
|
307
|
+
if (selection.kind !== 'selected')
|
|
308
|
+
return selection.submitResult;
|
|
309
|
+
const selected = selection.peer;
|
|
309
310
|
const applicationFrame = this.applicationFrame(payload);
|
|
310
311
|
if (selected.descriptor.nodeRoutingId === this.descriptor.nodeRoutingId) {
|
|
311
312
|
const applicationJobOwner = await this.reserveLocalIngress();
|
|
@@ -320,25 +321,49 @@ class RawServiceMeshRuntime {
|
|
|
320
321
|
});
|
|
321
322
|
if (!accepted)
|
|
322
323
|
applicationJob.close();
|
|
323
|
-
return accepted;
|
|
324
|
+
return accepted ? runtime_values_1.SubmitResult.Ok : runtime_values_1.SubmitResult.NotAdmitted;
|
|
324
325
|
}
|
|
325
326
|
finally {
|
|
326
327
|
applicationJobOwner.close();
|
|
327
328
|
}
|
|
328
329
|
}
|
|
329
|
-
return this.send(selected.descriptor.nodeRoutingId, [
|
|
330
|
+
return (await this.send(selected.descriptor.nodeRoutingId, [
|
|
330
331
|
(0, service_wire_m6a_codec_1.encodeChannelSendHeader)(channelName),
|
|
331
332
|
applicationFrame
|
|
332
|
-
])
|
|
333
|
+
]))
|
|
334
|
+
? runtime_values_1.SubmitResult.Ok
|
|
335
|
+
: runtime_values_1.SubmitResult.NotConnected;
|
|
333
336
|
}
|
|
334
337
|
requestToNode(targetNodeRoutingId, payload, timeoutMs) {
|
|
335
338
|
return this.requestToTarget(targetNodeRoutingId, payload, timeoutMs);
|
|
336
339
|
}
|
|
337
340
|
requestToChannel(channelName, payload, timeoutMs) {
|
|
341
|
+
const selection = this.selectChannelTarget(channelName);
|
|
342
|
+
if (selection.kind !== 'selected') {
|
|
343
|
+
const pending = this.operations.register(timeoutMs);
|
|
344
|
+
this.operations.complete(pending.id, selection.requestResult);
|
|
345
|
+
return pending;
|
|
346
|
+
}
|
|
347
|
+
return this.requestToTarget(selection.peer.descriptor.nodeRoutingId, payload, timeoutMs, channelName);
|
|
348
|
+
}
|
|
349
|
+
selectChannelTarget(channelName) {
|
|
338
350
|
const selected = this.topology.selectChannel(channelName, (peer) => this.isLocalOrReadyPeer(peer.descriptor.nodeRoutingId));
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
351
|
+
if (selected !== undefined)
|
|
352
|
+
return { kind: 'selected', peer: selected };
|
|
353
|
+
return this.topology.hasKnownChannelTarget(channelName)
|
|
354
|
+
? {
|
|
355
|
+
kind: 'knownButNotReady',
|
|
356
|
+
submitResult: runtime_values_1.SubmitResult.NotConnected,
|
|
357
|
+
requestResult: { terminalResult: runtime_values_1.RequestResult.NotConnected, failureCode: 0 }
|
|
358
|
+
}
|
|
359
|
+
: {
|
|
360
|
+
kind: 'noMember',
|
|
361
|
+
submitResult: runtime_values_1.SubmitResult.NotFound,
|
|
362
|
+
requestResult: {
|
|
363
|
+
terminalResult: runtime_values_1.RequestResult.NotFound,
|
|
364
|
+
failureCode: REQUEST_TARGET_NOT_FOUND_FAILURE_CODE
|
|
365
|
+
}
|
|
366
|
+
};
|
|
342
367
|
}
|
|
343
368
|
isLocalOrReadyPeer(nodeRoutingId) {
|
|
344
369
|
return nodeRoutingId === this.descriptor.nodeRoutingId || this.isPeerRouteReady(nodeRoutingId);
|
|
@@ -16,6 +16,7 @@ export interface ServiceSessionBindingAdmissionClaim {
|
|
|
16
16
|
}
|
|
17
17
|
export type ServiceSessionBindingAdmissionResult = 'passThrough' | 'retained' | 'rejected';
|
|
18
18
|
export interface ServiceSessionBindingIngressPort {
|
|
19
|
+
actorSlot(actorId: string, sessionRid: string): Promise<number | undefined>;
|
|
19
20
|
retainOutbound(claim: ServiceSessionBindingAdmissionClaim, delivery: ServiceSessionBindingRetainedDelivery): Promise<ServiceSessionBindingAdmissionResult>;
|
|
20
21
|
clearOutbound(actorId: string, error: unknown): Promise<void>;
|
|
21
22
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ZLinkDispatchErrorReporter } from '../channels/dispatch-error-reporter';
|
|
1
2
|
import type { RawServiceMeshRuntime } from './raw-service-mesh-runtime';
|
|
2
3
|
import { type ReceiveKindData, type StreamSessionActorAuthorityFence } from './service-runtime-contracts';
|
|
3
4
|
import type { ServiceMailboxRecord } from './service-mailbox';
|
|
@@ -221,6 +222,8 @@ export declare class ServiceStatefulRuntime {
|
|
|
221
222
|
private userSpotOperationHandler?;
|
|
222
223
|
private messageFollowHandler?;
|
|
223
224
|
private mailboxDropHandler?;
|
|
225
|
+
private dispatchErrors?;
|
|
226
|
+
private dispatchErrorMeshName?;
|
|
224
227
|
private readonly admittedUserSpotOperations;
|
|
225
228
|
private readonly pendingInstanceActivations;
|
|
226
229
|
private closed;
|
|
@@ -229,6 +232,7 @@ export declare class ServiceStatefulRuntime {
|
|
|
229
232
|
readonly kind: 'spot_multicast' | 'actor_control' | 'actor_binding';
|
|
230
233
|
readonly owner: string;
|
|
231
234
|
}) => void): void;
|
|
235
|
+
setDispatchErrorReporter(reporter: ZLinkDispatchErrorReporter, meshName: string): void;
|
|
232
236
|
createSpot(routingId?: string, kind?: 'user' | 'instance', stableType?: "user" | "instance"): ServiceSpotState;
|
|
233
237
|
restoreUserSpotAuthority(spotId: string, stableType: string, generation: bigint, authorityOwnerGeneration: bigint): ServiceSpotState;
|
|
234
238
|
restoreSpotAuthority(spotId: string, objectKind: 'user_spot' | 'instance_spot', stableType: string, generation: bigint, authorityOwnerGeneration: bigint): ServiceSpotState;
|
|
@@ -247,6 +251,7 @@ export declare class ServiceStatefulRuntime {
|
|
|
247
251
|
readonly objectGeneration: bigint;
|
|
248
252
|
} | undefined;
|
|
249
253
|
forgetInstanceIntent(spotId: string, objectGeneration: bigint, authorityOwnerGeneration: bigint, storeVersion?: string): void;
|
|
254
|
+
completeClosedInstance(spotId: string, objectGeneration: bigint): void;
|
|
250
255
|
registerInstanceActivationAuthority(authority: ServiceInstanceActivationAuthority): void;
|
|
251
256
|
registerAsyncInstanceActivationAuthority(authority: ServiceAsyncInstanceActivationAuthority): void;
|
|
252
257
|
registerInstanceApplicationLifecycle(lifecycle: ServiceInstanceApplicationLifecycle): void;
|
|
@@ -7,6 +7,7 @@ const abort_1 = require("../abort");
|
|
|
7
7
|
const execution_1 = require("../execution");
|
|
8
8
|
const framework_errors_internal_1 = require("../framework-errors-internal");
|
|
9
9
|
const runtime_values_1 = require("../backend/runtime-values");
|
|
10
|
+
const ZLinkDispatchOptions_1 = require("../../contracts/Dispatch/ZLinkDispatchOptions");
|
|
10
11
|
const service_runtime_contracts_1 = require("./service-runtime-contracts");
|
|
11
12
|
const message_follow_suppression_registry_1 = require("./message-follow-suppression-registry");
|
|
12
13
|
const service_stateful_registry_1 = require("./service-stateful-registry");
|
|
@@ -81,6 +82,8 @@ class ServiceStatefulRuntime {
|
|
|
81
82
|
userSpotOperationHandler;
|
|
82
83
|
messageFollowHandler;
|
|
83
84
|
mailboxDropHandler;
|
|
85
|
+
dispatchErrors;
|
|
86
|
+
dispatchErrorMeshName;
|
|
84
87
|
admittedUserSpotOperations = new Map();
|
|
85
88
|
pendingInstanceActivations = new Map();
|
|
86
89
|
closed = false;
|
|
@@ -95,6 +98,10 @@ class ServiceStatefulRuntime {
|
|
|
95
98
|
setMailboxDropHandler(handler) {
|
|
96
99
|
this.mailboxDropHandler = handler;
|
|
97
100
|
}
|
|
101
|
+
setDispatchErrorReporter(reporter, meshName) {
|
|
102
|
+
this.dispatchErrors = reporter;
|
|
103
|
+
this.dispatchErrorMeshName = meshName;
|
|
104
|
+
}
|
|
98
105
|
createSpot(routingId, kind = 'user', stableType = kind) {
|
|
99
106
|
this.requireOpen();
|
|
100
107
|
return this.registry.createSpot(routingId ?? `spot-${this.nodeRid}-${this.nextSpotId++}`, kind, stableType);
|
|
@@ -229,6 +236,12 @@ class ServiceStatefulRuntime {
|
|
|
229
236
|
this.instanceIntents.delete(spotId);
|
|
230
237
|
}
|
|
231
238
|
}
|
|
239
|
+
completeClosedInstance(spotId, objectGeneration) {
|
|
240
|
+
const current = this.instanceIntents.get(spotId);
|
|
241
|
+
if (current !== undefined && current.route.objectGeneration === objectGeneration) {
|
|
242
|
+
this.forgetClosedInstanceRoute(current.route);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
232
245
|
registerInstanceActivationAuthority(authority) {
|
|
233
246
|
this.requireOpen();
|
|
234
247
|
if (this.asyncInstanceAuthority !== undefined ||
|
|
@@ -623,13 +636,32 @@ class ServiceStatefulRuntime {
|
|
|
623
636
|
await this.enqueueLogicalMulticast(channelName, topic, sourceSpotId, payload);
|
|
624
637
|
const targets = this.raw.topology
|
|
625
638
|
.peers()
|
|
626
|
-
.filter((peer) => peer.descriptor.channels.some((channel) => channel.name === channelName && channel.weight > 0));
|
|
639
|
+
.filter((peer) => peer.descriptor.channels.some((channel) => channel.name === channelName && channel.weight > 0) && this.raw.isPeerRouteReady(peer.descriptor.nodeRoutingId));
|
|
627
640
|
const header = (0, service_stateful_wire_codec_1.encodeLogicalMulticastHeader)(channelName, topic, sourceSpotId);
|
|
628
641
|
const payloadFrame = (0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload);
|
|
642
|
+
const reporter = this.dispatchErrors;
|
|
643
|
+
const captureFailures = reporter?.captureEnabled() === true;
|
|
629
644
|
for (const target of targets) {
|
|
630
645
|
// Remote admission ends at the source outbound transport queue. The
|
|
631
646
|
// receiver's Spot queue and handler completion are not publish results.
|
|
632
|
-
await this.raw.sendService(target.descriptor.nodeRoutingId, [
|
|
647
|
+
const accepted = await this.raw.sendService(target.descriptor.nodeRoutingId, [
|
|
648
|
+
header,
|
|
649
|
+
payloadFrame
|
|
650
|
+
]);
|
|
651
|
+
if (!accepted) {
|
|
652
|
+
if (captureFailures) {
|
|
653
|
+
reporter.report({
|
|
654
|
+
surface: ZLinkDispatchOptions_1.ZLinkDispatchErrorSurface.SpotRoute,
|
|
655
|
+
messageKind: ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Send,
|
|
656
|
+
reason: serviceSpotPublishFailureReason(undefined, this.raw.isPeerRouteReady(target.descriptor.nodeRoutingId), this.closed),
|
|
657
|
+
action: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorAction.Drop,
|
|
658
|
+
meshName: this.dispatchErrorMeshName,
|
|
659
|
+
channelName,
|
|
660
|
+
topic,
|
|
661
|
+
targetRid: target.descriptor.nodeRoutingId
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
}
|
|
633
665
|
}
|
|
634
666
|
}
|
|
635
667
|
async sendToSpot(sourceSpotId, requested, payload) {
|
|
@@ -3504,6 +3536,24 @@ class ServiceStatefulRuntime {
|
|
|
3504
3536
|
}
|
|
3505
3537
|
}
|
|
3506
3538
|
exports.ServiceStatefulRuntime = ServiceStatefulRuntime;
|
|
3539
|
+
function serviceSpotPublishFailureReason(result, routeReady, closed) {
|
|
3540
|
+
if (closed || result === runtime_values_1.SubmitResult.Terminated)
|
|
3541
|
+
return 'shutdown';
|
|
3542
|
+
switch (result) {
|
|
3543
|
+
case runtime_values_1.SubmitResult.Backpressured:
|
|
3544
|
+
return 'backpressure';
|
|
3545
|
+
case runtime_values_1.SubmitResult.NotAdmitted:
|
|
3546
|
+
case runtime_values_1.SubmitResult.NotConnected:
|
|
3547
|
+
case runtime_values_1.SubmitResult.NotFound:
|
|
3548
|
+
case runtime_values_1.SubmitResult.InvalidHandle:
|
|
3549
|
+
return 'stale_target';
|
|
3550
|
+
default:
|
|
3551
|
+
// RawServiceMeshRuntime exposes failed Core submissions as false. A
|
|
3552
|
+
// ready route therefore identifies queue admission failure; a route
|
|
3553
|
+
// that became unready after selection is a stale target.
|
|
3554
|
+
return routeReady ? 'backpressure' : 'stale_target';
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3507
3557
|
function isEntrySpotFence(fence) {
|
|
3508
3558
|
return (fence.spot.spotId === fence.targetNodeRid &&
|
|
3509
3559
|
fence.spot.generation === fence.targetNodeGeneration &&
|
|
@@ -55,6 +55,7 @@ export declare class ServiceTopologyRegistry {
|
|
|
55
55
|
markNotRequired(descriptor: ServiceNodeDescriptor): void;
|
|
56
56
|
forgetNotRequired(nodeRoutingId: string): void;
|
|
57
57
|
knownDescriptor(nodeRoutingId: string): ServiceNodeDescriptor | undefined;
|
|
58
|
+
hasKnownChannelTarget(channelName: string): boolean;
|
|
58
59
|
private remember;
|
|
59
60
|
selectChannel(channelName: string, isReady?: (peer: AdmittedServicePeer) => boolean): AdmittedServicePeer | undefined;
|
|
60
61
|
selectPlacement(isReady?: (peer: AdmittedServicePeer) => boolean): AdmittedServicePeer | undefined;
|
|
@@ -170,6 +170,10 @@ class ServiceTopologyRegistry {
|
|
|
170
170
|
const descriptor = admitted ?? this.notRequiredByRid.get(nodeRoutingId) ?? this.knownByRid.get(nodeRoutingId);
|
|
171
171
|
return descriptor === undefined ? undefined : cloneDescriptor(descriptor);
|
|
172
172
|
}
|
|
173
|
+
hasKnownChannelTarget(channelName) {
|
|
174
|
+
requireText(channelName, 'channelName');
|
|
175
|
+
return [this.local, ...this.knownByRid.values()].some((descriptor) => selectableChannel(descriptor, channelName) !== undefined);
|
|
176
|
+
}
|
|
173
177
|
remember(descriptor) {
|
|
174
178
|
const current = this.knownByRid.get(descriptor.nodeRoutingId);
|
|
175
179
|
if (current === undefined ||
|
|
@@ -190,11 +194,9 @@ class ServiceTopologyRegistry {
|
|
|
190
194
|
return [local, ...this.peersByRid.values()]
|
|
191
195
|
.map((peer) => ({
|
|
192
196
|
peer: clonePeer(peer),
|
|
193
|
-
channel:
|
|
197
|
+
channel: selectableChannel(peer.descriptor, channelName)
|
|
194
198
|
}))
|
|
195
|
-
.filter((value) => value.channel !== undefined
|
|
196
|
-
value.peer.descriptor.state === 'serving' &&
|
|
197
|
-
value.channel.weight > 0);
|
|
199
|
+
.filter((value) => value.channel !== undefined);
|
|
198
200
|
}, (value) => value.channel.weight, (value) => value.peer.descriptor.nodeRoutingId, (left, right) => compareOrdinal(left.peer.descriptor.nodeRoutingId, right.peer.descriptor.nodeRoutingId), (value) => isReady(value.peer))?.peer;
|
|
199
201
|
}
|
|
200
202
|
selectPlacement(isReady = () => true) {
|
|
@@ -338,6 +340,12 @@ function validateSortedUnique(values, key, field) {
|
|
|
338
340
|
function findChannel(descriptor, channelName) {
|
|
339
341
|
return descriptor.channels.find((channel) => channel.name === channelName);
|
|
340
342
|
}
|
|
343
|
+
function selectableChannel(descriptor, channelName) {
|
|
344
|
+
if (descriptor.state !== 'serving')
|
|
345
|
+
return undefined;
|
|
346
|
+
const channel = findChannel(descriptor, channelName);
|
|
347
|
+
return channel !== undefined && channel.weight > 0 ? channel : undefined;
|
|
348
|
+
}
|
|
341
349
|
function requireText(value, field) {
|
|
342
350
|
if (value.length === 0 || value.includes('\0')) {
|
|
343
351
|
throw new TypeError(`${field} must be non-empty text without NUL.`);
|
|
@@ -1410,8 +1410,12 @@ class ZLinkFrameworkRuntimeHost {
|
|
|
1410
1410
|
// coordinator after application initialization. The legacy location
|
|
1411
1411
|
// claim would expose a Creating object before that Ready barrier.
|
|
1412
1412
|
locationLifecycle: () => undefined,
|
|
1413
|
-
releaseInstanceAuthority: (meshName, spotId, objectGeneration) =>
|
|
1414
|
-
|
|
1413
|
+
releaseInstanceAuthority: async (meshName, spotId, objectGeneration) => {
|
|
1414
|
+
await this.locationOwner.currentLifecycle?.releaseSpot(meshName, spotId, objectGeneration);
|
|
1415
|
+
this.spotNodeRuntime
|
|
1416
|
+
?.meshNode(meshName)
|
|
1417
|
+
?.completeClosedInstance?.(spotId, objectGeneration);
|
|
1418
|
+
},
|
|
1415
1419
|
beginInstanceIdleClosingAuthority: (meshName, spotId) => this.locationOwner.currentLifecycle?.beginInstanceSpotClosing(meshName, spotId) ??
|
|
1416
1420
|
Promise.resolve(undefined),
|
|
1417
1421
|
beginInstanceClosingAuthority: (meshName, spotId) => this.locationOwner.currentLifecycle?.beginInstanceSpotClosing(meshName, spotId) ??
|
|
@@ -2178,7 +2182,7 @@ class ZLinkFrameworkRuntimeHost {
|
|
|
2178
2182
|
const current = (0, diagnostics_1.currentOrCreateFlow)('Lifecycle', false);
|
|
2179
2183
|
const flow = current?.flowOrigin === 'Lifecycle'
|
|
2180
2184
|
? current
|
|
2181
|
-
: (0, diagnostics_1.createInboundFlow)(undefined, 'Lifecycle', this.flowCreationEnabled());
|
|
2185
|
+
: (0, diagnostics_1.createInboundFlow)(undefined, 'Lifecycle', this.flowCreationEnabled(), undefined);
|
|
2182
2186
|
return (0, diagnostics_1.runWithFlow)(flow, operation);
|
|
2183
2187
|
}
|
|
2184
2188
|
requirePrimaryMeshNode() {
|
|
@@ -134,6 +134,11 @@ class ZLinkInMemoryLocationStore {
|
|
|
134
134
|
meshNodeDescriptorFingerprint(current) === meshNodeDescriptorFingerprint(descriptor)) {
|
|
135
135
|
return stored(this.meshNodes.generations.get(key) ?? 1n, current.updatedAt);
|
|
136
136
|
}
|
|
137
|
+
if (intent === internal_location_contracts_1.ZLinkLocationWriteIntent.NewClaim &&
|
|
138
|
+
currentLease !== undefined &&
|
|
139
|
+
currentLease.leaseExpiresAt.getTime() > updatedAt.getTime()) {
|
|
140
|
+
return rejectedConflict();
|
|
141
|
+
}
|
|
137
142
|
if (current.ownerId !== descriptor.ownerId ||
|
|
138
143
|
current.leaseGeneration !== descriptor.leaseGeneration ||
|
|
139
144
|
current.lifecycleGeneration !== descriptor.lifecycleGeneration ||
|
|
@@ -1675,7 +1675,9 @@ class ZLinkLocationStoreRepository extends in_memory_location_store_1.ZLinkInMem
|
|
|
1675
1675
|
const renew = intent === 2 && canRenew(stored, descriptor);
|
|
1676
1676
|
if (!takeover && !renew) {
|
|
1677
1677
|
return {
|
|
1678
|
-
status:
|
|
1678
|
+
status: intent === internal_location_contracts_1.ZLinkLocationWriteIntent.NewClaim && currentLeaseGeneration !== undefined
|
|
1679
|
+
? Locations_1.ZLinkLocationWriteStatus.RejectedConflict
|
|
1680
|
+
: Locations_1.ZLinkLocationWriteStatus.IgnoredStale,
|
|
1679
1681
|
generation,
|
|
1680
1682
|
updatedAt: current.value.storeNow
|
|
1681
1683
|
};
|
|
@@ -66,22 +66,24 @@ function read64(bytes, at) { if (at.value + 8 > bytes.length)
|
|
|
66
66
|
r = (r << 8n) | BigInt(bytes[at.value++]); return r; }
|
|
67
67
|
function u32(out, value) { if (!Number.isInteger(value) || value < 0 || value > 0xffffffff)
|
|
68
68
|
throw new RangeError("u32"); out.push(value >>> 24, (value >>> 16) & 255, (value >>> 8) & 255, value & 255); }
|
|
69
|
+
function appendBytes(out, bytes) { for (const byte of bytes)
|
|
70
|
+
out.push(byte); }
|
|
69
71
|
function read32(bytes, at) { if (at.value + 4 > bytes.length)
|
|
70
72
|
throw new RangeError("truncated u32"); return ((bytes[at.value++] << 24) | (bytes[at.value++] << 16) | (bytes[at.value++] << 8) | bytes[at.value++]) >>> 0; }
|
|
71
73
|
function text8(out, value) { const b = text.encode(value); if (b.length === 0 || b.length > 255 || b.includes(0))
|
|
72
|
-
throw new RangeError("text8"); out.push(b.length,
|
|
74
|
+
throw new RangeError("text8"); out.push(b.length); appendBytes(out, b); }
|
|
73
75
|
function readText8(bytes, at) { const n = bytes[at.value++]; if (n === 0 || at.value + n > bytes.length)
|
|
74
76
|
throw new RangeError("truncated text8"); const b = bytes.slice(at.value, at.value + n); at.value += n; if (b.includes(0))
|
|
75
77
|
throw new RangeError("text8 NUL"); return decodeText.decode(b); }
|
|
76
78
|
function rid(out, value) { if (value.length === 0 || value.length > 255)
|
|
77
|
-
throw new RangeError("rid"); out.push(value.length,
|
|
79
|
+
throw new RangeError("rid"); out.push(value.length); appendBytes(out, value); }
|
|
78
80
|
function readRid(bytes, at) { const n = bytes[at.value++]; if (n === 0 || at.value + n > bytes.length)
|
|
79
81
|
throw new RangeError("truncated rid"); const r = bytes.slice(at.value, at.value + n); at.value += n; return r; }
|
|
80
82
|
function fence(out, value) { if (value.generation === 0n || value.targetNodeGeneration === 0n || value.expectedAuthorityOwnerGeneration === 0n || value.expectedOwnerLeaseGeneration === 0n)
|
|
81
83
|
throw new RangeError("invalid fence"); text8(out, value.id); u64(out, value.generation); rid(out, value.targetNodeRid); u64(out, value.targetNodeGeneration); u64(out, value.expectedAuthorityOwnerGeneration); u64(out, value.expectedOwnerLeaseGeneration); }
|
|
82
84
|
function readFence(bytes, at) { const value = { id: readText8(bytes, at), generation: read64(bytes, at), targetNodeRid: readRid(bytes, at), targetNodeGeneration: read64(bytes, at), expectedAuthorityOwnerGeneration: read64(bytes, at), expectedOwnerLeaseGeneration: read64(bytes, at) }; if (value.generation === 0n || value.targetNodeGeneration === 0n || value.expectedAuthorityOwnerGeneration === 0n || value.expectedOwnerLeaseGeneration === 0n)
|
|
83
85
|
throw new RangeError("invalid fence"); return value; }
|
|
84
|
-
function encodePayload(value) { const body = []; text8(body, value.packetName); text8(body, value.contentType); u32(body, value.payload.length); body
|
|
86
|
+
function encodePayload(value) { const body = []; text8(body, value.packetName); text8(body, value.contentType); u32(body, value.payload.length); appendBytes(body, value.payload); const out = [1]; u32(out, body.length); appendBytes(out, body); return Uint8Array.from(out); }
|
|
85
87
|
function decodePayload(bytes) { const at = { value: 0 }; if (bytes[at.value++] !== 1)
|
|
86
88
|
throw new RangeError("payload version"); const length = read32(bytes, at); if (length !== bytes.length - at.value)
|
|
87
89
|
throw new RangeError("payload length"); const end = bytes.length; const packetName = readText8(bytes, at); const contentType = readText8(bytes, at); const length2 = read32(bytes, at); if (length2 !== end - at.value)
|
|
@@ -93,10 +95,10 @@ function decodeActorJoin28(frames) { if (frames.length < 1 || frames.length > 2)
|
|
|
93
95
|
throw new RangeError("actorJoin frames"); const bytes = frames[0]; const at = { value: 0 }; if (bytes.length < 5 || bytes[at.value++] !== 90 || bytes[at.value++] !== 77 || bytes[at.value++] !== 1 || bytes[at.value++] !== 28 || bytes[at.value++] !== 0)
|
|
94
96
|
throw new RangeError("actorJoin header"); const correlation = read64(bytes, at); const actor = readFence(bytes, at); const entry = bytes[at.value++]; const targetSpot = readFence(bytes, at); if (correlation === 0n || entry > 1 || at.value !== bytes.length)
|
|
95
97
|
throw new RangeError("actorJoin body"); const payload = frames.length === 2 ? decodePayload(frames[1]) : undefined; return payload === undefined ? { correlation, actor, entry: entry === 1, targetSpot } : { correlation, actor, entry: entry === 1, targetSpot, payload }; }
|
|
96
|
-
function bytes64(out, v) { u64(out, BigInt(v.length)); out
|
|
98
|
+
function bytes64(out, v) { u64(out, BigInt(v.length)); appendBytes(out, v); }
|
|
97
99
|
function body16(out, write) { const b = []; write(b); if (b.length > 65535)
|
|
98
|
-
throw new RangeError("body16"); out.push(b.length >>> 8, b.length & 255,
|
|
99
|
-
function body64(out, write) { const b = []; write(b); u64(out, BigInt(b.length)); out
|
|
100
|
+
throw new RangeError("body16"); out.push(b.length >>> 8, b.length & 255); appendBytes(out, b); }
|
|
101
|
+
function body64(out, write) { const b = []; write(b); u64(out, BigInt(b.length)); appendBytes(out, b); }
|
|
100
102
|
function sorted(v, compare, name) { for (let i = 1; i < v.length; i++)
|
|
101
103
|
if (compare(v[i - 1], v[i]) >= 0)
|
|
102
104
|
throw new RangeError(name); }
|
|
@@ -112,7 +114,7 @@ function readText16(bytes, at, minimum, maximum, name) { const n = read16(bytes,
|
|
|
112
114
|
throw new RangeError(name); const b = take(bytes, at, n, name); if (b.includes(0))
|
|
113
115
|
throw new RangeError(name + " NUL"); return decodeText.decode(b); }
|
|
114
116
|
function writeText16(out, value, minimum, maximum, name) { const b = text.encode(value); if (b.length < minimum || b.length > maximum || b.includes(0))
|
|
115
|
-
throw new RangeError(name); out.push(b.length >>> 8, b.length & 255,
|
|
117
|
+
throw new RangeError(name); out.push(b.length >>> 8, b.length & 255); appendBytes(out, b); }
|
|
116
118
|
function boundedEnd(bytes, at, length, name) { if (at.value + length > bytes.length)
|
|
117
119
|
throw new RangeError("truncated " + name); return at.value + length; }
|
|
118
120
|
function exactEnd(at, end, name) { if (at.value !== end)
|
|
@@ -305,7 +307,7 @@ function encodeRelocationEnvelopeV1(value) { const out = []; if (value.relocatio
|
|
|
305
307
|
exactEnd(at, w.frozenRecord.length, "frozen record");
|
|
306
308
|
u64(out, w.participantId);
|
|
307
309
|
u64(out, w.order);
|
|
308
|
-
out
|
|
310
|
+
appendBytes(out, w.frozenRecord);
|
|
309
311
|
} sorted(value.timerRegistrations, (a, b) => a.participantId === b.participantId ? compareUtf8(a.name, b.name) : a.participantId < b.participantId ? -1 : 1, "timerRegistrations"); u32(out, value.timerRegistrations.length); for (const t of value.timerRegistrations) {
|
|
310
312
|
if (t.participantId === 0n || t.periodMilliseconds === 0n || t.maxCatchUpTicks === 0n || t.overrunPolicy < 1 || t.overrunPolicy > 3)
|
|
311
313
|
throw new RangeError("timer");
|
|
@@ -385,17 +387,17 @@ function crc32c(bytes) { let crc = 0xffffffff; for (const octet of bytes) {
|
|
|
385
387
|
for (let bit = 0; bit < 8; bit++)
|
|
386
388
|
crc = (crc >>> 1) ^ ((crc & 1) !== 0 ? 0x82f63b78 : 0);
|
|
387
389
|
} return (~crc) >>> 0; }
|
|
388
|
-
function encodeDurable(magic, body, maximum) { const out = [...magic, 1, 0, 0]; u32(out, body.length); out
|
|
390
|
+
function encodeDurable(magic, body, maximum) { const out = [...magic, 1, 0, 0]; u32(out, body.length); appendBytes(out, body); if (out.length + 4 > maximum)
|
|
389
391
|
throw new RangeError("durable format bytes"); u32(out, crc32c(Uint8Array.from(out))); return Uint8Array.from(out); }
|
|
390
392
|
function decodeDurable(bytes, magic, maximum) { if (bytes.length > maximum || bytes.length < 15 || magic.some((v, i) => bytes[i] !== v) || bytes[4] !== 1 || bytes[5] !== 0 || bytes[6] !== 0)
|
|
391
393
|
throw new RangeError("durable header"); const at = { value: 7 }, length = read32(bytes, at), end = boundedEnd(bytes, at, length, "durable body"); if (end + 4 !== bytes.length)
|
|
392
394
|
throw new RangeError("durable length"); const body = bytes.slice(at.value, end); at.value = end; const expected = read32(bytes, at); if (crc32c(bytes.slice(0, end)) !== expected)
|
|
393
395
|
throw new RangeError("durable checksum"); return body; }
|
|
394
396
|
function encodeRelocationDataChunkV1(value) { const body = []; u32(body, value.order); if (value.data.length > 67108864)
|
|
395
|
-
throw new RangeError("chunk data"); u32(body, value.data.length); body
|
|
397
|
+
throw new RangeError("chunk data"); u32(body, value.data.length); appendBytes(body, value.data); return encodeDurable([90, 76, 84, 67], body, 67108886); }
|
|
396
398
|
function decodeRelocationDataChunkV1(bytes) { const body = decodeDurable(bytes, [90, 76, 84, 67], 67108886), at = { value: 0 }, order = read32(body, at), data = take(body, at, read32(body, at), "chunk data"); exactEnd(at, body.length, "chunk"); return { order, data }; }
|
|
397
399
|
function encodeRelocationManifestV1(value) { if (Number(value.logicalFormatVersion) !== 1 || value.totalLength > 274877906944n || value.inventoryDigestSha256.length !== 32 || value.chunks.length > 4096)
|
|
398
|
-
throw new RangeError("manifest"); sorted(value.chunks, (a, b) => a.order - b.order, "manifest chunks"); const body = [1]; u64(body, value.totalLength); u32(body, value.totalChecksumCrc32c); body.push(32,
|
|
400
|
+
throw new RangeError("manifest"); sorted(value.chunks, (a, b) => a.order - b.order, "manifest chunks"); const body = [1]; u64(body, value.totalLength); u32(body, value.totalChecksumCrc32c); body.push(32); appendBytes(body, value.inventoryDigestSha256); u32(body, value.chunks.length); for (const chunk of value.chunks) {
|
|
399
401
|
if (chunk.length === 0n)
|
|
400
402
|
throw new RangeError("chunk length");
|
|
401
403
|
u32(body, chunk.order);
|
|
@@ -568,14 +570,14 @@ function b3WriteSourceBase(o, v) { b3WriteId(o, v.relocation, "relocation"); if
|
|
|
568
570
|
throw new RangeError("sender role"); writeRelocationObject(o, v.object); }
|
|
569
571
|
function b3ReadSourceBase(bytes, at) { const relocation = b3ReadId(bytes, at, "relocation"), targetAttemptGeneration = nonzero64(bytes, at, "target attempt"), coordinator = b3ReadCoordinator(bytes, at), senderRole = b3ReadRole(bytes, at); if (senderRole !== "source")
|
|
570
572
|
throw new RangeError("sender role"); return { relocation, targetAttemptGeneration, coordinator, senderRole, object: readRelocationObject(bytes, at) }; }
|
|
571
|
-
function encodeRelocationData31(v) { const o = b3Header(31); b3WriteSourceBase(o, v); const at = { value: 0 }; skipFrozenRecord(v.record, at); exactEnd(at, v.record.length, "frozen record"); o
|
|
573
|
+
function encodeRelocationData31(v) { const o = b3Header(31); b3WriteSourceBase(o, v); const at = { value: 0 }; skipFrozenRecord(v.record, at); exactEnd(at, v.record.length, "frozen record"); appendBytes(o, v.record); return Uint8Array.from(o); }
|
|
572
574
|
function decodeRelocationData31(bytes) { const at = b3ReadHeader(bytes, 31, "relocationData"), v = b3ReadSourceBase(bytes, at), start = at.value; skipFrozenRecord(bytes, at); const record = bytes.slice(start, at.value); b3Finish(at, bytes, "relocationData"); return { ...v, record }; }
|
|
573
575
|
function encodeRelocationCutover34(v) { const o = b3Header(34); b3WriteSourceBase(o, v); if (v.boundaryRecordCount < 0n || v.boundaryRecordCount > 9223372036854775807n)
|
|
574
576
|
throw new RangeError("boundary count"); u64(o, v.boundaryRecordCount); u32(o, v.boundaryChecksumCrc32c); return Uint8Array.from(o); }
|
|
575
577
|
function decodeRelocationCutover34(bytes) { const at = b3ReadHeader(bytes, 34, "relocationCutover"), v = b3ReadSourceBase(bytes, at), boundaryRecordCount = read64(bytes, at); if (boundaryRecordCount > 9223372036854775807n)
|
|
576
578
|
throw new RangeError("boundary count"); const boundaryChecksumCrc32c = read32(bytes, at); b3Finish(at, bytes, "relocationCutover"); return { ...v, boundaryRecordCount, boundaryChecksumCrc32c }; }
|
|
577
579
|
function encodeRelocationState52(v) { const o = b3Header(52); b3WriteSourceBase(o, v); if (v.chunkData.length > 67108864)
|
|
578
|
-
throw new RangeError("chunk data"); u32(o, v.chunkOrdinal); u32(o, v.chunkData.length); o
|
|
580
|
+
throw new RangeError("chunk data"); u32(o, v.chunkOrdinal); u32(o, v.chunkData.length); appendBytes(o, v.chunkData); return Uint8Array.from(o); }
|
|
579
581
|
function decodeRelocationState52(bytes) { const at = b3ReadHeader(bytes, 52, "relocationState"), v = b3ReadSourceBase(bytes, at), chunkOrdinal = read32(bytes, at), length = read32(bytes, at); if (length > 67108864)
|
|
580
582
|
throw new RangeError("chunk data"); const chunkData = take(bytes, at, length, "chunk data"); b3Finish(at, bytes, "relocationState"); return { ...v, chunkOrdinal, chunkData }; }
|
|
581
583
|
function encodeRelocationPrepare40(v) { const o = b3Header(40); b3WriteId(o, v.relocation, "relocation"); if (v.targetAttemptGeneration === 0n || v.sourceNodeGeneration === 0n)
|
|
@@ -615,7 +617,7 @@ function b4ReadReservation(bytes, at) { return { reservationId: readText8(bytes,
|
|
|
615
617
|
throw new RangeError("pending capacity"); return v; })() }; }
|
|
616
618
|
function b4Close(out, v) { const body = []; text8(body, v.spotId); if (v.objectGeneration === 0n || v.targetNodeGeneration === 0n || v.expectedAuthorityOwnerGeneration === 0n)
|
|
617
619
|
throw new RangeError("close fence"); u64(body, v.objectGeneration); rid(body, v.targetNodeRid); u64(body, v.targetNodeGeneration); u64(body, v.expectedAuthorityOwnerGeneration); writeText16(body, v.expectedStoreVersion, 1, 4096, "store version"); out.push(1); if (body.length > 65535)
|
|
618
|
-
throw new RangeError("close fence"); out.push(body.length >>> 8, body.length & 255,
|
|
620
|
+
throw new RangeError("close fence"); out.push(body.length >>> 8, body.length & 255); appendBytes(out, body); }
|
|
619
621
|
function b4ReadClose(bytes, at) { if (read8(bytes, at) !== 1)
|
|
620
622
|
throw new RangeError("close fence version"); const end = boundedEnd(bytes, at, read16(bytes, at), "close fence"); const value = { spotId: readText8(bytes, at), objectGeneration: nonzero64(bytes, at, "object generation"), targetNodeRid: readRid(bytes, at), targetNodeGeneration: nonzero64(bytes, at, "target node generation"), expectedAuthorityOwnerGeneration: nonzero64(bytes, at, "authority generation"), expectedStoreVersion: readText16(bytes, at, 1, 4096, "store version") }; exactEnd(at, end, "close fence"); return value; }
|
|
621
623
|
function b4CommandBase(out, v) { if (v.correlation === 0n || v.sourceNodeGeneration === 0n)
|
|
@@ -630,14 +632,14 @@ function decodeUserSpotClose48(bytes) { const at = b4ReadHeader(bytes, 48, "user
|
|
|
630
632
|
function encodeActorCreate49(v) { const out = b4Header(49); b4CommandBase(out, v); text8(out, v.actorId); text8(out, v.stableType); b4Reservation(out, v.reservation); if (v.deadlineUnixMs === 0n)
|
|
631
633
|
throw new RangeError("deadline"); u64(out, v.deadlineUnixMs); return Uint8Array.from(out); }
|
|
632
634
|
function decodeActorCreate49(bytes) { const at = b4ReadHeader(bytes, 49, "actorCreate"), base = b4ReadCommandBase(bytes, at), actorId = readText8(bytes, at), stableType = readText8(bytes, at), reservation = b4ReadReservation(bytes, at), deadlineUnixMs = nonzero64(bytes, at, "deadline"); b4Finish(at, bytes, "actorCreate"); return { ...base, actorId, stableType, reservation, deadlineUnixMs }; }
|
|
633
|
-
function b4ZljrPayload(v) { if (v.metadata.length > 262144
|
|
634
|
-
throw new RangeError("ZLJR length"); const out = [90, 76, 74, 82, 1]; u32(out, v.metadata.length); u32(out, v.request.length); u32(out, v.reply.length); out
|
|
635
|
+
function b4ZljrPayload(v) { if (v.metadata.length > 262144)
|
|
636
|
+
throw new RangeError("ZLJR length"); const out = [90, 76, 74, 82, 1]; u32(out, v.metadata.length); u32(out, v.request.length); u32(out, v.reply.length); appendBytes(out, v.metadata); appendBytes(out, v.request); appendBytes(out, v.reply); return Uint8Array.from(out); }
|
|
635
637
|
function b4ReadZljrPayload(bytes) { const at = { value: 0 }; if (read32(bytes, at) !== 0x5a4c4a52 || read8(bytes, at) !== 1)
|
|
636
|
-
throw new RangeError("ZLJR header"); const metadataLength = read32(bytes, at), requestLength = read32(bytes, at), replyLength = read32(bytes, at); if (metadataLength > 262144
|
|
638
|
+
throw new RangeError("ZLJR header"); const metadataLength = read32(bytes, at), requestLength = read32(bytes, at), replyLength = read32(bytes, at); if (metadataLength > 262144)
|
|
637
639
|
throw new RangeError("ZLJR length"); const metadata = take(bytes, at, metadataLength, "ZLJR metadata"), request = take(bytes, at, requestLength, "ZLJR request"), reply = take(bytes, at, replyLength, "ZLJR reply"); b4Finish(at, bytes, "ZLJR"); return { metadata, request, reply }; }
|
|
638
640
|
function encodeZljrRecordV1(v) { if (v.source.nodeGeneration === 0n || v.source.ownerLeaseGeneration === 0n)
|
|
639
641
|
throw new RangeError("ZLJR record"); const source = []; rid(source, v.source.nodeRid); u64(source, v.source.nodeGeneration); text8(source, v.source.ownerId); u64(source, v.source.ownerLeaseGeneration); if (source.length > 65535)
|
|
640
|
-
throw new RangeError("ZLJR source"); const payload = b4ZljrPayload(v), body = []; text8(body, "__zlink.actor.routed_join.recovery"); text8(body, "application/x-zlink-actor-routed-join-recovery-v1"); u32(body, payload.length); body
|
|
642
|
+
throw new RangeError("ZLJR source"); const payload = b4ZljrPayload(v), body = []; text8(body, "__zlink.actor.routed_join.recovery"); text8(body, "application/x-zlink-actor-routed-join-recovery-v1"); u32(body, payload.length); appendBytes(body, payload); const out = [1, 1, source.length >>> 8, source.length & 255, ...source, 0]; u64(out, v.operation.high); u64(out, v.operation.low); u32(out, 0); out.push(0, 0, 1); u32(out, body.length); appendBytes(out, body); return Uint8Array.from(out); }
|
|
641
643
|
function decodeZljrRecordV1(bytes) { const at = { value: 0 }; if (read8(bytes, at) !== 1 || read8(bytes, at) !== 1)
|
|
642
644
|
throw new RangeError("ZLJR frozen record"); const sourceEnd = boundedEnd(bytes, at, read16(bytes, at), "ZLJR source"), nodeRid = readRid(bytes, at), nodeGeneration = nonzero64(bytes, at, "source node generation"), ownerId = readText8(bytes, at), ownerLeaseGeneration = nonzero64(bytes, at, "source owner lease"); exactEnd(at, sourceEnd, "ZLJR source"); if (read8(bytes, at) !== 0)
|
|
643
645
|
throw new RangeError("ZLJR metadata flag"); const operation = { high: read64(bytes, at), low: read64(bytes, at) }; if (read32(bytes, at) !== 0 || read16(bytes, at) !== 0 || read8(bytes, at) !== 1)
|
|
@@ -231,6 +231,7 @@ class DefaultZLinkSpotManager {
|
|
|
231
231
|
await activation.nativeSpot?.dispose();
|
|
232
232
|
}
|
|
233
233
|
async materializeInstance(meshName, instanceType, spotId, objectGeneration, signal) {
|
|
234
|
+
const readyTarget = this.options.instanceSpotApplicationTargetProvider?.(meshName, spotId);
|
|
234
235
|
const existing = this.pendingInstanceMaterializations.get(instanceMaterializationKey(meshName, spotId, objectGeneration));
|
|
235
236
|
if (existing !== undefined) {
|
|
236
237
|
await (0, abort_1.awaitWithAbort)(existing, signal);
|
|
@@ -238,18 +239,23 @@ class DefaultZLinkSpotManager {
|
|
|
238
239
|
}
|
|
239
240
|
const release = await this.acquireInstanceActivation(meshName, signal);
|
|
240
241
|
try {
|
|
241
|
-
await this.materializeInstanceCore(meshName, instanceType, spotId, objectGeneration, signal);
|
|
242
|
+
await this.materializeInstanceCore(meshName, instanceType, spotId, objectGeneration, readyTarget?.objectGeneration === objectGeneration, signal);
|
|
242
243
|
}
|
|
243
244
|
finally {
|
|
244
245
|
release();
|
|
245
246
|
}
|
|
246
247
|
}
|
|
247
|
-
async materializeInstanceCore(meshName, instanceType, spotId, objectGeneration, signal) {
|
|
248
|
+
async materializeInstanceCore(meshName, instanceType, spotId, objectGeneration, requiresReadyTarget, signal) {
|
|
248
249
|
const factory = this.requireInstanceFactory(meshName, instanceType);
|
|
249
250
|
const key = `${meshName}\0${String(spotId)}`;
|
|
250
251
|
const materializationKey = instanceMaterializationKey(meshName, spotId, objectGeneration);
|
|
251
252
|
const materializationPrefix = instanceMaterializationPrefix(meshName, spotId);
|
|
252
253
|
for (;;) {
|
|
254
|
+
if (requiresReadyTarget &&
|
|
255
|
+
this.options.instanceSpotApplicationTargetProvider?.(meshName, spotId)?.objectGeneration !==
|
|
256
|
+
objectGeneration) {
|
|
257
|
+
throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.SpotGenerationStale, `Instance Spot '${String(spotId)}' no longer has its Ready application target.`);
|
|
258
|
+
}
|
|
253
259
|
// A close seals admission before cleanup removes the activation. An
|
|
254
260
|
// explicit Instance intent arriving at that boundary must wait for the
|
|
255
261
|
// old resources to be released before materializing the replacement.
|
|
@@ -907,7 +913,7 @@ class DefaultZLinkSpotManager {
|
|
|
907
913
|
throw new configuration_1.ZLinkConfigurationException('MeshNode Instance Spot record is missing the owner Spot RID.');
|
|
908
914
|
}
|
|
909
915
|
const envelope = (0, channel_envelope_1.decodeChannelEnvelope)(record.parts, undefined, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true);
|
|
910
|
-
const inboundFlow = (0, diagnostics_1.createInboundFlow)(envelope.header.flowId, envelope.header.flowOrigin, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true);
|
|
916
|
+
const inboundFlow = (0, diagnostics_1.createInboundFlow)(envelope.header.flowId, envelope.header.flowOrigin, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true, undefined);
|
|
911
917
|
await (0, diagnostics_1.runWithFlow)(inboundFlow, async () => {
|
|
912
918
|
const request = record.operationKind === service_runtime_contracts_1.OperationKind.InstanceSpotRequest;
|
|
913
919
|
this.traceInstanceMessage(ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Received, meshName, spotId, record, envelope, 'activating');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ActorRef, ZLinkActor, ZLinkMessageSerializer, ZLinkSpot } from '../../contracts';
|
|
1
|
+
import type { ActorRef, RoutingId, ZLinkActor, ZLinkMessageSerializer, ZLinkSpot } from '../../contracts';
|
|
2
2
|
import type { Message } from '../../contracts/Common/Message';
|
|
3
3
|
import type { ZLinkRemoteBoundSessionTarget } from '../actors';
|
|
4
4
|
import { ZLinkSpotActorHandlerRegistryRuntime } from '../actors';
|
|
@@ -24,6 +24,7 @@ export interface ZLinkActorPacketDelivery {
|
|
|
24
24
|
readonly fallbackActorRef?: ActorRef;
|
|
25
25
|
readonly requestTerminal?: ZLinkActorRequestTerminal;
|
|
26
26
|
readonly messageFollowOrigin?: ZLinkMessageFollowOrigin;
|
|
27
|
+
readonly sourceSessionRid?: RoutingId;
|
|
27
28
|
}
|
|
28
29
|
/**
|
|
29
30
|
* Spec 15 §4.2 relocation temporary queue: consulted when {@code
|
|
@@ -6,6 +6,7 @@ const contracts_1 = require("../../contracts");
|
|
|
6
6
|
const ZLinkDispatchOptions_1 = require("../../contracts/Dispatch/ZLinkDispatchOptions");
|
|
7
7
|
const diagnostics_1 = require("../diagnostics");
|
|
8
8
|
const flow_context_1 = require("../diagnostics/flow-context");
|
|
9
|
+
const managed_stream_1 = require("../streams/managed-stream");
|
|
9
10
|
const actors_1 = require("../actors");
|
|
10
11
|
const protocol_1 = require("../streams/protocol");
|
|
11
12
|
const payload_codec_1 = require("../messaging/payload-codec");
|
|
@@ -48,7 +49,11 @@ class ZLinkSpotActorPacketDispatch {
|
|
|
48
49
|
this.reportInvalidFrame(actorId, ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.ActorSend, error);
|
|
49
50
|
throw error;
|
|
50
51
|
}
|
|
51
|
-
return (0, flow_context_1.runWithFlow)((0, flow_context_1.createInboundFlow)(header.flowId, header.flowOrigin, flowEnabled
|
|
52
|
+
return (0, flow_context_1.runWithFlow)((0, flow_context_1.createInboundFlow)(header.flowId, header.flowOrigin, flowEnabled, !flowEnabled
|
|
53
|
+
? undefined
|
|
54
|
+
: delivery.sourceSessionRid === undefined
|
|
55
|
+
? (0, flow_context_1.currentFlowContext)()?.streamSessionId
|
|
56
|
+
: (0, managed_stream_1.streamSessionIdFromRoutingId)(delivery.sourceSessionRid)), async () => {
|
|
52
57
|
const messageKind = header.kind === protocol_1.ZLinkStreamMessageKind.Request
|
|
53
58
|
? ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.ActorRequest
|
|
54
59
|
: ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.ActorSend;
|
|
@@ -86,6 +86,7 @@ class ZLinkSpotActorPacketDrain {
|
|
|
86
86
|
actorId,
|
|
87
87
|
parts,
|
|
88
88
|
returnResponse: false,
|
|
89
|
+
sourceSessionRid,
|
|
89
90
|
fallbackActorRef: actorRef
|
|
90
91
|
});
|
|
91
92
|
}
|
|
@@ -133,6 +134,7 @@ class ZLinkSpotActorPacketDrain {
|
|
|
133
134
|
actorId,
|
|
134
135
|
parts,
|
|
135
136
|
returnResponse: true,
|
|
137
|
+
sourceSessionRid: info.sourceSessionRid,
|
|
136
138
|
fallbackActorRef: actorRef
|
|
137
139
|
});
|
|
138
140
|
this.options.replyActorNoBind?.(info, [this.encodeActorReplyFrame(parts[0], protocol_1.ZLinkStreamMessageKind.Response, response)], runtime_values_1.RequestResult.Ok);
|
|
@@ -143,6 +143,9 @@ class ZLinkSpotNodeRuntimeManager {
|
|
|
143
143
|
applicationJobReceiveFlowFailureSink: this.options.applicationJobReceiveFlowFailureSink,
|
|
144
144
|
peerAdmissionSealed: () => this.options.peerAdmissionSealed?.(spotNodeName) ?? false
|
|
145
145
|
});
|
|
146
|
+
if (this.options.dispatchErrors !== undefined) {
|
|
147
|
+
node.setDispatchErrorReporter?.(this.options.dispatchErrors, spotNodeName);
|
|
148
|
+
}
|
|
146
149
|
node.setMailboxRecordDroppedHandler?.((record) => this.options.dispatchErrors?.report({
|
|
147
150
|
surface: ZLinkDispatchOptions_1.ZLinkDispatchErrorSurface.RouteMeshChannel,
|
|
148
151
|
messageKind: ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Send,
|
|
@@ -813,7 +816,8 @@ class ZLinkSpotNodeRuntimeManager {
|
|
|
813
816
|
}
|
|
814
817
|
const parts = (0, flow_context_1.runWithOutboundFlow)(this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true, () => (0, channel_envelope_1.encodeChannelPublishEnvelopeParts)(channelName, topic, packetName, event, undefined, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true, metadata));
|
|
815
818
|
try {
|
|
816
|
-
publisher.publish(channelName, topic, parts, { flags });
|
|
819
|
+
const processing = publisher.publish(channelName, topic, parts, { flags });
|
|
820
|
+
void Promise.resolve(processing).catch(() => undefined);
|
|
817
821
|
return { status: submission_result_1.ZLinkSubmitStatus.Submitted };
|
|
818
822
|
}
|
|
819
823
|
catch (error) {
|
|
@@ -83,7 +83,7 @@ class ZLinkSpotRoutePacketDispatch {
|
|
|
83
83
|
let response;
|
|
84
84
|
const applicationClaim = this.options.claimApplicationWork?.();
|
|
85
85
|
try {
|
|
86
|
-
await (0, flow_context_1.runWithFlow)((0, flow_context_1.createInboundFlow)(envelope.header.flowId, envelope.header.flowOrigin, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true), () => this.options.serial.execute(async () => {
|
|
86
|
+
await (0, flow_context_1.runWithFlow)((0, flow_context_1.createInboundFlow)(envelope.header.flowId, envelope.header.flowOrigin, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true, undefined), () => this.options.serial.execute(async () => {
|
|
87
87
|
const spot = this.options.getTarget();
|
|
88
88
|
// Keep the wire payload raw until this Spot has acquired its
|
|
89
89
|
// execution authority. A rejected or superseded work item must not
|
|
@@ -12,7 +12,7 @@ class ZLinkSpotRoutedBoundSessionDispatch {
|
|
|
12
12
|
async dispatch(received) {
|
|
13
13
|
const boundSessionSend = (0, spot_remote_route_codec_1.decodeRemoteBoundSessionSend)(received.parts, this.options.channelCodecs(), this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true);
|
|
14
14
|
if (boundSessionSend !== undefined) {
|
|
15
|
-
await (0, flow_context_1.runWithFlow)((0, flow_context_1.createInboundFlow)(boundSessionSend.flowId ?? boundSessionSend.envelope?.header.flowId, boundSessionSend.flowOrigin ?? boundSessionSend.envelope?.header.flowOrigin, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true), () => this.options.routedBoundSessionReceiver?.(boundSessionSend.actorId, boundSessionSend.message, boundSessionSend.packetName, boundSessionSend.metadata, boundSessionSend.actorRef, boundSessionSend.actorPacketTarget));
|
|
15
|
+
await (0, flow_context_1.runWithFlow)((0, flow_context_1.createInboundFlow)(boundSessionSend.flowId ?? boundSessionSend.envelope?.header.flowId, boundSessionSend.flowOrigin ?? boundSessionSend.envelope?.header.flowOrigin, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true, undefined), () => this.options.routedBoundSessionReceiver?.(boundSessionSend.actorId, boundSessionSend.message, boundSessionSend.packetName, boundSessionSend.metadata, boundSessionSend.actorRef, boundSessionSend.actorPacketTarget));
|
|
16
16
|
if ((0, spot_route_replies_1.hasReplyToken)(received.replyToken)) {
|
|
17
17
|
(0, spot_route_replies_1.submitRoutePayloadReply)(received, boundSessionSend.envelope, { ok: true });
|
|
18
18
|
}
|
|
@@ -131,7 +131,7 @@ class ZLinkSpotSubscriptionDispatch {
|
|
|
131
131
|
}
|
|
132
132
|
const spot = this.options.getTarget();
|
|
133
133
|
const subSource = message.routingId === null ? undefined : String(message.routingId);
|
|
134
|
-
const inboundFlow = (0, flow_context_1.createInboundFlow)(envelope.header.flowId, envelope.header.flowOrigin, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true);
|
|
134
|
+
const inboundFlow = (0, flow_context_1.createInboundFlow)(envelope.header.flowId, envelope.header.flowOrigin, this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true, undefined);
|
|
135
135
|
try {
|
|
136
136
|
await this.options.serial.execute(() => (0, flow_context_1.runWithFlow)(inboundFlow, async () => {
|
|
137
137
|
// Payload deserialization belongs to the Spot serial turn. Queue
|
|
@@ -62,7 +62,7 @@ class ZLinkSpotTimerRegistry {
|
|
|
62
62
|
let callbackStarted = false;
|
|
63
63
|
const timer = startOutsideStateLane(() => new ZLinkManagedTimer(name, periodMs, normalizeTimerOptions(options), async (tick) => {
|
|
64
64
|
callbackStarted = false;
|
|
65
|
-
const timerFlow = (0, flow_context_1.createInboundFlow)(undefined, 'Timer', this.flowCreationEnabled());
|
|
65
|
+
const timerFlow = (0, flow_context_1.createInboundFlow)(undefined, 'Timer', this.flowCreationEnabled(), undefined);
|
|
66
66
|
const operation = () => {
|
|
67
67
|
const current = this.timers.get(name);
|
|
68
68
|
if (current === undefined ||
|