@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
|
@@ -20,7 +20,7 @@ class ZLinkDealerChannelClientTransport {
|
|
|
20
20
|
(0, abort_1.throwIfAborted)(signal);
|
|
21
21
|
// Call-scoped flow (spec 27 §4): the envelope flow pair does not outlive
|
|
22
22
|
// this outbound call.
|
|
23
|
-
await (0, flow_context_1.runWithOutboundFlow)(true, () => (0, channel_multipart_1.appendParts)(this.dealer.send(), (0, channel_envelope_1.encodeChannelEnvelopeParts)(3 /* ZLinkChannelMessageKind.Command */, channelName, packetName, message, undefined, undefined, undefined, undefined, true, metadata)).submit());
|
|
23
|
+
await (0, flow_context_1.runWithOutboundFlow)(true, () => (0, channel_multipart_1.appendParts)(this.dealer.send(), (0, channel_envelope_1.encodeChannelEnvelopeParts)(3 /* ZLinkChannelMessageKind.Command */, channelName, packetName, message, undefined, undefined, undefined, undefined, true, metadata)).submit().admitted);
|
|
24
24
|
return { status: submission_result_1.ZLinkSubmitStatus.Submitted };
|
|
25
25
|
}
|
|
26
26
|
async request(channelName, packetName, request, timeoutMs, signal, metadata = EMPTY_METADATA) {
|
|
@@ -28,7 +28,7 @@ class ZLinkDealerChannelClientTransport {
|
|
|
28
28
|
const operation = (0, flow_context_1.runWithOutboundFlow)(true, () => (0, channel_multipart_1.appendParts)(this.dealer.request(), (0, channel_envelope_1.encodeChannelEnvelopeParts)(1 /* ZLinkChannelMessageKind.Request */, channelName, packetName, request, timeoutMs, undefined, undefined, undefined, true, metadata)));
|
|
29
29
|
if (timeoutMs !== undefined)
|
|
30
30
|
operation.timeout(timeoutMs);
|
|
31
|
-
const reply = await (0, channel_multipart_1.submitRequestOperation)(operation, 'channel request');
|
|
31
|
+
const reply = await (0, channel_multipart_1.submitRequestOperation)({ submit: () => operation.submit().reply }, 'channel request');
|
|
32
32
|
try {
|
|
33
33
|
return (0, channel_envelope_1.decodeChannelReply)(reply);
|
|
34
34
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Socket } from '@zlink-systems/zlink';
|
|
2
|
+
/**
|
|
3
|
+
* Owns one binding public Poller and drives it from the socket's libuv
|
|
4
|
+
* readiness callback. PollCompletion is drained only by Poller.wait; the
|
|
5
|
+
* socket callback itself is only the event-loop wakeup.
|
|
6
|
+
*/
|
|
7
|
+
export declare class ZLinkNodeEventLoopPoller {
|
|
8
|
+
private readonly poller;
|
|
9
|
+
private readonly events;
|
|
10
|
+
private readableHandler;
|
|
11
|
+
private disposed;
|
|
12
|
+
constructor(socket: Socket, pollCompletion: boolean, readableHandler: () => void);
|
|
13
|
+
setReadableHandler(handler: () => void): void;
|
|
14
|
+
dispose(): void;
|
|
15
|
+
private readonly onReady;
|
|
16
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ZLinkNodeEventLoopPoller = void 0;
|
|
4
|
+
const node_backend_adapter_support_1 = require("./node-backend-adapter-support");
|
|
5
|
+
/**
|
|
6
|
+
* Owns one binding public Poller and drives it from the socket's libuv
|
|
7
|
+
* readiness callback. PollCompletion is drained only by Poller.wait; the
|
|
8
|
+
* socket callback itself is only the event-loop wakeup.
|
|
9
|
+
*/
|
|
10
|
+
class ZLinkNodeEventLoopPoller {
|
|
11
|
+
poller = node_backend_adapter_support_1.zlink.createPoller();
|
|
12
|
+
events = node_backend_adapter_support_1.zlink.createPollEvents(1);
|
|
13
|
+
readableHandler;
|
|
14
|
+
disposed = false;
|
|
15
|
+
constructor(socket, pollCompletion, readableHandler) {
|
|
16
|
+
this.readableHandler = readableHandler;
|
|
17
|
+
try {
|
|
18
|
+
const pollEvents = [node_backend_adapter_support_1.zlink.PollEventFlag.PollIn];
|
|
19
|
+
if (pollCompletion)
|
|
20
|
+
pollEvents.push(node_backend_adapter_support_1.zlink.PollEventFlag.PollCompletion);
|
|
21
|
+
this.poller.add(socket, pollEvents, 0);
|
|
22
|
+
socket.setReadableHandler(this.onReady);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
this.events.close();
|
|
26
|
+
this.poller.close();
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
setReadableHandler(handler) {
|
|
31
|
+
this.readableHandler = handler;
|
|
32
|
+
}
|
|
33
|
+
dispose() {
|
|
34
|
+
if (this.disposed)
|
|
35
|
+
return;
|
|
36
|
+
this.disposed = true;
|
|
37
|
+
this.events.close();
|
|
38
|
+
this.poller.close();
|
|
39
|
+
}
|
|
40
|
+
onReady = () => {
|
|
41
|
+
if (this.disposed)
|
|
42
|
+
return;
|
|
43
|
+
const ready = this.poller.wait(this.events, 0);
|
|
44
|
+
if (ready === 0 || this.events.hasEvent(0, node_backend_adapter_support_1.zlink.PollEventFlag.PollIn)) {
|
|
45
|
+
this.readableHandler();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
exports.ZLinkNodeEventLoopPoller = ZLinkNodeEventLoopPoller;
|
|
@@ -4,6 +4,7 @@ exports.ZLinkNodeRawBindingPort = void 0;
|
|
|
4
4
|
const zlink_1 = require("@zlink-systems/zlink");
|
|
5
5
|
const node_backend_adapter_support_1 = require("./node-backend-adapter-support");
|
|
6
6
|
const node_socket_backend_adapter_1 = require("./node-socket-backend-adapter");
|
|
7
|
+
const node_event_loop_poller_1 = require("./node-event-loop-poller");
|
|
7
8
|
class ZLinkNodeRawBindingPort {
|
|
8
9
|
sharedContext;
|
|
9
10
|
constructor(sharedContext) {
|
|
@@ -78,12 +79,15 @@ class NodeRawSocketPort {
|
|
|
78
79
|
socket;
|
|
79
80
|
endpoints = new Set();
|
|
80
81
|
monitors = new Set();
|
|
81
|
-
|
|
82
|
-
readableEvents = (0, zlink_1.createPollEvents)(1);
|
|
82
|
+
eventLoopPoller;
|
|
83
83
|
closed = false;
|
|
84
84
|
constructor(socket) {
|
|
85
85
|
this.socket = socket;
|
|
86
|
-
this.
|
|
86
|
+
this.eventLoopPoller = new node_event_loop_poller_1.ZLinkNodeEventLoopPoller(socket, true, () => { });
|
|
87
|
+
}
|
|
88
|
+
setReadableHandler(handler) {
|
|
89
|
+
this.requireOpen();
|
|
90
|
+
this.eventLoopPoller.setReadableHandler(handler);
|
|
87
91
|
}
|
|
88
92
|
bind(endpoint) {
|
|
89
93
|
this.requireOpen();
|
|
@@ -120,6 +124,12 @@ class NodeRawSocketPort {
|
|
|
120
124
|
if (this.closed)
|
|
121
125
|
return;
|
|
122
126
|
const failures = [];
|
|
127
|
+
try {
|
|
128
|
+
this.eventLoopPoller.dispose();
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
failures.push(error);
|
|
132
|
+
}
|
|
123
133
|
for (const monitor of [...this.monitors].reverse()) {
|
|
124
134
|
try {
|
|
125
135
|
monitor.close();
|
|
@@ -145,24 +155,6 @@ class NodeRawSocketPort {
|
|
|
145
155
|
}
|
|
146
156
|
}
|
|
147
157
|
this.endpoints.clear();
|
|
148
|
-
try {
|
|
149
|
-
this.readablePoller.remove(this.socket);
|
|
150
|
-
}
|
|
151
|
-
catch (error) {
|
|
152
|
-
failures.push(error);
|
|
153
|
-
}
|
|
154
|
-
try {
|
|
155
|
-
this.readableEvents.close();
|
|
156
|
-
}
|
|
157
|
-
catch (error) {
|
|
158
|
-
failures.push(error);
|
|
159
|
-
}
|
|
160
|
-
try {
|
|
161
|
-
this.readablePoller.close();
|
|
162
|
-
}
|
|
163
|
-
catch (error) {
|
|
164
|
-
failures.push(error);
|
|
165
|
-
}
|
|
166
158
|
try {
|
|
167
159
|
this.socket.close();
|
|
168
160
|
}
|
|
@@ -179,21 +171,6 @@ class NodeRawSocketPort {
|
|
|
179
171
|
if (this.closed)
|
|
180
172
|
throw new Error('Raw socket is closed.');
|
|
181
173
|
}
|
|
182
|
-
receiveRecord(dontWait) {
|
|
183
|
-
const timeoutMs = dontWait ? 0 : -1;
|
|
184
|
-
try {
|
|
185
|
-
if (this.readablePoller.wait(this.readableEvents, timeoutMs) <= 0
|
|
186
|
-
|| !this.readableEvents.hasEvent(0, zlink_1.PollEventFlag.PollIn)) {
|
|
187
|
-
return undefined;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
catch (error) {
|
|
191
|
-
if ((0, node_backend_adapter_support_1.isPollerInterruptedError)(error))
|
|
192
|
-
return undefined;
|
|
193
|
-
throw error;
|
|
194
|
-
}
|
|
195
|
-
return receiveRecord(this.socket, true);
|
|
196
|
-
}
|
|
197
174
|
}
|
|
198
175
|
class NodeRawRouterPort extends NodeRawSocketPort {
|
|
199
176
|
nextIncomingRequestSequence = 1n;
|
|
@@ -222,14 +199,17 @@ class NodeRawRouterPort extends NodeRawSocketPort {
|
|
|
222
199
|
}
|
|
223
200
|
async send(targetRid, parts) {
|
|
224
201
|
this.requireOpen();
|
|
225
|
-
|
|
202
|
+
const submission = appendSendParts(this.socket.send(bindingRoutingId(targetRid)), parts).submit();
|
|
203
|
+
if (submission.result === zlink_1.SubmitResult.Backpressured) {
|
|
204
|
+
await submission.admitted;
|
|
205
|
+
}
|
|
226
206
|
}
|
|
227
207
|
async request(targetRid, parts, timeoutMs) {
|
|
228
208
|
this.requireOpen();
|
|
229
209
|
try {
|
|
230
210
|
const replies = await appendRequestParts(this.socket.request(bindingRoutingId(targetRid)), parts)
|
|
231
211
|
.timeout(timeoutMs)
|
|
232
|
-
.submit();
|
|
212
|
+
.submit().reply;
|
|
233
213
|
return copyAndClose(replies);
|
|
234
214
|
}
|
|
235
215
|
catch (error) {
|
|
@@ -259,16 +239,19 @@ class NodeRawDealerPort extends NodeRawSocketPort {
|
|
|
259
239
|
}
|
|
260
240
|
async send(parts) {
|
|
261
241
|
this.requireOpen();
|
|
262
|
-
|
|
242
|
+
const submission = appendSendParts(this.socket.send(), parts).submit();
|
|
243
|
+
if (submission.result === zlink_1.SubmitResult.Backpressured) {
|
|
244
|
+
await submission.admitted;
|
|
245
|
+
}
|
|
263
246
|
}
|
|
264
247
|
async request(parts, timeoutMs) {
|
|
265
248
|
this.requireOpen();
|
|
266
|
-
const replies = await appendRequestParts(this.socket.request(), parts).timeout(timeoutMs).submit();
|
|
249
|
+
const replies = await appendRequestParts(this.socket.request(), parts).timeout(timeoutMs).submit().reply;
|
|
267
250
|
return copyAndClose(replies);
|
|
268
251
|
}
|
|
269
252
|
receive(dontWait = false) {
|
|
270
253
|
this.requireOpen();
|
|
271
|
-
return this.
|
|
254
|
+
return receiveRecord(this.socket, dontWait);
|
|
272
255
|
}
|
|
273
256
|
}
|
|
274
257
|
class NodeRawMonitorPort {
|
|
@@ -36,7 +36,11 @@ export declare class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
|
|
|
36
36
|
private runtime?;
|
|
37
37
|
private stateful?;
|
|
38
38
|
private readyHandler?;
|
|
39
|
-
private
|
|
39
|
+
private maintenanceTimer?;
|
|
40
|
+
private pumping;
|
|
41
|
+
private readable;
|
|
42
|
+
private readonly onReadable;
|
|
43
|
+
private readonly onMaintenance;
|
|
40
44
|
private nextPeerIntent;
|
|
41
45
|
private closed;
|
|
42
46
|
private objectRole;
|
|
@@ -49,11 +53,6 @@ export declare class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
|
|
|
49
53
|
private protocolError?;
|
|
50
54
|
private messageFollowHandler?;
|
|
51
55
|
private readonly peerDisconnectedHandlers;
|
|
52
|
-
private readonly receiveBatchBudget;
|
|
53
|
-
private observedPumpSourceRoutingId?;
|
|
54
|
-
private observedPumpByteCount;
|
|
55
|
-
private readonly observePump;
|
|
56
|
-
private takePumpObservation;
|
|
57
56
|
constructor(meshName: string, routingId: string | undefined, bindingPort: ZLinkRawBindingPort, applicationJobQueue?: ApplicationJobQueuePort | undefined, applicationJobReceiveFlowFailureSink?: ((error: unknown) => void) | undefined, peerAdmissionSealed?: (() => boolean) | undefined);
|
|
58
57
|
configureObjectPlacement(options: {
|
|
59
58
|
readonly role: ServiceNodeDescriptor['objectRole'];
|
|
@@ -239,8 +238,9 @@ export declare class ZLinkNodeRawMeshBackend implements ZLinkBackendMeshNode {
|
|
|
239
238
|
clearSpotSubscriptions(state: ServiceSpotState): void;
|
|
240
239
|
private createDescriptor;
|
|
241
240
|
private resolveAdvertisedEndpoint;
|
|
242
|
-
private
|
|
243
|
-
private
|
|
241
|
+
private scheduleMaintenance;
|
|
242
|
+
private pump;
|
|
243
|
+
private isClosed;
|
|
244
244
|
private notifyReady;
|
|
245
245
|
private observeCompletion;
|
|
246
246
|
private observeStateful;
|
|
@@ -3,11 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ZLinkNodeRawMeshBackend = void 0;
|
|
4
4
|
exports.genericOperationFailure = genericOperationFailure;
|
|
5
5
|
const node_crypto_1 = require("node:crypto");
|
|
6
|
+
const promises_1 = require("node:timers/promises");
|
|
6
7
|
const node_socket_backend_adapter_1 = require("./node-socket-backend-adapter");
|
|
7
8
|
const contracts_1 = require("../../../contracts");
|
|
8
9
|
const framework_errors_internal_1 = require("../../framework-errors-internal");
|
|
9
10
|
const zlink_1 = require("@zlink-systems/zlink");
|
|
10
11
|
const runtime_values_1 = require("../runtime-values");
|
|
12
|
+
const runtime_message_1 = require("../runtime-message");
|
|
11
13
|
const service_runtime_contracts_1 = require("../../foundation/service-runtime-contracts");
|
|
12
14
|
const operation_registry_1 = require("../../foundation/operation-registry");
|
|
13
15
|
const raw_service_mesh_runtime_1 = require("../../foundation/raw-service-mesh-runtime");
|
|
@@ -22,37 +24,15 @@ const MULTIPART_PACKET_NAME = service_wire_constants_generated_1.SERVICE_FRAMEWO
|
|
|
22
24
|
const MULTIPART_CONTENT_TYPE = service_wire_constants_generated_1.SERVICE_FRAMEWORK_MULTIPART_CONTENT_TYPE;
|
|
23
25
|
const FATAL_UTF8 = new TextDecoder('utf-8', { fatal: true });
|
|
24
26
|
const MAX_DRAIN_RECORDS = 64;
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
const MESH_BACKEND_IDLE_POLL_INTERVAL_MS = 1;
|
|
29
|
-
const MESH_RECEIVE_BATCH_BYTE_LIMIT = 4 * 1024 * 1024;
|
|
30
|
-
const MESH_RECEIVE_BATCH_TIME_LIMIT_MS = 2;
|
|
27
|
+
// Preserve the existing monitor/admission/liveness cadence. Only the binding
|
|
28
|
+
// readable handler admits receive work; this timer never probes the socket.
|
|
29
|
+
const MESH_BACKEND_MAINTENANCE_INTERVAL_MS = 1;
|
|
31
30
|
/**
|
|
32
31
|
* Conservative Actor Join admission cap for relocation state chunks (spec 15
|
|
33
32
|
* §4.2): a stable lower bound safe on any deployment, never lowered on
|
|
34
33
|
* recompute. Matches the source's own conservative chunk floor.
|
|
35
34
|
*/
|
|
36
35
|
const ACTOR_JOIN_ADVERTISED_RECEIVE_CHUNK_LIMIT_BYTES = 32 * 1024;
|
|
37
|
-
class ZLinkMeshReceiveBatchBudget {
|
|
38
|
-
peerMessages = new Map();
|
|
39
|
-
peerBytes = new Map();
|
|
40
|
-
startedAtMs = 0;
|
|
41
|
-
reset(nowMs) {
|
|
42
|
-
this.peerMessages.clear();
|
|
43
|
-
this.peerBytes.clear();
|
|
44
|
-
this.startedAtMs = nowMs;
|
|
45
|
-
}
|
|
46
|
-
record(peer, byteCount, nowMs) {
|
|
47
|
-
const messages = (this.peerMessages.get(peer) ?? 0) + 1;
|
|
48
|
-
const bytes = (this.peerBytes.get(peer) ?? 0) + byteCount;
|
|
49
|
-
this.peerMessages.set(peer, messages);
|
|
50
|
-
this.peerBytes.set(peer, bytes);
|
|
51
|
-
return messages >= MAX_DRAIN_RECORDS
|
|
52
|
-
|| bytes >= MESH_RECEIVE_BATCH_BYTE_LIMIT
|
|
53
|
-
|| nowMs - this.startedAtMs >= MESH_RECEIVE_BATCH_TIME_LIMIT_MS;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
36
|
/**
|
|
57
37
|
* M6A MeshNode backend. Stateful Spot/Actor entry points stay explicit until
|
|
58
38
|
* their owning M6B runtime is connected; topology and node/channel dispatch do
|
|
@@ -77,7 +57,18 @@ class ZLinkNodeRawMeshBackend {
|
|
|
77
57
|
runtime;
|
|
78
58
|
stateful;
|
|
79
59
|
readyHandler;
|
|
80
|
-
|
|
60
|
+
maintenanceTimer;
|
|
61
|
+
pumping = false;
|
|
62
|
+
readable = false;
|
|
63
|
+
onReadable = () => {
|
|
64
|
+
this.readable = true;
|
|
65
|
+
if (!this.closed && !this.pumping)
|
|
66
|
+
void this.pump();
|
|
67
|
+
};
|
|
68
|
+
onMaintenance = () => {
|
|
69
|
+
this.maintenanceTimer = undefined;
|
|
70
|
+
void this.pump();
|
|
71
|
+
};
|
|
81
72
|
nextPeerIntent = 1n;
|
|
82
73
|
closed = false;
|
|
83
74
|
objectRole = 'none';
|
|
@@ -90,19 +81,6 @@ class ZLinkNodeRawMeshBackend {
|
|
|
90
81
|
protocolError;
|
|
91
82
|
messageFollowHandler;
|
|
92
83
|
peerDisconnectedHandlers = new Set();
|
|
93
|
-
receiveBatchBudget = new ZLinkMeshReceiveBatchBudget();
|
|
94
|
-
observedPumpSourceRoutingId;
|
|
95
|
-
observedPumpByteCount = 0;
|
|
96
|
-
observePump = (sourceRoutingId, byteCount) => {
|
|
97
|
-
this.observedPumpSourceRoutingId = sourceRoutingId;
|
|
98
|
-
this.observedPumpByteCount = byteCount;
|
|
99
|
-
};
|
|
100
|
-
takePumpObservation() {
|
|
101
|
-
const sourceRoutingId = this.observedPumpSourceRoutingId;
|
|
102
|
-
if (sourceRoutingId === undefined)
|
|
103
|
-
return undefined;
|
|
104
|
-
return { sourceRoutingId, byteCount: this.observedPumpByteCount };
|
|
105
|
-
}
|
|
106
84
|
constructor(meshName, routingId, bindingPort, applicationJobQueue, applicationJobReceiveFlowFailureSink, peerAdmissionSealed) {
|
|
107
85
|
this.meshName = meshName;
|
|
108
86
|
this.bindingPort = bindingPort;
|
|
@@ -232,7 +210,14 @@ class ZLinkNodeRawMeshBackend {
|
|
|
232
210
|
}
|
|
233
211
|
this.stateful.setMessageFollowHandler((record) => this.messageFollowHandler?.(record));
|
|
234
212
|
this.runtime = runtime;
|
|
235
|
-
|
|
213
|
+
try {
|
|
214
|
+
runtime.setReadableHandler(this.onReadable);
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
this.close();
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
this.scheduleMaintenance();
|
|
236
221
|
}
|
|
237
222
|
setMessageFollowHandler(handler) {
|
|
238
223
|
this.messageFollowHandler = handler;
|
|
@@ -253,9 +238,9 @@ class ZLinkNodeRawMeshBackend {
|
|
|
253
238
|
if (this.closed)
|
|
254
239
|
return;
|
|
255
240
|
this.closed = true;
|
|
256
|
-
if (this.
|
|
257
|
-
clearTimeout(this.
|
|
258
|
-
this.
|
|
241
|
+
if (this.maintenanceTimer !== undefined)
|
|
242
|
+
clearTimeout(this.maintenanceTimer);
|
|
243
|
+
this.maintenanceTimer = undefined;
|
|
259
244
|
this.stateful?.close();
|
|
260
245
|
this.stateful = undefined;
|
|
261
246
|
this.runtime?.close();
|
|
@@ -372,7 +357,7 @@ class ZLinkNodeRawMeshBackend {
|
|
|
372
357
|
return this.requireRuntime().isPeerRouteReady(String(targetRid), lifecycleGeneration);
|
|
373
358
|
}
|
|
374
359
|
async sendToNode(targetRid, parts) {
|
|
375
|
-
return await this.requireRuntime().sendToNode(String(targetRid),
|
|
360
|
+
return await this.requireRuntime().sendToNode(String(targetRid), encodeMultipartApplicationFrame(parts)) ? zlink_1.SubmitResult.Ok : zlink_1.SubmitResult.NotConnected;
|
|
376
361
|
}
|
|
377
362
|
async sendInfrastructureControl(targetRid, record) {
|
|
378
363
|
return await this.requireRuntime().sendService(String(targetRid), (Array.isArray(record) ? record : [record]).map(value => Buffer.from(value))) ? zlink_1.SubmitResult.Ok : zlink_1.SubmitResult.NotConnected;
|
|
@@ -384,14 +369,14 @@ class ZLinkNodeRawMeshBackend {
|
|
|
384
369
|
return await this.requireRuntime().requestService(String(targetRid), frames.map(frame => Buffer.from(frame)), options?.timeoutMs ?? 30_000);
|
|
385
370
|
}
|
|
386
371
|
requestToNode(targetRid, parts, options) {
|
|
387
|
-
const pending = this.requireRuntime().requestToNode(String(targetRid),
|
|
372
|
+
const pending = this.requireRuntime().requestToNode(String(targetRid), encodeMultipartApplicationFrame(parts), options?.timeoutMs ?? 30_000);
|
|
388
373
|
return this.observeCompletion(pending.id, service_runtime_contracts_1.OperationKind.NodeRequest, pending.promise);
|
|
389
374
|
}
|
|
390
375
|
async sendToChannel(channelName, parts) {
|
|
391
|
-
return await this.requireRuntime().sendToChannel(channelName,
|
|
376
|
+
return await this.requireRuntime().sendToChannel(channelName, encodeMultipartApplicationFrame(parts)) ? zlink_1.SubmitResult.Ok : zlink_1.SubmitResult.NotConnected;
|
|
392
377
|
}
|
|
393
378
|
requestToChannel(channelName, parts, options) {
|
|
394
|
-
const pending = this.requireRuntime().requestToChannel(channelName,
|
|
379
|
+
const pending = this.requireRuntime().requestToChannel(channelName, encodeMultipartApplicationFrame(parts), options?.timeoutMs ?? 30_000);
|
|
395
380
|
if (pending === undefined) {
|
|
396
381
|
return this.enqueueImmediateFailure(service_runtime_contracts_1.OperationKind.ChannelRequest, zlink_1.RequestResult.NotFound);
|
|
397
382
|
}
|
|
@@ -768,49 +753,38 @@ class ZLinkNodeRawMeshBackend {
|
|
|
768
753
|
}
|
|
769
754
|
return result;
|
|
770
755
|
}
|
|
771
|
-
|
|
772
|
-
if (this.closed || this.
|
|
756
|
+
scheduleMaintenance() {
|
|
757
|
+
if (this.closed || this.maintenanceTimer !== undefined)
|
|
773
758
|
return;
|
|
774
|
-
this.
|
|
775
|
-
this.pollTimer = undefined;
|
|
776
|
-
try {
|
|
777
|
-
const received = await this.poll();
|
|
778
|
-
// A zero-delay timer yields to I/O and other ready work first without
|
|
779
|
-
// inserting the idle polling interval between readable batches.
|
|
780
|
-
delayMs = received ? 0 : MESH_BACKEND_IDLE_POLL_INTERVAL_MS;
|
|
781
|
-
}
|
|
782
|
-
finally {
|
|
783
|
-
if (!this.closed)
|
|
784
|
-
this.schedulePoll(delayMs);
|
|
785
|
-
}
|
|
786
|
-
}, delayMs);
|
|
759
|
+
this.maintenanceTimer = setTimeout(this.onMaintenance, MESH_BACKEND_MAINTENANCE_INTERVAL_MS);
|
|
787
760
|
}
|
|
788
|
-
async
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
this.
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
&& this.receiveBatchBudget.record(observation.sourceRoutingId, observation.byteCount, performance.now()))
|
|
808
|
-
break;
|
|
761
|
+
async pump() {
|
|
762
|
+
if (this.isClosed() || this.pumping)
|
|
763
|
+
return;
|
|
764
|
+
this.pumping = true;
|
|
765
|
+
if (this.maintenanceTimer !== undefined)
|
|
766
|
+
clearTimeout(this.maintenanceTimer);
|
|
767
|
+
this.maintenanceTimer = undefined;
|
|
768
|
+
try {
|
|
769
|
+
do {
|
|
770
|
+
const receiveReady = this.readable;
|
|
771
|
+
this.readable = false;
|
|
772
|
+
const more = await this.requireRuntime().pumpBatch(receiveReady);
|
|
773
|
+
if (more) {
|
|
774
|
+
// Yield only after actual progress, retaining readiness across the
|
|
775
|
+
// existing batch limits until receive reports no data.
|
|
776
|
+
this.readable = true;
|
|
777
|
+
await (0, promises_1.setImmediate)();
|
|
778
|
+
}
|
|
779
|
+
} while (!this.closed && this.readable);
|
|
809
780
|
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
781
|
+
finally {
|
|
782
|
+
this.pumping = false;
|
|
783
|
+
this.scheduleMaintenance();
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
isClosed() {
|
|
787
|
+
return this.closed;
|
|
814
788
|
}
|
|
815
789
|
notifyReady() {
|
|
816
790
|
const runtime = this.runtime;
|
|
@@ -892,14 +866,7 @@ class ZLinkNodeRawMeshBackend {
|
|
|
892
866
|
if (claim === undefined)
|
|
893
867
|
break;
|
|
894
868
|
const owner = readyOwner(claim.owner, this.routingId, this.stateful, domain);
|
|
895
|
-
batch.push(owner, new MailboxClaim(runtime, claim
|
|
896
|
-
// A receive batch may intentionally consume fewer records than the
|
|
897
|
-
// mailbox claim. Releasing the claim re-queues the remainder, so
|
|
898
|
-
// expose that newly ready work to the dispatch pump.
|
|
899
|
-
if (runtime.mailbox.pendingMessages(domain) > 0) {
|
|
900
|
-
this.readyHandler?.(domain === 'infrastructure' ? service_runtime_contracts_1.ReadyDomain.Infrastructure : service_runtime_contracts_1.ReadyDomain.Application);
|
|
901
|
-
}
|
|
902
|
-
}));
|
|
869
|
+
batch.push(owner, new MailboxClaim(runtime, claim));
|
|
903
870
|
}
|
|
904
871
|
}
|
|
905
872
|
requireRuntime() {
|
|
@@ -1100,7 +1067,7 @@ class RawStreamSessionService {
|
|
|
1100
1067
|
submit = submit.message(parts[index]);
|
|
1101
1068
|
}
|
|
1102
1069
|
try {
|
|
1103
|
-
await submit.submit();
|
|
1070
|
+
await submit.submit().admitted;
|
|
1104
1071
|
return true;
|
|
1105
1072
|
}
|
|
1106
1073
|
catch (error) {
|
|
@@ -1161,29 +1128,37 @@ class RawReadyBatch {
|
|
|
1161
1128
|
}
|
|
1162
1129
|
}
|
|
1163
1130
|
class RawReceiveBatch {
|
|
1164
|
-
|
|
1131
|
+
capacity;
|
|
1165
1132
|
partCapacity;
|
|
1166
|
-
|
|
1167
|
-
|
|
1133
|
+
receiveCapacity;
|
|
1134
|
+
constructor(capacity, partCapacity) {
|
|
1135
|
+
this.capacity = capacity;
|
|
1168
1136
|
this.partCapacity = partCapacity;
|
|
1169
|
-
if ([
|
|
1137
|
+
if ([capacity, partCapacity].some(value => !Number.isInteger(value) || value < 1)) {
|
|
1170
1138
|
throw new RangeError('Receive batch capacities must be positive.');
|
|
1171
1139
|
}
|
|
1140
|
+
this.receiveCapacity = capacity;
|
|
1141
|
+
}
|
|
1142
|
+
get messageCapacity() {
|
|
1143
|
+
return this.receiveCapacity;
|
|
1144
|
+
}
|
|
1145
|
+
reset(messageCapacity = this.capacity) {
|
|
1146
|
+
if (!Number.isInteger(messageCapacity) || messageCapacity < 1 || messageCapacity > this.capacity) {
|
|
1147
|
+
throw new RangeError('Receive limit must be within the batch capacity.');
|
|
1148
|
+
}
|
|
1149
|
+
this.receiveCapacity = messageCapacity;
|
|
1172
1150
|
}
|
|
1173
|
-
reset() { }
|
|
1174
1151
|
close() { }
|
|
1175
1152
|
}
|
|
1176
1153
|
class MailboxClaim {
|
|
1177
1154
|
runtime;
|
|
1178
1155
|
claim;
|
|
1179
|
-
onRelease;
|
|
1180
1156
|
consumed = false;
|
|
1181
1157
|
released = false;
|
|
1182
1158
|
remaining = [];
|
|
1183
|
-
constructor(runtime, claim
|
|
1159
|
+
constructor(runtime, claim) {
|
|
1184
1160
|
this.runtime = runtime;
|
|
1185
1161
|
this.claim = claim;
|
|
1186
|
-
this.onRelease = onRelease;
|
|
1187
1162
|
}
|
|
1188
1163
|
recvBatch(batch) {
|
|
1189
1164
|
if (this.consumed)
|
|
@@ -1206,6 +1181,13 @@ class MailboxClaim {
|
|
|
1206
1181
|
}
|
|
1207
1182
|
this.runtime.mailbox.releaseClaimedPayload(record);
|
|
1208
1183
|
this.remaining = this.claim.records.slice(index + 1);
|
|
1184
|
+
// Nothing was transferred to the receive caller when decoding throws.
|
|
1185
|
+
// Earlier records therefore still belong to this failed batch.
|
|
1186
|
+
for (const decoded of records) {
|
|
1187
|
+
for (const part of decoded.parts)
|
|
1188
|
+
part.close();
|
|
1189
|
+
decoded.releaseRetainedIngress?.();
|
|
1190
|
+
}
|
|
1209
1191
|
throw error;
|
|
1210
1192
|
}
|
|
1211
1193
|
const nextParts = decoded.parts.length;
|
|
@@ -1227,7 +1209,6 @@ class MailboxClaim {
|
|
|
1227
1209
|
return;
|
|
1228
1210
|
this.released = true;
|
|
1229
1211
|
this.runtime.mailbox.release(this.claim, this.remaining);
|
|
1230
|
-
this.onRelease?.();
|
|
1231
1212
|
}
|
|
1232
1213
|
}
|
|
1233
1214
|
class CompletionClaim {
|
|
@@ -1371,7 +1352,7 @@ function decodeMultipartRecord(runtime, record) {
|
|
|
1371
1352
|
reply(parts) {
|
|
1372
1353
|
if (record.correlation === undefined)
|
|
1373
1354
|
return zlink_1.SubmitResult.InvalidState;
|
|
1374
|
-
runtime.reply(record,
|
|
1355
|
+
runtime.reply(record, encodeMultipartApplicationFrame(parts));
|
|
1375
1356
|
return zlink_1.SubmitResult.Ok;
|
|
1376
1357
|
},
|
|
1377
1358
|
replyActorJoin: () => zlink_1.SubmitResult.NotSupported
|
|
@@ -1630,7 +1611,19 @@ function decodeApplicationEnvelope(frame) {
|
|
|
1630
1611
|
}
|
|
1631
1612
|
function decodeMultipart(payload) {
|
|
1632
1613
|
const buffers = decodeMultipartBuffers(payload);
|
|
1633
|
-
|
|
1614
|
+
const parts = new Array(buffers.length);
|
|
1615
|
+
let materialized = 0;
|
|
1616
|
+
try {
|
|
1617
|
+
for (; materialized < buffers.length; materialized += 1) {
|
|
1618
|
+
parts[materialized] = runtime_message_1.ZLinkBufferMessage.fromOwned(buffers[materialized]);
|
|
1619
|
+
}
|
|
1620
|
+
return parts;
|
|
1621
|
+
}
|
|
1622
|
+
catch (error) {
|
|
1623
|
+
for (let index = 0; index < materialized; index += 1)
|
|
1624
|
+
parts[index].close();
|
|
1625
|
+
throw error;
|
|
1626
|
+
}
|
|
1634
1627
|
}
|
|
1635
1628
|
function decodeMultipartBuffers(payload) {
|
|
1636
1629
|
const bytes = Buffer.from(payload.buffer, payload.byteOffset, payload.byteLength);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { ZLinkBackendObject } from '../contracts';
|
|
2
|
+
import { ZLinkNodeEventLoopPoller } from './node-event-loop-poller';
|
|
2
3
|
export declare function wrapSocket<T extends {
|
|
3
4
|
close(): void;
|
|
4
|
-
}>(nativeInstance: T): T & ZLinkBackendObject;
|
|
5
|
+
}>(nativeInstance: T, pollCompletion?: boolean): T & ZLinkBackendObject;
|
|
6
|
+
export declare function nodeEventLoopPollerOf(socket: object): ZLinkNodeEventLoopPoller;
|
|
5
7
|
export declare function disconnectStreamPeer(socket: {
|
|
6
8
|
disconnectRid(routingId: unknown): void;
|
|
7
9
|
}, routingId: unknown): void;
|
|
8
|
-
export declare function isDisconnectRouteNotFoundError(error: unknown): boolean;
|
|
9
10
|
export declare function isEndpointCloseIgnorableError(error: unknown): boolean;
|