@zlink-systems/framework 0.31.0 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/contracts/Actors/ZLinkActor.d.ts +2 -0
- package/dist/contracts/Actors/ZLinkSessionBindingIdentity.d.ts +6 -0
- package/dist/contracts/Actors/ZLinkSessionBindingIdentity.js +2 -0
- package/dist/contracts/Actors/index.d.ts +1 -0
- package/dist/contracts/Actors/index.js +1 -0
- package/dist/contracts/Common/payload-slab.d.ts +4 -0
- package/dist/contracts/Common/payload-slab.js +30 -0
- package/dist/contracts/Handlers/Contexts.d.ts +1 -0
- package/dist/internal.d.ts +9 -0
- package/dist/internal.js +23 -1
- package/dist/runtime/actors/actor-client.d.ts +1 -1
- package/dist/runtime/actors/actor-client.js +32 -23
- package/dist/runtime/actors/actor-mailbox.d.ts +1 -1
- package/dist/runtime/actors/actor-runtime-state.d.ts +1 -1
- package/dist/runtime/actors/spot-actor-dispatch.d.ts +4 -2
- package/dist/runtime/actors/spot-actor-dispatch.js +2 -1
- package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +1 -0
- package/dist/runtime/application-jobs/application-ingress-record-owner.js +3 -0
- package/dist/runtime/backend/contracts/index.d.ts +2 -0
- package/dist/runtime/backend/mesh-completion-table.d.ts +1 -0
- package/dist/runtime/backend/mesh-completion-table.js +1 -0
- package/dist/runtime/backend/node/node-mesh-backend-adapter.js +5 -1
- package/dist/runtime/backend/node/node-raw-binding-port.d.ts +7 -2
- package/dist/runtime/backend/node/node-raw-binding-port.js +17 -11
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +2 -2
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +15 -8
- package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +7 -6
- package/dist/runtime/channels/channel-dispatch-pipeline.js +1 -0
- package/dist/runtime/channels/channel-dispatchers.d.ts +7 -3
- package/dist/runtime/channels/channel-dispatchers.js +27 -19
- package/dist/runtime/channels/channel-envelope.js +543 -23
- package/dist/runtime/channels/channel-receive-loops.d.ts +9 -6
- package/dist/runtime/channels/channel-receive-loops.js +59 -18
- package/dist/runtime/channels/channel-runtime-lifecycle.d.ts +22 -3
- package/dist/runtime/channels/channel-socket-registry.d.ts +22 -4
- package/dist/runtime/diagnostics/runtime-metrics.d.ts +1 -1
- package/dist/runtime/diagnostics/runtime-metrics.js +2 -1
- package/dist/runtime/execution/serial-execution-queue.d.ts +26 -13
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +27 -11
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +29 -25
- package/dist/runtime/foundation/service-liveness-registry.d.ts +1 -1
- package/dist/runtime/foundation/service-mailbox.d.ts +3 -1
- package/dist/runtime/foundation/service-mailbox.js +4 -1
- package/dist/runtime/foundation/service-runtime-contracts.d.ts +3 -1
- package/dist/runtime/foundation/service-stateful-registry.d.ts +2 -0
- package/dist/runtime/foundation/service-stateful-registry.js +28 -12
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +51 -19
- package/dist/runtime/foundation/service-stateful-runtime.js +54 -5
- package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +3 -0
- package/dist/runtime/foundation/service-stateful-wire-codec.js +15 -0
- package/dist/runtime/foundation/service-wire-constants.generated.d.ts +1 -0
- package/dist/runtime/foundation/service-wire-constants.generated.js +12 -0
- package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +2 -0
- package/dist/runtime/foundation/service-wire-m6a-codec.js +17 -7
- package/dist/runtime/host/actor-placement-coordinator.d.ts +1 -1
- package/dist/runtime/host/actor-transfer-runtime.d.ts +3 -3
- package/dist/runtime/host/application-job-queue.d.ts +1 -1
- package/dist/runtime/host/index.d.ts +89 -36
- package/dist/runtime/host/index.js +101 -7
- package/dist/runtime/host/location-runtime-owner.d.ts +2 -2
- package/dist/runtime/host/service-relocation-host-runtime.d.ts +181 -44
- package/dist/runtime/host/service-relocation-host-runtime.js +47 -13
- package/dist/runtime/host/spot-address-transport.js +50 -42
- package/dist/runtime/locations/auto-connect-loop.d.ts +0 -1
- package/dist/runtime/locations/auto-connect-loop.js +25 -19
- package/dist/runtime/locations/auto-connect-planner.js +2 -5
- package/dist/runtime/locations/auto-connect-reconciler.d.ts +1 -0
- package/dist/runtime/locations/auto-connect-reconciler.js +35 -23
- package/dist/runtime/locations/in-memory-location-store.d.ts +0 -2
- package/dist/runtime/locations/in-memory-location-store.js +1 -38
- package/dist/runtime/locations/location-store-repository.d.ts +0 -1
- package/dist/runtime/locations/location-store-repository.js +13 -115
- package/dist/runtime/locations/runtime.d.ts +3 -3
- package/dist/runtime/locations/runtime.js +12 -16
- package/dist/runtime/messaging/creation-payload-codec.js +2 -2
- package/dist/runtime/protocol/service_wire_codec.generated.d.ts +4 -0
- package/dist/runtime/protocol/service_wire_codec.generated.js +17 -7
- package/dist/runtime/spots/index.d.ts +20 -5
- package/dist/runtime/spots/index.js +14 -0
- package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +3 -3
- package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +1 -0
- package/dist/runtime/spots/spot-actor-packet-dispatch.js +13 -7
- package/dist/runtime/spots/spot-actor-packet-drain.d.ts +1 -0
- package/dist/runtime/spots/spot-actor-packet-drain.js +12 -5
- package/dist/runtime/spots/spot-entry-activation.d.ts +2 -1
- package/dist/runtime/spots/spot-node-runtime-manager.d.ts +8 -6
- package/dist/runtime/spots/spot-node-runtime-manager.js +2 -0
- package/dist/runtime/spots/spot-route-packet-dispatch.js +7 -4
- package/dist/runtime/spots/spot-routed-spot-packet-dispatch.js +3 -1
- package/dist/runtime/spots/spot-serial-executor.d.ts +2 -1
- package/dist/runtime/spots/spot-subscription-dispatch.js +3 -1
- package/dist/runtime/spots/spot-timer.d.ts +1 -1
- package/dist/runtime/streams/actor-session-binding-registry.d.ts +9 -3
- package/dist/runtime/streams/actor-session-binding-registry.js +17 -0
- package/dist/runtime/streams/index.d.ts +3 -3
- package/dist/runtime/streams/index.js +22 -2
- package/dist/runtime/streams/managed-stream.js +2 -1
- package/dist/runtime/streams/session-calls.d.ts +1 -1
- package/dist/runtime/streams/session-calls.js +3 -9
- package/dist/runtime/streams/session-context.d.ts +2 -2
- package/dist/runtime/streams/session-context.js +2 -2
- package/dist/runtime/streams/stream-session-runtime.d.ts +1 -0
- package/dist/runtime/streams/stream-session-runtime.js +37 -12
- package/package.json +3 -3
- package/src/contracts/Actors/ZLinkActor.ts +6 -0
- package/src/contracts/Actors/ZLinkSessionBindingIdentity.ts +7 -0
- package/src/contracts/Actors/index.ts +1 -0
- package/src/contracts/Common/payload-slab.ts +29 -0
- package/src/contracts/Handlers/Contexts.ts +1 -0
- package/src/internal.ts +17 -0
- package/src/runtime/actors/actor-client.ts +48 -23
- package/src/runtime/actors/actor-mailbox.ts +1 -1
- package/src/runtime/actors/actor-runtime-state.ts +1 -1
- package/src/runtime/actors/spot-actor-dispatch.ts +4 -3
- package/src/runtime/application-jobs/application-ingress-record-owner.ts +4 -0
- package/src/runtime/backend/contracts/index.ts +2 -0
- package/src/runtime/backend/mesh-completion-table.ts +2 -0
- package/src/runtime/backend/node/node-mesh-backend-adapter.ts +5 -1
- package/src/runtime/backend/node/node-raw-binding-port.ts +20 -7
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +18 -10
- package/src/runtime/channels/channel-dispatch-pipeline.ts +6 -5
- package/src/runtime/channels/channel-dispatchers.ts +39 -19
- package/src/runtime/channels/channel-envelope.ts +547 -20
- package/src/runtime/channels/channel-receive-loops.ts +93 -36
- package/src/runtime/channels/channel-runtime-lifecycle.ts +2 -2
- package/src/runtime/channels/channel-socket-registry.ts +3 -3
- package/src/runtime/diagnostics/runtime-metrics.ts +3 -1
- package/src/runtime/execution/serial-execution-queue.ts +13 -13
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +40 -39
- package/src/runtime/foundation/service-liveness-registry.ts +1 -1
- package/src/runtime/foundation/service-mailbox.ts +6 -1
- package/src/runtime/foundation/service-runtime-contracts.ts +7 -1
- package/src/runtime/foundation/service-stateful-registry.ts +39 -14
- package/src/runtime/foundation/service-stateful-runtime.ts +87 -21
- package/src/runtime/foundation/service-stateful-wire-codec.ts +18 -0
- package/src/runtime/foundation/service-wire-constants.generated.ts +10 -0
- package/src/runtime/foundation/service-wire-m6a-codec.ts +29 -7
- package/src/runtime/host/actor-placement-coordinator.ts +1 -1
- package/src/runtime/host/actor-transfer-runtime.ts +3 -3
- package/src/runtime/host/application-job-queue.ts +1 -1
- package/src/runtime/host/index.ts +183 -44
- package/src/runtime/host/location-runtime-owner.ts +2 -2
- package/src/runtime/host/service-relocation-host-runtime.ts +101 -65
- package/src/runtime/host/spot-address-transport.ts +67 -61
- package/src/runtime/locations/auto-connect-loop.ts +24 -16
- package/src/runtime/locations/auto-connect-planner.ts +2 -5
- package/src/runtime/locations/auto-connect-reconciler.ts +37 -24
- package/src/runtime/locations/in-memory-location-store.ts +1 -74
- package/src/runtime/locations/location-store-repository.ts +32 -153
- package/src/runtime/locations/runtime.ts +16 -28
- package/src/runtime/messaging/creation-payload-codec.ts +3 -3
- package/src/runtime/protocol/service_wire_codec.generated.ts +18 -8
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
- package/src/runtime/spots/index.ts +28 -4
- package/src/runtime/spots/spot-actor-admission-coordinator.ts +1 -1
- package/src/runtime/spots/spot-actor-packet-dispatch.ts +16 -6
- package/src/runtime/spots/spot-actor-packet-drain.ts +21 -6
- package/src/runtime/spots/spot-entry-activation.ts +1 -1
- package/src/runtime/spots/spot-node-runtime-manager.ts +8 -6
- package/src/runtime/spots/spot-route-packet-dispatch.ts +8 -4
- package/src/runtime/spots/spot-routed-spot-packet-dispatch.ts +3 -1
- package/src/runtime/spots/spot-serial-executor.ts +1 -1
- package/src/runtime/spots/spot-subscription-dispatch.ts +3 -1
- package/src/runtime/spots/spot-timer.ts +1 -1
- package/src/runtime/streams/actor-session-binding-registry.ts +30 -3
- package/src/runtime/streams/index.ts +30 -4
- package/src/runtime/streams/managed-stream.ts +2 -1
- package/src/runtime/streams/session-calls.ts +9 -14
- package/src/runtime/streams/session-context.ts +6 -11
- package/src/runtime/streams/stream-session-runtime.ts +49 -20
|
@@ -8,6 +8,7 @@ const internal_observation_1 = require("../internal-observation");
|
|
|
8
8
|
const node_os_1 = require("node:os");
|
|
9
9
|
const application_ingress_record_owner_1 = require("../application-jobs/application-ingress-record-owner");
|
|
10
10
|
const submission_result_1 = require("../messaging/submission-result");
|
|
11
|
+
const payload_slab_1 = require("../../contracts/Common/payload-slab");
|
|
11
12
|
const runtime_values_1 = require("../backend/runtime-values");
|
|
12
13
|
const service_wire_codec_generated_1 = require("../protocol/service_wire_codec.generated");
|
|
13
14
|
const service_wire_binary_primitives_1 = require("./service-wire-binary-primitives");
|
|
@@ -774,28 +775,13 @@ class RawServiceMeshRuntime {
|
|
|
774
775
|
replyGenericProtocolError(received) {
|
|
775
776
|
if (received.requestSeq === undefined || received.parts.length === 0)
|
|
776
777
|
return false;
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
if (correlation === undefined)
|
|
785
|
-
return false;
|
|
786
|
-
const reply = [
|
|
787
|
-
(0, service_wire_m6a_codec_1.encodeReplyHeader)(correlation, runtime_values_1.RequestResult.ProtocolError, service_wire_constants_generated_1.ServiceWireFrameworkErrorCode.requestProtocolError)
|
|
788
|
-
];
|
|
789
|
-
if (received.reply === undefined)
|
|
790
|
-
return false;
|
|
791
|
-
received.reply(reply);
|
|
792
|
-
return true;
|
|
793
|
-
}
|
|
794
|
-
catch (error) {
|
|
795
|
-
if (error instanceof service_wire_m6a_codec_1.ServiceWireProtocolError)
|
|
796
|
-
return false;
|
|
797
|
-
throw error;
|
|
798
|
-
}
|
|
778
|
+
const correlation = (0, service_wire_constants_generated_1.recoverRequestCorrelation)(received.parts[0]);
|
|
779
|
+
if (correlation === undefined || received.reply === undefined)
|
|
780
|
+
return false;
|
|
781
|
+
received.reply([
|
|
782
|
+
(0, service_wire_m6a_codec_1.encodeReplyHeader)(correlation, runtime_values_1.RequestResult.ProtocolError, service_wire_constants_generated_1.ServiceWireFrameworkErrorCode.requestProtocolError)
|
|
783
|
+
]);
|
|
784
|
+
return true;
|
|
799
785
|
}
|
|
800
786
|
processReceived(received, nowMs, applicationJobOwner) {
|
|
801
787
|
const peer = this.topology.admittedPeer(received.sourceRid);
|
|
@@ -1081,12 +1067,29 @@ class RawServiceMeshRuntime {
|
|
|
1081
1067
|
];
|
|
1082
1068
|
this.startControlSend(probe.nodeRoutingId, service_wire_m6a_codec_1.M6aServiceWireCommand.livenessProbe, () => this.submitControl(probe.nodeRoutingId, parts));
|
|
1083
1069
|
}
|
|
1070
|
+
const failures = [];
|
|
1084
1071
|
for (const nodeRoutingId of result.timedOutNodes) {
|
|
1085
1072
|
const peer = this.topology.peer(nodeRoutingId);
|
|
1086
1073
|
if (peer === undefined)
|
|
1087
1074
|
continue;
|
|
1088
|
-
|
|
1075
|
+
try {
|
|
1076
|
+
this.removePeer(peer, () => {
|
|
1077
|
+
try {
|
|
1078
|
+
this.requireStarted().disconnectRid(nodeRoutingId);
|
|
1079
|
+
}
|
|
1080
|
+
catch (error) {
|
|
1081
|
+
failures.push(error);
|
|
1082
|
+
}
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
catch (error) {
|
|
1086
|
+
failures.push(error);
|
|
1087
|
+
}
|
|
1089
1088
|
}
|
|
1089
|
+
if (failures.length === 1)
|
|
1090
|
+
throw failures[0];
|
|
1091
|
+
if (failures.length > 1)
|
|
1092
|
+
throw new AggregateError(failures, 'Liveness peer cleanup failed.');
|
|
1090
1093
|
return result;
|
|
1091
1094
|
}
|
|
1092
1095
|
/**
|
|
@@ -1168,7 +1171,7 @@ class RawServiceMeshRuntime {
|
|
|
1168
1171
|
const header = channelName === undefined
|
|
1169
1172
|
? (0, service_wire_m6a_codec_1.encodeNodeRequestHeader)(correlation)
|
|
1170
1173
|
: (0, service_wire_m6a_codec_1.encodeChannelRequestHeader)(correlation, channelName);
|
|
1171
|
-
|
|
1174
|
+
let encodedPayload = this.applicationFrame(payload);
|
|
1172
1175
|
let selectedTargetNodeRoutingId = targetNodeRoutingId;
|
|
1173
1176
|
if (selectedTargetNodeRoutingId !== this.descriptor.nodeRoutingId &&
|
|
1174
1177
|
!this.isPeerRouteReady(selectedTargetNodeRoutingId)) {
|
|
@@ -1195,6 +1198,7 @@ class RawServiceMeshRuntime {
|
|
|
1195
1198
|
}
|
|
1196
1199
|
}
|
|
1197
1200
|
if (selectedTargetNodeRoutingId === this.descriptor.nodeRoutingId) {
|
|
1201
|
+
encodedPayload = (0, payload_slab_1.copyPayloadBytes)(encodedPayload);
|
|
1198
1202
|
// The local admission path aborts its capacity wait on the terminal
|
|
1199
1203
|
// Promise, so a local request always keeps the Promise entry.
|
|
1200
1204
|
const pending = requestOperation === undefined
|
|
@@ -1317,7 +1321,7 @@ class RawServiceMeshRuntime {
|
|
|
1317
1321
|
return Buffer.isBuffer(payload) ? payload : (0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload);
|
|
1318
1322
|
}
|
|
1319
1323
|
applicationPayload(payload) {
|
|
1320
|
-
return Buffer.isBuffer(payload) ? (0, service_wire_m6a_codec_1.
|
|
1324
|
+
return Buffer.isBuffer(payload) ? (0, service_wire_m6a_codec_1.decodeApplicationPayload)(payload) : payload;
|
|
1321
1325
|
}
|
|
1322
1326
|
admitPeer(descriptor, connectionId, nowMs, expected, command = service_wire_m6a_codec_1.M6aServiceWireCommand.hello, descriptorBytes) {
|
|
1323
1327
|
const previous = this.topology.admittedPeer(descriptor.nodeRoutingId);
|
|
@@ -25,7 +25,7 @@ export declare class ServiceLivenessConnection {
|
|
|
25
25
|
export declare class ServiceLivenessRegistry {
|
|
26
26
|
private readonly probeIntervalMs;
|
|
27
27
|
private readonly peerTimeoutMs;
|
|
28
|
-
|
|
28
|
+
readonly peers: Map<string, ServiceLivenessConnection>;
|
|
29
29
|
private nextProbeId;
|
|
30
30
|
/**
|
|
31
31
|
* The earliest probe or timeout among admitted peers. Received traffic only
|
|
@@ -6,6 +6,7 @@ export interface ServiceMailboxDispatchOwner {
|
|
|
6
6
|
prepareDispatch(record: ServiceMailboxRecord, signal: AbortSignal): boolean | Promise<boolean>;
|
|
7
7
|
}
|
|
8
8
|
export interface ServiceMailboxRecord {
|
|
9
|
+
receivedAt?: Date;
|
|
9
10
|
readonly sourcePreparationOwner?: ServiceMailboxDispatchOwner;
|
|
10
11
|
readonly ingress?: RawServiceIngressRecord;
|
|
11
12
|
readonly owner: string;
|
|
@@ -42,6 +43,7 @@ export interface ServiceMailboxRelocationSeal {
|
|
|
42
43
|
/** Level-triggered queues with one active application claim per owner. */
|
|
43
44
|
export declare class ServiceMailbox {
|
|
44
45
|
private readonly onReady?;
|
|
46
|
+
private readonly receivedAtClock;
|
|
45
47
|
private readonly application;
|
|
46
48
|
private readonly infrastructure;
|
|
47
49
|
private readonly relocationSeals;
|
|
@@ -49,7 +51,7 @@ export declare class ServiceMailbox {
|
|
|
49
51
|
private nextClaimSerial;
|
|
50
52
|
private nextRelocationSerial;
|
|
51
53
|
private closed;
|
|
52
|
-
constructor(onReady?: ((domain: ServiceMailboxDomain) => void) | undefined);
|
|
54
|
+
constructor(onReady?: ((domain: ServiceMailboxDomain) => void) | undefined, receivedAtClock?: () => Date);
|
|
53
55
|
releaseClaimedPayload(record: ServiceMailboxRecord): void;
|
|
54
56
|
tryEnqueue(record: ServiceMailboxRecord): boolean;
|
|
55
57
|
tryClaim(domain: ServiceMailboxDomain, messageBudget: number, byteBudget: number): ServiceMailboxClaim | undefined;
|
|
@@ -4,6 +4,7 @@ exports.ServiceMailbox = void 0;
|
|
|
4
4
|
/** Level-triggered queues with one active application claim per owner. */
|
|
5
5
|
class ServiceMailbox {
|
|
6
6
|
onReady;
|
|
7
|
+
receivedAtClock;
|
|
7
8
|
application;
|
|
8
9
|
infrastructure;
|
|
9
10
|
relocationSeals = new Map();
|
|
@@ -11,8 +12,9 @@ class ServiceMailbox {
|
|
|
11
12
|
nextClaimSerial = 1n;
|
|
12
13
|
nextRelocationSerial = 1n;
|
|
13
14
|
closed = false;
|
|
14
|
-
constructor(onReady) {
|
|
15
|
+
constructor(onReady, receivedAtClock = () => new Date()) {
|
|
15
16
|
this.onReady = onReady;
|
|
17
|
+
this.receivedAtClock = receivedAtClock;
|
|
16
18
|
// Host ApplicationJobQueue permits own ordinary-record admission. The
|
|
17
19
|
// Framework-owned payload lifetime does not retain Core HWM credit. A
|
|
18
20
|
// second mailbox cap would reject already-admitted work before the shared
|
|
@@ -32,6 +34,7 @@ class ServiceMailbox {
|
|
|
32
34
|
const bytes = retainedBytes(record);
|
|
33
35
|
if (this.closed)
|
|
34
36
|
return false;
|
|
37
|
+
record.receivedAt ??= this.receivedAtClock();
|
|
35
38
|
if (record.domain === 'application') {
|
|
36
39
|
if (this.relocatedOwners.has(record.owner))
|
|
37
40
|
return false;
|
|
@@ -214,6 +214,7 @@ export interface ZLinkMessageFollowOrigin {
|
|
|
214
214
|
readonly target?: import('./service-stateful-wire-codec').ServiceActorRouteFence;
|
|
215
215
|
}
|
|
216
216
|
export interface ReceiveRecord {
|
|
217
|
+
readonly receivedAt?: Date;
|
|
217
218
|
/** Resolve record prerequisites in owner order, outside an application handler turn. */
|
|
218
219
|
readonly prepareDispatch?: (this: ReceiveRecord, signal: AbortSignal) => boolean | Promise<boolean>;
|
|
219
220
|
readonly statefulMailboxRecord?: import('./service-mailbox').ServiceMailboxRecord;
|
|
@@ -234,6 +235,7 @@ export interface ReceiveRecord {
|
|
|
234
235
|
readonly kindData: ReceiveKindData | null;
|
|
235
236
|
readonly terminalResult: number;
|
|
236
237
|
readonly failureErrno: number;
|
|
238
|
+
readonly failureMessage?: string;
|
|
237
239
|
readonly parts: Message[];
|
|
238
240
|
/** Returns whether the local operation can still accept a lifecycle reply. */
|
|
239
241
|
readonly isPending?: () => boolean;
|
|
@@ -251,7 +253,7 @@ export interface ReceiveRecord {
|
|
|
251
253
|
reply(parts: MessageLike | readonly MessageLike[], flags?: number): SubmitResult;
|
|
252
254
|
replyActorJoin(joinResult: number, parts: MessageLike | readonly MessageLike[], flags?: number, replyContentType?: string): SubmitResult;
|
|
253
255
|
/** Emits a typed command-20 terminal for a lifecycle request before admission. */
|
|
254
|
-
replyFailure?(terminalResult: number, failureCode: number): SubmitResult;
|
|
256
|
+
replyFailure?(terminalResult: number, failureCode: number, tail?: import('./service-stateful-wire-codec').ServiceStatefulReplyTail): SubmitResult;
|
|
255
257
|
}
|
|
256
258
|
export interface ClaimReceiveResult {
|
|
257
259
|
readonly ok: boolean;
|
|
@@ -119,6 +119,8 @@ export declare class ServiceStatefulRegistry {
|
|
|
119
119
|
installSessionBinding(binding: ServiceSessionBinding): ServiceSessionBinding;
|
|
120
120
|
binding(actor: ServiceActorRef): ServiceSessionBinding | undefined;
|
|
121
121
|
private bindingCore;
|
|
122
|
+
/** The Actor owner admits each relayed Session sequence once per binding. */
|
|
123
|
+
acceptBoundSessionSequence(actor: ServiceActorRef, expectedBindingGeneration: bigint, sequence: bigint): boolean;
|
|
122
124
|
unbindSession(actor: ServiceActorRef, expectedBindingGeneration: bigint, expectedSessionRid?: string, expectedSessionOwnerNodeRid?: string): boolean;
|
|
123
125
|
validateBoundSession(actor: ServiceActorRef, expectedBindingGeneration: bigint): ServiceSessionBinding;
|
|
124
126
|
reserve(objectKind: ServiceObjectReservation['objectKind'], key: string, stableType: string, attempt: bigint): ServiceReservationResult;
|
|
@@ -246,7 +246,7 @@ class ServiceStatefulRegistry {
|
|
|
246
246
|
});
|
|
247
247
|
requireText(binding.sessionOwnerNodeRid, 'sessionOwnerNodeRid');
|
|
248
248
|
requirePositive(binding.bindingGeneration, 'bindingGeneration');
|
|
249
|
-
const current = this.bindings.get(actorKey(binding.actor));
|
|
249
|
+
const current = this.bindings.get(actorKey(binding.actor))?.binding;
|
|
250
250
|
if (current !== undefined &&
|
|
251
251
|
current.sessionOwnerNodeRid === binding.sessionOwnerNodeRid &&
|
|
252
252
|
current.sessionOwnerNodeGeneration === binding.sessionOwnerNodeGeneration &&
|
|
@@ -257,21 +257,34 @@ class ServiceStatefulRegistry {
|
|
|
257
257
|
throw new ServiceStaleGenerationError('binding', binding.actor.actorId);
|
|
258
258
|
}
|
|
259
259
|
const accepted = Object.freeze({ ...binding });
|
|
260
|
-
this.bindings.set(actorKey(binding.actor), accepted);
|
|
260
|
+
this.bindings.set(actorKey(binding.actor), { binding: accepted, lastAcceptedSequence: 0n });
|
|
261
261
|
return accepted;
|
|
262
262
|
}
|
|
263
263
|
binding(actor) {
|
|
264
264
|
return this.bindingCore(actor);
|
|
265
265
|
}
|
|
266
266
|
bindingCore(actor) {
|
|
267
|
-
const binding = this.bindings.get(actorKey(actor));
|
|
267
|
+
const binding = this.bindings.get(actorKey(actor))?.binding;
|
|
268
268
|
return binding?.actor.generation === actor.generation ? binding : undefined;
|
|
269
269
|
}
|
|
270
|
+
/** The Actor owner admits each relayed Session sequence once per binding. */
|
|
271
|
+
acceptBoundSessionSequence(actor, expectedBindingGeneration, sequence) {
|
|
272
|
+
const entry = this.bindings.get(actorKey(actor));
|
|
273
|
+
if (entry === undefined ||
|
|
274
|
+
entry.binding.actor.generation !== actor.generation ||
|
|
275
|
+
entry.binding.bindingGeneration !== expectedBindingGeneration ||
|
|
276
|
+
sequence <= entry.lastAcceptedSequence) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
entry.lastAcceptedSequence = sequence;
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
270
282
|
unbindSession(actor, expectedBindingGeneration, expectedSessionRid, expectedSessionOwnerNodeRid) {
|
|
271
283
|
const key = actorKey(actor);
|
|
272
|
-
const
|
|
273
|
-
if (
|
|
284
|
+
const entry = this.bindings.get(key);
|
|
285
|
+
if (entry === undefined)
|
|
274
286
|
return false;
|
|
287
|
+
const binding = entry.binding;
|
|
275
288
|
if (binding.bindingGeneration !== expectedBindingGeneration ||
|
|
276
289
|
(expectedSessionRid !== undefined &&
|
|
277
290
|
!(0, routing_id_1.routingIdsEqual)(binding.sessionRid, expectedSessionRid)) ||
|
|
@@ -387,14 +400,17 @@ class ServiceStatefulRegistry {
|
|
|
387
400
|
}
|
|
388
401
|
updateBindingMembership(actor) {
|
|
389
402
|
const key = actorKey(actor.ref);
|
|
390
|
-
const
|
|
391
|
-
if (
|
|
403
|
+
const entry = this.bindings.get(key);
|
|
404
|
+
if (entry === undefined)
|
|
392
405
|
return;
|
|
393
|
-
this.bindings.set(key,
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
406
|
+
this.bindings.set(key, {
|
|
407
|
+
binding: Object.freeze({
|
|
408
|
+
...entry.binding,
|
|
409
|
+
actor: actor.ref,
|
|
410
|
+
membershipEpoch: actor.membershipEpoch
|
|
411
|
+
}),
|
|
412
|
+
lastAcceptedSequence: entry.lastAcceptedSequence
|
|
413
|
+
});
|
|
398
414
|
}
|
|
399
415
|
}
|
|
400
416
|
exports.ServiceStatefulRegistry = ServiceStatefulRegistry;
|
|
@@ -3,12 +3,13 @@ import type { RequestRuntime } from './request-runtime';
|
|
|
3
3
|
import { inspectRuntime } from '../internal-observation';
|
|
4
4
|
import type { ZLinkAuthoritySnapshot } from '../locations/internal-location-contracts';
|
|
5
5
|
import type { ZLinkDispatchErrorReporter } from '../channels/dispatch-error-reporter';
|
|
6
|
-
import type
|
|
6
|
+
import { type ActorJoin28 } from '../protocol/service_wire_pilot_codec.generated';
|
|
7
|
+
import type { RawServiceIngressRecord, RawServiceMeshRuntime, RawServicePumpResult } from './raw-service-mesh-runtime';
|
|
7
8
|
import type { ServiceInstanceActivationRecoveryEnvelope } from './service-instance-activation-recovery-codec';
|
|
8
9
|
import type { ServiceMailboxRecord } from './service-mailbox';
|
|
9
10
|
import { type ReceiveKindData, type ServiceStreamSessionBinding, type StreamSessionActorAuthorityFence } from './service-runtime-contracts';
|
|
10
|
-
import { ServiceStatefulRegistry, type ServiceActorRef, type ServiceActorState, type ServiceSessionBinding, type ServiceSpotRef, type ServiceSpotState } from './service-stateful-registry';
|
|
11
|
-
import { type ServiceActorCreateRecord, type ServiceActorRouteFence, type ServiceDirectSpotRouteFence, type ServiceInstanceActivationTarget, type ServiceInstanceColdActivationTarget, type ServiceInstanceRouteFence, type ServiceRetiredBoundSessionRouteFence, type ServiceStatefulReplyTail, type ServiceStatefulWireRecord, type ServiceUserSpotCloseRecord, type ServiceUserSpotCreateRecord } from './service-stateful-wire-codec';
|
|
11
|
+
import { ServiceStatefulRegistry, ServiceTerminalOperationRegistry, type ServiceActorRef, type ServiceActorState, type ServiceSessionBinding, type ServiceSpotRef, type ServiceSpotState } from './service-stateful-registry';
|
|
12
|
+
import { type ServiceActorCreateRecord, type ServiceActorRouteFence, type ServiceDirectSpotRouteFence, type ServiceInstanceActivationTarget, type ServiceInstanceColdActivationTarget, type ServiceInstanceRouteFence, type ServiceRetiredBoundSessionRouteFence, type ServiceSpotRouteFence, type ServiceStatefulReplyTail, type ServiceStatefulWireRecord, type ServiceUserSpotCloseRecord, type ServiceUserSpotCreateRecord } from './service-stateful-wire-codec';
|
|
12
13
|
import { type ServiceApplicationPayload } from './service-wire-m6a-codec';
|
|
13
14
|
export interface ServiceSpotMessageFollowSeal {
|
|
14
15
|
readonly key: string;
|
|
@@ -17,6 +18,7 @@ export interface ServiceSpotMessageFollowSeal {
|
|
|
17
18
|
export interface ServiceStatefulResult {
|
|
18
19
|
readonly terminalResult: number;
|
|
19
20
|
readonly failureCode: number;
|
|
21
|
+
readonly failureMessage?: string;
|
|
20
22
|
readonly payload?: ServiceApplicationPayload;
|
|
21
23
|
readonly kindData?: ReceiveKindData;
|
|
22
24
|
readonly applicationMetadata?: Buffer;
|
|
@@ -248,7 +250,7 @@ export declare class ServiceStatefulRuntime {
|
|
|
248
250
|
}>;
|
|
249
251
|
private static readonly detachedIngressScope;
|
|
250
252
|
readonly registry: ServiceStatefulRegistry;
|
|
251
|
-
|
|
253
|
+
readonly operations: ServiceTerminalOperationRegistry<ServiceStatefulResult, bigint>;
|
|
252
254
|
private readonly sessionDeliveries;
|
|
253
255
|
/**
|
|
254
256
|
* A local Actor owner can replace its own session binding before the
|
|
@@ -284,7 +286,12 @@ export declare class ServiceStatefulRuntime {
|
|
|
284
286
|
private dispatchErrors?;
|
|
285
287
|
private sessionOwnerDescriptors?;
|
|
286
288
|
private dispatchErrorMeshName?;
|
|
287
|
-
|
|
289
|
+
readonly admittedUserSpotOperations: Map<string, {
|
|
290
|
+
readonly request: string;
|
|
291
|
+
readonly replayExpiresAtMs: number;
|
|
292
|
+
readonly result: Promise<ServiceUserSpotOperationResult>;
|
|
293
|
+
settled: boolean;
|
|
294
|
+
}>;
|
|
288
295
|
private readonly pendingInstanceActivations;
|
|
289
296
|
private closed;
|
|
290
297
|
constructor(raw: RawServiceMeshRuntime, nodeRid: string, nodeGeneration: bigint, onPendingOperationsChanged?: () => void, requests?: RequestRuntime | undefined);
|
|
@@ -397,31 +404,56 @@ export declare class ServiceStatefulRuntime {
|
|
|
397
404
|
close(): void;
|
|
398
405
|
cancelPendingOperations(reason: unknown): void;
|
|
399
406
|
private ingress;
|
|
400
|
-
|
|
407
|
+
handleIngress(ingress: RawServiceIngressRecord, record: ServiceStatefulWireRecord, payloadFrame: Buffer | undefined, metadataFrame?: Buffer, canonicalActorJoin?: ActorJoin28, canonicalActorJoinPayload?: ServiceApplicationPayload): Promise<RawServicePumpResult>;
|
|
401
408
|
private enqueueInstanceSpot;
|
|
402
409
|
private needsInstanceApplicationMaterialization;
|
|
403
|
-
|
|
404
|
-
|
|
410
|
+
enqueueActivatedInstanceSpot(ingress: RawServiceIngressRecord, record: Extract<ServiceStatefulWireRecord, {
|
|
411
|
+
readonly kind: 'instanceSpot';
|
|
412
|
+
}>, payloadFrame: Buffer, spot: Pick<ServiceSpotState, 'ref'>, localReply?: NonNullable<ServiceStatefulMailboxData['reply']>, onTerminalCompletion?: NonNullable<ServiceStatefulMailboxData['onTerminalCompletion']>, metadataFrame?: Buffer, onHandlerTurnStarted?: () => void, applicationTargetOverride?: ServiceInstanceApplicationTarget): RawServicePumpResult;
|
|
413
|
+
continueMissingInstanceActivation(ingress: RawServiceIngressRecord, record: Extract<ServiceStatefulWireRecord, {
|
|
414
|
+
readonly kind: 'instanceSpot';
|
|
415
|
+
readonly activation: 'missing';
|
|
416
|
+
}>, payloadFrame: Buffer, localReply?: NonNullable<ServiceStatefulMailboxData['reply']>, metadataFrame?: Buffer): Promise<void>;
|
|
405
417
|
private continueReadyInstanceMaterialization;
|
|
406
418
|
/**
|
|
407
419
|
* Spot messaging §4 step 12 with §7 step 1: a Close waits for the first terminal
|
|
408
420
|
* record only of a message whose handler turn started before it; a message queued
|
|
409
421
|
* behind that Close is not counted, so it cannot hold the Close it follows.
|
|
410
422
|
*/
|
|
411
|
-
|
|
412
|
-
|
|
423
|
+
activationTerminalCompletion(target: ServiceInstanceActivationTarget, route: ServiceInstanceRouteFence): {
|
|
424
|
+
readonly onTerminalCompletion: () => Promise<void>;
|
|
425
|
+
readonly onHandlerTurnStarted: () => void;
|
|
426
|
+
};
|
|
427
|
+
instanceApplicationTerminalCompletion(target: ServiceInstanceApplicationTarget): (() => Promise<void>) | undefined;
|
|
413
428
|
private instanceApplicationTarget;
|
|
414
429
|
private beginInstanceAuthorityOperation;
|
|
415
430
|
private completeInstanceAuthorityOperation;
|
|
416
431
|
private completeInstanceApplicationWaitersIfQuiescent;
|
|
417
|
-
|
|
432
|
+
finishMissingInstanceActivationFailure(ingress: RawServiceIngressRecord, record: Extract<ServiceStatefulWireRecord, {
|
|
433
|
+
readonly kind: 'instanceSpot';
|
|
434
|
+
readonly activation: 'missing';
|
|
435
|
+
}>, error: unknown, localReply?: NonNullable<ServiceStatefulMailboxData['reply']>, payloadFrame?: Buffer): void;
|
|
418
436
|
private requireInstanceActivation;
|
|
419
437
|
private requireReadyInstanceApplicationRoute;
|
|
420
|
-
|
|
438
|
+
validateInstanceIngress(ingress: RawServiceIngressRecord, record: Extract<ServiceStatefulWireRecord, {
|
|
439
|
+
readonly kind: 'instanceSpot';
|
|
440
|
+
}>): void;
|
|
421
441
|
private activateMissingInstance;
|
|
422
442
|
private localReadyInstance;
|
|
423
|
-
|
|
424
|
-
|
|
443
|
+
activateMissingInstanceAsync(record: Extract<ServiceStatefulWireRecord, {
|
|
444
|
+
readonly kind: 'instanceSpot';
|
|
445
|
+
readonly activation: 'missing';
|
|
446
|
+
}>, payloadFrame: Buffer, metadataFrame: Buffer | undefined, admit: (activation: {
|
|
447
|
+
readonly spot: Pick<ServiceSpotState, 'ref'>;
|
|
448
|
+
readonly route: ServiceInstanceRouteFence;
|
|
449
|
+
}) => void): Promise<unknown>;
|
|
450
|
+
runMissingInstanceActivation(record: Extract<ServiceStatefulWireRecord, {
|
|
451
|
+
readonly kind: 'instanceSpot';
|
|
452
|
+
readonly activation: 'missing';
|
|
453
|
+
}>, payloadFrame: Buffer, metadataFrame?: Buffer): Promise<{
|
|
454
|
+
readonly spot: Pick<ServiceSpotState, 'ref'>;
|
|
455
|
+
readonly route: ServiceInstanceRouteFence;
|
|
456
|
+
}>;
|
|
425
457
|
private runMissingInstanceActivationCore;
|
|
426
458
|
private acceptsLocalMissingInstancePlacement;
|
|
427
459
|
private reconcileReadyInstanceProjection;
|
|
@@ -447,7 +479,7 @@ export declare class ServiceStatefulRuntime {
|
|
|
447
479
|
private traceRejectedBoundSessionSend;
|
|
448
480
|
private prepareRequestSource;
|
|
449
481
|
prepareDispatch(record: ServiceMailboxRecord, signal: AbortSignal): boolean | Promise<boolean>;
|
|
450
|
-
|
|
482
|
+
enqueueApplicationFrame(ingress: RawServiceIngressRecord, owner: string, payloadFrame: Buffer, stateful: ServiceStatefulMailboxData): RawServicePumpResult;
|
|
451
483
|
private enqueueLifecycleFrame;
|
|
452
484
|
private enqueueFrame;
|
|
453
485
|
private enqueueLogicalMulticast;
|
|
@@ -465,13 +497,13 @@ export declare class ServiceStatefulRuntime {
|
|
|
465
497
|
private submitOneWay;
|
|
466
498
|
private submitRequest;
|
|
467
499
|
private submitActorJoin;
|
|
468
|
-
|
|
500
|
+
submitLocalRequest(ingress: RawServiceIngressRecord, pending: ServiceStatefulPendingOperation, operationKind: 'spotRequest' | 'actorRequest' | 'actorLookup' | 'actorDestroy' | 'actorJoin' | 'streamBind' | 'streamUnbind' | 'instanceSpotRequest', actor?: ServiceActorRef, deadlineUnixMs?: bigint, receiver?: import('./operation-registry').OperationObserver<unknown, unknown>): Promise<void>;
|
|
469
501
|
private completeRemoteReply;
|
|
470
502
|
private resultFromReply;
|
|
471
|
-
|
|
472
|
-
|
|
503
|
+
validateActorFence(fence: ServiceActorRouteFence): ServiceActorState;
|
|
504
|
+
validateActorMessageFence(fence: ServiceActorRouteFence): ServiceActorState;
|
|
473
505
|
private validateSpotFence;
|
|
474
|
-
|
|
506
|
+
validateDirectSpotFence(fence: ServiceSpotRouteFence): ServiceSpotState;
|
|
475
507
|
private holdOrRelaySpotMessage;
|
|
476
508
|
private restoreSpotMessageFollowRecord;
|
|
477
509
|
private drainSpotMessageFollow;
|
|
@@ -33,6 +33,7 @@ const service_stateful_registry_1 = require("./service-stateful-registry");
|
|
|
33
33
|
const service_stateful_wire_codec_1 = require("./service-stateful-wire-codec");
|
|
34
34
|
const service_wire_binary_primitives_1 = require("./service-wire-binary-primitives");
|
|
35
35
|
const service_wire_constants_generated_1 = require("./service-wire-constants.generated");
|
|
36
|
+
const payload_slab_1 = require("../../contracts/Common/payload-slab");
|
|
36
37
|
const service_wire_m6a_codec_1 = require("./service-wire-m6a-codec");
|
|
37
38
|
const MAX_NODE_TIMER_DELAY_MS = 2_147_483_647;
|
|
38
39
|
const SPOT_MOVING = 34;
|
|
@@ -1339,7 +1340,7 @@ class ServiceStatefulRuntime {
|
|
|
1339
1340
|
? record.parts.length >= 2
|
|
1340
1341
|
? record.parts[record.parts.length - 1]
|
|
1341
1342
|
: undefined
|
|
1342
|
-
: (0, service_wire_m6a_codec_1.
|
|
1343
|
+
: (0, service_wire_m6a_codec_1.encodeOwnedApplicationPayload)(canonicalPayload);
|
|
1343
1344
|
try {
|
|
1344
1345
|
return await this.handleIngress(record, decoded, payloadFrame, metadataFrame, canonicalActorJoin, canonicalPayload);
|
|
1345
1346
|
}
|
|
@@ -1455,6 +1456,48 @@ class ServiceStatefulRuntime {
|
|
|
1455
1456
|
binding.sessionOwnerNodeRid !== ingress.sourceRoutingId) {
|
|
1456
1457
|
throw new service_stateful_registry_1.ServiceStaleGenerationError('binding', record.target.actor.actorId);
|
|
1457
1458
|
}
|
|
1459
|
+
if (!this.registry.acceptBoundSessionSequence(record.target.actor, record.boundSession.bindingGeneration, record.boundSession.sequence)) {
|
|
1460
|
+
const error = new contracts_1.ZLinkFrameworkException(contracts_1.ZLinkFrameworkErrorKind.InvalidOperation, 'Bound Session relay sequence does not increase.');
|
|
1461
|
+
const reporter = this.dispatchErrors;
|
|
1462
|
+
if (record.kind === 'actorRequest') {
|
|
1463
|
+
if (reporter?.captureEnabled() === true) {
|
|
1464
|
+
reporter.report({
|
|
1465
|
+
surface: ZLinkDispatchOptions_1.ZLinkDispatchErrorSurface.StreamSession,
|
|
1466
|
+
messageKind: ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Request,
|
|
1467
|
+
reason: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorReason.StaleTarget,
|
|
1468
|
+
action: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorAction.ReplyError,
|
|
1469
|
+
meshName: this.dispatchErrorMeshName,
|
|
1470
|
+
sourceRid: ingress.sourceRoutingId,
|
|
1471
|
+
actorId: record.target.actor.actorId,
|
|
1472
|
+
correlationId: record.correlation?.toString(),
|
|
1473
|
+
error
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1476
|
+
const result = failure(error);
|
|
1477
|
+
this.replyWire(ingress, record.correlation, result.terminalResult, result.failureCode);
|
|
1478
|
+
return 'infrastructure';
|
|
1479
|
+
}
|
|
1480
|
+
const tracePoint = (0, message_flow_1.flowIfEnabled)(reporter?.flow, ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Dropped);
|
|
1481
|
+
if (tracePoint !== undefined) {
|
|
1482
|
+
tracePoint.trace({
|
|
1483
|
+
outcome: ZLinkDispatchOptions_1.ZLinkRuntimeMessageFlowOutcome.Dropped,
|
|
1484
|
+
surface: ZLinkDispatchOptions_1.ZLinkDispatchErrorSurface.StreamSession,
|
|
1485
|
+
messageKind: ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Send,
|
|
1486
|
+
packetName: (0, service_wire_m6a_codec_1.decodeApplicationPayloadView)(payloadFrame).packetName,
|
|
1487
|
+
...(this.dispatchErrorMeshName === undefined
|
|
1488
|
+
? {}
|
|
1489
|
+
: { meshName: this.dispatchErrorMeshName }),
|
|
1490
|
+
...(record.correlation === undefined
|
|
1491
|
+
? {}
|
|
1492
|
+
: { correlationId: record.correlation.toString() }),
|
|
1493
|
+
sourceRid: ingress.sourceRoutingId,
|
|
1494
|
+
actorId: record.target.actor.actorId,
|
|
1495
|
+
streamSessionId: record.boundSession.sessionRid,
|
|
1496
|
+
errorReason: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorReason.StaleTarget
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
return 'application';
|
|
1500
|
+
}
|
|
1458
1501
|
}
|
|
1459
1502
|
return this.enqueueApplicationFrame(ingress, `actor:${actor.ref.actorId}\0${actor.ref.generation}`, payloadFrame, {
|
|
1460
1503
|
receiveKind: record.kind === 'actorSend' ? service_runtime_contracts_1.ReceiveKind.ActorSend : service_runtime_contracts_1.ReceiveKind.ActorRequest,
|
|
@@ -2273,7 +2316,7 @@ class ServiceStatefulRuntime {
|
|
|
2273
2316
|
}
|
|
2274
2317
|
})
|
|
2275
2318
|
};
|
|
2276
|
-
const applicationFrame = payloadFrame ?? (0, service_wire_m6a_codec_1.
|
|
2319
|
+
const applicationFrame = payloadFrame ?? (0, service_wire_m6a_codec_1.encodeOwnedApplicationPayload)(emptyPayload());
|
|
2277
2320
|
return this.enqueueLifecycleFrame(ingress, `spot:${record.target.spot.spotId}`, applicationFrame, {
|
|
2278
2321
|
receiveKind: service_runtime_contracts_1.ReceiveKind.SpotControl,
|
|
2279
2322
|
operationKind: service_runtime_contracts_1.OperationKind.ActorJoin,
|
|
@@ -2694,7 +2737,7 @@ class ServiceStatefulRuntime {
|
|
|
2694
2737
|
async enqueueLogicalMulticast(channelName, topic, sourceSpotId, payload, sourceNodeRid = this.nodeRid) {
|
|
2695
2738
|
const applicationJobOwner = await this.raw.reserveLocalIngress();
|
|
2696
2739
|
try {
|
|
2697
|
-
await this.enqueueLogicalMulticastFrame(channelName, topic, sourceSpotId, (0, service_wire_m6a_codec_1.
|
|
2740
|
+
await this.enqueueLogicalMulticastFrame(channelName, topic, sourceSpotId, (0, service_wire_m6a_codec_1.encodeOwnedApplicationPayload)(payload), sourceNodeRid, applicationJobOwner);
|
|
2698
2741
|
}
|
|
2699
2742
|
finally {
|
|
2700
2743
|
applicationJobOwner.close();
|
|
@@ -3046,6 +3089,7 @@ class ServiceStatefulRuntime {
|
|
|
3046
3089
|
async submitOneWay(targetNodeRid, parts) {
|
|
3047
3090
|
this.requireOpen();
|
|
3048
3091
|
if (targetNodeRid === this.nodeRid) {
|
|
3092
|
+
parts = copyRetainedServiceFrames(parts);
|
|
3049
3093
|
const applicationJobOwner = await this.raw.reserveLocalIngress();
|
|
3050
3094
|
if ((0, execution_1.captureZLinkExecutionTurn)() !== undefined) {
|
|
3051
3095
|
// The sender is inside a Spot serial turn. Running ingress inline
|
|
@@ -3107,6 +3151,7 @@ class ServiceStatefulRuntime {
|
|
|
3107
3151
|
}
|
|
3108
3152
|
submitRequest(pending, targetNodeRid, parts, timeoutMs, operationKind, actor, onAdmitted, receiver) {
|
|
3109
3153
|
if (targetNodeRid === this.nodeRid) {
|
|
3154
|
+
parts = copyRetainedServiceFrames(parts);
|
|
3110
3155
|
const deadlineUnixMs = BigInt(Date.now() + Math.max(0, timeoutMs));
|
|
3111
3156
|
const capacityStop = new AbortController();
|
|
3112
3157
|
void pending.promise.then(() => capacityStop.abort(), () => capacityStop.abort());
|
|
@@ -3231,7 +3276,7 @@ class ServiceStatefulRuntime {
|
|
|
3231
3276
|
? ingress.parts.length < 2
|
|
3232
3277
|
? undefined
|
|
3233
3278
|
: ingress.parts[1]
|
|
3234
|
-
: (0, service_wire_m6a_codec_1.
|
|
3279
|
+
: (0, service_wire_m6a_codec_1.encodeOwnedApplicationPayload)({
|
|
3235
3280
|
packetName: canonicalActorJoin.payload.packetName,
|
|
3236
3281
|
contentType: canonicalActorJoin.payload.contentType,
|
|
3237
3282
|
payload: Buffer.from(canonicalActorJoin.payload.payload)
|
|
@@ -3323,7 +3368,7 @@ class ServiceStatefulRuntime {
|
|
|
3323
3368
|
currentMembershipEpoch: (previous?.membershipEpoch ?? 0n) + 1n,
|
|
3324
3369
|
resultCode: 0
|
|
3325
3370
|
};
|
|
3326
|
-
this.enqueueLifecycleFrame(ingress, `spot:${decoded.target.spot.spotId}`, payloadFrame ?? (0, service_wire_m6a_codec_1.
|
|
3371
|
+
this.enqueueLifecycleFrame(ingress, `spot:${decoded.target.spot.spotId}`, payloadFrame ?? (0, service_wire_m6a_codec_1.encodeOwnedApplicationPayload)(emptyPayload()), {
|
|
3327
3372
|
receiveKind: service_runtime_contracts_1.ReceiveKind.SpotControl,
|
|
3328
3373
|
operationKind: service_runtime_contracts_1.OperationKind.ActorJoin,
|
|
3329
3374
|
correlation: decoded.correlation,
|
|
@@ -3481,6 +3526,7 @@ class ServiceStatefulRuntime {
|
|
|
3481
3526
|
return {
|
|
3482
3527
|
terminalResult,
|
|
3483
3528
|
failureCode,
|
|
3529
|
+
...(tail?.kind === 'streamBindRejected' ? { failureMessage: tail.rejectionMessage } : {}),
|
|
3484
3530
|
...(payload === undefined ? {} : { payload }),
|
|
3485
3531
|
...(tail?.kind === 'streamBind'
|
|
3486
3532
|
? {
|
|
@@ -4219,6 +4265,9 @@ function emptyPayload() {
|
|
|
4219
4265
|
payload: Buffer.alloc(0)
|
|
4220
4266
|
};
|
|
4221
4267
|
}
|
|
4268
|
+
function copyRetainedServiceFrames(parts) {
|
|
4269
|
+
return parts.map((part) => (0, payload_slab_1.copyPayloadBytes)(part));
|
|
4270
|
+
}
|
|
4222
4271
|
/**
|
|
4223
4272
|
* The generated command-28 decoder is the canonical wire authority. Keep
|
|
4224
4273
|
* the stateful mailbox's internal record shape as an adapter only; it must
|
|
@@ -515,6 +515,9 @@ export type ServiceStatefulReplyTail = {
|
|
|
515
515
|
readonly kind: 'streamBind';
|
|
516
516
|
readonly bindingGeneration: bigint;
|
|
517
517
|
readonly authorityOwnerGeneration: bigint;
|
|
518
|
+
} | {
|
|
519
|
+
readonly kind: 'streamBindRejected';
|
|
520
|
+
readonly rejectionMessage: string;
|
|
518
521
|
} | {
|
|
519
522
|
readonly kind: 'userSpotCreate';
|
|
520
523
|
readonly createResult: 'existing' | 'created' | 'rejected';
|
|
@@ -40,6 +40,7 @@ const service_wire_codec_generated_1 = require("../protocol/service_wire_codec.g
|
|
|
40
40
|
const stream_wire_1 = require("@zlink-systems/stream-wire");
|
|
41
41
|
const InternalDefaults_1 = require("../../contracts/Configuration/InternalDefaults");
|
|
42
42
|
const framework_errors_internal_1 = require("../framework-errors-internal");
|
|
43
|
+
const runtime_values_1 = require("../backend/runtime-values");
|
|
43
44
|
const service_wire_codec_generated_2 = require("../protocol/service_wire_codec.generated");
|
|
44
45
|
const service_wire_pilot_codec_generated_1 = require("../protocol/service_wire_pilot_codec.generated");
|
|
45
46
|
const routing_id_2 = require("../routing-id");
|
|
@@ -1226,6 +1227,12 @@ function decodeStatefulReply(frame, expectedCorrelation, operationKind, hasPaylo
|
|
|
1226
1227
|
};
|
|
1227
1228
|
}
|
|
1228
1229
|
}
|
|
1230
|
+
else if (operationKind === 'streamBind' && terminalResult === runtime_values_1.RequestResult.Rejected) {
|
|
1231
|
+
tail = {
|
|
1232
|
+
kind: 'streamBindRejected',
|
|
1233
|
+
rejectionMessage: reader.text16('rejectionMessage')
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1229
1236
|
reader.end();
|
|
1230
1237
|
return {
|
|
1231
1238
|
correlation,
|
|
@@ -1275,6 +1282,8 @@ function encodeReplyTail(tail) {
|
|
|
1275
1282
|
}
|
|
1276
1283
|
case 'streamBind':
|
|
1277
1284
|
return concat(u64(tail.bindingGeneration), u64(tail.authorityOwnerGeneration));
|
|
1285
|
+
case 'streamBindRejected':
|
|
1286
|
+
return concat(text16(tail.rejectionMessage, 'rejectionMessage'));
|
|
1278
1287
|
case 'userSpotCreate':
|
|
1279
1288
|
return concat(Buffer.of(['existing', 'created', 'rejected'].indexOf(tail.createResult) + 1), rid(tail.spotId, 'spotId'), u64(tail.objectGeneration));
|
|
1280
1289
|
case 'userSpotClose':
|
|
@@ -1385,6 +1394,12 @@ function rid(value, name) {
|
|
|
1385
1394
|
function text8(value, name) {
|
|
1386
1395
|
return sized8(value, name);
|
|
1387
1396
|
}
|
|
1397
|
+
function text16(value, name) {
|
|
1398
|
+
const bytes = (0, service_wire_binary_primitives_1.encodeCanonicalServiceWireText)(value, name, 0xffff, outOfRange);
|
|
1399
|
+
if (bytes.byteLength === 0)
|
|
1400
|
+
fail(`${name} must not be empty.`);
|
|
1401
|
+
return concat(u16(bytes.byteLength), bytes);
|
|
1402
|
+
}
|
|
1388
1403
|
function optionalRid(value) {
|
|
1389
1404
|
return value === undefined ? Buffer.of(0) : rid(value, 'sourceSpotId');
|
|
1390
1405
|
}
|
|
@@ -110,3 +110,4 @@ export declare const ServiceWireExactTerminalByFailureCode: {
|
|
|
110
110
|
readonly 35: 105;
|
|
111
111
|
};
|
|
112
112
|
export declare const isValidServiceWireTerminalFailure: (terminal: number, failureCode: number) => boolean;
|
|
113
|
+
export declare function recoverRequestCorrelation(bytes: Uint8Array): bigint | undefined;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isValidServiceWireTerminalFailure = exports.ServiceWireExactTerminalByFailureCode = exports.ServiceWireBoundaryTerminalResults = exports.ServiceWireFrameworkErrorCode = exports.ServiceWireFlag = exports.ServiceWireCommand = exports.SERVICE_FRAMEWORK_MULTIPART_CONTENT_TYPE = exports.SERVICE_FRAMEWORK_MULTIPART_PACKET_NAME = exports.SERVICE_WIRE_REQUIRED_CAPABILITY = exports.SERVICE_WIRE_METADATA_BYTES = exports.SERVICE_WIRE_MAJOR = exports.SERVICE_WIRE_MAGIC = void 0;
|
|
4
|
+
exports.recoverRequestCorrelation = recoverRequestCorrelation;
|
|
4
5
|
// Generated from service-wire-v1.schema.json. Do not edit.
|
|
5
6
|
exports.SERVICE_WIRE_MAGIC = [90, 77];
|
|
6
7
|
exports.SERVICE_WIRE_MAJOR = 1;
|
|
@@ -132,3 +133,14 @@ const isValidServiceWireTerminalFailure = (terminal, failureCode) => {
|
|
|
132
133
|
return exports.ServiceWireExactTerminalByFailureCode[failureCode] === terminal;
|
|
133
134
|
};
|
|
134
135
|
exports.isValidServiceWireTerminalFailure = isValidServiceWireTerminalFailure;
|
|
136
|
+
function recoverRequestCorrelation(bytes) {
|
|
137
|
+
if (bytes.length < 13
|
|
138
|
+
|| bytes[0] !== 90 || bytes[1] !== 77
|
|
139
|
+
|| bytes[2] !== 1
|
|
140
|
+
|| ![17, 19, 22, 25, 26, 27, 28, 38, 47, 48, 49, 54].includes(bytes[3]))
|
|
141
|
+
return undefined;
|
|
142
|
+
let correlation = 0n;
|
|
143
|
+
for (let index = 5; index < 13; ++index)
|
|
144
|
+
correlation = (correlation << 8n) | BigInt(bytes[index]);
|
|
145
|
+
return correlation === 0n ? undefined : correlation;
|
|
146
|
+
}
|
|
@@ -78,6 +78,8 @@ export declare function decodeChannelRequestHeader(frame: Uint8Array, header?: S
|
|
|
78
78
|
export declare function encodeReplyHeader(correlation: bigint, terminalResult?: number, failureCode?: number, tail?: Uint8Array): Buffer;
|
|
79
79
|
export declare function decodeReplyHeader(frame: Uint8Array): ServiceReplyHeader;
|
|
80
80
|
export declare function encodeApplicationPayload(payload: ServiceApplicationPayload): Buffer;
|
|
81
|
+
/** Encodes a retained internal application frame with its own backing store. */
|
|
82
|
+
export declare function encodeOwnedApplicationPayload(payload: ServiceApplicationPayload): Buffer;
|
|
81
83
|
/**
|
|
82
84
|
* Encodes the framework multipart payload directly into its M6A application
|
|
83
85
|
* frame. The caller supplies borrowed message views; this function performs
|