@zlink-systems/framework 0.27.0 → 0.29.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/Actors/ZLinkActorClient.d.ts +1 -1
- package/dist/contracts/Channels/Calls.d.ts +1 -1
- package/dist/contracts/Configuration/Builders.d.ts +2 -1
- package/dist/contracts/Configuration/Configs.d.ts +0 -1
- package/dist/contracts/Configuration/InternalDefaults.d.ts +3 -0
- package/dist/contracts/Configuration/InternalDefaults.js +4 -1
- package/dist/contracts/Configuration/RegistrationBuilders.js +9 -0
- package/dist/contracts/Configuration/RegistrationNormalizers.d.ts +1 -0
- package/dist/contracts/Configuration/RegistrationNormalizers.js +16 -0
- package/dist/contracts/Configuration/RegistrationTypes.d.ts +3 -5
- package/dist/contracts/Configuration/RegistrationValidators.js +0 -2
- package/dist/contracts/Spots/Contracts.d.ts +1 -1
- package/dist/contracts/Streams/BoundSessionContracts.d.ts +1 -1
- package/dist/contracts/Streams/IZLinkSession.d.ts +2 -3
- package/dist/contracts/Streams/IZLinkSessionActor.d.ts +2 -2
- package/dist/runtime/actors/actor-client.js +7 -10
- package/dist/runtime/actors/actor-mailbox.d.ts +2 -2
- package/dist/runtime/actors/actor-mailbox.js +2 -2
- package/dist/runtime/actors/actor-request-deadline.d.ts +1 -3
- package/dist/runtime/actors/actor-request-deadline.js +4 -29
- package/dist/runtime/actors/actor-routed-join-transport.d.ts +2 -3
- package/dist/runtime/backend/contracts/index.d.ts +1 -4
- package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +2 -0
- package/dist/runtime/backend/node/node-backend-adapter-support.js +35 -4
- package/dist/runtime/backend/node/node-dealer-channel-client-transport.d.ts +1 -1
- package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +1 -2
- package/dist/runtime/backend/node/node-raw-binding-port.js +10 -27
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +1 -3
- package/dist/runtime/backend/node/node-socket-backend-adapter.js +1 -1
- package/dist/runtime/channels/channel-clients.js +8 -10
- package/dist/runtime/channels/channel-envelope.d.ts +1 -1
- package/dist/runtime/channels/channel-envelope.js +4 -10
- package/dist/runtime/channels/channel-outbound-operations.d.ts +2 -2
- package/dist/runtime/channels/channel-outbound-operations.js +20 -36
- package/dist/runtime/channels/channel-runtime-manager.d.ts +4 -4
- package/dist/runtime/channels/channel-runtime-manager.js +8 -8
- package/dist/runtime/channels/channel-socket-options.js +0 -10
- package/dist/runtime/channels/channel-socket-registry.d.ts +1 -1
- package/dist/runtime/channels/channel-socket-registry.js +3 -14
- package/dist/runtime/channels/channel-transports.d.ts +10 -14
- package/dist/runtime/channels/channel-transports.js +11 -14
- package/dist/runtime/channels/local-spot-route-transport.d.ts +1 -1
- package/dist/runtime/channels/local-spot-route-transport.js +1 -3
- package/dist/runtime/channels/router-socket-spot-route-transport.d.ts +1 -1
- package/dist/runtime/channels/router-socket-spot-route-transport.js +2 -2
- package/dist/runtime/channels/source-spot-router.d.ts +1 -1
- package/dist/runtime/channels/source-spot-router.js +2 -4
- package/dist/runtime/channels/spot-node-route-transport.d.ts +1 -1
- package/dist/runtime/channels/spot-node-route-transport.js +2 -3
- package/dist/runtime/channels/spot-route-bridge-transport.d.ts +1 -1
- package/dist/runtime/channels/spot-route-bridge-transport.js +2 -2
- package/dist/runtime/channels/spot-route-dispatch-strategy.d.ts +2 -4
- package/dist/runtime/channels/spot-route-dispatch-strategy.js +11 -12
- package/dist/runtime/execution/serial-execution-queue.d.ts +12 -3
- package/dist/runtime/execution/serial-execution-queue.js +116 -28
- package/dist/runtime/foundation/operation-registry.d.ts +3 -2
- package/dist/runtime/foundation/operation-registry.js +5 -1
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +17 -11
- package/dist/runtime/foundation/service-authority-payload-codec.d.ts +0 -2
- package/dist/runtime/foundation/service-authority-payload-codec.js +0 -43
- package/dist/runtime/foundation/service-stateful-registry.d.ts +4 -3
- package/dist/runtime/foundation/service-stateful-registry.js +5 -2
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +1 -8
- package/dist/runtime/foundation/service-stateful-runtime.js +78 -126
- package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +0 -11
- package/dist/runtime/foundation/service-stateful-wire-codec.js +1 -23
- package/dist/runtime/foundation/service-topology-registry.d.ts +5 -0
- package/dist/runtime/foundation/service-topology-registry.js +16 -17
- package/dist/runtime/host/actor-packet-relay.d.ts +2 -2
- package/dist/runtime/host/actor-packet-relay.js +8 -10
- package/dist/runtime/host/index.js +1 -15
- package/dist/runtime/host/instance-activation-authority.d.ts +0 -1
- package/dist/runtime/host/instance-activation-authority.js +46 -97
- package/dist/runtime/host/remote-bound-session-relay.js +8 -8
- package/dist/runtime/host/spot-address-transport.d.ts +1 -9
- package/dist/runtime/host/spot-address-transport.js +47 -87
- package/dist/runtime/host/stateful-authority-route-runtime.d.ts +1 -0
- package/dist/runtime/host/stateful-authority-route-runtime.js +17 -9
- package/dist/runtime/locations/domain-store-contract.d.ts +1 -0
- package/dist/runtime/locations/in-memory-authority-store.d.ts +2 -0
- package/dist/runtime/locations/in-memory-authority-store.js +22 -8
- package/dist/runtime/locations/in-memory-location-store.d.ts +1 -0
- package/dist/runtime/locations/in-memory-location-store.js +3 -0
- package/dist/runtime/locations/internal-store-contracts.d.ts +1 -1
- package/dist/runtime/locations/location-store-repository.d.ts +1 -0
- package/dist/runtime/locations/location-store-repository.js +98 -53
- package/dist/runtime/messaging/request-deadline.d.ts +3 -0
- package/dist/runtime/messaging/request-deadline.js +39 -0
- package/dist/runtime/messaging/submission-result.d.ts +2 -1
- package/dist/runtime/messaging/submission-result.js +11 -4
- package/dist/runtime/spots/spot-outbound.d.ts +3 -5
- package/dist/runtime/spots/spot-outbound.js +12 -15
- package/dist/runtime/spots/spot-serial-executor.js +4 -1
- package/dist/runtime/spots/spot-serial-turn-executor.d.ts +3 -0
- package/dist/runtime/spots/spot-serial-turn-executor.js +5 -0
- package/dist/runtime/streams/bound-actor-relay-sender.d.ts +2 -2
- package/dist/runtime/streams/bound-actor-relay-sender.js +14 -14
- package/dist/runtime/streams/bound-session-service.js +1 -1
- package/dist/runtime/streams/index.d.ts +2 -3
- package/dist/runtime/streams/index.js +6 -5
- package/dist/runtime/streams/managed-stream.d.ts +2 -2
- package/dist/runtime/streams/managed-stream.js +3 -5
- package/dist/runtime/streams/native-fallback-bound-session.js +13 -16
- package/dist/runtime/streams/protocol.js +0 -9
- package/dist/runtime/streams/session-calls.d.ts +5 -7
- package/dist/runtime/streams/session-calls.js +6 -23
- package/dist/runtime/streams/session-context.d.ts +4 -8
- package/dist/runtime/streams/session-context.js +3 -32
- package/dist/runtime/streams/session-serial-executor.d.ts +0 -1
- package/dist/runtime/streams/session-serial-executor.js +0 -3
- package/dist/runtime/streams/stream-message-utils.d.ts +0 -1
- package/dist/runtime/streams/stream-message-utils.js +0 -20
- package/dist/runtime/streams/stream-session-runtime.d.ts +6 -3
- package/dist/runtime/streams/stream-session-runtime.js +40 -85
- package/package.json +3 -3
- package/src/contracts/Actors/ZLinkActorClient.ts +1 -1
- package/src/contracts/Channels/Calls.ts +1 -1
- package/src/contracts/Configuration/Builders.ts +2 -1
- package/src/contracts/Configuration/Configs.ts +0 -1
- package/src/contracts/Configuration/InternalDefaults.ts +4 -0
- package/src/contracts/Configuration/RegistrationBuilders.ts +13 -3
- package/src/contracts/Configuration/RegistrationNormalizers.ts +25 -0
- package/src/contracts/Configuration/RegistrationTypes.ts +3 -5
- package/src/contracts/Configuration/RegistrationValidators.ts +0 -4
- package/src/contracts/Spots/Contracts.ts +1 -1
- package/src/contracts/Streams/BoundSessionContracts.ts +1 -1
- package/src/contracts/Streams/IZLinkSession.ts +2 -3
- package/src/contracts/Streams/IZLinkSessionActor.ts +2 -6
- package/src/runtime/actors/actor-client.ts +9 -15
- package/src/runtime/actors/actor-mailbox.ts +7 -2
- package/src/runtime/actors/actor-request-deadline.ts +4 -49
- package/src/runtime/actors/actor-routed-join-transport.ts +3 -5
- package/src/runtime/backend/contracts/index.ts +1 -8
- package/src/runtime/backend/node/node-backend-adapter-support.ts +45 -5
- package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +0 -2
- package/src/runtime/backend/node/node-raw-binding-port.ts +15 -41
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +1 -7
- package/src/runtime/backend/node/node-socket-backend-adapter.ts +1 -1
- package/src/runtime/channels/channel-clients.ts +9 -19
- package/src/runtime/channels/channel-envelope.ts +16 -28
- package/src/runtime/channels/channel-outbound-operations.ts +36 -38
- package/src/runtime/channels/channel-runtime-manager.ts +6 -28
- package/src/runtime/channels/channel-socket-options.ts +0 -13
- package/src/runtime/channels/channel-socket-registry.ts +3 -17
- package/src/runtime/channels/channel-transports.ts +7 -33
- package/src/runtime/channels/local-spot-route-transport.ts +1 -5
- package/src/runtime/channels/router-socket-spot-route-transport.ts +2 -6
- package/src/runtime/channels/source-spot-router.ts +2 -9
- package/src/runtime/channels/spot-node-route-transport.ts +2 -7
- package/src/runtime/channels/spot-route-bridge-transport.ts +5 -12
- package/src/runtime/channels/spot-route-dispatch-strategy.ts +11 -30
- package/src/runtime/execution/serial-execution-queue.ts +117 -29
- package/src/runtime/foundation/operation-registry.ts +16 -9
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +17 -14
- package/src/runtime/foundation/service-authority-payload-codec.ts +0 -46
- package/src/runtime/foundation/service-stateful-registry.ts +10 -5
- package/src/runtime/foundation/service-stateful-runtime.ts +103 -181
- package/src/runtime/foundation/service-stateful-wire-codec.ts +0 -25
- package/src/runtime/foundation/service-topology-registry.ts +30 -20
- package/src/runtime/host/actor-packet-relay.ts +8 -15
- package/src/runtime/host/index.ts +2 -26
- package/src/runtime/host/instance-activation-authority.ts +58 -125
- package/src/runtime/host/remote-bound-session-relay.ts +8 -16
- package/src/runtime/host/spot-address-transport.ts +107 -166
- package/src/runtime/host/stateful-authority-route-runtime.ts +22 -9
- package/src/runtime/locations/domain-store-contract.ts +5 -0
- package/src/runtime/locations/in-memory-authority-store.ts +42 -8
- package/src/runtime/locations/in-memory-location-store.ts +8 -0
- package/src/runtime/locations/internal-store-contracts.ts +1 -0
- package/src/runtime/locations/location-store-repository.ts +130 -68
- package/src/runtime/messaging/request-deadline.ts +58 -0
- package/src/runtime/messaging/submission-result.ts +18 -4
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
- package/src/runtime/spots/spot-outbound.ts +14 -24
- package/src/runtime/spots/spot-serial-executor.ts +4 -1
- package/src/runtime/spots/spot-serial-turn-executor.ts +10 -0
- package/src/runtime/streams/bound-actor-relay-sender.ts +12 -26
- package/src/runtime/streams/bound-session-service.ts +1 -1
- package/src/runtime/streams/index.ts +5 -5
- package/src/runtime/streams/managed-stream.ts +3 -13
- package/src/runtime/streams/native-fallback-bound-session.ts +13 -25
- package/src/runtime/streams/protocol.ts +0 -13
- package/src/runtime/streams/session-calls.ts +8 -34
- package/src/runtime/streams/session-context.ts +6 -47
- package/src/runtime/streams/session-serial-executor.ts +0 -8
- package/src/runtime/streams/stream-message-utils.ts +1 -27
- package/src/runtime/streams/stream-session-runtime.ts +54 -97
- package/dist/runtime/streams/session-requests.d.ts +0 -25
- package/dist/runtime/streams/session-requests.js +0 -101
- package/src/runtime/streams/session-requests.ts +0 -110
|
@@ -32,8 +32,6 @@ export interface ZLinkLocalSpotRouteDispatcher {
|
|
|
32
32
|
message: unknown,
|
|
33
33
|
context: {
|
|
34
34
|
readonly channelName: string;
|
|
35
|
-
readonly admissionTimeoutMs?: number;
|
|
36
|
-
readonly signal?: AbortSignal;
|
|
37
35
|
readonly metadata?: ReadonlyMap<string, string>;
|
|
38
36
|
}
|
|
39
37
|
): Promise<void>;
|
|
@@ -101,14 +99,12 @@ export class ZLinkSpotRouteDispatchStrategy {
|
|
|
101
99
|
spotRouteTarget: ZLinkSpotRouteTarget,
|
|
102
100
|
packetName: string | undefined,
|
|
103
101
|
message: unknown,
|
|
104
|
-
|
|
105
|
-
metadata?: ReadonlyMap<string, string>,
|
|
106
|
-
timeoutMs?: number
|
|
102
|
+
metadata?: ReadonlyMap<string, string>
|
|
107
103
|
): Promise<void> {
|
|
108
104
|
// Call-scoped flow (spec 27 §4): the envelope flow pair lives only for
|
|
109
105
|
// the duration of this outbound call, never in the caller's context.
|
|
110
106
|
return runWithOutboundFlow(this.options.flowCreationEnabled?.() ?? true, () =>
|
|
111
|
-
this.routeSendToSpotScoped(spotRouteTarget, packetName, message,
|
|
107
|
+
this.routeSendToSpotScoped(spotRouteTarget, packetName, message, metadata)
|
|
112
108
|
);
|
|
113
109
|
}
|
|
114
110
|
|
|
@@ -116,11 +112,8 @@ export class ZLinkSpotRouteDispatchStrategy {
|
|
|
116
112
|
spotRouteTarget: ZLinkSpotRouteTarget,
|
|
117
113
|
packetName: string | undefined,
|
|
118
114
|
message: unknown,
|
|
119
|
-
|
|
120
|
-
metadata?: ReadonlyMap<string, string>,
|
|
121
|
-
timeoutMs?: number
|
|
115
|
+
metadata?: ReadonlyMap<string, string>
|
|
122
116
|
): Promise<void> {
|
|
123
|
-
throwIfAborted(signal);
|
|
124
117
|
const localSpotRouteNode = this.targets.localRouteNode(spotRouteTarget);
|
|
125
118
|
if (localSpotRouteNode !== undefined) {
|
|
126
119
|
await this.localTransport.send(
|
|
@@ -128,9 +121,7 @@ export class ZLinkSpotRouteDispatchStrategy {
|
|
|
128
121
|
spotRouteTarget.spotId,
|
|
129
122
|
packetName,
|
|
130
123
|
message,
|
|
131
|
-
|
|
132
|
-
metadata,
|
|
133
|
-
timeoutMs
|
|
124
|
+
metadata
|
|
134
125
|
);
|
|
135
126
|
return;
|
|
136
127
|
}
|
|
@@ -148,22 +139,22 @@ export class ZLinkSpotRouteDispatchStrategy {
|
|
|
148
139
|
) as readonly Message[];
|
|
149
140
|
if (
|
|
150
141
|
this.targets.hasNamedSpotNode(spotRouteTarget.routerChannelId) &&
|
|
151
|
-
(await this.spotNodeTransport.send(spotRouteTarget, parts
|
|
142
|
+
(await this.spotNodeTransport.send(spotRouteTarget, parts))
|
|
152
143
|
) {
|
|
153
144
|
return;
|
|
154
145
|
}
|
|
155
146
|
if (this.bridgeTransport.has(spotRouteTarget.routerChannelId)) {
|
|
156
|
-
await this.bridgeTransport.send(spotRouteTarget, parts
|
|
147
|
+
await this.bridgeTransport.send(spotRouteTarget, parts);
|
|
157
148
|
return;
|
|
158
149
|
}
|
|
159
150
|
if (this.targets.hasBoundRouteRouter(spotRouteTarget.routerChannelId)) {
|
|
160
|
-
await this.routerSocketTransport.send(spotRouteTarget, parts, ZLINK_SEND_DONT_WAIT
|
|
151
|
+
await this.routerSocketTransport.send(spotRouteTarget, parts, ZLINK_SEND_DONT_WAIT);
|
|
161
152
|
return;
|
|
162
153
|
}
|
|
163
|
-
if (await this.spotNodeTransport.send(spotRouteTarget, parts
|
|
154
|
+
if (await this.spotNodeTransport.send(spotRouteTarget, parts)) {
|
|
164
155
|
return;
|
|
165
156
|
}
|
|
166
|
-
await this.routerSocketTransport.send(spotRouteTarget, parts, ZLINK_SEND_DONT_WAIT
|
|
157
|
+
await this.routerSocketTransport.send(spotRouteTarget, parts, ZLINK_SEND_DONT_WAIT);
|
|
167
158
|
}
|
|
168
159
|
|
|
169
160
|
routeRequestToSpot<TReply>(
|
|
@@ -291,19 +282,9 @@ export class ZLinkSpotRouteDispatchStrategy {
|
|
|
291
282
|
spotRouteTarget: ZLinkSpotRouteTarget,
|
|
292
283
|
packetName: string | undefined,
|
|
293
284
|
message: unknown,
|
|
294
|
-
|
|
295
|
-
metadata?: ReadonlyMap<string, string>,
|
|
296
|
-
timeoutMs?: number
|
|
285
|
+
metadata?: ReadonlyMap<string, string>
|
|
297
286
|
): Promise<void> {
|
|
298
|
-
await this.sourceSpotRouter.send(
|
|
299
|
-
sourceSpot,
|
|
300
|
-
spotRouteTarget,
|
|
301
|
-
packetName,
|
|
302
|
-
message,
|
|
303
|
-
signal,
|
|
304
|
-
metadata,
|
|
305
|
-
timeoutMs
|
|
306
|
-
);
|
|
287
|
+
await this.sourceSpotRouter.send(sourceSpot, spotRouteTarget, packetName, message, metadata);
|
|
307
288
|
}
|
|
308
289
|
|
|
309
290
|
routeRequestRawFromSpotToSpot(
|
|
@@ -52,6 +52,7 @@ export interface ZLinkSerialWorkPreparation {
|
|
|
52
52
|
type SerialWorkRecord<T> = Omit<ZLinkSerialWorkRecord<T>, 'acceptedSequence'> & {
|
|
53
53
|
acceptedSequence: bigint;
|
|
54
54
|
readonly operationEnvelope: boolean;
|
|
55
|
+
readonly mailbox?: ZLinkSerialExecutionQueue;
|
|
55
56
|
readonly preparation?: ZLinkSerialWorkPreparation;
|
|
56
57
|
preparationState: 'idle' | 'pending' | 'canceling' | 'ready' | 'failed';
|
|
57
58
|
preparationController?: AbortController;
|
|
@@ -70,11 +71,13 @@ export class ZLinkSerialExecutionQueue {
|
|
|
70
71
|
private readonly application: ZLinkSerialAdmissionLane;
|
|
71
72
|
private readonly lifecycle: ZLinkSerialAdmissionLane;
|
|
72
73
|
private activeLifecycle?: ZLinkSerialWorkRecord<unknown>;
|
|
74
|
+
private activeRecord?: ZLinkSerialWorkRecord<unknown>;
|
|
75
|
+
/** The admitted node itself owns ready membership; no second readiness flag. */
|
|
76
|
+
private ownerMembership?: SerialWorkRecord<unknown>;
|
|
73
77
|
private readonly ownerTimeBudget: number;
|
|
74
78
|
private readonly lifecycleBurstLimit: number;
|
|
75
79
|
private nextAcceptedSequence = 1n;
|
|
76
|
-
private
|
|
77
|
-
private drainScheduled = false;
|
|
80
|
+
private readonly consumer?: { running: boolean; scheduled: boolean };
|
|
78
81
|
private lifecycleStreak = 0;
|
|
79
82
|
private lifecycleDebt = false;
|
|
80
83
|
private claimStartedAt?: number;
|
|
@@ -83,7 +86,8 @@ export class ZLinkSerialExecutionQueue {
|
|
|
83
86
|
|
|
84
87
|
constructor(
|
|
85
88
|
private readonly executeRecord: (record: ZLinkSerialWorkRecord<unknown>) => Promise<void>,
|
|
86
|
-
options: ZLinkSerialSchedulerOptions = {}
|
|
89
|
+
options: ZLinkSerialSchedulerOptions = {},
|
|
90
|
+
private readonly sharedOwner?: ZLinkSerialExecutionQueue
|
|
87
91
|
) {
|
|
88
92
|
const configured = { ...ZLINK_DEFAULT_SERIAL_SCHEDULER_OPTIONS, ...options };
|
|
89
93
|
validateNonNegative(configured.ownerTimeBudget, 'ownerTimeBudget');
|
|
@@ -92,6 +96,7 @@ export class ZLinkSerialExecutionQueue {
|
|
|
92
96
|
this.lifecycle = createLane();
|
|
93
97
|
this.ownerTimeBudget = configured.ownerTimeBudget;
|
|
94
98
|
this.lifecycleBurstLimit = configured.lifecycleBurstLimit;
|
|
99
|
+
if (sharedOwner === undefined) this.consumer = { running: false, scheduled: false };
|
|
95
100
|
}
|
|
96
101
|
|
|
97
102
|
submit<T>(
|
|
@@ -162,12 +167,15 @@ export class ZLinkSerialExecutionQueue {
|
|
|
162
167
|
}
|
|
163
168
|
|
|
164
169
|
visitPendingApplication(visitor: (record: ZLinkSerialWorkRecord<unknown>) => void): void {
|
|
165
|
-
for (const record of this.application.records)
|
|
170
|
+
for (const record of this.application.records) {
|
|
171
|
+
if (record.mailbox === undefined) visitor(record);
|
|
172
|
+
}
|
|
166
173
|
}
|
|
167
174
|
|
|
168
175
|
get hasPendingWork(): boolean {
|
|
169
176
|
return (
|
|
170
|
-
this.
|
|
177
|
+
this.consumer?.running === true ||
|
|
178
|
+
this.activeRecord !== undefined ||
|
|
171
179
|
this.activeLifecycle !== undefined ||
|
|
172
180
|
this.application.records.length > 0 ||
|
|
173
181
|
this.lifecycle.records.length > 0
|
|
@@ -219,7 +227,7 @@ export class ZLinkSerialExecutionQueue {
|
|
|
219
227
|
this.activeLifecycle = undefined;
|
|
220
228
|
if (this.application.records.length > 0 || this.lifecycle.records.length > 0) {
|
|
221
229
|
this.scheduleDrain();
|
|
222
|
-
} else if (
|
|
230
|
+
} else if (this.consumer?.running !== true) {
|
|
223
231
|
this.resolveIdleWaiters();
|
|
224
232
|
}
|
|
225
233
|
}
|
|
@@ -266,39 +274,95 @@ export class ZLinkSerialExecutionQueue {
|
|
|
266
274
|
}
|
|
267
275
|
|
|
268
276
|
private scheduleDrain(): void {
|
|
269
|
-
if (this.
|
|
270
|
-
|
|
277
|
+
if (this.sharedOwner !== undefined) {
|
|
278
|
+
if (this.activeRecord !== undefined) return;
|
|
279
|
+
// Ready membership in the existing owner queue is the only notification state.
|
|
280
|
+
if (this.ownerMembership !== undefined) return;
|
|
281
|
+
this.enqueueMailboxTurn(() => this.consumeMailbox());
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
const consumer = this.consumer!;
|
|
285
|
+
if (consumer.scheduled || consumer.running) return;
|
|
286
|
+
consumer.scheduled = true;
|
|
271
287
|
queueMicrotask(() => {
|
|
272
|
-
|
|
288
|
+
consumer.scheduled = false;
|
|
273
289
|
void this.drain();
|
|
274
290
|
});
|
|
275
291
|
}
|
|
276
292
|
|
|
293
|
+
/** Notifications carry no payload, admission sequence, result Promise, or host permit. */
|
|
294
|
+
private enqueueMailboxTurn(operation: () => void): void {
|
|
295
|
+
const entry: SerialWorkRecord<unknown> = {
|
|
296
|
+
acceptedSequence: 0n,
|
|
297
|
+
lane: 'application',
|
|
298
|
+
mailbox: this,
|
|
299
|
+
operation,
|
|
300
|
+
operationEnvelope: false,
|
|
301
|
+
preparationState: 'ready',
|
|
302
|
+
resolve: unexpectedMailboxSettlement,
|
|
303
|
+
reject: unexpectedMailboxSettlement,
|
|
304
|
+
release: unexpectedMailboxSettlement,
|
|
305
|
+
fail: unexpectedMailboxSettlement
|
|
306
|
+
};
|
|
307
|
+
const owner = this.sharedOwner!;
|
|
308
|
+
owner.application.records.push(entry);
|
|
309
|
+
this.ownerMembership = entry;
|
|
310
|
+
owner.scheduleDrain();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** Called only by the shared scheduler's consumer, never by a producer. */
|
|
314
|
+
private consumeMailbox(): void {
|
|
315
|
+
const selection = this.selectNext();
|
|
316
|
+
if (selection === undefined) {
|
|
317
|
+
if (!this.hasPendingWork) this.resolveIdleWaiters();
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const record = selection.record;
|
|
321
|
+
if (!this.prepareSelected(selection)) return;
|
|
322
|
+
this.takeSelected(selection);
|
|
323
|
+
if (record.preparationState === 'failed') {
|
|
324
|
+
record.preparation?.cancel();
|
|
325
|
+
record.fail(record.preparationError);
|
|
326
|
+
this.scheduleDrain();
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
this.activeRecord = record;
|
|
330
|
+
// The operation can submit Spot turns, including Yield continuations. Its
|
|
331
|
+
// Actor claim remains held until terminal settlement returns to this gate.
|
|
332
|
+
let terminal: Promise<unknown>;
|
|
333
|
+
try {
|
|
334
|
+
terminal = Promise.resolve(record.operation());
|
|
335
|
+
} catch (error) {
|
|
336
|
+
terminal = Promise.reject(error);
|
|
337
|
+
}
|
|
338
|
+
void terminal.then(
|
|
339
|
+
(value) => this.finishMailbox(record, () => record.resolve(value)),
|
|
340
|
+
(error) => this.finishMailbox(record, () => record.reject(error))
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
private finishMailbox(record: SerialWorkRecord<unknown>, settle: () => void): void {
|
|
345
|
+
this.enqueueMailboxTurn(() => {
|
|
346
|
+
settle();
|
|
347
|
+
record.release();
|
|
348
|
+
record.preparation?.cancel();
|
|
349
|
+
this.activeRecord = undefined;
|
|
350
|
+
if (this.selectNext() !== undefined) this.scheduleDrain();
|
|
351
|
+
else if (!this.hasPendingWork) this.resolveIdleWaiters();
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
277
355
|
private async drain(): Promise<void> {
|
|
278
|
-
|
|
279
|
-
|
|
356
|
+
const consumer = this.consumer!;
|
|
357
|
+
if (consumer.running) return;
|
|
358
|
+
consumer.running = true;
|
|
280
359
|
let waitingForPreparation = false;
|
|
281
360
|
try {
|
|
282
361
|
for (;;) {
|
|
283
362
|
const selection = this.selectNext();
|
|
284
363
|
if (selection === undefined) break;
|
|
285
364
|
const record = selection.record;
|
|
286
|
-
if (selection
|
|
287
|
-
const applicationHead = this.application.records[0]!;
|
|
288
|
-
if (
|
|
289
|
-
this.application.records.length > 0 &&
|
|
290
|
-
!this.cancelPreparationForRearbitration(applicationHead)
|
|
291
|
-
) {
|
|
292
|
-
waitingForPreparation = true;
|
|
293
|
-
break;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
if (record.preparationState === 'idle') {
|
|
297
|
-
this.startPreparation(record);
|
|
298
|
-
waitingForPreparation = true;
|
|
299
|
-
break;
|
|
300
|
-
}
|
|
301
|
-
if (record.preparationState === 'pending' || record.preparationState === 'canceling') {
|
|
365
|
+
if (!this.prepareSelected(selection)) {
|
|
302
366
|
waitingForPreparation = true;
|
|
303
367
|
break;
|
|
304
368
|
}
|
|
@@ -310,7 +374,9 @@ export class ZLinkSerialExecutionQueue {
|
|
|
310
374
|
}
|
|
311
375
|
this.claimStartedAt ??= performance.now();
|
|
312
376
|
try {
|
|
313
|
-
if (record.
|
|
377
|
+
if (record.mailbox !== undefined) {
|
|
378
|
+
record.operation();
|
|
379
|
+
} else if (record.operationEnvelope) {
|
|
314
380
|
try {
|
|
315
381
|
const outcome = record.operation();
|
|
316
382
|
void Promise.resolve(outcome).then(
|
|
@@ -330,6 +396,7 @@ export class ZLinkSerialExecutionQueue {
|
|
|
330
396
|
await this.executeRecord(record);
|
|
331
397
|
}
|
|
332
398
|
} catch (error) {
|
|
399
|
+
if (record.mailbox !== undefined) throw error;
|
|
333
400
|
record.fail(error);
|
|
334
401
|
} finally {
|
|
335
402
|
record.preparation?.cancel();
|
|
@@ -340,7 +407,7 @@ export class ZLinkSerialExecutionQueue {
|
|
|
340
407
|
}
|
|
341
408
|
}
|
|
342
409
|
} finally {
|
|
343
|
-
|
|
410
|
+
consumer.running = false;
|
|
344
411
|
this.claimStartedAt = undefined;
|
|
345
412
|
if (!waitingForPreparation && this.selectNext() !== undefined) {
|
|
346
413
|
this.scheduleDrain();
|
|
@@ -352,6 +419,21 @@ export class ZLinkSerialExecutionQueue {
|
|
|
352
419
|
}
|
|
353
420
|
}
|
|
354
421
|
|
|
422
|
+
private prepareSelected(selection: {
|
|
423
|
+
readonly lane: ZLinkSerialWorkLane;
|
|
424
|
+
readonly record: SerialWorkRecord<unknown>;
|
|
425
|
+
}): boolean {
|
|
426
|
+
if (
|
|
427
|
+
selection.lane === 'lifecycle' &&
|
|
428
|
+
this.application.records.length > 0 &&
|
|
429
|
+
!this.cancelPreparationForRearbitration(this.application.records[0]!)
|
|
430
|
+
)
|
|
431
|
+
return false;
|
|
432
|
+
const record = selection.record;
|
|
433
|
+
if (record.preparationState === 'idle') this.startPreparation(record);
|
|
434
|
+
return record.preparationState !== 'pending' && record.preparationState !== 'canceling';
|
|
435
|
+
}
|
|
436
|
+
|
|
355
437
|
private selectNext():
|
|
356
438
|
| {
|
|
357
439
|
readonly lane: ZLinkSerialWorkLane;
|
|
@@ -389,6 +471,8 @@ export class ZLinkSerialExecutionQueue {
|
|
|
389
471
|
if (record !== selection.record) {
|
|
390
472
|
throw new Error('The selected serial work record changed before owner claim.');
|
|
391
473
|
}
|
|
474
|
+
if (selection.record.mailbox !== undefined)
|
|
475
|
+
selection.record.mailbox.ownerMembership = undefined;
|
|
392
476
|
return record;
|
|
393
477
|
}
|
|
394
478
|
|
|
@@ -565,6 +649,10 @@ function createLane(): ZLinkSerialAdmissionLane {
|
|
|
565
649
|
};
|
|
566
650
|
}
|
|
567
651
|
|
|
652
|
+
function unexpectedMailboxSettlement(): never {
|
|
653
|
+
throw new Error('A mailbox notification has no independent terminal result.');
|
|
654
|
+
}
|
|
655
|
+
|
|
568
656
|
function validatePositive(value: number, field: string): void {
|
|
569
657
|
if (!Number.isSafeInteger(value) || value < 1) {
|
|
570
658
|
throw new RangeError(`${field} must be a positive safe integer.`);
|
|
@@ -24,7 +24,8 @@ export interface PendingOperation<T> {
|
|
|
24
24
|
readonly promise: Promise<T>;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
interface Entry<T> {
|
|
27
|
+
interface Entry<T, Context> {
|
|
28
|
+
readonly context: Context | undefined;
|
|
28
29
|
readonly deadlineMs?: number;
|
|
29
30
|
readonly resolve: (value: T | PromiseLike<T>) => void;
|
|
30
31
|
readonly reject: (reason: unknown) => void;
|
|
@@ -35,10 +36,10 @@ const systemClock: OperationClock = {
|
|
|
35
36
|
};
|
|
36
37
|
|
|
37
38
|
/** Owns request completion so reply, timeout, cancellation, and shutdown race safely. */
|
|
38
|
-
export class OperationRegistry<T> {
|
|
39
|
-
private readonly entries = new Map<bigint, Entry<T>>();
|
|
39
|
+
export class OperationRegistry<T, Context = unknown> {
|
|
40
|
+
private readonly entries = new Map<bigint, Entry<T, Context>>();
|
|
40
41
|
private nextId = 1n;
|
|
41
|
-
private deadlineCursor?: MapIterator<[bigint, Entry<T>]>;
|
|
42
|
+
private deadlineCursor?: MapIterator<[bigint, Entry<T, Context>]>;
|
|
42
43
|
private closed = false;
|
|
43
44
|
|
|
44
45
|
constructor(
|
|
@@ -48,20 +49,22 @@ export class OperationRegistry<T> {
|
|
|
48
49
|
|
|
49
50
|
register(
|
|
50
51
|
timeoutMs: number,
|
|
51
|
-
timeoutOwner: 'registry' | 'sender' = 'registry'
|
|
52
|
+
timeoutOwner: 'registry' | 'sender' = 'registry',
|
|
53
|
+
context?: Context
|
|
52
54
|
): PendingOperation<T> {
|
|
53
55
|
if (this.closed) throw new Error('Operation registry is closed.');
|
|
54
56
|
if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
|
|
55
57
|
throw new RangeError('timeoutMs must be a non-negative finite number.');
|
|
56
58
|
}
|
|
57
59
|
const id = this.nextId++;
|
|
58
|
-
let resolve!: Entry<T>['resolve'];
|
|
59
|
-
let reject!: Entry<T>['reject'];
|
|
60
|
+
let resolve!: Entry<T, Context>['resolve'];
|
|
61
|
+
let reject!: Entry<T, Context>['reject'];
|
|
60
62
|
const promise = new Promise<T>((onResolve, onReject) => {
|
|
61
63
|
resolve = onResolve;
|
|
62
64
|
reject = onReject;
|
|
63
65
|
});
|
|
64
|
-
const entry: Entry<T> = {
|
|
66
|
+
const entry: Entry<T, Context> = {
|
|
67
|
+
context,
|
|
65
68
|
deadlineMs: timeoutOwner === 'registry' ? this.clock.now() + timeoutMs : undefined,
|
|
66
69
|
resolve,
|
|
67
70
|
reject
|
|
@@ -93,6 +96,10 @@ export class OperationRegistry<T> {
|
|
|
93
96
|
return this.entries.has(id);
|
|
94
97
|
}
|
|
95
98
|
|
|
99
|
+
context(id: bigint): Context | undefined {
|
|
100
|
+
return this.entries.get(id)?.context;
|
|
101
|
+
}
|
|
102
|
+
|
|
96
103
|
close(reason = 'Operation registry closed.'): void {
|
|
97
104
|
if (this.closed) return;
|
|
98
105
|
this.closed = true;
|
|
@@ -121,7 +128,7 @@ export class OperationRegistry<T> {
|
|
|
121
128
|
return expired;
|
|
122
129
|
}
|
|
123
130
|
|
|
124
|
-
private take(id: bigint): Entry<T> | undefined {
|
|
131
|
+
private take(id: bigint): Entry<T, Context> | undefined {
|
|
125
132
|
const entry = this.entries.get(id);
|
|
126
133
|
if (entry === undefined) return undefined;
|
|
127
134
|
this.entries.delete(id);
|
|
@@ -10,7 +10,8 @@ import type {
|
|
|
10
10
|
ZLinkRawReceivedRecord,
|
|
11
11
|
ZLinkRawRouterPort
|
|
12
12
|
} from '../backend/raw-binding-port';
|
|
13
|
-
import {
|
|
13
|
+
import { submitToRequestResult } from '../messaging/submission-result';
|
|
14
|
+
import { RequestResult, SubmitResult, isZLinkBackendResultError } from '../backend/runtime-values';
|
|
14
15
|
import { enumWireRejectReason } from '../protocol/service_wire_codec.generated';
|
|
15
16
|
import { OperationRegistry, type PendingOperation } from './operation-registry';
|
|
16
17
|
import { ServiceLivenessRegistry, type ServiceLivenessTick } from './service-liveness-registry';
|
|
@@ -830,17 +831,14 @@ export class RawServiceMeshRuntime {
|
|
|
830
831
|
peer.connectionId,
|
|
831
832
|
record.probeId
|
|
832
833
|
);
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
if (!sent) {
|
|
842
|
-
return 'protocolError';
|
|
843
|
-
}
|
|
834
|
+
if (ack === undefined) return 'protocolError';
|
|
835
|
+
const sent = await this.send(received.sourceRid, [
|
|
836
|
+
livenessCodec.encodeLivenessRecord({
|
|
837
|
+
command: M6aServiceWireCommand.livenessAck,
|
|
838
|
+
probeId: record.probeId
|
|
839
|
+
})
|
|
840
|
+
]);
|
|
841
|
+
if (!sent) return 'dropped';
|
|
844
842
|
} else {
|
|
845
843
|
this.liveness.acknowledge(received.sourceRid, peer.connectionId, record.probeId, nowMs);
|
|
846
844
|
}
|
|
@@ -1263,8 +1261,13 @@ export class RawServiceMeshRuntime {
|
|
|
1263
1261
|
try {
|
|
1264
1262
|
await this.requireStarted().send(targetNodeRoutingId, parts);
|
|
1265
1263
|
return true;
|
|
1266
|
-
} catch {
|
|
1267
|
-
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
if (isZLinkBackendResultError(error) && error.operation === 'submit') {
|
|
1266
|
+
const terminal = submitToRequestResult(error.result, error.phase);
|
|
1267
|
+
if (terminal === RequestResult.NotConnected || terminal === RequestResult.NotFound)
|
|
1268
|
+
return false;
|
|
1269
|
+
}
|
|
1270
|
+
throw error;
|
|
1268
1271
|
}
|
|
1269
1272
|
}
|
|
1270
1273
|
}
|
|
@@ -62,52 +62,6 @@ export interface ServiceUserSpotAuthorityPayload {
|
|
|
62
62
|
readonly ownerNodeGeneration: bigint;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
/** Replaces only authority-relocation-state and preserves the canonical owner payload. */
|
|
66
|
-
export function replaceServiceAuthorityRelocationState(
|
|
67
|
-
payload: Uint8Array,
|
|
68
|
-
relocationState: Uint8Array
|
|
69
|
-
): Buffer {
|
|
70
|
-
const bytes = Buffer.from(payload);
|
|
71
|
-
const reader = new AuthorityReader(bytes);
|
|
72
|
-
reader.expect(AUTHORITY_MAGIC);
|
|
73
|
-
if (reader.u8() !== AUTHORITY_FORMAT_VERSION || reader.u16() !== AUTHORITY_FLAGS) {
|
|
74
|
-
throw new TypeError('Authority payload header is invalid.');
|
|
75
|
-
}
|
|
76
|
-
const bodyLength = reader.u32();
|
|
77
|
-
const body = reader.takeReader(bodyLength);
|
|
78
|
-
const checksumOffset = reader.offset;
|
|
79
|
-
const checksum = reader.u32();
|
|
80
|
-
if (!reader.done || crc32c(bytes.subarray(0, checksumOffset)) !== checksum) {
|
|
81
|
-
throw new TypeError('Authority payload checksum is invalid.');
|
|
82
|
-
}
|
|
83
|
-
body.u8();
|
|
84
|
-
body.u8();
|
|
85
|
-
body.skip(body.u16());
|
|
86
|
-
body.text8();
|
|
87
|
-
body.nonZeroU64();
|
|
88
|
-
body.text8();
|
|
89
|
-
body.rid();
|
|
90
|
-
body.nonZeroU64();
|
|
91
|
-
const relocationOffset = body.offset;
|
|
92
|
-
body.bool8();
|
|
93
|
-
body.skip(body.u32());
|
|
94
|
-
const relocationEnd = body.offset;
|
|
95
|
-
const currentBody = bytes.subarray(11, 11 + bodyLength);
|
|
96
|
-
const nextBody = Buffer.concat([
|
|
97
|
-
currentBody.subarray(0, relocationOffset),
|
|
98
|
-
Buffer.from(relocationState),
|
|
99
|
-
currentBody.subarray(relocationEnd)
|
|
100
|
-
]);
|
|
101
|
-
const envelope = Buffer.concat([
|
|
102
|
-
AUTHORITY_MAGIC,
|
|
103
|
-
Buffer.of(AUTHORITY_FORMAT_VERSION),
|
|
104
|
-
u16(AUTHORITY_FLAGS),
|
|
105
|
-
u32(nextBody.byteLength),
|
|
106
|
-
nextBody
|
|
107
|
-
]);
|
|
108
|
-
return Buffer.concat([envelope, u32(crc32c(envelope))]);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
65
|
export function encodeServiceUserSpotAuthorityPayload(
|
|
112
66
|
value: ServiceUserSpotAuthorityPayload
|
|
113
67
|
): Buffer {
|
|
@@ -621,18 +621,19 @@ function createSpotState(
|
|
|
621
621
|
}
|
|
622
622
|
}
|
|
623
623
|
|
|
624
|
-
export class ServiceTerminalOperationRegistry<T> {
|
|
625
|
-
private readonly operations: OperationRegistry<T>;
|
|
624
|
+
export class ServiceTerminalOperationRegistry<T, Context = unknown> {
|
|
625
|
+
private readonly operations: OperationRegistry<T, Context>;
|
|
626
626
|
|
|
627
|
-
constructor(operations = new OperationRegistry<T>()) {
|
|
627
|
+
constructor(operations = new OperationRegistry<T, Context>()) {
|
|
628
628
|
this.operations = operations;
|
|
629
629
|
}
|
|
630
630
|
|
|
631
631
|
register(
|
|
632
632
|
timeoutMs: number,
|
|
633
|
-
timeoutOwner: 'registry' | 'sender' = 'registry'
|
|
633
|
+
timeoutOwner: 'registry' | 'sender' = 'registry',
|
|
634
|
+
context?: Context
|
|
634
635
|
): PendingOperation<T> {
|
|
635
|
-
return this.operations.register(timeoutMs, timeoutOwner);
|
|
636
|
+
return this.operations.register(timeoutMs, timeoutOwner, context);
|
|
636
637
|
}
|
|
637
638
|
|
|
638
639
|
reply(id: bigint, value: T): boolean {
|
|
@@ -651,6 +652,10 @@ export class ServiceTerminalOperationRegistry<T> {
|
|
|
651
652
|
return this.operations.isPending(id);
|
|
652
653
|
}
|
|
653
654
|
|
|
655
|
+
context(id: bigint): Context | undefined {
|
|
656
|
+
return this.operations.context(id);
|
|
657
|
+
}
|
|
658
|
+
|
|
654
659
|
get size(): number {
|
|
655
660
|
return this.operations.size;
|
|
656
661
|
}
|