@zlink-systems/framework 0.11.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 +5 -1
- 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 +3 -1
- package/dist/runtime/channels/channel-transports.d.ts +2 -1
- package/dist/runtime/channels/channel-transports.js +12 -10
- 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/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 +3 -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 +7 -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 +4 -1
- package/src/runtime/channels/channel-transports.ts +20 -10
- 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/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
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ZLinkMeshDispatchPump = void 0;
|
|
4
4
|
const node_async_hooks_1 = require("node:async_hooks");
|
|
5
|
+
const node_os_1 = require("node:os");
|
|
5
6
|
const runtime_values_1 = require("./runtime-values");
|
|
6
7
|
const service_runtime_contracts_1 = require("../foundation/service-runtime-contracts");
|
|
7
8
|
const execution_1 = require("../execution");
|
|
8
9
|
const application_job_queue_scope_1 = require("../application-jobs/application-job-queue-scope");
|
|
9
|
-
const
|
|
10
|
-
const
|
|
10
|
+
const MESH_DISPATCH_YIELD_RECORDS = 16;
|
|
11
|
+
const MESH_DISPATCH_YIELD_INTERVAL_MS = 2;
|
|
11
12
|
const MESH_DISPATCH_LIFECYCLE_CLAIM_BUDGET = 4;
|
|
13
|
+
const MESH_DISPATCH_RECEIVE_CAPACITY = 64;
|
|
14
|
+
const MESH_DISPATCH_MAX_APPLICATION_WORKERS = Math.max(2, (0, node_os_1.availableParallelism)());
|
|
12
15
|
// Captured while the runtime module is loaded, before any application owner
|
|
13
16
|
// exists. A ready callback may run inside a Spot turn; the shared Mesh pump
|
|
14
17
|
// must enter like an independent receive-loop task, without inheriting that
|
|
@@ -28,24 +31,27 @@ class ZLinkMeshDispatchPump {
|
|
|
28
31
|
node;
|
|
29
32
|
options;
|
|
30
33
|
pendingDomains = service_runtime_contracts_1.ReadyDomain.None;
|
|
31
|
-
scheduled = false;
|
|
32
34
|
infrastructureScheduled = false;
|
|
33
35
|
disposed = false;
|
|
34
|
-
drainPromise;
|
|
35
36
|
infrastructureDrainPromise;
|
|
36
37
|
activeDrains = new Set();
|
|
38
|
+
idleApplicationWorkers = new Set();
|
|
39
|
+
applicationWorkerCount = 0;
|
|
37
40
|
capacityStop = new AbortController();
|
|
41
|
+
recordsSinceYield = 0;
|
|
42
|
+
yieldStartedAtMs = 0;
|
|
38
43
|
constructor(node, options) {
|
|
39
44
|
this.node = node;
|
|
40
45
|
this.options = options;
|
|
41
46
|
}
|
|
42
47
|
start() {
|
|
48
|
+
if (this.applicationWorkerCount === 0)
|
|
49
|
+
this.startApplicationWorker();
|
|
43
50
|
this.node.setReadyHandler((domains) => {
|
|
44
51
|
if (this.disposed) {
|
|
45
52
|
return service_runtime_contracts_1.ReadyDomain.None;
|
|
46
53
|
}
|
|
47
|
-
this.
|
|
48
|
-
this.schedule();
|
|
54
|
+
this.markReady(domains);
|
|
49
55
|
return domains;
|
|
50
56
|
});
|
|
51
57
|
}
|
|
@@ -56,42 +62,100 @@ class ZLinkMeshDispatchPump {
|
|
|
56
62
|
this.disposed = true;
|
|
57
63
|
this.capacityStop.abort();
|
|
58
64
|
this.pendingDomains = service_runtime_contracts_1.ReadyDomain.None;
|
|
65
|
+
for (const wake of this.idleApplicationWorkers)
|
|
66
|
+
wake();
|
|
67
|
+
this.idleApplicationWorkers.clear();
|
|
59
68
|
while (this.activeDrains.size > 0) {
|
|
60
69
|
await Promise.all([...this.activeDrains]);
|
|
61
70
|
}
|
|
62
71
|
}
|
|
63
|
-
|
|
64
|
-
if (
|
|
72
|
+
markReady(domains) {
|
|
73
|
+
if (domains === service_runtime_contracts_1.ReadyDomain.None || this.disposed)
|
|
74
|
+
return;
|
|
75
|
+
const previous = this.pendingDomains;
|
|
76
|
+
this.pendingDomains |= domains;
|
|
77
|
+
const startInfrastructure = (domains & service_runtime_contracts_1.ReadyDomain.Infrastructure) !== 0
|
|
78
|
+
&& (previous & service_runtime_contracts_1.ReadyDomain.Infrastructure) === 0;
|
|
79
|
+
const startApplication = (domains & service_runtime_contracts_1.ReadyDomain.Application) !== 0
|
|
80
|
+
&& (previous & service_runtime_contracts_1.ReadyDomain.Application) === 0;
|
|
81
|
+
if (startInfrastructure && startApplication) {
|
|
82
|
+
const startTurn = this.yieldIfNeeded() ?? Promise.resolve();
|
|
83
|
+
this.scheduleInfrastructure(startTurn);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (startInfrastructure)
|
|
65
87
|
this.scheduleInfrastructure();
|
|
88
|
+
if (startApplication) {
|
|
89
|
+
if ((domains & service_runtime_contracts_1.ReadyDomain.Infrastructure) !== 0) {
|
|
90
|
+
setImmediate(() => this.wakeApplicationWorker());
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
this.wakeApplicationWorker();
|
|
94
|
+
}
|
|
66
95
|
}
|
|
67
|
-
|
|
96
|
+
}
|
|
97
|
+
wakeApplicationWorker() {
|
|
98
|
+
if (this.disposed || (this.pendingDomains & service_runtime_contracts_1.ReadyDomain.Application) === 0)
|
|
68
99
|
return;
|
|
69
|
-
|
|
70
|
-
if (
|
|
100
|
+
const idle = this.idleApplicationWorkers.values().next().value;
|
|
101
|
+
if (idle !== undefined) {
|
|
102
|
+
this.idleApplicationWorkers.delete(idle);
|
|
103
|
+
this.pendingDomains &= ~service_runtime_contracts_1.ReadyDomain.Application;
|
|
104
|
+
idle();
|
|
71
105
|
return;
|
|
72
106
|
}
|
|
73
|
-
this.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
107
|
+
if (this.applicationWorkerCount < MESH_DISPATCH_MAX_APPLICATION_WORKERS) {
|
|
108
|
+
this.startApplicationWorker();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
startApplicationWorker() {
|
|
112
|
+
this.applicationWorkerCount += 1;
|
|
113
|
+
let worker;
|
|
114
|
+
worker = detachedMeshDispatchScope(() => (0, execution_1.runZLinkExecutionArea)('application', () => this.runApplicationWorker()))
|
|
115
|
+
.finally(() => {
|
|
116
|
+
this.applicationWorkerCount -= 1;
|
|
117
|
+
this.activeDrains.delete(worker);
|
|
82
118
|
});
|
|
83
|
-
this.activeDrains.add(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
119
|
+
this.activeDrains.add(worker);
|
|
120
|
+
}
|
|
121
|
+
async runApplicationWorker() {
|
|
122
|
+
// A worker claims one owner at a time so a suspended handler cannot hold
|
|
123
|
+
// unrelated owners. Each owner may supply up to 64 pre-admitted records.
|
|
124
|
+
const readyBatch = this.node.createReadyBatch(1);
|
|
125
|
+
const receiveBatch = this.node.createReceiveBatch(MESH_DISPATCH_RECEIVE_CAPACITY, this.options.partCapacity ?? 256);
|
|
126
|
+
try {
|
|
127
|
+
for (;;) {
|
|
128
|
+
await this.waitForApplicationReady();
|
|
129
|
+
if (this.disposed)
|
|
130
|
+
return;
|
|
131
|
+
try {
|
|
132
|
+
await this.drainDomain(service_runtime_contracts_1.ReadyDomain.Application, readyBatch, receiveBatch);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
this.reportDispatchError(error);
|
|
92
136
|
}
|
|
93
137
|
}
|
|
94
|
-
}
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
receiveBatch.close();
|
|
141
|
+
readyBatch.close();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
waitForApplicationReady() {
|
|
145
|
+
if (this.disposed)
|
|
146
|
+
return Promise.resolve();
|
|
147
|
+
if ((this.pendingDomains & service_runtime_contracts_1.ReadyDomain.Application) !== 0) {
|
|
148
|
+
this.pendingDomains &= ~service_runtime_contracts_1.ReadyDomain.Application;
|
|
149
|
+
return Promise.resolve();
|
|
150
|
+
}
|
|
151
|
+
return new Promise(resolve => this.idleApplicationWorkers.add(resolve));
|
|
152
|
+
}
|
|
153
|
+
reportDispatchError(error) {
|
|
154
|
+
if (error instanceof ZLinkMeshDispatchFailure) {
|
|
155
|
+
this.options.reportError?.(error.dispatchCause, error.context);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
this.options.reportError?.(error);
|
|
95
159
|
}
|
|
96
160
|
/**
|
|
97
161
|
* Infrastructure controls must not wait for an application permit. During
|
|
@@ -100,13 +164,13 @@ class ZLinkMeshDispatchPump {
|
|
|
100
164
|
* the infrastructure mailbox and prevent the relocation coordinator from
|
|
101
165
|
* completing the drain that releases the application work.
|
|
102
166
|
*/
|
|
103
|
-
scheduleInfrastructure() {
|
|
167
|
+
scheduleInfrastructure(startTurn) {
|
|
104
168
|
if (this.infrastructureScheduled) {
|
|
105
169
|
return;
|
|
106
170
|
}
|
|
107
171
|
this.infrastructureScheduled = true;
|
|
108
|
-
const drain =
|
|
109
|
-
.then(() => detachedMeshDispatchScope(() => this.drainInfrastructure()))
|
|
172
|
+
const drain = (startTurn ?? this.yieldIfNeeded() ?? Promise.resolve())
|
|
173
|
+
.then(() => detachedMeshDispatchScope(() => (0, execution_1.runZLinkExecutionArea)('infrastructure', () => this.drainInfrastructure())))
|
|
110
174
|
.catch((error) => {
|
|
111
175
|
if (error instanceof ZLinkMeshDispatchFailure) {
|
|
112
176
|
this.options.reportError?.(error.dispatchCause, error.context);
|
|
@@ -127,39 +191,31 @@ class ZLinkMeshDispatchPump {
|
|
|
127
191
|
}
|
|
128
192
|
});
|
|
129
193
|
}
|
|
130
|
-
async drain() {
|
|
131
|
-
while (!this.disposed) {
|
|
132
|
-
if ((this.pendingDomains & service_runtime_contracts_1.ReadyDomain.Application) === 0) {
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
this.pendingDomains &= ~service_runtime_contracts_1.ReadyDomain.Application;
|
|
136
|
-
await this.drainDomain(service_runtime_contracts_1.ReadyDomain.Application);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
194
|
async drainInfrastructure() {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
this.pendingDomains
|
|
195
|
+
const readyBatch = this.node.createReadyBatch(Math.min(this.options.readyCapacity ?? 32, MESH_DISPATCH_LIFECYCLE_CLAIM_BUDGET));
|
|
196
|
+
const receiveBatch = this.node.createReceiveBatch(MESH_DISPATCH_RECEIVE_CAPACITY, this.options.partCapacity ?? 256);
|
|
197
|
+
try {
|
|
198
|
+
while (!this.disposed) {
|
|
199
|
+
if ((this.pendingDomains & service_runtime_contracts_1.ReadyDomain.Infrastructure) === 0) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
this.pendingDomains &= ~service_runtime_contracts_1.ReadyDomain.Infrastructure;
|
|
203
|
+
if ((this.pendingDomains & service_runtime_contracts_1.ReadyDomain.Application) !== 0) {
|
|
204
|
+
setImmediate(() => this.wakeApplicationWorker());
|
|
205
|
+
}
|
|
206
|
+
const lifecycleBudgetExhausted = await this.drainDomain(service_runtime_contracts_1.ReadyDomain.Infrastructure, readyBatch, receiveBatch, MESH_DISPATCH_LIFECYCLE_CLAIM_BUDGET);
|
|
207
|
+
if (lifecycleBudgetExhausted) {
|
|
208
|
+
this.pendingDomains |= service_runtime_contracts_1.ReadyDomain.Infrastructure;
|
|
209
|
+
await yieldToEventLoop();
|
|
210
|
+
}
|
|
148
211
|
}
|
|
149
212
|
}
|
|
213
|
+
finally {
|
|
214
|
+
receiveBatch.close();
|
|
215
|
+
readyBatch.close();
|
|
216
|
+
}
|
|
150
217
|
}
|
|
151
|
-
async drainDomain(domain, claimBudget) {
|
|
152
|
-
const readyCapacity = domain === service_runtime_contracts_1.ReadyDomain.Application
|
|
153
|
-
? 1
|
|
154
|
-
: claimBudget === undefined
|
|
155
|
-
? (this.options.readyCapacity ?? 32)
|
|
156
|
-
: Math.min(this.options.readyCapacity ?? 32, claimBudget);
|
|
157
|
-
const readyBatch = this.node.createReadyBatch(readyCapacity);
|
|
158
|
-
// One record per claim receive keeps one ordinary-ingress permit paired
|
|
159
|
-
// with exactly one dispatch turn. Terminal completion claims bypass it.
|
|
160
|
-
const receiveBatch = this.node.createReceiveBatch(1, this.options.partCapacity ?? 256);
|
|
161
|
-
let receiveBatchesSinceTimerYield = 0;
|
|
162
|
-
let timerYieldStartedAtMs = this.nowMs();
|
|
218
|
+
async drainDomain(domain, readyBatch, receiveBatch, claimBudget) {
|
|
163
219
|
let claimsDrained = 0;
|
|
164
220
|
try {
|
|
165
221
|
for (;;) {
|
|
@@ -174,113 +230,124 @@ class ZLinkMeshDispatchPump {
|
|
|
174
230
|
return false;
|
|
175
231
|
}
|
|
176
232
|
if (domain === service_runtime_contracts_1.ReadyDomain.Application && drained.hasResidue) {
|
|
177
|
-
this.
|
|
233
|
+
this.markReady(service_runtime_contracts_1.ReadyDomain.Application);
|
|
178
234
|
}
|
|
179
235
|
for (let index = 0; index < drained.records.length; index += 1) {
|
|
180
236
|
const claim = readyBatch.takeClaim(index);
|
|
181
237
|
claimsDrained += 1;
|
|
238
|
+
const owner = drained.records[index];
|
|
239
|
+
// Raw ingress already attached one host permit to each record.
|
|
240
|
+
// A claim that reserves admission here may receive only that permit's record.
|
|
182
241
|
try {
|
|
183
|
-
|
|
242
|
+
const capacity = owner.ordinaryIngressPreAdmitted === true
|
|
243
|
+
? MESH_DISPATCH_RECEIVE_CAPACITY : 1;
|
|
184
244
|
for (;;) {
|
|
185
|
-
|
|
186
|
-
const claimPermit = owner.terminalCompletion === true
|
|
245
|
+
let claimPermit = owner.terminalCompletion === true
|
|
187
246
|
|| owner.ordinaryIngressPreAdmitted === true
|
|
188
247
|
|| domain === service_runtime_contracts_1.ReadyDomain.Infrastructure
|
|
189
248
|
? undefined
|
|
190
249
|
: await this.acquirePermit();
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
if (owner.ordinaryIngressPreAdmitted === true && permit === undefined) {
|
|
207
|
-
record.releaseRetainedIngress?.();
|
|
208
|
-
throw new Error('Pre-admitted raw ingress record lost its Application Job Queue permit.');
|
|
250
|
+
try {
|
|
251
|
+
if (this.capacityStop.signal.aborted)
|
|
252
|
+
return false;
|
|
253
|
+
receiveBatch.reset(capacity);
|
|
254
|
+
const received = claim.recvBatch(receiveBatch, runtime_values_1.ZLINK_BACKEND_RECV_DONT_WAIT);
|
|
255
|
+
if (!received.ok)
|
|
256
|
+
break;
|
|
257
|
+
// This owner keeps its claim while it drains. Other owners and
|
|
258
|
+
// infrastructure may progress if one of its handlers suspends.
|
|
259
|
+
if (domain === service_runtime_contracts_1.ReadyDomain.Infrastructure) {
|
|
260
|
+
this.infrastructureScheduled = false;
|
|
261
|
+
this.infrastructureDrainPromise = undefined;
|
|
262
|
+
if ((this.pendingDomains & service_runtime_contracts_1.ReadyDomain.Infrastructure) !== 0) {
|
|
263
|
+
this.scheduleInfrastructure();
|
|
264
|
+
}
|
|
209
265
|
}
|
|
210
266
|
try {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
// handler deadlocks against its own claim (spec
|
|
218
|
-
// 46-internal-dispatch-loop, receive/dispatch wake/ordering).
|
|
219
|
-
this.scheduled = false;
|
|
220
|
-
this.drainPromise = undefined;
|
|
221
|
-
if (domain === service_runtime_contracts_1.ReadyDomain.Infrastructure) {
|
|
222
|
-
this.infrastructureScheduled = false;
|
|
223
|
-
this.infrastructureDrainPromise = undefined;
|
|
224
|
-
}
|
|
225
|
-
if (this.pendingDomains !== service_runtime_contracts_1.ReadyDomain.None) {
|
|
226
|
-
this.schedule();
|
|
227
|
-
}
|
|
228
|
-
const dispatch = () => (0, execution_1.runZLinkExecutionArea)(domain === service_runtime_contracts_1.ReadyDomain.Infrastructure ? 'infrastructure' : 'application', async () => {
|
|
229
|
-
try {
|
|
230
|
-
await this.options.dispatch(drained.records[index], record);
|
|
267
|
+
for (const record of received.records) {
|
|
268
|
+
if (this.disposed)
|
|
269
|
+
break;
|
|
270
|
+
const permit = record.applicationJobPermit ?? claimPermit;
|
|
271
|
+
if (owner.ordinaryIngressPreAdmitted === true && permit === undefined) {
|
|
272
|
+
throw new Error('Pre-admitted raw ingress record lost its Application Job Queue permit.');
|
|
231
273
|
}
|
|
232
|
-
|
|
233
|
-
|
|
274
|
+
this.recordsSinceYield += 1;
|
|
275
|
+
const dispatch = async () => {
|
|
276
|
+
try {
|
|
277
|
+
await this.options.dispatch(drained.records[index], record);
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
throw new ZLinkMeshDispatchFailure(meshDispatchFailureContext(record), error);
|
|
281
|
+
}
|
|
282
|
+
await record.onTerminalCompletion?.();
|
|
283
|
+
};
|
|
284
|
+
if (permit === undefined) {
|
|
285
|
+
await dispatch();
|
|
234
286
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
287
|
+
else {
|
|
288
|
+
if (domain === service_runtime_contracts_1.ReadyDomain.Application
|
|
289
|
+
&& record.applicationJobPermit === undefined)
|
|
290
|
+
permit.markApplicationQueued();
|
|
291
|
+
// The scope now owns this reservation, including failure
|
|
292
|
+
// and detached handler handoff. Only unused permits remain
|
|
293
|
+
// owned by the receive attempt's finally block.
|
|
294
|
+
if (permit === claimPermit)
|
|
295
|
+
claimPermit = undefined;
|
|
296
|
+
await (0, application_job_queue_scope_1.runWithApplicationJobPermit)(permit, dispatch);
|
|
297
|
+
}
|
|
298
|
+
const yieldTurn = this.yieldIfNeeded();
|
|
299
|
+
if (yieldTurn !== undefined)
|
|
300
|
+
await yieldTurn;
|
|
245
301
|
}
|
|
246
302
|
}
|
|
247
303
|
finally {
|
|
248
|
-
|
|
249
|
-
|
|
304
|
+
// Batch ownership includes records whose dispatch never began,
|
|
305
|
+
// including on failure or shutdown in the middle of the batch.
|
|
306
|
+
for (const record of received.records) {
|
|
307
|
+
for (const part of record.parts)
|
|
308
|
+
part.close();
|
|
309
|
+
record.releaseRetainedIngress?.();
|
|
250
310
|
}
|
|
251
|
-
record.releaseRetainedIngress?.();
|
|
252
311
|
}
|
|
253
312
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
// Promise, closure, and timer allocation to the hot path.
|
|
257
|
-
receiveBatchesSinceTimerYield += 1;
|
|
258
|
-
if (receiveBatchesSinceTimerYield >= MESH_DISPATCH_TIMER_YIELD_BATCHES
|
|
259
|
-
|| this.nowMs() - timerYieldStartedAtMs >= MESH_DISPATCH_TIMER_YIELD_INTERVAL_MS) {
|
|
260
|
-
receiveBatchesSinceTimerYield = 0;
|
|
261
|
-
await yieldToTimers();
|
|
262
|
-
timerYieldStartedAtMs = this.nowMs();
|
|
313
|
+
finally {
|
|
314
|
+
claimPermit?.releaseAfterInternalProcessing();
|
|
263
315
|
}
|
|
264
|
-
receiveBatch.reset();
|
|
265
316
|
}
|
|
266
317
|
}
|
|
267
318
|
finally {
|
|
268
319
|
claim.release();
|
|
269
320
|
}
|
|
270
321
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
322
|
+
if (claimBudget !== undefined && claimsDrained >= claimBudget) {
|
|
323
|
+
this.wakeApplicationWorker();
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
// Empty ready claims also count as work, so they cannot monopolize
|
|
327
|
+
// the microtask queue without giving I/O and deadlines a turn.
|
|
328
|
+
this.recordsSinceYield += 1;
|
|
329
|
+
const yieldTurn = this.yieldIfNeeded();
|
|
330
|
+
if (yieldTurn !== undefined)
|
|
331
|
+
await yieldTurn;
|
|
274
332
|
if (!drained.hasResidue) {
|
|
275
333
|
return false;
|
|
276
334
|
}
|
|
277
335
|
}
|
|
278
336
|
}
|
|
279
337
|
finally {
|
|
280
|
-
|
|
281
|
-
readyBatch.close();
|
|
338
|
+
readyBatch.reset();
|
|
282
339
|
}
|
|
283
340
|
}
|
|
341
|
+
yieldIfNeeded() {
|
|
342
|
+
const nowMs = this.nowMs();
|
|
343
|
+
if (this.recordsSinceYield < MESH_DISPATCH_YIELD_RECORDS
|
|
344
|
+
&& nowMs - this.yieldStartedAtMs < MESH_DISPATCH_YIELD_INTERVAL_MS)
|
|
345
|
+
return undefined;
|
|
346
|
+
this.recordsSinceYield = 0;
|
|
347
|
+
return yieldToTimers().then(() => {
|
|
348
|
+
this.yieldStartedAtMs = this.nowMs();
|
|
349
|
+
});
|
|
350
|
+
}
|
|
284
351
|
nowMs() {
|
|
285
352
|
return this.options.monotonicNowMs?.() ?? performance.now();
|
|
286
353
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { ZLinkBackendAdapterFactory, ZLinkChannelBackendAdapter, ZLinkMonitoringBackendAdapter, ZLinkStreamBackendAdapter } from '../contracts';
|
|
1
|
+
import type { TopicMessage } from '@zlink-systems/zlink';
|
|
2
|
+
import type { ZLinkBackendAdapterFactory, ZLinkBackendReceived, ZLinkChannelBackendAdapter, ZLinkMonitoringBackendAdapter, ZLinkStreamBackendAdapter } from '../contracts';
|
|
3
3
|
import { ZLinkNodeMeshBackendAdapter } from './node-mesh-backend-adapter';
|
|
4
|
-
export { isDisconnectRouteNotFoundError } from './node-socket-backend-adapter';
|
|
5
4
|
export declare class ZLinkNodeBackendAdapterFactory implements ZLinkBackendAdapterFactory {
|
|
6
|
-
createReceived():
|
|
5
|
+
createReceived(): ZLinkBackendReceived;
|
|
7
6
|
createTopicMessage(): TopicMessage;
|
|
8
7
|
createChannelAdapter(): ZLinkChannelBackendAdapter;
|
|
9
8
|
createMeshAdapter(): ZLinkNodeMeshBackendAdapter;
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ZLinkNodeBackendAdapterFactory =
|
|
3
|
+
exports.ZLinkNodeBackendAdapterFactory = void 0;
|
|
4
4
|
const node_backend_adapter_support_1 = require("./node-backend-adapter-support");
|
|
5
5
|
const node_monitor_backend_adapter_1 = require("./node-monitor-backend-adapter");
|
|
6
6
|
const node_socket_backend_adapter_1 = require("./node-socket-backend-adapter");
|
|
7
7
|
const node_mesh_backend_adapter_1 = require("./node-mesh-backend-adapter");
|
|
8
|
-
var node_socket_backend_adapter_2 = require("./node-socket-backend-adapter");
|
|
9
|
-
Object.defineProperty(exports, "isDisconnectRouteNotFoundError", { enumerable: true, get: function () { return node_socket_backend_adapter_2.isDisconnectRouteNotFoundError; } });
|
|
10
8
|
class ZLinkNodeBackendAdapterFactory {
|
|
11
9
|
createReceived() {
|
|
12
10
|
return new node_backend_adapter_support_1.zlink.Received();
|
|
@@ -36,16 +34,16 @@ class ZLinkNodeChannelBackendAdapter {
|
|
|
36
34
|
return new node_backend_adapter_support_1.zlink.TopicMessage();
|
|
37
35
|
}
|
|
38
36
|
createDealerSocket(context) {
|
|
39
|
-
return (0, node_socket_backend_adapter_1.wrapSocket)(node_backend_adapter_support_1.zlink.createDealerSocket(asNodeContext(context)));
|
|
37
|
+
return (0, node_socket_backend_adapter_1.wrapSocket)(node_backend_adapter_support_1.zlink.createDealerSocket(asNodeContext(context)), true);
|
|
40
38
|
}
|
|
41
39
|
createRouterSocket(context) {
|
|
42
|
-
return (0, node_socket_backend_adapter_1.wrapSocket)(node_backend_adapter_support_1.zlink.createRouterSocket(asNodeContext(context)));
|
|
40
|
+
return (0, node_socket_backend_adapter_1.wrapSocket)(node_backend_adapter_support_1.zlink.createRouterSocket(asNodeContext(context)), true);
|
|
43
41
|
}
|
|
44
42
|
createPublisherSocket(context) {
|
|
45
43
|
return (0, node_socket_backend_adapter_1.wrapSocket)(node_backend_adapter_support_1.zlink.createPubSocket(asNodeContext(context)));
|
|
46
44
|
}
|
|
47
45
|
createSubscriberSocket(context) {
|
|
48
|
-
return (0, node_socket_backend_adapter_1.wrapSocket)(node_backend_adapter_support_1.zlink.createSubSocket(asNodeContext(context)));
|
|
46
|
+
return (0, node_socket_backend_adapter_1.wrapSocket)(node_backend_adapter_support_1.zlink.createSubSocket(asNodeContext(context)), false);
|
|
49
47
|
}
|
|
50
48
|
createReadablePoller(socket) {
|
|
51
49
|
return createNodeReadablePoller(socket);
|
|
@@ -55,7 +53,7 @@ class ZLinkNodeStreamBackendAdapter {
|
|
|
55
53
|
createStreamSocket(context) {
|
|
56
54
|
const socket = node_backend_adapter_support_1.zlink.createStreamSocket(asNodeContext(context));
|
|
57
55
|
socket.options.recvMode = node_backend_adapter_support_1.zlink.StreamRecvMode.Packet;
|
|
58
|
-
return (0, node_socket_backend_adapter_1.wrapSocket)(socket);
|
|
56
|
+
return (0, node_socket_backend_adapter_1.wrapSocket)(socket, true);
|
|
59
57
|
}
|
|
60
58
|
createStreamPacket() {
|
|
61
59
|
return new node_backend_adapter_support_1.zlink.StreamPacket();
|
|
@@ -118,44 +116,60 @@ function asNodeContext(context) {
|
|
|
118
116
|
return context.nativeInstance;
|
|
119
117
|
}
|
|
120
118
|
function createNodeReadablePoller(socket) {
|
|
121
|
-
const
|
|
122
|
-
const events = node_backend_adapter_support_1.zlink.createPollEvents(1);
|
|
123
|
-
try {
|
|
124
|
-
poller.add(socket.nativeInstance, [node_backend_adapter_support_1.zlink.PollEventFlag.PollIn], 0);
|
|
125
|
-
}
|
|
126
|
-
catch (error) {
|
|
127
|
-
events.close();
|
|
128
|
-
poller.close();
|
|
129
|
-
throw error;
|
|
130
|
-
}
|
|
119
|
+
const eventLoopPoller = (0, node_socket_backend_adapter_1.nodeEventLoopPollerOf)(socket);
|
|
131
120
|
let disposed = false;
|
|
121
|
+
let readable = false;
|
|
122
|
+
let pending;
|
|
123
|
+
const settlePending = (value) => {
|
|
124
|
+
const current = pending;
|
|
125
|
+
if (current === undefined)
|
|
126
|
+
return;
|
|
127
|
+
pending = undefined;
|
|
128
|
+
if (current.signal !== undefined && current.onAbort !== undefined) {
|
|
129
|
+
current.signal.removeEventListener('abort', current.onAbort);
|
|
130
|
+
}
|
|
131
|
+
current.resolve(value);
|
|
132
|
+
};
|
|
133
|
+
eventLoopPoller.setReadableHandler(() => {
|
|
134
|
+
if (disposed)
|
|
135
|
+
return;
|
|
136
|
+
readable = true;
|
|
137
|
+
settlePending(true);
|
|
138
|
+
});
|
|
132
139
|
return {
|
|
133
|
-
wait(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
wait(_timeoutMs) {
|
|
141
|
+
return !disposed && readable;
|
|
142
|
+
},
|
|
143
|
+
waitForReadable(signal) {
|
|
144
|
+
if (disposed || signal?.aborted === true)
|
|
145
|
+
return Promise.resolve(false);
|
|
146
|
+
if (readable)
|
|
147
|
+
return Promise.resolve(true);
|
|
148
|
+
if (pending !== undefined)
|
|
149
|
+
return pending.promise;
|
|
150
|
+
let resolvePending;
|
|
151
|
+
const promise = new Promise((resolve) => {
|
|
152
|
+
resolvePending = resolve;
|
|
153
|
+
});
|
|
154
|
+
const onAbort = signal === undefined
|
|
155
|
+
? undefined
|
|
156
|
+
: () => settlePending(false);
|
|
157
|
+
pending = { promise, resolve: resolvePending, signal, onAbort };
|
|
158
|
+
if (signal !== undefined && onAbort !== undefined) {
|
|
159
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
142
160
|
}
|
|
161
|
+
return promise;
|
|
162
|
+
},
|
|
163
|
+
markDrained() {
|
|
164
|
+
readable = false;
|
|
143
165
|
},
|
|
144
166
|
dispose() {
|
|
145
167
|
if (disposed)
|
|
146
168
|
return;
|
|
147
169
|
disposed = true;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
finally {
|
|
152
|
-
try {
|
|
153
|
-
events.close();
|
|
154
|
-
}
|
|
155
|
-
finally {
|
|
156
|
-
poller.close();
|
|
157
|
-
}
|
|
158
|
-
}
|
|
170
|
+
readable = false;
|
|
171
|
+
settlePending(false);
|
|
172
|
+
eventLoopPoller.setReadableHandler(() => { });
|
|
159
173
|
}
|
|
160
174
|
};
|
|
161
175
|
}
|
|
@@ -22,7 +22,7 @@ interface ZLinkBindingReplySubmitOperation {
|
|
|
22
22
|
}
|
|
23
23
|
interface ZLinkBindingAsyncSendSubmitOperation {
|
|
24
24
|
message(message: unknown): ZLinkBindingAsyncSendSubmitOperation;
|
|
25
|
-
submit():
|
|
25
|
+
submit(): import('@zlink-systems/zlink').SendSubmission;
|
|
26
26
|
submit_sync(): void;
|
|
27
27
|
}
|
|
28
28
|
export interface ZLinkBindingRequestOperation {
|
|
@@ -31,7 +31,7 @@ export interface ZLinkBindingRequestOperation {
|
|
|
31
31
|
interface ZLinkBindingRequestSubmitOperation {
|
|
32
32
|
message(message: unknown): ZLinkBindingRequestSubmitOperation;
|
|
33
33
|
timeout(timeoutMs: number): ZLinkBindingRequestSubmitOperation;
|
|
34
|
-
submit():
|
|
34
|
+
submit(): import('@zlink-systems/zlink').RequestSubmission;
|
|
35
35
|
}
|
|
36
36
|
export declare function isBindingNotFound(error: unknown): boolean;
|
|
37
37
|
export declare function isRouteRecvRetryable(error: unknown): boolean;
|
|
@@ -73,7 +73,7 @@ async function submitBindingAsyncSend(operation, payload) {
|
|
|
73
73
|
current = current === undefined ? operation.message(nativePart) : current.message(nativePart);
|
|
74
74
|
}
|
|
75
75
|
current ??= operation.message(Buffer.alloc(0));
|
|
76
|
-
await current.submit();
|
|
76
|
+
await current.submit().admitted;
|
|
77
77
|
}
|
|
78
78
|
catch (error) {
|
|
79
79
|
throw translateBindingResultError(error);
|
|
@@ -110,7 +110,7 @@ async function submitBindingRequest(operation, payload, timeoutMs) {
|
|
|
110
110
|
if (timeoutMs !== undefined) {
|
|
111
111
|
current = current.timeout(timeoutMs);
|
|
112
112
|
}
|
|
113
|
-
return await current.submit();
|
|
113
|
+
return await current.submit().reply;
|
|
114
114
|
}
|
|
115
115
|
catch (error) {
|
|
116
116
|
throw translateBindingResultError(error);
|