@zlink-systems/framework 0.30.0 → 0.30.1
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/LICENSE +150 -68
- package/dist/.tsbuildinfo +1 -1
- package/dist/contracts/Actors/ZLinkActorClient.d.ts +2 -1
- package/dist/contracts/Channels/Calls.d.ts +2 -1
- package/dist/contracts/Common/ZLinkMessageMetadata.js +11 -5
- package/dist/contracts/Handlers/Attributes.d.ts +5 -13
- package/dist/contracts/Handlers/Attributes.js +7 -11
- package/dist/contracts/Handlers/JsonContract.d.ts +2 -46
- package/dist/contracts/Handlers/JsonContract.js +4 -153
- package/dist/contracts/Spots/Contracts.d.ts +2 -1
- package/dist/nest-integration.d.ts +1 -1
- package/dist/nest-integration.js +2 -1
- package/dist/runtime/actors/actor-client.d.ts +1 -1
- package/dist/runtime/actors/actor-client.js +14 -12
- package/dist/runtime/actors/actor-handoff.d.ts +1 -1
- package/dist/runtime/actors/actor-handoff.js +7 -4
- package/dist/runtime/actors/actor-join-relocation.d.ts +1 -0
- package/dist/runtime/actors/actor-local-native-join.js +9 -12
- package/dist/runtime/actors/actor-remote-wire.js +15 -0
- package/dist/runtime/actors/actor-routed-join-transport.d.ts +6 -2
- package/dist/runtime/actors/actor-routed-json-request.d.ts +2 -0
- package/dist/runtime/actors/index.d.ts +2 -1
- package/dist/runtime/actors/index.js +18 -6
- package/dist/runtime/actors/spot-actor-dispatch.js +2 -2
- package/dist/runtime/admission.d.ts +1 -1
- package/dist/runtime/admission.js +15 -5
- package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +6 -8
- package/dist/runtime/application-jobs/application-ingress-record-owner.js +39 -22
- package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +2 -1
- package/dist/runtime/application-jobs/application-job-queue-scope.js +37 -22
- package/dist/runtime/application-jobs/contracts.d.ts +10 -1
- package/dist/runtime/backend/contracts/index.d.ts +18 -9
- package/dist/runtime/backend/mesh-completion-table.d.ts +9 -1
- package/dist/runtime/backend/mesh-completion-table.js +149 -49
- package/dist/runtime/backend/mesh-dispatch-pump.d.ts +3 -0
- package/dist/runtime/backend/mesh-dispatch-pump.js +32 -11
- package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +2 -2
- package/dist/runtime/backend/node/node-backend-adapter-support.js +16 -9
- package/dist/runtime/backend/node/node-dealer-channel-client-transport.d.ts +1 -1
- package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +5 -2
- package/dist/runtime/backend/node/node-raw-binding-port.js +42 -22
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +13 -5
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +69 -51
- package/dist/runtime/backend/node/node-socket-backend-adapter.js +6 -6
- package/dist/runtime/backend/raw-binding-port.d.ts +2 -2
- package/dist/runtime/backend/runtime-message.d.ts +1 -0
- package/dist/runtime/backend/runtime-message.js +5 -0
- package/dist/runtime/backend/runtime-values.d.ts +1 -0
- package/dist/runtime/backend/runtime-values.js +2 -1
- package/dist/runtime/channels/channel-clients.d.ts +1 -0
- package/dist/runtime/channels/channel-clients.js +58 -21
- package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +3 -2
- package/dist/runtime/channels/channel-dispatch-pipeline.js +20 -11
- package/dist/runtime/channels/channel-dispatch-services.d.ts +1 -0
- package/dist/runtime/channels/channel-dispatch-services.js +45 -0
- package/dist/runtime/channels/channel-dispatchers.d.ts +1 -1
- package/dist/runtime/channels/channel-dispatchers.js +27 -26
- package/dist/runtime/channels/channel-envelope-inspection.d.ts +1 -2
- package/dist/runtime/channels/channel-envelope-inspection.js +1 -16
- package/dist/runtime/channels/channel-envelope.d.ts +1 -0
- package/dist/runtime/channels/channel-envelope.js +83 -8
- package/dist/runtime/channels/channel-outbound-operations.d.ts +2 -2
- package/dist/runtime/channels/channel-outbound-operations.js +8 -8
- package/dist/runtime/channels/channel-runtime-manager.d.ts +6 -6
- package/dist/runtime/channels/channel-runtime-manager.js +12 -12
- package/dist/runtime/channels/channel-transports.d.ts +18 -15
- package/dist/runtime/channels/channel-transports.js +48 -31
- package/dist/runtime/channels/local-spot-route-transport.d.ts +1 -1
- package/dist/runtime/channels/local-spot-route-transport.js +2 -1
- package/dist/runtime/channels/router-socket-spot-route-transport.d.ts +2 -2
- package/dist/runtime/channels/router-socket-spot-route-transport.js +4 -4
- package/dist/runtime/channels/source-spot-router.d.ts +2 -2
- package/dist/runtime/channels/source-spot-router.js +4 -4
- package/dist/runtime/channels/spot-node-route-transport.d.ts +2 -2
- package/dist/runtime/channels/spot-node-route-transport.js +4 -4
- package/dist/runtime/channels/spot-route-bridge-transport.d.ts +2 -2
- package/dist/runtime/channels/spot-route-bridge-transport.js +4 -4
- package/dist/runtime/channels/spot-route-dispatch-strategy.d.ts +5 -4
- package/dist/runtime/channels/spot-route-dispatch-strategy.js +18 -18
- package/dist/runtime/execution/serial-execution-queue.d.ts +1 -0
- package/dist/runtime/execution/serial-execution-queue.js +1 -0
- package/dist/runtime/foundation/actor-join-recovery-codec.js +2 -2
- package/dist/runtime/foundation/durable-service-request.d.ts +14 -0
- package/dist/runtime/foundation/durable-service-request.js +98 -0
- package/dist/runtime/foundation/operation-identity.d.ts +1 -0
- package/dist/runtime/foundation/operation-identity.js +5 -1
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +22 -5
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +356 -232
- package/dist/runtime/foundation/service-relocation-object-owner.d.ts +1 -0
- package/dist/runtime/foundation/service-relocation-object-owner.js +1 -0
- package/dist/runtime/foundation/service-relocation-runtime.d.ts +2 -0
- package/dist/runtime/foundation/service-relocation-runtime.js +13 -1
- package/dist/runtime/foundation/service-runtime-contracts.d.ts +2 -0
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +4 -4
- package/dist/runtime/foundation/service-stateful-runtime.js +44 -103
- package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +0 -5
- package/dist/runtime/foundation/service-topology-registry.d.ts +12 -2
- package/dist/runtime/foundation/service-topology-registry.js +61 -27
- package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +3 -3
- package/dist/runtime/foundation/service-wire-m6a-codec.js +36 -38
- package/dist/runtime/handlers/handler-instance-scope.d.ts +8 -0
- package/dist/runtime/handlers/handler-instance-scope.js +54 -11
- package/dist/runtime/host/actor-runtime-options-factory.js +2 -2
- package/dist/runtime/host/application-job-queue.d.ts +19 -7
- package/dist/runtime/host/application-job-queue.js +150 -42
- package/dist/runtime/host/entry-actor-runtime.d.ts +1 -0
- package/dist/runtime/host/entry-actor-runtime.js +3 -0
- package/dist/runtime/host/index.d.ts +7 -1
- package/dist/runtime/host/index.js +142 -118
- package/dist/runtime/host/remote-bound-session-relay.js +2 -18
- package/dist/runtime/host/route-mesh-runtime.js +1 -1
- package/dist/runtime/host/service-relocation-control.d.ts +1 -0
- package/dist/runtime/host/service-relocation-control.js +10 -4
- package/dist/runtime/host/service-relocation-host-runtime.d.ts +6 -8
- package/dist/runtime/host/service-relocation-host-runtime.js +213 -241
- package/dist/runtime/host/spot-address-transport.js +3 -2
- package/dist/runtime/host/spot-runtime-options-factory.js +1 -0
- package/dist/runtime/locations/actor-location-claims.d.ts +2 -2
- package/dist/runtime/locations/actor-location-claims.js +34 -19
- package/dist/runtime/locations/lifecycle.d.ts +1 -2
- package/dist/runtime/locations/lifecycle.js +2 -5
- package/dist/runtime/locations/runtime.d.ts +1 -1
- package/dist/runtime/locations/runtime.js +24 -23
- package/dist/runtime/messaging/framework-json-v1.d.ts +4 -6
- package/dist/runtime/messaging/framework-json-v1.js +38 -294
- package/dist/runtime/messaging/packet-name.d.ts +1 -1
- package/dist/runtime/messaging/packet-name.js +8 -24
- package/dist/runtime/messaging/request-result.d.ts +2 -0
- package/dist/runtime/messaging/request-result.js +54 -0
- package/dist/runtime/messaging/submission-result.d.ts +1 -0
- package/dist/runtime/messaging/submission-result.js +5 -2
- package/dist/runtime/protocol/service_wire_codec.generated.d.ts +15 -0
- package/dist/runtime/protocol/service_wire_codec.generated.js +144 -0
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.d.ts +9 -0
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +14 -1
- package/dist/runtime/spots/formal-remote-actor-admission-registry.d.ts +7 -2
- package/dist/runtime/spots/formal-remote-actor-admission-registry.js +33 -19
- package/dist/runtime/spots/index.d.ts +3 -0
- package/dist/runtime/spots/index.js +9 -2
- package/dist/runtime/spots/spot-activation.d.ts +1 -0
- package/dist/runtime/spots/spot-activation.js +17 -8
- package/dist/runtime/spots/spot-entry-activation.js +6 -2
- package/dist/runtime/spots/spot-handler-registry.d.ts +3 -1
- package/dist/runtime/spots/spot-handler-registry.js +18 -8
- package/dist/runtime/spots/spot-node-autoconnect.js +4 -3
- package/dist/runtime/spots/spot-outbound.d.ts +3 -0
- package/dist/runtime/spots/spot-outbound.js +24 -15
- package/dist/runtime/spots/spot-routed-spot-packet-dispatch.d.ts +1 -0
- package/dist/runtime/spots/spot-routed-spot-packet-dispatch.js +1 -1
- package/dist/runtime/spots/spot-runtime-ports.d.ts +1 -0
- package/dist/runtime/spots/spot-subscription-dispatch.js +0 -3
- package/dist/runtime/spots/spot-timer.js +1 -0
- package/dist/runtime/streams/stream-session-runtime.d.ts +1 -1
- package/dist/runtime/streams/stream-session-runtime.js +13 -27
- package/package.json +2 -2
- package/src/contracts/Actors/ZLinkActorClient.ts +2 -1
- package/src/contracts/Channels/Calls.ts +2 -1
- package/src/contracts/Common/ZLinkMessageMetadata.ts +13 -8
- package/src/contracts/Handlers/Attributes.ts +14 -21
- package/src/contracts/Handlers/JsonContract.ts +8 -216
- package/src/contracts/Spots/Contracts.ts +2 -1
- package/src/nest-integration.ts +1 -0
- package/src/runtime/actors/actor-client.ts +40 -16
- package/src/runtime/actors/actor-handoff.ts +9 -4
- package/src/runtime/actors/actor-join-relocation.ts +1 -0
- package/src/runtime/actors/actor-local-native-join.ts +17 -26
- package/src/runtime/actors/actor-remote-wire.ts +17 -0
- package/src/runtime/actors/actor-routed-join-transport.ts +14 -4
- package/src/runtime/actors/actor-routed-json-request.ts +10 -2
- package/src/runtime/actors/index.ts +19 -5
- package/src/runtime/actors/spot-actor-dispatch.ts +4 -2
- package/src/runtime/admission.ts +18 -5
- package/src/runtime/application-jobs/application-ingress-record-owner.ts +41 -25
- package/src/runtime/application-jobs/application-job-queue-scope.ts +51 -22
- package/src/runtime/application-jobs/contracts.ts +14 -1
- package/src/runtime/backend/contracts/index.ts +53 -14
- package/src/runtime/backend/mesh-completion-table.ts +156 -57
- package/src/runtime/backend/mesh-dispatch-pump.ts +40 -10
- package/src/runtime/backend/node/node-backend-adapter-support.ts +17 -8
- package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +12 -2
- package/src/runtime/backend/node/node-raw-binding-port.ts +57 -30
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +128 -65
- package/src/runtime/backend/node/node-socket-backend-adapter.ts +21 -6
- package/src/runtime/backend/raw-binding-port.ts +7 -2
- package/src/runtime/backend/runtime-message.ts +6 -0
- package/src/runtime/backend/runtime-values.ts +1 -0
- package/src/runtime/channels/channel-clients.ts +90 -41
- package/src/runtime/channels/channel-dispatch-pipeline.ts +69 -34
- package/src/runtime/channels/channel-dispatch-services.ts +69 -1
- package/src/runtime/channels/channel-dispatchers.ts +29 -27
- package/src/runtime/channels/channel-envelope-inspection.ts +2 -16
- package/src/runtime/channels/channel-envelope.ts +133 -8
- package/src/runtime/channels/channel-outbound-operations.ts +22 -8
- package/src/runtime/channels/channel-runtime-manager.ts +29 -12
- package/src/runtime/channels/channel-transports.ts +128 -60
- package/src/runtime/channels/local-spot-route-transport.ts +3 -1
- package/src/runtime/channels/router-socket-spot-route-transport.ts +13 -4
- package/src/runtime/channels/source-spot-router.ts +8 -4
- package/src/runtime/channels/spot-node-route-transport.ts +8 -4
- package/src/runtime/channels/spot-route-bridge-transport.ts +6 -4
- package/src/runtime/channels/spot-route-dispatch-strategy.ts +47 -18
- package/src/runtime/execution/serial-execution-queue.ts +2 -0
- package/src/runtime/foundation/actor-join-recovery-codec.ts +2 -2
- package/src/runtime/foundation/durable-service-request.ts +137 -0
- package/src/runtime/foundation/operation-identity.ts +5 -1
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +484 -293
- package/src/runtime/foundation/service-relocation-object-owner.ts +2 -0
- package/src/runtime/foundation/service-relocation-runtime.ts +18 -1
- package/src/runtime/foundation/service-runtime-contracts.ts +7 -1
- package/src/runtime/foundation/service-stateful-runtime.ts +67 -125
- package/src/runtime/foundation/service-stateful-wire-codec.ts +0 -5
- package/src/runtime/foundation/service-topology-registry.ts +91 -36
- package/src/runtime/foundation/service-wire-m6a-codec.ts +57 -38
- package/src/runtime/handlers/handler-instance-scope.ts +67 -16
- package/src/runtime/host/actor-runtime-options-factory.ts +13 -2
- package/src/runtime/host/application-job-queue.ts +175 -41
- package/src/runtime/host/entry-actor-runtime.ts +4 -0
- package/src/runtime/host/index.ts +201 -156
- package/src/runtime/host/remote-bound-session-relay.ts +2 -23
- package/src/runtime/host/route-mesh-runtime.ts +1 -1
- package/src/runtime/host/service-relocation-control.ts +11 -5
- package/src/runtime/host/service-relocation-host-runtime.ts +315 -291
- package/src/runtime/host/spot-address-transport.ts +4 -2
- package/src/runtime/host/spot-runtime-options-factory.ts +2 -0
- package/src/runtime/locations/actor-location-claims.ts +52 -30
- package/src/runtime/locations/lifecycle.ts +8 -26
- package/src/runtime/locations/runtime.ts +28 -24
- package/src/runtime/messaging/framework-json-v1.ts +5 -325
- package/src/runtime/messaging/packet-name.ts +5 -25
- package/src/runtime/messaging/request-result.ts +58 -0
- package/src/runtime/messaging/submission-result.ts +5 -1
- package/src/runtime/protocol/service_wire_codec.generated.ts +131 -1
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +4 -4
- package/src/runtime/spots/formal-remote-actor-admission-registry.ts +43 -23
- package/src/runtime/spots/index.ts +14 -2
- package/src/runtime/spots/spot-activation.ts +28 -10
- package/src/runtime/spots/spot-entry-activation.ts +9 -2
- package/src/runtime/spots/spot-handler-registry.ts +41 -8
- package/src/runtime/spots/spot-node-autoconnect.ts +6 -2
- package/src/runtime/spots/spot-outbound.ts +36 -16
- package/src/runtime/spots/spot-routed-spot-packet-dispatch.ts +2 -1
- package/src/runtime/spots/spot-runtime-ports.ts +1 -0
- package/src/runtime/spots/spot-subscription-dispatch.ts +0 -3
- package/src/runtime/spots/spot-timer.ts +5 -1
- package/src/runtime/streams/stream-session-runtime.ts +12 -29
|
@@ -6,6 +6,7 @@ const application_ingress_record_owner_1 = require("../application-jobs/applicat
|
|
|
6
6
|
const submission_result_1 = require("../messaging/submission-result");
|
|
7
7
|
const runtime_values_1 = require("../backend/runtime-values");
|
|
8
8
|
const service_wire_codec_generated_1 = require("../protocol/service_wire_codec.generated");
|
|
9
|
+
const service_wire_binary_primitives_1 = require("./service-wire-binary-primitives");
|
|
9
10
|
const operation_registry_1 = require("./operation-registry");
|
|
10
11
|
const service_liveness_registry_1 = require("./service-liveness-registry");
|
|
11
12
|
const service_mailbox_1 = require("./service-mailbox");
|
|
@@ -17,6 +18,7 @@ const nativeErrnoValues = node_os_1.constants.errno;
|
|
|
17
18
|
// Framework error code 13 (RequestTargetNotFound) is encoded as 14 on a
|
|
18
19
|
// RequestResult.NotFound reply. Boundary transport results keep failureCode 0.
|
|
19
20
|
const RAW_MESH_RECEIVE_RECORD_BUDGET = 64;
|
|
21
|
+
const NODE_SEND_HEADER = (0, service_wire_m6a_codec_1.encodeNodeSendHeader)();
|
|
20
22
|
const RAW_MESH_RECEIVE_BYTE_BUDGET = 4 * 1024 * 1024;
|
|
21
23
|
exports.RAW_MESH_RECEIVE_TIME_BUDGET_MS = 2;
|
|
22
24
|
const REQUEST_TARGET_NOT_FOUND_FAILURE_CODE = service_wire_constants_generated_1.ServiceWireFrameworkErrorCode.requestTargetNotFound;
|
|
@@ -37,6 +39,7 @@ class RawServiceMeshRuntime {
|
|
|
37
39
|
expectedPeers = new Map();
|
|
38
40
|
endpointOnlyPeers = new Set();
|
|
39
41
|
peerConnectionIntentRemoved = new Set();
|
|
42
|
+
peerRouteChanged = new Set();
|
|
40
43
|
/**
|
|
41
44
|
* The last observed Core selected route of each RID (Core ROUTER §10.1):
|
|
42
45
|
* RID -> route generation. Only `observeSelectedRoutes` replaces it.
|
|
@@ -44,10 +47,12 @@ class RawServiceMeshRuntime {
|
|
|
44
47
|
selectedRoutes = new Map();
|
|
45
48
|
bindingPort;
|
|
46
49
|
applicationJobQueue;
|
|
50
|
+
nodeMailboxOwner;
|
|
47
51
|
peerAdmissionSealed;
|
|
48
52
|
applicationJobStop = new AbortController();
|
|
49
53
|
receiveAcquisition;
|
|
50
54
|
receivePermit;
|
|
55
|
+
receiveRotation;
|
|
51
56
|
controlSends = new Map();
|
|
52
57
|
infrastructureFailure;
|
|
53
58
|
onReceiveReady;
|
|
@@ -62,12 +67,14 @@ class RawServiceMeshRuntime {
|
|
|
62
67
|
router;
|
|
63
68
|
nextCorrelation = 1n;
|
|
64
69
|
serviceIngress;
|
|
70
|
+
serviceIngressAccepts;
|
|
65
71
|
closed = false;
|
|
66
72
|
constructor(options) {
|
|
67
73
|
this.onReceiveReady = options.onReceiveReady;
|
|
68
74
|
this.infrastructureTaskRunner = options.infrastructureTaskRunner;
|
|
69
75
|
this.operations = new operation_registry_1.OperationRegistry(undefined, options.onPendingOperationsChanged);
|
|
70
76
|
this.descriptor = options.descriptor;
|
|
77
|
+
this.nodeMailboxOwner = `node:${options.descriptor.nodeRoutingId}`;
|
|
71
78
|
this.topology = new service_topology_registry_1.ServiceTopologyRegistry(options.descriptor);
|
|
72
79
|
this.mailbox = new service_mailbox_1.ServiceMailbox(options.onMailboxReady);
|
|
73
80
|
this.liveness = new service_liveness_registry_1.ServiceLivenessRegistry(options.probeIntervalMs, options.peerTimeoutMs);
|
|
@@ -222,6 +229,30 @@ class RawServiceMeshRuntime {
|
|
|
222
229
|
this.peerConnectionIntentRemoved.delete(listener);
|
|
223
230
|
};
|
|
224
231
|
}
|
|
232
|
+
observePeerRoute(nodeRoutingId) {
|
|
233
|
+
const selected = this.selectedRoutes.get(nodeRoutingId);
|
|
234
|
+
const admitted = this.topology.admittedPeer(nodeRoutingId)?.liveness?.connectionId;
|
|
235
|
+
return {
|
|
236
|
+
generation: selected ?? (typeof admitted === 'bigint' ? admitted : undefined),
|
|
237
|
+
ready: admitted !== undefined && (selected === undefined || admitted === selected)
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
get requestStopSignal() {
|
|
241
|
+
return this.applicationJobStop.signal;
|
|
242
|
+
}
|
|
243
|
+
observePeerRouteChanged(listener) {
|
|
244
|
+
this.peerRouteChanged.add(listener);
|
|
245
|
+
return () => {
|
|
246
|
+
this.peerRouteChanged.delete(listener);
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
hasSelectedPeerRoute(nodeRoutingId) {
|
|
250
|
+
return this.selectedRoutes.has(nodeRoutingId);
|
|
251
|
+
}
|
|
252
|
+
notifyPeerRouteChanged(nodeRoutingId) {
|
|
253
|
+
for (const listener of this.peerRouteChanged)
|
|
254
|
+
listener(nodeRoutingId);
|
|
255
|
+
}
|
|
225
256
|
notifyPeerConnectionIntentRemoved(nodeRoutingId) {
|
|
226
257
|
// Only the logical owner's removal publishes lifecycle termination.
|
|
227
258
|
// A physical disconnect alone retains the connection expectation.
|
|
@@ -231,26 +262,31 @@ class RawServiceMeshRuntime {
|
|
|
231
262
|
listener(nodeRoutingId);
|
|
232
263
|
}
|
|
233
264
|
announcePeer(nodeRoutingId) {
|
|
234
|
-
const expected = this.expectedPeers.get(nodeRoutingId);
|
|
235
265
|
const generation = this.selectedRoutes.get(nodeRoutingId);
|
|
236
|
-
if (
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
this.
|
|
266
|
+
if (generation === undefined)
|
|
267
|
+
return Promise.resolve(false);
|
|
268
|
+
const current = () => {
|
|
269
|
+
const admission = this.topology.routeAdmission(nodeRoutingId);
|
|
270
|
+
return (this.selectedRoutes.get(nodeRoutingId) === generation &&
|
|
271
|
+
!(admission?.connectionId === routeConnectionId(generation) &&
|
|
272
|
+
admission.remoteAdmitReceived === true) &&
|
|
273
|
+
this.peerAdmissionSealed?.() !== true);
|
|
274
|
+
};
|
|
275
|
+
const key = controlSendKey(nodeRoutingId, service_wire_m6a_codec_1.M6aServiceWireCommand.hello);
|
|
276
|
+
if (!current()) {
|
|
277
|
+
this.controlSends.delete(key);
|
|
240
278
|
return Promise.resolve(false);
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
expected.helloSubmittedGeneration = generation;
|
|
248
|
-
}), current));
|
|
279
|
+
}
|
|
280
|
+
const existing = this.controlSends.get(key);
|
|
281
|
+
const hello = existing?.current?.() === true
|
|
282
|
+
? existing.hello
|
|
283
|
+
: (0, service_wire_m6a_codec_1.encodeRouteMeshAdmission)(service_wire_m6a_codec_1.M6aServiceWireCommand.hello, this.topology.localDescriptor());
|
|
284
|
+
return Promise.resolve(this.startControlSend(nodeRoutingId, service_wire_m6a_codec_1.M6aServiceWireCommand.hello, () => this.submitControl(nodeRoutingId, [hello]), current, hello));
|
|
249
285
|
}
|
|
250
286
|
isPeerRouteReady(nodeRoutingId, lifecycleGeneration) {
|
|
251
287
|
// Admission is bound to the observed selected route: the route observer
|
|
252
288
|
// removes the peer when that route ends or is replaced.
|
|
253
|
-
const peer = this.topology.
|
|
289
|
+
const peer = this.topology.admittedPeer(nodeRoutingId);
|
|
254
290
|
return (peer !== undefined &&
|
|
255
291
|
(lifecycleGeneration === undefined ||
|
|
256
292
|
peer.descriptor.lifecycleGeneration === lifecycleGeneration));
|
|
@@ -287,22 +323,22 @@ class RawServiceMeshRuntime {
|
|
|
287
323
|
// placement and channel selectors therefore need the new mutable fields
|
|
288
324
|
// on that same connection before the next request is selected; waiting for
|
|
289
325
|
// a reconnect would leave a race where a drained node is selected again.
|
|
326
|
+
await this.publishLocalDescriptor(this.topology.peers().map((peer) => peer.descriptor.nodeRoutingId));
|
|
327
|
+
}
|
|
328
|
+
async publishLocalDescriptor(targets) {
|
|
290
329
|
const update = (0, service_wire_m6a_codec_1.encodeRouteMeshAdmission)(service_wire_m6a_codec_1.M6aServiceWireCommand.update, this.topology.localDescriptor());
|
|
291
|
-
for (const
|
|
292
|
-
await this.
|
|
330
|
+
for (const target of targets) {
|
|
331
|
+
await this.sendService(target, [update]);
|
|
293
332
|
}
|
|
294
333
|
}
|
|
295
334
|
replaceDiscoveredNotRequired(descriptors) {
|
|
296
335
|
this.topology.replaceDiscoveredNotRequired(descriptors);
|
|
297
336
|
}
|
|
298
337
|
isObjectClientNodeDirectTarget(nodeRoutingId) {
|
|
299
|
-
|
|
300
|
-
? this.topology.localDescriptor()
|
|
301
|
-
: this.topology.knownDescriptor(nodeRoutingId);
|
|
302
|
-
return descriptor?.objectRole === 'client';
|
|
338
|
+
return this.topology.isObjectClientNodeDirectTarget(nodeRoutingId);
|
|
303
339
|
}
|
|
304
|
-
|
|
305
|
-
return this.send(targetNodeRoutingId, [
|
|
340
|
+
sendToNode(targetNodeRoutingId, payload) {
|
|
341
|
+
return this.send(targetNodeRoutingId, [NODE_SEND_HEADER, this.applicationFrame(payload)]);
|
|
306
342
|
}
|
|
307
343
|
async sendToChannel(channelName, payload) {
|
|
308
344
|
const selection = this.selectChannelTarget(channelName);
|
|
@@ -317,17 +353,17 @@ class RawServiceMeshRuntime {
|
|
|
317
353
|
? runtime_values_1.SubmitResult.Ok
|
|
318
354
|
: runtime_values_1.SubmitResult.NotConnected;
|
|
319
355
|
}
|
|
320
|
-
requestToNode(targetNodeRoutingId, payload, timeoutMs) {
|
|
321
|
-
return this.requestToTarget(targetNodeRoutingId, payload, timeoutMs);
|
|
356
|
+
requestToNode(targetNodeRoutingId, payload, timeoutMs, onAdmitted) {
|
|
357
|
+
return this.requestToTarget(targetNodeRoutingId, payload, timeoutMs, undefined, onAdmitted);
|
|
322
358
|
}
|
|
323
|
-
requestToChannel(channelName, payload, timeoutMs) {
|
|
359
|
+
requestToChannel(channelName, payload, timeoutMs, onAdmitted) {
|
|
324
360
|
const selection = this.selectChannelTarget(channelName);
|
|
325
361
|
if (selection.kind !== 'selected') {
|
|
326
362
|
const pending = this.operations.register(timeoutMs);
|
|
327
363
|
this.operations.complete(pending.id, selection.requestResult);
|
|
328
364
|
return pending;
|
|
329
365
|
}
|
|
330
|
-
return this.requestToTarget(selection.peer.descriptor.nodeRoutingId, payload, timeoutMs, channelName);
|
|
366
|
+
return this.requestToTarget(selection.peer.descriptor.nodeRoutingId, payload, timeoutMs, channelName, onAdmitted);
|
|
331
367
|
}
|
|
332
368
|
selectChannelTarget(channelName) {
|
|
333
369
|
const selected = this.topology.selectChannel(channelName, (peer) => this.isPeerRouteReady(peer.descriptor.nodeRoutingId));
|
|
@@ -348,11 +384,12 @@ class RawServiceMeshRuntime {
|
|
|
348
384
|
}
|
|
349
385
|
};
|
|
350
386
|
}
|
|
351
|
-
setServiceIngress(handler) {
|
|
387
|
+
setServiceIngress(handler, accepts) {
|
|
352
388
|
if (this.serviceIngress !== undefined && this.serviceIngress !== handler) {
|
|
353
389
|
throw new Error('Raw service ingress is already registered.');
|
|
354
390
|
}
|
|
355
391
|
this.serviceIngress = handler;
|
|
392
|
+
this.serviceIngressAccepts = accepts;
|
|
356
393
|
}
|
|
357
394
|
async reserveLocalIngress(signal) {
|
|
358
395
|
const waitSignal = signal === undefined
|
|
@@ -362,11 +399,17 @@ class RawServiceMeshRuntime {
|
|
|
362
399
|
return application_ingress_record_owner_1.ApplicationIngressRecordOwner.create(this.applicationJobQueue, permit, { close() { } });
|
|
363
400
|
}
|
|
364
401
|
async sendService(targetNodeRoutingId, parts) {
|
|
365
|
-
|
|
402
|
+
try {
|
|
403
|
+
await this.requireStarted().send(targetNodeRoutingId, parts);
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
return this.sendFailure(error);
|
|
408
|
+
}
|
|
366
409
|
}
|
|
367
|
-
requestService(targetNodeRoutingId, parts, timeoutMs) {
|
|
368
|
-
const admission = this.topology.
|
|
369
|
-
return this.requireStarted().request(targetNodeRoutingId, parts, timeoutMs, admission?.recordReceived);
|
|
410
|
+
requestService(targetNodeRoutingId, parts, timeoutMs, onAdmitted) {
|
|
411
|
+
const admission = this.topology.admittedPeer(targetNodeRoutingId)?.liveness;
|
|
412
|
+
return this.requireStarted().request(targetNodeRoutingId, parts, timeoutMs, admission?.recordReceived, onAdmitted);
|
|
370
413
|
}
|
|
371
414
|
replyService(record, parts) {
|
|
372
415
|
if (record.requestSequence === undefined) {
|
|
@@ -423,51 +466,59 @@ class RawServiceMeshRuntime {
|
|
|
423
466
|
const observe = (_source, byteCount) => {
|
|
424
467
|
bytes += byteCount;
|
|
425
468
|
};
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
469
|
+
const rotation = (this.receiveRotation ??= this.applicationJobQueue.createReceiveRotation(RAW_MESH_RECEIVE_RECORD_BUDGET, 'remote'));
|
|
470
|
+
if (receiveReady)
|
|
471
|
+
rotation.reserve();
|
|
472
|
+
try {
|
|
473
|
+
while (receiveReady && messages < RAW_MESH_RECEIVE_RECORD_BUDGET && !this.closed) {
|
|
474
|
+
const received = this.receiveOne(performance.now(), observe, rotation);
|
|
475
|
+
const result = received instanceof Promise ? await received : received;
|
|
476
|
+
if (result === 'noData') {
|
|
477
|
+
receiveReady = false;
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
messages += 1;
|
|
481
|
+
// Core owns the per-peer fair-queue cursor. The Framework limit bounds
|
|
482
|
+
// the entire round, so changing peers does not renew its byte/count budget.
|
|
483
|
+
if (bytes >= RAW_MESH_RECEIVE_BYTE_BUDGET ||
|
|
484
|
+
performance.now() - startedAtMs >= exports.RAW_MESH_RECEIVE_TIME_BUDGET_MS)
|
|
485
|
+
break;
|
|
431
486
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
if (bytes >= RAW_MESH_RECEIVE_BYTE_BUDGET ||
|
|
436
|
-
performance.now() - startedAtMs >= exports.RAW_MESH_RECEIVE_TIME_BUDGET_MS)
|
|
437
|
-
break;
|
|
487
|
+
}
|
|
488
|
+
finally {
|
|
489
|
+
rotation.releaseUnused();
|
|
438
490
|
}
|
|
439
491
|
if (!this.closed) {
|
|
440
|
-
for (const nodeRoutingId of this.expectedPeers.keys()) {
|
|
441
|
-
if (this.topology.peer(nodeRoutingId) === undefined)
|
|
442
|
-
await this.announcePeer(nodeRoutingId);
|
|
443
|
-
}
|
|
444
492
|
this.tickLiveness();
|
|
445
493
|
}
|
|
446
494
|
return receiveReady && messages > 0;
|
|
447
495
|
}
|
|
448
|
-
|
|
496
|
+
receiveOne(nowMs, observe, rotation) {
|
|
449
497
|
const router = this.requireStarted();
|
|
450
498
|
this.observeInfrastructureFailure();
|
|
451
|
-
|
|
499
|
+
let permit = this.receivePermit;
|
|
500
|
+
this.receivePermit = undefined;
|
|
501
|
+
permit ??= rotation?.take();
|
|
502
|
+
if (permit === undefined && this.receiveAcquisition === undefined) {
|
|
452
503
|
let resolveAcquisition;
|
|
453
504
|
let rejectAcquisition;
|
|
454
|
-
|
|
455
|
-
resolveAcquisition = resolve;
|
|
456
|
-
rejectAcquisition = reject;
|
|
457
|
-
});
|
|
458
|
-
const permit = this.applicationJobQueue.acquireOrResume((permit) => {
|
|
505
|
+
permit = this.applicationJobQueue.acquireOrResume((permit) => {
|
|
459
506
|
this.receiveAcquisition = undefined;
|
|
460
|
-
if (this.closed)
|
|
507
|
+
if (this.closed) {
|
|
461
508
|
permit.releaseAfterInternalProcessing();
|
|
509
|
+
permit.releaseStorage?.();
|
|
510
|
+
}
|
|
462
511
|
else {
|
|
463
512
|
this.receivePermit = permit;
|
|
464
513
|
this.onReceiveReady?.();
|
|
465
514
|
}
|
|
466
|
-
resolveAcquisition(permit);
|
|
467
|
-
}, rejectAcquisition, this.applicationJobStop.signal, 'remote');
|
|
468
|
-
if (permit
|
|
469
|
-
|
|
470
|
-
|
|
515
|
+
resolveAcquisition?.(permit);
|
|
516
|
+
}, (error) => rejectAcquisition?.(error), this.applicationJobStop.signal, 'remote');
|
|
517
|
+
if (permit === undefined) {
|
|
518
|
+
const acquisition = new Promise((resolve, reject) => {
|
|
519
|
+
resolveAcquisition = resolve;
|
|
520
|
+
rejectAcquisition = reject;
|
|
521
|
+
});
|
|
471
522
|
this.receiveAcquisition = acquisition;
|
|
472
523
|
acquisition.catch((error) => {
|
|
473
524
|
this.receiveAcquisition = undefined;
|
|
@@ -476,12 +527,11 @@ class RawServiceMeshRuntime {
|
|
|
476
527
|
});
|
|
477
528
|
}
|
|
478
529
|
}
|
|
479
|
-
const permit = this.receivePermit;
|
|
480
|
-
this.receivePermit = undefined;
|
|
481
530
|
if (permit === undefined)
|
|
482
531
|
return 'noData';
|
|
483
532
|
if (this.closed) {
|
|
484
533
|
permit.releaseAfterInternalProcessing();
|
|
534
|
+
permit.releaseStorage?.();
|
|
485
535
|
return 'noData';
|
|
486
536
|
}
|
|
487
537
|
let received;
|
|
@@ -490,23 +540,44 @@ class RawServiceMeshRuntime {
|
|
|
490
540
|
}
|
|
491
541
|
catch (error) {
|
|
492
542
|
permit.releaseAfterInternalProcessing();
|
|
543
|
+
permit.releaseStorage?.();
|
|
493
544
|
throw error;
|
|
494
545
|
}
|
|
495
546
|
if (received === undefined) {
|
|
496
547
|
permit.releaseAfterInternalProcessing();
|
|
548
|
+
permit.releaseStorage?.();
|
|
497
549
|
return 'noData';
|
|
498
550
|
}
|
|
499
551
|
const applicationJobOwner = application_ingress_record_owner_1.ApplicationIngressRecordOwner.create(this.applicationJobQueue, permit, received);
|
|
552
|
+
let result;
|
|
500
553
|
try {
|
|
501
|
-
|
|
502
|
-
|
|
554
|
+
result = this.processReceived(received, nowMs, applicationJobOwner);
|
|
555
|
+
}
|
|
556
|
+
catch (error) {
|
|
557
|
+
applicationJobOwner.close();
|
|
558
|
+
throw error;
|
|
559
|
+
}
|
|
560
|
+
return result instanceof Promise
|
|
561
|
+
? result.then((value) => this.finishReceive(received, applicationJobOwner, observe, value), (error) => {
|
|
562
|
+
applicationJobOwner.close();
|
|
563
|
+
throw error;
|
|
564
|
+
})
|
|
565
|
+
: this.finishReceive(received, applicationJobOwner, observe, result);
|
|
566
|
+
}
|
|
567
|
+
finishReceive(received, owner, observe, result) {
|
|
568
|
+
try {
|
|
569
|
+
if (result === 'protocolError')
|
|
503
570
|
this.reportProtocolError(received);
|
|
571
|
+
if (observe !== undefined) {
|
|
572
|
+
let bytes = 0;
|
|
573
|
+
for (const part of received.parts)
|
|
574
|
+
bytes += part.byteLength;
|
|
575
|
+
observe(received.sourceRid, bytes);
|
|
504
576
|
}
|
|
505
|
-
observe?.(received.sourceRid, received.parts.reduce((sum, part) => sum + part.byteLength, 0));
|
|
506
577
|
return result;
|
|
507
578
|
}
|
|
508
579
|
finally {
|
|
509
|
-
|
|
580
|
+
owner.close();
|
|
510
581
|
}
|
|
511
582
|
}
|
|
512
583
|
reportProtocolError(received) {
|
|
@@ -545,61 +616,19 @@ class RawServiceMeshRuntime {
|
|
|
545
616
|
throw error;
|
|
546
617
|
}
|
|
547
618
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
return (await this.sendControl(received.sourceRid, [
|
|
554
|
-
(0, service_wire_m6a_codec_1.encodeRouteMeshAdmission)(service_wire_m6a_codec_1.M6aServiceWireCommand.hello, this.topology.localDescriptor())
|
|
555
|
-
]))
|
|
556
|
-
? 'infrastructure'
|
|
557
|
-
: 'dropped';
|
|
558
|
-
}
|
|
619
|
+
processReceived(received, nowMs, applicationJobOwner) {
|
|
620
|
+
const peer = this.topology.admittedPeer(received.sourceRid);
|
|
621
|
+
const currentSource = peer?.liveness !== undefined && peer.liveness.connectionId === received.routeGeneration;
|
|
622
|
+
if (received.parts.length === 0)
|
|
623
|
+
return currentSource ? 'protocolError' : 'dropped';
|
|
559
624
|
try {
|
|
560
625
|
const header = (0, service_wire_m6a_codec_1.decodeHeader)(received.parts[0]);
|
|
561
626
|
if (header.command === service_wire_m6a_codec_1.M6aServiceWireCommand.hello && this.peerAdmissionSealed?.() === true) {
|
|
562
627
|
return 'dropped';
|
|
563
628
|
}
|
|
564
629
|
if (header.command === service_wire_m6a_codec_1.M6aServiceWireCommand.hello ||
|
|
565
|
-
header.command === service_wire_m6a_codec_1.M6aServiceWireCommand.admit
|
|
566
|
-
header.
|
|
567
|
-
if (received.parts.length !== 1)
|
|
568
|
-
return 'protocolError';
|
|
569
|
-
const descriptor = (0, service_wire_m6a_codec_1.decodeRouteMeshAdmission)(received.parts[0], header.command, received.sourceRid);
|
|
570
|
-
const expected = this.expectedPeers.get(received.sourceRid);
|
|
571
|
-
if (expected !== undefined &&
|
|
572
|
-
(expected.meshName !== descriptor.meshName ||
|
|
573
|
-
expected.nodeRoutingId !== descriptor.nodeRoutingId)) {
|
|
574
|
-
await this.sendControl(received.sourceRid, [
|
|
575
|
-
(0, service_wire_m6a_codec_1.encodeReject)((0, service_wire_codec_generated_1.enumWireRejectReason)('identityMismatch'))
|
|
576
|
-
]);
|
|
577
|
-
return 'infrastructure';
|
|
578
|
-
}
|
|
579
|
-
const result = this.admitPeer(descriptor, received.routeGeneration, nowMs, expected);
|
|
580
|
-
if (result !== 'admitted' && result !== 'notRequired') {
|
|
581
|
-
const detail = result === 'invalidDescriptor'
|
|
582
|
-
? describeInvalidAdmissionDescriptor(descriptor, expected, this.topology.peer(descriptor.nodeRoutingId)?.descriptor)
|
|
583
|
-
: undefined;
|
|
584
|
-
console.warn(`RouteMesh admission rejected peer '${received.sourceRid}' as ${result}` +
|
|
585
|
-
(detail === undefined ? '.' : ` (${detail}).`));
|
|
586
|
-
await this.sendControl(received.sourceRid, [(0, service_wire_m6a_codec_1.encodeReject)(admissionReason(result))]);
|
|
587
|
-
return 'infrastructure';
|
|
588
|
-
}
|
|
589
|
-
if (result === 'admitted')
|
|
590
|
-
this.selectBilateralConnection(descriptor);
|
|
591
|
-
if (header.command === service_wire_m6a_codec_1.M6aServiceWireCommand.hello) {
|
|
592
|
-
// Hello supplies only the sender's descriptor. Even NotRequired
|
|
593
|
-
// peers must receive ours before the intent owner closes transport;
|
|
594
|
-
// submitting a response does not guarantee delivery before close.
|
|
595
|
-
await this.sendControl(received.sourceRid, [
|
|
596
|
-
(0, service_wire_m6a_codec_1.encodeRouteMeshAdmission)(service_wire_m6a_codec_1.M6aServiceWireCommand.admit, this.topology.localDescriptor())
|
|
597
|
-
]);
|
|
598
|
-
}
|
|
599
|
-
else if (result === 'notRequired') {
|
|
600
|
-
this.retireNotRequiredExpectedPeer(received.sourceRid, descriptor.advertisedEndpoint);
|
|
601
|
-
}
|
|
602
|
-
return 'infrastructure';
|
|
630
|
+
header.command === service_wire_m6a_codec_1.M6aServiceWireCommand.admit) {
|
|
631
|
+
return this.processAdmission(received, header, nowMs).catch((error) => this.receiveProtocolFailure(error));
|
|
603
632
|
}
|
|
604
633
|
if (header.command === service_wire_m6a_codec_1.M6aServiceWireCommand.reject) {
|
|
605
634
|
if (received.parts.length !== 1)
|
|
@@ -607,47 +636,122 @@ class RawServiceMeshRuntime {
|
|
|
607
636
|
(0, service_wire_m6a_codec_1.decodeReject)(received.parts[0]);
|
|
608
637
|
return 'infrastructure';
|
|
609
638
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
if (
|
|
614
|
-
this.
|
|
639
|
+
if (!currentSource)
|
|
640
|
+
return 'dropped';
|
|
641
|
+
this.liveness.recordReceived(peer.liveness, nowMs);
|
|
642
|
+
if (header.command === service_wire_m6a_codec_1.M6aServiceWireCommand.update) {
|
|
643
|
+
return this.processAdmission(received, header, nowMs).catch((error) => this.receiveProtocolFailure(error));
|
|
644
|
+
}
|
|
615
645
|
if (header.command === service_wire_m6a_codec_1.M6aServiceWireCommand.livenessProbe ||
|
|
616
646
|
header.command === service_wire_m6a_codec_1.M6aServiceWireCommand.livenessAck) {
|
|
617
|
-
|
|
618
|
-
return 'protocolError';
|
|
619
|
-
const record = livenessCodec.decodeLivenessRecord(received.parts[0]);
|
|
620
|
-
if (record.command === service_wire_m6a_codec_1.M6aServiceWireCommand.livenessProbe) {
|
|
621
|
-
const ack = this.liveness.acknowledgeProbe(received.sourceRid, received.routeGeneration, record.probeId);
|
|
622
|
-
if (ack === undefined)
|
|
623
|
-
return 'protocolError';
|
|
624
|
-
const sent = await this.sendControl(received.sourceRid, [
|
|
625
|
-
livenessCodec.encodeLivenessRecord({
|
|
626
|
-
command: service_wire_m6a_codec_1.M6aServiceWireCommand.livenessAck,
|
|
627
|
-
probeId: record.probeId
|
|
628
|
-
})
|
|
629
|
-
]);
|
|
630
|
-
if (!sent)
|
|
631
|
-
return 'dropped';
|
|
632
|
-
}
|
|
633
|
-
else {
|
|
634
|
-
this.liveness.acknowledge(received.sourceRid, received.routeGeneration, record.probeId, nowMs);
|
|
635
|
-
}
|
|
636
|
-
return 'infrastructure';
|
|
647
|
+
return this.processLiveness(received, nowMs).catch((error) => this.receiveProtocolFailure(error));
|
|
637
648
|
}
|
|
638
|
-
const stateful =
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
+
const stateful = this.serviceIngress !== undefined && this.serviceIngressAccepts?.(header.command) !== false
|
|
650
|
+
? this.serviceIngress({
|
|
651
|
+
command: header.command,
|
|
652
|
+
flags: header.flags,
|
|
653
|
+
sourceRoutingId: received.sourceRid,
|
|
654
|
+
sourceNodeGeneration: peer.descriptor.lifecycleGeneration,
|
|
655
|
+
sourceRoute: received.sourceRoute,
|
|
656
|
+
...(received.reply === undefined ? {} : { reply: received.reply }),
|
|
657
|
+
...(received.requestSeq === undefined
|
|
658
|
+
? {}
|
|
659
|
+
: { requestSequence: received.requestSeq }),
|
|
660
|
+
parts: received.parts,
|
|
661
|
+
applicationJobOwner
|
|
662
|
+
})
|
|
663
|
+
: undefined;
|
|
664
|
+
if (stateful instanceof Promise)
|
|
665
|
+
return stateful.then((result) => result ?? this.processApplicationReceived(received, header, applicationJobOwner), (error) => this.receiveProtocolFailure(error));
|
|
649
666
|
if (stateful !== undefined)
|
|
650
667
|
return stateful;
|
|
668
|
+
return this.processApplicationReceived(received, header, applicationJobOwner);
|
|
669
|
+
}
|
|
670
|
+
catch (error) {
|
|
671
|
+
if (!currentSource && error instanceof service_wire_m6a_codec_1.ServiceWireProtocolError)
|
|
672
|
+
return 'dropped';
|
|
673
|
+
return this.receiveProtocolFailure(error);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
receiveProtocolFailure(error) {
|
|
677
|
+
if (error instanceof service_wire_m6a_codec_1.ServiceWireProtocolError)
|
|
678
|
+
return 'protocolError';
|
|
679
|
+
throw error;
|
|
680
|
+
}
|
|
681
|
+
async processAdmission(received, header, nowMs) {
|
|
682
|
+
if (received.parts.length !== 1)
|
|
683
|
+
return 'protocolError';
|
|
684
|
+
const descriptor = (0, service_wire_m6a_codec_1.decodeRouteMeshAdmission)(received.parts[0], header.command, received.sourceRid);
|
|
685
|
+
const expected = this.expectedPeers.get(received.sourceRid);
|
|
686
|
+
if (expected !== undefined &&
|
|
687
|
+
(expected.meshName !== descriptor.meshName ||
|
|
688
|
+
expected.nodeRoutingId !== descriptor.nodeRoutingId)) {
|
|
689
|
+
await this.sendControl(received.sourceRid, [
|
|
690
|
+
(0, service_wire_m6a_codec_1.encodeReject)((0, service_wire_codec_generated_1.enumWireRejectReason)('identityMismatch'))
|
|
691
|
+
]);
|
|
692
|
+
return 'infrastructure';
|
|
693
|
+
}
|
|
694
|
+
const helloKey = controlSendKey(received.sourceRid, service_wire_m6a_codec_1.M6aServiceWireCommand.hello);
|
|
695
|
+
const exchange = this.controlSends.get(helloKey);
|
|
696
|
+
const result = this.admitPeer(descriptor, received.routeGeneration, nowMs, expected, header.command, received.parts[0].subarray(service_wire_binary_primitives_1.SERVICE_WIRE_PREFIX_SIZE));
|
|
697
|
+
if (result !== 'admitted' && result !== 'notRequired') {
|
|
698
|
+
const detail = result === 'invalidDescriptor'
|
|
699
|
+
? describeInvalidAdmissionDescriptor(descriptor, expected, this.topology.peer(descriptor.nodeRoutingId)?.descriptor)
|
|
700
|
+
: undefined;
|
|
701
|
+
console.warn(`RouteMesh admission rejected peer '${received.sourceRid}' as ${result}` +
|
|
702
|
+
(detail === undefined ? '.' : ` (${detail}).`));
|
|
703
|
+
await this.sendControl(received.sourceRid, [(0, service_wire_m6a_codec_1.encodeReject)(admissionReason(result))]);
|
|
704
|
+
return 'infrastructure';
|
|
705
|
+
}
|
|
706
|
+
if (result === 'admitted')
|
|
707
|
+
this.selectBilateralConnection(descriptor);
|
|
708
|
+
if (header.command === service_wire_m6a_codec_1.M6aServiceWireCommand.hello) {
|
|
709
|
+
// Hello supplies only the sender's descriptor. Even NotRequired
|
|
710
|
+
// peers must receive ours before the intent owner closes transport;
|
|
711
|
+
// submitting a response does not guarantee delivery before close.
|
|
712
|
+
const local = exchange?.current?.() === true && exchange.hello !== undefined
|
|
713
|
+
? (0, service_wire_m6a_codec_1.decodeRouteMeshAdmission)(exchange.hello, service_wire_m6a_codec_1.M6aServiceWireCommand.hello, this.descriptor.nodeRoutingId)
|
|
714
|
+
: this.topology.localDescriptor();
|
|
715
|
+
await this.sendControl(received.sourceRid, [
|
|
716
|
+
(0, service_wire_m6a_codec_1.encodeRouteMeshAdmission)(service_wire_m6a_codec_1.M6aServiceWireCommand.admit, local)
|
|
717
|
+
]);
|
|
718
|
+
}
|
|
719
|
+
else if (header.command === service_wire_m6a_codec_1.M6aServiceWireCommand.admit) {
|
|
720
|
+
this.controlSends.delete(helloKey);
|
|
721
|
+
if (result === 'notRequired') {
|
|
722
|
+
this.retireNotRequiredExpectedPeer(received.sourceRid, descriptor.advertisedEndpoint);
|
|
723
|
+
}
|
|
724
|
+
else if (exchange?.hello !== undefined &&
|
|
725
|
+
(0, service_wire_m6a_codec_1.decodeRouteMeshAdmission)(exchange.hello, service_wire_m6a_codec_1.M6aServiceWireCommand.hello, this.descriptor.nodeRoutingId).descriptorRevision < this.descriptor.descriptorRevision) {
|
|
726
|
+
await this.publishLocalDescriptor([received.sourceRid]);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return 'infrastructure';
|
|
730
|
+
}
|
|
731
|
+
async processLiveness(received, nowMs) {
|
|
732
|
+
if (received.parts.length !== 1)
|
|
733
|
+
return 'protocolError';
|
|
734
|
+
const record = livenessCodec.decodeLivenessRecord(received.parts[0]);
|
|
735
|
+
if (record.command === service_wire_m6a_codec_1.M6aServiceWireCommand.livenessProbe) {
|
|
736
|
+
const ack = this.liveness.acknowledgeProbe(received.sourceRid, received.routeGeneration, record.probeId);
|
|
737
|
+
if (ack === undefined)
|
|
738
|
+
return 'protocolError';
|
|
739
|
+
const sent = await this.sendControl(received.sourceRid, [
|
|
740
|
+
livenessCodec.encodeLivenessRecord({
|
|
741
|
+
command: service_wire_m6a_codec_1.M6aServiceWireCommand.livenessAck,
|
|
742
|
+
probeId: record.probeId
|
|
743
|
+
})
|
|
744
|
+
]);
|
|
745
|
+
if (!sent)
|
|
746
|
+
return 'dropped';
|
|
747
|
+
}
|
|
748
|
+
else {
|
|
749
|
+
this.liveness.acknowledge(received.sourceRid, received.routeGeneration, record.probeId, nowMs);
|
|
750
|
+
}
|
|
751
|
+
return 'infrastructure';
|
|
752
|
+
}
|
|
753
|
+
processApplicationReceived(received, header, applicationJobOwner) {
|
|
754
|
+
try {
|
|
651
755
|
if (header.flags === 0 &&
|
|
652
756
|
// A bare infrastructure control is normally exactly one frame, but a
|
|
653
757
|
// relocation Prepare now rides its bound-session journal as
|
|
@@ -663,7 +767,7 @@ class RawServiceMeshRuntime {
|
|
|
663
767
|
received.parts.length >= 1 &&
|
|
664
768
|
isBareInfrastructureControl(header.command)) {
|
|
665
769
|
const accepted = this.mailbox.tryEnqueue({
|
|
666
|
-
owner:
|
|
770
|
+
owner: this.nodeMailboxOwner,
|
|
667
771
|
domain: 'infrastructure',
|
|
668
772
|
parts: received.parts,
|
|
669
773
|
sourceRoutingId: received.sourceRid,
|
|
@@ -686,51 +790,54 @@ class RawServiceMeshRuntime {
|
|
|
686
790
|
let owner;
|
|
687
791
|
let correlation;
|
|
688
792
|
if (header.command === service_wire_m6a_codec_1.M6aServiceWireCommand.nodeSend) {
|
|
689
|
-
owner =
|
|
793
|
+
owner = this.nodeMailboxOwner;
|
|
690
794
|
}
|
|
691
795
|
else if (header.command === service_wire_m6a_codec_1.M6aServiceWireCommand.nodeRequest) {
|
|
692
|
-
owner =
|
|
693
|
-
correlation = (0, service_wire_m6a_codec_1.decodeNodeRequestHeader)(received.parts[0]);
|
|
796
|
+
owner = this.nodeMailboxOwner;
|
|
797
|
+
correlation = (0, service_wire_m6a_codec_1.decodeNodeRequestHeader)(received.parts[0], header);
|
|
694
798
|
}
|
|
695
799
|
else if (header.command === service_wire_m6a_codec_1.M6aServiceWireCommand.channelSend) {
|
|
696
|
-
owner = `channel:${(0, service_wire_m6a_codec_1.decodeChannelSendHeader)(received.parts[0])}`;
|
|
800
|
+
owner = `channel:${(0, service_wire_m6a_codec_1.decodeChannelSendHeader)(received.parts[0], header)}`;
|
|
697
801
|
}
|
|
698
802
|
else {
|
|
699
|
-
const channel = (0, service_wire_m6a_codec_1.decodeChannelRequestHeader)(received.parts[0]);
|
|
803
|
+
const channel = (0, service_wire_m6a_codec_1.decodeChannelRequestHeader)(received.parts[0], header);
|
|
700
804
|
owner = `channel:${channel.channelName}`;
|
|
701
805
|
correlation = channel.correlation;
|
|
702
806
|
}
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
owner,
|
|
706
|
-
|
|
707
|
-
|
|
807
|
+
const job = applicationJobOwner.acquire('application');
|
|
808
|
+
return job instanceof Promise
|
|
809
|
+
? job.then((permit) => this.enqueueOrdinaryRecord(received, owner, correlation, permit))
|
|
810
|
+
: this.enqueueOrdinaryRecord(received, owner, correlation, job);
|
|
811
|
+
}
|
|
812
|
+
catch (error) {
|
|
813
|
+
return this.receiveProtocolFailure(error);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
enqueueOrdinaryRecord(received, owner, correlation, applicationJob) {
|
|
817
|
+
const accepted = this.mailbox.tryEnqueue({
|
|
818
|
+
owner,
|
|
819
|
+
domain: 'application',
|
|
820
|
+
parts: received.parts,
|
|
821
|
+
sourceRoutingId: received.sourceRid,
|
|
822
|
+
sourceRoute: received.sourceRoute,
|
|
823
|
+
...(received.reply === undefined ? {} : { reply: received.reply }),
|
|
824
|
+
requestSequence: received.requestSeq,
|
|
825
|
+
...(correlation === undefined ? {} : { correlation }),
|
|
826
|
+
applicationJob
|
|
827
|
+
});
|
|
828
|
+
if (accepted)
|
|
829
|
+
return 'application';
|
|
830
|
+
applicationJob.close();
|
|
831
|
+
if (correlation !== undefined && received.requestSeq !== undefined) {
|
|
832
|
+
this.replyService({
|
|
708
833
|
sourceRoutingId: received.sourceRid,
|
|
709
834
|
sourceRoute: received.sourceRoute,
|
|
710
|
-
...(received.reply === undefined ? {} : { reply: received.reply }),
|
|
711
835
|
requestSequence: received.requestSeq,
|
|
712
|
-
...(
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
if (accepted)
|
|
716
|
-
return 'application';
|
|
717
|
-
applicationJob.close();
|
|
718
|
-
if (correlation !== undefined && received.requestSeq !== undefined) {
|
|
719
|
-
this.replyService({
|
|
720
|
-
sourceRoutingId: received.sourceRid,
|
|
721
|
-
sourceRoute: received.sourceRoute,
|
|
722
|
-
requestSequence: received.requestSeq,
|
|
723
|
-
...(received.reply === undefined ? {} : { reply: received.reply })
|
|
724
|
-
}, [(0, service_wire_m6a_codec_1.encodeReplyHeader)(correlation, runtime_values_1.RequestResult.NotConnected, 0)]);
|
|
725
|
-
return 'infrastructure';
|
|
726
|
-
}
|
|
727
|
-
return 'dropped';
|
|
728
|
-
}
|
|
729
|
-
catch (error) {
|
|
730
|
-
if (error instanceof service_wire_m6a_codec_1.ServiceWireProtocolError)
|
|
731
|
-
return 'protocolError';
|
|
732
|
-
throw error;
|
|
836
|
+
...(received.reply === undefined ? {} : { reply: received.reply })
|
|
837
|
+
}, [(0, service_wire_m6a_codec_1.encodeReplyHeader)(correlation, runtime_values_1.RequestResult.NotConnected, 0)]);
|
|
838
|
+
return 'infrastructure';
|
|
733
839
|
}
|
|
840
|
+
return 'dropped';
|
|
734
841
|
}
|
|
735
842
|
expireOperations(nowMs, turnDeadlineMs) {
|
|
736
843
|
return this.operations.expire(nowMs, turnDeadlineMs);
|
|
@@ -741,6 +848,8 @@ class RawServiceMeshRuntime {
|
|
|
741
848
|
tickLiveness(nowMs = performance.now()) {
|
|
742
849
|
const result = this.liveness.tick(nowMs);
|
|
743
850
|
this.requireStarted();
|
|
851
|
+
for (const nodeRoutingId of this.selectedRoutes.keys())
|
|
852
|
+
void this.announcePeer(nodeRoutingId);
|
|
744
853
|
for (const probe of result.probes) {
|
|
745
854
|
const parts = [
|
|
746
855
|
livenessCodec.encodeLivenessRecord({
|
|
@@ -777,10 +886,8 @@ class RawServiceMeshRuntime {
|
|
|
777
886
|
for (const [nodeRoutingId, generation] of this.selectedRoutes) {
|
|
778
887
|
if (observed.get(nodeRoutingId) === generation)
|
|
779
888
|
continue;
|
|
889
|
+
this.controlSends.delete(controlSendKey(nodeRoutingId, service_wire_m6a_codec_1.M6aServiceWireCommand.hello));
|
|
780
890
|
changes++;
|
|
781
|
-
const expected = this.expectedPeers.get(nodeRoutingId);
|
|
782
|
-
if (expected !== undefined)
|
|
783
|
-
delete expected.helloSubmittedGeneration;
|
|
784
891
|
const peer = this.topology.peer(nodeRoutingId);
|
|
785
892
|
if (peer?.liveness?.connectionId === generation) {
|
|
786
893
|
this.removePeer(peer);
|
|
@@ -795,6 +902,8 @@ class RawServiceMeshRuntime {
|
|
|
795
902
|
selected.push(nodeRoutingId);
|
|
796
903
|
}
|
|
797
904
|
this.selectedRoutes = observed;
|
|
905
|
+
for (const nodeRoutingId of selected)
|
|
906
|
+
this.notifyPeerRouteChanged(nodeRoutingId);
|
|
798
907
|
// A new selected route admits only through a new handshake.
|
|
799
908
|
for (const nodeRoutingId of selected)
|
|
800
909
|
await this.announcePeer(nodeRoutingId);
|
|
@@ -806,9 +915,11 @@ class RawServiceMeshRuntime {
|
|
|
806
915
|
this.closed = true;
|
|
807
916
|
this.applicationJobStop.abort(new Error('Raw service runtime application job admission stopped.'));
|
|
808
917
|
this.receivePermit?.releaseAfterInternalProcessing();
|
|
918
|
+
this.receivePermit?.releaseStorage?.();
|
|
809
919
|
this.receivePermit = undefined;
|
|
810
920
|
for (const send of this.controlSends.values())
|
|
811
921
|
delete send.next;
|
|
922
|
+
this.controlSends.clear();
|
|
812
923
|
this.mailbox.close();
|
|
813
924
|
this.operations.close('Raw service runtime closed.');
|
|
814
925
|
this.serviceIngress = undefined;
|
|
@@ -822,7 +933,7 @@ class RawServiceMeshRuntime {
|
|
|
822
933
|
this.router = undefined;
|
|
823
934
|
this.host = undefined;
|
|
824
935
|
}
|
|
825
|
-
requestToTarget(targetNodeRoutingId, payload, timeoutMs, channelName) {
|
|
936
|
+
requestToTarget(targetNodeRoutingId, payload, timeoutMs, channelName, onAdmitted) {
|
|
826
937
|
const correlation = this.nextCorrelation++;
|
|
827
938
|
const header = channelName === undefined
|
|
828
939
|
? (0, service_wire_m6a_codec_1.encodeNodeRequestHeader)(correlation)
|
|
@@ -888,6 +999,9 @@ class RawServiceMeshRuntime {
|
|
|
888
999
|
failureCode: 0
|
|
889
1000
|
});
|
|
890
1001
|
}
|
|
1002
|
+
else {
|
|
1003
|
+
onAdmitted?.();
|
|
1004
|
+
}
|
|
891
1005
|
}
|
|
892
1006
|
finally {
|
|
893
1007
|
applicationJobOwner.close();
|
|
@@ -895,7 +1009,7 @@ class RawServiceMeshRuntime {
|
|
|
895
1009
|
})().catch((error) => this.operations.fail(pending.id, error));
|
|
896
1010
|
return pending;
|
|
897
1011
|
}
|
|
898
|
-
const selectedPeer = this.topology.
|
|
1012
|
+
const selectedPeer = this.topology.admittedPeer(selectedTargetNodeRoutingId);
|
|
899
1013
|
if (selectedPeer === undefined) {
|
|
900
1014
|
// Logical target selection is the requester's decision: an RID that is
|
|
901
1015
|
// not an admitted peer is not a target. Whether an admitted target's
|
|
@@ -909,7 +1023,7 @@ class RawServiceMeshRuntime {
|
|
|
909
1023
|
const parts = [header, encodedPayload];
|
|
910
1024
|
let request;
|
|
911
1025
|
try {
|
|
912
|
-
request = this.requireStarted().request(selectedTargetNodeRoutingId, parts, timeoutMs, selectedPeer.liveness?.recordReceived);
|
|
1026
|
+
request = this.requireStarted().request(selectedTargetNodeRoutingId, parts, timeoutMs, selectedPeer.liveness?.recordReceived, onAdmitted);
|
|
913
1027
|
}
|
|
914
1028
|
catch (error) {
|
|
915
1029
|
this.operations.fail(pending.id, error);
|
|
@@ -958,24 +1072,22 @@ class RawServiceMeshRuntime {
|
|
|
958
1072
|
applicationPayload(payload) {
|
|
959
1073
|
return Buffer.isBuffer(payload) ? (0, service_wire_m6a_codec_1.decodeApplicationPayloadView)(payload) : payload;
|
|
960
1074
|
}
|
|
961
|
-
admitPeer(descriptor, connectionId, nowMs, expected) {
|
|
1075
|
+
admitPeer(descriptor, connectionId, nowMs, expected, command = service_wire_m6a_codec_1.M6aServiceWireCommand.hello, descriptorBytes) {
|
|
962
1076
|
const previous = this.topology.peer(descriptor.nodeRoutingId);
|
|
963
|
-
|
|
964
|
-
previous.descriptor.lifecycleGeneration === descriptor.lifecycleGeneration &&
|
|
965
|
-
(descriptor.descriptorRevision < previous.descriptor.descriptorRevision ||
|
|
966
|
-
(descriptor.descriptorRevision === previous.descriptor.descriptorRevision &&
|
|
967
|
-
!(0, service_topology_registry_1.sameServiceNodeDescriptor)(previous.descriptor, descriptor)))) {
|
|
968
|
-
throw new service_wire_m6a_codec_1.ServiceWireProtocolError(`RouteMesh peer '${descriptor.nodeRoutingId}' descriptor revision is stale or conflicting.`);
|
|
969
|
-
}
|
|
970
|
-
const topologyConnectionId = typeof connectionId === 'bigint' ? `route:${connectionId}` : connectionId;
|
|
1077
|
+
const topologyConnectionId = typeof connectionId === 'bigint' ? routeConnectionId(connectionId) : connectionId;
|
|
971
1078
|
const result = this.topology.admit(descriptor, topologyConnectionId, expected, () => {
|
|
972
1079
|
const admission = this.liveness.admit(descriptor.nodeRoutingId, connectionId, nowMs);
|
|
973
1080
|
this.liveness.recordReceived(admission, nowMs);
|
|
974
1081
|
return admission;
|
|
975
|
-
});
|
|
1082
|
+
}, command, descriptorBytes);
|
|
1083
|
+
if (result === 'staleDescriptor') {
|
|
1084
|
+
throw new service_wire_m6a_codec_1.ServiceWireProtocolError(`RouteMesh peer '${descriptor.nodeRoutingId}' descriptor revision is stale or conflicting.`);
|
|
1085
|
+
}
|
|
976
1086
|
if (result === 'notRequired' && previous !== undefined) {
|
|
977
1087
|
this.liveness.disconnect(descriptor.nodeRoutingId, previous.liveness?.connectionId ?? previous.connectionId);
|
|
978
1088
|
}
|
|
1089
|
+
if (result === 'admitted')
|
|
1090
|
+
this.notifyPeerRouteChanged(descriptor.nodeRoutingId);
|
|
979
1091
|
return result;
|
|
980
1092
|
}
|
|
981
1093
|
retireNotRequiredExpectedPeer(nodeRoutingId, advertisedEndpoint) {
|
|
@@ -1020,7 +1132,7 @@ class RawServiceMeshRuntime {
|
|
|
1020
1132
|
async sendControl(targetNodeRoutingId, parts) {
|
|
1021
1133
|
return this.startControlSend(targetNodeRoutingId, (0, service_wire_m6a_codec_1.decodeHeader)(parts[0]).command, () => this.submitControl(targetNodeRoutingId, parts));
|
|
1022
1134
|
}
|
|
1023
|
-
submitControl(target, parts
|
|
1135
|
+
submitControl(target, parts) {
|
|
1024
1136
|
let submission;
|
|
1025
1137
|
try {
|
|
1026
1138
|
submission = this.requireStarted().submitSend(target, parts);
|
|
@@ -1028,19 +1140,15 @@ class RawServiceMeshRuntime {
|
|
|
1028
1140
|
catch (error) {
|
|
1029
1141
|
return this.sendFailure(error);
|
|
1030
1142
|
}
|
|
1031
|
-
if (submission.result === runtime_values_1.SubmitResult.Ok)
|
|
1032
|
-
accepted?.();
|
|
1143
|
+
if (submission.result === runtime_values_1.SubmitResult.Ok)
|
|
1033
1144
|
return true;
|
|
1034
|
-
|
|
1035
|
-
return submission.admitted.then(() => {
|
|
1036
|
-
accepted?.();
|
|
1037
|
-
return true;
|
|
1038
|
-
}, (error) => this.sendFailure(error));
|
|
1145
|
+
return submission.admitted.then(() => true, (error) => this.sendFailure(error));
|
|
1039
1146
|
}
|
|
1040
|
-
startControlSend(target, command, attempt, current) {
|
|
1041
|
-
const key =
|
|
1147
|
+
startControlSend(target, command, attempt, current, hello) {
|
|
1148
|
+
const key = controlSendKey(target, command);
|
|
1042
1149
|
const existing = this.controlSends.get(key);
|
|
1043
1150
|
if (existing !== undefined &&
|
|
1151
|
+
existing.task !== undefined &&
|
|
1044
1152
|
(command !== service_wire_m6a_codec_1.M6aServiceWireCommand.hello || existing.current?.() === true)) {
|
|
1045
1153
|
if (command !== service_wire_m6a_codec_1.M6aServiceWireCommand.hello) {
|
|
1046
1154
|
existing.next = attempt;
|
|
@@ -1049,31 +1157,39 @@ class RawServiceMeshRuntime {
|
|
|
1049
1157
|
return true;
|
|
1050
1158
|
}
|
|
1051
1159
|
const first = attempt();
|
|
1052
|
-
if (typeof first === 'boolean')
|
|
1160
|
+
if (typeof first === 'boolean') {
|
|
1161
|
+
if (hello !== undefined)
|
|
1162
|
+
this.controlSends.set(key, { current, hello });
|
|
1053
1163
|
return first;
|
|
1164
|
+
}
|
|
1054
1165
|
const owner = this;
|
|
1055
|
-
const state = {
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
if (owner.controlSends.get(key) === state)
|
|
1064
|
-
owner.controlSends.delete(key);
|
|
1065
|
-
return accepted;
|
|
1066
|
-
})
|
|
1166
|
+
const state = { current, hello };
|
|
1167
|
+
const completeSend = () => {
|
|
1168
|
+
if (owner.controlSends.get(key) !== state)
|
|
1169
|
+
return;
|
|
1170
|
+
if (hello !== undefined && current?.() === true)
|
|
1171
|
+
delete state.task;
|
|
1172
|
+
else
|
|
1173
|
+
owner.controlSends.delete(key);
|
|
1067
1174
|
};
|
|
1175
|
+
const task = first.then(function drain(accepted) {
|
|
1176
|
+
if (!owner.closed && state.next !== undefined) {
|
|
1177
|
+
const next = state.next;
|
|
1178
|
+
delete state.next;
|
|
1179
|
+
return Promise.resolve(next()).then(drain);
|
|
1180
|
+
}
|
|
1181
|
+
completeSend();
|
|
1182
|
+
return accepted;
|
|
1183
|
+
});
|
|
1184
|
+
state.task = task;
|
|
1068
1185
|
this.controlSends.set(key, state);
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
this.controlSends.delete(key);
|
|
1186
|
+
task.then(() => { }, (error) => {
|
|
1187
|
+
completeSend();
|
|
1072
1188
|
if (this.infrastructureTaskRunner === undefined)
|
|
1073
1189
|
this.reportInfrastructureFailure(error);
|
|
1074
1190
|
});
|
|
1075
1191
|
this.infrastructureTaskRunner?.runDetached('RouteMesh control SEND', async () => {
|
|
1076
|
-
await
|
|
1192
|
+
await task;
|
|
1077
1193
|
});
|
|
1078
1194
|
return true;
|
|
1079
1195
|
}
|
|
@@ -1092,13 +1208,15 @@ class RawServiceMeshRuntime {
|
|
|
1092
1208
|
throw failure.error;
|
|
1093
1209
|
}
|
|
1094
1210
|
send(targetNodeRoutingId, parts) {
|
|
1095
|
-
|
|
1096
|
-
.
|
|
1097
|
-
.
|
|
1098
|
-
|
|
1099
|
-
|
|
1211
|
+
try {
|
|
1212
|
+
const submission = this.requireStarted().submitSend(targetNodeRoutingId, parts);
|
|
1213
|
+
if (submission.result === runtime_values_1.SubmitResult.Ok)
|
|
1214
|
+
return true;
|
|
1215
|
+
return submission.admitted.then(() => true, (error) => this.sendFailure(error));
|
|
1216
|
+
}
|
|
1217
|
+
catch (error) {
|
|
1100
1218
|
return this.sendFailure(error);
|
|
1101
|
-
}
|
|
1219
|
+
}
|
|
1102
1220
|
}
|
|
1103
1221
|
sendFailure(error) {
|
|
1104
1222
|
if ((0, runtime_values_1.isZLinkBackendResultError)(error) && error.operation === 'submit') {
|
|
@@ -1110,6 +1228,12 @@ class RawServiceMeshRuntime {
|
|
|
1110
1228
|
}
|
|
1111
1229
|
}
|
|
1112
1230
|
exports.RawServiceMeshRuntime = RawServiceMeshRuntime;
|
|
1231
|
+
function controlSendKey(target, command) {
|
|
1232
|
+
return JSON.stringify([target, command]);
|
|
1233
|
+
}
|
|
1234
|
+
function routeConnectionId(generation) {
|
|
1235
|
+
return `route:${generation}`;
|
|
1236
|
+
}
|
|
1113
1237
|
/**
|
|
1114
1238
|
* Relocation and bound-Session controls are service-wire records themselves,
|
|
1115
1239
|
* not application NodeSend(16) envelopes. Keep this list closed: a bare
|