@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
|
@@ -17,6 +17,7 @@ const DEFAULT_RELOCATION_CALLBACK_CONCURRENCY = 8;
|
|
|
17
17
|
|
|
18
18
|
export interface ServiceRelocationSealedWork {
|
|
19
19
|
readonly boundSessionState: Uint8Array;
|
|
20
|
+
readonly sessionBinding?: ServiceRelocationParticipant['sessionBinding'];
|
|
20
21
|
readonly queuedMessages: readonly ServiceRelocationQueuedMessage[];
|
|
21
22
|
readonly timers: readonly ServiceRelocationTimer[];
|
|
22
23
|
}
|
|
@@ -185,6 +186,7 @@ export class ServiceRelocationObjectCaptureOwner {
|
|
|
185
186
|
authorityOwnerGeneration: unit.authorityOwnerGeneration,
|
|
186
187
|
applicationState: Buffer.from(await unit.captureApplicationState(signal)),
|
|
187
188
|
boundSessionState: Buffer.from(work.boundSessionState),
|
|
189
|
+
...(work.sessionBinding === undefined ? {} : { sessionBinding: work.sessionBinding }),
|
|
188
190
|
queuedMessages: work.queuedMessages.map((message) => ({
|
|
189
191
|
sequence: message.sequence,
|
|
190
192
|
payload: Buffer.from(message.payload)
|
|
@@ -39,6 +39,7 @@ export interface ServiceRelocationParticipant {
|
|
|
39
39
|
readonly applicationStatePresent?: boolean;
|
|
40
40
|
readonly applicationState: Uint8Array;
|
|
41
41
|
readonly boundSessionState: Uint8Array;
|
|
42
|
+
readonly sessionBinding?: GeneratedRelocationEnvelopeV1['sessionBindings'][number];
|
|
42
43
|
readonly queuedMessages: readonly ServiceRelocationQueuedMessage[];
|
|
43
44
|
readonly timers: readonly ServiceRelocationTimer[];
|
|
44
45
|
}
|
|
@@ -726,6 +727,11 @@ export function encodeServiceRelocationEnvelope(
|
|
|
726
727
|
'timer due time'
|
|
727
728
|
)
|
|
728
729
|
})),
|
|
730
|
+
sessionBindings: participants.flatMap((participant, index) =>
|
|
731
|
+
participant.sessionBinding === undefined
|
|
732
|
+
? []
|
|
733
|
+
: [{ ...participant.sessionBinding, participantId: BigInt(index + 1) }]
|
|
734
|
+
),
|
|
729
735
|
pendingTimerTicks: pending.map((value) => ({
|
|
730
736
|
participantId: value.participantId,
|
|
731
737
|
order: value.order,
|
|
@@ -765,11 +771,22 @@ export function decodeServiceRelocationEnvelope(
|
|
|
765
771
|
const aggregateId = uuid(generated.relocationHigh, generated.relocationLow);
|
|
766
772
|
if (aggregateId === undefined) throw new TypeError('Invalid relocation identity.');
|
|
767
773
|
positiveBigInt(aggregateGeneration, 'aggregate generation');
|
|
774
|
+
const bindings = new Map(
|
|
775
|
+
generated.sessionBindings.map((binding) => [binding.participantId, binding])
|
|
776
|
+
);
|
|
777
|
+
for (const id of bindings.keys()) {
|
|
778
|
+
if (id > BigInt(generated.applicationStates.length))
|
|
779
|
+
throw new TypeError('Session binding participant is outside its inventory.');
|
|
780
|
+
}
|
|
768
781
|
const participants = generated.applicationStates.map((state, index) => {
|
|
769
782
|
if (state.participantId !== BigInt(index + 1)) {
|
|
770
783
|
throw new TypeError('Application state participant order is invalid.');
|
|
771
784
|
}
|
|
772
|
-
|
|
785
|
+
const binding = bindings.get(state.participantId);
|
|
786
|
+
return {
|
|
787
|
+
...wireParticipant(state.participantId, state.hasState, Buffer.from(state.payload)),
|
|
788
|
+
...(binding === undefined ? {} : { sessionBinding: binding })
|
|
789
|
+
};
|
|
773
790
|
});
|
|
774
791
|
const byId = new Map(participants.map((value) => [value.participantId!, value]));
|
|
775
792
|
for (const work of generated.savedWork) {
|
|
@@ -422,7 +422,12 @@ export interface ServiceSpot {
|
|
|
422
422
|
requestToChannel(
|
|
423
423
|
channelName: string,
|
|
424
424
|
parts: MessageLike | readonly MessageLike[],
|
|
425
|
-
options?: {
|
|
425
|
+
options?: {
|
|
426
|
+
flags?: number;
|
|
427
|
+
timeoutMs?: number;
|
|
428
|
+
applicationMetadata?: Buffer;
|
|
429
|
+
onAdmitted?: () => void;
|
|
430
|
+
}
|
|
426
431
|
): MeshOperationId;
|
|
427
432
|
sendToSpot(
|
|
428
433
|
targetNodeRid: unknown,
|
|
@@ -445,6 +450,7 @@ export interface ServiceSpot {
|
|
|
445
450
|
timeoutMs?: number;
|
|
446
451
|
applicationMetadata?: Buffer;
|
|
447
452
|
routeFence?: ServiceDirectSpotRouteFence;
|
|
453
|
+
onAdmitted?: () => void;
|
|
448
454
|
entrySpot?: boolean;
|
|
449
455
|
}
|
|
450
456
|
): MeshOperationId;
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
ZLinkDispatchMessageKind,
|
|
11
11
|
ZLinkRuntimeMessageFlowOutcome as ZLinkMessageFlowOutcome
|
|
12
12
|
} from '../../contracts/Dispatch/ZLinkDispatchOptions';
|
|
13
|
-
import {
|
|
13
|
+
import { requestDurableService } from './durable-service-request';
|
|
14
14
|
import { shouldCompactBackingArray } from '../admission';
|
|
15
15
|
import { RequestResult, SubmitResult, isZLinkBackendResultError } from '../backend/runtime-values';
|
|
16
16
|
import type { ZLinkDispatchErrorReporter } from '../channels/dispatch-error-reporter';
|
|
@@ -112,7 +112,6 @@ import {
|
|
|
112
112
|
encodeApplicationPayload,
|
|
113
113
|
type ServiceApplicationPayload
|
|
114
114
|
} from './service-wire-m6a-codec';
|
|
115
|
-
const STATEFUL_OPERATION_RETRY_TICK_MS = 20;
|
|
116
115
|
|
|
117
116
|
const MAX_NODE_TIMER_DELAY_MS = 2_147_483_647;
|
|
118
117
|
|
|
@@ -454,7 +453,7 @@ export class ServiceStatefulRuntime {
|
|
|
454
453
|
);
|
|
455
454
|
this.registry = new ServiceStatefulRegistry(nodeRid, nodeGeneration);
|
|
456
455
|
this.registry.createEntrySpot(nodeRid);
|
|
457
|
-
raw.setServiceIngress((record) => this.ingress(record));
|
|
456
|
+
raw.setServiceIngress((record) => this.ingress(record), acceptsStatefulIngress);
|
|
458
457
|
}
|
|
459
458
|
|
|
460
459
|
setMailboxDropHandler(
|
|
@@ -1326,7 +1325,8 @@ export class ServiceStatefulRuntime {
|
|
|
1326
1325
|
sourceSpotId: string,
|
|
1327
1326
|
requested: ServiceDirectSpotRouteFence,
|
|
1328
1327
|
payload: ServiceApplicationPayload,
|
|
1329
|
-
timeoutMs: number
|
|
1328
|
+
timeoutMs: number,
|
|
1329
|
+
onAdmitted?: () => void
|
|
1330
1330
|
): ServiceStatefulPendingOperation {
|
|
1331
1331
|
const pending = this.operations.register(timeoutMs);
|
|
1332
1332
|
const target = this.acceptSpotAuthority(requested);
|
|
@@ -1346,7 +1346,9 @@ export class ServiceStatefulRuntime {
|
|
|
1346
1346
|
target.targetNodeRid,
|
|
1347
1347
|
[header, encodeApplicationPayload(payload)],
|
|
1348
1348
|
timeoutMs,
|
|
1349
|
-
'spotRequest'
|
|
1349
|
+
'spotRequest',
|
|
1350
|
+
undefined,
|
|
1351
|
+
onAdmitted
|
|
1350
1352
|
);
|
|
1351
1353
|
return pending;
|
|
1352
1354
|
}
|
|
@@ -1384,7 +1386,8 @@ export class ServiceStatefulRuntime {
|
|
|
1384
1386
|
payload: ServiceApplicationPayload,
|
|
1385
1387
|
timeoutMs: number,
|
|
1386
1388
|
sourceActor?: ServiceActorRef,
|
|
1387
|
-
boundSession?: { readonly sessionRid: string; readonly bindingGeneration: bigint }
|
|
1389
|
+
boundSession?: { readonly sessionRid: string; readonly bindingGeneration: bigint },
|
|
1390
|
+
onAdmitted?: () => void
|
|
1388
1391
|
): ServiceStatefulPendingOperation {
|
|
1389
1392
|
const pending = this.operations.register(timeoutMs);
|
|
1390
1393
|
const route = this.tryActorFence(target);
|
|
@@ -1413,7 +1416,9 @@ export class ServiceStatefulRuntime {
|
|
|
1413
1416
|
target.nodeRid,
|
|
1414
1417
|
[header, encodeApplicationPayload(payload)],
|
|
1415
1418
|
timeoutMs,
|
|
1416
|
-
'actorRequest'
|
|
1419
|
+
'actorRequest',
|
|
1420
|
+
undefined,
|
|
1421
|
+
onAdmitted
|
|
1417
1422
|
);
|
|
1418
1423
|
return pending;
|
|
1419
1424
|
}
|
|
@@ -1499,7 +1504,8 @@ export class ServiceStatefulRuntime {
|
|
|
1499
1504
|
timeoutMs: number,
|
|
1500
1505
|
sourceSpotId?: string,
|
|
1501
1506
|
metadataFrame?: Uint8Array,
|
|
1502
|
-
instanceIntent = false
|
|
1507
|
+
instanceIntent = false,
|
|
1508
|
+
onAdmitted?: () => void
|
|
1503
1509
|
): ServiceStatefulPendingOperation {
|
|
1504
1510
|
const pending = this.operations.register(timeoutMs);
|
|
1505
1511
|
this.submitRequest(
|
|
@@ -1523,7 +1529,9 @@ export class ServiceStatefulRuntime {
|
|
|
1523
1529
|
metadataFrame
|
|
1524
1530
|
),
|
|
1525
1531
|
timeoutMs,
|
|
1526
|
-
'instanceSpotRequest'
|
|
1532
|
+
'instanceSpotRequest',
|
|
1533
|
+
undefined,
|
|
1534
|
+
onAdmitted
|
|
1527
1535
|
);
|
|
1528
1536
|
return pending;
|
|
1529
1537
|
}
|
|
@@ -1551,7 +1559,8 @@ export class ServiceStatefulRuntime {
|
|
|
1551
1559
|
payloadFrame: Buffer,
|
|
1552
1560
|
deadlineUnixMs: bigint,
|
|
1553
1561
|
sourceSpotId?: string,
|
|
1554
|
-
metadataFrame?: Uint8Array
|
|
1562
|
+
metadataFrame?: Uint8Array,
|
|
1563
|
+
onAdmitted?: () => void
|
|
1555
1564
|
): ServiceStatefulPendingOperation {
|
|
1556
1565
|
const timeoutMs = remainingDeadlineMs(deadlineUnixMs);
|
|
1557
1566
|
const pending = this.operations.register(timeoutMs);
|
|
@@ -1576,7 +1585,9 @@ export class ServiceStatefulRuntime {
|
|
|
1576
1585
|
metadataFrame
|
|
1577
1586
|
),
|
|
1578
1587
|
timeoutMs,
|
|
1579
|
-
'instanceSpotRequest'
|
|
1588
|
+
'instanceSpotRequest',
|
|
1589
|
+
undefined,
|
|
1590
|
+
onAdmitted
|
|
1580
1591
|
);
|
|
1581
1592
|
return pending;
|
|
1582
1593
|
}
|
|
@@ -2029,29 +2040,6 @@ export class ServiceStatefulRuntime {
|
|
|
2029
2040
|
private async ingress(
|
|
2030
2041
|
record: RawServiceIngressRecord
|
|
2031
2042
|
): Promise<RawServicePumpResult | undefined> {
|
|
2032
|
-
// These frozen relocation controls overlap the legacy stateful command
|
|
2033
|
-
// number range (30-34). They are bare infrastructure records and must
|
|
2034
|
-
// fall through to RawServiceMeshRuntime's relocation ingress instead of
|
|
2035
|
-
// being decoded as stateful actor/spot work.
|
|
2036
|
-
if (
|
|
2037
|
-
record.command === M6bServiceWireCommand.relocationReady ||
|
|
2038
|
-
record.command === M6bServiceWireCommand.relocationData ||
|
|
2039
|
-
record.command === M6bServiceWireCommand.replyRelay ||
|
|
2040
|
-
record.command === M6bServiceWireCommand.relocationCutover
|
|
2041
|
-
) {
|
|
2042
|
-
return undefined;
|
|
2043
|
-
}
|
|
2044
|
-
if (
|
|
2045
|
-
record.command < M6bServiceWireCommand.spotSend ||
|
|
2046
|
-
(record.command > M6bServiceWireCommand.instanceSpot &&
|
|
2047
|
-
record.command !== M6bServiceWireCommand.userSpotCreate &&
|
|
2048
|
-
record.command !== M6bServiceWireCommand.userSpotClose &&
|
|
2049
|
-
record.command !== M6bServiceWireCommand.actorCreate &&
|
|
2050
|
-
record.command !== M6bServiceWireCommand.messageFollow &&
|
|
2051
|
-
record.command !== M6bServiceWireCommand.boundSessionReplaced)
|
|
2052
|
-
) {
|
|
2053
|
-
return undefined;
|
|
2054
|
-
}
|
|
2055
2043
|
let decoded: ServiceStatefulWireRecord;
|
|
2056
2044
|
let canonicalActorJoin: ActorJoin28 | undefined;
|
|
2057
2045
|
try {
|
|
@@ -2371,6 +2359,7 @@ export class ServiceStatefulRuntime {
|
|
|
2371
2359
|
localReply,
|
|
2372
2360
|
metadataFrame
|
|
2373
2361
|
).finally(() => ownedIngress.applicationJobOwner?.close());
|
|
2362
|
+
ingress.onAdmitted?.();
|
|
2374
2363
|
return 'infrastructure';
|
|
2375
2364
|
}
|
|
2376
2365
|
if (
|
|
@@ -2384,6 +2373,7 @@ export class ServiceStatefulRuntime {
|
|
|
2384
2373
|
payloadFrame,
|
|
2385
2374
|
metadataFrame
|
|
2386
2375
|
).finally(() => ownedIngress.applicationJobOwner?.close());
|
|
2376
|
+
ingress.onAdmitted?.();
|
|
2387
2377
|
return 'infrastructure';
|
|
2388
2378
|
}
|
|
2389
2379
|
const spot = this.requireInstanceActivation(ingress, record);
|
|
@@ -3821,7 +3811,10 @@ export class ServiceStatefulRuntime {
|
|
|
3821
3811
|
stateful,
|
|
3822
3812
|
applicationJob
|
|
3823
3813
|
});
|
|
3824
|
-
if (accepted)
|
|
3814
|
+
if (accepted) {
|
|
3815
|
+
ingress.onAdmitted?.();
|
|
3816
|
+
return domain;
|
|
3817
|
+
}
|
|
3825
3818
|
applicationJob.close();
|
|
3826
3819
|
if (stateful.reply !== undefined) {
|
|
3827
3820
|
const result = failure(
|
|
@@ -4212,64 +4205,15 @@ export class ServiceStatefulRuntime {
|
|
|
4212
4205
|
operationKind: 'userSpotCreate' | 'userSpotClose' | 'actorCreate' | 'streamBind' | 'actorJoin',
|
|
4213
4206
|
deadlineMs: number
|
|
4214
4207
|
): Promise<readonly Buffer[]> {
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
true
|
|
4223
|
-
)
|
|
4224
|
-
);
|
|
4225
|
-
}
|
|
4226
|
-
});
|
|
4227
|
-
let wasAdmitted = false;
|
|
4228
|
-
try {
|
|
4229
|
-
// Only durable lifecycle operations replay these frozen frames. Received
|
|
4230
|
-
// envelopes leave this loop before decoding, including failure terminals.
|
|
4231
|
-
for (;;) {
|
|
4232
|
-
this.requireOpen();
|
|
4233
|
-
stop.signal.throwIfAborted();
|
|
4234
|
-
const remainingMs = Math.ceil(deadlineMs - performance.now());
|
|
4235
|
-
if (remainingMs <= 0) throw durableOperationExhausted(operationKind, wasAdmitted);
|
|
4236
|
-
try {
|
|
4237
|
-
return await awaitWithAbort(
|
|
4238
|
-
targetNodeRid === this.nodeRid
|
|
4239
|
-
? this.requestLocalInfrastructure(parts[0]!, correlation, remainingMs)
|
|
4240
|
-
: this.raw.requestService(targetNodeRid, parts, remainingMs),
|
|
4241
|
-
stop.signal
|
|
4242
|
-
);
|
|
4243
|
-
} catch (error) {
|
|
4244
|
-
stop.signal.throwIfAborted();
|
|
4245
|
-
if (!durableRequestCanReplay(error)) throw error;
|
|
4246
|
-
if (durableRequestWasAdmitted(error)) wasAdmitted = true;
|
|
4247
|
-
const retryDelayMs = Math.min(
|
|
4248
|
-
STATEFUL_OPERATION_RETRY_TICK_MS,
|
|
4249
|
-
deadlineMs - performance.now()
|
|
4250
|
-
);
|
|
4251
|
-
if (retryDelayMs <= 0) {
|
|
4252
|
-
throw durableOperationExhausted(operationKind, wasAdmitted, error);
|
|
4253
|
-
}
|
|
4254
|
-
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
4255
|
-
try {
|
|
4256
|
-
await awaitWithAbort(
|
|
4257
|
-
new Promise<void>((resolve) => {
|
|
4258
|
-
timer = setTimeout(resolve, retryDelayMs);
|
|
4259
|
-
}),
|
|
4260
|
-
stop.signal
|
|
4261
|
-
);
|
|
4262
|
-
} finally {
|
|
4263
|
-
if (timer !== undefined) clearTimeout(timer);
|
|
4264
|
-
}
|
|
4265
|
-
}
|
|
4266
|
-
}
|
|
4267
|
-
} catch (error) {
|
|
4268
|
-
stop.signal.throwIfAborted();
|
|
4269
|
-
throw error;
|
|
4270
|
-
} finally {
|
|
4271
|
-
unobserve();
|
|
4208
|
+
this.requireOpen();
|
|
4209
|
+
if (targetNodeRid === this.nodeRid) {
|
|
4210
|
+
return this.requestLocalInfrastructure(
|
|
4211
|
+
parts[0]!,
|
|
4212
|
+
correlation,
|
|
4213
|
+
Math.ceil(deadlineMs - performance.now())
|
|
4214
|
+
);
|
|
4272
4215
|
}
|
|
4216
|
+
return requestDurableService(this.raw, targetNodeRid, parts, deadlineMs, operationKind);
|
|
4273
4217
|
}
|
|
4274
4218
|
|
|
4275
4219
|
private requestLocalInfrastructure(
|
|
@@ -4393,7 +4337,8 @@ export class ServiceStatefulRuntime {
|
|
|
4393
4337
|
| 'streamBind'
|
|
4394
4338
|
| 'streamUnbind'
|
|
4395
4339
|
| 'instanceSpotRequest',
|
|
4396
|
-
actor?: ServiceActorRef
|
|
4340
|
+
actor?: ServiceActorRef,
|
|
4341
|
+
onAdmitted?: () => void
|
|
4397
4342
|
): void {
|
|
4398
4343
|
if (targetNodeRid === this.nodeRid) {
|
|
4399
4344
|
const deadlineUnixMs = BigInt(Date.now() + Math.max(0, timeoutMs));
|
|
@@ -4411,6 +4356,7 @@ export class ServiceStatefulRuntime {
|
|
|
4411
4356
|
flags: parts[0]![SERVICE_WIRE_FLAGS_OFFSET]!,
|
|
4412
4357
|
sourceRoutingId: this.nodeRid,
|
|
4413
4358
|
requestSequence: pending.id,
|
|
4359
|
+
onAdmitted,
|
|
4414
4360
|
parts,
|
|
4415
4361
|
applicationJobOwner
|
|
4416
4362
|
};
|
|
@@ -4429,7 +4375,7 @@ export class ServiceStatefulRuntime {
|
|
|
4429
4375
|
})().catch((error) => this.operations.reply(pending.id, failure(error)));
|
|
4430
4376
|
return;
|
|
4431
4377
|
}
|
|
4432
|
-
void this.raw.requestService(targetNodeRid, parts, timeoutMs).then(
|
|
4378
|
+
void this.raw.requestService(targetNodeRid, parts, timeoutMs, onAdmitted).then(
|
|
4433
4379
|
(reply) => this.completeRemoteReply(pending, targetNodeRid, operationKind, actor, reply),
|
|
4434
4380
|
(error) => this.operations.fail(pending.id, error)
|
|
4435
4381
|
);
|
|
@@ -5414,37 +5360,6 @@ function operationReplayExpired(replayExpiresAtMs: number): boolean {
|
|
|
5414
5360
|
return performance.now() > replayExpiresAtMs;
|
|
5415
5361
|
}
|
|
5416
5362
|
|
|
5417
|
-
function durableRequestWasAdmitted(error: unknown): boolean {
|
|
5418
|
-
// Request NotConnected includes Core handover of an admitted request. The
|
|
5419
|
-
// submit/request phase, rather than the result number, owns this distinction.
|
|
5420
|
-
return isZLinkBackendResultError(error) && error.operation === 'request';
|
|
5421
|
-
}
|
|
5422
|
-
|
|
5423
|
-
function durableOperationExhausted(
|
|
5424
|
-
operationKind: 'userSpotCreate' | 'userSpotClose' | 'actorCreate' | 'streamBind' | 'actorJoin',
|
|
5425
|
-
wasAdmitted: boolean,
|
|
5426
|
-
cause?: unknown
|
|
5427
|
-
): ZLinkFrameworkException {
|
|
5428
|
-
return createInternalFrameworkException(
|
|
5429
|
-
wasAdmitted
|
|
5430
|
-
? ZLinkFrameworkInternalErrorKind.DeadlineExceeded
|
|
5431
|
-
: ZLinkFrameworkInternalErrorKind.RouteNotConnected,
|
|
5432
|
-
`${operationKind} exhausted its end-to-end deadline.`,
|
|
5433
|
-
true,
|
|
5434
|
-
cause
|
|
5435
|
-
);
|
|
5436
|
-
}
|
|
5437
|
-
|
|
5438
|
-
function durableRequestCanReplay(error: unknown): boolean {
|
|
5439
|
-
if (!isZLinkBackendResultError(error)) return false;
|
|
5440
|
-
return error.operation === 'request'
|
|
5441
|
-
? error.result === RequestResult.NotConnected || error.result === RequestResult.TimedOut
|
|
5442
|
-
: error.result === SubmitResult.NotConnected ||
|
|
5443
|
-
error.result === SubmitResult.NotFound ||
|
|
5444
|
-
error.result === SubmitResult.NotAdmitted ||
|
|
5445
|
-
error.result === SubmitResult.Backpressured;
|
|
5446
|
-
}
|
|
5447
|
-
|
|
5448
5363
|
function remainingDeadlineMs(deadlineUnixMs: bigint): number {
|
|
5449
5364
|
const remainingMs = deadlineUnixMs - BigInt(Date.now());
|
|
5450
5365
|
if (remainingMs <= 0n) {
|
|
@@ -5937,3 +5852,30 @@ export function statefulMailboxData(
|
|
|
5937
5852
|
): ServiceStatefulMailboxData | undefined {
|
|
5938
5853
|
return record.stateful as ServiceStatefulMailboxData | undefined;
|
|
5939
5854
|
}
|
|
5855
|
+
|
|
5856
|
+
function acceptsStatefulIngress(command: number): boolean {
|
|
5857
|
+
// These frozen relocation controls overlap the legacy stateful command
|
|
5858
|
+
// number range (30-34). They are bare infrastructure records and must
|
|
5859
|
+
// fall through to RawServiceMeshRuntime's relocation ingress instead of
|
|
5860
|
+
// being decoded as stateful actor/spot work.
|
|
5861
|
+
if (
|
|
5862
|
+
command === M6bServiceWireCommand.relocationReady ||
|
|
5863
|
+
command === M6bServiceWireCommand.relocationData ||
|
|
5864
|
+
command === M6bServiceWireCommand.replyRelay ||
|
|
5865
|
+
command === M6bServiceWireCommand.relocationCutover
|
|
5866
|
+
) {
|
|
5867
|
+
return false;
|
|
5868
|
+
}
|
|
5869
|
+
if (
|
|
5870
|
+
command < M6bServiceWireCommand.spotSend ||
|
|
5871
|
+
(command > M6bServiceWireCommand.instanceSpot &&
|
|
5872
|
+
command !== M6bServiceWireCommand.userSpotCreate &&
|
|
5873
|
+
command !== M6bServiceWireCommand.userSpotClose &&
|
|
5874
|
+
command !== M6bServiceWireCommand.actorCreate &&
|
|
5875
|
+
command !== M6bServiceWireCommand.messageFollow &&
|
|
5876
|
+
command !== M6bServiceWireCommand.boundSessionReplaced)
|
|
5877
|
+
) {
|
|
5878
|
+
return false;
|
|
5879
|
+
}
|
|
5880
|
+
return true;
|
|
5881
|
+
}
|
|
@@ -189,11 +189,6 @@ export interface ServiceMaintenanceRelocationPrepare extends ServiceWireRelocati
|
|
|
189
189
|
/** CRC-32C (Castagnoli) over the fully assembled payload bytes. */
|
|
190
190
|
readonly payloadChecksumCrc32c: number;
|
|
191
191
|
readonly applicationVersion: bigint;
|
|
192
|
-
/** NODE-INTERNAL; serialized as extra frames after canonical command 40. */
|
|
193
|
-
readonly nodeInternalBoundSessions?: readonly {
|
|
194
|
-
readonly participantKey: string;
|
|
195
|
-
readonly payload: Uint8Array;
|
|
196
|
-
}[];
|
|
197
192
|
}
|
|
198
193
|
|
|
199
194
|
export interface ServiceMaintenanceRelocationReady extends ServiceWireRelocationBase {
|
|
@@ -8,7 +8,10 @@ import {
|
|
|
8
8
|
} from '../../contracts/Configuration/RegistrationBuilderPolicy';
|
|
9
9
|
import { descriptorConnectionNotRequired } from './route-mesh-connection-policy';
|
|
10
10
|
import { SmoothWeightedSelection } from './service-weighted-selection';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
SERVICE_WIRE_REQUIRED_CAPABILITY,
|
|
13
|
+
ServiceWireCommand
|
|
14
|
+
} from './service-wire-constants.generated';
|
|
12
15
|
|
|
13
16
|
export type ServiceNodeState =
|
|
14
17
|
'preparing' | 'serving' | 'retiring' | 'draining' | 'stopped' | 'error';
|
|
@@ -40,8 +43,14 @@ export interface ServiceNodeDescriptor {
|
|
|
40
43
|
readonly pendingCapacityUsed: number;
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
interface ServiceRouteAdmission {
|
|
44
47
|
readonly descriptor: ServiceNodeDescriptor;
|
|
48
|
+
readonly descriptorBytes?: Uint8Array;
|
|
49
|
+
readonly connectionId?: string | bigint;
|
|
50
|
+
readonly remoteAdmitReceived?: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface AdmittedServicePeer extends ServiceRouteAdmission {
|
|
45
54
|
// The Core selected-route generation this admission belongs to (Core ROUTER
|
|
46
55
|
// §10.1), as an opaque token compared only for equality.
|
|
47
56
|
readonly connectionId: string | bigint;
|
|
@@ -69,7 +78,7 @@ interface ServiceSelectionCacheEntry {
|
|
|
69
78
|
export class ServiceTopologyRegistry {
|
|
70
79
|
private local: ServiceNodeDescriptor;
|
|
71
80
|
private readonly peersByRid = new Map<string, AdmittedServicePeer>();
|
|
72
|
-
private readonly notRequiredByRid = new Map<string,
|
|
81
|
+
private readonly notRequiredByRid = new Map<string, ServiceRouteAdmission>();
|
|
73
82
|
private readonly knownByRid = new Map<string, ServiceNodeDescriptor>();
|
|
74
83
|
private readonly selections = new Map<string, ServiceSelectionCacheEntry>();
|
|
75
84
|
|
|
@@ -96,7 +105,7 @@ export class ServiceTopologyRegistry {
|
|
|
96
105
|
}
|
|
97
106
|
this.local = cloneDescriptor(descriptor);
|
|
98
107
|
for (const [nodeRoutingId, remote] of this.notRequiredByRid) {
|
|
99
|
-
if (!descriptorConnectionNotRequired(this.local, remote)) {
|
|
108
|
+
if (!descriptorConnectionNotRequired(this.local, remote.descriptor)) {
|
|
100
109
|
this.notRequiredByRid.delete(nodeRoutingId);
|
|
101
110
|
}
|
|
102
111
|
}
|
|
@@ -107,7 +116,9 @@ export class ServiceTopologyRegistry {
|
|
|
107
116
|
descriptor: ServiceNodeDescriptor,
|
|
108
117
|
connectionId: string | bigint,
|
|
109
118
|
expected?: ServicePeerAdmissionExpectation,
|
|
110
|
-
admitLiveness?: () => ServiceLivenessConnection
|
|
119
|
+
admitLiveness?: () => ServiceLivenessConnection,
|
|
120
|
+
command: number = ServiceWireCommand.hello,
|
|
121
|
+
descriptorBytes?: Uint8Array
|
|
111
122
|
): PeerAdmissionResult {
|
|
112
123
|
try {
|
|
113
124
|
validateDescriptor(descriptor);
|
|
@@ -127,37 +138,53 @@ export class ServiceTopologyRegistry {
|
|
|
127
138
|
) {
|
|
128
139
|
return 'invalidDescriptor';
|
|
129
140
|
}
|
|
141
|
+
const current = this.peersByRid.get(descriptor.nodeRoutingId);
|
|
142
|
+
const previous = current ?? this.notRequiredByRid.get(descriptor.nodeRoutingId);
|
|
143
|
+
const receivedAdmit =
|
|
144
|
+
command === ServiceWireCommand.admit ||
|
|
145
|
+
(previous?.connectionId === connectionId && previous.remoteAdmitReceived === true);
|
|
146
|
+
if (
|
|
147
|
+
previous !== undefined &&
|
|
148
|
+
previous.descriptor.lifecycleGeneration === descriptor.lifecycleGeneration
|
|
149
|
+
) {
|
|
150
|
+
const existing = previous.descriptor;
|
|
151
|
+
if (
|
|
152
|
+
descriptor.descriptorRevision < existing.descriptorRevision ||
|
|
153
|
+
(descriptor.descriptorRevision === existing.descriptorRevision &&
|
|
154
|
+
!(previous.descriptorBytes !== undefined && descriptorBytes !== undefined
|
|
155
|
+
? Buffer.compare(previous.descriptorBytes, descriptorBytes) === 0
|
|
156
|
+
: sameServiceNodeDescriptor(existing, descriptor))) ||
|
|
157
|
+
(descriptor.descriptorRevision > existing.descriptorRevision &&
|
|
158
|
+
command !== ServiceWireCommand.update)
|
|
159
|
+
)
|
|
160
|
+
return 'staleDescriptor';
|
|
161
|
+
if (
|
|
162
|
+
descriptor.descriptorRevision > existing.descriptorRevision &&
|
|
163
|
+
!sameImmutableDescriptor(existing, descriptor)
|
|
164
|
+
)
|
|
165
|
+
return 'invalidDescriptor';
|
|
166
|
+
} else if (command === ServiceWireCommand.update) {
|
|
167
|
+
return 'staleDescriptor';
|
|
168
|
+
}
|
|
169
|
+
const admission: ServiceRouteAdmission = {
|
|
170
|
+
descriptor: cloneDescriptor(descriptor),
|
|
171
|
+
...(descriptorBytes === undefined ? {} : { descriptorBytes: Buffer.from(descriptorBytes) }),
|
|
172
|
+
connectionId,
|
|
173
|
+
remoteAdmitReceived: receivedAdmit
|
|
174
|
+
};
|
|
130
175
|
if (descriptorConnectionNotRequired(this.local, descriptor)) {
|
|
131
176
|
this.peersByRid.delete(descriptor.nodeRoutingId);
|
|
132
177
|
this.remember(descriptor);
|
|
133
|
-
this.notRequiredByRid.set(descriptor.nodeRoutingId,
|
|
178
|
+
this.notRequiredByRid.set(descriptor.nodeRoutingId, admission);
|
|
134
179
|
this.rebuildSelections();
|
|
135
180
|
return 'notRequired';
|
|
136
181
|
}
|
|
137
182
|
this.notRequiredByRid.delete(descriptor.nodeRoutingId);
|
|
138
183
|
|
|
139
|
-
const current = this.peersByRid.get(descriptor.nodeRoutingId);
|
|
140
|
-
if (
|
|
141
|
-
current !== undefined &&
|
|
142
|
-
current.descriptor.lifecycleGeneration === descriptor.lifecycleGeneration &&
|
|
143
|
-
descriptor.descriptorRevision > current.descriptor.descriptorRevision &&
|
|
144
|
-
!sameImmutableDescriptor(current.descriptor, descriptor)
|
|
145
|
-
) {
|
|
146
|
-
return 'invalidDescriptor';
|
|
147
|
-
}
|
|
148
|
-
if (
|
|
149
|
-
current !== undefined &&
|
|
150
|
-
current.descriptor.lifecycleGeneration === descriptor.lifecycleGeneration &&
|
|
151
|
-
(current.descriptor.descriptorRevision > descriptor.descriptorRevision ||
|
|
152
|
-
(current.descriptor.descriptorRevision === descriptor.descriptorRevision &&
|
|
153
|
-
!sameServiceNodeDescriptor(current.descriptor, descriptor)))
|
|
154
|
-
) {
|
|
155
|
-
return 'staleDescriptor';
|
|
156
|
-
}
|
|
157
184
|
// Core selects the one route per RID (Core ROUTER §10.1); an admission on
|
|
158
185
|
// that route replaces the admission of a route it replaced.
|
|
159
186
|
this.peersByRid.set(descriptor.nodeRoutingId, {
|
|
160
|
-
|
|
187
|
+
...admission,
|
|
161
188
|
connectionId,
|
|
162
189
|
liveness: admitLiveness?.() ?? current?.liveness
|
|
163
190
|
});
|
|
@@ -175,10 +202,18 @@ export class ServiceTopologyRegistry {
|
|
|
175
202
|
}
|
|
176
203
|
|
|
177
204
|
peer(nodeRoutingId: string): AdmittedServicePeer | undefined {
|
|
178
|
-
const peer = this.
|
|
205
|
+
const peer = this.admittedPeer(nodeRoutingId);
|
|
179
206
|
return peer === undefined ? undefined : clonePeer(peer);
|
|
180
207
|
}
|
|
181
208
|
|
|
209
|
+
routeAdmission(nodeRoutingId: string): ServiceRouteAdmission | undefined {
|
|
210
|
+
return this.peersByRid.get(nodeRoutingId) ?? this.notRequiredByRid.get(nodeRoutingId);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
admittedPeer(nodeRoutingId: string): AdmittedServicePeer | undefined {
|
|
214
|
+
return this.peersByRid.get(nodeRoutingId);
|
|
215
|
+
}
|
|
216
|
+
|
|
182
217
|
peers(): readonly AdmittedServicePeer[] {
|
|
183
218
|
return this.peersCore();
|
|
184
219
|
}
|
|
@@ -193,12 +228,14 @@ export class ServiceTopologyRegistry {
|
|
|
193
228
|
|
|
194
229
|
notRequiredPeers(): readonly ServiceNodeDescriptor[] {
|
|
195
230
|
return [...this.notRequiredByRid.values()]
|
|
196
|
-
.sort((left, right) =>
|
|
197
|
-
|
|
231
|
+
.sort((left, right) =>
|
|
232
|
+
compareOrdinal(left.descriptor.nodeRoutingId, right.descriptor.nodeRoutingId)
|
|
233
|
+
)
|
|
234
|
+
.map((peer) => cloneDescriptor(peer.descriptor));
|
|
198
235
|
}
|
|
199
236
|
|
|
200
237
|
replaceDiscoveredNotRequired(descriptors: readonly ServiceNodeDescriptor[]): void {
|
|
201
|
-
const next = new Map<string,
|
|
238
|
+
const next = new Map<string, ServiceRouteAdmission>();
|
|
202
239
|
for (const descriptor of descriptors) {
|
|
203
240
|
validateDescriptor(descriptor);
|
|
204
241
|
if (
|
|
@@ -206,13 +243,16 @@ export class ServiceTopologyRegistry {
|
|
|
206
243
|
descriptor.nodeRoutingId !== this.local.nodeRoutingId &&
|
|
207
244
|
descriptorConnectionNotRequired(this.local, descriptor)
|
|
208
245
|
) {
|
|
209
|
-
next.set(descriptor.nodeRoutingId,
|
|
246
|
+
next.set(descriptor.nodeRoutingId, {
|
|
247
|
+
...this.notRequiredByRid.get(descriptor.nodeRoutingId),
|
|
248
|
+
descriptor: cloneDescriptor(descriptor)
|
|
249
|
+
});
|
|
210
250
|
}
|
|
211
251
|
}
|
|
212
252
|
this.notRequiredByRid.clear();
|
|
213
|
-
for (const [nodeRoutingId,
|
|
214
|
-
this.notRequiredByRid.set(nodeRoutingId,
|
|
215
|
-
this.remember(descriptor);
|
|
253
|
+
for (const [nodeRoutingId, admission] of next) {
|
|
254
|
+
this.notRequiredByRid.set(nodeRoutingId, admission);
|
|
255
|
+
this.remember(admission.descriptor);
|
|
216
256
|
}
|
|
217
257
|
this.rebuildSelections();
|
|
218
258
|
}
|
|
@@ -222,12 +262,27 @@ export class ServiceTopologyRegistry {
|
|
|
222
262
|
}
|
|
223
263
|
|
|
224
264
|
knownDescriptor(nodeRoutingId: string): ServiceNodeDescriptor | undefined {
|
|
225
|
-
const
|
|
226
|
-
const descriptor =
|
|
227
|
-
admitted ?? this.notRequiredByRid.get(nodeRoutingId) ?? this.knownByRid.get(nodeRoutingId);
|
|
265
|
+
const descriptor = this.knownPeerDescriptor(nodeRoutingId);
|
|
228
266
|
return descriptor === undefined ? undefined : cloneDescriptor(descriptor);
|
|
229
267
|
}
|
|
230
268
|
|
|
269
|
+
isObjectClientNodeDirectTarget(nodeRoutingId: string): boolean {
|
|
270
|
+
const descriptor =
|
|
271
|
+
nodeRoutingId === this.local.nodeRoutingId
|
|
272
|
+
? this.local
|
|
273
|
+
: this.knownPeerDescriptor(nodeRoutingId);
|
|
274
|
+
return descriptor?.objectRole === 'client';
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private knownPeerDescriptor(nodeRoutingId: string): ServiceNodeDescriptor | undefined {
|
|
278
|
+
const admitted = this.peersByRid.get(nodeRoutingId)?.descriptor;
|
|
279
|
+
return (
|
|
280
|
+
admitted ??
|
|
281
|
+
this.notRequiredByRid.get(nodeRoutingId)?.descriptor ??
|
|
282
|
+
this.knownByRid.get(nodeRoutingId)
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
231
286
|
hasKnownChannelTarget(channelName: string): boolean {
|
|
232
287
|
requireText(channelName, 'channelName');
|
|
233
288
|
return [...this.knownByRid.values()].some(
|