@zlink-systems/framework 0.30.1 → 0.30.2
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/Common/ZLinkEncodedPayload.d.ts +6 -0
- package/dist/contracts/Common/ZLinkEncodedPayload.js +18 -3
- package/dist/contracts/Common/encoded-payload-storage.d.ts +1 -1
- package/dist/contracts/Common/encoded-payload-storage.js +5 -2
- package/dist/contracts/Configuration/Builders.d.ts +1 -1
- package/dist/contracts/Configuration/RegistrationBuilderPolicy.d.ts +2 -1
- package/dist/contracts/Configuration/RegistrationBuilderPolicy.js +5 -1
- package/dist/contracts/Configuration/RegistrationBuilders.js +2 -2
- package/dist/contracts/Configuration/RegistrationTypes.d.ts +1 -0
- package/dist/contracts/Dispatch/ZLinkDispatchOptions.d.ts +2 -2
- package/dist/contracts/Locations/Rows.d.ts +2 -0
- package/dist/contracts/Spots/Contracts.d.ts +4 -0
- package/dist/contracts/Spots/ZLinkSpot.d.ts +3 -3
- package/dist/runtime/abort.d.ts +5 -0
- package/dist/runtime/abort.js +10 -0
- package/dist/runtime/actors/actor-authority-publication.js +3 -2
- package/dist/runtime/actors/actor-client.d.ts +0 -1
- package/dist/runtime/actors/actor-client.js +38 -45
- package/dist/runtime/actors/actor-context.d.ts +2 -0
- package/dist/runtime/actors/actor-context.js +46 -9
- package/dist/runtime/actors/actor-handoff.d.ts +7 -3
- package/dist/runtime/actors/actor-handoff.js +212 -88
- package/dist/runtime/actors/actor-join-relocation.d.ts +2 -0
- package/dist/runtime/actors/actor-local-native-join.js +4 -0
- package/dist/runtime/actors/actor-mailbox.js +27 -1
- package/dist/runtime/actors/actor-remote-joiner.js +3 -1
- package/dist/runtime/actors/actor-reply.d.ts +4 -0
- package/dist/runtime/actors/actor-reply.js +29 -0
- package/dist/runtime/actors/actor-runtime-contracts.d.ts +2 -0
- package/dist/runtime/actors/actor-runtime-state.js +2 -20
- package/dist/runtime/actors/actor-source-transfer.d.ts +1 -17
- package/dist/runtime/actors/index.d.ts +6 -2
- package/dist/runtime/actors/index.js +37 -21
- package/dist/runtime/actors/session-disconnected-wire.d.ts +8 -0
- package/dist/runtime/actors/session-disconnected-wire.js +48 -0
- package/dist/runtime/admission.d.ts +9 -1
- package/dist/runtime/admission.js +55 -32
- package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +13 -0
- package/dist/runtime/application-jobs/application-job-queue-scope.js +27 -1
- package/dist/runtime/application-jobs/contracts.d.ts +2 -0
- package/dist/runtime/backend/contracts/index.d.ts +19 -3
- package/dist/runtime/backend/mesh-actor-session-node-adapter.js +2 -2
- package/dist/runtime/backend/mesh-completion-table.js +5 -0
- package/dist/runtime/backend/mesh-dispatch-pump.d.ts +26 -2
- package/dist/runtime/backend/mesh-dispatch-pump.js +193 -107
- package/dist/runtime/backend/node/node-raw-binding-port.js +90 -58
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +33 -8
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +142 -143
- package/dist/runtime/backend/node/node-socket-backend-adapter.d.ts +1 -1
- package/dist/runtime/backend/node/node-socket-backend-adapter.js +5 -5
- package/dist/runtime/channels/channel-clients.d.ts +1 -0
- package/dist/runtime/channels/channel-clients.js +20 -5
- package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +22 -3
- package/dist/runtime/channels/channel-dispatch-pipeline.js +103 -57
- package/dist/runtime/channels/channel-dispatchers.d.ts +3 -2
- package/dist/runtime/channels/channel-dispatchers.js +44 -37
- package/dist/runtime/channels/channel-envelope.d.ts +1 -0
- package/dist/runtime/channels/channel-envelope.js +74 -31
- package/dist/runtime/channels/channel-runtime-lifecycle.d.ts +4 -3
- package/dist/runtime/channels/channel-runtime-lifecycle.js +19 -24
- package/dist/runtime/channels/channel-runtime-manager.d.ts +3 -2
- package/dist/runtime/channels/channel-runtime-manager.js +4 -4
- package/dist/runtime/channels/channel-transports.d.ts +0 -1
- package/dist/runtime/channels/channel-transports.js +35 -33
- package/dist/runtime/channels/client-server-location-runtime.js +5 -4
- package/dist/runtime/channels/dispatch-error-reporter.d.ts +2 -1
- package/dist/runtime/channels/dispatch-error-reporter.js +2 -1
- package/dist/runtime/channels/fanout-location-runtime.d.ts +1 -1
- package/dist/runtime/channels/fanout-location-runtime.js +3 -3
- package/dist/runtime/diagnostics/flow-context.js +19 -7
- package/dist/runtime/diagnostics/index.js +4 -9
- package/dist/runtime/diagnostics/message-flow.d.ts +8 -0
- package/dist/runtime/diagnostics/message-flow.js +35 -5
- package/dist/runtime/diagnostics/runtime-metrics.d.ts +2 -2
- package/dist/runtime/diagnostics/runtime-metrics.js +10 -8
- package/dist/runtime/execution/index.d.ts +4 -2
- package/dist/runtime/execution/index.js +33 -10
- package/dist/runtime/execution/serial-execution-queue.d.ts +4 -0
- package/dist/runtime/execution/serial-execution-queue.js +21 -2
- package/dist/runtime/execution/state-lane.d.ts +2 -0
- package/dist/runtime/execution/state-lane.js +5 -0
- package/dist/runtime/foundation/operation-registry.d.ts +25 -1
- package/dist/runtime/foundation/operation-registry.js +86 -18
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +39 -3
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +151 -55
- package/dist/runtime/foundation/service-liveness-registry.d.ts +12 -1
- package/dist/runtime/foundation/service-liveness-registry.js +38 -0
- package/dist/runtime/foundation/service-mailbox.d.ts +7 -0
- package/dist/runtime/foundation/service-maintenance-runtime.js +1 -6
- package/dist/runtime/foundation/service-relocation-object-owner.d.ts +2 -3
- package/dist/runtime/foundation/service-relocation-object-owner.js +48 -35
- package/dist/runtime/foundation/service-relocation-runtime.js +7 -3
- package/dist/runtime/foundation/service-runtime-contracts.d.ts +8 -1
- package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +2 -1
- package/dist/runtime/foundation/service-stateful-registry.d.ts +5 -4
- package/dist/runtime/foundation/service-stateful-registry.js +24 -26
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +72 -6
- package/dist/runtime/foundation/service-stateful-runtime.js +444 -117
- package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +14 -1
- package/dist/runtime/foundation/service-stateful-wire-codec.js +48 -27
- package/dist/runtime/foundation/service-wire-binary-primitives.d.ts +6 -1
- package/dist/runtime/foundation/service-wire-binary-primitives.js +27 -11
- package/dist/runtime/foundation/service-wire-constants.generated.d.ts +1 -0
- package/dist/runtime/foundation/service-wire-constants.generated.js +1 -0
- package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +10 -0
- package/dist/runtime/foundation/service-wire-m6a-codec.js +166 -53
- package/dist/runtime/host/actor-packet-relay.d.ts +6 -4
- package/dist/runtime/host/actor-packet-relay.js +59 -72
- package/dist/runtime/host/actor-placement-coordinator.js +25 -8
- package/dist/runtime/host/actor-transfer-runtime.d.ts +7 -31
- package/dist/runtime/host/actor-transfer-runtime.js +85 -331
- package/dist/runtime/host/application-job-queue.d.ts +24 -3
- package/dist/runtime/host/application-job-queue.js +28 -41
- package/dist/runtime/host/index.d.ts +5 -1
- package/dist/runtime/host/index.js +171 -97
- package/dist/runtime/host/location-runtime-owner.js +2 -6
- package/dist/runtime/host/mesh-router-resolver.d.ts +4 -0
- package/dist/runtime/host/mesh-router-resolver.js +14 -6
- package/dist/runtime/host/remote-bound-session-relay.d.ts +2 -4
- package/dist/runtime/host/remote-bound-session-relay.js +71 -190
- package/dist/runtime/host/route-mesh-runtime.d.ts +2 -2
- package/dist/runtime/host/route-mesh-runtime.js +5 -3
- package/dist/runtime/host/runtime-shutdown.d.ts +1 -0
- package/dist/runtime/host/runtime-shutdown.js +3 -2
- package/dist/runtime/host/service-relocation-host-runtime.d.ts +17 -7
- package/dist/runtime/host/service-relocation-host-runtime.js +550 -367
- package/dist/runtime/host/spot-runtime-options-factory.js +15 -7
- package/dist/runtime/host/user-spot-creation-coordinator.js +33 -84
- package/dist/runtime/locations/auto-connect-loop.d.ts +2 -0
- package/dist/runtime/locations/auto-connect-loop.js +22 -14
- package/dist/runtime/locations/auto-connect-reconciler.js +0 -6
- package/dist/runtime/locations/auto-connect-types.d.ts +0 -1
- package/dist/runtime/locations/descriptor-publication.d.ts +4 -0
- package/dist/runtime/locations/descriptor-publication.js +11 -0
- package/dist/runtime/locations/domain-store-contract.d.ts +1 -0
- package/dist/runtime/locations/in-memory-authority-store.d.ts +7 -1
- package/dist/runtime/locations/in-memory-authority-store.js +56 -19
- package/dist/runtime/locations/in-memory-location-store.js +1 -10
- package/dist/runtime/locations/index.d.ts +1 -0
- package/dist/runtime/locations/index.js +1 -0
- package/dist/runtime/locations/lease-tracker.js +1 -6
- package/dist/runtime/locations/location-store-repository.d.ts +51 -1
- package/dist/runtime/locations/location-store-repository.js +363 -139
- package/dist/runtime/locations/relocation-blob.d.ts +18 -1
- package/dist/runtime/locations/relocation-blob.js +52 -1
- package/dist/runtime/locations/request-source-resolver.d.ts +37 -0
- package/dist/runtime/locations/request-source-resolver.js +80 -0
- package/dist/runtime/locations/resolvers.js +1 -1
- package/dist/runtime/locations/runtime.d.ts +6 -2
- package/dist/runtime/locations/runtime.js +37 -30
- package/dist/runtime/messaging/payload-codec.d.ts +7 -1
- package/dist/runtime/messaging/payload-codec.js +7 -6
- package/dist/runtime/messaging/request-result.js +29 -26
- package/dist/runtime/protocol/service_wire_codec.generated.d.ts +19 -1
- package/dist/runtime/protocol/service_wire_codec.generated.js +85 -0
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.d.ts +7 -1
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +23 -7
- package/dist/runtime/spots/formal-remote-actor-admission-registry.js +3 -2
- package/dist/runtime/spots/index.d.ts +18 -11
- package/dist/runtime/spots/index.js +234 -176
- package/dist/runtime/spots/spot-activation-registry.d.ts +4 -2
- package/dist/runtime/spots/spot-activation-registry.js +21 -11
- package/dist/runtime/spots/spot-activation-state.d.ts +1 -1
- package/dist/runtime/spots/spot-activation-state.js +7 -7
- package/dist/runtime/spots/spot-activation.d.ts +6 -3
- package/dist/runtime/spots/spot-activation.js +123 -44
- package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +1 -0
- package/dist/runtime/spots/spot-actor-admission-coordinator.js +13 -22
- package/dist/runtime/spots/spot-actor-membership.d.ts +0 -1
- package/dist/runtime/spots/spot-actor-membership.js +0 -8
- package/dist/runtime/spots/spot-actor-packet-dispatch.js +33 -8
- package/dist/runtime/spots/spot-actor-packet-drain.js +5 -33
- package/dist/runtime/spots/spot-entry-activation.d.ts +2 -0
- package/dist/runtime/spots/spot-entry-activation.js +14 -9
- package/dist/runtime/spots/spot-handler-registry.d.ts +4 -3
- package/dist/runtime/spots/spot-handler-registry.js +6 -2
- package/dist/runtime/spots/spot-manager-internal-contracts.d.ts +2 -2
- package/dist/runtime/spots/spot-manager-public.js +2 -2
- package/dist/runtime/spots/spot-node-autoconnect.d.ts +2 -1
- package/dist/runtime/spots/spot-node-autoconnect.js +18 -39
- package/dist/runtime/spots/spot-node-runtime-manager.d.ts +19 -2
- package/dist/runtime/spots/spot-node-runtime-manager.js +115 -50
- package/dist/runtime/spots/spot-outbound.js +3 -3
- package/dist/runtime/spots/spot-remote-codec.js +5 -1
- package/dist/runtime/spots/spot-runtime-ports.d.ts +0 -1
- package/dist/runtime/spots/spot-serial-executor.d.ts +10 -0
- package/dist/runtime/spots/spot-serial-executor.js +61 -8
- package/dist/runtime/spots/spot-serial-turn-executor.js +19 -2
- package/dist/runtime/spots/spot-subscription-dispatch.js +1 -1
- package/dist/runtime/spots/spot-timer.d.ts +57 -5
- package/dist/runtime/spots/spot-timer.js +287 -103
- package/dist/runtime/streams/actor-session-binding-registry.d.ts +42 -11
- package/dist/runtime/streams/actor-session-binding-registry.js +165 -48
- package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +8 -4
- package/dist/runtime/streams/bound-actor-relay-sender.d.ts +2 -1
- package/dist/runtime/streams/bound-actor-relay-sender.js +15 -8
- package/dist/runtime/streams/bound-session-service.d.ts +1 -1
- package/dist/runtime/streams/bound-session-service.js +6 -11
- package/dist/runtime/streams/index.d.ts +3 -2
- package/dist/runtime/streams/index.js +6 -4
- package/dist/runtime/streams/managed-stream.js +3 -2
- package/dist/runtime/streams/native-fallback-bound-session.js +8 -73
- package/dist/runtime/streams/session-actor-coordinator.d.ts +2 -8
- package/dist/runtime/streams/session-actor-coordinator.js +15 -32
- package/dist/runtime/streams/stream-binding-runtime-ports.d.ts +3 -10
- package/dist/runtime/streams/stream-session-runtime.js +1 -4
- package/dist/runtime/workers/index.js +1 -1
- package/package.json +3 -3
- package/src/contracts/Common/ZLinkEncodedPayload.ts +24 -4
- package/src/contracts/Common/encoded-payload-storage.ts +6 -3
- package/src/contracts/Configuration/Builders.ts +4 -1
- package/src/contracts/Configuration/RegistrationBuilderPolicy.ts +7 -2
- package/src/contracts/Configuration/RegistrationBuilders.ts +6 -2
- package/src/contracts/Configuration/RegistrationTypes.ts +1 -0
- package/src/contracts/Dispatch/ZLinkDispatchOptions.ts +2 -1
- package/src/contracts/Locations/Rows.ts +2 -0
- package/src/contracts/Spots/Contracts.ts +8 -0
- package/src/contracts/Spots/ZLinkSpot.ts +3 -3
- package/src/runtime/abort.ts +12 -0
- package/src/runtime/actors/actor-authority-publication.ts +3 -2
- package/src/runtime/actors/actor-client.ts +61 -70
- package/src/runtime/actors/actor-context.ts +49 -9
- package/src/runtime/actors/actor-handoff.ts +282 -106
- package/src/runtime/actors/actor-join-relocation.ts +2 -0
- package/src/runtime/actors/actor-local-native-join.ts +4 -0
- package/src/runtime/actors/actor-mailbox.ts +40 -2
- package/src/runtime/actors/actor-remote-joiner.ts +6 -1
- package/src/runtime/actors/actor-reply.ts +42 -0
- package/src/runtime/actors/actor-runtime-contracts.ts +2 -0
- package/src/runtime/actors/actor-runtime-state.ts +2 -20
- package/src/runtime/actors/actor-source-transfer.ts +1 -34
- package/src/runtime/actors/index.ts +41 -27
- package/src/runtime/actors/session-disconnected-wire.ts +66 -0
- package/src/runtime/admission.ts +69 -34
- package/src/runtime/application-jobs/application-job-queue-scope.ts +33 -1
- package/src/runtime/application-jobs/contracts.ts +2 -0
- package/src/runtime/backend/contracts/index.ts +39 -4
- package/src/runtime/backend/mesh-actor-session-node-adapter.ts +5 -3
- package/src/runtime/backend/mesh-completion-table.ts +4 -0
- package/src/runtime/backend/mesh-dispatch-pump.ts +223 -124
- package/src/runtime/backend/node/node-raw-binding-port.ts +106 -70
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +221 -170
- package/src/runtime/backend/node/node-socket-backend-adapter.ts +5 -5
- package/src/runtime/channels/channel-clients.ts +49 -24
- package/src/runtime/channels/channel-dispatch-pipeline.ts +173 -124
- package/src/runtime/channels/channel-dispatchers.ts +116 -72
- package/src/runtime/channels/channel-envelope.ts +105 -36
- package/src/runtime/channels/channel-runtime-lifecycle.ts +13 -13
- package/src/runtime/channels/channel-runtime-manager.ts +12 -5
- package/src/runtime/channels/channel-transports.ts +49 -58
- package/src/runtime/channels/client-server-location-runtime.ts +11 -10
- package/src/runtime/channels/dispatch-error-reporter.ts +4 -2
- package/src/runtime/channels/fanout-location-runtime.ts +17 -9
- package/src/runtime/diagnostics/flow-context.ts +20 -7
- package/src/runtime/diagnostics/index.ts +1 -7
- package/src/runtime/diagnostics/message-flow.ts +47 -9
- package/src/runtime/diagnostics/runtime-metrics.ts +12 -10
- package/src/runtime/execution/index.ts +40 -14
- package/src/runtime/execution/serial-execution-queue.ts +31 -4
- package/src/runtime/execution/state-lane.ts +7 -0
- package/src/runtime/foundation/operation-registry.ts +98 -20
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +244 -67
- package/src/runtime/foundation/service-liveness-registry.ts +39 -0
- package/src/runtime/foundation/service-mailbox.ts +8 -0
- package/src/runtime/foundation/service-maintenance-runtime.ts +1 -8
- package/src/runtime/foundation/service-relocation-object-owner.ts +54 -37
- package/src/runtime/foundation/service-relocation-runtime.ts +7 -3
- package/src/runtime/foundation/service-runtime-contracts.ts +12 -1
- package/src/runtime/foundation/service-session-binding-ingress-port.ts +2 -1
- package/src/runtime/foundation/service-stateful-registry.ts +38 -35
- package/src/runtime/foundation/service-stateful-runtime.ts +667 -152
- package/src/runtime/foundation/service-stateful-wire-codec.ts +49 -11
- package/src/runtime/foundation/service-wire-binary-primitives.ts +35 -12
- package/src/runtime/foundation/service-wire-constants.generated.ts +1 -0
- package/src/runtime/foundation/service-wire-m6a-codec.ts +187 -51
- package/src/runtime/host/actor-packet-relay.ts +113 -94
- package/src/runtime/host/actor-placement-coordinator.ts +31 -8
- package/src/runtime/host/actor-transfer-runtime.ts +104 -430
- package/src/runtime/host/application-job-queue.ts +29 -44
- package/src/runtime/host/index.ts +286 -129
- package/src/runtime/host/location-runtime-owner.ts +2 -6
- package/src/runtime/host/mesh-router-resolver.ts +13 -5
- package/src/runtime/host/remote-bound-session-relay.ts +86 -248
- package/src/runtime/host/route-mesh-runtime.ts +11 -5
- package/src/runtime/host/runtime-shutdown.ts +15 -4
- package/src/runtime/host/service-relocation-host-runtime.ts +784 -475
- package/src/runtime/host/spot-runtime-options-factory.ts +18 -7
- package/src/runtime/host/user-spot-creation-coordinator.ts +41 -100
- package/src/runtime/locations/auto-connect-loop.ts +22 -14
- package/src/runtime/locations/auto-connect-reconciler.ts +0 -6
- package/src/runtime/locations/auto-connect-types.ts +0 -1
- package/src/runtime/locations/descriptor-publication.ts +7 -0
- package/src/runtime/locations/domain-store-contract.ts +5 -0
- package/src/runtime/locations/in-memory-authority-store.ts +85 -23
- package/src/runtime/locations/in-memory-location-store.ts +1 -12
- package/src/runtime/locations/index.ts +1 -0
- package/src/runtime/locations/lease-tracker.ts +2 -9
- package/src/runtime/locations/location-store-repository.ts +628 -152
- package/src/runtime/locations/relocation-blob.ts +75 -1
- package/src/runtime/locations/request-source-resolver.ts +138 -0
- package/src/runtime/locations/resolvers.ts +2 -2
- package/src/runtime/locations/runtime.ts +70 -40
- package/src/runtime/messaging/payload-codec.ts +8 -11
- package/src/runtime/messaging/request-result.ts +33 -29
- package/src/runtime/protocol/service_wire_codec.generated.ts +87 -4
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +13 -10
- package/src/runtime/spots/formal-remote-actor-admission-registry.ts +3 -2
- package/src/runtime/spots/index.ts +375 -281
- package/src/runtime/spots/spot-activation-registry.ts +25 -13
- package/src/runtime/spots/spot-activation-state.ts +12 -19
- package/src/runtime/spots/spot-activation.ts +170 -86
- package/src/runtime/spots/spot-actor-admission-coordinator.ts +25 -55
- package/src/runtime/spots/spot-actor-membership.ts +0 -15
- package/src/runtime/spots/spot-actor-packet-dispatch.ts +56 -11
- package/src/runtime/spots/spot-actor-packet-drain.ts +9 -52
- package/src/runtime/spots/spot-entry-activation.ts +24 -19
- package/src/runtime/spots/spot-handler-registry.ts +15 -4
- package/src/runtime/spots/spot-manager-internal-contracts.ts +2 -2
- package/src/runtime/spots/spot-manager-public.ts +2 -2
- package/src/runtime/spots/spot-node-autoconnect.ts +22 -45
- package/src/runtime/spots/spot-node-runtime-manager.ts +197 -67
- package/src/runtime/spots/spot-outbound.ts +3 -3
- package/src/runtime/spots/spot-remote-codec.ts +9 -2
- package/src/runtime/spots/spot-runtime-ports.ts +0 -1
- package/src/runtime/spots/spot-serial-executor.ts +97 -25
- package/src/runtime/spots/spot-serial-turn-executor.ts +22 -3
- package/src/runtime/spots/spot-subscription-dispatch.ts +1 -1
- package/src/runtime/spots/spot-timer.ts +413 -157
- package/src/runtime/streams/actor-session-binding-registry.ts +236 -64
- package/src/runtime/streams/actor-session-binding-runtime-owner.ts +16 -8
- package/src/runtime/streams/bound-actor-relay-sender.ts +23 -9
- package/src/runtime/streams/bound-session-service.ts +7 -10
- package/src/runtime/streams/index.ts +12 -6
- package/src/runtime/streams/managed-stream.ts +4 -2
- package/src/runtime/streams/native-fallback-bound-session.ts +14 -103
- package/src/runtime/streams/session-actor-coordinator.ts +39 -88
- package/src/runtime/streams/stream-binding-runtime-ports.ts +3 -9
- package/src/runtime/streams/stream-session-runtime.ts +1 -5
- package/src/runtime/workers/index.ts +1 -1
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
import { encodeActorReply } from '../actors/actor-reply';
|
|
2
|
+
import { ZLinkStreamMessageKind } from '../streams/protocol';
|
|
3
|
+
import {
|
|
4
|
+
decodeApplicationPayloadView,
|
|
5
|
+
encodeMultipartApplicationPayload
|
|
6
|
+
} from '../foundation/service-wire-m6a-codec';
|
|
7
|
+
import {
|
|
8
|
+
SERVICE_FRAMEWORK_MULTIPART_PACKET_NAME,
|
|
9
|
+
SERVICE_FRAMEWORK_MULTIPART_CONTENT_TYPE
|
|
10
|
+
} from '../foundation/service-wire-constants.generated';
|
|
11
|
+
import { OperationTimeoutError } from '../foundation/operation-registry';
|
|
1
12
|
import { durableRequestCanReplay } from '../foundation/durable-service-request';
|
|
2
13
|
import { UINT64_MAX, ZlinkStreamContentType } from '@zlink-systems/stream-wire';
|
|
3
14
|
import { randomBytes } from 'node:crypto';
|
|
@@ -19,6 +30,12 @@ import {
|
|
|
19
30
|
ZLinkSpotRelocationReadyOutcome,
|
|
20
31
|
ZLinkUserSpotExecutionMode
|
|
21
32
|
} from '../../contracts';
|
|
33
|
+
import {
|
|
34
|
+
ZLinkDispatchErrorSurface,
|
|
35
|
+
ZLinkDispatchMessageKind,
|
|
36
|
+
ZLinkRuntimeDispatchErrorAction as ZLinkDispatchErrorAction,
|
|
37
|
+
ZLinkRuntimeMessageFlowOutcome as ZLinkMessageFlowOutcome
|
|
38
|
+
} from '../../contracts/Dispatch/ZLinkDispatchOptions';
|
|
22
39
|
|
|
23
40
|
import type { ZLinkProviderResolver } from '../../contracts/Common/ZLinkProviderResolver';
|
|
24
41
|
import { zlinkRuntimeDefaultLocationOptions } from '../../contracts/Locations/Options';
|
|
@@ -30,7 +47,6 @@ import {
|
|
|
30
47
|
rewriteActorAuthorityRoute
|
|
31
48
|
} from '../actors/actor-authority-publication';
|
|
32
49
|
import {
|
|
33
|
-
replayActorHandoffBacklog,
|
|
34
50
|
type ZLinkActorHandoffPacket,
|
|
35
51
|
type ZLinkActorHandoffResult
|
|
36
52
|
} from '../actors/actor-handoff';
|
|
@@ -49,12 +65,19 @@ import { RequestResult, SubmitResult, ZLinkBackendResultError } from '../backend
|
|
|
49
65
|
import type { ZLinkFrameworkRegistration } from '../configuration';
|
|
50
66
|
import {
|
|
51
67
|
type ZLinkRuntimeEventPublisher,
|
|
68
|
+
type ZLinkRelocationMetricOperation,
|
|
52
69
|
type ZLinkRuntimeMetrics,
|
|
53
70
|
isRelocationDebugEnabled,
|
|
54
71
|
relocationDebug
|
|
55
72
|
} from '../diagnostics';
|
|
56
73
|
|
|
57
74
|
import { METRIC_NAMES, MILLISECONDS_PER_SECOND } from '../diagnostics/runtime-metrics';
|
|
75
|
+
import {
|
|
76
|
+
traceSessionBindingControlDrop,
|
|
77
|
+
flowIfEnabled,
|
|
78
|
+
type ZLinkMessageFlowTracer
|
|
79
|
+
} from '../diagnostics/message-flow';
|
|
80
|
+
import { dispatchErrorDetails } from '../diagnostics/dispatch-error-details';
|
|
58
81
|
import { isStructuralGuardEnabled, trackDiagnosticCompletion } from '../execution/state-lane';
|
|
59
82
|
import {
|
|
60
83
|
RECREATE_RELOCATION_CONTENT_TYPE,
|
|
@@ -160,8 +183,9 @@ import type {
|
|
|
160
183
|
} from '../locations/internal-location-contracts';
|
|
161
184
|
import type { ZLinkTrackedInstanceAuthority } from '../locations/spot-location-claims';
|
|
162
185
|
import { decodeRoutingId, encodeRoutingIdStorageHex, routingIdsEqual } from '../routing-id';
|
|
163
|
-
import type { DefaultZLinkSpotManager
|
|
186
|
+
import type { DefaultZLinkSpotManager } from '../spots';
|
|
164
187
|
import type { ZLinkSpotActivation } from '../spots/spot-activation-state';
|
|
188
|
+
import type { ZLinkSpotRouteTarget } from '../spots/spot-routing-internal';
|
|
165
189
|
import { createProviderInstance } from '../spots/spot-provider';
|
|
166
190
|
import { decodeHandoffBacklog } from '../spots/spot-remote-codec';
|
|
167
191
|
import { committedActorOwnerFence, type ZLinkActorTransferRuntime } from './actor-transfer-runtime';
|
|
@@ -219,11 +243,14 @@ const SERVICE_CONTROL_TERMINAL_CAPACITY = 4096;
|
|
|
219
243
|
const INITIAL_MEMBERSHIP_EPOCH = 1n;
|
|
220
244
|
|
|
221
245
|
interface ZLinkHostRelocationOptions {
|
|
246
|
+
readonly flowCreationEnabled?: () => boolean;
|
|
247
|
+
readonly messageFlow?: () => ZLinkMessageFlowTracer | undefined;
|
|
222
248
|
readonly registration: ZLinkFrameworkRegistration;
|
|
223
249
|
readonly providerResolver?: ZLinkProviderResolver;
|
|
224
250
|
readonly locationStore: () => ZLinkLocationStore | undefined;
|
|
225
251
|
readonly currentOwner: () => ZLinkLocationOwnerToken | undefined;
|
|
226
252
|
readonly targetAdmissionSealed?: () => boolean;
|
|
253
|
+
readonly requestPeerReconcile?: (meshName: string, peerRid: string) => Promise<void>;
|
|
227
254
|
readonly sourceQuiescenceChanged?: () => void;
|
|
228
255
|
readonly liveDescriptors: (
|
|
229
256
|
meshName: string,
|
|
@@ -233,16 +260,16 @@ interface ZLinkHostRelocationOptions {
|
|
|
233
260
|
readonly meshNode: (meshName: string) => ZLinkBackendMeshNode | undefined;
|
|
234
261
|
readonly completions: (meshName: string) => ZLinkMeshCompletionTable | undefined;
|
|
235
262
|
readonly spotManager: () => DefaultZLinkSpotManager | undefined;
|
|
236
|
-
readonly spotNodeRuntime: () => ZLinkSpotNodeRuntimeManager | undefined;
|
|
237
263
|
readonly actorManager: () => DefaultZLinkActorManager | undefined;
|
|
238
264
|
readonly actorTransfer: ZLinkActorTransferRuntime;
|
|
239
265
|
readonly boundSessionRelocation?: {
|
|
240
266
|
receiveSeal(
|
|
241
267
|
value: ServiceSessionRelocationSeal,
|
|
242
|
-
signal?: AbortSignal
|
|
243
|
-
|
|
268
|
+
signal?: AbortSignal,
|
|
269
|
+
onInstalled?: () => void
|
|
270
|
+
): Promise<ServiceSessionRelocationSealed | undefined>;
|
|
244
271
|
receiveRoute(value: ServiceSessionRelocationRoute): Promise<void>;
|
|
245
|
-
clear?(): void
|
|
272
|
+
clear?(): void | Promise<void>;
|
|
246
273
|
};
|
|
247
274
|
readonly trackInstanceSpot?: (input: ZLinkTrackedInstanceAuthority) => void;
|
|
248
275
|
readonly invalidateActorRoute?: (actorId: string) => void;
|
|
@@ -264,8 +291,11 @@ interface LocalHidden extends ServiceRelocationHiddenObject {
|
|
|
264
291
|
readonly actor?: ZLinkActor;
|
|
265
292
|
readonly activation?: ZLinkSpotActivation;
|
|
266
293
|
initialized: boolean;
|
|
267
|
-
|
|
268
|
-
|
|
294
|
+
replayStart?: Promise<void>;
|
|
295
|
+
onReplayResult?: (
|
|
296
|
+
packet: ZLinkActorHandoffPacket,
|
|
297
|
+
result: ZLinkActorHandoffResult
|
|
298
|
+
) => Promise<void>;
|
|
269
299
|
readonly replayPackets: ZLinkActorHandoffPacket[];
|
|
270
300
|
targetAuthorityOwnerGeneration?: bigint;
|
|
271
301
|
}
|
|
@@ -293,6 +323,12 @@ interface TargetPayloadAssembly {
|
|
|
293
323
|
readonly assembly: ZLinkRelocationPayloadAssembly;
|
|
294
324
|
}
|
|
295
325
|
|
|
326
|
+
/** One authority read decides commit and the snapshots eligible for Follow. */
|
|
327
|
+
type SourceAuthorityCommit = {
|
|
328
|
+
readonly kind: 'target';
|
|
329
|
+
readonly followAuthorities: ReadonlyMap<string, ZLinkAuthoritySnapshot>;
|
|
330
|
+
};
|
|
331
|
+
|
|
296
332
|
interface SourceCutoverWindow {
|
|
297
333
|
readonly meshName: string;
|
|
298
334
|
readonly targetNodeRid: RoutingId;
|
|
@@ -343,12 +379,14 @@ interface SourceActorJoinProfile {
|
|
|
343
379
|
readonly canonicalRecovery?: NonNullable<
|
|
344
380
|
Parameters<ZLinkActorJoinRelocation['relocateActorJoin']>[0]['canonicalRecovery']
|
|
345
381
|
>;
|
|
382
|
+
readonly relayReady: () => void;
|
|
346
383
|
readonly ready: Promise<void>;
|
|
347
384
|
readonly resolveReady: (failure?: ServiceRelocationPostCommitError) => void;
|
|
385
|
+
retirement?: Promise<void>;
|
|
348
386
|
}
|
|
349
387
|
|
|
350
388
|
interface PendingRelocationReplyRelay {
|
|
351
|
-
readonly ackTargetNodeRid:
|
|
389
|
+
readonly ackTargetNodeRid: RoutingId;
|
|
352
390
|
readonly identityKey: string;
|
|
353
391
|
readonly request: ServiceMaintenanceReplyRelay;
|
|
354
392
|
readonly expectedRequestSource: ServiceWireRequestSourceFence;
|
|
@@ -370,9 +408,8 @@ interface PendingSessionRelocation {
|
|
|
370
408
|
|
|
371
409
|
interface TerminalSessionRelocationControl {
|
|
372
410
|
readonly targetNodeRid: string;
|
|
373
|
-
readonly request: ServiceSessionRelocationSeal;
|
|
374
411
|
readonly requestFingerprint: string;
|
|
375
|
-
readonly
|
|
412
|
+
readonly response: ServiceSessionRelocationSealed;
|
|
376
413
|
}
|
|
377
414
|
|
|
378
415
|
/** Production host bridge from Retire inventory to remote RouteMesh owners. */
|
|
@@ -381,7 +418,6 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
381
418
|
private readonly terminalTargets = new BoundedReplayMap<string, string>(
|
|
382
419
|
RELOCATION_TARGET_TOMBSTONE_LIMIT
|
|
383
420
|
);
|
|
384
|
-
private readonly relocationAuthorityKeys = new Map<string, string>();
|
|
385
421
|
private readonly targetPrepareOperations = new Map<string, TargetPrepareOperation>();
|
|
386
422
|
private readonly pendingReplyRelays = new Map<string, PendingRelocationReplyRelay>();
|
|
387
423
|
private readonly pendingSessionRelocations = new Map<string, PendingSessionRelocation>();
|
|
@@ -432,6 +468,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
432
468
|
* checksum integrity failure — it must not be reported as DataLost(35).
|
|
433
469
|
*/
|
|
434
470
|
private disposed = false;
|
|
471
|
+
private readonly targetPreparationStop = new AbortController();
|
|
435
472
|
|
|
436
473
|
constructor(private readonly options: ZLinkHostRelocationOptions) {}
|
|
437
474
|
|
|
@@ -467,6 +504,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
467
504
|
else reject(failure.cause);
|
|
468
505
|
};
|
|
469
506
|
});
|
|
507
|
+
let acceptRelay!: () => void;
|
|
508
|
+
const relayReady = new Promise<void>((resolve) => {
|
|
509
|
+
acceptRelay = resolve;
|
|
510
|
+
});
|
|
470
511
|
this.sourceActorJoinProfiles.set(input.relocationId, {
|
|
471
512
|
actor: input.actor,
|
|
472
513
|
state: input.state,
|
|
@@ -481,43 +522,53 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
481
522
|
: RECREATE_RELOCATION_CONTENT_TYPE,
|
|
482
523
|
completionOperationId: input.completionOperationId,
|
|
483
524
|
canonicalRecovery: input.canonicalRecovery,
|
|
525
|
+
relayReady: acceptRelay,
|
|
484
526
|
ready,
|
|
485
527
|
resolveReady
|
|
486
528
|
});
|
|
487
529
|
const membershipEpoch =
|
|
488
530
|
input.state.spotMembershipEpoch > 0n ? input.state.spotMembershipEpoch : 1n;
|
|
489
531
|
const spotGeneration = input.target.targetSpotGeneration ?? target.lifecycleGeneration;
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
532
|
+
const cleanup = (async () => {
|
|
533
|
+
try {
|
|
534
|
+
await this.relocateStandaloneActor(
|
|
535
|
+
input.meshName,
|
|
536
|
+
input.state,
|
|
537
|
+
target,
|
|
538
|
+
target.applicationVersion,
|
|
539
|
+
input.signal,
|
|
540
|
+
{
|
|
541
|
+
...input.target,
|
|
542
|
+
spotKind:
|
|
543
|
+
input.target.spotKind === ZLinkSpotKind.Entry
|
|
544
|
+
? ZLinkSpotKind.Entry
|
|
545
|
+
: ZLinkSpotKind.User,
|
|
546
|
+
spotObjectGeneration: spotGeneration
|
|
547
|
+
},
|
|
548
|
+
input.relocationId,
|
|
549
|
+
true,
|
|
550
|
+
input.advertisedReceiveChunkLimitBytes,
|
|
551
|
+
input.deadlineAtMs
|
|
552
|
+
);
|
|
553
|
+
resolveReady();
|
|
554
|
+
} catch (error) {
|
|
555
|
+
if (error instanceof ServiceRelocationPostCommitError) {
|
|
556
|
+
resolveReady(error);
|
|
557
|
+
this.completeActorJoinSourceCleanup(input.state.actorId);
|
|
558
|
+
} else {
|
|
559
|
+
this.sourceActorJoinProfiles.delete(input.relocationId);
|
|
560
|
+
resolveReady();
|
|
561
|
+
throw error;
|
|
562
|
+
}
|
|
518
563
|
}
|
|
519
|
-
}
|
|
564
|
+
})();
|
|
565
|
+
const settledCleanup = Promise.allSettled([cleanup]);
|
|
566
|
+
await Promise.race([relayReady, cleanup]);
|
|
520
567
|
return {
|
|
568
|
+
sourceCleanup: async () => {
|
|
569
|
+
const [outcome] = await settledCleanup;
|
|
570
|
+
if (outcome.status === 'rejected') throw outcome.reason;
|
|
571
|
+
},
|
|
521
572
|
actorRef: {
|
|
522
573
|
actorId: sourceRef.actorId,
|
|
523
574
|
generation: sourceRef.generation,
|
|
@@ -550,8 +601,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
550
601
|
const hadSourceWindows = this.sourceCutoverWindows.size !== 0;
|
|
551
602
|
this.sourceCutoverWindows.clear();
|
|
552
603
|
if (hadSourceWindows) this.options.sourceQuiescenceChanged?.();
|
|
553
|
-
const stopped = new
|
|
554
|
-
stopped.name = 'AbortError';
|
|
604
|
+
const stopped = new ServiceRelocationRuntimeStoppedError();
|
|
555
605
|
this.targetPrepareOperations.clear();
|
|
556
606
|
for (const pending of this.pendingReplyRelays.values()) {
|
|
557
607
|
if (pending.timer !== undefined) clearTimeout(pending.timer);
|
|
@@ -566,16 +616,27 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
566
616
|
this.pendingSessionRelocations.clear();
|
|
567
617
|
this.terminalSessionRelocations.clear();
|
|
568
618
|
this.failedSessionRouteSubmits.clear();
|
|
569
|
-
this.
|
|
619
|
+
await this.drainActorJoinSources();
|
|
570
620
|
this.terminalActorJoinSourceLeaves.clear();
|
|
571
|
-
this.options.boundSessionRelocation?.clear?.();
|
|
572
|
-
this.relocationAuthorityKeys.clear();
|
|
621
|
+
await this.options.boundSessionRelocation?.clear?.();
|
|
573
622
|
if (errors.length === 1) throw errors[0];
|
|
574
623
|
if (errors.length > 1) throw new AggregateError(errors, 'Relocation runtime stop failed.');
|
|
575
624
|
}
|
|
576
625
|
|
|
577
626
|
/** Host shutdown waits for the existing attempt terminal before releasing its lease. */
|
|
578
627
|
async drainTargetAttempts(): Promise<void> {
|
|
628
|
+
this.targetPreparationStop.abort(
|
|
629
|
+
new ZLinkFrameworkException(
|
|
630
|
+
ZLinkFrameworkErrorKind.ShuttingDown,
|
|
631
|
+
'Relocation target is shutting down.'
|
|
632
|
+
)
|
|
633
|
+
);
|
|
634
|
+
for (const pending of this.targetAssemblies.values()) {
|
|
635
|
+
pending.assembly.fail('Relocation target is shutting down.');
|
|
636
|
+
}
|
|
637
|
+
await Promise.allSettled(
|
|
638
|
+
[...this.targetPrepareOperations.values()].map((value) => value.promise)
|
|
639
|
+
);
|
|
579
640
|
const results = await Promise.allSettled(
|
|
580
641
|
[...this.targetStages.entries()].map(([stagingId, stage]) => {
|
|
581
642
|
if (stage.fallback !== undefined) clearTimeout(stage.fallback);
|
|
@@ -603,27 +664,58 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
603
664
|
|
|
604
665
|
/** Spec 28 §4.4: the target owner alone orders shutdown seal and verified cutover. */
|
|
605
666
|
private targetStageSealed(stage: LocalStage): boolean {
|
|
606
|
-
return (
|
|
607
|
-
|
|
608
|
-
|
|
667
|
+
return !stage.cutoverReceived && this.targetAdmissionSealed();
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/** The Shutdown admission seal: closed once the target owner starts shutdown. */
|
|
671
|
+
private targetAdmissionSealed(): boolean {
|
|
672
|
+
return this.disposed || this.options.targetAdmissionSealed?.() === true;
|
|
609
673
|
}
|
|
610
674
|
|
|
611
675
|
completeActorJoinSourceCleanup(actorId: string): void {
|
|
612
676
|
for (const [relocationId, profile] of this.sourceActorJoinProfiles) {
|
|
613
677
|
if (profile.state.actorId !== actorId) continue;
|
|
614
|
-
this.
|
|
615
|
-
this.terminalActorJoinSourceLeaves.remember(relocationId, actorId);
|
|
616
|
-
void this.finalizeActorJoinSource(profile);
|
|
678
|
+
void this.retireActorJoinSource(relocationId, profile);
|
|
617
679
|
}
|
|
618
680
|
}
|
|
619
681
|
|
|
620
|
-
|
|
682
|
+
/** Drains the exact source scopes before their Spot lifecycle owners stop. */
|
|
683
|
+
async drainActorJoinSources(meshName?: string): Promise<void> {
|
|
684
|
+
await Promise.all(
|
|
685
|
+
[...this.sourceActorJoinProfiles].map(([relocationId, profile]) =>
|
|
686
|
+
meshName === undefined || profile.state.meshName === meshName
|
|
687
|
+
? this.retireActorJoinSource(relocationId, profile)
|
|
688
|
+
: undefined
|
|
689
|
+
)
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
private retireActorJoinSource(
|
|
694
|
+
relocationId: string,
|
|
695
|
+
profile: SourceActorJoinProfile
|
|
696
|
+
): Promise<void> {
|
|
697
|
+
return (profile.retirement ??= this.finalizeActorJoinSource(profile, relocationId).finally(
|
|
698
|
+
() => {
|
|
699
|
+
if (this.sourceActorJoinProfiles.get(relocationId) === profile) {
|
|
700
|
+
this.sourceActorJoinProfiles.delete(relocationId);
|
|
701
|
+
this.terminalActorJoinSourceLeaves.remember(relocationId, profile.state.actorId);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
));
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
private async finalizeActorJoinSource(
|
|
708
|
+
profile: SourceActorJoinProfile,
|
|
709
|
+
relocationId: string
|
|
710
|
+
): Promise<void> {
|
|
621
711
|
const failures: unknown[] = [];
|
|
622
712
|
try {
|
|
623
713
|
await profile.ready;
|
|
624
714
|
} catch (error) {
|
|
625
715
|
failures.push(error);
|
|
626
716
|
}
|
|
717
|
+
// A pre-commit failure removed this attempt and restored the source.
|
|
718
|
+
if (this.sourceActorJoinProfiles.get(relocationId) !== profile) return;
|
|
627
719
|
try {
|
|
628
720
|
await this.options.actorTransfer.completeRelocationSourceLeave(
|
|
629
721
|
profile.actor,
|
|
@@ -635,7 +727,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
635
727
|
try {
|
|
636
728
|
await this.options
|
|
637
729
|
.actorManager()
|
|
638
|
-
?.
|
|
730
|
+
?.completeCoreRelocationSource(profile.state.actorId, profile.state);
|
|
639
731
|
} catch (error) {
|
|
640
732
|
failures.push(error);
|
|
641
733
|
}
|
|
@@ -699,6 +791,11 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
699
791
|
id: `actor:${state.actorId}`,
|
|
700
792
|
relocate: async (relocationSignal) => {
|
|
701
793
|
await relocateShell(relocationSignal);
|
|
794
|
+
const spotAuthority = await requireAuthority(
|
|
795
|
+
this.requireLocationStore(),
|
|
796
|
+
encodeAuthorityKey('user_spot', String(activation.spotId)),
|
|
797
|
+
relocationSignal
|
|
798
|
+
);
|
|
702
799
|
await this.relocateStandaloneActor(
|
|
703
800
|
meshName,
|
|
704
801
|
state,
|
|
@@ -706,15 +803,14 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
706
803
|
targetApplicationVersion,
|
|
707
804
|
relocationSignal,
|
|
708
805
|
{
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
).objectGeneration
|
|
806
|
+
...committedSpotRoute(
|
|
807
|
+
meshName,
|
|
808
|
+
activation.spotId,
|
|
809
|
+
ZLinkSpotKind.User,
|
|
810
|
+
spotAuthority.allocation.descriptor.rid,
|
|
811
|
+
spotAuthority
|
|
812
|
+
),
|
|
813
|
+
spotObjectGeneration: spotAuthority.objectGeneration
|
|
718
814
|
}
|
|
719
815
|
);
|
|
720
816
|
}
|
|
@@ -822,10 +918,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
822
918
|
const payload = record.parts[0]!.data();
|
|
823
919
|
const command = serviceRelocationControlCommand(payload);
|
|
824
920
|
if (command === undefined) {
|
|
825
|
-
if (record.kind !== ReceiveKind.NodeSend) return false;
|
|
921
|
+
if (record.kind !== ReceiveKind.NodeSend || record.parts.length !== 1) return false;
|
|
826
922
|
const sourceLeave = decodeRemoteActorSourceLeaveTerminal(payload);
|
|
827
923
|
if (sourceLeave === undefined) return false;
|
|
828
|
-
if (record.parts.length !== 1) return false;
|
|
829
924
|
return this.acceptActorJoinSourceLeave(sourceLeave);
|
|
830
925
|
}
|
|
831
926
|
return this.handleControl(meshName, record, payload, command, signal);
|
|
@@ -862,18 +957,52 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
862
957
|
return true;
|
|
863
958
|
}
|
|
864
959
|
if (command === M6bServiceWireCommand.sessionRelocationSeal) {
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
960
|
+
let request: ServiceSessionRelocationSeal;
|
|
961
|
+
try {
|
|
962
|
+
request = decodeSessionRelocationSeal(payload);
|
|
963
|
+
} catch (error) {
|
|
964
|
+
const outcome = ZLinkMessageFlowOutcome.Error;
|
|
965
|
+
flowIfEnabled(this.options.messageFlow?.(), outcome)?.trace({
|
|
966
|
+
outcome,
|
|
967
|
+
surface: ZLinkDispatchErrorSurface.StreamSession,
|
|
968
|
+
messageKind: ZLinkDispatchMessageKind.Control,
|
|
969
|
+
packetName: 'sessionRelocationSeal',
|
|
970
|
+
sourceRid: record.sourceNodeRid ?? undefined,
|
|
971
|
+
errorReason: 'invalid_frame',
|
|
972
|
+
errorAction: ZLinkDispatchErrorAction.Drop,
|
|
973
|
+
...dispatchErrorDetails(error)
|
|
974
|
+
});
|
|
975
|
+
throw error;
|
|
976
|
+
}
|
|
977
|
+
let response: ServiceSessionRelocationSealed | undefined;
|
|
978
|
+
try {
|
|
979
|
+
response = await this.handleSessionRelocationSeal(
|
|
980
|
+
meshName,
|
|
981
|
+
request,
|
|
982
|
+
record.sourceNodeRid,
|
|
983
|
+
signal
|
|
984
|
+
);
|
|
985
|
+
} catch (error) {
|
|
986
|
+
traceSessionBindingControlDrop(
|
|
987
|
+
this.options.messageFlow?.(),
|
|
988
|
+
record.sourceNodeRid ?? '',
|
|
989
|
+
request.actor.actor.actorId,
|
|
990
|
+
'sessionRelocationSeal',
|
|
991
|
+
error instanceof ZLinkFrameworkException &&
|
|
992
|
+
error.kind === ZLinkFrameworkErrorKind.Unavailable
|
|
993
|
+
? 'location_unavailable'
|
|
994
|
+
: 'stale_target',
|
|
995
|
+
error
|
|
996
|
+
);
|
|
997
|
+
throw error;
|
|
998
|
+
}
|
|
999
|
+
if (response !== undefined) {
|
|
1000
|
+
await this.sendSessionRelocationResponse(
|
|
1001
|
+
meshName,
|
|
1002
|
+
record.sourceNodeRid,
|
|
1003
|
+
encodeSessionRelocationSealed(response)
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
877
1006
|
return true;
|
|
878
1007
|
}
|
|
879
1008
|
if (command === M6bServiceWireCommand.sessionRelocationRoute) {
|
|
@@ -918,7 +1047,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
918
1047
|
this.targetAssemblies.get(operationKey)?.fingerprint ??
|
|
919
1048
|
this.targetStages.get(operationKey)?.offer.prepareFingerprint ??
|
|
920
1049
|
this.terminalTargets.get(operationKey);
|
|
921
|
-
if (request.sourceNodeRid
|
|
1050
|
+
if (!routingIdsEqual(request.sourceNodeRid, record.sourceNodeRid)) return true;
|
|
922
1051
|
if (acceptedFingerprint !== undefined && acceptedFingerprint !== fingerprint) {
|
|
923
1052
|
if (!samePrepareIdentity(acceptedFingerprint, request)) return true;
|
|
924
1053
|
this.replyPrepare(
|
|
@@ -948,7 +1077,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
948
1077
|
// finish the restore and the Ready reply asynchronously.
|
|
949
1078
|
let promise: Promise<ServiceMaintenanceRelocationReady>;
|
|
950
1079
|
try {
|
|
951
|
-
this.registerTargetAssembly(operationKey, request,
|
|
1080
|
+
this.registerTargetAssembly(operationKey, request, record.sourceNodeRid);
|
|
952
1081
|
promise = this.handlePrepareControl(
|
|
953
1082
|
meshName,
|
|
954
1083
|
operationKey,
|
|
@@ -1083,7 +1212,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1083
1212
|
if (
|
|
1084
1213
|
pending === undefined ||
|
|
1085
1214
|
sourceNodeRid === null ||
|
|
1086
|
-
|
|
1215
|
+
!routingIdsEqual(sourceNodeRid, pending.authenticatedSourceNodeRid) ||
|
|
1087
1216
|
request.senderRole !== 'source' ||
|
|
1088
1217
|
!sameCoordinator(request.coordinator, pending.prepare.coordinator) ||
|
|
1089
1218
|
!sameRelocationObject(request.object, pending.prepare.object)
|
|
@@ -1162,7 +1291,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1162
1291
|
// to itself; RouteMesh has no self connection (NotConnected), so the
|
|
1163
1292
|
// control dispatches through the same inbound handler locally.
|
|
1164
1293
|
const localRid = this.requireMeshNode(meshName).status().routingId;
|
|
1165
|
-
if (
|
|
1294
|
+
if (routingIdsEqual(localRid, targetNodeRid)) {
|
|
1166
1295
|
await this.handleSessionRelocationRoute(meshName, request, localRid, signal);
|
|
1167
1296
|
return;
|
|
1168
1297
|
}
|
|
@@ -1191,7 +1320,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1191
1320
|
const key = sessionRelocationPendingKey(request);
|
|
1192
1321
|
const bytes = encodeSessionRelocationSeal(request);
|
|
1193
1322
|
const requestFingerprint = bytes.toString('base64');
|
|
1194
|
-
const expectedTarget =
|
|
1323
|
+
const expectedTarget = encodeRoutingIdStorageHex(targetNodeRid);
|
|
1195
1324
|
const terminal = this.terminalSessionRelocations.get(key);
|
|
1196
1325
|
if (terminal !== undefined) {
|
|
1197
1326
|
if (
|
|
@@ -1205,6 +1334,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1205
1334
|
);
|
|
1206
1335
|
}
|
|
1207
1336
|
this.terminalSessionRelocations.touch(key);
|
|
1337
|
+
return Promise.resolve(terminal.response);
|
|
1208
1338
|
}
|
|
1209
1339
|
const existing = this.pendingSessionRelocations.get(key);
|
|
1210
1340
|
if (existing !== undefined) {
|
|
@@ -1268,7 +1398,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1268
1398
|
// connection (NotConnected); dispatch through the inbound handler
|
|
1269
1399
|
// locally instead of the wire.
|
|
1270
1400
|
const localRid = this.requireMeshNode(meshName).status().routingId;
|
|
1271
|
-
if (
|
|
1401
|
+
if (encodeRoutingIdStorageHex(localRid) === expectedTarget) {
|
|
1272
1402
|
try {
|
|
1273
1403
|
const response = await this.handleSessionRelocationSeal(
|
|
1274
1404
|
meshName,
|
|
@@ -1276,7 +1406,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1276
1406
|
localRid,
|
|
1277
1407
|
signal
|
|
1278
1408
|
);
|
|
1279
|
-
pending.resolve(response);
|
|
1409
|
+
if (response !== undefined) pending.resolve(response);
|
|
1280
1410
|
} catch (error) {
|
|
1281
1411
|
pending.reject(error);
|
|
1282
1412
|
}
|
|
@@ -1327,9 +1457,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1327
1457
|
finish(() => {
|
|
1328
1458
|
this.terminalSessionRelocations.remember(key, {
|
|
1329
1459
|
targetNodeRid: expectedTarget,
|
|
1330
|
-
request,
|
|
1331
1460
|
requestFingerprint,
|
|
1332
|
-
|
|
1461
|
+
response
|
|
1333
1462
|
});
|
|
1334
1463
|
resolvePromise(response);
|
|
1335
1464
|
}),
|
|
@@ -1375,19 +1504,14 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1375
1504
|
if (pending === undefined) {
|
|
1376
1505
|
const terminal = this.terminalSessionRelocations.get(key);
|
|
1377
1506
|
if (terminal === undefined) return;
|
|
1378
|
-
|
|
1379
|
-
throw new ServiceWireProtocolError(`Session relocation ACK '${key}' source node changed.`);
|
|
1380
|
-
}
|
|
1381
|
-
validateSessionRelocationResponse(terminal.request, response);
|
|
1382
|
-
if (sessionRelocationResponseFingerprint(response) !== terminal.responseFingerprint) {
|
|
1383
|
-
throw new ServiceWireProtocolError(
|
|
1384
|
-
`Session relocation ACK '${key}' repeated with different bytes.`
|
|
1385
|
-
);
|
|
1386
|
-
}
|
|
1507
|
+
// Session–Actor binding §8.3: the first command 43 is final for this participant.
|
|
1387
1508
|
this.terminalSessionRelocations.touch(key);
|
|
1388
1509
|
return;
|
|
1389
1510
|
}
|
|
1390
|
-
if (
|
|
1511
|
+
if (
|
|
1512
|
+
sourceNodeRid === null ||
|
|
1513
|
+
encodeRoutingIdStorageHex(sourceNodeRid) !== pending.targetNodeRid
|
|
1514
|
+
) {
|
|
1391
1515
|
pending.reject(
|
|
1392
1516
|
new ServiceWireProtocolError(`Session relocation ACK '${key}' source node changed.`)
|
|
1393
1517
|
);
|
|
@@ -1411,12 +1535,12 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1411
1535
|
if (sourceNodeRid === null) return undefined;
|
|
1412
1536
|
const node = this.requireMeshNode(meshName);
|
|
1413
1537
|
const status = node.status();
|
|
1414
|
-
if (
|
|
1538
|
+
if (routingIdsEqual(status.routingId, sourceNodeRid)) {
|
|
1415
1539
|
return status.lifecycleGeneration;
|
|
1416
1540
|
}
|
|
1417
1541
|
return node
|
|
1418
1542
|
.peers()
|
|
1419
|
-
.find((peer) => peer.routingId !== null &&
|
|
1543
|
+
.find((peer) => peer.routingId !== null && routingIdsEqual(peer.routingId, sourceNodeRid))
|
|
1420
1544
|
?.lifecycleGeneration;
|
|
1421
1545
|
}
|
|
1422
1546
|
|
|
@@ -1425,11 +1549,11 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1425
1549
|
request: ServiceSessionRelocationSeal,
|
|
1426
1550
|
sourceNodeRid: RoutingId | null,
|
|
1427
1551
|
signal?: AbortSignal
|
|
1428
|
-
): Promise<ServiceSessionRelocationSealed> {
|
|
1552
|
+
): Promise<ServiceSessionRelocationSealed | undefined> {
|
|
1429
1553
|
if (
|
|
1430
1554
|
sourceNodeRid === null ||
|
|
1431
|
-
|
|
1432
|
-
|
|
1555
|
+
!routingIdsEqual(sourceNodeRid, request.coordinator.nodeRid) ||
|
|
1556
|
+
!routingIdsEqual(sourceNodeRid, request.actor.actor.nodeRid)
|
|
1433
1557
|
) {
|
|
1434
1558
|
throw new ServiceWireProtocolError(
|
|
1435
1559
|
'Session relocation seal source does not match its coordinator and Actor fence.'
|
|
@@ -1449,7 +1573,19 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1449
1573
|
if (handler === undefined) {
|
|
1450
1574
|
throw new Error('Session relocation seal ingress is not configured.');
|
|
1451
1575
|
}
|
|
1452
|
-
return await handler(request, signal)
|
|
1576
|
+
return await handler(request, signal, () => {
|
|
1577
|
+
const node = this.requireMeshNode(meshName);
|
|
1578
|
+
if (
|
|
1579
|
+
routingIdsEqual(node.status().routingId, request.targetNodeRid) ||
|
|
1580
|
+
node.isPeerRouteReady?.(request.targetNodeRid, request.targetNodeGeneration) === true
|
|
1581
|
+
)
|
|
1582
|
+
return;
|
|
1583
|
+
void this.options
|
|
1584
|
+
.requestPeerReconcile?.(meshName, request.targetNodeRid)
|
|
1585
|
+
.catch((error) =>
|
|
1586
|
+
console.error('[zlink.runtime.relocation.session_route_reconcile_failed]', error)
|
|
1587
|
+
);
|
|
1588
|
+
});
|
|
1453
1589
|
}
|
|
1454
1590
|
|
|
1455
1591
|
private async handleSessionRelocationRoute(
|
|
@@ -1461,7 +1597,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1461
1597
|
this.validateSessionOwnerFence(meshName, request.session);
|
|
1462
1598
|
const expectedSource =
|
|
1463
1599
|
request.route.action === 'commit' ? request.route.targetNodeRid : request.coordinator.nodeRid;
|
|
1464
|
-
if (sourceNodeRid === null ||
|
|
1600
|
+
if (sourceNodeRid === null || !routingIdsEqual(sourceNodeRid, expectedSource)) {
|
|
1465
1601
|
throw new ServiceWireProtocolError(
|
|
1466
1602
|
'Session relocation route source does not match its action fence.'
|
|
1467
1603
|
);
|
|
@@ -1489,10 +1625,17 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1489
1625
|
): void {
|
|
1490
1626
|
const status = this.requireMeshNode(meshName).status();
|
|
1491
1627
|
const owner = this.options.currentOwner();
|
|
1628
|
+
// Session-Actor binding §4: without this host's own descriptor the command is Unavailable.
|
|
1629
|
+
if (owner === undefined) {
|
|
1630
|
+
throw createInternalFrameworkException(
|
|
1631
|
+
ZLinkFrameworkInternalErrorKind.RouteNotConnected,
|
|
1632
|
+
'Session owner has no live MeshNode descriptor.'
|
|
1633
|
+
);
|
|
1634
|
+
}
|
|
1492
1635
|
if (
|
|
1493
1636
|
!routingIdsEqual(status.routingId, session.sessionOwnerNodeRid) ||
|
|
1494
1637
|
status.lifecycleGeneration !== session.sessionOwnerNodeGeneration ||
|
|
1495
|
-
owner
|
|
1638
|
+
owner.ownerId !== session.sessionOwnerId ||
|
|
1496
1639
|
owner.leaseGeneration !== session.sessionOwnerLeaseGeneration
|
|
1497
1640
|
) {
|
|
1498
1641
|
throw new ServiceWireProtocolError(
|
|
@@ -1569,7 +1712,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1569
1712
|
);
|
|
1570
1713
|
target ??= await this.selectTarget(
|
|
1571
1714
|
meshName,
|
|
1572
|
-
|
|
1715
|
+
this.requireMeshNode(meshName).status().routingId,
|
|
1573
1716
|
targetApplicationVersion,
|
|
1574
1717
|
relocationTargetRequirements([
|
|
1575
1718
|
{
|
|
@@ -1601,17 +1744,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1601
1744
|
objectGeneration: spotAuthority.objectGeneration,
|
|
1602
1745
|
authorityOwnerGeneration: spotAuthority.authorityOwnerGeneration,
|
|
1603
1746
|
seal: async (captureSignal) => {
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
() => {
|
|
1608
|
-
interruptionStartedAt = performance.now();
|
|
1609
|
-
spotMessageFollowSeal = this.sealSpotMessageFollow(meshName, spotAuthority, activation);
|
|
1610
|
-
// Both seals run in the same event-loop turn after the Close decision.
|
|
1611
|
-
return { spotCaptureOperation: activation.captureRelocation(captureSignal) };
|
|
1612
|
-
}
|
|
1613
|
-
);
|
|
1614
|
-
const preparedSessions = Promise.all(
|
|
1747
|
+
// Relocation 04 §4.1: every bound member's Session is sealed before
|
|
1748
|
+
// the Spot capture stops member dispatch.
|
|
1749
|
+
const preparations = await Promise.allSettled(
|
|
1615
1750
|
actorStates.map(async (state) => ({
|
|
1616
1751
|
state,
|
|
1617
1752
|
actor: state.actor!,
|
|
@@ -1620,27 +1755,40 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1620
1755
|
state,
|
|
1621
1756
|
captureSignal,
|
|
1622
1757
|
false,
|
|
1623
|
-
relocationWireId(aggregateId)
|
|
1758
|
+
relocationWireId(aggregateId),
|
|
1759
|
+
target
|
|
1624
1760
|
)
|
|
1625
1761
|
}))
|
|
1626
1762
|
);
|
|
1627
|
-
const
|
|
1628
|
-
|
|
1629
|
-
|
|
1763
|
+
const failures: unknown[] = [];
|
|
1764
|
+
for (const preparation of preparations) {
|
|
1765
|
+
if (preparation.status === 'fulfilled') sessions.push(preparation.value);
|
|
1766
|
+
else failures.push(preparation.reason);
|
|
1767
|
+
}
|
|
1768
|
+
if (failures.length === 1) throw failures[0];
|
|
1769
|
+
if (failures.length > 1) {
|
|
1770
|
+
throw new AggregateError(failures, 'Spot relocation source preparation failed.');
|
|
1771
|
+
}
|
|
1772
|
+
const { spotCaptureOperation } = await this.afterSpotCloseDecision(
|
|
1773
|
+
meshName,
|
|
1774
|
+
activation.spotId,
|
|
1775
|
+
() => {
|
|
1776
|
+
interruptionStartedAt = performance.now();
|
|
1777
|
+
spotMessageFollowSeal = this.sealSpotMessageFollow(meshName, spotAuthority, activation);
|
|
1778
|
+
// Both Spot seals run in the same event-loop turn after the Close decision.
|
|
1779
|
+
return { spotCaptureOperation: activation.captureRelocation(captureSignal) };
|
|
1780
|
+
}
|
|
1630
1781
|
);
|
|
1631
1782
|
spotCapture = await spotCaptureOperation;
|
|
1632
|
-
const preparation = await preparedSessionsOutcome;
|
|
1633
|
-
if ('error' in preparation) throw preparation.error;
|
|
1634
|
-
sessions.push(...preparation.value);
|
|
1635
1783
|
return {
|
|
1636
1784
|
boundSessionState: Buffer.alloc(0),
|
|
1637
1785
|
queuedMessages: [],
|
|
1638
|
-
timers: spotCapture
|
|
1786
|
+
timers: spotCapture!.timers.map(toServiceTimer)
|
|
1639
1787
|
};
|
|
1640
1788
|
},
|
|
1641
1789
|
captureApplicationState: (captureSignal) =>
|
|
1642
1790
|
this.captureApplication(spotRegistration.relocation, activation.spot, captureSignal),
|
|
1643
|
-
commitSeal: async () => {
|
|
1791
|
+
commitSeal: async (followAuthorities) => {
|
|
1644
1792
|
const failures: unknown[] = [];
|
|
1645
1793
|
try {
|
|
1646
1794
|
if (!spotSealCommitted) {
|
|
@@ -1653,9 +1801,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1653
1801
|
await this.commitSpotMessageFollow(
|
|
1654
1802
|
meshName,
|
|
1655
1803
|
spotMessageFollowSeal,
|
|
1656
|
-
spotKey,
|
|
1657
|
-
activation
|
|
1658
|
-
target
|
|
1804
|
+
followAuthorities.get(spotKey.value),
|
|
1805
|
+
activation
|
|
1659
1806
|
);
|
|
1660
1807
|
}
|
|
1661
1808
|
} catch (error) {
|
|
@@ -1663,10 +1810,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1663
1810
|
}
|
|
1664
1811
|
for (const session of sessions) {
|
|
1665
1812
|
try {
|
|
1666
|
-
const committedAuthority =
|
|
1667
|
-
|
|
1668
|
-
encodeAuthorityKey('actor', session.state.actorId)
|
|
1813
|
+
const committedAuthority = followAuthorities.get(
|
|
1814
|
+
encodeAuthorityKey('actor', session.state.actorId).value
|
|
1669
1815
|
);
|
|
1816
|
+
const committedSpot = followAuthorities.get(spotKey.value);
|
|
1670
1817
|
const targetActorRef = {
|
|
1671
1818
|
actorId: session.state.actorId,
|
|
1672
1819
|
objectGeneration: actorAuthorities.get(session.state.actorId)!.objectGeneration,
|
|
@@ -1674,15 +1821,21 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1674
1821
|
nodeRid: target.rid
|
|
1675
1822
|
};
|
|
1676
1823
|
await session.prepared.commit(
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1824
|
+
committedSpotRoute(
|
|
1825
|
+
meshName,
|
|
1826
|
+
activation.spotId,
|
|
1827
|
+
kind === 'user_spot' ? ZLinkSpotKind.User : ZLinkSpotKind.Instance,
|
|
1828
|
+
target.rid,
|
|
1829
|
+
committedSpot
|
|
1830
|
+
),
|
|
1684
1831
|
targetActorRef,
|
|
1685
|
-
|
|
1832
|
+
committedAuthority === undefined || committedSpot === undefined
|
|
1833
|
+
? null
|
|
1834
|
+
: committedActorOwnerFence(
|
|
1835
|
+
session.state.actorId,
|
|
1836
|
+
targetActorRef,
|
|
1837
|
+
committedAuthority
|
|
1838
|
+
)
|
|
1686
1839
|
);
|
|
1687
1840
|
} catch (error) {
|
|
1688
1841
|
failures.push(error);
|
|
@@ -1711,6 +1864,16 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1711
1864
|
abortSeal: async () => {
|
|
1712
1865
|
const failures: unknown[] = [];
|
|
1713
1866
|
if (spotCapture !== undefined) {
|
|
1867
|
+
// Spot model §5.1: Continued completes while the source seal still holds jobs.
|
|
1868
|
+
if (readinessBoundaryConsumed) {
|
|
1869
|
+
try {
|
|
1870
|
+
await activation.completeConsumedRelocationBoundary(
|
|
1871
|
+
ZLinkSpotRelocationReadyOutcome.Continued
|
|
1872
|
+
);
|
|
1873
|
+
} catch (error) {
|
|
1874
|
+
failures.push(error);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1714
1877
|
try {
|
|
1715
1878
|
activation.abortRelocation(spotCapture);
|
|
1716
1879
|
} catch (error) {
|
|
@@ -1759,7 +1922,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1759
1922
|
spotObjectGeneration: spotAuthority.objectGeneration,
|
|
1760
1923
|
membershipEpoch: state.spotMembershipEpoch > 0n ? state.spotMembershipEpoch : 1n
|
|
1761
1924
|
}));
|
|
1762
|
-
|
|
1925
|
+
const relocationMetric =
|
|
1926
|
+
spotRegistration.relocation.kind === 'disabled'
|
|
1927
|
+
? undefined
|
|
1928
|
+
: this.options.metrics?.startRelocation(meshName, kind, spotRegistration.relocation.kind);
|
|
1763
1929
|
try {
|
|
1764
1930
|
const waitForRelocationBoundary = (
|
|
1765
1931
|
activation as unknown as {
|
|
@@ -1808,10 +1974,12 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1808
1974
|
)
|
|
1809
1975
|
]),
|
|
1810
1976
|
signal,
|
|
1811
|
-
sessions
|
|
1977
|
+
sessions,
|
|
1978
|
+
undefined,
|
|
1979
|
+
undefined,
|
|
1980
|
+
relocationMetric
|
|
1812
1981
|
);
|
|
1813
1982
|
} catch (error) {
|
|
1814
|
-
outcome = 'failed';
|
|
1815
1983
|
if (
|
|
1816
1984
|
readinessBoundaryConsumed &&
|
|
1817
1985
|
!(error instanceof ServiceRelocationPostCommitError) &&
|
|
@@ -1823,14 +1991,14 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1823
1991
|
}
|
|
1824
1992
|
throw error;
|
|
1825
1993
|
} finally {
|
|
1994
|
+
this.completeSourceRelocationMetric(relocationMetric);
|
|
1826
1995
|
this.sourceRelocationIds.delete(aggregateId);
|
|
1827
1996
|
if (interruptionStartedAt !== undefined) {
|
|
1828
1997
|
this.recordRelocationInterruption(
|
|
1829
1998
|
meshName,
|
|
1830
1999
|
kind,
|
|
1831
2000
|
kind === 'user_spot' ? 'spot_wide' : undefined,
|
|
1832
|
-
interruptionStartedAt
|
|
1833
|
-
outcome
|
|
2001
|
+
interruptionStartedAt
|
|
1834
2002
|
);
|
|
1835
2003
|
}
|
|
1836
2004
|
}
|
|
@@ -1842,8 +2010,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1842
2010
|
target: ZLinkMeshNodeDescriptor | undefined,
|
|
1843
2011
|
targetApplicationVersion: bigint | undefined,
|
|
1844
2012
|
signal?: AbortSignal,
|
|
1845
|
-
targetMembership?: {
|
|
1846
|
-
readonly spotId: RoutingId;
|
|
2013
|
+
targetMembership?: ZLinkSpotRouteTarget & {
|
|
1847
2014
|
readonly spotKind: ZLinkSpotKind.Entry | ZLinkSpotKind.User;
|
|
1848
2015
|
readonly spotObjectGeneration: bigint;
|
|
1849
2016
|
},
|
|
@@ -1861,18 +2028,19 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1861
2028
|
encodeAuthorityKey('actor', state.actorId),
|
|
1862
2029
|
signal
|
|
1863
2030
|
);
|
|
2031
|
+
const actorPolicy = this.actorRegistration(
|
|
2032
|
+
state.meshName ?? meshName,
|
|
2033
|
+
authority.allocation.stableType
|
|
2034
|
+
).relocation.kind;
|
|
1864
2035
|
target ??= await this.selectTarget(
|
|
1865
2036
|
meshName,
|
|
1866
|
-
|
|
2037
|
+
this.requireMeshNode(meshName).status().routingId,
|
|
1867
2038
|
targetApplicationVersion,
|
|
1868
2039
|
[
|
|
1869
2040
|
{
|
|
1870
2041
|
objectKind: 'actor',
|
|
1871
2042
|
stableType: authority.allocation.stableType,
|
|
1872
|
-
policy:
|
|
1873
|
-
state.meshName ?? meshName,
|
|
1874
|
-
authority.allocation.stableType
|
|
1875
|
-
).relocation.kind,
|
|
2043
|
+
policy: actorPolicy,
|
|
1876
2044
|
count: 1
|
|
1877
2045
|
}
|
|
1878
2046
|
],
|
|
@@ -1889,10 +2057,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1889
2057
|
true,
|
|
1890
2058
|
target,
|
|
1891
2059
|
meshName,
|
|
1892
|
-
|
|
1893
|
-
spotId: membershipSpotId,
|
|
1894
|
-
spotKind: targetMembership?.spotKind ?? ZLinkSpotKind.Entry
|
|
1895
|
-
},
|
|
2060
|
+
targetMembership,
|
|
1896
2061
|
relocationWireId(aggregateId),
|
|
1897
2062
|
retainSourceForActorJoin
|
|
1898
2063
|
);
|
|
@@ -1910,7 +2075,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1910
2075
|
return unit.seal(captureSignal);
|
|
1911
2076
|
}
|
|
1912
2077
|
};
|
|
1913
|
-
|
|
2078
|
+
const relocationMetric =
|
|
2079
|
+
actorPolicy === 'disabled'
|
|
2080
|
+
? undefined
|
|
2081
|
+
: this.options.metrics?.startRelocation(meshName, 'actor', actorPolicy);
|
|
1914
2082
|
try {
|
|
1915
2083
|
if (isRelocationDebugEnabled()) {
|
|
1916
2084
|
relocationDebug('standalone_actor.wait_budget', {
|
|
@@ -1941,21 +2109,14 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1941
2109
|
signal,
|
|
1942
2110
|
sessions,
|
|
1943
2111
|
advertisedReceiveChunkLimitBytes,
|
|
1944
|
-
deadlineAtMs
|
|
2112
|
+
deadlineAtMs,
|
|
2113
|
+
relocationMetric
|
|
1945
2114
|
);
|
|
1946
|
-
} catch (error) {
|
|
1947
|
-
outcome = 'failed';
|
|
1948
|
-
throw error;
|
|
1949
2115
|
} finally {
|
|
2116
|
+
this.completeSourceRelocationMetric(relocationMetric);
|
|
1950
2117
|
this.sourceRelocationIds.delete(aggregateId);
|
|
1951
2118
|
if (interruptionStartedAt !== undefined) {
|
|
1952
|
-
this.recordRelocationInterruption(
|
|
1953
|
-
meshName,
|
|
1954
|
-
'actor',
|
|
1955
|
-
undefined,
|
|
1956
|
-
interruptionStartedAt,
|
|
1957
|
-
outcome
|
|
1958
|
-
);
|
|
2119
|
+
this.recordRelocationInterruption(meshName, 'actor', undefined, interruptionStartedAt);
|
|
1959
2120
|
}
|
|
1960
2121
|
}
|
|
1961
2122
|
}
|
|
@@ -1994,7 +2155,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1994
2155
|
);
|
|
1995
2156
|
target ??= await this.selectTarget(
|
|
1996
2157
|
meshName,
|
|
1997
|
-
|
|
2158
|
+
this.requireMeshNode(meshName).status().routingId,
|
|
1998
2159
|
targetApplicationVersion,
|
|
1999
2160
|
[
|
|
2000
2161
|
{
|
|
@@ -2036,7 +2197,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2036
2197
|
// PerActor User Spots are stateless shells. Application state and the
|
|
2037
2198
|
// Spot relocation adapter are intentionally not part of this unit.
|
|
2038
2199
|
captureApplicationState: async () => Buffer.alloc(0),
|
|
2039
|
-
commitSeal: async () => {
|
|
2200
|
+
commitSeal: async (followAuthorities) => {
|
|
2040
2201
|
if (!sealCommitted) {
|
|
2041
2202
|
if (spotCapture === undefined || !(await activation.commitRelocation(spotCapture))) {
|
|
2042
2203
|
throw new Error(
|
|
@@ -2049,9 +2210,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2049
2210
|
await this.commitSpotMessageFollow(
|
|
2050
2211
|
meshName,
|
|
2051
2212
|
spotMessageFollowSeal,
|
|
2052
|
-
spotKey,
|
|
2053
|
-
activation
|
|
2054
|
-
target
|
|
2213
|
+
followAuthorities.get(spotKey.value),
|
|
2214
|
+
activation
|
|
2055
2215
|
);
|
|
2056
2216
|
}
|
|
2057
2217
|
await this.requireSpotManager().completeRelocationSource(activation);
|
|
@@ -2068,7 +2228,14 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2068
2228
|
}
|
|
2069
2229
|
};
|
|
2070
2230
|
const aggregateId = this.reserveRelocationId();
|
|
2071
|
-
|
|
2231
|
+
const relocationMetric =
|
|
2232
|
+
spotRegistration.relocation.kind === 'disabled'
|
|
2233
|
+
? undefined
|
|
2234
|
+
: this.options.metrics?.startRelocation(
|
|
2235
|
+
meshName,
|
|
2236
|
+
'user_spot',
|
|
2237
|
+
spotRegistration.relocation.kind
|
|
2238
|
+
);
|
|
2072
2239
|
try {
|
|
2073
2240
|
await this.waitForRelocationBudgetHeadroom(String(target.rid), signal);
|
|
2074
2241
|
const captured = await new ServiceRelocationObjectCaptureOwner().captureUserSpotAggregate(
|
|
@@ -2086,20 +2253,20 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2086
2253
|
captured,
|
|
2087
2254
|
new Map([[spotKey.value, spotAuthority]]),
|
|
2088
2255
|
signal,
|
|
2089
|
-
[]
|
|
2256
|
+
[],
|
|
2257
|
+
undefined,
|
|
2258
|
+
undefined,
|
|
2259
|
+
relocationMetric
|
|
2090
2260
|
);
|
|
2091
|
-
} catch (error) {
|
|
2092
|
-
outcome = 'failed';
|
|
2093
|
-
throw error;
|
|
2094
2261
|
} finally {
|
|
2262
|
+
this.completeSourceRelocationMetric(relocationMetric);
|
|
2095
2263
|
this.sourceRelocationIds.delete(aggregateId);
|
|
2096
2264
|
if (interruptionStartedAt !== undefined) {
|
|
2097
2265
|
this.recordRelocationInterruption(
|
|
2098
2266
|
meshName,
|
|
2099
2267
|
'user_spot',
|
|
2100
2268
|
'per_actor',
|
|
2101
|
-
interruptionStartedAt
|
|
2102
|
-
outcome
|
|
2269
|
+
interruptionStartedAt
|
|
2103
2270
|
);
|
|
2104
2271
|
}
|
|
2105
2272
|
}
|
|
@@ -2112,10 +2279,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2112
2279
|
standalone = false,
|
|
2113
2280
|
target?: ZLinkMeshNodeDescriptor,
|
|
2114
2281
|
meshName?: string,
|
|
2115
|
-
targetMembership?:
|
|
2116
|
-
readonly spotId: RoutingId;
|
|
2117
|
-
readonly spotKind: ZLinkSpotKind.Entry | ZLinkSpotKind.User;
|
|
2118
|
-
},
|
|
2282
|
+
targetMembership?: ZLinkSpotRouteTarget,
|
|
2119
2283
|
sessionRelocation?: ServiceWireOperationId,
|
|
2120
2284
|
retainSourceForActorJoin = false
|
|
2121
2285
|
): ServiceRelocationCaptureUnit {
|
|
@@ -2140,7 +2304,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2140
2304
|
state,
|
|
2141
2305
|
signal,
|
|
2142
2306
|
true,
|
|
2143
|
-
sessionRelocation
|
|
2307
|
+
sessionRelocation,
|
|
2308
|
+
target
|
|
2144
2309
|
)
|
|
2145
2310
|
};
|
|
2146
2311
|
sessions.push(ownSession);
|
|
@@ -2180,7 +2345,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2180
2345
|
// The sealed Session identity is part of the canonical participant vector.
|
|
2181
2346
|
captureApplicationState: (signal) =>
|
|
2182
2347
|
this.captureApplication(registration.relocation, state.actor!, signal),
|
|
2183
|
-
commitSeal: async () => {
|
|
2348
|
+
commitSeal: async (followAuthorities) => {
|
|
2184
2349
|
if (
|
|
2185
2350
|
!standalone ||
|
|
2186
2351
|
ownSession === undefined ||
|
|
@@ -2190,11 +2355,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2190
2355
|
return;
|
|
2191
2356
|
const failures: unknown[] = [];
|
|
2192
2357
|
try {
|
|
2193
|
-
const
|
|
2194
|
-
|
|
2195
|
-
const committedAuthority = await requireAuthority(
|
|
2196
|
-
this.requireLocationStore(),
|
|
2197
|
-
encodeAuthorityKey('actor', state.actorId)
|
|
2358
|
+
const committedAuthority = followAuthorities.get(
|
|
2359
|
+
encodeAuthorityKey('actor', state.actorId).value
|
|
2198
2360
|
);
|
|
2199
2361
|
const targetActorRef = {
|
|
2200
2362
|
actorId: state.actorId,
|
|
@@ -2203,25 +2365,28 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2203
2365
|
nodeRid: target.rid
|
|
2204
2366
|
};
|
|
2205
2367
|
await ownSession.prepared.commit(
|
|
2206
|
-
{
|
|
2368
|
+
targetMembership ?? {
|
|
2207
2369
|
routerChannelId: meshName,
|
|
2208
2370
|
targetNodeRid: target.rid,
|
|
2209
|
-
spotId:
|
|
2210
|
-
spotKind:
|
|
2211
|
-
|
|
2212
|
-
} as never,
|
|
2371
|
+
spotId: (target.entrySpotId ?? String(target.rid)) as RoutingId,
|
|
2372
|
+
spotKind: ZLinkSpotKind.Entry
|
|
2373
|
+
},
|
|
2213
2374
|
targetActorRef,
|
|
2214
|
-
|
|
2375
|
+
committedAuthority === undefined
|
|
2376
|
+
? null
|
|
2377
|
+
: committedActorOwnerFence(state.actorId, targetActorRef, committedAuthority)
|
|
2215
2378
|
);
|
|
2216
2379
|
} catch (error) {
|
|
2217
2380
|
failures.push(error);
|
|
2218
2381
|
}
|
|
2219
|
-
|
|
2220
|
-
|
|
2382
|
+
try {
|
|
2383
|
+
if (retainSourceForActorJoin) {
|
|
2384
|
+
this.requireActorManager().detachRelocationSource(state);
|
|
2385
|
+
} else {
|
|
2221
2386
|
await this.requireActorManager().completeRelocationSource(state.actorId);
|
|
2222
|
-
} catch (error) {
|
|
2223
|
-
failures.push(error);
|
|
2224
2387
|
}
|
|
2388
|
+
} catch (error) {
|
|
2389
|
+
failures.push(error);
|
|
2225
2390
|
}
|
|
2226
2391
|
if (failures.length > 0)
|
|
2227
2392
|
throw failures.length === 1
|
|
@@ -2272,7 +2437,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2272
2437
|
sessions: readonly SourceActorSession[] = [],
|
|
2273
2438
|
/** Target's advertised relocation state chunk cap (Actor Join only; spec 15 §4.2). */
|
|
2274
2439
|
advertisedReceiveChunkLimitBytes?: number,
|
|
2275
|
-
controlDeadlineAtMs = performance.now() + RELOCATION_OPERATION_TIMEOUT_MS
|
|
2440
|
+
controlDeadlineAtMs = performance.now() + RELOCATION_OPERATION_TIMEOUT_MS,
|
|
2441
|
+
relocationMetric?: ZLinkRelocationMetricOperation
|
|
2276
2442
|
): Promise<void> {
|
|
2277
2443
|
if (isRelocationDebugEnabled()) {
|
|
2278
2444
|
relocationDebug('coordinator.begin', {
|
|
@@ -2284,12 +2450,12 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2284
2450
|
const coordinator = {
|
|
2285
2451
|
ownerId: primary.ownerId,
|
|
2286
2452
|
leaseGeneration: primary.ownerLeaseGeneration,
|
|
2287
|
-
nodeRid:
|
|
2453
|
+
nodeRid: localStatus.routingId,
|
|
2288
2454
|
nodeGeneration: localStatus.lifecycleGeneration,
|
|
2289
2455
|
expectedAuthorityStoreVersion: primary.storeVersion.value
|
|
2290
2456
|
} satisfies ServiceWireRelocationCoordinatorFence;
|
|
2291
2457
|
const targetFence = {
|
|
2292
|
-
nodeRid:
|
|
2458
|
+
nodeRid: target.rid,
|
|
2293
2459
|
nodeGeneration: target.lifecycleGeneration,
|
|
2294
2460
|
ownerId: target.ownerId,
|
|
2295
2461
|
ownerLeaseGeneration: target.leaseGeneration
|
|
@@ -2302,13 +2468,6 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2302
2468
|
// attempt even when it reuses the same routing id.
|
|
2303
2469
|
const targetAttemptGeneration = target.lifecycleGeneration;
|
|
2304
2470
|
const relayAuthorityId = wireIdText(relocationWireId(captured.envelope.aggregateId));
|
|
2305
|
-
const relayAuthorityKey = primaryKey(captured.envelope);
|
|
2306
|
-
if (this.relocationAuthorityKeys.has(relayAuthorityId)) {
|
|
2307
|
-
throw new Error(
|
|
2308
|
-
`Relocation reply relay authority '${relayAuthorityId}' is already registered.`
|
|
2309
|
-
);
|
|
2310
|
-
}
|
|
2311
|
-
this.relocationAuthorityKeys.set(relayAuthorityId, relayAuthorityKey);
|
|
2312
2471
|
// The captured payload stays in source memory: this copy is the only
|
|
2313
2472
|
// handoff original until the cutover submit terminal and the
|
|
2314
2473
|
// retransmission window end (spec 28 §4.2).
|
|
@@ -2328,6 +2487,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2328
2487
|
primary
|
|
2329
2488
|
);
|
|
2330
2489
|
const encoded = encodeServiceRelocationEnvelope(transportEnvelope, target.applicationVersion);
|
|
2490
|
+
relocationMetric?.recordBytes(encoded.byteLength);
|
|
2331
2491
|
const limits = this.relocationLimits();
|
|
2332
2492
|
// Actor Join threads the target's admission-time advertised cap in;
|
|
2333
2493
|
// other relocation paths have no such negotiation and fall back to the
|
|
@@ -2344,7 +2504,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2344
2504
|
const restoreDeadlineReached = () =>
|
|
2345
2505
|
signal?.aborted === true || performance.now() >= controlDeadlineAtMs;
|
|
2346
2506
|
let readyReceived = false;
|
|
2347
|
-
let settlement:
|
|
2507
|
+
let settlement:
|
|
2508
|
+
Awaited<ReturnType<ZLinkHostServiceRelocationRuntime['settleSourceAuthority']>> | undefined;
|
|
2348
2509
|
try {
|
|
2349
2510
|
const prepare = {
|
|
2350
2511
|
kind: 'prepare',
|
|
@@ -2359,7 +2520,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2359
2520
|
payloadTotalLength: BigInt(plan.totalLength),
|
|
2360
2521
|
payloadChunkCount: plan.chunkCount,
|
|
2361
2522
|
payloadChecksumCrc32c: plan.checksumCrc32c,
|
|
2362
|
-
applicationVersion: target.applicationVersion
|
|
2523
|
+
applicationVersion: target.applicationVersion,
|
|
2524
|
+
deadlineUnixMs: BigInt(
|
|
2525
|
+
Date.now() + Math.max(0, Math.ceil(controlDeadlineAtMs - performance.now()))
|
|
2526
|
+
)
|
|
2363
2527
|
} satisfies ServiceMaintenanceRelocationPrepare;
|
|
2364
2528
|
// Start the Restore request first (it also registers the temporary
|
|
2365
2529
|
// queue and the assembly on the target), then stream the payload chunks
|
|
@@ -2400,6 +2564,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2400
2564
|
// forward the source can no longer reopen dispatch without risking two
|
|
2401
2565
|
// owners.
|
|
2402
2566
|
readyReceived = true;
|
|
2567
|
+
actorJoinProfile?.relayReady();
|
|
2403
2568
|
|
|
2404
2569
|
const boundaryBatch: Array<{
|
|
2405
2570
|
readonly frame: Buffer;
|
|
@@ -2480,7 +2645,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2480
2645
|
primary,
|
|
2481
2646
|
targetFence,
|
|
2482
2647
|
restoreDeadlineReached,
|
|
2483
|
-
window
|
|
2648
|
+
window,
|
|
2649
|
+
relocationMetric
|
|
2484
2650
|
);
|
|
2485
2651
|
} finally {
|
|
2486
2652
|
this.closeSourceCutoverWindow(relayAuthorityId, window);
|
|
@@ -2495,11 +2661,13 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2495
2661
|
// Preserve won: the captured queue, timers and held records return to
|
|
2496
2662
|
// the source queue in their accepted order, and dispatch reopens.
|
|
2497
2663
|
await captured.abortSource();
|
|
2664
|
+
this.completeSourceRelocationMetric(relocationMetric, settlement);
|
|
2498
2665
|
throw new Error(
|
|
2499
2666
|
`Relocation '${relayAuthorityId}' ended by the source Preserve fence; the source kept the authority.`
|
|
2500
2667
|
);
|
|
2501
2668
|
}
|
|
2502
2669
|
if (settlement === 'lost') {
|
|
2670
|
+
this.completeSourceRelocationMetric(relocationMetric, settlement);
|
|
2503
2671
|
// Expired owner: dispatch and Store changes stop for good and every
|
|
2504
2672
|
// pending request without a terminal gets one Unavailable.
|
|
2505
2673
|
const unavailable = createInternalFrameworkException(
|
|
@@ -2510,11 +2678,20 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2510
2678
|
await captured.discardSource(unavailable);
|
|
2511
2679
|
throw new ServiceRelocationAuthorityError(unavailable.message, { cause: unavailable });
|
|
2512
2680
|
}
|
|
2681
|
+
this.completeSourceRelocationMetric(relocationMetric, settlement);
|
|
2513
2682
|
for (const session of sessions) session.prepared.setReplayResults([]);
|
|
2514
|
-
await captured.commitSource();
|
|
2683
|
+
await captured.commitSource(settlement.followAuthorities);
|
|
2515
2684
|
await this.options.reconcileStatefulAuthorityRoutes?.(signal);
|
|
2516
2685
|
} catch (error) {
|
|
2517
|
-
if (
|
|
2686
|
+
if (
|
|
2687
|
+
this.disposed &&
|
|
2688
|
+
(error instanceof ServiceRelocationRuntimeStoppedError ||
|
|
2689
|
+
(error instanceof ZLinkBackendResultError &&
|
|
2690
|
+
error.result ===
|
|
2691
|
+
(error.operation === 'request' ? RequestResult.Terminated : SubmitResult.Terminated)))
|
|
2692
|
+
)
|
|
2693
|
+
this.completeSourceRelocationMetric(relocationMetric, undefined, true);
|
|
2694
|
+
if (settlement === undefined || typeof settlement === 'string') throw error;
|
|
2518
2695
|
// Target commit is confirmed; later source cleanup failures cannot move
|
|
2519
2696
|
// the authority back (spec 28 §10).
|
|
2520
2697
|
const authority = await requireAuthority(
|
|
@@ -2528,17 +2705,33 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2528
2705
|
error
|
|
2529
2706
|
);
|
|
2530
2707
|
} finally {
|
|
2708
|
+
this.completeSourceRelocationMetric(relocationMetric);
|
|
2531
2709
|
if (!readyReceived) {
|
|
2532
2710
|
// Only an explicit failure before the Ready reply becomes accepted
|
|
2533
2711
|
// restores the source from the memory payload (spec 28 §9).
|
|
2534
2712
|
await captured.abortSource().catch(() => undefined);
|
|
2535
2713
|
}
|
|
2536
|
-
if (this.relocationAuthorityKeys.get(relayAuthorityId) === relayAuthorityKey) {
|
|
2537
|
-
this.relocationAuthorityKeys.delete(relayAuthorityId);
|
|
2538
|
-
}
|
|
2539
2714
|
}
|
|
2540
2715
|
}
|
|
2541
2716
|
|
|
2717
|
+
private completeSourceRelocationMetric(
|
|
2718
|
+
metric: ZLinkRelocationMetricOperation | undefined,
|
|
2719
|
+
settlement?: SourceAuthorityCommit | 'source' | 'lost',
|
|
2720
|
+
sourceStopped = false
|
|
2721
|
+
): void {
|
|
2722
|
+
metric?.complete(
|
|
2723
|
+
settlement === 'source'
|
|
2724
|
+
? 'aborted'
|
|
2725
|
+
: settlement === 'lost'
|
|
2726
|
+
? 'failed'
|
|
2727
|
+
: settlement !== undefined
|
|
2728
|
+
? 'completed'
|
|
2729
|
+
: sourceStopped
|
|
2730
|
+
? 'shutdown'
|
|
2731
|
+
: 'failed'
|
|
2732
|
+
);
|
|
2733
|
+
}
|
|
2734
|
+
|
|
2542
2735
|
/** Streams the payload chunks of one stage under the effective in-flight budgets. */
|
|
2543
2736
|
private async sendRelocationStateChunks(
|
|
2544
2737
|
meshName: string,
|
|
@@ -2719,7 +2912,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2719
2912
|
* excludes a late target commit. `target`: the
|
|
2720
2913
|
* target committed. `source`: Preserve fenced the target out while the
|
|
2721
2914
|
* source owner lease was valid. `lost`: the source owner lease ended first
|
|
2722
|
-
* or authority is Missing or
|
|
2915
|
+
* or authority is Missing or fails the same-object, advanced-generation fence.
|
|
2916
|
+
* A committed later owner has no Follow snapshot (Location runtime §10). An uncertain
|
|
2723
2917
|
* Store or lease result keeps the work retained and repeats the same decision.
|
|
2724
2918
|
*/
|
|
2725
2919
|
private async settleSourceAuthority(
|
|
@@ -2727,12 +2921,16 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2727
2921
|
primary: ZLinkAuthoritySnapshot,
|
|
2728
2922
|
target: ServiceWireRelocationTarget,
|
|
2729
2923
|
restoreDeadlineReached: () => boolean,
|
|
2730
|
-
window: SourceCutoverWindow
|
|
2731
|
-
|
|
2924
|
+
window: SourceCutoverWindow,
|
|
2925
|
+
relocationMetric?: ZLinkRelocationMetricOperation
|
|
2926
|
+
): Promise<SourceAuthorityCommit | 'source' | 'lost'> {
|
|
2732
2927
|
const owner = { ownerId: primary.ownerId, leaseGeneration: primary.ownerLeaseGeneration };
|
|
2733
2928
|
const store = this.requireLocationStore();
|
|
2734
2929
|
for (;;) {
|
|
2735
|
-
if (this.disposed)
|
|
2930
|
+
if (this.disposed) {
|
|
2931
|
+
this.completeSourceRelocationMetric(relocationMetric, undefined, true);
|
|
2932
|
+
throw new ServiceRelocationRuntimeStoppedError();
|
|
2933
|
+
}
|
|
2736
2934
|
await this.resendSourceCutover(window);
|
|
2737
2935
|
const observed = await this.readSourceSettlement(envelope, primary, target).catch(
|
|
2738
2936
|
(error: unknown) => {
|
|
@@ -2740,7 +2938,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2740
2938
|
return undefined;
|
|
2741
2939
|
}
|
|
2742
2940
|
);
|
|
2743
|
-
if (observed?.kind === 'target') return
|
|
2941
|
+
if (observed?.kind === 'target') return observed;
|
|
2744
2942
|
if (observed?.kind === 'other') return 'lost';
|
|
2745
2943
|
if (restoreDeadlineReached() && observed?.kind === 'source') {
|
|
2746
2944
|
// An unreadable lease is an uncertain result, not an expiry.
|
|
@@ -2783,12 +2981,13 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2783
2981
|
primary: ZLinkAuthoritySnapshot,
|
|
2784
2982
|
target: ServiceWireRelocationTarget
|
|
2785
2983
|
): Promise<
|
|
2786
|
-
|
|
|
2984
|
+
| SourceAuthorityCommit
|
|
2787
2985
|
| { readonly kind: 'source'; readonly current: ZLinkAuthoritySnapshot }
|
|
2788
2986
|
| { readonly kind: 'other' }
|
|
2789
2987
|
> {
|
|
2790
2988
|
const primaryAuthorityKey = primaryKey(envelope);
|
|
2791
2989
|
let committed = 0;
|
|
2990
|
+
const followAuthorities = new Map<string, ZLinkAuthoritySnapshot>();
|
|
2792
2991
|
let primaryCurrent: ZLinkAuthoritySnapshot | undefined;
|
|
2793
2992
|
for (const participant of envelope.participants) {
|
|
2794
2993
|
const current = await this.requireLocationStore().readAuthority({
|
|
@@ -2797,17 +2996,23 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2797
2996
|
if (current.kind !== 'snapshot') return { kind: 'other' };
|
|
2798
2997
|
if (
|
|
2799
2998
|
current.objectGeneration === participant.objectGeneration &&
|
|
2800
|
-
current.ownerId === target.ownerId &&
|
|
2801
|
-
current.ownerLeaseGeneration === target.ownerLeaseGeneration &&
|
|
2802
2999
|
current.authorityOwnerGeneration > participant.authorityOwnerGeneration &&
|
|
2803
|
-
|
|
2804
|
-
|
|
3000
|
+
// Location runtime §6: an owner is the (OwnerId, OwnerLeaseGeneration) pair;
|
|
3001
|
+
// a restarted node keeps its OwnerId under a new lease generation.
|
|
3002
|
+
(current.ownerId !== primary.ownerId ||
|
|
3003
|
+
current.ownerLeaseGeneration !== primary.ownerLeaseGeneration)
|
|
2805
3004
|
) {
|
|
2806
3005
|
committed += 1;
|
|
3006
|
+
// Location runtime §10: only the observed first target can receive Follow.
|
|
3007
|
+
if (
|
|
3008
|
+
current.ownerId === target.ownerId &&
|
|
3009
|
+
current.ownerLeaseGeneration === target.ownerLeaseGeneration
|
|
3010
|
+
)
|
|
3011
|
+
followAuthorities.set(participant.key, current);
|
|
2807
3012
|
}
|
|
2808
3013
|
if (participant.key === primaryAuthorityKey) primaryCurrent = current;
|
|
2809
3014
|
}
|
|
2810
|
-
if (committed === envelope.participants.length) return { kind: 'target' };
|
|
3015
|
+
if (committed === envelope.participants.length) return { kind: 'target', followAuthorities };
|
|
2811
3016
|
if (primaryCurrent !== undefined && isSourceOwner(primaryCurrent, primary)) {
|
|
2812
3017
|
return { kind: 'source', current: primaryCurrent };
|
|
2813
3018
|
}
|
|
@@ -2858,12 +3063,25 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2858
3063
|
sourceNodeRid: RoutingId,
|
|
2859
3064
|
signal?: AbortSignal
|
|
2860
3065
|
): Promise<ServiceMaintenanceRelocationReady> {
|
|
3066
|
+
// Host relocation flow 05: a sealed target refuses a new PREPARE before any
|
|
3067
|
+
// staging. The typed ShuttingDown reaches the source as relocationFailed.
|
|
3068
|
+
if (this.targetAdmissionSealed()) {
|
|
3069
|
+
throw new ZLinkFrameworkException(
|
|
3070
|
+
ZLinkFrameworkErrorKind.ShuttingDown,
|
|
3071
|
+
'Relocation target admission is sealed for shutdown.'
|
|
3072
|
+
);
|
|
3073
|
+
}
|
|
3074
|
+
signal =
|
|
3075
|
+
signal === undefined
|
|
3076
|
+
? this.targetPreparationStop.signal
|
|
3077
|
+
: AbortSignal.any([signal, this.targetPreparationStop.signal]);
|
|
3078
|
+
signal.throwIfAborted();
|
|
2861
3079
|
const targetStatus = this.requireMeshNode(meshName).status();
|
|
2862
3080
|
const targetOwner = this.options.currentOwner();
|
|
2863
3081
|
const targetDescriptor = this.options.localDescriptor?.(meshName);
|
|
2864
3082
|
if (
|
|
2865
3083
|
targetOwner === undefined ||
|
|
2866
|
-
|
|
3084
|
+
!routingIdsEqual(targetStatus.routingId, request.target.nodeRid) ||
|
|
2867
3085
|
targetStatus.lifecycleGeneration !== request.target.nodeGeneration ||
|
|
2868
3086
|
targetOwner.ownerId !== request.target.ownerId ||
|
|
2869
3087
|
targetOwner.leaseGeneration !== request.target.ownerLeaseGeneration ||
|
|
@@ -2893,11 +3111,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2893
3111
|
try {
|
|
2894
3112
|
payload = await pendingAssembly.assembly.payload();
|
|
2895
3113
|
} catch (error) {
|
|
2896
|
-
//
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
// wire code in the vocabulary yet, so it falls through unwrapped to
|
|
2900
|
-
// the generic InternalFailure code (spec 15 §4).
|
|
3114
|
+
// Target shutdown preserves its lifecycle failure instead of reporting payload corruption.
|
|
3115
|
+
this.targetAssemblies.delete(stagingId);
|
|
3116
|
+
if (this.targetPreparationStop.signal.aborted) throw this.targetPreparationStop.signal.reason;
|
|
2901
3117
|
if (this.disposed) throw error;
|
|
2902
3118
|
const message =
|
|
2903
3119
|
error !== null && typeof error === 'object' && 'message' in error
|
|
@@ -2930,6 +3146,51 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2930
3146
|
// rejects unrelated authority CAS until commit/abort.
|
|
2931
3147
|
materialized = await this.materializeTargetEnvelope(meshName, inventoryEnvelope, signal);
|
|
2932
3148
|
await materialized.owner.restoreSavedWork(materialized.staging, signal);
|
|
3149
|
+
for (const participant of inventoryEnvelope.participants) {
|
|
3150
|
+
const binding = participant.sessionBinding;
|
|
3151
|
+
if (binding === undefined) continue;
|
|
3152
|
+
const peerRid = decodeRoutingId(
|
|
3153
|
+
'',
|
|
3154
|
+
Buffer.from(binding.sessionOwnerNodeRid).toString('hex')
|
|
3155
|
+
);
|
|
3156
|
+
const node = this.requireMeshNode(meshName);
|
|
3157
|
+
if (routingIdsEqual(node.status().routingId, peerRid)) continue;
|
|
3158
|
+
if (node.isPeerRouteReady?.(peerRid, binding.sessionOwnerNodeGeneration) !== true) {
|
|
3159
|
+
// Subscribe before requesting reconciliation so a synchronous admission cannot be lost.
|
|
3160
|
+
const wait = node.waitForPeerRoute;
|
|
3161
|
+
if (wait === undefined) throw new Error('Mesh peer route observation is unavailable.');
|
|
3162
|
+
const ready = wait.call(
|
|
3163
|
+
node,
|
|
3164
|
+
peerRid,
|
|
3165
|
+
binding.sessionOwnerNodeGeneration,
|
|
3166
|
+
request.deadlineUnixMs,
|
|
3167
|
+
signal
|
|
3168
|
+
);
|
|
3169
|
+
void this.options
|
|
3170
|
+
.requestPeerReconcile?.(meshName, String(peerRid))
|
|
3171
|
+
.catch((error) =>
|
|
3172
|
+
console.error('[zlink.runtime.relocation.session_route_reconcile_failed]', error)
|
|
3173
|
+
);
|
|
3174
|
+
try {
|
|
3175
|
+
await ready;
|
|
3176
|
+
} catch (error) {
|
|
3177
|
+
if (error instanceof OperationTimeoutError) {
|
|
3178
|
+
throw new ZLinkFrameworkException(
|
|
3179
|
+
ZLinkFrameworkErrorKind.DeadlineExceeded,
|
|
3180
|
+
'Relocation source operation deadline exceeded waiting for Session owner route.'
|
|
3181
|
+
);
|
|
3182
|
+
}
|
|
3183
|
+
throw error;
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
if (request.deadlineUnixMs <= BigInt(Date.now())) {
|
|
3188
|
+
throw new ZLinkFrameworkException(
|
|
3189
|
+
ZLinkFrameworkErrorKind.DeadlineExceeded,
|
|
3190
|
+
'Relocation source operation deadline exceeded before relay-ready.'
|
|
3191
|
+
);
|
|
3192
|
+
}
|
|
3193
|
+
signal.throwIfAborted();
|
|
2933
3194
|
reservation = await this.reserveTargetForPrepare(
|
|
2934
3195
|
meshName,
|
|
2935
3196
|
request,
|
|
@@ -2939,7 +3200,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2939
3200
|
const offer: TargetRelocationOffer = {
|
|
2940
3201
|
prepare: request,
|
|
2941
3202
|
prepareFingerprint: fingerprint,
|
|
2942
|
-
authenticatedSourceNodeRid:
|
|
3203
|
+
authenticatedSourceNodeRid: sourceNodeRid,
|
|
2943
3204
|
envelope: inventoryEnvelope,
|
|
2944
3205
|
reservation
|
|
2945
3206
|
};
|
|
@@ -2955,6 +3216,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2955
3216
|
};
|
|
2956
3217
|
this.targetStages.set(stagingId, stage);
|
|
2957
3218
|
} catch (error) {
|
|
3219
|
+
this.targetAssemblies.delete(stagingId);
|
|
2958
3220
|
releaseJoinAdmission();
|
|
2959
3221
|
releaseActivation();
|
|
2960
3222
|
if (reservation !== undefined) {
|
|
@@ -3122,30 +3384,51 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3122
3384
|
stage.releaseActivation();
|
|
3123
3385
|
resumeStartedAt = performance.now();
|
|
3124
3386
|
stage.phase = 'committed';
|
|
3125
|
-
await stage.owner.normalize(stage.staging, authority, signal);
|
|
3126
3387
|
await stage.owner.publish(stage.staging, authority, signal);
|
|
3127
|
-
// The target may have visited this Actor before and still retain that
|
|
3128
|
-
// tenure in its private Actor-client resolver. The target-only CAS is
|
|
3129
|
-
// now authoritative, so expire the old projection before OnJoinedActor
|
|
3130
|
-
// can issue an Actor request and deadlock behind source leave.
|
|
3131
|
-
for (const hidden of stage.staging.hidden.values()) {
|
|
3132
|
-
if (hidden.actor !== undefined) {
|
|
3133
|
-
this.options.invalidateActorRoute?.(hidden.actor.context.actorId);
|
|
3134
|
-
}
|
|
3135
|
-
}
|
|
3136
|
-
await this.finalizeActorJoinProfiles(meshName, stage, signal);
|
|
3137
3388
|
// Clear the final relocation publication before admission becomes
|
|
3138
3389
|
// externally visible so a later Join cannot reserve against the
|
|
3139
3390
|
// previous root. A bounded CAS conflict must not roll back already
|
|
3140
3391
|
// committed target ownership or strand its staged object.
|
|
3141
3392
|
try {
|
|
3142
|
-
await this.clearTargetRelocationPublication(stage, authority, signal);
|
|
3393
|
+
authority = await this.clearTargetRelocationPublication(stage, authority, signal);
|
|
3143
3394
|
} catch (error) {
|
|
3144
3395
|
console.warn('[zlink.runtime.relocation.publication_clear_failed]', stagingId, error);
|
|
3145
3396
|
this.options.metrics?.count('zlink.relocation.publication_clear_failed', 1, {
|
|
3146
3397
|
mesh_name: meshName
|
|
3147
3398
|
});
|
|
3148
3399
|
}
|
|
3400
|
+
for (const [index, participant] of stage.staging.envelope.participants.entries()) {
|
|
3401
|
+
const hidden = stage.staging.hidden.get(participant.key);
|
|
3402
|
+
if (hidden?.actor === undefined || hidden.replayPackets.length === 0) continue;
|
|
3403
|
+
const coordinator = this.targetReplyRelayCoordinator(meshName, stage, authority);
|
|
3404
|
+
hidden.replayStart = stage.finalize;
|
|
3405
|
+
hidden.onReplayResult = (packet, result) =>
|
|
3406
|
+
this.relayTerminalReply(
|
|
3407
|
+
meshName,
|
|
3408
|
+
stage,
|
|
3409
|
+
coordinator,
|
|
3410
|
+
BigInt(index + 1),
|
|
3411
|
+
packet,
|
|
3412
|
+
result,
|
|
3413
|
+
signal
|
|
3414
|
+
);
|
|
3415
|
+
}
|
|
3416
|
+
await stage.owner.normalize(stage.staging, authority, signal);
|
|
3417
|
+
// The target may have visited this Actor before and still retain that
|
|
3418
|
+
// tenure in its private Actor-client resolver. The target-only CAS is
|
|
3419
|
+
// now authoritative, so expire the old projection before OnJoinedActor
|
|
3420
|
+
// can issue an Actor request and deadlock behind source leave.
|
|
3421
|
+
for (const hidden of stage.staging.hidden.values()) {
|
|
3422
|
+
if (hidden.actor !== undefined) {
|
|
3423
|
+
this.options.invalidateActorRoute?.(hidden.actor.context.actorId);
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
await this.finalizeActorJoinProfiles(meshName, stage, signal);
|
|
3427
|
+
await this.publishSessionRoutes(stage.staging);
|
|
3428
|
+
stage.phase = 'open';
|
|
3429
|
+
this.terminalTargets.remember(stagingId, stage.offer.prepareFingerprint);
|
|
3430
|
+
this.targetStages.delete(stagingId);
|
|
3431
|
+
this.targetAssemblies.delete(stagingId);
|
|
3149
3432
|
await stage.owner.openAdmission(stage.staging, signal);
|
|
3150
3433
|
// S2→S3: owner confirmation to application dispatch opening.
|
|
3151
3434
|
this.options.metrics?.duration(
|
|
@@ -3153,17 +3436,6 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3153
3436
|
Math.max(0, performance.now() - resumeStartedAt) / MILLISECONDS_PER_SECOND,
|
|
3154
3437
|
{ mesh_name: meshName }
|
|
3155
3438
|
);
|
|
3156
|
-
await this.publishSessionRoutes(stage.staging);
|
|
3157
|
-
await this.relayTerminalReplies(
|
|
3158
|
-
meshName,
|
|
3159
|
-
stage,
|
|
3160
|
-
this.targetReplyRelayCoordinator(meshName, stage, authority),
|
|
3161
|
-
signal
|
|
3162
|
-
);
|
|
3163
|
-
stage.phase = 'open';
|
|
3164
|
-
this.terminalTargets.remember(stagingId, stage.offer.prepareFingerprint);
|
|
3165
|
-
this.targetStages.delete(stagingId);
|
|
3166
|
-
this.targetAssemblies.delete(stagingId);
|
|
3167
3439
|
} catch (error) {
|
|
3168
3440
|
this.terminalTargets.remember(stagingId, stage.offer.prepareFingerprint);
|
|
3169
3441
|
this.targetStages.delete(stagingId);
|
|
@@ -3309,9 +3581,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3309
3581
|
if (profile.state.actorId !== input.actorId || !input.succeeded) {
|
|
3310
3582
|
throw new Error(`Actor Join relocation '${input.transferId}' source leave identity changed.`);
|
|
3311
3583
|
}
|
|
3312
|
-
this.
|
|
3313
|
-
this.terminalActorJoinSourceLeaves.remember(input.transferId, input.actorId);
|
|
3314
|
-
void this.finalizeActorJoinSource(profile);
|
|
3584
|
+
void this.retireActorJoinSource(input.transferId, profile);
|
|
3315
3585
|
return true;
|
|
3316
3586
|
}
|
|
3317
3587
|
|
|
@@ -3349,7 +3619,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3349
3619
|
stage: LocalStage,
|
|
3350
3620
|
primary: ZLinkAuthoritySnapshot,
|
|
3351
3621
|
signal?: AbortSignal
|
|
3352
|
-
): Promise<
|
|
3622
|
+
): Promise<ZLinkAuthoritySnapshot> {
|
|
3353
3623
|
await this.releaseParticipantAuthorities(
|
|
3354
3624
|
stage.staging.envelope,
|
|
3355
3625
|
stage.staging.primaryAuthorityKey.value,
|
|
@@ -3360,7 +3630,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3360
3630
|
// committed snapshot decides. A conflict means another owner changed the
|
|
3361
3631
|
// row; the caller records it and leaves the committed ownership intact.
|
|
3362
3632
|
const publication = this.codec.read(primary.payload);
|
|
3363
|
-
if (publication === undefined) return;
|
|
3633
|
+
if (publication === undefined) return primary;
|
|
3364
3634
|
const result = await this.requireLocationStore().compareExchangeAuthority(
|
|
3365
3635
|
stage.staging.primaryAuthorityKey,
|
|
3366
3636
|
primary.storeVersion,
|
|
@@ -3374,6 +3644,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3374
3644
|
if (result.kind !== 'stored') {
|
|
3375
3645
|
throw new Error('Relocation target authority normalization CAS failed.');
|
|
3376
3646
|
}
|
|
3647
|
+
return { ...result, kind: 'snapshot' };
|
|
3377
3648
|
}
|
|
3378
3649
|
|
|
3379
3650
|
private async handleReplyRelay(
|
|
@@ -3385,49 +3656,53 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3385
3656
|
if (sourceNodeRid === null) {
|
|
3386
3657
|
throw new Error('Relocation reply relay has no authenticated target node.');
|
|
3387
3658
|
}
|
|
3388
|
-
await this.requireAdmittedPeer(meshName,
|
|
3389
|
-
const
|
|
3390
|
-
const
|
|
3391
|
-
if (authorityKey === undefined) {
|
|
3392
|
-
throw new Error('Relocation reply relay has no registered durable authority.');
|
|
3393
|
-
}
|
|
3394
|
-
const authority = await requireAuthority(
|
|
3395
|
-
this.requireLocationStore(),
|
|
3396
|
-
{ value: authorityKey } as ZLinkAuthorityKey,
|
|
3397
|
-
signal
|
|
3398
|
-
);
|
|
3399
|
-
if (
|
|
3400
|
-
authority.storeVersion.value !== relay.coordinator.expectedAuthorityStoreVersion ||
|
|
3401
|
-
authority.ownerId !== relay.coordinator.ownerId ||
|
|
3402
|
-
authority.ownerLeaseGeneration !== relay.coordinator.leaseGeneration ||
|
|
3403
|
-
String(authority.allocation.descriptor.rid) !== relay.coordinator.nodeRid ||
|
|
3404
|
-
authority.allocation.descriptorLifecycleGeneration !== relay.coordinator.nodeGeneration
|
|
3405
|
-
) {
|
|
3406
|
-
throw new Error('Relocation reply relay coordinator authority fence is stale.');
|
|
3407
|
-
}
|
|
3408
|
-
const accepted = this.options.actorTransfer.relayCanonicalMaintenanceTerminal(
|
|
3409
|
-
wireIdText(relay.operation),
|
|
3410
|
-
relay.replyRouteId.toString(),
|
|
3411
|
-
handoffResultFromRelay(relay),
|
|
3412
|
-
sourceNodeRid
|
|
3413
|
-
);
|
|
3414
|
-
if (
|
|
3415
|
-
(accepted.status !== 'terminalReceived' && accepted.status !== 'alreadyTerminal') ||
|
|
3416
|
-
accepted.source === undefined
|
|
3417
|
-
) {
|
|
3418
|
-
throw new Error('Canonical relocation reply relay collided with another source route.');
|
|
3419
|
-
}
|
|
3420
|
-
const requestSource = handoffSourceFence(accepted.source);
|
|
3421
|
-
const localStatus = this.requireMeshNode(meshName).status();
|
|
3659
|
+
await this.requireAdmittedPeer(meshName, sourceNodeRid, relay.coordinator, signal);
|
|
3660
|
+
const node = this.requireMeshNode(meshName);
|
|
3661
|
+
const localStatus = node.status();
|
|
3422
3662
|
const localOwner = this.options.currentOwner();
|
|
3423
|
-
if (
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3663
|
+
if (localOwner === undefined)
|
|
3664
|
+
throw new Error('Relocation requester owner lease is unavailable.');
|
|
3665
|
+
let status = node.acceptRelocatedActorTerminal?.(relay.operation, relay.replyRouteId, {
|
|
3666
|
+
terminalResult: relay.terminalResult,
|
|
3667
|
+
failureCode: relay.failureCode,
|
|
3668
|
+
...(relay.payload === undefined
|
|
3669
|
+
? {}
|
|
3670
|
+
: {
|
|
3671
|
+
payload: {
|
|
3672
|
+
packetName: relay.payload.packetName,
|
|
3673
|
+
contentType: relay.payload.contentType,
|
|
3674
|
+
payload: Buffer.from(relay.payload.bytes)
|
|
3675
|
+
}
|
|
3676
|
+
})
|
|
3677
|
+
});
|
|
3678
|
+
let requestSource: ServiceWireRequestSourceFence = {
|
|
3679
|
+
ownerId: localOwner.ownerId,
|
|
3680
|
+
leaseGeneration: localOwner.leaseGeneration,
|
|
3681
|
+
nodeRid: localStatus.routingId,
|
|
3682
|
+
nodeGeneration: localStatus.lifecycleGeneration
|
|
3683
|
+
};
|
|
3684
|
+
if (status === undefined) {
|
|
3685
|
+
const accepted = this.options.actorTransfer.relayCanonicalMaintenanceTerminal(
|
|
3686
|
+
relay.operation.high.toString(16).padStart(16, '0') +
|
|
3687
|
+
relay.operation.low.toString(16).padStart(16, '0'),
|
|
3688
|
+
relay.replyRouteId.toString(),
|
|
3689
|
+
handoffResultFromRelay(relay),
|
|
3690
|
+
sourceNodeRid
|
|
3691
|
+
);
|
|
3692
|
+
if (accepted.status === 'notAcknowledged' || accepted.source === undefined) {
|
|
3693
|
+
throw new Error('Canonical relocation reply relay collided with another source route.');
|
|
3694
|
+
}
|
|
3695
|
+
requestSource = handoffSourceFence(accepted.source);
|
|
3696
|
+
if (
|
|
3697
|
+
!sameRequestSource(requestSource, {
|
|
3698
|
+
ownerId: localOwner.ownerId,
|
|
3699
|
+
leaseGeneration: localOwner.leaseGeneration,
|
|
3700
|
+
nodeRid: localStatus.routingId,
|
|
3701
|
+
nodeGeneration: localStatus.lifecycleGeneration
|
|
3702
|
+
})
|
|
3703
|
+
)
|
|
3704
|
+
throw new Error('Relocation reply relay request-source fence changed after capture.');
|
|
3705
|
+
status = accepted.status;
|
|
3431
3706
|
}
|
|
3432
3707
|
const ack: ServiceMaintenanceReplyRelayAck = {
|
|
3433
3708
|
relocation: relay.relocation,
|
|
@@ -3435,7 +3710,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3435
3710
|
operation: relay.operation,
|
|
3436
3711
|
replyRouteId: relay.replyRouteId,
|
|
3437
3712
|
requestSource,
|
|
3438
|
-
status
|
|
3713
|
+
status
|
|
3439
3714
|
};
|
|
3440
3715
|
if (
|
|
3441
3716
|
(await this.sendInfrastructureControl(
|
|
@@ -3455,9 +3730,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3455
3730
|
expectedRequestSource: ServiceWireRequestSourceFence,
|
|
3456
3731
|
signal?: AbortSignal
|
|
3457
3732
|
): Promise<RelocationTerminalDelivery> {
|
|
3458
|
-
const targetRid = String(ackTargetNodeRid);
|
|
3459
3733
|
const identityKey = replyRelayIdentityKey(request);
|
|
3460
|
-
const key = replyRelayPendingKey(
|
|
3734
|
+
const key = replyRelayPendingKey(ackTargetNodeRid, request);
|
|
3461
3735
|
if (this.pendingReplyRelays.has(key)) {
|
|
3462
3736
|
throw new Error(`Relocation reply relay ACK '${key}' is already pending.`);
|
|
3463
3737
|
}
|
|
@@ -3470,7 +3744,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3470
3744
|
action();
|
|
3471
3745
|
};
|
|
3472
3746
|
const pending: PendingRelocationReplyRelay = {
|
|
3473
|
-
ackTargetNodeRid
|
|
3747
|
+
ackTargetNodeRid,
|
|
3474
3748
|
identityKey,
|
|
3475
3749
|
request,
|
|
3476
3750
|
expectedRequestSource,
|
|
@@ -3515,8 +3789,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3515
3789
|
signal?: AbortSignal
|
|
3516
3790
|
): Promise<void> {
|
|
3517
3791
|
const identityKey = replyRelayIdentityKey(ack);
|
|
3518
|
-
const exactKey =
|
|
3519
|
-
sourceNodeRid === null ? undefined : replyRelayPendingKey(String(sourceNodeRid), ack);
|
|
3792
|
+
const exactKey = sourceNodeRid === null ? undefined : replyRelayPendingKey(sourceNodeRid, ack);
|
|
3520
3793
|
const pending = exactKey === undefined ? undefined : this.pendingReplyRelays.get(exactKey);
|
|
3521
3794
|
if (pending === undefined) {
|
|
3522
3795
|
const collision = [...this.pendingReplyRelays.values()].find(
|
|
@@ -3554,14 +3827,14 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3554
3827
|
signal?: AbortSignal
|
|
3555
3828
|
): Promise<void> {
|
|
3556
3829
|
const descriptor = (await this.options.liveDescriptors(meshName, signal)).find(
|
|
3557
|
-
(value) =>
|
|
3830
|
+
(value) => routingIdsEqual(value.rid, authenticatedRid) && value.state === 1
|
|
3558
3831
|
);
|
|
3559
3832
|
if (
|
|
3560
3833
|
descriptor === undefined ||
|
|
3561
3834
|
(expected !== undefined &&
|
|
3562
3835
|
(descriptor.ownerId !== expected.ownerId ||
|
|
3563
3836
|
descriptor.leaseGeneration !== expected.leaseGeneration ||
|
|
3564
|
-
|
|
3837
|
+
!routingIdsEqual(descriptor.rid, expected.nodeRid) ||
|
|
3565
3838
|
descriptor.lifecycleGeneration !== expected.nodeGeneration))
|
|
3566
3839
|
) {
|
|
3567
3840
|
throw new Error('Relocation reply relay peer is not the exact admitted source fence.');
|
|
@@ -3580,7 +3853,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3580
3853
|
owner === undefined ||
|
|
3581
3854
|
owner.ownerId !== target.ownerId ||
|
|
3582
3855
|
owner.leaseGeneration !== target.ownerLeaseGeneration ||
|
|
3583
|
-
|
|
3856
|
+
!routingIdsEqual(status.routingId, target.nodeRid) ||
|
|
3584
3857
|
status.lifecycleGeneration !== target.nodeGeneration
|
|
3585
3858
|
) {
|
|
3586
3859
|
throw new Error('Relocation reply relay target coordinator fence is stale.');
|
|
@@ -3588,42 +3861,47 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3588
3861
|
return {
|
|
3589
3862
|
ownerId: owner.ownerId,
|
|
3590
3863
|
leaseGeneration: owner.leaseGeneration,
|
|
3591
|
-
nodeRid:
|
|
3864
|
+
nodeRid: status.routingId,
|
|
3592
3865
|
nodeGeneration: status.lifecycleGeneration,
|
|
3593
3866
|
expectedAuthorityStoreVersion: authority.storeVersion.value
|
|
3594
3867
|
};
|
|
3595
3868
|
}
|
|
3596
3869
|
|
|
3597
|
-
private async
|
|
3870
|
+
private async relayTerminalReply(
|
|
3598
3871
|
meshName: string,
|
|
3599
3872
|
stage: LocalStage,
|
|
3600
3873
|
coordinator: ServiceWireRelocationCoordinatorFence,
|
|
3874
|
+
participantId: bigint,
|
|
3875
|
+
packet: ZLinkActorHandoffPacket,
|
|
3876
|
+
result: ZLinkActorHandoffResult,
|
|
3601
3877
|
signal?: AbortSignal
|
|
3602
3878
|
): Promise<void> {
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
)
|
|
3625
|
-
|
|
3626
|
-
|
|
3879
|
+
if (!packet.returnResponse || packet.source === undefined) return;
|
|
3880
|
+
if (result.index !== packet.index) {
|
|
3881
|
+
throw new Error('Relocation request result changed after target dispatch.');
|
|
3882
|
+
}
|
|
3883
|
+
const completion: RelocationTerminalCompletion = {
|
|
3884
|
+
index: packet.index,
|
|
3885
|
+
operationId: packet.messageFollowContext.operationId,
|
|
3886
|
+
source: packet.source,
|
|
3887
|
+
result
|
|
3888
|
+
};
|
|
3889
|
+
const source = handoffSourceFence(packet.source);
|
|
3890
|
+
await this.sendReplyRelay(
|
|
3891
|
+
meshName,
|
|
3892
|
+
source.nodeRid,
|
|
3893
|
+
maintenanceReplyRelay(
|
|
3894
|
+
stage,
|
|
3895
|
+
coordinator,
|
|
3896
|
+
participantId,
|
|
3897
|
+
completion,
|
|
3898
|
+
packet,
|
|
3899
|
+
this.options.registration.messageSerializers,
|
|
3900
|
+
this.options.flowCreationEnabled?.() ?? false
|
|
3901
|
+
),
|
|
3902
|
+
source,
|
|
3903
|
+
signal
|
|
3904
|
+
);
|
|
3627
3905
|
}
|
|
3628
3906
|
|
|
3629
3907
|
private async exactSourceLeaseExpired(
|
|
@@ -3704,13 +3982,13 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3704
3982
|
const descriptors = await this.options.liveDescriptors(meshName, signal);
|
|
3705
3983
|
const local =
|
|
3706
3984
|
this.options.localDescriptor?.(meshName) ??
|
|
3707
|
-
descriptors.find((descriptor) =>
|
|
3985
|
+
descriptors.find((descriptor) => routingIdsEqual(descriptor.rid, localRid));
|
|
3708
3986
|
const requiredVersion = targetApplicationVersion ?? local?.applicationVersion;
|
|
3709
3987
|
const peers = this.requireMeshNode(meshName).peers();
|
|
3710
3988
|
const candidates = descriptors
|
|
3711
3989
|
.filter(
|
|
3712
3990
|
(descriptor) =>
|
|
3713
|
-
|
|
3991
|
+
!routingIdsEqual(descriptor.rid, localRid) &&
|
|
3714
3992
|
descriptor.state === ZLinkFrameworkRuntimeState.Serving &&
|
|
3715
3993
|
descriptor.objectRole === ZLinkObjectRole.Server &&
|
|
3716
3994
|
descriptor.placementWeight > 0 &&
|
|
@@ -3720,7 +3998,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3720
3998
|
peers.some(
|
|
3721
3999
|
(peer) =>
|
|
3722
4000
|
peer.routingId !== null &&
|
|
3723
|
-
|
|
4001
|
+
routingIdsEqual(peer.routingId, descriptor.rid) &&
|
|
3724
4002
|
peer.lifecycleGeneration === descriptor.lifecycleGeneration &&
|
|
3725
4003
|
peer.state === MeshPeerRuntimeState.Serving
|
|
3726
4004
|
) &&
|
|
@@ -4308,8 +4586,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
4308
4586
|
meshName: string,
|
|
4309
4587
|
unitKind: 'actor' | 'user_spot' | 'instance_spot',
|
|
4310
4588
|
executionMode: 'spot_wide' | 'per_actor' | undefined,
|
|
4311
|
-
startedAt: number
|
|
4312
|
-
outcome: string
|
|
4589
|
+
startedAt: number
|
|
4313
4590
|
): void {
|
|
4314
4591
|
const seconds = Math.max(0, performance.now() - startedAt) / MILLISECONDS_PER_SECOND;
|
|
4315
4592
|
const attributes = {
|
|
@@ -4317,12 +4594,6 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
4317
4594
|
...(executionMode === undefined ? {} : { execution_mode: executionMode })
|
|
4318
4595
|
};
|
|
4319
4596
|
this.options.metrics?.duration(METRIC_NAMES.RelocationInterruption, seconds, attributes);
|
|
4320
|
-
this.options.metrics?.count(METRIC_NAMES.RelocationCompleted, 1, {
|
|
4321
|
-
mesh_name: meshName,
|
|
4322
|
-
object_kind: unitKind,
|
|
4323
|
-
policy: unitKind === 'user_spot' && executionMode === 'per_actor' ? 'recreate' : 'configured',
|
|
4324
|
-
outcome
|
|
4325
|
-
});
|
|
4326
4597
|
if (seconds <= 1) return;
|
|
4327
4598
|
console.warn(
|
|
4328
4599
|
`[zlink.runtime.relocation.changed] ${unitKind} admission interruption ` +
|
|
@@ -4376,30 +4647,30 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
4376
4647
|
private async commitSpotMessageFollow(
|
|
4377
4648
|
meshName: string,
|
|
4378
4649
|
seal: ServiceSpotMessageFollowSeal,
|
|
4379
|
-
|
|
4380
|
-
activation: ZLinkSpotActivation
|
|
4381
|
-
target: ZLinkMeshNodeDescriptor
|
|
4650
|
+
committed: ZLinkAuthoritySnapshot | undefined,
|
|
4651
|
+
activation: ZLinkSpotActivation
|
|
4382
4652
|
): Promise<void> {
|
|
4383
4653
|
const node = this.requireMeshNode(meshName);
|
|
4384
4654
|
const commit = node.commitSpotMessageFollowIngress;
|
|
4385
4655
|
if (commit === undefined) {
|
|
4386
4656
|
throw new Error('Spot Message Follow backend support is incomplete.');
|
|
4387
4657
|
}
|
|
4388
|
-
const committed = await requireAuthority(this.requireLocationStore(), authorityKey);
|
|
4389
4658
|
const accepted = await commit.call(
|
|
4390
4659
|
node,
|
|
4391
4660
|
seal,
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4661
|
+
committed === undefined
|
|
4662
|
+
? null
|
|
4663
|
+
: {
|
|
4664
|
+
spot: {
|
|
4665
|
+
spotId: String(activation.spotId),
|
|
4666
|
+
generation: committed.objectGeneration
|
|
4667
|
+
},
|
|
4668
|
+
targetNodeRid: String(committed.allocation.descriptor.rid),
|
|
4669
|
+
targetNodeGeneration: committed.allocation.descriptorLifecycleGeneration,
|
|
4670
|
+
authorityOwnerGeneration: committed.authorityOwnerGeneration,
|
|
4671
|
+
ownerLeaseGeneration: committed.ownerLeaseGeneration,
|
|
4672
|
+
storeVersion: committed.storeVersion.value
|
|
4673
|
+
},
|
|
4403
4674
|
this.relocationLimits().messageFollowDurationMs
|
|
4404
4675
|
);
|
|
4405
4676
|
if (!accepted) {
|
|
@@ -4518,6 +4789,27 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
4518
4789
|
}
|
|
4519
4790
|
}
|
|
4520
4791
|
|
|
4792
|
+
function committedSpotRoute<TKind extends ZLinkSpotKind.User | ZLinkSpotKind.Instance>(
|
|
4793
|
+
meshName: string,
|
|
4794
|
+
spotId: RoutingId,
|
|
4795
|
+
spotKind: TKind,
|
|
4796
|
+
targetNodeRid: RoutingId,
|
|
4797
|
+
authority: ZLinkAuthoritySnapshot | undefined
|
|
4798
|
+
): ZLinkSpotRouteTarget & { readonly spotKind: TKind } {
|
|
4799
|
+
return {
|
|
4800
|
+
routerChannelId: meshName,
|
|
4801
|
+
targetNodeRid,
|
|
4802
|
+
spotId,
|
|
4803
|
+
spotKind,
|
|
4804
|
+
targetSpotGeneration: authority?.objectGeneration,
|
|
4805
|
+
targetNodeGeneration: authority?.allocation.descriptorLifecycleGeneration,
|
|
4806
|
+
authorityOwnerGeneration: authority?.authorityOwnerGeneration,
|
|
4807
|
+
targetOwnerId: authority?.ownerId,
|
|
4808
|
+
ownerLeaseGeneration: authority?.ownerLeaseGeneration,
|
|
4809
|
+
authorityStoreVersion: authority?.storeVersion.value
|
|
4810
|
+
};
|
|
4811
|
+
}
|
|
4812
|
+
|
|
4521
4813
|
function canonicalActorJoinRecovery(
|
|
4522
4814
|
participants: readonly ServiceRelocationParticipant[]
|
|
4523
4815
|
): CanonicalActorJoinRecovery | undefined {
|
|
@@ -4604,6 +4896,9 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4604
4896
|
): Promise<LocalHidden> {
|
|
4605
4897
|
const identity = decodeAuthorityKey({ value: participant.key } as ZLinkAuthorityKey).globalId;
|
|
4606
4898
|
if (participant.objectKind === 'actor') {
|
|
4899
|
+
if (participant.timers.length !== 0) {
|
|
4900
|
+
throw new Error('Actor relocation cannot restore a Spot timer.');
|
|
4901
|
+
}
|
|
4607
4902
|
const membership = this.envelope.memberships.find(
|
|
4608
4903
|
(value) => value.actorKey === participant.key
|
|
4609
4904
|
);
|
|
@@ -4630,8 +4925,6 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4630
4925
|
participant,
|
|
4631
4926
|
actor,
|
|
4632
4927
|
initialized: false,
|
|
4633
|
-
restoredTimers: [],
|
|
4634
|
-
replayResults: [],
|
|
4635
4928
|
replayPackets: []
|
|
4636
4929
|
};
|
|
4637
4930
|
}
|
|
@@ -4644,6 +4937,19 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4644
4937
|
identity as RoutingId,
|
|
4645
4938
|
participant.objectGeneration,
|
|
4646
4939
|
participant.authorityOwnerGeneration,
|
|
4940
|
+
participant.timers.map((value) => ({
|
|
4941
|
+
name: value.timerId,
|
|
4942
|
+
handlerType: value.handlerType,
|
|
4943
|
+
periodMs: value.intervalMs,
|
|
4944
|
+
overrunPolicy: value.overrunPolicy as never,
|
|
4945
|
+
maxCatchUpTicks: value.maxCatchUpTicks,
|
|
4946
|
+
stopOnUnhandledException: value.stopOnUnhandledException,
|
|
4947
|
+
startedAtUnixMs: value.startedAtUnixMs,
|
|
4948
|
+
deliveryIndex: value.deliveryIndex,
|
|
4949
|
+
lastScheduledIndex: value.lastScheduledIndex,
|
|
4950
|
+
nextDueAtUnixMs: value.dueAtUnixMs,
|
|
4951
|
+
pendingTicks: value.pendingTicks
|
|
4952
|
+
})),
|
|
4647
4953
|
signal
|
|
4648
4954
|
);
|
|
4649
4955
|
return {
|
|
@@ -4651,8 +4957,6 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4651
4957
|
participant,
|
|
4652
4958
|
activation,
|
|
4653
4959
|
initialized: false,
|
|
4654
|
-
restoredTimers: [],
|
|
4655
|
-
replayResults: [],
|
|
4656
4960
|
replayPackets: []
|
|
4657
4961
|
};
|
|
4658
4962
|
}
|
|
@@ -4843,66 +5147,31 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4843
5147
|
}
|
|
4844
5148
|
|
|
4845
5149
|
async openAdmission(hidden: LocalHidden): Promise<void> {
|
|
4846
|
-
if (hidden.actor
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
if (state.spotId === undefined) {
|
|
4855
|
-
const runtime = this.options.spotNodeRuntime();
|
|
4856
|
-
if (runtime === undefined) {
|
|
4857
|
-
throw new Error('Relocated Entry Spot Actor dispatch is unavailable.');
|
|
4858
|
-
}
|
|
4859
|
-
return runtime.dispatchEntryActorPacket(
|
|
4860
|
-
'remote',
|
|
4861
|
-
hidden.actor!.context.actorId,
|
|
4862
|
-
parts,
|
|
4863
|
-
returnResponse,
|
|
4864
|
-
remoteBoundSessionTarget,
|
|
4865
|
-
fallbackActorRef
|
|
4866
|
-
);
|
|
4867
|
-
}
|
|
4868
|
-
return this.requireSpotManager().dispatchRoutedActorPacket(
|
|
4869
|
-
'remote',
|
|
4870
|
-
state.spotId,
|
|
4871
|
-
hidden.actor!.context.actorId,
|
|
4872
|
-
parts,
|
|
4873
|
-
returnResponse,
|
|
4874
|
-
remoteBoundSessionTarget,
|
|
4875
|
-
fallbackActorRef
|
|
4876
|
-
);
|
|
4877
|
-
}
|
|
4878
|
-
);
|
|
4879
|
-
hidden.replayResults.push(...results);
|
|
5150
|
+
if (hidden.actor !== undefined) {
|
|
5151
|
+
this.requireActorManager().publishRelocationActor(hidden.actor.context.actorId);
|
|
5152
|
+
this.requireSpotManager().openActorJoinRelocation(
|
|
5153
|
+
this.meshName,
|
|
5154
|
+
actorJoinAdmissionIdentity(this.envelope),
|
|
5155
|
+
hidden.actor
|
|
5156
|
+
);
|
|
5157
|
+
}
|
|
4880
5158
|
}
|
|
4881
5159
|
|
|
4882
|
-
async
|
|
4883
|
-
if (hidden.
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
hidden.
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
maxCatchUpTicks: value.maxCatchUpTicks,
|
|
4894
|
-
stopOnUnhandledException: value.stopOnUnhandledException,
|
|
4895
|
-
startedAtUnixMs: value.startedAtUnixMs,
|
|
4896
|
-
deliveryIndex: value.deliveryIndex,
|
|
4897
|
-
lastScheduledIndex: value.lastScheduledIndex,
|
|
4898
|
-
nextDueAtUnixMs: value.dueAtUnixMs,
|
|
4899
|
-
pendingTicks: value.pendingTicks
|
|
4900
|
-
}))
|
|
5160
|
+
async normalize(hidden: LocalHidden): Promise<void> {
|
|
5161
|
+
if (hidden.actor === undefined || hidden.replayPackets.length === 0) return;
|
|
5162
|
+
if (hidden.replayStart === undefined || hidden.onReplayResult === undefined) {
|
|
5163
|
+
throw new Error('Relocated Actor backlog has no target activation owner.');
|
|
5164
|
+
}
|
|
5165
|
+
this.options.actorTransfer.admitRelocationPrefix(
|
|
5166
|
+
hidden.actor,
|
|
5167
|
+
hidden.participant.objectGeneration,
|
|
5168
|
+
hidden.replayPackets,
|
|
5169
|
+
hidden.replayStart,
|
|
5170
|
+
hidden.onReplayResult
|
|
4901
5171
|
);
|
|
5172
|
+
hidden.replayPackets.length = 0;
|
|
4902
5173
|
}
|
|
4903
5174
|
|
|
4904
|
-
async normalize(_hidden: LocalHidden): Promise<void> {}
|
|
4905
|
-
|
|
4906
5175
|
async abort(hidden: LocalHidden): Promise<void> {
|
|
4907
5176
|
if (hidden.actor !== undefined) {
|
|
4908
5177
|
await this.requireActorManager().abortRelocationActor(hidden.actor.context.actorId);
|
|
@@ -5132,6 +5401,22 @@ export function decodeQueuedHandoffPacket(
|
|
|
5132
5401
|
...(messageFollowOrigin === undefined ? {} : { messageFollowOrigin })
|
|
5133
5402
|
}
|
|
5134
5403
|
])[0]!;
|
|
5404
|
+
const requestSource =
|
|
5405
|
+
decoded.source === undefined ? undefined : handoffSourceFence(decoded.source);
|
|
5406
|
+
if (
|
|
5407
|
+
messageFollowOrigin !== undefined &&
|
|
5408
|
+
requestSource !== undefined &&
|
|
5409
|
+
routingIdsEqual(messageFollowOrigin.sourceNodeRid, requestSource.nodeRid)
|
|
5410
|
+
) {
|
|
5411
|
+
return {
|
|
5412
|
+
...decoded,
|
|
5413
|
+
index: index as number,
|
|
5414
|
+
messageFollowOrigin: {
|
|
5415
|
+
...messageFollowOrigin,
|
|
5416
|
+
requestSource
|
|
5417
|
+
}
|
|
5418
|
+
};
|
|
5419
|
+
}
|
|
5135
5420
|
return { ...decoded, index: index as number };
|
|
5136
5421
|
}
|
|
5137
5422
|
|
|
@@ -5306,16 +5591,6 @@ export function createServiceRelocationId(
|
|
|
5306
5591
|
}
|
|
5307
5592
|
}
|
|
5308
5593
|
|
|
5309
|
-
function operationWireId(value: string): { readonly high: bigint; readonly low: bigint } {
|
|
5310
|
-
const match = /^(\d+):(\d+)$/.exec(value);
|
|
5311
|
-
if (match === null) throw new Error(`Actor handoff operation '${value}' is not canonical.`);
|
|
5312
|
-
const result = { high: BigInt(match[1]!), low: BigInt(match[2]!) };
|
|
5313
|
-
if (result.high === 0n && result.low === 0n) {
|
|
5314
|
-
throw new Error('Actor handoff operation identity must not be zero.');
|
|
5315
|
-
}
|
|
5316
|
-
return result;
|
|
5317
|
-
}
|
|
5318
|
-
|
|
5319
5594
|
function sameWireId(
|
|
5320
5595
|
left: { readonly high: bigint; readonly low: bigint },
|
|
5321
5596
|
right: { readonly high: bigint; readonly low: bigint }
|
|
@@ -5330,7 +5605,7 @@ function sameCoordinator(
|
|
|
5330
5605
|
return (
|
|
5331
5606
|
left.ownerId === right.ownerId &&
|
|
5332
5607
|
BigInt(left.leaseGeneration) === right.leaseGeneration &&
|
|
5333
|
-
left.nodeRid
|
|
5608
|
+
routingIdsEqual(left.nodeRid, right.nodeRid) &&
|
|
5334
5609
|
BigInt(left.nodeGeneration) === right.nodeGeneration &&
|
|
5335
5610
|
left.expectedAuthorityStoreVersion === right.expectedAuthorityStoreVersion
|
|
5336
5611
|
);
|
|
@@ -5341,7 +5616,7 @@ function sameRelocationTarget(
|
|
|
5341
5616
|
right: ServiceWireRelocationTarget
|
|
5342
5617
|
): boolean {
|
|
5343
5618
|
return (
|
|
5344
|
-
left.nodeRid
|
|
5619
|
+
routingIdsEqual(left.nodeRid, right.nodeRid) &&
|
|
5345
5620
|
BigInt(left.nodeGeneration) === right.nodeGeneration &&
|
|
5346
5621
|
left.ownerId === right.ownerId &&
|
|
5347
5622
|
BigInt(left.ownerLeaseGeneration) === right.ownerLeaseGeneration
|
|
@@ -5394,7 +5669,7 @@ function isSourceOwner(current: ZLinkAuthoritySnapshot, expected: ZLinkAuthority
|
|
|
5394
5669
|
current.allocation.state === expected.allocation.state &&
|
|
5395
5670
|
current.allocation.objectKind === expected.allocation.objectKind &&
|
|
5396
5671
|
current.allocation.stableType === expected.allocation.stableType &&
|
|
5397
|
-
|
|
5672
|
+
routingIdsEqual(current.allocation.descriptor.rid, expected.allocation.descriptor.rid) &&
|
|
5398
5673
|
current.allocation.descriptorLifecycleGeneration ===
|
|
5399
5674
|
expected.allocation.descriptorLifecycleGeneration
|
|
5400
5675
|
);
|
|
@@ -5411,7 +5686,7 @@ function isExactCommittedTarget(
|
|
|
5411
5686
|
current.authorityOwnerGeneration > expected.authorityOwnerGeneration &&
|
|
5412
5687
|
current.ownerId === target.ownerId &&
|
|
5413
5688
|
current.ownerLeaseGeneration === target.ownerLeaseGeneration &&
|
|
5414
|
-
|
|
5689
|
+
routingIdsEqual(current.allocation.descriptor.rid, target.nodeRid) &&
|
|
5415
5690
|
current.allocation.descriptorLifecycleGeneration === target.nodeGeneration &&
|
|
5416
5691
|
Buffer.from(current.payload).equals(Buffer.from(payload))
|
|
5417
5692
|
);
|
|
@@ -5440,7 +5715,7 @@ function sameRequestSource(
|
|
|
5440
5715
|
return (
|
|
5441
5716
|
left.ownerId === right.ownerId &&
|
|
5442
5717
|
left.leaseGeneration === right.leaseGeneration &&
|
|
5443
|
-
left.nodeRid
|
|
5718
|
+
routingIdsEqual(left.nodeRid, right.nodeRid) &&
|
|
5444
5719
|
left.nodeGeneration === right.nodeGeneration
|
|
5445
5720
|
);
|
|
5446
5721
|
}
|
|
@@ -5451,7 +5726,7 @@ function handoffSourceFence(
|
|
|
5451
5726
|
return {
|
|
5452
5727
|
ownerId: value.ownerId,
|
|
5453
5728
|
leaseGeneration: BigInt(value.ownerLeaseGeneration),
|
|
5454
|
-
nodeRid: value.nodeRid,
|
|
5729
|
+
nodeRid: decodeRoutingId(value.nodeRid, value.nodeRidHex),
|
|
5455
5730
|
nodeGeneration: BigInt(value.nodeGeneration)
|
|
5456
5731
|
};
|
|
5457
5732
|
}
|
|
@@ -5460,14 +5735,34 @@ function maintenanceReplyRelay(
|
|
|
5460
5735
|
stage: LocalStage,
|
|
5461
5736
|
coordinator: ServiceWireRelocationCoordinatorFence,
|
|
5462
5737
|
participantId: bigint,
|
|
5463
|
-
completion: RelocationTerminalCompletion
|
|
5738
|
+
completion: RelocationTerminalCompletion,
|
|
5739
|
+
packet: ZLinkActorHandoffPacket,
|
|
5740
|
+
serializers: Parameters<typeof encodeActorReply>[3],
|
|
5741
|
+
flowEnabled: boolean
|
|
5464
5742
|
): ServiceMaintenanceReplyRelay {
|
|
5465
5743
|
const value = completion.result.ok ? completion.result.response : undefined;
|
|
5744
|
+
const direct = packet.messageFollowOrigin?.requestSource !== undefined;
|
|
5745
|
+
const application =
|
|
5746
|
+
direct && completion.result.ok
|
|
5747
|
+
? decodeApplicationPayloadView(
|
|
5748
|
+
encodeMultipartApplicationPayload(
|
|
5749
|
+
encodeActorReply(
|
|
5750
|
+
Buffer.from(packet.header, 'base64'),
|
|
5751
|
+
ZLinkStreamMessageKind.Response,
|
|
5752
|
+
value,
|
|
5753
|
+
serializers,
|
|
5754
|
+
flowEnabled
|
|
5755
|
+
),
|
|
5756
|
+
SERVICE_FRAMEWORK_MULTIPART_PACKET_NAME,
|
|
5757
|
+
SERVICE_FRAMEWORK_MULTIPART_CONTENT_TYPE
|
|
5758
|
+
)
|
|
5759
|
+
)
|
|
5760
|
+
: undefined;
|
|
5466
5761
|
return {
|
|
5467
5762
|
relocation: relocationWireId(stage.staging.envelope.aggregateId),
|
|
5468
5763
|
targetAttemptGeneration: stage.offer.prepare.targetAttemptGeneration,
|
|
5469
5764
|
coordinator,
|
|
5470
|
-
operation:
|
|
5765
|
+
operation: parseHandoffOperationId(completion.operationId),
|
|
5471
5766
|
replyRouteId: BigInt(completion.source.replyRouteId),
|
|
5472
5767
|
participantId,
|
|
5473
5768
|
sequence: BigInt(completion.index + 1),
|
|
@@ -5475,15 +5770,23 @@ function maintenanceReplyRelay(
|
|
|
5475
5770
|
failureCode: completion.result.ok
|
|
5476
5771
|
? ServiceWireFrameworkErrorCode.none
|
|
5477
5772
|
: ServiceWireFrameworkErrorCode.requestFailed,
|
|
5478
|
-
...(
|
|
5479
|
-
? {
|
|
5480
|
-
: {
|
|
5773
|
+
...(application !== undefined
|
|
5774
|
+
? {
|
|
5481
5775
|
payload: {
|
|
5482
|
-
packetName:
|
|
5483
|
-
contentType:
|
|
5484
|
-
bytes:
|
|
5776
|
+
packetName: application.packetName,
|
|
5777
|
+
contentType: application.contentType,
|
|
5778
|
+
bytes: application.payload
|
|
5485
5779
|
}
|
|
5486
|
-
}
|
|
5780
|
+
}
|
|
5781
|
+
: value === undefined
|
|
5782
|
+
? {}
|
|
5783
|
+
: {
|
|
5784
|
+
payload: {
|
|
5785
|
+
packetName: SERVICE_RELOCATION_REPLY_PACKET_NAME,
|
|
5786
|
+
contentType: ZlinkStreamContentType.Json,
|
|
5787
|
+
bytes: Buffer.from(JSON.stringify(value), 'utf8')
|
|
5788
|
+
}
|
|
5789
|
+
})
|
|
5487
5790
|
};
|
|
5488
5791
|
}
|
|
5489
5792
|
|
|
@@ -5519,6 +5822,14 @@ function wireIdText(value: { readonly high: bigint; readonly low: bigint }): str
|
|
|
5519
5822
|
*/
|
|
5520
5823
|
export class ServiceRelocationDataLostError extends Error {}
|
|
5521
5824
|
|
|
5825
|
+
/** The local source lifecycle ended a pending relocation operation. */
|
|
5826
|
+
class ServiceRelocationRuntimeStoppedError extends Error {
|
|
5827
|
+
constructor() {
|
|
5828
|
+
super('Relocation runtime stopped.');
|
|
5829
|
+
this.name = 'AbortError';
|
|
5830
|
+
}
|
|
5831
|
+
}
|
|
5832
|
+
|
|
5522
5833
|
/** Classifies relocation failure with the error model §2.1 mapping. */
|
|
5523
5834
|
export function relocationFailedFailureCode(error: unknown): number {
|
|
5524
5835
|
if (error instanceof ServiceRelocationDataLostError) {
|
|
@@ -5653,7 +5964,7 @@ function validateTargetOneWayControl(
|
|
|
5653
5964
|
): void {
|
|
5654
5965
|
const prepare = stage.offer.prepare;
|
|
5655
5966
|
if (
|
|
5656
|
-
|
|
5967
|
+
!routingIdsEqual(sourceNodeRid, stage.offer.authenticatedSourceNodeRid) ||
|
|
5657
5968
|
!sameWireId(request.relocation, prepare.relocation) ||
|
|
5658
5969
|
request.targetAttemptGeneration !== prepare.targetAttemptGeneration ||
|
|
5659
5970
|
!sameCoordinator(request.coordinator, prepare.coordinator) ||
|
|
@@ -5731,12 +6042,7 @@ function canonicalQueuedFrozenRecord(
|
|
|
5731
6042
|
const source =
|
|
5732
6043
|
packet.source === undefined
|
|
5733
6044
|
? coordinatorSource(coordinator)
|
|
5734
|
-
:
|
|
5735
|
-
ownerId: packet.source.ownerId,
|
|
5736
|
-
leaseGeneration: BigInt(packet.source.ownerLeaseGeneration),
|
|
5737
|
-
nodeRid: packet.source.nodeRid,
|
|
5738
|
-
nodeGeneration: BigInt(packet.source.nodeGeneration)
|
|
5739
|
-
};
|
|
6045
|
+
: handoffSourceFence(packet.source);
|
|
5740
6046
|
const operationId = parseHandoffOperationId(packet.messageFollowContext.operationId);
|
|
5741
6047
|
if (packet.returnResponse && packet.source === undefined) {
|
|
5742
6048
|
throw new Error('Captured Actor request has no exact request-source fence.');
|
|
@@ -5874,17 +6180,13 @@ function sessionRelocationRouteSubmitKey(
|
|
|
5874
6180
|
request: ServiceSessionRelocationRoute,
|
|
5875
6181
|
targetNodeRid: RoutingId
|
|
5876
6182
|
): string {
|
|
5877
|
-
return `${sessionRelocationIdentityKey(request)}:${
|
|
6183
|
+
return `${sessionRelocationIdentityKey(request)}:${encodeRoutingIdStorageHex(targetNodeRid)}`;
|
|
5878
6184
|
}
|
|
5879
6185
|
|
|
5880
6186
|
function sessionRelocationResponseKey(response: ServiceSessionRelocationSealed): string {
|
|
5881
6187
|
return sessionRelocationIdentityKey(response);
|
|
5882
6188
|
}
|
|
5883
6189
|
|
|
5884
|
-
function sessionRelocationResponseFingerprint(response: ServiceSessionRelocationSealed): string {
|
|
5885
|
-
return encodeSessionRelocationSealed(response).toString('base64');
|
|
5886
|
-
}
|
|
5887
|
-
|
|
5888
6190
|
function sessionRelocationIdentityKey(
|
|
5889
6191
|
value:
|
|
5890
6192
|
ServiceSessionRelocationSeal | ServiceSessionRelocationSealed | ServiceSessionRelocationRoute
|
|
@@ -5899,16 +6201,16 @@ function validateSessionRelocationResponse(
|
|
|
5899
6201
|
const sameCoordinator =
|
|
5900
6202
|
request.coordinator.ownerId === response.coordinator.ownerId &&
|
|
5901
6203
|
request.coordinator.leaseGeneration === response.coordinator.leaseGeneration &&
|
|
5902
|
-
request.coordinator.nodeRid
|
|
6204
|
+
routingIdsEqual(request.coordinator.nodeRid, response.coordinator.nodeRid) &&
|
|
5903
6205
|
request.coordinator.nodeGeneration === response.coordinator.nodeGeneration &&
|
|
5904
6206
|
request.coordinator.expectedAuthorityStoreVersion ===
|
|
5905
6207
|
response.coordinator.expectedAuthorityStoreVersion;
|
|
5906
6208
|
const sameSession =
|
|
5907
|
-
request.session.sessionOwnerNodeRid
|
|
6209
|
+
routingIdsEqual(request.session.sessionOwnerNodeRid, response.session.sessionOwnerNodeRid) &&
|
|
5908
6210
|
request.session.sessionOwnerNodeGeneration === response.session.sessionOwnerNodeGeneration &&
|
|
5909
6211
|
request.session.sessionOwnerId === response.session.sessionOwnerId &&
|
|
5910
6212
|
request.session.sessionOwnerLeaseGeneration === response.session.sessionOwnerLeaseGeneration &&
|
|
5911
|
-
request.session.sessionRid
|
|
6213
|
+
routingIdsEqual(request.session.sessionRid, response.session.sessionRid) &&
|
|
5912
6214
|
request.session.bindingGeneration === response.session.bindingGeneration;
|
|
5913
6215
|
if (
|
|
5914
6216
|
request.relocation.high !== response.relocation.high ||
|
|
@@ -5921,7 +6223,7 @@ function validateSessionRelocationResponse(
|
|
|
5921
6223
|
if (
|
|
5922
6224
|
request.actor.actor.actorId !== response.actor.actor.actorId ||
|
|
5923
6225
|
request.actor.actor.generation !== response.actor.actor.generation ||
|
|
5924
|
-
request.actor.actor.nodeRid
|
|
6226
|
+
!routingIdsEqual(request.actor.actor.nodeRid, response.actor.actor.nodeRid) ||
|
|
5925
6227
|
request.actor.targetNodeGeneration !== response.actor.targetNodeGeneration ||
|
|
5926
6228
|
request.actor.authorityOwnerGeneration !== response.actor.authorityOwnerGeneration ||
|
|
5927
6229
|
request.actor.ownerLeaseGeneration !== response.actor.ownerLeaseGeneration
|
|
@@ -5944,17 +6246,20 @@ function replyRelayIdentityKey(value: {
|
|
|
5944
6246
|
value.relocation.low.toString(),
|
|
5945
6247
|
value.coordinator.ownerId,
|
|
5946
6248
|
value.coordinator.leaseGeneration.toString(),
|
|
5947
|
-
value.coordinator.nodeRid,
|
|
6249
|
+
encodeRoutingIdStorageHex(value.coordinator.nodeRid),
|
|
5948
6250
|
value.coordinator.nodeGeneration.toString(),
|
|
5949
6251
|
value.coordinator.expectedAuthorityStoreVersion
|
|
5950
6252
|
]);
|
|
5951
6253
|
}
|
|
5952
6254
|
|
|
5953
6255
|
function replyRelayPendingKey(
|
|
5954
|
-
ackTargetNodeRid:
|
|
6256
|
+
ackTargetNodeRid: RoutingId,
|
|
5955
6257
|
value: Parameters<typeof replyRelayIdentityKey>[0]
|
|
5956
6258
|
): string {
|
|
5957
|
-
return JSON.stringify([
|
|
6259
|
+
return JSON.stringify([
|
|
6260
|
+
encodeRoutingIdStorageHex(ackTargetNodeRid),
|
|
6261
|
+
replyRelayIdentityKey(value)
|
|
6262
|
+
]);
|
|
5958
6263
|
}
|
|
5959
6264
|
|
|
5960
6265
|
export function encodeHandoffQueuedMessages(
|
|
@@ -6104,6 +6409,7 @@ function encodeActorSession(target: ZLinkRemoteBoundSessionTarget | undefined):
|
|
|
6104
6409
|
version: ACTOR_SESSION_JOURNAL_VERSION,
|
|
6105
6410
|
routerChannelId: target.routerChannelId,
|
|
6106
6411
|
targetNodeRid: String(target.targetNodeRid),
|
|
6412
|
+
targetNodeRidHex: encodeRoutingIdStorageHex(target.targetNodeRid),
|
|
6107
6413
|
spotId: String(target.spotId),
|
|
6108
6414
|
sessionNodeRid:
|
|
6109
6415
|
target.sessionNodeRid === undefined ? undefined : String(target.sessionNodeRid),
|
|
@@ -6130,12 +6436,15 @@ function encodeActorSession(target: ZLinkRemoteBoundSessionTarget | undefined):
|
|
|
6130
6436
|
coordinatorOwnerId: target.serviceWireRelocation.coordinator.ownerId,
|
|
6131
6437
|
coordinatorLeaseGeneration:
|
|
6132
6438
|
target.serviceWireRelocation.coordinator.leaseGeneration.toString(),
|
|
6133
|
-
coordinatorNodeRid: target.serviceWireRelocation.coordinator.nodeRid,
|
|
6439
|
+
coordinatorNodeRid: String(target.serviceWireRelocation.coordinator.nodeRid),
|
|
6440
|
+
coordinatorNodeRidHex: encodeRoutingIdStorageHex(
|
|
6441
|
+
target.serviceWireRelocation.coordinator.nodeRid
|
|
6442
|
+
),
|
|
6134
6443
|
coordinatorNodeGeneration:
|
|
6135
6444
|
target.serviceWireRelocation.coordinator.nodeGeneration.toString(),
|
|
6136
6445
|
expectedAuthorityStoreVersion:
|
|
6137
6446
|
target.serviceWireRelocation.coordinator.expectedAuthorityStoreVersion,
|
|
6138
|
-
sessionOwnerNodeRid: target.serviceWireRelocation.session.sessionOwnerNodeRid,
|
|
6447
|
+
sessionOwnerNodeRid: String(target.serviceWireRelocation.session.sessionOwnerNodeRid),
|
|
6139
6448
|
sessionOwnerNodeRidHex: encodeRoutingIdStorageHex(
|
|
6140
6449
|
target.serviceWireRelocation.session.sessionOwnerNodeRid as RoutingId
|
|
6141
6450
|
),
|
|
@@ -6144,7 +6453,7 @@ function encodeActorSession(target: ZLinkRemoteBoundSessionTarget | undefined):
|
|
|
6144
6453
|
sessionOwnerId: target.serviceWireRelocation.session.sessionOwnerId,
|
|
6145
6454
|
sessionOwnerLeaseGeneration:
|
|
6146
6455
|
target.serviceWireRelocation.session.sessionOwnerLeaseGeneration.toString(),
|
|
6147
|
-
sessionRid: target.serviceWireRelocation.session.sessionRid,
|
|
6456
|
+
sessionRid: String(target.serviceWireRelocation.session.sessionRid),
|
|
6148
6457
|
sessionRidHex: encodeRoutingIdStorageHex(
|
|
6149
6458
|
target.serviceWireRelocation.session.sessionRid as RoutingId
|
|
6150
6459
|
),
|
|
@@ -6171,7 +6480,7 @@ function decodeActorSession(payload: Uint8Array): ZLinkRemoteBoundSessionTarget
|
|
|
6171
6480
|
const serviceWire = decodeActorSessionServiceWireFence(value.serviceWireRelocation);
|
|
6172
6481
|
return {
|
|
6173
6482
|
routerChannelId: value.routerChannelId,
|
|
6174
|
-
targetNodeRid: value.targetNodeRid
|
|
6483
|
+
targetNodeRid: decodeRoutingId(value.targetNodeRid, value.targetNodeRidHex),
|
|
6175
6484
|
spotId: value.spotId as RoutingId,
|
|
6176
6485
|
...(typeof value.sessionNodeRid === 'string'
|
|
6177
6486
|
? { sessionNodeRid: decodeRoutingId(value.sessionNodeRid, value.sessionNodeRidHex) }
|
|
@@ -6242,7 +6551,7 @@ function decodeActorSessionServiceWireFence(
|
|
|
6242
6551
|
coordinator: {
|
|
6243
6552
|
ownerId: text('coordinatorOwnerId'),
|
|
6244
6553
|
leaseGeneration: positive('coordinatorLeaseGeneration'),
|
|
6245
|
-
nodeRid: text('coordinatorNodeRid'),
|
|
6554
|
+
nodeRid: decodeRoutingId(text('coordinatorNodeRid'), value.coordinatorNodeRidHex),
|
|
6246
6555
|
nodeGeneration: positive('coordinatorNodeGeneration'),
|
|
6247
6556
|
expectedAuthorityStoreVersion: text('expectedAuthorityStoreVersion')
|
|
6248
6557
|
},
|