@zlink-systems/framework 0.10.0 → 0.12.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/Configuration/EndpointNotation.d.ts +5 -4
- package/dist/contracts/Configuration/EndpointNotation.js +5 -4
- package/dist/contracts/Configuration/InternalDefaults.d.ts +0 -1
- package/dist/contracts/Configuration/InternalDefaults.js +1 -2
- package/dist/contracts/Configuration/RegistrationNormalizers.js +1 -2
- package/dist/contracts/Configuration/RegistrationTypes.d.ts +2 -5
- package/dist/contracts/Configuration/RegistrationValidators.js +0 -1
- package/dist/contracts/Errors/ZLinkFrameworkException.d.ts +6 -7
- package/dist/contracts/Errors/ZLinkFrameworkException.js +6 -7
- package/dist/runtime/actors/actor-client.js +1 -1
- package/dist/runtime/actors/actor-mailbox.js +1 -5
- package/dist/runtime/backend/contracts/index.d.ts +2 -0
- package/dist/runtime/backend/mesh-completion-table.d.ts +1 -3
- package/dist/runtime/backend/mesh-completion-table.js +11 -15
- package/dist/runtime/backend/mesh-dispatch-pump.d.ts +11 -4
- package/dist/runtime/backend/mesh-dispatch-pump.js +201 -134
- package/dist/runtime/backend/node/node-backend-adapter-factory.d.ts +3 -4
- package/dist/runtime/backend/node/node-backend-adapter-factory.js +51 -37
- package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +2 -2
- package/dist/runtime/backend/node/node-backend-adapter-support.js +2 -2
- package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +2 -2
- package/dist/runtime/backend/node/node-event-loop-poller.d.ts +16 -0
- package/dist/runtime/backend/node/node-event-loop-poller.js +49 -0
- package/dist/runtime/backend/node/node-raw-binding-port.js +24 -41
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +8 -8
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +100 -107
- package/dist/runtime/backend/node/node-socket-backend-adapter.d.ts +3 -2
- package/dist/runtime/backend/node/node-socket-backend-adapter.js +18 -67
- package/dist/runtime/backend/raw-binding-port.d.ts +1 -0
- package/dist/runtime/backend/runtime-values.d.ts +6 -1
- package/dist/runtime/backend/runtime-values.js +6 -0
- package/dist/runtime/channels/channel-dispatchers.js +2 -2
- package/dist/runtime/channels/channel-envelope.js +324 -121
- package/dist/runtime/channels/channel-multipart.d.ts +8 -1
- package/dist/runtime/channels/channel-outbound-operations.js +0 -2
- package/dist/runtime/channels/channel-receive-loops.d.ts +1 -3
- package/dist/runtime/channels/channel-receive-loops.js +25 -65
- package/dist/runtime/channels/channel-socket-registry.js +14 -1
- package/dist/runtime/channels/channel-transports.d.ts +2 -1
- package/dist/runtime/channels/channel-transports.js +12 -10
- package/dist/runtime/channels/client-server-location-runtime.js +15 -3
- package/dist/runtime/execution/serial-execution-queue.d.ts +5 -18
- package/dist/runtime/execution/serial-execution-queue.js +16 -80
- package/dist/runtime/foundation/event-loop-resources.d.ts +0 -3
- package/dist/runtime/foundation/event-loop-resources.js +2 -11
- package/dist/runtime/foundation/operation-registry.d.ts +2 -8
- package/dist/runtime/foundation/operation-registry.js +3 -21
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +19 -7
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +56 -10
- package/dist/runtime/foundation/service-mailbox.d.ts +1 -7
- package/dist/runtime/foundation/service-mailbox.js +3 -2
- package/dist/runtime/foundation/service-runtime-contracts.d.ts +2 -1
- package/dist/runtime/foundation/service-stateful-registry.d.ts +1 -1
- package/dist/runtime/foundation/service-stateful-registry.js +2 -2
- package/dist/runtime/foundation/service-stateful-runtime.js +14 -14
- package/dist/runtime/framework-errors-internal.d.ts +4 -4
- package/dist/runtime/framework-errors-internal.js +8 -10
- package/dist/runtime/host/actor-placement-coordinator.js +1 -1
- package/dist/runtime/host/index.js +1 -1
- package/dist/runtime/host/service-relocation-host-runtime.js +0 -4
- package/dist/runtime/host/spot-address-transport.js +1 -1
- package/dist/runtime/host/user-spot-creation-coordinator.js +1 -1
- package/dist/runtime/locations/location-store-repository.d.ts +1 -0
- package/dist/runtime/locations/location-store-repository.js +64 -75
- package/dist/runtime/spots/spot-node-runtime-manager.d.ts +0 -12
- package/dist/runtime/spots/spot-node-runtime-manager.js +6 -166
- package/dist/runtime/spots/spot-serial-turn-executor.js +1 -4
- package/dist/runtime/spots/spot-timer.d.ts +3 -0
- package/dist/runtime/spots/spot-timer.js +87 -18
- package/dist/runtime/streams/session-serial-executor.js +1 -3
- package/dist/runtime/streams/stream-session-runtime.d.ts +2 -2
- package/dist/runtime/streams/stream-session-runtime.js +13 -19
- package/dist/runtime/workers/index.d.ts +0 -1
- package/dist/runtime/workers/index.js +1 -9
- package/package.json +9 -3
- package/src/contracts/Configuration/EndpointNotation.ts +5 -4
- package/src/contracts/Configuration/InternalDefaults.ts +0 -1
- package/src/contracts/Configuration/RegistrationNormalizers.ts +2 -4
- package/src/contracts/Configuration/RegistrationTypes.ts +2 -5
- package/src/contracts/Configuration/RegistrationValidators.ts +0 -1
- package/src/contracts/Errors/ZLinkFrameworkException.ts +6 -7
- package/src/runtime/actors/actor-client.ts +1 -1
- package/src/runtime/actors/actor-mailbox.ts +1 -12
- package/src/runtime/backend/contracts/index.ts +2 -0
- package/src/runtime/backend/mesh-completion-table.ts +12 -23
- package/src/runtime/backend/mesh-dispatch-pump.ts +206 -138
- package/src/runtime/backend/node/node-backend-adapter-factory.ts +66 -35
- package/src/runtime/backend/node/node-backend-adapter-support.ts +4 -4
- package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +2 -2
- package/src/runtime/backend/node/node-event-loop-poller.ts +55 -0
- package/src/runtime/backend/node/node-raw-binding-port.ts +30 -45
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +97 -110
- package/src/runtime/backend/node/node-socket-backend-adapter.ts +23 -69
- package/src/runtime/backend/raw-binding-port.ts +1 -0
- package/src/runtime/backend/runtime-values.ts +13 -1
- package/src/runtime/channels/channel-dispatchers.ts +2 -2
- package/src/runtime/channels/channel-envelope.ts +379 -109
- package/src/runtime/channels/channel-multipart.ts +9 -1
- package/src/runtime/channels/channel-outbound-operations.ts +0 -1
- package/src/runtime/channels/channel-receive-loops.ts +24 -75
- package/src/runtime/channels/channel-socket-registry.ts +17 -1
- package/src/runtime/channels/channel-transports.ts +20 -10
- package/src/runtime/channels/client-server-location-runtime.ts +16 -2
- package/src/runtime/execution/serial-execution-queue.ts +16 -108
- package/src/runtime/foundation/event-loop-resources.ts +2 -11
- package/src/runtime/foundation/operation-registry.ts +2 -22
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +74 -19
- package/src/runtime/foundation/service-mailbox.ts +3 -13
- package/src/runtime/foundation/service-runtime-contracts.ts +2 -1
- package/src/runtime/foundation/service-stateful-registry.ts +2 -2
- package/src/runtime/foundation/service-stateful-runtime.ts +14 -14
- package/src/runtime/framework-errors-internal.ts +8 -10
- package/src/runtime/host/actor-placement-coordinator.ts +1 -1
- package/src/runtime/host/index.ts +2 -1
- package/src/runtime/host/service-relocation-host-runtime.ts +0 -4
- package/src/runtime/host/spot-address-transport.ts +1 -1
- package/src/runtime/host/user-spot-creation-coordinator.ts +1 -1
- package/src/runtime/locations/location-store-repository.ts +118 -80
- package/src/runtime/spots/spot-node-runtime-manager.ts +13 -233
- package/src/runtime/spots/spot-serial-turn-executor.ts +1 -8
- package/src/runtime/spots/spot-timer.ts +109 -18
- package/src/runtime/streams/session-serial-executor.ts +1 -7
- package/src/runtime/streams/stream-session-runtime.ts +13 -17
- package/src/runtime/workers/index.ts +2 -17
|
@@ -59,7 +59,9 @@ class ZLinkSpotTimerRegistry {
|
|
|
59
59
|
: undefined;
|
|
60
60
|
if (this.executionBarrier !== undefined)
|
|
61
61
|
executionSerial?.setExecutionBarrier(this.executionBarrier);
|
|
62
|
+
let callbackStarted = false;
|
|
62
63
|
const timer = startOutsideStateLane(() => new ZLinkManagedTimer(name, periodMs, normalizeTimerOptions(options), async (tick) => {
|
|
64
|
+
callbackStarted = false;
|
|
63
65
|
const timerFlow = (0, flow_context_1.createInboundFlow)(undefined, 'Timer', this.flowCreationEnabled());
|
|
64
66
|
const operation = () => {
|
|
65
67
|
const current = this.timers.get(name);
|
|
@@ -68,6 +70,7 @@ class ZLinkSpotTimerRegistry {
|
|
|
68
70
|
}
|
|
69
71
|
if (!this.executionAllowed())
|
|
70
72
|
return undefined;
|
|
73
|
+
callbackStarted = true;
|
|
71
74
|
return (0, flow_context_1.runWithFlow)(timerFlow, () => handler.handle(spot, tick));
|
|
72
75
|
};
|
|
73
76
|
if (this.executeTimer !== undefined) {
|
|
@@ -76,9 +79,7 @@ class ZLinkSpotTimerRegistry {
|
|
|
76
79
|
else {
|
|
77
80
|
await executionSerial.execute(operation);
|
|
78
81
|
}
|
|
79
|
-
}, reportFailure, () => this.
|
|
80
|
-
? !executionSerial.isExecuting
|
|
81
|
-
: this.isTimerExecuting?.(name) !== true, this.clock));
|
|
82
|
+
}, reportFailure, () => callbackStarted, this.clock));
|
|
82
83
|
const registered = await this.lane.run(() => this.completeAddCore(name, prepared.generation, handlerType.name, timer));
|
|
83
84
|
if (!registered)
|
|
84
85
|
await timer.cancel(signal);
|
|
@@ -86,13 +87,18 @@ class ZLinkSpotTimerRegistry {
|
|
|
86
87
|
}
|
|
87
88
|
async dispose() {
|
|
88
89
|
const timers = await this.lane.run(() => {
|
|
89
|
-
const active = [...this.timers.
|
|
90
|
+
const active = [...this.timers.entries()].map(([name, entry]) => ({
|
|
91
|
+
name,
|
|
92
|
+
timer: entry.timer,
|
|
93
|
+
wait: this.executeTimer === undefined
|
|
94
|
+
? true
|
|
95
|
+
: this.isTimerExecuting?.(name) !== true
|
|
96
|
+
}));
|
|
90
97
|
this.timers.clear();
|
|
91
98
|
return active;
|
|
92
99
|
});
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
100
|
+
const finalizations = timers.map(({ timer }) => timer.dispose());
|
|
101
|
+
await Promise.all(finalizations.filter((_, index) => timers[index].wait));
|
|
96
102
|
}
|
|
97
103
|
async captureRelocation() {
|
|
98
104
|
const timers = await this.lane.run(() => [...this.timers.entries()].sort(([left], [right]) => left.localeCompare(right)));
|
|
@@ -155,6 +161,7 @@ class ZLinkRegisteredTimer {
|
|
|
155
161
|
name;
|
|
156
162
|
generation;
|
|
157
163
|
timer;
|
|
164
|
+
finalization;
|
|
158
165
|
constructor(registry, name, generation, timer) {
|
|
159
166
|
this.registry = registry;
|
|
160
167
|
this.name = name;
|
|
@@ -165,7 +172,16 @@ class ZLinkRegisteredTimer {
|
|
|
165
172
|
return this.timer.isDisposed;
|
|
166
173
|
}
|
|
167
174
|
cancel(signal) {
|
|
168
|
-
|
|
175
|
+
if (this.finalization !== undefined)
|
|
176
|
+
return this.finalization;
|
|
177
|
+
try {
|
|
178
|
+
(0, abort_1.throwIfAborted)(signal);
|
|
179
|
+
}
|
|
180
|
+
catch (failure) {
|
|
181
|
+
return Promise.reject(failure);
|
|
182
|
+
}
|
|
183
|
+
this.finalization = this.registry.cancel(this.name, this.generation, this.timer);
|
|
184
|
+
return this.finalization;
|
|
169
185
|
}
|
|
170
186
|
dispose() {
|
|
171
187
|
return this.cancel();
|
|
@@ -188,6 +204,8 @@ class ZLinkManagedTimer {
|
|
|
188
204
|
lastScheduledIndex = 0n;
|
|
189
205
|
timeout;
|
|
190
206
|
running = Promise.resolve();
|
|
207
|
+
dispatchFailure;
|
|
208
|
+
finalization;
|
|
191
209
|
constructor(name, periodMs, options, onTick, onFailure, shouldWaitForRunningOnCancel = () => true, clock = systemTimerClock) {
|
|
192
210
|
this.name = name;
|
|
193
211
|
this.periodMs = periodMs;
|
|
@@ -203,9 +221,27 @@ class ZLinkManagedTimer {
|
|
|
203
221
|
get isDisposed() {
|
|
204
222
|
return this.disposed;
|
|
205
223
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
224
|
+
cancel(_signal) {
|
|
225
|
+
if (this.finalization !== undefined)
|
|
226
|
+
return this.finalization.promise;
|
|
227
|
+
let resolve;
|
|
228
|
+
let reject;
|
|
229
|
+
const promise = new Promise((complete, fail) => {
|
|
230
|
+
resolve = complete;
|
|
231
|
+
reject = fail;
|
|
232
|
+
});
|
|
233
|
+
const finalization = { promise, resolve, reject };
|
|
234
|
+
this.finalization = finalization;
|
|
235
|
+
try {
|
|
236
|
+
const preparation = this.lane.run(() => this.cancelCore());
|
|
237
|
+
void preparation.then(start => {
|
|
238
|
+
void this.completeFinalization(start, finalization).then(undefined, reject);
|
|
239
|
+
}, reject);
|
|
240
|
+
}
|
|
241
|
+
catch (failure) {
|
|
242
|
+
reject(failure);
|
|
243
|
+
}
|
|
244
|
+
return promise;
|
|
209
245
|
}
|
|
210
246
|
dispose() {
|
|
211
247
|
return this.cancel();
|
|
@@ -264,7 +300,10 @@ class ZLinkManagedTimer {
|
|
|
264
300
|
: Math.max(0, Number(this.lastScheduledIndex + 1n) * this.periodMs - this.elapsedMs());
|
|
265
301
|
this.timeout = this.clock.setTimeout(() => {
|
|
266
302
|
this.timeout = undefined;
|
|
267
|
-
|
|
303
|
+
const execution = startOutsideStateLane(() => this.fire());
|
|
304
|
+
this.running = execution.then(() => undefined, cause => this.lane.run(() => {
|
|
305
|
+
this.dispatchFailure ??= { cause };
|
|
306
|
+
}));
|
|
268
307
|
}, delayMs);
|
|
269
308
|
}
|
|
270
309
|
async fire() {
|
|
@@ -284,14 +323,44 @@ class ZLinkManagedTimer {
|
|
|
284
323
|
await this.lane.run(() => this.completeFireCore(prepared.scheduledIndex, shouldContinue));
|
|
285
324
|
}
|
|
286
325
|
cancelCore() {
|
|
287
|
-
if (this.disposed)
|
|
288
|
-
return { running: Promise.resolve() };
|
|
289
326
|
this.disposed = true;
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
327
|
+
const timeout = this.timeout;
|
|
328
|
+
this.timeout = undefined;
|
|
329
|
+
return {
|
|
330
|
+
timeout,
|
|
331
|
+
running: this.shouldWaitForRunningOnCancel()
|
|
332
|
+
? this.running
|
|
333
|
+
: Promise.resolve()
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
async completeFinalization(start, finalization) {
|
|
337
|
+
const failures = [];
|
|
338
|
+
if (start.timeout !== undefined) {
|
|
339
|
+
try {
|
|
340
|
+
this.clock.clearTimeout(start.timeout);
|
|
341
|
+
}
|
|
342
|
+
catch (failure) {
|
|
343
|
+
failures.push(failure);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
try {
|
|
347
|
+
await start.running;
|
|
348
|
+
}
|
|
349
|
+
catch (failure) {
|
|
350
|
+
failures.push(failure);
|
|
351
|
+
}
|
|
352
|
+
const dispatchFailure = await this.lane.run(() => this.dispatchFailure);
|
|
353
|
+
if (dispatchFailure !== undefined)
|
|
354
|
+
failures.push(dispatchFailure.cause);
|
|
355
|
+
if (failures.length === 0) {
|
|
356
|
+
finalization.resolve();
|
|
357
|
+
}
|
|
358
|
+
else if (failures.length === 1) {
|
|
359
|
+
finalization.reject(failures[0]);
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
finalization.reject(new AggregateError(failures, `Timer '${this.name}' cleanup failed.`));
|
|
293
363
|
}
|
|
294
|
-
return { running: this.shouldWaitForRunningOnCancel() ? this.running : Promise.resolve() };
|
|
295
364
|
}
|
|
296
365
|
prepareCaptureCore() {
|
|
297
366
|
if (this.disposed)
|
|
@@ -5,9 +5,7 @@ const serial_execution_queue_1 = require("../execution/serial-execution-queue");
|
|
|
5
5
|
const framework_errors_internal_1 = require("../framework-errors-internal");
|
|
6
6
|
const application_job_queue_scope_1 = require("../application-jobs/application-job-queue-scope");
|
|
7
7
|
class ZLinkSessionSerialExecutor {
|
|
8
|
-
scheduler = new serial_execution_queue_1.ZLinkSerialExecutionQueue(async (record) => this.execute(record), {
|
|
9
|
-
capacityError: (lane) => (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.WorkerQueueFull, `Stream Session ${lane} execution capacity is full.`, true)
|
|
10
|
-
});
|
|
8
|
+
scheduler = new serial_execution_queue_1.ZLinkSerialExecutionQueue(async (record) => this.execute(record), {});
|
|
11
9
|
closed = false;
|
|
12
10
|
executeApplication(work, options = {}, onRejected) {
|
|
13
11
|
return this.submit(work, 'application', options, onRejected);
|
|
@@ -131,7 +131,7 @@ export declare class ZLinkStreamSessionNodeRuntime {
|
|
|
131
131
|
private stopped;
|
|
132
132
|
private readonly receiveAbortController;
|
|
133
133
|
private receiveLoop;
|
|
134
|
-
private readonly
|
|
134
|
+
private readonly receiveWake;
|
|
135
135
|
private receiveWorkSinceYield;
|
|
136
136
|
constructor(options: ZLinkStreamSessionNodeRuntimeOptions & ZLinkStreamLivenessOptions);
|
|
137
137
|
start(): void;
|
|
@@ -159,7 +159,7 @@ export declare class ZLinkStreamSessionNodeRuntime {
|
|
|
159
159
|
private removePendingConnectionMetadata;
|
|
160
160
|
private resolveMonitorSession;
|
|
161
161
|
private enqueueEndpointlessDisconnect;
|
|
162
|
-
private
|
|
162
|
+
private waitReceiveLoopWake;
|
|
163
163
|
private wakeReceiveLoop;
|
|
164
164
|
private getOrCreateSession;
|
|
165
165
|
}
|
|
@@ -30,31 +30,21 @@ const systemLivenessClock = {
|
|
|
30
30
|
setTimer: (callback, delayMs) => setTimeout(callback, delayMs),
|
|
31
31
|
clearTimer: (timer) => clearTimeout(timer)
|
|
32
32
|
};
|
|
33
|
-
class
|
|
34
|
-
delayMs;
|
|
33
|
+
class ZLinkStreamReceiveWake {
|
|
35
34
|
pending;
|
|
36
35
|
resolvePending;
|
|
37
|
-
timer;
|
|
38
36
|
finish = () => {
|
|
39
|
-
if (this.timer !== undefined) {
|
|
40
|
-
clearTimeout(this.timer);
|
|
41
|
-
this.timer = undefined;
|
|
42
|
-
}
|
|
43
37
|
const resolve = this.resolvePending;
|
|
44
38
|
this.pending = undefined;
|
|
45
39
|
this.resolvePending = undefined;
|
|
46
40
|
resolve?.();
|
|
47
41
|
};
|
|
48
|
-
constructor(delayMs) {
|
|
49
|
-
this.delayMs = delayMs;
|
|
50
|
-
}
|
|
51
42
|
wait() {
|
|
52
43
|
if (this.pending !== undefined)
|
|
53
44
|
return this.pending;
|
|
54
45
|
this.pending = new Promise((resolve) => {
|
|
55
46
|
this.resolvePending = resolve;
|
|
56
47
|
});
|
|
57
|
-
this.timer = setTimeout(this.finish, this.delayMs);
|
|
58
48
|
return this.pending;
|
|
59
49
|
}
|
|
60
50
|
wake() {
|
|
@@ -664,7 +654,7 @@ class ZLinkStreamSessionNodeRuntime {
|
|
|
664
654
|
stopped = false;
|
|
665
655
|
receiveAbortController = new AbortController();
|
|
666
656
|
receiveLoop;
|
|
667
|
-
|
|
657
|
+
receiveWake = new ZLinkStreamReceiveWake();
|
|
668
658
|
receiveWorkSinceYield = 0;
|
|
669
659
|
constructor(options) {
|
|
670
660
|
this.options = options;
|
|
@@ -734,14 +724,15 @@ class ZLinkStreamSessionNodeRuntime {
|
|
|
734
724
|
}
|
|
735
725
|
if (!this.options.readablePoller.wait(0)) {
|
|
736
726
|
permit.releaseAfterInternalProcessing();
|
|
737
|
-
await this.
|
|
727
|
+
await this.waitReceiveLoopWake(signal);
|
|
738
728
|
continue;
|
|
739
729
|
}
|
|
740
730
|
packet = this.takePacket();
|
|
741
731
|
if (!this.options.socket.recvPacket(packet, ZLINK_RECV_DONT_WAIT)) {
|
|
732
|
+
this.options.readablePoller.markDrained();
|
|
742
733
|
permit.releaseAfterInternalProcessing();
|
|
743
734
|
this.recyclePacket(packet);
|
|
744
|
-
await this.
|
|
735
|
+
await this.waitReceiveLoopWake(signal);
|
|
745
736
|
continue;
|
|
746
737
|
}
|
|
747
738
|
packetTransferred = true;
|
|
@@ -757,11 +748,11 @@ class ZLinkStreamSessionNodeRuntime {
|
|
|
757
748
|
if (!this.isReceiveStopped(signal) && !packetTransferred) {
|
|
758
749
|
this.options.onError?.(error);
|
|
759
750
|
this.receiveWorkSinceYield = 0;
|
|
760
|
-
await
|
|
751
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
761
752
|
}
|
|
762
753
|
else if (!this.isReceiveStopped(signal)) {
|
|
763
754
|
this.receiveWorkSinceYield = 0;
|
|
764
|
-
await
|
|
755
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
765
756
|
}
|
|
766
757
|
continue;
|
|
767
758
|
}
|
|
@@ -1091,11 +1082,14 @@ class ZLinkStreamSessionNodeRuntime {
|
|
|
1091
1082
|
pending.session.enqueueDisconnected(pending.error);
|
|
1092
1083
|
});
|
|
1093
1084
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1085
|
+
async waitReceiveLoopWake(signal) {
|
|
1086
|
+
await Promise.race([
|
|
1087
|
+
this.options.readablePoller.waitForReadable(signal),
|
|
1088
|
+
this.receiveWake.wait()
|
|
1089
|
+
]);
|
|
1096
1090
|
}
|
|
1097
1091
|
wakeReceiveLoop() {
|
|
1098
|
-
this.
|
|
1092
|
+
this.receiveWake.wake();
|
|
1099
1093
|
}
|
|
1100
1094
|
getOrCreateSession(routingId) {
|
|
1101
1095
|
const sessionId = (0, managed_stream_1.streamSessionIdFromRoutingId)(routingId);
|
|
@@ -6,7 +6,6 @@ export interface ZLinkWorkerRuntimeOptions {
|
|
|
6
6
|
readonly minThreads: number;
|
|
7
7
|
readonly maxThreads: number;
|
|
8
8
|
readonly idleTimeoutMs: number;
|
|
9
|
-
readonly maxQueueLength: number;
|
|
10
9
|
}
|
|
11
10
|
export declare function resolveWorkerRuntimeOptions(options?: ZLinkWorkerOptions): ZLinkWorkerRuntimeOptions;
|
|
12
11
|
export declare class ZLinkWorkerRuntime {
|
|
@@ -13,13 +13,11 @@ function resolveWorkerRuntimeOptions(options) {
|
|
|
13
13
|
const resolved = {
|
|
14
14
|
minThreads: options?.minThreads ?? InternalDefaults_1.DEFAULT_WORKER_MIN_THREADS,
|
|
15
15
|
maxThreads: options?.maxThreads ?? (0, InternalDefaults_1.defaultWorkerMaxThreads)(),
|
|
16
|
-
idleTimeoutMs: options?.idleTimeoutMs ?? InternalDefaults_1.DEFAULT_WORKER_IDLE_TIMEOUT_MS
|
|
17
|
-
maxQueueLength: options?.maxQueueLength ?? InternalDefaults_1.DEFAULT_WORKER_QUEUE_LENGTH
|
|
16
|
+
idleTimeoutMs: options?.idleTimeoutMs ?? InternalDefaults_1.DEFAULT_WORKER_IDLE_TIMEOUT_MS
|
|
18
17
|
};
|
|
19
18
|
requireNonNegativeInteger('Worker minThreads', resolved.minThreads);
|
|
20
19
|
requirePositiveInteger('Worker maxThreads', resolved.maxThreads);
|
|
21
20
|
requireNonNegativeInteger('Worker idleTimeoutMs', resolved.idleTimeoutMs);
|
|
22
|
-
requirePositiveInteger('Worker maxQueueLength', resolved.maxQueueLength);
|
|
23
21
|
if (resolved.maxThreads < resolved.minThreads) {
|
|
24
22
|
throw new configuration_1.ZLinkConfigurationException('Worker maxThreads must be greater than or equal to minThreads.');
|
|
25
23
|
}
|
|
@@ -53,9 +51,6 @@ class ZLinkCpuWorkerPool {
|
|
|
53
51
|
if (signal?.aborted === true) {
|
|
54
52
|
return Promise.reject((0, abort_1.createAbortError)());
|
|
55
53
|
}
|
|
56
|
-
if (this.queueCount >= this.options.maxQueueLength) {
|
|
57
|
-
return Promise.reject(workerQueueFull(this.options.maxQueueLength));
|
|
58
|
-
}
|
|
59
54
|
return new Promise((resolve, reject) => {
|
|
60
55
|
const job = {
|
|
61
56
|
source: work.toString(),
|
|
@@ -446,9 +441,6 @@ class ZLinkSerialDeliveredPromise {
|
|
|
446
441
|
return this.then((value) => this.serial.execute(() => { onfinally?.(); return value; }), (reason) => this.serial.execute(() => { onfinally?.(); throw reason; }));
|
|
447
442
|
}
|
|
448
443
|
}
|
|
449
|
-
function workerQueueFull(maxQueueLength) {
|
|
450
|
-
return (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.WorkerQueueFull, `CPU worker queue is full (maxQueueLength=${maxQueueLength}).`, true);
|
|
451
|
-
}
|
|
452
444
|
function workerTimedOut(timeoutMs) {
|
|
453
445
|
return (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.WorkerTimedOut, `Worker job timed out after ${timeoutMs}ms.`, true);
|
|
454
446
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zlink-systems/framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/zlink-systems/zlink.git",
|
|
8
|
+
"directory": "framework/languages/node/packages/framework"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://zlink.systems/",
|
|
5
11
|
"main": "dist/index.js",
|
|
6
12
|
"types": "dist/index.d.ts",
|
|
7
13
|
"exports": {
|
|
@@ -13,7 +19,7 @@
|
|
|
13
19
|
"dependencies": {
|
|
14
20
|
"@opentelemetry/api": "^1.9.1",
|
|
15
21
|
"@opentelemetry/api-logs": "^0.221.0",
|
|
16
|
-
"@zlink-systems/stream-wire": "0.
|
|
17
|
-
"@zlink-systems/zlink": "
|
|
22
|
+
"@zlink-systems/stream-wire": "0.12.0",
|
|
23
|
+
"@zlink-systems/zlink": "1.1.0"
|
|
18
24
|
}
|
|
19
25
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Deterministic endpoint-notation normalization (endpoint 표기
|
|
3
|
-
*
|
|
2
|
+
* Deterministic endpoint-notation normalization (endpoint 표기 정책),
|
|
3
|
+
* applied identically across all four language bindings so endpoint
|
|
4
|
+
* strings compare equal.
|
|
4
5
|
*
|
|
5
6
|
* `new URL()` cannot represent this domain: it throws on bracketed IPv6
|
|
6
7
|
* literals that carry a zone id (`tcp://[fe80::1%eth0]:80`, even with the
|
|
@@ -16,7 +17,7 @@
|
|
|
16
17
|
* public connect/disconnect APIs). Everything downstream compares the
|
|
17
18
|
* resulting strings with plain `===`.
|
|
18
19
|
*
|
|
19
|
-
* Rules
|
|
20
|
+
* Rules, applied only to authority-bearing schemes
|
|
20
21
|
* (`tcp`, `tls`, `ws`, `wss` -- schemes whose grammar after `scheme://` is
|
|
21
22
|
* `host:port`):
|
|
22
23
|
* - scheme: lowercased
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
* `normalizeEndpoint(normalizeEndpoint(x)) === normalizeEndpoint(x)`.
|
|
40
41
|
*
|
|
41
42
|
* DNS names are never resolved. `localhost` and `127.0.0.1` remain distinct
|
|
42
|
-
* endpoints
|
|
43
|
+
* endpoints.
|
|
43
44
|
*/
|
|
44
45
|
|
|
45
46
|
const SCHEME_PATTERN = /^([A-Za-z][A-Za-z0-9+.-]*):\/\/(.*)$/s;
|
|
@@ -6,7 +6,6 @@ export const DEFAULT_STREAM_NODE_MAX_MESSAGE_SIZE = 64 * 1024;
|
|
|
6
6
|
/** Defaults shared by registration normalization and the runtime worker pool. */
|
|
7
7
|
export const DEFAULT_WORKER_MIN_THREADS = 0;
|
|
8
8
|
export const DEFAULT_WORKER_IDLE_TIMEOUT_MS = 30_000;
|
|
9
|
-
export const DEFAULT_WORKER_QUEUE_LENGTH = 1024;
|
|
10
9
|
|
|
11
10
|
export function defaultWorkerMaxThreads(): number {
|
|
12
11
|
return Math.max(2, os.availableParallelism());
|
|
@@ -25,8 +25,7 @@ import {
|
|
|
25
25
|
defaultWorkerMaxThreads,
|
|
26
26
|
DEFAULT_STREAM_NODE_MAX_MESSAGE_SIZE,
|
|
27
27
|
DEFAULT_WORKER_IDLE_TIMEOUT_MS,
|
|
28
|
-
DEFAULT_WORKER_MIN_THREADS
|
|
29
|
-
DEFAULT_WORKER_QUEUE_LENGTH
|
|
28
|
+
DEFAULT_WORKER_MIN_THREADS
|
|
30
29
|
} from './InternalDefaults';
|
|
31
30
|
|
|
32
31
|
export function normalizeWorkerOptions(
|
|
@@ -37,8 +36,7 @@ export function normalizeWorkerOptions(
|
|
|
37
36
|
return {
|
|
38
37
|
minThreads: partial.minThreads ?? DEFAULT_WORKER_MIN_THREADS,
|
|
39
38
|
maxThreads: partial.maxThreads ?? defaultWorkerMaxThreads(),
|
|
40
|
-
idleTimeoutMs: partial.idleTimeoutMs ?? DEFAULT_WORKER_IDLE_TIMEOUT_MS
|
|
41
|
-
maxQueueLength: partial.maxQueueLength ?? DEFAULT_WORKER_QUEUE_LENGTH
|
|
39
|
+
idleTimeoutMs: partial.idleTimeoutMs ?? DEFAULT_WORKER_IDLE_TIMEOUT_MS
|
|
42
40
|
};
|
|
43
41
|
}
|
|
44
42
|
|
|
@@ -104,16 +104,13 @@ export interface ZLinkLocationRegistration {
|
|
|
104
104
|
/**
|
|
105
105
|
* CPU worker-thread pool settings. I/O workers do not consume this pool.
|
|
106
106
|
* `minThreads` keeps a warm baseline, `maxThreads` bounds active CPU jobs,
|
|
107
|
-
* `idleTimeoutMs` reclaims workers above the baseline
|
|
108
|
-
*
|
|
109
|
-
* JavaScript caller omits a field: `0`, `max(2, availableParallelism())`,
|
|
110
|
-
* `30000`, and `1024`, respectively.
|
|
107
|
+
* `idleTimeoutMs` reclaims workers above the baseline. The registration
|
|
108
|
+
* normalizer supplies defaults when a JavaScript caller omits a field.
|
|
111
109
|
*/
|
|
112
110
|
export interface ZLinkWorkerOptions {
|
|
113
111
|
readonly minThreads: number;
|
|
114
112
|
readonly maxThreads: number;
|
|
115
113
|
readonly idleTimeoutMs: number;
|
|
116
|
-
readonly maxQueueLength: number;
|
|
117
114
|
}
|
|
118
115
|
|
|
119
116
|
export interface ZLinkCodecSerializerRegistration {
|
|
@@ -97,7 +97,6 @@ function validateWorkerOptions(worker: ZLinkWorkerOptions | undefined): void {
|
|
|
97
97
|
requireNonNegativeInteger('Worker minThreads', worker.minThreads);
|
|
98
98
|
requirePositiveInteger('Worker maxThreads', worker.maxThreads);
|
|
99
99
|
requireNonNegativeInteger('Worker idleTimeoutMs', worker.idleTimeoutMs);
|
|
100
|
-
requirePositiveInteger('Worker maxQueueLength', worker.maxQueueLength);
|
|
101
100
|
if (worker.maxThreads < worker.minThreads) {
|
|
102
101
|
throw new ZLinkConfigurationException(
|
|
103
102
|
'Worker maxThreads must be greater than or equal to minThreads.'
|
|
@@ -6,13 +6,12 @@ export enum ZLinkFrameworkErrorKind {
|
|
|
6
6
|
NotConfigured = 3,
|
|
7
7
|
Rejected = 4,
|
|
8
8
|
Unavailable = 5,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
InternalFailure = 12
|
|
9
|
+
DeadlineExceeded = 6,
|
|
10
|
+
ShuttingDown = 7,
|
|
11
|
+
ProtocolError = 8,
|
|
12
|
+
InvalidOperation = 9,
|
|
13
|
+
DataLost = 10,
|
|
14
|
+
InternalFailure = 11
|
|
16
15
|
}
|
|
17
16
|
|
|
18
17
|
export class ZLinkFrameworkException extends Error {
|
|
@@ -916,7 +916,7 @@ function submitted(): ZLinkSubmitResult {
|
|
|
916
916
|
// the reply comes from a remote target, so a fine framework failure code
|
|
917
917
|
// refines the coarse terminal, and a terminal-only conflict/busy is the
|
|
918
918
|
// target's owner/queue state (retryable stale/Unavailable), never a
|
|
919
|
-
// source-owned
|
|
919
|
+
// source-owned queue exhaustion. Backpressure never appears on this reply
|
|
920
920
|
// path. Internal kinds are preserved so the stale-actor re-resolve retry
|
|
921
921
|
// (isStaleActorError) keeps working.
|
|
922
922
|
function actorFailureCodeKind(
|
|
@@ -5,10 +5,6 @@ import {
|
|
|
5
5
|
type ZLinkSerialWorkOptions,
|
|
6
6
|
type ZLinkSerialWorkRecord
|
|
7
7
|
} from '../execution/serial-execution-queue';
|
|
8
|
-
import {
|
|
9
|
-
ZLinkFrameworkInternalErrorKind,
|
|
10
|
-
createInternalFrameworkException
|
|
11
|
-
} from '../framework-errors-internal';
|
|
12
8
|
import { runZLinkActorExecution } from './actor-execution-context';
|
|
13
9
|
import {
|
|
14
10
|
bindApplicationJobPermit,
|
|
@@ -25,14 +21,7 @@ export class ZLinkActorSerialExecutor {
|
|
|
25
21
|
) {
|
|
26
22
|
this.scheduler = new ZLinkSerialExecutionQueue(
|
|
27
23
|
(record) => this.runRecord(record),
|
|
28
|
-
|
|
29
|
-
...options,
|
|
30
|
-
capacityError: options?.capacityError ?? (() =>
|
|
31
|
-
createInternalFrameworkException(
|
|
32
|
-
ZLinkFrameworkInternalErrorKind.WorkerQueueFull,
|
|
33
|
-
'Actor execution queue capacity was exceeded.'
|
|
34
|
-
))
|
|
35
|
-
}
|
|
24
|
+
options
|
|
36
25
|
);
|
|
37
26
|
}
|
|
38
27
|
|
|
@@ -578,6 +578,8 @@ export interface ZLinkBackendSubscriberSocket extends ZLinkBackendConnectableSoc
|
|
|
578
578
|
|
|
579
579
|
export interface ZLinkBackendReadablePoller {
|
|
580
580
|
wait(timeoutMs: number): boolean;
|
|
581
|
+
waitForReadable(signal?: AbortSignal): Promise<boolean>;
|
|
582
|
+
markDrained(): void;
|
|
581
583
|
dispose(): void;
|
|
582
584
|
}
|
|
583
585
|
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import { ZLinkBufferMessage as Message } from './runtime-message';
|
|
2
|
+
import type { Message as ZLinkMessage } from '../../contracts/Common/Message';
|
|
2
3
|
import type {
|
|
3
4
|
MeshOperationId,
|
|
4
5
|
ReceiveKindData,
|
|
5
6
|
ReceiveRecord
|
|
6
7
|
} from '../foundation/service-runtime-contracts';
|
|
7
8
|
import { operationIdentityKey } from '../foundation/operation-identity';
|
|
8
|
-
import {
|
|
9
|
-
ZLinkFrameworkErrorKind,
|
|
10
|
-
ZLinkFrameworkException
|
|
11
|
-
} from '../../contracts';
|
|
12
|
-
|
|
13
|
-
export const ZLINK_MESH_COMPLETION_CAPACITY = 4_096;
|
|
14
9
|
|
|
15
10
|
export interface ZLinkMeshCompletionDiagnostic {
|
|
16
11
|
readonly kind: 'unknownOrLate';
|
|
@@ -36,14 +31,7 @@ export class ZLinkMeshCompletionTable {
|
|
|
36
31
|
private readonly pending = new Map<string, PendingCompletion>();
|
|
37
32
|
private disposed = false;
|
|
38
33
|
|
|
39
|
-
constructor(
|
|
40
|
-
private readonly maxPendingOperations = ZLINK_MESH_COMPLETION_CAPACITY,
|
|
41
|
-
private readonly onDiagnostic?: (diagnostic: ZLinkMeshCompletionDiagnostic) => void
|
|
42
|
-
) {
|
|
43
|
-
if (!Number.isSafeInteger(maxPendingOperations) || maxPendingOperations <= 0) {
|
|
44
|
-
throw new RangeError('maxPendingOperations must be a positive safe integer.');
|
|
45
|
-
}
|
|
46
|
-
}
|
|
34
|
+
constructor(private readonly onDiagnostic?: (diagnostic: ZLinkMeshCompletionDiagnostic) => void) {}
|
|
47
35
|
|
|
48
36
|
/**
|
|
49
37
|
* Submits and registers without yielding control to the event loop. Mesh
|
|
@@ -63,12 +51,6 @@ export class ZLinkMeshCompletionTable {
|
|
|
63
51
|
signal.reason ?? new DOMException('The operation was aborted.', 'AbortError')
|
|
64
52
|
);
|
|
65
53
|
}
|
|
66
|
-
if (this.pending.size >= this.maxPendingOperations) {
|
|
67
|
-
return Promise.reject(new ZLinkFrameworkException(
|
|
68
|
-
ZLinkFrameworkErrorKind.CapacityExceeded,
|
|
69
|
-
`Mesh completion capacity ${this.maxPendingOperations} is exhausted.`
|
|
70
|
-
));
|
|
71
|
-
}
|
|
72
54
|
const operationId = operation();
|
|
73
55
|
// The backend submission callback can synchronously re-enter `dispose()`.
|
|
74
56
|
// TypeScript's control-flow analysis cannot observe mutation through it.
|
|
@@ -117,7 +99,7 @@ export class ZLinkMeshCompletionTable {
|
|
|
117
99
|
this.pending.delete(key);
|
|
118
100
|
pending.removeAbort?.();
|
|
119
101
|
try {
|
|
120
|
-
pending.resolve(
|
|
102
|
+
pending.resolve(retainCompletion(record));
|
|
121
103
|
} catch (error) {
|
|
122
104
|
pending.reject(error);
|
|
123
105
|
}
|
|
@@ -144,16 +126,23 @@ export class ZLinkMeshCompletionTable {
|
|
|
144
126
|
|
|
145
127
|
}
|
|
146
128
|
|
|
147
|
-
function
|
|
129
|
+
function retainCompletion(record: ReceiveRecord): ZLinkMeshCompletion {
|
|
148
130
|
return {
|
|
149
131
|
terminalResult: record.terminalResult,
|
|
150
132
|
failureErrno: record.failureErrno,
|
|
151
133
|
operationKind: record.operationKind,
|
|
152
134
|
kindData: record.kindData,
|
|
153
|
-
parts: record.parts.map(
|
|
135
|
+
parts: record.parts.map(retainCompletionPart)
|
|
154
136
|
};
|
|
155
137
|
}
|
|
156
138
|
|
|
139
|
+
function retainCompletionPart(part: ZLinkMessage): Message {
|
|
140
|
+
// A runtime message already owns a managed Buffer and close() is a no-op.
|
|
141
|
+
// Binding-native messages can expose native storage through data(), which
|
|
142
|
+
// becomes invalid when their owner closes; materialize that one boundary copy.
|
|
143
|
+
return part instanceof Message ? part : Message.fromOwned(Buffer.from(part.data()));
|
|
144
|
+
}
|
|
145
|
+
|
|
157
146
|
export function closeMeshCompletion(completion: ZLinkMeshCompletion): void {
|
|
158
147
|
closeParts(completion.parts);
|
|
159
148
|
}
|