@zlink-systems/framework 0.25.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/contracts/Channels/IZLinkFanoutClient.d.ts +0 -6
- package/dist/contracts/Common/CoreTypes.d.ts +6 -0
- package/dist/contracts/Common/CoreTypes.js +7 -0
- package/dist/contracts/Common/index.d.ts +1 -1
- package/dist/contracts/Common/index.js +0 -1
- package/dist/contracts/Configuration/InternalDefaults.d.ts +8 -0
- package/dist/contracts/Configuration/InternalDefaults.js +9 -1
- package/dist/contracts/Configuration/Registration.d.ts +13 -2
- package/dist/contracts/Configuration/Registration.js +58 -36
- package/dist/contracts/Configuration/RegistrationBuilderPolicy.d.ts +13 -1
- package/dist/contracts/Configuration/RegistrationBuilderPolicy.js +38 -1
- package/dist/contracts/Configuration/RegistrationBuilders.d.ts +2 -3
- package/dist/contracts/Configuration/RegistrationBuilders.js +27 -32
- package/dist/contracts/Configuration/RegistrationTypes.d.ts +1 -0
- package/dist/contracts/Configuration/RegistrationValidators.js +15 -12
- package/dist/contracts/Configuration/TimerRegistrationValidator.js +3 -2
- package/dist/contracts/Dispatch/ZLinkDispatchOptions.d.ts +1 -1
- package/dist/contracts/Handlers/JsonContract.js +10 -4
- package/dist/contracts/Locations/Authority.d.ts +1 -1
- package/dist/contracts/Locations/Authority.js +3 -2
- package/dist/contracts/Locations/Options.js +1 -1
- package/dist/contracts/Locations/Stores.d.ts +12 -0
- package/dist/contracts/Locations/Stores.js +9 -0
- package/dist/contracts/RouteMesh/RuntimeTopology.d.ts +8 -0
- package/dist/contracts/Spots/Contracts.d.ts +1 -1
- package/dist/internal.d.ts +5 -0
- package/dist/internal.js +16 -1
- package/dist/nest-integration.d.ts +4 -2
- package/dist/nest-integration.js +14 -4
- package/dist/runtime/abort.d.ts +1 -0
- package/dist/runtime/abort.js +8 -5
- package/dist/runtime/activation-admission.d.ts +28 -0
- package/dist/runtime/activation-admission.js +57 -0
- package/dist/runtime/actors/actor-authority-payload-codec.d.ts +1 -2
- package/dist/runtime/actors/actor-authority-payload-codec.js +34 -18
- package/dist/runtime/actors/actor-client.d.ts +2 -2
- package/dist/runtime/actors/actor-client.js +42 -143
- package/dist/runtime/actors/actor-context.d.ts +3 -5
- package/dist/runtime/actors/actor-context.js +19 -17
- package/dist/runtime/actors/actor-handoff.d.ts +13 -7
- package/dist/runtime/actors/actor-handoff.js +36 -25
- package/dist/runtime/actors/actor-lifecycle-snapshot.js +2 -1
- package/dist/runtime/actors/actor-local-native-join.d.ts +11 -6
- package/dist/runtime/actors/actor-local-native-join.js +85 -123
- package/dist/runtime/actors/actor-native-lookup.js +2 -8
- package/dist/runtime/actors/actor-packet-relay-wire.d.ts +3 -3
- package/dist/runtime/actors/actor-packet-relay-wire.js +5 -3
- package/dist/runtime/actors/actor-remote-joiner.d.ts +3 -3
- package/dist/runtime/actors/actor-remote-joiner.js +11 -17
- package/dist/runtime/actors/actor-remote-wire.js +5 -3
- package/dist/runtime/actors/actor-request-deadline.d.ts +3 -0
- package/dist/runtime/actors/actor-request-deadline.js +31 -0
- package/dist/runtime/actors/actor-retry-delay.js +2 -1
- package/dist/runtime/actors/actor-runtime-contracts.d.ts +4 -2
- package/dist/runtime/actors/actor-runtime-state.d.ts +9 -21
- package/dist/runtime/actors/actor-runtime-state.js +70 -187
- package/dist/runtime/actors/actor-source-transfer.d.ts +5 -5
- package/dist/runtime/actors/bound-session-wire.js +2 -7
- package/dist/runtime/actors/deferred-join-completion.d.ts +19 -0
- package/dist/runtime/actors/deferred-join-completion.js +21 -0
- package/dist/runtime/actors/index.d.ts +10 -8
- package/dist/runtime/actors/index.js +101 -73
- package/dist/runtime/actors/spot-actor-dispatch.d.ts +3 -3
- package/dist/runtime/actors/spot-actor-dispatch.js +25 -10
- package/dist/runtime/actors/transferred-actor-rollback.d.ts +3 -5
- package/dist/runtime/actors/transferred-actor-rollback.js +9 -42
- package/dist/runtime/admission.d.ts +7 -1
- package/dist/runtime/admission.js +11 -1
- package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +1 -1
- package/dist/runtime/application-jobs/application-job-queue-scope.js +1 -1
- package/dist/runtime/backend/contracts/index.d.ts +8 -33
- package/dist/runtime/backend/mesh-actor-session-node-adapter.js +3 -26
- package/dist/runtime/backend/mesh-dispatch-pump.d.ts +1 -2
- package/dist/runtime/backend/mesh-dispatch-pump.js +95 -107
- package/dist/runtime/backend/node/node-backend-adapter-factory.d.ts +1 -2
- package/dist/runtime/backend/node/node-backend-adapter-factory.js +4 -18
- package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +7 -4
- package/dist/runtime/backend/node/node-backend-adapter-support.js +28 -34
- package/dist/runtime/backend/node/node-event-loop-poller.d.ts +2 -2
- package/dist/runtime/backend/node/node-event-loop-poller.js +11 -6
- package/dist/runtime/backend/node/node-monitor-backend-adapter.js +1 -0
- package/dist/runtime/backend/node/node-raw-binding-port.d.ts +1 -1
- package/dist/runtime/backend/node/node-raw-binding-port.js +51 -19
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +21 -19
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +171 -111
- package/dist/runtime/backend/node/node-socket-backend-adapter.js +4 -3
- package/dist/runtime/backend/raw-binding-port.d.ts +13 -1
- package/dist/runtime/backend/runtime-values.d.ts +4 -1
- package/dist/runtime/backend/runtime-values.js +2 -0
- package/dist/runtime/channels/channel-clients.d.ts +20 -9
- package/dist/runtime/channels/channel-clients.js +68 -60
- package/dist/runtime/channels/channel-envelope-inspection.js +2 -1
- package/dist/runtime/channels/channel-envelope.js +47 -29
- package/dist/runtime/channels/channel-outbound-operations.d.ts +3 -0
- package/dist/runtime/channels/channel-outbound-operations.js +9 -18
- package/dist/runtime/channels/channel-receive-loops.d.ts +8 -3
- package/dist/runtime/channels/channel-receive-loops.js +27 -6
- package/dist/runtime/channels/channel-runtime-lifecycle.d.ts +12 -1
- package/dist/runtime/channels/channel-runtime-lifecycle.js +48 -28
- package/dist/runtime/channels/channel-runtime-manager.d.ts +2 -5
- package/dist/runtime/channels/channel-runtime-manager.js +4 -12
- package/dist/runtime/channels/channel-socket-options.js +3 -2
- package/dist/runtime/channels/channel-socket-registry.d.ts +30 -32
- package/dist/runtime/channels/channel-socket-registry.js +296 -235
- package/dist/runtime/channels/channel-transports.d.ts +11 -20
- package/dist/runtime/channels/channel-transports.js +68 -47
- package/dist/runtime/channels/client-server-location-runtime.d.ts +1 -1
- package/dist/runtime/channels/client-server-location-runtime.js +10 -11
- package/dist/runtime/channels/client-server-service-wire.d.ts +7 -2
- package/dist/runtime/channels/client-server-service-wire.js +32 -11
- package/dist/runtime/channels/dispatch-error-reporter.d.ts +1 -2
- package/dist/runtime/channels/dispatch-error-reporter.js +2 -1
- package/dist/runtime/channels/fanout-location-runtime.d.ts +16 -3
- package/dist/runtime/channels/fanout-location-runtime.js +133 -81
- package/dist/runtime/channels/spot-direct-envelope.d.ts +1 -0
- package/dist/runtime/channels/spot-direct-envelope.js +4 -3
- package/dist/runtime/channels/spot-node-route-transport.js +3 -2
- package/dist/runtime/diagnostics/diagnostic-text.d.ts +2 -0
- package/dist/runtime/diagnostics/diagnostic-text.js +7 -0
- package/dist/runtime/diagnostics/dispatch-error-details.d.ts +3 -0
- package/dist/runtime/diagnostics/dispatch-error-details.js +30 -2
- package/dist/runtime/diagnostics/flow-context.d.ts +6 -0
- package/dist/runtime/diagnostics/flow-context.js +16 -2
- package/dist/runtime/diagnostics/index.d.ts +4 -2
- package/dist/runtime/diagnostics/index.js +14 -4
- package/dist/runtime/diagnostics/message-flow.d.ts +1 -1
- package/dist/runtime/diagnostics/message-flow.js +10 -19
- package/dist/runtime/diagnostics/runtime-metrics.d.ts +94 -31
- package/dist/runtime/diagnostics/runtime-metrics.js +168 -105
- package/dist/runtime/diagnostics/runtime-observation-queue.js +3 -2
- package/dist/runtime/diagnostics/topology-runtime-projections.d.ts +2 -4
- package/dist/runtime/diagnostics/topology-runtime-projections.js +135 -154
- package/dist/runtime/disposal.d.ts +5 -0
- package/dist/runtime/disposal.js +22 -0
- package/dist/runtime/execution/index.d.ts +22 -9
- package/dist/runtime/execution/index.js +107 -37
- package/dist/runtime/execution/serial-execution-queue.d.ts +12 -7
- package/dist/runtime/execution/serial-execution-queue.js +99 -26
- package/dist/runtime/execution/state-lane.d.ts +8 -1
- package/dist/runtime/execution/state-lane.js +118 -3
- package/dist/runtime/foundation/actor-join-recovery-codec.d.ts +3 -0
- package/dist/runtime/foundation/actor-join-recovery-codec.js +15 -11
- package/dist/runtime/foundation/event-loop-resources.d.ts +2 -24
- package/dist/runtime/foundation/event-loop-resources.js +6 -119
- package/dist/runtime/foundation/listener-records.d.ts +11 -0
- package/dist/runtime/foundation/listener-records.js +20 -0
- package/dist/runtime/foundation/operation-identity.d.ts +2 -0
- package/dist/runtime/foundation/operation-identity.js +9 -5
- package/dist/runtime/foundation/operation-registry.d.ts +5 -7
- package/dist/runtime/foundation/operation-registry.js +34 -44
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +18 -26
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +122 -469
- package/dist/runtime/foundation/runtime-state-projections.d.ts +3 -1
- package/dist/runtime/foundation/runtime-state-projections.js +8 -0
- package/dist/runtime/foundation/service-authority-payload-codec.d.ts +1 -0
- package/dist/runtime/foundation/service-authority-payload-codec.js +30 -12
- package/dist/runtime/foundation/service-discovery-registry.d.ts +4 -0
- package/dist/runtime/foundation/service-discovery-registry.js +22 -3
- package/dist/runtime/foundation/service-instance-activation-recovery-codec.js +8 -4
- package/dist/runtime/foundation/service-liveness-registry.js +2 -1
- package/dist/runtime/foundation/service-maintenance-runtime.d.ts +0 -1
- package/dist/runtime/foundation/service-maintenance-runtime.js +23 -8
- package/dist/runtime/foundation/service-metadata-codec.js +3 -3
- package/dist/runtime/foundation/service-relocation-aggregate-committer.d.ts +2 -5
- package/dist/runtime/foundation/service-relocation-aggregate-committer.js +1 -48
- package/dist/runtime/foundation/service-relocation-coordinator.d.ts +9 -0
- package/dist/runtime/foundation/service-relocation-coordinator.js +14 -1
- package/dist/runtime/foundation/service-relocation-object-owner.d.ts +8 -0
- package/dist/runtime/foundation/service-relocation-object-owner.js +19 -6
- package/dist/runtime/foundation/service-relocation-runtime.js +43 -32
- package/dist/runtime/foundation/service-runtime-contracts.d.ts +40 -8
- package/dist/runtime/foundation/service-runtime-contracts.js +25 -1
- package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +0 -19
- package/dist/runtime/foundation/service-stateful-registry.d.ts +2 -0
- package/dist/runtime/foundation/service-stateful-registry.js +10 -2
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +28 -24
- package/dist/runtime/foundation/service-stateful-runtime.js +297 -244
- package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +12 -7
- package/dist/runtime/foundation/service-stateful-wire-codec.js +240 -143
- package/dist/runtime/foundation/service-topology-registry.d.ts +1 -3
- package/dist/runtime/foundation/service-topology-registry.js +22 -61
- package/dist/runtime/foundation/service-wire-binary-primitives.d.ts +5 -0
- package/dist/runtime/foundation/service-wire-binary-primitives.js +6 -0
- package/dist/runtime/foundation/service-wire-codec.js +10 -8
- package/dist/runtime/foundation/service-wire-constants.generated.d.ts +1 -0
- package/dist/runtime/foundation/service-wire-constants.generated.js +2 -1
- package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +2 -0
- package/dist/runtime/foundation/service-wire-m6a-codec.js +23 -13
- package/dist/runtime/framework-errors-internal.d.ts +2 -1
- package/dist/runtime/framework-errors-internal.js +343 -122
- package/dist/runtime/handlers/handler-instance-scope.d.ts +3 -1
- package/dist/runtime/handlers/handler-instance-scope.js +61 -74
- package/dist/runtime/host/actor-packet-relay.d.ts +9 -6
- package/dist/runtime/host/actor-packet-relay.js +48 -91
- package/dist/runtime/host/actor-placement-coordinator.js +7 -5
- package/dist/runtime/host/actor-runtime-options-factory.d.ts +2 -0
- package/dist/runtime/host/actor-runtime-options-factory.js +17 -4
- package/dist/runtime/host/actor-transfer-runtime.d.ts +28 -28
- package/dist/runtime/host/actor-transfer-runtime.js +210 -256
- package/dist/runtime/host/application-job-queue.js +16 -33
- package/dist/runtime/host/bound-session-relay.js +1 -1
- package/dist/runtime/host/channel-runtime-options-factory.js +1 -1
- package/dist/runtime/host/index.d.ts +19 -14
- package/dist/runtime/host/index.js +234 -175
- package/dist/runtime/host/instance-activation-authority.d.ts +2 -3
- package/dist/runtime/host/instance-activation-authority.js +17 -45
- package/dist/runtime/host/location-runtime-owner.d.ts +1 -1
- package/dist/runtime/host/location-runtime-owner.js +2 -2
- package/dist/runtime/host/remote-actor-join-receiver.d.ts +2 -2
- package/dist/runtime/host/remote-actor-join-receiver.js +1 -14
- package/dist/runtime/host/remote-actor-packet-target-store.d.ts +3 -2
- package/dist/runtime/host/remote-actor-packet-target-store.js +8 -47
- package/dist/runtime/host/remote-bound-session-relay.d.ts +2 -3
- package/dist/runtime/host/remote-bound-session-relay.js +11 -23
- package/dist/runtime/host/route-mesh-runtime.d.ts +14 -10
- package/dist/runtime/host/route-mesh-runtime.js +178 -257
- package/dist/runtime/host/runtime-shutdown.d.ts +2 -0
- package/dist/runtime/host/runtime-shutdown.js +18 -21
- package/dist/runtime/host/service-relocation-control.js +6 -1
- package/dist/runtime/host/service-relocation-host-runtime.d.ts +71 -38
- package/dist/runtime/host/service-relocation-host-runtime.js +816 -651
- package/dist/runtime/host/spot-address-transport.d.ts +2 -3
- package/dist/runtime/host/spot-address-transport.js +34 -136
- package/dist/runtime/host/spot-node-runtime-options-factory.d.ts +1 -1
- package/dist/runtime/host/spot-node-runtime-options-factory.js +3 -2
- package/dist/runtime/host/spot-runtime-options-factory.d.ts +4 -7
- package/dist/runtime/host/spot-runtime-options-factory.js +53 -21
- package/dist/runtime/host/stateful-authority-route-runtime.js +6 -2
- package/dist/runtime/host/user-spot-creation-coordinator.d.ts +14 -6
- package/dist/runtime/host/user-spot-creation-coordinator.js +96 -95
- package/dist/runtime/locations/aggregate-inventory-store.js +13 -10
- package/dist/runtime/locations/authority-key-codec.js +4 -3
- package/dist/runtime/locations/auto-connect-reconciler.d.ts +4 -0
- package/dist/runtime/locations/auto-connect-reconciler.js +37 -25
- package/dist/runtime/locations/creation-operation-id.js +6 -4
- package/dist/runtime/locations/domain-store-contract.d.ts +3 -3
- package/dist/runtime/locations/in-memory-authority-store.js +31 -15
- package/dist/runtime/locations/in-memory-location-store.d.ts +2 -2
- package/dist/runtime/locations/in-memory-location-store.js +17 -14
- package/dist/runtime/locations/in-memory-provider-location-store.d.ts +1 -0
- package/dist/runtime/locations/in-memory-provider-location-store.js +79 -27
- package/dist/runtime/locations/key-codec.js +3 -2
- package/dist/runtime/locations/lease-tracker.d.ts +3 -5
- package/dist/runtime/locations/lease-tracker.js +7 -11
- package/dist/runtime/locations/lifecycle.d.ts +5 -10
- package/dist/runtime/locations/lifecycle.js +4 -33
- package/dist/runtime/locations/location-store-failure.d.ts +2 -0
- package/dist/runtime/locations/location-store-failure.js +7 -0
- package/dist/runtime/locations/location-store-pages.d.ts +3 -0
- package/dist/runtime/locations/location-store-pages.js +13 -0
- package/dist/runtime/locations/location-store-repository.d.ts +5 -7
- package/dist/runtime/locations/location-store-repository.js +303 -332
- package/dist/runtime/locations/relocation-blob.js +18 -5
- package/dist/runtime/locations/resolvers.d.ts +5 -5
- package/dist/runtime/locations/resolvers.js +44 -30
- package/dist/runtime/locations/runtime.d.ts +5 -4
- package/dist/runtime/locations/runtime.js +123 -92
- package/dist/runtime/locations/spot-location-claims.d.ts +8 -2
- package/dist/runtime/locations/spot-location-claims.js +40 -23
- package/dist/runtime/messaging/payload-codec.js +26 -20
- package/dist/runtime/messaging/submission-result.d.ts +3 -0
- package/dist/runtime/messaging/submission-result.js +48 -0
- package/dist/runtime/protocol/service_wire_codec.generated.d.ts +2 -0
- package/dist/runtime/protocol/service_wire_codec.generated.js +17 -8
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.d.ts +1 -0
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +3 -2
- package/dist/runtime/spots/formal-remote-actor-admission-registry.d.ts +3 -3
- package/dist/runtime/spots/formal-remote-actor-admission-registry.js +4 -4
- package/dist/runtime/spots/formal-remote-actor-transfer-registry.d.ts +6 -3
- package/dist/runtime/spots/index.d.ts +29 -29
- package/dist/runtime/spots/index.js +441 -313
- package/dist/runtime/spots/spot-activation-registry.d.ts +4 -4
- package/dist/runtime/spots/spot-activation-registry.js +27 -60
- package/dist/runtime/spots/spot-activation-state.d.ts +2 -11
- package/dist/runtime/spots/spot-activation-state.js +4 -17
- package/dist/runtime/spots/spot-activation.d.ts +10 -9
- package/dist/runtime/spots/spot-activation.js +103 -81
- package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +1 -2
- package/dist/runtime/spots/spot-actor-admission-coordinator.js +3 -40
- package/dist/runtime/spots/spot-actor-join-dispatch.d.ts +10 -18
- package/dist/runtime/spots/spot-actor-join-dispatch.js +5 -55
- package/dist/runtime/spots/spot-actor-lifecycle-drain.js +2 -2
- package/dist/runtime/spots/spot-actor-membership.d.ts +7 -5
- package/dist/runtime/spots/spot-actor-membership.js +44 -53
- package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +0 -1
- package/dist/runtime/spots/spot-actor-packet-dispatch.js +0 -9
- package/dist/runtime/spots/spot-actor-packet-relay-dispatch.d.ts +2 -2
- package/dist/runtime/spots/spot-actor-packet-relay-dispatch.js +1 -7
- package/dist/runtime/spots/spot-context.d.ts +2 -2
- package/dist/runtime/spots/spot-entry-activation.d.ts +3 -4
- package/dist/runtime/spots/spot-entry-activation.js +9 -21
- package/dist/runtime/spots/spot-lifecycle-metrics.js +3 -2
- package/dist/runtime/spots/spot-manager-public.d.ts +5 -6
- package/dist/runtime/spots/spot-manager-public.js +6 -5
- package/dist/runtime/spots/spot-node-autoconnect.js +22 -31
- package/dist/runtime/spots/spot-node-runtime-manager.d.ts +22 -21
- package/dist/runtime/spots/spot-node-runtime-manager.js +73 -82
- package/dist/runtime/spots/spot-outbound.d.ts +4 -3
- package/dist/runtime/spots/spot-outbound.js +10 -37
- package/dist/runtime/spots/spot-remote-codec.js +5 -3
- package/dist/runtime/spots/spot-route-packet-dispatch.d.ts +2 -2
- package/dist/runtime/spots/spot-route-packet-dispatch.js +8 -8
- package/dist/runtime/spots/spot-route-replies.d.ts +2 -2
- package/dist/runtime/spots/spot-route-replies.js +3 -1
- package/dist/runtime/spots/spot-routed-actor-admission.d.ts +3 -2
- package/dist/runtime/spots/spot-routed-actor-admission.js +3 -2
- package/dist/runtime/spots/spot-routed-frame-dispatch.d.ts +2 -7
- package/dist/runtime/spots/spot-routed-frame-dispatch.js +1 -70
- package/dist/runtime/spots/spot-routed-spot-packet-dispatch.d.ts +4 -0
- package/dist/runtime/spots/spot-routed-spot-packet-dispatch.js +65 -15
- package/dist/runtime/spots/spot-runtime-ports.d.ts +4 -20
- package/dist/runtime/spots/spot-serial-executor.d.ts +5 -0
- package/dist/runtime/spots/spot-serial-executor.js +9 -2
- package/dist/runtime/spots/spot-serial-turn-executor.d.ts +29 -6
- package/dist/runtime/spots/spot-serial-turn-executor.js +89 -13
- package/dist/runtime/spots/spot-timer.d.ts +3 -2
- package/dist/runtime/streams/actor-session-binding-registry.d.ts +17 -32
- package/dist/runtime/streams/actor-session-binding-registry.js +145 -367
- package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +2 -6
- package/dist/runtime/streams/bound-actor-relay-sender.d.ts +6 -2
- package/dist/runtime/streams/bound-actor-relay-sender.js +57 -33
- package/dist/runtime/streams/bound-session-service.d.ts +7 -0
- package/dist/runtime/streams/bound-session-service.js +12 -19
- package/dist/runtime/streams/index.d.ts +15 -14
- package/dist/runtime/streams/index.js +38 -39
- package/dist/runtime/streams/managed-stream.d.ts +3 -0
- package/dist/runtime/streams/managed-stream.js +17 -8
- package/dist/runtime/streams/native-fallback-bound-session.js +1 -3
- package/dist/runtime/streams/protocol.d.ts +6 -33
- package/dist/runtime/streams/protocol.js +38 -110
- package/dist/runtime/streams/session-actor-coordinator.d.ts +2 -3
- package/dist/runtime/streams/session-actor-coordinator.js +12 -4
- package/dist/runtime/streams/session-calls.d.ts +1 -1
- package/dist/runtime/streams/session-calls.js +5 -4
- package/dist/runtime/streams/session-context.d.ts +6 -6
- package/dist/runtime/streams/session-context.js +22 -4
- package/dist/runtime/streams/session-requests.js +2 -1
- package/dist/runtime/streams/session-serial-executor.js +2 -2
- package/dist/runtime/streams/stream-binding-runtime-ports.d.ts +16 -1
- package/dist/runtime/streams/stream-frame-factory.js +2 -2
- package/dist/runtime/streams/stream-session-runtime.d.ts +10 -11
- package/dist/runtime/streams/stream-session-runtime.js +25 -33
- package/dist/runtime/workers/index.d.ts +2 -2
- package/dist/runtime/workers/index.js +5 -4
- package/package.json +3 -3
- package/src/contracts/Channels/IZLinkFanoutClient.ts +0 -7
- package/src/contracts/Common/CoreTypes.ts +7 -0
- package/src/contracts/Common/index.ts +1 -1
- package/src/contracts/Configuration/InternalDefaults.ts +12 -0
- package/src/contracts/Configuration/Registration.ts +92 -48
- package/src/contracts/Configuration/RegistrationBuilderPolicy.ts +44 -2
- package/src/contracts/Configuration/RegistrationBuilders.ts +77 -68
- package/src/contracts/Configuration/RegistrationTypes.ts +1 -0
- package/src/contracts/Configuration/RegistrationValidators.ts +19 -13
- package/src/contracts/Configuration/TimerRegistrationValidator.ts +4 -2
- package/src/contracts/Dispatch/ZLinkDispatchOptions.ts +1 -0
- package/src/contracts/Handlers/JsonContract.ts +14 -4
- package/src/contracts/Locations/Authority.ts +7 -3
- package/src/contracts/Locations/Options.ts +1 -1
- package/src/contracts/Locations/Stores.ts +14 -0
- package/src/contracts/RouteMesh/RuntimeTopology.ts +10 -0
- package/src/contracts/Spots/Contracts.ts +1 -1
- package/src/internal.ts +15 -0
- package/src/nest-integration.ts +28 -10
- package/src/runtime/abort.ts +6 -5
- package/src/runtime/activation-admission.ts +72 -0
- package/src/runtime/actors/actor-authority-payload-codec.ts +58 -20
- package/src/runtime/actors/actor-client.ts +77 -204
- package/src/runtime/actors/actor-context.ts +45 -39
- package/src/runtime/actors/actor-handoff.ts +55 -45
- package/src/runtime/actors/actor-lifecycle-snapshot.ts +2 -1
- package/src/runtime/actors/actor-local-native-join.ts +174 -215
- package/src/runtime/actors/actor-native-lookup.ts +2 -9
- package/src/runtime/actors/actor-packet-relay-wire.ts +18 -12
- package/src/runtime/actors/actor-remote-joiner.ts +15 -36
- package/src/runtime/actors/actor-remote-wire.ts +5 -3
- package/src/runtime/actors/actor-request-deadline.ts +49 -0
- package/src/runtime/actors/actor-retry-delay.ts +2 -1
- package/src/runtime/actors/actor-runtime-contracts.ts +4 -10
- package/src/runtime/actors/actor-runtime-state.ts +85 -247
- package/src/runtime/actors/actor-source-transfer.ts +5 -18
- package/src/runtime/actors/bound-session-wire.ts +2 -8
- package/src/runtime/actors/deferred-join-completion.ts +44 -0
- package/src/runtime/actors/index.ts +122 -88
- package/src/runtime/actors/spot-actor-dispatch.ts +60 -29
- package/src/runtime/actors/transferred-actor-rollback.ts +11 -62
- package/src/runtime/admission.ts +13 -2
- package/src/runtime/application-jobs/application-job-queue-scope.ts +4 -4
- package/src/runtime/backend/contracts/index.ts +15 -42
- package/src/runtime/backend/mesh-actor-session-node-adapter.ts +4 -28
- package/src/runtime/backend/mesh-dispatch-pump.ts +111 -117
- package/src/runtime/backend/node/node-backend-adapter-factory.ts +5 -25
- package/src/runtime/backend/node/node-backend-adapter-support.ts +32 -35
- package/src/runtime/backend/node/node-event-loop-poller.ts +16 -8
- package/src/runtime/backend/node/node-monitor-backend-adapter.ts +1 -0
- package/src/runtime/backend/node/node-raw-binding-port.ts +58 -23
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +252 -162
- package/src/runtime/backend/node/node-socket-backend-adapter.ts +5 -4
- package/src/runtime/backend/raw-binding-port.ts +14 -1
- package/src/runtime/backend/runtime-values.ts +4 -1
- package/src/runtime/channels/channel-clients.ts +132 -96
- package/src/runtime/channels/channel-envelope-inspection.ts +2 -1
- package/src/runtime/channels/channel-envelope.ts +58 -29
- package/src/runtime/channels/channel-outbound-operations.ts +10 -31
- package/src/runtime/channels/channel-receive-loops.ts +39 -7
- package/src/runtime/channels/channel-runtime-lifecycle.ts +60 -27
- package/src/runtime/channels/channel-runtime-manager.ts +8 -15
- package/src/runtime/channels/channel-socket-options.ts +8 -2
- package/src/runtime/channels/channel-socket-registry.ts +369 -262
- package/src/runtime/channels/channel-transports.ts +161 -115
- package/src/runtime/channels/client-server-location-runtime.ts +17 -17
- package/src/runtime/channels/client-server-service-wire.ts +40 -12
- package/src/runtime/channels/dispatch-error-reporter.ts +7 -5
- package/src/runtime/channels/fanout-location-runtime.ts +175 -106
- package/src/runtime/channels/spot-direct-envelope.ts +1 -1
- package/src/runtime/channels/spot-node-route-transport.ts +5 -2
- package/src/runtime/diagnostics/diagnostic-text.ts +4 -0
- package/src/runtime/diagnostics/dispatch-error-details.ts +36 -1
- package/src/runtime/diagnostics/flow-context.ts +16 -2
- package/src/runtime/diagnostics/index.ts +27 -17
- package/src/runtime/diagnostics/message-flow.ts +15 -25
- package/src/runtime/diagnostics/runtime-metrics.ts +181 -113
- package/src/runtime/diagnostics/runtime-observation-queue.ts +3 -2
- package/src/runtime/diagnostics/topology-runtime-projections.ts +194 -162
- package/src/runtime/disposal.ts +19 -0
- package/src/runtime/execution/index.ts +116 -38
- package/src/runtime/execution/serial-execution-queue.ts +179 -34
- package/src/runtime/execution/state-lane.ts +142 -4
- package/src/runtime/foundation/actor-join-recovery-codec.ts +13 -8
- package/src/runtime/foundation/event-loop-resources.ts +5 -122
- package/src/runtime/foundation/listener-records.ts +21 -0
- package/src/runtime/foundation/operation-identity.ts +11 -5
- package/src/runtime/foundation/operation-registry.ts +33 -50
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +170 -571
- package/src/runtime/foundation/runtime-state-projections.ts +9 -1
- package/src/runtime/foundation/service-authority-payload-codec.ts +41 -13
- package/src/runtime/foundation/service-discovery-registry.ts +32 -3
- package/src/runtime/foundation/service-instance-activation-recovery-codec.ts +12 -4
- package/src/runtime/foundation/service-liveness-registry.ts +2 -1
- package/src/runtime/foundation/service-maintenance-runtime.ts +30 -19
- package/src/runtime/foundation/service-metadata-codec.ts +2 -2
- package/src/runtime/foundation/service-relocation-aggregate-committer.ts +3 -66
- package/src/runtime/foundation/service-relocation-coordinator.ts +13 -0
- package/src/runtime/foundation/service-relocation-object-owner.ts +23 -7
- package/src/runtime/foundation/service-relocation-runtime.ts +57 -35
- package/src/runtime/foundation/service-runtime-contracts.ts +46 -10
- package/src/runtime/foundation/service-session-binding-ingress-port.ts +0 -25
- package/src/runtime/foundation/service-stateful-registry.ts +14 -2
- package/src/runtime/foundation/service-stateful-runtime.ts +435 -327
- package/src/runtime/foundation/service-stateful-wire-codec.ts +329 -199
- package/src/runtime/foundation/service-topology-registry.ts +30 -79
- package/src/runtime/foundation/service-wire-binary-primitives.ts +6 -0
- package/src/runtime/foundation/service-wire-codec.ts +15 -8
- package/src/runtime/foundation/service-wire-constants.generated.ts +1 -0
- package/src/runtime/foundation/service-wire-m6a-codec.ts +29 -15
- package/src/runtime/framework-errors-internal.ts +380 -134
- package/src/runtime/handlers/handler-instance-scope.ts +68 -95
- package/src/runtime/host/actor-packet-relay.ts +57 -147
- package/src/runtime/host/actor-placement-coordinator.ts +8 -5
- package/src/runtime/host/actor-runtime-options-factory.ts +27 -8
- package/src/runtime/host/actor-transfer-runtime.ts +266 -418
- package/src/runtime/host/application-job-queue.ts +28 -34
- package/src/runtime/host/bound-session-relay.ts +1 -2
- package/src/runtime/host/channel-runtime-options-factory.ts +1 -2
- package/src/runtime/host/index.ts +408 -312
- package/src/runtime/host/instance-activation-authority.ts +89 -109
- package/src/runtime/host/location-runtime-owner.ts +4 -2
- package/src/runtime/host/remote-actor-join-receiver.ts +3 -31
- package/src/runtime/host/remote-actor-packet-target-store.ts +17 -58
- package/src/runtime/host/remote-bound-session-relay.ts +12 -44
- package/src/runtime/host/route-mesh-runtime.ts +236 -275
- package/src/runtime/host/runtime-shutdown.ts +17 -21
- package/src/runtime/host/service-relocation-control.ts +14 -1
- package/src/runtime/host/service-relocation-host-runtime.ts +1161 -904
- package/src/runtime/host/spot-address-transport.ts +73 -195
- package/src/runtime/host/spot-node-runtime-options-factory.ts +6 -3
- package/src/runtime/host/spot-runtime-options-factory.ts +92 -30
- package/src/runtime/host/stateful-authority-route-runtime.ts +8 -4
- package/src/runtime/host/user-spot-creation-coordinator.ts +182 -163
- package/src/runtime/locations/aggregate-inventory-store.ts +16 -13
- package/src/runtime/locations/authority-key-codec.ts +7 -3
- package/src/runtime/locations/auto-connect-reconciler.ts +39 -26
- package/src/runtime/locations/creation-operation-id.ts +6 -3
- package/src/runtime/locations/domain-store-contract.ts +12 -3
- package/src/runtime/locations/in-memory-authority-store.ts +44 -15
- package/src/runtime/locations/in-memory-location-store.ts +50 -39
- package/src/runtime/locations/in-memory-provider-location-store.ts +110 -31
- package/src/runtime/locations/key-codec.ts +3 -2
- package/src/runtime/locations/lease-tracker.ts +8 -14
- package/src/runtime/locations/lifecycle.ts +16 -45
- package/src/runtime/locations/location-store-failure.ts +4 -0
- package/src/runtime/locations/location-store-pages.ts +18 -0
- package/src/runtime/locations/location-store-repository.ts +450 -478
- package/src/runtime/locations/relocation-blob.ts +17 -6
- package/src/runtime/locations/resolvers.ts +90 -58
- package/src/runtime/locations/runtime.ts +220 -110
- package/src/runtime/locations/spot-location-claims.ts +54 -26
- package/src/runtime/messaging/payload-codec.ts +35 -24
- package/src/runtime/messaging/submission-result.ts +57 -1
- package/src/runtime/protocol/service_wire_codec.generated.ts +27 -10
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +4 -4
- package/src/runtime/spots/formal-remote-actor-admission-registry.ts +6 -6
- package/src/runtime/spots/formal-remote-actor-transfer-registry.ts +6 -3
- package/src/runtime/spots/index.ts +687 -437
- package/src/runtime/spots/spot-activation-registry.ts +42 -71
- package/src/runtime/spots/spot-activation-state.ts +4 -16
- package/src/runtime/spots/spot-activation.ts +167 -115
- package/src/runtime/spots/spot-actor-admission-coordinator.ts +5 -57
- package/src/runtime/spots/spot-actor-join-dispatch.ts +25 -79
- package/src/runtime/spots/spot-actor-lifecycle-drain.ts +20 -18
- package/src/runtime/spots/spot-actor-membership.ts +107 -75
- package/src/runtime/spots/spot-actor-packet-dispatch.ts +0 -16
- package/src/runtime/spots/spot-actor-packet-relay-dispatch.ts +3 -15
- package/src/runtime/spots/spot-context.ts +3 -3
- package/src/runtime/spots/spot-entry-activation.ts +13 -33
- package/src/runtime/spots/spot-lifecycle-metrics.ts +3 -2
- package/src/runtime/spots/spot-manager-public.ts +29 -21
- package/src/runtime/spots/spot-node-autoconnect.ts +20 -26
- package/src/runtime/spots/spot-node-runtime-manager.ts +145 -140
- package/src/runtime/spots/spot-outbound.ts +47 -68
- package/src/runtime/spots/spot-remote-codec.ts +7 -3
- package/src/runtime/spots/spot-route-packet-dispatch.ts +25 -24
- package/src/runtime/spots/spot-route-replies.ts +6 -4
- package/src/runtime/spots/spot-routed-actor-admission.ts +13 -6
- package/src/runtime/spots/spot-routed-frame-dispatch.ts +6 -77
- package/src/runtime/spots/spot-routed-spot-packet-dispatch.ts +95 -22
- package/src/runtime/spots/spot-runtime-ports.ts +10 -43
- package/src/runtime/spots/spot-serial-executor.ts +10 -2
- package/src/runtime/spots/spot-serial-turn-executor.ts +120 -13
- package/src/runtime/spots/spot-timer.ts +3 -2
- package/src/runtime/streams/actor-session-binding-registry.ts +182 -560
- package/src/runtime/streams/actor-session-binding-runtime-owner.ts +1 -19
- package/src/runtime/streams/bound-actor-relay-sender.ts +89 -43
- package/src/runtime/streams/bound-session-service.ts +23 -37
- package/src/runtime/streams/index.ts +102 -88
- package/src/runtime/streams/managed-stream.ts +29 -10
- package/src/runtime/streams/native-fallback-bound-session.ts +1 -3
- package/src/runtime/streams/protocol.ts +34 -88
- package/src/runtime/streams/session-actor-coordinator.ts +17 -5
- package/src/runtime/streams/session-calls.ts +6 -5
- package/src/runtime/streams/session-context.ts +54 -10
- package/src/runtime/streams/session-requests.ts +2 -1
- package/src/runtime/streams/session-serial-executor.ts +2 -2
- package/src/runtime/streams/stream-binding-runtime-ports.ts +22 -1
- package/src/runtime/streams/stream-frame-factory.ts +2 -3
- package/src/runtime/streams/stream-session-runtime.ts +57 -59
- package/src/runtime/workers/index.ts +13 -12
- package/dist/runtime/actors/deferred-join-accepted-journal.d.ts +0 -47
- package/dist/runtime/actors/deferred-join-accepted-journal.js +0 -992
- package/dist/runtime/actors/post-commit-actor-location.d.ts +0 -19
- package/dist/runtime/actors/post-commit-actor-location.js +0 -72
- package/dist/runtime/spots/spot-native-actor-join-admission.d.ts +0 -26
- package/dist/runtime/spots/spot-native-actor-join-admission.js +0 -106
- package/src/runtime/actors/deferred-join-accepted-journal.ts +0 -1346
- package/src/runtime/actors/post-commit-actor-location.ts +0 -121
- package/src/runtime/spots/spot-native-actor-join-admission.ts +0 -155
|
@@ -1,47 +1,88 @@
|
|
|
1
|
+
import { UINT64_MAX, ZlinkStreamContentType } from '@zlink-systems/stream-wire';
|
|
1
2
|
import { randomBytes } from 'node:crypto';
|
|
2
|
-
import { SubmitResult } from '../backend/runtime-values';
|
|
3
|
-
import type {
|
|
4
|
-
RoutingId,
|
|
5
|
-
Type,
|
|
6
|
-
ZLinkActor,
|
|
7
|
-
ZLinkActorRelocationAdapter,
|
|
8
|
-
ZLinkMeshNodeDescriptor,
|
|
9
|
-
ZLinkSpot,
|
|
10
|
-
ZLinkSpotRelocationAdapter,
|
|
11
|
-
ZLinkInstanceSpot
|
|
12
|
-
} from '../../contracts';
|
|
13
|
-
import type {
|
|
14
|
-
ZLinkAuthorityKey,
|
|
15
|
-
ZLinkAuthorityScanCursor,
|
|
16
|
-
ZLinkAuthoritySnapshot,
|
|
17
|
-
ZLinkCapacityVector,
|
|
18
|
-
ZLinkLocationOwnerToken
|
|
19
|
-
} from '../locations/internal-location-contracts';
|
|
20
3
|
import {
|
|
4
|
+
type RoutingId,
|
|
5
|
+
type Type,
|
|
6
|
+
type ZLinkActor,
|
|
7
|
+
type ZLinkActorRelocationAdapter,
|
|
8
|
+
type ZLinkInstanceSpot,
|
|
9
|
+
type ZLinkMeshNodeDescriptor,
|
|
10
|
+
type ZLinkSpot,
|
|
11
|
+
type ZLinkSpotRelocationAdapter,
|
|
21
12
|
ZLinkFrameworkErrorKind,
|
|
22
13
|
ZLinkFrameworkException,
|
|
23
14
|
ZLinkFrameworkRuntimeState,
|
|
24
15
|
ZLinkObjectRole,
|
|
16
|
+
ZLinkSpotKind,
|
|
25
17
|
ZLinkSpotRelocationCoordinationMode,
|
|
26
18
|
ZLinkSpotRelocationReadyOutcome,
|
|
27
|
-
ZLinkSpotKind,
|
|
28
19
|
ZLinkUserSpotExecutionMode
|
|
29
20
|
} from '../../contracts';
|
|
21
|
+
|
|
22
|
+
import type { ZLinkProviderResolver } from '../../contracts/Common/ZLinkProviderResolver';
|
|
30
23
|
import { zlinkRuntimeDefaultLocationOptions } from '../../contracts/Locations/Options';
|
|
24
|
+
import { ZLINK_PROVIDER_MAX_PAGE_SIZE } from '../../contracts/Locations/Stores';
|
|
25
|
+
import type { ZLinkActivationAdmission } from '../activation-admission';
|
|
26
|
+
import type { DefaultZLinkActorManager } from '../actors';
|
|
31
27
|
import {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
} from '../
|
|
35
|
-
import
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
28
|
+
decodeRelocatingActorAuthorityIdentity,
|
|
29
|
+
rewriteActorAuthorityRoute
|
|
30
|
+
} from '../actors/actor-authority-publication';
|
|
31
|
+
import {
|
|
32
|
+
replayActorHandoffBacklog,
|
|
33
|
+
type ZLinkActorHandoffPacket,
|
|
34
|
+
type ZLinkActorHandoffResult
|
|
35
|
+
} from '../actors/actor-handoff';
|
|
36
|
+
import type { ZLinkActorJoinRelocation } from '../actors/actor-join-relocation';
|
|
37
|
+
import {
|
|
38
|
+
ZLINK_REMOTE_ACTOR_SOURCE_LEAVE_TERMINAL,
|
|
39
|
+
decodeRemoteActorSourceLeaveTerminal
|
|
40
|
+
} from '../actors/actor-remote-wire';
|
|
41
|
+
import {
|
|
42
|
+
toFrameworkActorRef,
|
|
43
|
+
type ZLinkActorRuntimeState,
|
|
44
|
+
type ZLinkRemoteBoundSessionTarget
|
|
45
|
+
} from '../actors/actor-runtime-state';
|
|
40
46
|
import type { ZLinkBackendMeshNode, ZLinkMeshCompletionTable } from '../backend';
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
44
|
-
|
|
47
|
+
import { RequestResult, SubmitResult } from '../backend/runtime-values';
|
|
48
|
+
import type { ZLinkFrameworkRegistration } from '../configuration';
|
|
49
|
+
import {
|
|
50
|
+
type ZLinkRuntimeEventPublisher,
|
|
51
|
+
type ZLinkRuntimeMetrics,
|
|
52
|
+
isRelocationDebugEnabled,
|
|
53
|
+
relocationDebug
|
|
54
|
+
} from '../diagnostics';
|
|
55
|
+
|
|
56
|
+
import { METRIC_NAMES, MILLISECONDS_PER_SECOND } from '../diagnostics/runtime-metrics';
|
|
57
|
+
import { isStructuralGuardEnabled, trackDiagnosticCompletion } from '../execution/state-lane';
|
|
58
|
+
import {
|
|
59
|
+
RECREATE_RELOCATION_CONTENT_TYPE,
|
|
60
|
+
SNAPSHOT_RELOCATION_CONTENT_TYPE,
|
|
61
|
+
decodeCanonicalActorJoinRecoverySavedWork,
|
|
62
|
+
encodeCanonicalActorJoinRecoverySavedWork,
|
|
63
|
+
type CanonicalActorJoinRecovery
|
|
64
|
+
} from '../foundation/actor-join-recovery-codec';
|
|
65
|
+
import { OPERATION_IDENTITY_BYTES } from '../foundation/operation-identity';
|
|
66
|
+
import { rewriteServiceAuthorityRoute } from '../foundation/service-authority-payload-codec';
|
|
67
|
+
import { ServiceMaintenanceRuntime } from '../foundation/service-maintenance-runtime';
|
|
68
|
+
import {
|
|
69
|
+
ServiceRelocationAggregateCommitter,
|
|
70
|
+
type ServicePreparedRelocationAggregate,
|
|
71
|
+
type ServiceRelocationAggregatePlan
|
|
72
|
+
} from '../foundation/service-relocation-aggregate-committer';
|
|
73
|
+
import {
|
|
74
|
+
ServiceRelocationAuthorityError,
|
|
75
|
+
ServiceRelocationPostCommitError
|
|
76
|
+
} from '../foundation/service-relocation-coordinator';
|
|
77
|
+
import {
|
|
78
|
+
ServiceRelocationObjectCaptureOwner,
|
|
79
|
+
ServiceRelocationObjectRestoreOwner,
|
|
80
|
+
type ServiceCapturedObjectRelocation,
|
|
81
|
+
type ServiceObjectRelocationStaging,
|
|
82
|
+
type ServiceRelocationCaptureUnit,
|
|
83
|
+
type ServiceRelocationHiddenObject,
|
|
84
|
+
type ServiceRelocationTargetObjectPort
|
|
85
|
+
} from '../foundation/service-relocation-object-owner';
|
|
45
86
|
import {
|
|
46
87
|
ServiceRelocationAuthorityPayloadCodec,
|
|
47
88
|
crc32c,
|
|
@@ -60,114 +101,108 @@ import {
|
|
|
60
101
|
type ServiceRelocationTimer
|
|
61
102
|
} from '../foundation/service-relocation-runtime';
|
|
62
103
|
import {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
relocationChunkAt
|
|
69
|
-
} from './relocation-direct-transfer';
|
|
70
|
-
import {
|
|
71
|
-
captureRelocationAdapterState,
|
|
72
|
-
restoreRelocationAdapterState,
|
|
73
|
-
type ZLinkRelocationStateAdapterLike
|
|
74
|
-
} from './relocation-state-adapter';
|
|
75
|
-
import { ServiceRelocationPostCommitError } from '../foundation/service-relocation-coordinator';
|
|
76
|
-
import { ServiceMaintenanceRuntime } from '../foundation/service-maintenance-runtime';
|
|
77
|
-
import {
|
|
78
|
-
ServiceRelocationObjectCaptureOwner,
|
|
79
|
-
ServiceRelocationObjectRestoreOwner,
|
|
80
|
-
type ServiceCapturedObjectRelocation,
|
|
81
|
-
type ServiceObjectRelocationStaging,
|
|
82
|
-
type ServiceRelocationCaptureUnit,
|
|
83
|
-
type ServiceRelocationHiddenObject,
|
|
84
|
-
type ServiceRelocationTargetObjectPort
|
|
85
|
-
} from '../foundation/service-relocation-object-owner';
|
|
86
|
-
import {
|
|
87
|
-
ServiceRelocationAggregateCommitter,
|
|
88
|
-
type ServicePreparedRelocationAggregate,
|
|
89
|
-
type ServiceRelocationAggregatePlan
|
|
90
|
-
} from '../foundation/service-relocation-aggregate-committer';
|
|
91
|
-
import { createProviderInstance } from '../spots/spot-provider';
|
|
92
|
-
import type { DefaultZLinkSpotManager, ZLinkSpotNodeRuntimeManager } from '../spots';
|
|
93
|
-
import type { ZLinkSpotActivation } from '../spots/spot-activation-state';
|
|
94
|
-
import type { DefaultZLinkActorManager } from '../actors';
|
|
95
|
-
import type { ZLinkDeferredJoinAcceptedRoot } from '../actors/deferred-join-accepted-journal';
|
|
96
|
-
import {
|
|
97
|
-
toFrameworkActorRef,
|
|
98
|
-
type ZLinkActorRuntimeState,
|
|
99
|
-
type ZLinkRemoteBoundSessionTarget
|
|
100
|
-
} from '../actors/actor-runtime-state';
|
|
101
|
-
import {
|
|
102
|
-
replayActorHandoffBacklog,
|
|
103
|
-
type ZLinkActorHandoffPacket,
|
|
104
|
-
type ZLinkActorHandoffResult
|
|
105
|
-
} from '../actors/actor-handoff';
|
|
106
|
-
import { decodeHandoffBacklog } from '../spots/spot-remote-codec';
|
|
107
|
-
import {
|
|
108
|
-
decodeRelocatingActorAuthorityIdentity,
|
|
109
|
-
rewriteActorAuthorityRoute
|
|
110
|
-
} from '../actors/actor-authority-publication';
|
|
111
|
-
import { rewriteServiceAuthorityRoute } from '../foundation/service-authority-payload-codec';
|
|
112
|
-
import { committedActorOwnerFence, type ZLinkActorTransferRuntime } from './actor-transfer-runtime';
|
|
113
|
-
import { decodeAuthorityKey, encodeAuthorityKey } from '../locations/authority-key-codec';
|
|
114
|
-
import {
|
|
115
|
-
decodeServiceRelocationControlRequest,
|
|
116
|
-
decodeServiceRelocationControlResponse,
|
|
117
|
-
encodeServiceRelocationControlRequest,
|
|
118
|
-
encodeServiceRelocationControlResponse,
|
|
119
|
-
type ZLinkServiceRelocationControlRequest,
|
|
120
|
-
type ZLinkServiceRelocationControlResponse
|
|
121
|
-
} from './service-relocation-control';
|
|
104
|
+
MeshPeerRuntimeState,
|
|
105
|
+
ReceiveKind,
|
|
106
|
+
type ReceiveRecord
|
|
107
|
+
} from '../foundation/service-runtime-contracts';
|
|
108
|
+
import type { ServiceSpotMessageFollowSeal } from '../foundation/service-stateful-runtime';
|
|
122
109
|
import {
|
|
110
|
+
M6bServiceWireCommand,
|
|
123
111
|
decodeMaintenanceReplyRelay,
|
|
124
112
|
decodeMaintenanceReplyRelayAck,
|
|
113
|
+
decodeServiceWireFrozenRecord,
|
|
125
114
|
decodeSessionRelocationRoute,
|
|
126
115
|
decodeSessionRelocationSeal,
|
|
127
116
|
decodeSessionRelocationSealed,
|
|
128
117
|
encodeMaintenanceReplyRelay,
|
|
129
118
|
encodeMaintenanceReplyRelayAck,
|
|
119
|
+
encodeServiceWireFrozenActorApplicationRecord,
|
|
130
120
|
encodeSessionRelocationRoute,
|
|
131
121
|
encodeSessionRelocationSeal,
|
|
132
122
|
encodeSessionRelocationSealed,
|
|
133
123
|
serviceSessionRelocationIdentityKey,
|
|
134
|
-
decodeServiceWireFrozenRecord,
|
|
135
|
-
encodeServiceWireFrozenActorApplicationRecord,
|
|
136
|
-
M6bServiceWireCommand,
|
|
137
|
-
type ServiceMaintenanceReplyRelay,
|
|
138
|
-
type ServiceMaintenanceReplyRelayAck,
|
|
139
124
|
type ServiceMaintenanceRelocationControl,
|
|
140
125
|
type ServiceMaintenanceRelocationCutover,
|
|
141
126
|
type ServiceMaintenanceRelocationData,
|
|
142
127
|
type ServiceMaintenanceRelocationFailed,
|
|
143
|
-
type ServiceMaintenanceRelocationReady,
|
|
144
128
|
type ServiceMaintenanceRelocationPrepare,
|
|
129
|
+
type ServiceMaintenanceRelocationReady,
|
|
145
130
|
type ServiceMaintenanceRelocationState,
|
|
131
|
+
type ServiceMaintenanceReplyRelay,
|
|
132
|
+
type ServiceMaintenanceReplyRelayAck,
|
|
146
133
|
type ServiceSessionRelocationRoute,
|
|
147
134
|
type ServiceSessionRelocationSeal,
|
|
148
135
|
type ServiceSessionRelocationSealed,
|
|
149
136
|
type ServiceWireOperationId,
|
|
150
|
-
type ServiceWireRequestSourceFence,
|
|
151
|
-
type ServiceWireRelocationTarget,
|
|
152
137
|
type ServiceWireRelocationCoordinatorFence,
|
|
153
|
-
type ServiceWireRelocationObject
|
|
138
|
+
type ServiceWireRelocationObject,
|
|
139
|
+
type ServiceWireRelocationTarget,
|
|
140
|
+
type ServiceWireRequestSourceFence
|
|
154
141
|
} from '../foundation/service-stateful-wire-codec';
|
|
155
142
|
import { ServiceWireFrameworkErrorCode } from '../foundation/service-wire-constants.generated';
|
|
143
|
+
import { ServiceWireProtocolError } from '../foundation/service-wire-m6a-codec';
|
|
144
|
+
import {
|
|
145
|
+
ZLinkFrameworkInternalErrorKind,
|
|
146
|
+
createInternalFrameworkException,
|
|
147
|
+
frameworkRelocationFailureCode,
|
|
148
|
+
internalFrameworkErrorKindFromWireFailureCode
|
|
149
|
+
} from '../framework-errors-internal';
|
|
150
|
+
import { decodeAuthorityKey, encodeAuthorityKey } from '../locations/authority-key-codec';
|
|
151
|
+
import { isIndeterminateLocationStoreFailure } from '../locations/location-store-failure';
|
|
152
|
+
import type { ZLinkDomainLocationStore as ZLinkLocationStore } from '../locations/domain-store-contract';
|
|
153
|
+
import type {
|
|
154
|
+
ZLinkAuthorityKey,
|
|
155
|
+
ZLinkAuthorityScanCursor,
|
|
156
|
+
ZLinkAuthoritySnapshot,
|
|
157
|
+
ZLinkCapacityVector,
|
|
158
|
+
ZLinkLocationOwnerToken
|
|
159
|
+
} from '../locations/internal-location-contracts';
|
|
160
|
+
import type { ZLinkTrackedInstanceAuthority } from '../locations/spot-location-claims';
|
|
161
|
+
import { decodeRoutingId, encodeRoutingIdStorageHex, routingIdsEqual } from '../routing-id';
|
|
162
|
+
import type { DefaultZLinkSpotManager, ZLinkSpotNodeRuntimeManager } from '../spots';
|
|
163
|
+
import type { ZLinkSpotActivation } from '../spots/spot-activation-state';
|
|
164
|
+
import { createProviderInstance } from '../spots/spot-provider';
|
|
165
|
+
import { decodeHandoffBacklog } from '../spots/spot-remote-codec';
|
|
166
|
+
import { committedActorOwnerFence, type ZLinkActorTransferRuntime } from './actor-transfer-runtime';
|
|
156
167
|
import { BoundedReplayMap } from './bounded-replay-map';
|
|
157
|
-
import
|
|
168
|
+
import {
|
|
169
|
+
ZLinkRelocationInFlightBudget,
|
|
170
|
+
ZLinkRelocationPayloadAssembly,
|
|
171
|
+
effectiveActorJoinChunkLimitBytes,
|
|
172
|
+
effectiveRelocationBudgetBytes,
|
|
173
|
+
planRelocationChunks,
|
|
174
|
+
relocationChunkAt
|
|
175
|
+
} from './relocation-direct-transfer';
|
|
158
176
|
import {
|
|
159
177
|
ZLINK_INTERNAL_RELOCATION_INTEGRITY_FAULT_GATE,
|
|
160
178
|
type ZLinkInternalRelocationIntegrityFaultGate
|
|
161
179
|
} from './relocation-integrity-fault-gate';
|
|
162
180
|
import {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
181
|
+
captureRelocationAdapterState,
|
|
182
|
+
restoreRelocationAdapterState,
|
|
183
|
+
type ZLinkRelocationStateAdapterLike
|
|
184
|
+
} from './relocation-state-adapter';
|
|
166
185
|
import {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
186
|
+
decodeServiceRelocationControlRequest,
|
|
187
|
+
decodeServiceRelocationControlResponse,
|
|
188
|
+
encodeServiceRelocationControlRequest,
|
|
189
|
+
encodeServiceRelocationControlResponse,
|
|
190
|
+
type ZLinkServiceRelocationControlRequest,
|
|
191
|
+
type ZLinkServiceRelocationControlResponse
|
|
192
|
+
} from './service-relocation-control';
|
|
193
|
+
const RELOCATION_ATTEMPT_TIMEOUT_MS = 250;
|
|
194
|
+
|
|
195
|
+
const RELOCATION_AUTHORITY_RETRY_DELAY_MS = 25;
|
|
196
|
+
|
|
197
|
+
const RELOCATION_MAX_SEND_ATTEMPTS = 120;
|
|
198
|
+
|
|
199
|
+
const RELOCATION_AUTHORITY_POLL_INTERVAL_MS = 10;
|
|
200
|
+
|
|
201
|
+
const RELOCATION_RESEND_DELAY_MS = 250;
|
|
202
|
+
|
|
203
|
+
const RELOCATION_ABORTABLE_OPERATION_TIMEOUT_MS = 24 * 60 * 60 * 1000;
|
|
204
|
+
|
|
205
|
+
const RELOCATION_OPERATION_TIMEOUT_MS = 30_000;
|
|
171
206
|
|
|
172
207
|
export class ZLinkRelocationStateIncompatibleError extends Error {
|
|
173
208
|
constructor(message: string) {
|
|
@@ -186,6 +221,7 @@ interface ZLinkHostRelocationOptions {
|
|
|
186
221
|
readonly providerResolver?: ZLinkProviderResolver;
|
|
187
222
|
readonly locationStore: () => ZLinkLocationStore | undefined;
|
|
188
223
|
readonly currentOwner: () => ZLinkLocationOwnerToken | undefined;
|
|
224
|
+
readonly targetAdmissionSealed?: () => boolean;
|
|
189
225
|
readonly liveDescriptors: (
|
|
190
226
|
meshName: string,
|
|
191
227
|
signal?: AbortSignal
|
|
@@ -210,6 +246,7 @@ interface ZLinkHostRelocationOptions {
|
|
|
210
246
|
readonly reconcileStatefulAuthorityRoutes?: (signal?: AbortSignal) => Promise<void>;
|
|
211
247
|
readonly runtimeEventPublisher?: ZLinkRuntimeEventPublisher;
|
|
212
248
|
readonly metrics?: ZLinkRuntimeMetrics;
|
|
249
|
+
readonly activationAdmission?: ZLinkActivationAdmission;
|
|
213
250
|
}
|
|
214
251
|
|
|
215
252
|
interface RelocationTargetRequirement {
|
|
@@ -241,6 +278,8 @@ interface LocalStage {
|
|
|
241
278
|
readonly boundaryRelay: ServiceMaintenanceRelocationData[];
|
|
242
279
|
fallback?: ReturnType<typeof setTimeout>;
|
|
243
280
|
finalize?: Promise<void>;
|
|
281
|
+
/** The Restore's activation admission (MeshNode §5.1); held until target commit or abort. */
|
|
282
|
+
readonly releaseActivation: () => void;
|
|
244
283
|
}
|
|
245
284
|
|
|
246
285
|
interface TargetPayloadAssembly {
|
|
@@ -253,11 +292,11 @@ interface TargetPayloadAssembly {
|
|
|
253
292
|
interface SourceCutoverWindow {
|
|
254
293
|
readonly meshName: string;
|
|
255
294
|
readonly targetNodeRid: RoutingId;
|
|
256
|
-
/** Boundary batch frames plus the cutover frame, retained
|
|
295
|
+
/** Boundary batch frames plus the cutover frame, retained until authority settlement. */
|
|
257
296
|
frames?: readonly Buffer[];
|
|
297
|
+
/** A failed submit or a lost target connection requires the whole batch again. */
|
|
298
|
+
resendPending: boolean;
|
|
258
299
|
readonly cutoverSubmittedAtMs: number;
|
|
259
|
-
windowTimer?: ReturnType<typeof setTimeout>;
|
|
260
|
-
retryTimer?: ReturnType<typeof setTimeout>;
|
|
261
300
|
followTimer?: ReturnType<typeof setTimeout>;
|
|
262
301
|
windowClosed: boolean;
|
|
263
302
|
followExpired: boolean;
|
|
@@ -272,7 +311,6 @@ interface TargetRelocationOffer {
|
|
|
272
311
|
readonly prepareFingerprint: string;
|
|
273
312
|
readonly authenticatedSourceNodeRid: string;
|
|
274
313
|
readonly envelope: ServiceRelocationEnvelope;
|
|
275
|
-
readonly restoreDeadlineAtMs: number;
|
|
276
314
|
readonly reservation: TargetRelocationReservation;
|
|
277
315
|
}
|
|
278
316
|
|
|
@@ -302,7 +340,7 @@ interface SourceActorJoinProfile {
|
|
|
302
340
|
Parameters<ZLinkActorJoinRelocation['relocateActorJoin']>[0]['canonicalRecovery']
|
|
303
341
|
>;
|
|
304
342
|
readonly ready: Promise<void>;
|
|
305
|
-
readonly resolveReady: () => void;
|
|
343
|
+
readonly resolveReady: (failure?: ServiceRelocationPostCommitError) => void;
|
|
306
344
|
}
|
|
307
345
|
|
|
308
346
|
interface PendingRelocationReplyRelay {
|
|
@@ -361,8 +399,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
361
399
|
* Terminal restore failures (assembly/checksum/factory/restore error): the
|
|
362
400
|
* target already sent relocationFailed (command 53) once. An exact-identity
|
|
363
401
|
* Prepare resend replays the stored response rather than retrying the
|
|
364
|
-
* restore
|
|
365
|
-
* key supersedes the stale entry (spec 28 §3, §9).
|
|
402
|
+
* restore. A different exact identity never changes this entry (spec 28 §4.3).
|
|
366
403
|
*/
|
|
367
404
|
private readonly targetReadyFailures = new Map<
|
|
368
405
|
string,
|
|
@@ -419,9 +456,12 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
419
456
|
);
|
|
420
457
|
}
|
|
421
458
|
this.reserveExactRelocationId(input.relocationId);
|
|
422
|
-
let resolveReady!: () => void;
|
|
423
|
-
const ready = new Promise<void>((resolve) => {
|
|
424
|
-
resolveReady =
|
|
459
|
+
let resolveReady!: (failure?: ServiceRelocationPostCommitError) => void;
|
|
460
|
+
const ready = new Promise<void>((resolve, reject) => {
|
|
461
|
+
resolveReady = (failure) => {
|
|
462
|
+
if (failure === undefined) resolve();
|
|
463
|
+
else reject(failure.cause);
|
|
464
|
+
};
|
|
425
465
|
});
|
|
426
466
|
this.sourceActorJoinProfiles.set(input.relocationId, {
|
|
427
467
|
actor: input.actor,
|
|
@@ -433,8 +473,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
433
473
|
relocationContentType:
|
|
434
474
|
this.actorRegistration(input.state.meshName ?? input.meshName, input.state.actorType ?? '')
|
|
435
475
|
.relocation.kind === 'snapshot'
|
|
436
|
-
?
|
|
437
|
-
:
|
|
476
|
+
? SNAPSHOT_RELOCATION_CONTENT_TYPE
|
|
477
|
+
: RECREATE_RELOCATION_CONTENT_TYPE,
|
|
438
478
|
completionOperationId: input.completionOperationId,
|
|
439
479
|
canonicalRecovery: input.canonicalRecovery,
|
|
440
480
|
ready,
|
|
@@ -464,8 +504,13 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
464
504
|
);
|
|
465
505
|
resolveReady();
|
|
466
506
|
} catch (error) {
|
|
467
|
-
|
|
468
|
-
|
|
507
|
+
if (error instanceof ServiceRelocationPostCommitError) {
|
|
508
|
+
resolveReady(error);
|
|
509
|
+
this.completeActorJoinSourceCleanup(input.state.actorId);
|
|
510
|
+
} else {
|
|
511
|
+
this.sourceActorJoinProfiles.delete(input.relocationId);
|
|
512
|
+
throw error;
|
|
513
|
+
}
|
|
469
514
|
}
|
|
470
515
|
return {
|
|
471
516
|
actorRef: {
|
|
@@ -481,17 +526,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
481
526
|
async dispose(): Promise<void> {
|
|
482
527
|
this.disposed = true;
|
|
483
528
|
const errors: unknown[] = [];
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
await stage.finalize;
|
|
489
|
-
} else if (stage.phase === 'ready') {
|
|
490
|
-
await this.abortTargetStage(stage);
|
|
491
|
-
}
|
|
492
|
-
} catch (error) {
|
|
493
|
-
errors.push(error);
|
|
494
|
-
}
|
|
529
|
+
try {
|
|
530
|
+
await this.drainTargetAttempts();
|
|
531
|
+
} catch (error) {
|
|
532
|
+
errors.push(error);
|
|
495
533
|
}
|
|
496
534
|
this.targetStages.clear();
|
|
497
535
|
this.terminalTargets.clear();
|
|
@@ -502,8 +540,6 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
502
540
|
this.targetReadyFailures.clear();
|
|
503
541
|
this.targetReadyResponses.clear();
|
|
504
542
|
for (const window of this.sourceCutoverWindows.values()) {
|
|
505
|
-
if (window.windowTimer !== undefined) clearTimeout(window.windowTimer);
|
|
506
|
-
if (window.retryTimer !== undefined) clearTimeout(window.retryTimer);
|
|
507
543
|
if (window.followTimer !== undefined) clearTimeout(window.followTimer);
|
|
508
544
|
}
|
|
509
545
|
this.sourceCutoverWindows.clear();
|
|
@@ -531,24 +567,78 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
531
567
|
if (errors.length > 1) throw new AggregateError(errors, 'Relocation runtime stop failed.');
|
|
532
568
|
}
|
|
533
569
|
|
|
570
|
+
/** Host shutdown waits for the existing attempt terminal before releasing its lease. */
|
|
571
|
+
async drainTargetAttempts(): Promise<void> {
|
|
572
|
+
const results = await Promise.allSettled(
|
|
573
|
+
[...this.targetStages.entries()].map(([stagingId, stage]) => {
|
|
574
|
+
if (stage.fallback !== undefined) clearTimeout(stage.fallback);
|
|
575
|
+
return stage.lane.finally(() => {
|
|
576
|
+
if (stage.phase === 'failed') return stage.finalize;
|
|
577
|
+
if (this.targetStageSealed(stage)) {
|
|
578
|
+
const terminal = this.discardTargetStage(stagingId, stage);
|
|
579
|
+
stage.finalize = terminal;
|
|
580
|
+
return terminal;
|
|
581
|
+
}
|
|
582
|
+
return this.beginTargetFinalize(
|
|
583
|
+
stage.offer.reservation.prepared.plan.targetDescriptor.meshName,
|
|
584
|
+
stagingId,
|
|
585
|
+
stage
|
|
586
|
+
);
|
|
587
|
+
});
|
|
588
|
+
})
|
|
589
|
+
);
|
|
590
|
+
const errors = results.flatMap((result) =>
|
|
591
|
+
result.status === 'rejected' ? [result.reason as unknown] : []
|
|
592
|
+
);
|
|
593
|
+
if (errors.length === 1) throw errors[0];
|
|
594
|
+
if (errors.length > 1) throw new AggregateError(errors, 'Relocation target drain failed.');
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/** Spec 28 §4.4: the target owner alone orders shutdown seal and verified cutover. */
|
|
598
|
+
private targetStageSealed(stage: LocalStage): boolean {
|
|
599
|
+
return (
|
|
600
|
+
!stage.cutoverReceived && (this.disposed || this.options.targetAdmissionSealed?.() === true)
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
|
|
534
604
|
completeActorJoinSourceCleanup(actorId: string): void {
|
|
535
605
|
for (const [relocationId, profile] of this.sourceActorJoinProfiles) {
|
|
536
606
|
if (profile.state.actorId !== actorId) continue;
|
|
537
607
|
this.sourceActorJoinProfiles.delete(relocationId);
|
|
538
608
|
this.terminalActorJoinSourceLeaves.remember(relocationId, actorId);
|
|
539
|
-
void profile
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
609
|
+
void this.finalizeActorJoinSource(profile);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
private async finalizeActorJoinSource(profile: SourceActorJoinProfile): Promise<void> {
|
|
614
|
+
const failures: unknown[] = [];
|
|
615
|
+
try {
|
|
616
|
+
await profile.ready;
|
|
617
|
+
} catch (error) {
|
|
618
|
+
failures.push(error);
|
|
619
|
+
}
|
|
620
|
+
try {
|
|
621
|
+
await this.options.actorTransfer.completeRelocationSourceLeave(
|
|
622
|
+
profile.actor,
|
|
623
|
+
profile.sourceSpotId
|
|
624
|
+
);
|
|
625
|
+
} catch (error) {
|
|
626
|
+
failures.push(error);
|
|
627
|
+
}
|
|
628
|
+
try {
|
|
629
|
+
await this.options
|
|
630
|
+
.actorManager()
|
|
631
|
+
?.completeRelocationSource(profile.state.actorId, profile.sourceActorRef);
|
|
632
|
+
} catch (error) {
|
|
633
|
+
failures.push(error);
|
|
634
|
+
}
|
|
635
|
+
if (failures.length > 0) {
|
|
636
|
+
console.error(
|
|
637
|
+
'[zlink.runtime.relocation.source_cleanup_failed]',
|
|
638
|
+
failures.length === 1
|
|
639
|
+
? failures[0]
|
|
640
|
+
: new AggregateError(failures, 'Actor source retirement failed.')
|
|
641
|
+
);
|
|
552
642
|
}
|
|
553
643
|
}
|
|
554
644
|
|
|
@@ -661,22 +751,59 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
661
751
|
publishState: () => undefined,
|
|
662
752
|
forceStop: () => undefined
|
|
663
753
|
});
|
|
754
|
+
const started: Array<Promise<void>> = [];
|
|
664
755
|
for (const unit of work) {
|
|
665
756
|
maintenance.enqueue({
|
|
666
757
|
id: unit.id,
|
|
667
758
|
ready: () => true,
|
|
668
|
-
relocate:
|
|
759
|
+
relocate: (relocationSignal) => {
|
|
760
|
+
const run = unit.relocate(relocationSignal);
|
|
761
|
+
if (isStructuralGuardEnabled()) {
|
|
762
|
+
trackDiagnosticCompletion(run, `relocation ${unit.id}`);
|
|
763
|
+
}
|
|
764
|
+
started.push(run);
|
|
765
|
+
return run;
|
|
766
|
+
}
|
|
669
767
|
});
|
|
670
768
|
}
|
|
671
769
|
const operationSignal = signal ?? new AbortController().signal;
|
|
672
|
-
const deadlineMs =
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
770
|
+
const deadlineMs =
|
|
771
|
+
signal === undefined
|
|
772
|
+
? RELOCATION_OPERATION_TIMEOUT_MS
|
|
773
|
+
: RELOCATION_ABORTABLE_OPERATION_TIMEOUT_MS;
|
|
774
|
+
let failure: unknown;
|
|
775
|
+
try {
|
|
776
|
+
const result = await raceAbort(
|
|
777
|
+
maintenance.start('retire', deadlineMs, stopStartingSignal, operationSignal),
|
|
778
|
+
operationSignal
|
|
779
|
+
);
|
|
780
|
+
if (result.state === 'completed') return;
|
|
781
|
+
failure = result.terminalError ?? new Error(`Host relocation ended in '${result.state}'.`);
|
|
782
|
+
} catch (error) {
|
|
783
|
+
failure = error;
|
|
784
|
+
}
|
|
785
|
+
// The host result follows the settled authority of every started unit
|
|
786
|
+
// (spec 30 §13): a unit completes only after its target commit is
|
|
787
|
+
// confirmed, so a failure beside a completed unit leaves units on both
|
|
788
|
+
// sides.
|
|
789
|
+
const settled = await Promise.allSettled(started);
|
|
790
|
+
const targetSide = settled.filter(
|
|
791
|
+
(value) =>
|
|
792
|
+
value.status === 'fulfilled' || value.reason instanceof ServiceRelocationPostCommitError
|
|
793
|
+
).length;
|
|
794
|
+
const authorityError = settled.find(
|
|
795
|
+
(value): value is PromiseRejectedResult =>
|
|
796
|
+
value.status === 'rejected' && value.reason instanceof ServiceRelocationAuthorityError
|
|
676
797
|
);
|
|
677
|
-
if (
|
|
678
|
-
|
|
798
|
+
if (authorityError !== undefined) throw authorityError.reason;
|
|
799
|
+
// A unit that never started also stays on the source.
|
|
800
|
+
if (targetSide > 0 && targetSide < work.length) {
|
|
801
|
+
throw new ServiceRelocationAuthorityError(
|
|
802
|
+
'Relocation units settled on both the source and the target.',
|
|
803
|
+
{ cause: failure }
|
|
804
|
+
);
|
|
679
805
|
}
|
|
806
|
+
throw failure;
|
|
680
807
|
}
|
|
681
808
|
|
|
682
809
|
async tryHandleControl(
|
|
@@ -764,47 +891,57 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
764
891
|
}
|
|
765
892
|
const operationKey = relocationStagingId(request);
|
|
766
893
|
const fingerprint = stringifyWire(request);
|
|
894
|
+
const readyFailure = this.targetReadyFailures.get(operationKey);
|
|
895
|
+
const readyPending = this.targetReadyResponses.get(operationKey);
|
|
896
|
+
let operation = this.targetPrepareOperations.get(operationKey);
|
|
897
|
+
const acceptedFingerprint =
|
|
898
|
+
readyFailure?.fingerprint ??
|
|
899
|
+
readyPending?.fingerprint ??
|
|
900
|
+
operation?.fingerprint ??
|
|
901
|
+
this.targetAssemblies.get(operationKey)?.fingerprint ??
|
|
902
|
+
this.targetStages.get(operationKey)?.offer.prepareFingerprint ??
|
|
903
|
+
this.terminalTargets.get(operationKey);
|
|
904
|
+
if (request.sourceNodeRid !== String(record.sourceNodeRid)) return true;
|
|
905
|
+
if (acceptedFingerprint !== undefined && acceptedFingerprint !== fingerprint) {
|
|
906
|
+
if (!samePrepareIdentity(acceptedFingerprint, request)) return true;
|
|
907
|
+
this.replyPrepare(
|
|
908
|
+
record,
|
|
909
|
+
relocationFailed(request, ServiceWireFrameworkErrorCode.relocationDataLost)
|
|
910
|
+
);
|
|
911
|
+
return true;
|
|
912
|
+
}
|
|
767
913
|
// A restore that failed outright is terminal: the source observes the
|
|
768
914
|
// same explicit relocationFailed on every identical Prepare resend
|
|
769
|
-
// rather than a silent stall.
|
|
770
|
-
// same RelocationId+attempt key supersedes the stale failure — the
|
|
771
|
-
// newer attempt wins (spec 28 §3).
|
|
772
|
-
const readyFailure = this.targetReadyFailures.get(operationKey);
|
|
915
|
+
// rather than a silent stall.
|
|
773
916
|
if (readyFailure !== undefined) {
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
return true;
|
|
777
|
-
}
|
|
778
|
-
this.targetReadyFailures.delete(operationKey);
|
|
917
|
+
this.replyPrepare(record, readyFailure.response);
|
|
918
|
+
return true;
|
|
779
919
|
}
|
|
780
920
|
// Spec 28: an exact-identity Restore resend against staging that
|
|
781
921
|
// already restored successfully reuses that staging — it re-submits
|
|
782
922
|
// READY rather than restarting the restore.
|
|
783
|
-
const readyPending = this.targetReadyResponses.get(operationKey);
|
|
784
923
|
if (readyPending !== undefined) {
|
|
785
|
-
if (readyPending.fingerprint !== fingerprint) {
|
|
786
|
-
throw new Error(`Relocation '${operationKey}' repeated Prepare with different bytes.`);
|
|
787
|
-
}
|
|
788
924
|
this.submitTargetReady(meshName, operationKey, record, readyPending.response);
|
|
789
925
|
return true;
|
|
790
926
|
}
|
|
791
|
-
let operation = this.targetPrepareOperations.get(operationKey);
|
|
792
|
-
if (operation !== undefined && operation.fingerprint !== fingerprint) {
|
|
793
|
-
throw new Error(`Relocation '${operationKey}' repeated Prepare with different bytes.`);
|
|
794
|
-
}
|
|
795
927
|
if (operation === undefined) {
|
|
796
928
|
// The prepare turn must not block the ordered dispatch lane: the
|
|
797
929
|
// payload chunks that complete this operation arrive as later records
|
|
798
930
|
// on the same connection. Register the assembly synchronously, then
|
|
799
931
|
// finish the restore and the Ready reply asynchronously.
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
932
|
+
let promise: Promise<ServiceMaintenanceRelocationReady>;
|
|
933
|
+
try {
|
|
934
|
+
this.registerTargetAssembly(operationKey, request, String(record.sourceNodeRid));
|
|
935
|
+
promise = this.handlePrepareControl(
|
|
936
|
+
meshName,
|
|
937
|
+
operationKey,
|
|
938
|
+
request,
|
|
939
|
+
record.sourceNodeRid,
|
|
940
|
+
signal
|
|
941
|
+
);
|
|
942
|
+
} catch (error) {
|
|
943
|
+
promise = Promise.reject(error);
|
|
944
|
+
}
|
|
808
945
|
operation = { fingerprint, promise };
|
|
809
946
|
this.targetPrepareOperations.set(operationKey, operation);
|
|
810
947
|
void promise
|
|
@@ -838,10 +975,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
838
975
|
// restore or staging): this is terminal. Send relocationFailed
|
|
839
976
|
// (command 53) once and remember it so an identical Prepare resend
|
|
840
977
|
// replays the same response instead of retrying the restore.
|
|
841
|
-
const failed = relocationFailed(
|
|
842
|
-
request,
|
|
843
|
-
relocationFailedFailureCode(error, request.object.kind)
|
|
844
|
-
);
|
|
978
|
+
const failed = relocationFailed(request, relocationFailedFailureCode(error));
|
|
845
979
|
this.targetReadyFailures.set(operationKey, {
|
|
846
980
|
fingerprint: stringifyWire(request),
|
|
847
981
|
response: failed
|
|
@@ -935,7 +1069,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
935
1069
|
String(sourceNodeRid) !== pending.authenticatedSourceNodeRid ||
|
|
936
1070
|
request.senderRole !== 'source' ||
|
|
937
1071
|
!sameCoordinator(request.coordinator, pending.prepare.coordinator) ||
|
|
938
|
-
|
|
1072
|
+
!sameRelocationObject(request.object, pending.prepare.object)
|
|
939
1073
|
) {
|
|
940
1074
|
// Spec 28 §4.3 — a chunk with a different exact identity is never
|
|
941
1075
|
// attached to an in-progress assembly; it is discarded.
|
|
@@ -959,14 +1093,6 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
959
1093
|
): TargetPayloadAssembly {
|
|
960
1094
|
const existing = this.targetAssemblies.get(operationKey);
|
|
961
1095
|
if (existing !== undefined) {
|
|
962
|
-
if (existing.fingerprint !== stringifyWire(request)) {
|
|
963
|
-
// Same exact identity with a different declared length or checksum is
|
|
964
|
-
// an explicit conflict failure; the existing assembly is neither
|
|
965
|
-
// reused nor overwritten (spec 28 §4.3).
|
|
966
|
-
throw existing.assembly.fail(
|
|
967
|
-
`Relocation '${operationKey}' repeated Prepare with a conflicting manifest.`
|
|
968
|
-
);
|
|
969
|
-
}
|
|
970
1096
|
return existing;
|
|
971
1097
|
}
|
|
972
1098
|
const pending: TargetPayloadAssembly = {
|
|
@@ -981,15 +1107,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
981
1107
|
};
|
|
982
1108
|
this.targetAssemblies.set(operationKey, pending);
|
|
983
1109
|
if (this.relocationIntegrityFaultGate()?.consumeIdentityConflict() === true) {
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
{
|
|
989
|
-
...request,
|
|
990
|
-
payloadChecksumCrc32c: request.payloadChecksumCrc32c ^ 1
|
|
991
|
-
},
|
|
992
|
-
authenticatedSourceNodeRid
|
|
1110
|
+
throw new ServiceRelocationDataLostError(
|
|
1111
|
+
pending.assembly.fail(
|
|
1112
|
+
`Relocation '${operationKey}' repeated Prepare with a conflicting manifest.`
|
|
1113
|
+
).message
|
|
993
1114
|
);
|
|
994
1115
|
}
|
|
995
1116
|
return pending;
|
|
@@ -1167,7 +1288,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1167
1288
|
return;
|
|
1168
1289
|
}
|
|
1169
1290
|
if (this.pendingSessionRelocations.get(key) === pending) {
|
|
1170
|
-
pending.retryTimer = setTimeout(() => void send(),
|
|
1291
|
+
pending.retryTimer = setTimeout(() => void send(), RELOCATION_RESEND_DELAY_MS);
|
|
1171
1292
|
}
|
|
1172
1293
|
} catch (error) {
|
|
1173
1294
|
pending.reject(
|
|
@@ -1385,6 +1506,28 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1385
1506
|
target: ZLinkMeshNodeDescriptor | undefined,
|
|
1386
1507
|
targetApplicationVersion: bigint | undefined,
|
|
1387
1508
|
signal?: AbortSignal
|
|
1509
|
+
): Promise<void> {
|
|
1510
|
+
await activation.serial.executeControlLifecycleOperation(() =>
|
|
1511
|
+
this.relocateSpotAggregateCore(
|
|
1512
|
+
meshName,
|
|
1513
|
+
activation,
|
|
1514
|
+
kind,
|
|
1515
|
+
actorStates,
|
|
1516
|
+
target,
|
|
1517
|
+
targetApplicationVersion,
|
|
1518
|
+
signal
|
|
1519
|
+
)
|
|
1520
|
+
);
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
private async relocateSpotAggregateCore(
|
|
1524
|
+
meshName: string,
|
|
1525
|
+
activation: ZLinkSpotActivation,
|
|
1526
|
+
kind: 'user_spot' | 'instance_spot',
|
|
1527
|
+
actorStates: readonly ZLinkActorRuntimeState[],
|
|
1528
|
+
target: ZLinkMeshNodeDescriptor | undefined,
|
|
1529
|
+
targetApplicationVersion: bigint | undefined,
|
|
1530
|
+
signal?: AbortSignal
|
|
1388
1531
|
): Promise<void> {
|
|
1389
1532
|
const store = this.requireLocationStore();
|
|
1390
1533
|
const spotKey = encodeAuthorityKey(kind, String(activation.spotId));
|
|
@@ -1441,12 +1584,16 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1441
1584
|
objectGeneration: spotAuthority.objectGeneration,
|
|
1442
1585
|
authorityOwnerGeneration: spotAuthority.authorityOwnerGeneration,
|
|
1443
1586
|
seal: async (captureSignal) => {
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1587
|
+
const { spotCaptureOperation } = await this.afterSpotCloseDecision(
|
|
1588
|
+
meshName,
|
|
1589
|
+
activation.spotId,
|
|
1590
|
+
() => {
|
|
1591
|
+
interruptionStartedAt = performance.now();
|
|
1592
|
+
spotMessageFollowSeal = this.sealSpotMessageFollow(meshName, spotAuthority, activation);
|
|
1593
|
+
// Both seals run in the same event-loop turn after the Close decision.
|
|
1594
|
+
return { spotCaptureOperation: activation.captureRelocation(captureSignal) };
|
|
1595
|
+
}
|
|
1596
|
+
);
|
|
1450
1597
|
const preparedSessions = Promise.all(
|
|
1451
1598
|
actorStates.map(async (state) => ({
|
|
1452
1599
|
state,
|
|
@@ -1477,47 +1624,72 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1477
1624
|
captureApplicationState: (captureSignal) =>
|
|
1478
1625
|
this.captureApplication(spotRegistration.relocation, activation.spot, captureSignal),
|
|
1479
1626
|
commitSeal: async () => {
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1627
|
+
const failures: unknown[] = [];
|
|
1628
|
+
try {
|
|
1629
|
+
if (!spotSealCommitted) {
|
|
1630
|
+
if (spotCapture === undefined || !(await activation.commitRelocation(spotCapture))) {
|
|
1631
|
+
throw new Error(`Spot '${String(activation.spotId)}' relocation seal became stale.`);
|
|
1632
|
+
}
|
|
1633
|
+
spotSealCommitted = true;
|
|
1483
1634
|
}
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1635
|
+
if (spotMessageFollowSeal !== undefined) {
|
|
1636
|
+
await this.commitSpotMessageFollow(
|
|
1637
|
+
meshName,
|
|
1638
|
+
spotMessageFollowSeal,
|
|
1639
|
+
spotKey,
|
|
1640
|
+
activation,
|
|
1641
|
+
target
|
|
1642
|
+
);
|
|
1643
|
+
}
|
|
1644
|
+
} catch (error) {
|
|
1645
|
+
failures.push(error);
|
|
1494
1646
|
}
|
|
1495
1647
|
for (const session of sessions) {
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1648
|
+
try {
|
|
1649
|
+
const committedAuthority = await requireAuthority(
|
|
1650
|
+
this.requireLocationStore(),
|
|
1651
|
+
encodeAuthorityKey('actor', session.state.actorId)
|
|
1652
|
+
);
|
|
1653
|
+
const targetActorRef = {
|
|
1654
|
+
actorId: session.state.actorId,
|
|
1655
|
+
objectGeneration: actorAuthorities.get(session.state.actorId)!.objectGeneration,
|
|
1656
|
+
meshName,
|
|
1657
|
+
nodeRid: target.rid
|
|
1658
|
+
};
|
|
1659
|
+
await session.prepared.commit(
|
|
1660
|
+
{
|
|
1661
|
+
routerChannelId: meshName,
|
|
1662
|
+
targetNodeRid: target.rid,
|
|
1663
|
+
spotId: activation.spotId,
|
|
1664
|
+
spotKind: kind === 'user_spot' ? ZLinkSpotKind.User : ZLinkSpotKind.Instance,
|
|
1665
|
+
authorityOwnerGeneration: committedAuthority.authorityOwnerGeneration
|
|
1666
|
+
} as never,
|
|
1667
|
+
targetActorRef,
|
|
1668
|
+
committedActorOwnerFence(session.state.actorId, targetActorRef, committedAuthority)
|
|
1669
|
+
);
|
|
1670
|
+
} catch (error) {
|
|
1671
|
+
failures.push(error);
|
|
1672
|
+
}
|
|
1673
|
+
try {
|
|
1674
|
+
activation.commitActorDeparture(session.state.actorId);
|
|
1675
|
+
} catch (error) {
|
|
1676
|
+
failures.push(error);
|
|
1677
|
+
}
|
|
1678
|
+
try {
|
|
1679
|
+
await this.requireActorManager().completeRelocationSource(session.state.actorId);
|
|
1680
|
+
} catch (error) {
|
|
1681
|
+
failures.push(error);
|
|
1682
|
+
}
|
|
1519
1683
|
}
|
|
1520
|
-
|
|
1684
|
+
try {
|
|
1685
|
+
await this.requireSpotManager().completeRelocationSource(activation);
|
|
1686
|
+
} catch (error) {
|
|
1687
|
+
failures.push(error);
|
|
1688
|
+
}
|
|
1689
|
+
if (failures.length > 0)
|
|
1690
|
+
throw failures.length === 1
|
|
1691
|
+
? failures[0]
|
|
1692
|
+
: new AggregateError(failures, 'Spot source retirement failed.');
|
|
1521
1693
|
},
|
|
1522
1694
|
abortSeal: async () => {
|
|
1523
1695
|
const failures: unknown[] = [];
|
|
@@ -1623,7 +1795,11 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1623
1795
|
);
|
|
1624
1796
|
} catch (error) {
|
|
1625
1797
|
outcome = 'failed';
|
|
1626
|
-
if (
|
|
1798
|
+
if (
|
|
1799
|
+
readinessBoundaryConsumed &&
|
|
1800
|
+
!(error instanceof ServiceRelocationPostCommitError) &&
|
|
1801
|
+
!(error instanceof ServiceRelocationAuthorityError)
|
|
1802
|
+
) {
|
|
1627
1803
|
await activation
|
|
1628
1804
|
.completeConsumedRelocationBoundary(ZLinkSpotRelocationReadyOutcome.Continued)
|
|
1629
1805
|
.catch(() => undefined);
|
|
@@ -1659,7 +1835,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1659
1835
|
/** Target's advertised relocation state chunk cap (Actor Join only; spec 15 §4.2). */
|
|
1660
1836
|
advertisedReceiveChunkLimitBytes?: number
|
|
1661
1837
|
): Promise<void> {
|
|
1662
|
-
|
|
1838
|
+
if (isRelocationDebugEnabled()) {
|
|
1839
|
+
relocationDebug('standalone_actor.begin', { actorId: state.actorId });
|
|
1840
|
+
}
|
|
1663
1841
|
const authority = await requireAuthority(
|
|
1664
1842
|
this.requireLocationStore(),
|
|
1665
1843
|
encodeAuthorityKey('actor', state.actorId),
|
|
@@ -1716,12 +1894,16 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1716
1894
|
};
|
|
1717
1895
|
let outcome = 'completed';
|
|
1718
1896
|
try {
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1897
|
+
if (isRelocationDebugEnabled()) {
|
|
1898
|
+
relocationDebug('standalone_actor.wait_budget', {
|
|
1899
|
+
actorId: state.actorId,
|
|
1900
|
+
targetRid: String(target.rid)
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1723
1903
|
await this.waitForRelocationBudgetHeadroom(String(target.rid), signal);
|
|
1724
|
-
|
|
1904
|
+
if (isRelocationDebugEnabled()) {
|
|
1905
|
+
relocationDebug('standalone_actor.capture_begin', { actorId: state.actorId });
|
|
1906
|
+
}
|
|
1725
1907
|
const captured = await new ServiceRelocationObjectCaptureOwner().captureStandaloneActor(
|
|
1726
1908
|
aggregateId,
|
|
1727
1909
|
1n,
|
|
@@ -1729,7 +1911,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1729
1911
|
membership,
|
|
1730
1912
|
signal
|
|
1731
1913
|
);
|
|
1732
|
-
|
|
1914
|
+
if (isRelocationDebugEnabled()) {
|
|
1915
|
+
relocationDebug('standalone_actor.capture_complete', { actorId: state.actorId });
|
|
1916
|
+
}
|
|
1733
1917
|
await this.runCoordinator(
|
|
1734
1918
|
meshName,
|
|
1735
1919
|
target,
|
|
@@ -1763,6 +1947,24 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1763
1947
|
target: ZLinkMeshNodeDescriptor | undefined,
|
|
1764
1948
|
targetApplicationVersion: bigint | undefined,
|
|
1765
1949
|
signal?: AbortSignal
|
|
1950
|
+
): Promise<void> {
|
|
1951
|
+
await activation.serial.executeControlLifecycleOperation(() =>
|
|
1952
|
+
this.relocatePerActorSpotShellCore(
|
|
1953
|
+
meshName,
|
|
1954
|
+
activation,
|
|
1955
|
+
target,
|
|
1956
|
+
targetApplicationVersion,
|
|
1957
|
+
signal
|
|
1958
|
+
)
|
|
1959
|
+
);
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
private async relocatePerActorSpotShellCore(
|
|
1963
|
+
meshName: string,
|
|
1964
|
+
activation: ZLinkSpotActivation,
|
|
1965
|
+
target: ZLinkMeshNodeDescriptor | undefined,
|
|
1966
|
+
targetApplicationVersion: bigint | undefined,
|
|
1967
|
+
signal?: AbortSignal
|
|
1766
1968
|
): Promise<void> {
|
|
1767
1969
|
const spotKey = encodeAuthorityKey('user_spot', String(activation.spotId));
|
|
1768
1970
|
const spotAuthority = await requireAuthority(this.requireLocationStore(), spotKey, signal);
|
|
@@ -1796,9 +1998,16 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1796
1998
|
objectGeneration: spotAuthority.objectGeneration,
|
|
1797
1999
|
authorityOwnerGeneration: spotAuthority.authorityOwnerGeneration,
|
|
1798
2000
|
seal: async (captureSignal) => {
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
2001
|
+
const { spotCaptureOperation } = await this.afterSpotCloseDecision(
|
|
2002
|
+
meshName,
|
|
2003
|
+
activation.spotId,
|
|
2004
|
+
() => {
|
|
2005
|
+
interruptionStartedAt = performance.now();
|
|
2006
|
+
spotMessageFollowSeal = this.sealSpotMessageFollow(meshName, spotAuthority, activation);
|
|
2007
|
+
return { spotCaptureOperation: activation.captureRelocation(captureSignal) };
|
|
2008
|
+
}
|
|
2009
|
+
);
|
|
2010
|
+
spotCapture = await spotCaptureOperation;
|
|
1802
2011
|
return {
|
|
1803
2012
|
boundSessionState: Buffer.alloc(0),
|
|
1804
2013
|
queuedMessages: [],
|
|
@@ -1937,36 +2146,54 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1937
2146
|
meshName === undefined
|
|
1938
2147
|
)
|
|
1939
2148
|
return;
|
|
1940
|
-
const
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
2149
|
+
const failures: unknown[] = [];
|
|
2150
|
+
try {
|
|
2151
|
+
const membershipSpotId =
|
|
2152
|
+
targetMembership?.spotId ?? ((target.entrySpotId ?? String(target.rid)) as RoutingId);
|
|
2153
|
+
const committedAuthority = await requireAuthority(
|
|
2154
|
+
this.requireLocationStore(),
|
|
2155
|
+
encodeAuthorityKey('actor', state.actorId)
|
|
2156
|
+
);
|
|
2157
|
+
const targetActorRef = {
|
|
2158
|
+
actorId: state.actorId,
|
|
2159
|
+
objectGeneration: authority.objectGeneration,
|
|
2160
|
+
meshName,
|
|
2161
|
+
nodeRid: target.rid
|
|
2162
|
+
};
|
|
2163
|
+
await ownSession.prepared.commit(
|
|
2164
|
+
{
|
|
2165
|
+
routerChannelId: meshName,
|
|
2166
|
+
targetNodeRid: target.rid,
|
|
2167
|
+
spotId: membershipSpotId,
|
|
2168
|
+
spotKind: targetMembership?.spotKind ?? ZLinkSpotKind.Entry,
|
|
2169
|
+
authorityOwnerGeneration: committedAuthority.authorityOwnerGeneration
|
|
2170
|
+
} as never,
|
|
2171
|
+
targetActorRef,
|
|
2172
|
+
committedActorOwnerFence(state.actorId, targetActorRef, committedAuthority)
|
|
2173
|
+
);
|
|
2174
|
+
} catch (error) {
|
|
2175
|
+
failures.push(error);
|
|
2176
|
+
}
|
|
1963
2177
|
if (!retainSourceForActorJoin) {
|
|
1964
|
-
|
|
2178
|
+
try {
|
|
2179
|
+
await this.requireActorManager().completeRelocationSource(state.actorId);
|
|
2180
|
+
} catch (error) {
|
|
2181
|
+
failures.push(error);
|
|
2182
|
+
}
|
|
1965
2183
|
}
|
|
2184
|
+
if (failures.length > 0)
|
|
2185
|
+
throw failures.length === 1
|
|
2186
|
+
? failures[0]
|
|
2187
|
+
: new AggregateError(failures, 'Actor source retirement failed.');
|
|
1966
2188
|
},
|
|
1967
2189
|
abortSeal: async () => {
|
|
1968
2190
|
if (standalone && ownSession !== undefined) await ownSession.prepared.rollback();
|
|
1969
|
-
}
|
|
2191
|
+
},
|
|
2192
|
+
discardSeal: (reason) =>
|
|
2193
|
+
(standalone
|
|
2194
|
+
? ownSession
|
|
2195
|
+
: sessions.find((value) => value.state === state)
|
|
2196
|
+
)?.prepared.discard(reason)
|
|
1970
2197
|
};
|
|
1971
2198
|
}
|
|
1972
2199
|
|
|
@@ -2004,13 +2231,17 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2004
2231
|
/** Target's advertised relocation state chunk cap (Actor Join only; spec 15 §4.2). */
|
|
2005
2232
|
advertisedReceiveChunkLimitBytes?: number
|
|
2006
2233
|
): Promise<void> {
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2234
|
+
if (isRelocationDebugEnabled()) {
|
|
2235
|
+
relocationDebug('coordinator.begin', {
|
|
2236
|
+
aggregateId: captured.envelope.aggregateId,
|
|
2237
|
+
targetRid: String(target.rid)
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
2011
2240
|
const localStatus = this.requireMeshNode(meshName).status();
|
|
2012
2241
|
const controlDeadlineAtMs =
|
|
2013
|
-
signal === undefined
|
|
2242
|
+
signal === undefined
|
|
2243
|
+
? performance.now() + RELOCATION_OPERATION_TIMEOUT_MS
|
|
2244
|
+
: Number.MAX_SAFE_INTEGER;
|
|
2014
2245
|
const coordinator = {
|
|
2015
2246
|
ownerId: primary.ownerId,
|
|
2016
2247
|
leaseGeneration: primary.ownerLeaseGeneration,
|
|
@@ -2067,12 +2298,14 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2067
2298
|
advertisedReceiveChunkLimitBytes
|
|
2068
2299
|
);
|
|
2069
2300
|
const plan = planRelocationChunks(encoded, chunkLimitBytes);
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2301
|
+
// The Restore absolute deadline is this unit's relocation deadline: the
|
|
2302
|
+
// caller's signal, or the control deadline without one. From it the
|
|
2303
|
+
// source settles the authority with its Preserve fence (Location runtime
|
|
2304
|
+
// §10). The target keeps no deadline of its own.
|
|
2305
|
+
const restoreDeadlineReached = () =>
|
|
2306
|
+
signal?.aborted === true || performance.now() >= controlDeadlineAtMs;
|
|
2074
2307
|
let readyReceived = false;
|
|
2075
|
-
let
|
|
2308
|
+
let settlement: 'target' | 'source' | 'lost' | undefined;
|
|
2076
2309
|
try {
|
|
2077
2310
|
const prepare = {
|
|
2078
2311
|
kind: 'prepare',
|
|
@@ -2102,17 +2335,25 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2102
2335
|
encodePrepareSideband(captured.envelope)
|
|
2103
2336
|
);
|
|
2104
2337
|
try {
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2338
|
+
if (isRelocationDebugEnabled()) {
|
|
2339
|
+
relocationDebug('coordinator.state_transfer_begin', {
|
|
2340
|
+
aggregateId: captured.envelope.aggregateId
|
|
2341
|
+
});
|
|
2342
|
+
}
|
|
2108
2343
|
await this.sendRelocationStateChunks(meshName, target, prepare, encoded, plan, signal);
|
|
2109
2344
|
} catch (chunkError) {
|
|
2110
2345
|
readyOperation.catch(() => undefined);
|
|
2111
2346
|
throw chunkError;
|
|
2112
2347
|
}
|
|
2113
|
-
|
|
2348
|
+
if (isRelocationDebugEnabled()) {
|
|
2349
|
+
relocationDebug('coordinator.await_ready', { aggregateId: captured.envelope.aggregateId });
|
|
2350
|
+
}
|
|
2114
2351
|
const ready = await readyOperation;
|
|
2115
|
-
|
|
2352
|
+
if (isRelocationDebugEnabled()) {
|
|
2353
|
+
relocationDebug('coordinator.ready_received', {
|
|
2354
|
+
aggregateId: captured.envelope.aggregateId
|
|
2355
|
+
});
|
|
2356
|
+
}
|
|
2116
2357
|
validateControlResponse(prepare, ready);
|
|
2117
2358
|
// Ready arms the target's cutover-wait fallback. From this point
|
|
2118
2359
|
// forward the source can no longer reopen dispatch without risking two
|
|
@@ -2169,9 +2410,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2169
2410
|
Buffer.concat(boundaryBatch.map((value) => value.canonicalBytes))
|
|
2170
2411
|
)
|
|
2171
2412
|
});
|
|
2172
|
-
// The
|
|
2173
|
-
//
|
|
2174
|
-
//
|
|
2413
|
+
// The cutover submit result is not the authority result. The memory
|
|
2414
|
+
// copy of the boundary batch and cutover stays until the authority
|
|
2415
|
+
// settlement below (spec 28 §4.4).
|
|
2175
2416
|
let submitFailure: unknown;
|
|
2176
2417
|
try {
|
|
2177
2418
|
for (const record of boundaryBatch) {
|
|
@@ -2182,7 +2423,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2182
2423
|
submitFailure = error;
|
|
2183
2424
|
console.warn('[zlink.runtime.relocation.cutover_submit_failed]', relayAuthorityId, error);
|
|
2184
2425
|
}
|
|
2185
|
-
this.beginSourceCutoverWindow(
|
|
2426
|
+
const window = this.beginSourceCutoverWindow(
|
|
2186
2427
|
meshName,
|
|
2187
2428
|
target.rid,
|
|
2188
2429
|
relayAuthorityId,
|
|
@@ -2190,53 +2431,59 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2190
2431
|
submitFailure !== undefined,
|
|
2191
2432
|
limits
|
|
2192
2433
|
);
|
|
2193
|
-
|
|
2194
|
-
await this.waitForTargetAuthorities(
|
|
2195
|
-
captured.envelope,
|
|
2196
|
-
targetFence,
|
|
2197
|
-
convergenceDeadlineAtMs,
|
|
2198
|
-
signal
|
|
2199
|
-
);
|
|
2200
|
-
relocationDebug('coordinator.target_authorities_observed', {
|
|
2201
|
-
aggregateId: captured.envelope.aggregateId
|
|
2202
|
-
});
|
|
2203
|
-
for (const session of sessions) session.prepared.setReplayResults([]);
|
|
2204
|
-
await captured.commitSource();
|
|
2205
|
-
sourceCommitted = true;
|
|
2206
|
-
await this.options.reconcileStatefulAuthorityRoutes?.(signal);
|
|
2207
|
-
} catch (error) {
|
|
2208
|
-
if (!readyReceived) throw error;
|
|
2209
2434
|
try {
|
|
2210
|
-
await this.
|
|
2435
|
+
settlement = await this.settleSourceAuthority(
|
|
2211
2436
|
captured.envelope,
|
|
2437
|
+
primary,
|
|
2212
2438
|
targetFence,
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
if (!sourceCommitted) {
|
|
2216
|
-
for (const session of sessions) session.prepared.setReplayResults([]);
|
|
2217
|
-
await captured.commitSource();
|
|
2218
|
-
sourceCommitted = true;
|
|
2219
|
-
}
|
|
2220
|
-
await this.options.reconcileStatefulAuthorityRoutes?.();
|
|
2221
|
-
const authority = await requireAuthority(
|
|
2222
|
-
this.requireLocationStore(),
|
|
2223
|
-
{ value: primaryKey(captured.envelope) } as ZLinkAuthorityKey,
|
|
2224
|
-
undefined
|
|
2439
|
+
restoreDeadlineReached,
|
|
2440
|
+
window
|
|
2225
2441
|
);
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2442
|
+
} finally {
|
|
2443
|
+
this.closeSourceCutoverWindow(relayAuthorityId, window);
|
|
2444
|
+
}
|
|
2445
|
+
if (isRelocationDebugEnabled()) {
|
|
2446
|
+
relocationDebug('coordinator.authority_settled', {
|
|
2447
|
+
aggregateId: captured.envelope.aggregateId,
|
|
2448
|
+
settlement
|
|
2449
|
+
});
|
|
2450
|
+
}
|
|
2451
|
+
if (settlement === 'source') {
|
|
2452
|
+
// Preserve won: the captured queue, timers and held records return to
|
|
2453
|
+
// the source queue in their accepted order, and dispatch reopens.
|
|
2454
|
+
await captured.abortSource();
|
|
2455
|
+
throw new Error(
|
|
2456
|
+
`Relocation '${relayAuthorityId}' ended by the source Preserve fence; the source kept the authority.`
|
|
2230
2457
|
);
|
|
2231
|
-
}
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2458
|
+
}
|
|
2459
|
+
if (settlement === 'lost') {
|
|
2460
|
+
// Expired owner: dispatch and Store changes stop for good and every
|
|
2461
|
+
// pending request without a terminal gets one Unavailable.
|
|
2462
|
+
const unavailable = createInternalFrameworkException(
|
|
2463
|
+
ZLinkFrameworkInternalErrorKind.RouteNotConnected,
|
|
2464
|
+
`Relocation '${relayAuthorityId}' source owner lease ended before its authority settled.`,
|
|
2465
|
+
true
|
|
2238
2466
|
);
|
|
2467
|
+
await captured.discardSource(unavailable);
|
|
2468
|
+
throw new ServiceRelocationAuthorityError(unavailable.message, { cause: unavailable });
|
|
2239
2469
|
}
|
|
2470
|
+
for (const session of sessions) session.prepared.setReplayResults([]);
|
|
2471
|
+
await captured.commitSource();
|
|
2472
|
+
await this.options.reconcileStatefulAuthorityRoutes?.(signal);
|
|
2473
|
+
} catch (error) {
|
|
2474
|
+
if (settlement !== 'target') throw error;
|
|
2475
|
+
// Target commit is confirmed; later source cleanup failures cannot move
|
|
2476
|
+
// the authority back (spec 28 §10).
|
|
2477
|
+
const authority = await requireAuthority(
|
|
2478
|
+
this.requireLocationStore(),
|
|
2479
|
+
{ value: primaryKey(captured.envelope) } as ZLinkAuthorityKey,
|
|
2480
|
+
undefined
|
|
2481
|
+
);
|
|
2482
|
+
throw new ServiceRelocationPostCommitError(
|
|
2483
|
+
authority,
|
|
2484
|
+
{ id: `${captured.envelope.aggregateId}:${captured.envelope.aggregateGeneration}` },
|
|
2485
|
+
error
|
|
2486
|
+
);
|
|
2240
2487
|
} finally {
|
|
2241
2488
|
if (!readyReceived) {
|
|
2242
2489
|
// Only an explicit failure before the Ready reply becomes accepted
|
|
@@ -2339,12 +2586,11 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2339
2586
|
}
|
|
2340
2587
|
|
|
2341
2588
|
/**
|
|
2342
|
-
* Starts the source
|
|
2343
|
-
*
|
|
2344
|
-
*
|
|
2345
|
-
*
|
|
2346
|
-
*
|
|
2347
|
-
* route removal point — closes the unit for SafeToShutdown.
|
|
2589
|
+
* Starts the source cutover window after the cutover submit terminal. The
|
|
2590
|
+
* boundary batch and cutover copies live in framework memory (not charged
|
|
2591
|
+
* to the in-flight budget) until the authority settlement closes the
|
|
2592
|
+
* window. S4 — the Message Follow route removal point — closes the unit for
|
|
2593
|
+
* SafeToShutdown.
|
|
2348
2594
|
*/
|
|
2349
2595
|
private beginSourceCutoverWindow(
|
|
2350
2596
|
meshName: string,
|
|
@@ -2353,65 +2599,184 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2353
2599
|
frames: readonly Buffer[],
|
|
2354
2600
|
initialSubmitFailed: boolean,
|
|
2355
2601
|
limits: ReturnType<ZLinkHostServiceRelocationRuntime['relocationLimits']>
|
|
2356
|
-
):
|
|
2602
|
+
): SourceCutoverWindow {
|
|
2357
2603
|
const submittedAt = performance.now();
|
|
2358
2604
|
const window: SourceCutoverWindow = {
|
|
2359
2605
|
meshName,
|
|
2360
2606
|
targetNodeRid,
|
|
2361
2607
|
frames,
|
|
2608
|
+
resendPending: initialSubmitFailed,
|
|
2362
2609
|
cutoverSubmittedAtMs: submittedAt,
|
|
2363
2610
|
windowClosed: false,
|
|
2364
2611
|
followExpired: false
|
|
2365
2612
|
};
|
|
2366
2613
|
this.sourceCutoverWindows.set(relocationKey, window);
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2614
|
+
window.followTimer = setTimeout(() => {
|
|
2615
|
+
window.followExpired = true;
|
|
2616
|
+
// S1→S4 route convergence: the span the source must keep its Message
|
|
2617
|
+
// Follow route (spec 30 §7.1).
|
|
2618
|
+
this.options.metrics?.duration(
|
|
2619
|
+
METRIC_NAMES.RelocationRouteConvergence,
|
|
2620
|
+
Math.max(0, performance.now() - submittedAt) / MILLISECONDS_PER_SECOND,
|
|
2621
|
+
{ mesh_name: meshName }
|
|
2622
|
+
);
|
|
2623
|
+
this.releaseSourceCutoverWindow(relocationKey, window);
|
|
2624
|
+
}, limits.messageFollowDurationMs);
|
|
2625
|
+
window.followTimer.unref();
|
|
2626
|
+
return window;
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
/** The authority settlement ended: the retained batch copy is released. */
|
|
2630
|
+
private closeSourceCutoverWindow(relocationKey: string, window: SourceCutoverWindow): void {
|
|
2631
|
+
window.windowClosed = true;
|
|
2632
|
+
window.frames = undefined;
|
|
2633
|
+
this.releaseSourceCutoverWindow(relocationKey, window);
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
private releaseSourceCutoverWindow(relocationKey: string, window: SourceCutoverWindow): void {
|
|
2637
|
+
if (
|
|
2638
|
+
window.windowClosed &&
|
|
2639
|
+
window.followExpired &&
|
|
2640
|
+
this.sourceCutoverWindows.get(relocationKey) === window
|
|
2641
|
+
) {
|
|
2642
|
+
this.sourceCutoverWindows.delete(relocationKey);
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
|
|
2646
|
+
/**
|
|
2647
|
+
* Sends the whole boundary batch and cutover again after a failed submit
|
|
2648
|
+
* or an observed loss of the target connection (spec 28 §4.4). The target
|
|
2649
|
+
* replaces its partial span with the whole batch.
|
|
2650
|
+
*/
|
|
2651
|
+
private async resendSourceCutover(window: SourceCutoverWindow): Promise<void> {
|
|
2652
|
+
const frames = window.frames;
|
|
2653
|
+
if (frames === undefined) return;
|
|
2654
|
+
const peers = this.requireMeshNode(window.meshName).peers();
|
|
2655
|
+
const peer = peers.find(
|
|
2656
|
+
(value) =>
|
|
2657
|
+
value.routingId !== null && String(value.routingId) === String(window.targetNodeRid)
|
|
2658
|
+
);
|
|
2659
|
+
if (peer !== undefined && peer.state !== MeshPeerRuntimeState.Serving) {
|
|
2660
|
+
window.resendPending = true;
|
|
2661
|
+
return;
|
|
2662
|
+
}
|
|
2663
|
+
if (!window.resendPending) return;
|
|
2664
|
+
try {
|
|
2665
|
+
for (const frame of frames) {
|
|
2666
|
+
await this.submitControlFrame(
|
|
2667
|
+
window.meshName,
|
|
2668
|
+
window.targetNodeRid,
|
|
2669
|
+
frame,
|
|
2670
|
+
'cutover retransmission'
|
|
2671
|
+
);
|
|
2672
|
+
}
|
|
2673
|
+
window.resendPending = false;
|
|
2674
|
+
} catch {
|
|
2675
|
+
// The next settlement round retries the whole batch.
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
|
|
2679
|
+
/**
|
|
2680
|
+
* The one source authority settlement after Ready (Location runtime
|
|
2681
|
+
* §6.1·§10, spec 28 §4.4). It reads the Store until the target commit or
|
|
2682
|
+
* the Restore deadline. From the Restore deadline it runs the source
|
|
2683
|
+
* Preserve fence: a CAS on the StoreVersion expected by target NewOwner
|
|
2684
|
+
* excludes a late target commit. `target`: the
|
|
2685
|
+
* target committed. `source`: Preserve fenced the target out while the
|
|
2686
|
+
* source owner lease was valid. `lost`: the source owner lease ended first
|
|
2687
|
+
* or another authority owns the object. An uncertain Store or lease result
|
|
2688
|
+
* keeps the work retained and repeats the same decision.
|
|
2689
|
+
*/
|
|
2690
|
+
private async settleSourceAuthority(
|
|
2691
|
+
envelope: ServiceRelocationEnvelope,
|
|
2692
|
+
primary: ZLinkAuthoritySnapshot,
|
|
2693
|
+
target: ServiceWireRelocationTarget,
|
|
2694
|
+
restoreDeadlineReached: () => boolean,
|
|
2695
|
+
window: SourceCutoverWindow
|
|
2696
|
+
): Promise<'target' | 'source' | 'lost'> {
|
|
2697
|
+
const owner = { ownerId: primary.ownerId, leaseGeneration: primary.ownerLeaseGeneration };
|
|
2698
|
+
const store = this.requireLocationStore();
|
|
2699
|
+
for (;;) {
|
|
2700
|
+
if (this.disposed) throw new Error('Relocation runtime stopped.');
|
|
2701
|
+
await this.resendSourceCutover(window);
|
|
2702
|
+
const observed = await this.readSourceSettlement(envelope, primary, target).catch(
|
|
2703
|
+
(error: unknown) => {
|
|
2704
|
+
if (!isIndeterminateLocationStoreFailure(error)) throw error;
|
|
2705
|
+
return undefined;
|
|
2706
|
+
}
|
|
2707
|
+
);
|
|
2708
|
+
if (observed?.kind === 'target') return 'target';
|
|
2709
|
+
if (observed?.kind === 'other') return 'lost';
|
|
2710
|
+
if (restoreDeadlineReached() && observed?.kind === 'source') {
|
|
2711
|
+
// An unreadable lease is an uncertain result, not an expiry.
|
|
2712
|
+
if (
|
|
2713
|
+
await this.exactSourceLeaseExpired(owner).catch((error: unknown) => {
|
|
2714
|
+
if (!isIndeterminateLocationStoreFailure(error)) throw error;
|
|
2715
|
+
return false;
|
|
2716
|
+
})
|
|
2717
|
+
)
|
|
2718
|
+
return 'lost';
|
|
2719
|
+
const publication = this.codec.read(observed.current.payload);
|
|
2720
|
+
const preserved = await store
|
|
2721
|
+
.compareExchangeAuthority(
|
|
2722
|
+
{ value: primaryKey(envelope) } as ZLinkAuthorityKey,
|
|
2723
|
+
observed.current.storeVersion,
|
|
2724
|
+
{
|
|
2725
|
+
kind: 'put',
|
|
2726
|
+
generationTransition: 'preserve',
|
|
2727
|
+
payload:
|
|
2728
|
+
publication === undefined
|
|
2729
|
+
? observed.current.payload
|
|
2730
|
+
: this.codec.clear(observed.current.payload, publication.reference)
|
|
2731
|
+
}
|
|
2732
|
+
)
|
|
2733
|
+
.catch((error: unknown) => {
|
|
2734
|
+
if (!isIndeterminateLocationStoreFailure(error)) throw error;
|
|
2735
|
+
return undefined;
|
|
2736
|
+
});
|
|
2737
|
+
if (preserved?.kind === 'stored') return 'source';
|
|
2370
2738
|
}
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
window.frames = undefined;
|
|
2375
|
-
if (window.retryTimer !== undefined) clearTimeout(window.retryTimer);
|
|
2376
|
-
settle();
|
|
2377
|
-
}, limits.relocationCutoverWaitTimeoutMs);
|
|
2378
|
-
window.windowTimer.unref();
|
|
2379
|
-
window.followTimer = setTimeout(() => {
|
|
2380
|
-
window.followExpired = true;
|
|
2381
|
-
// S1→S4 route convergence: the span the source must keep its Message
|
|
2382
|
-
// Follow route (spec 30 §7.1).
|
|
2383
|
-
this.options.metrics?.duration(
|
|
2384
|
-
'zlink.relocation.route_convergence',
|
|
2385
|
-
Math.max(0, performance.now() - submittedAt) / 1000,
|
|
2386
|
-
{ mesh_name: meshName }
|
|
2739
|
+
// The caller awaits this settlement, so the retry timer keeps the loop alive.
|
|
2740
|
+
await new Promise<void>((resolve) =>
|
|
2741
|
+
setTimeout(resolve, RELOCATION_AUTHORITY_POLL_INTERVAL_MS)
|
|
2387
2742
|
);
|
|
2388
|
-
|
|
2389
|
-
}, limits.messageFollowDurationMs);
|
|
2390
|
-
window.followTimer.unref();
|
|
2391
|
-
if (initialSubmitFailed) this.scheduleCutoverRetransmit(window);
|
|
2743
|
+
}
|
|
2392
2744
|
}
|
|
2393
2745
|
|
|
2394
|
-
private
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2746
|
+
private async readSourceSettlement(
|
|
2747
|
+
envelope: ServiceRelocationEnvelope,
|
|
2748
|
+
primary: ZLinkAuthoritySnapshot,
|
|
2749
|
+
target: ServiceWireRelocationTarget
|
|
2750
|
+
): Promise<
|
|
2751
|
+
| { readonly kind: 'target' }
|
|
2752
|
+
| { readonly kind: 'source'; readonly current: ZLinkAuthoritySnapshot }
|
|
2753
|
+
| { readonly kind: 'other' }
|
|
2754
|
+
> {
|
|
2755
|
+
const primaryAuthorityKey = primaryKey(envelope);
|
|
2756
|
+
let committed = 0;
|
|
2757
|
+
let primaryCurrent: ZLinkAuthoritySnapshot | undefined;
|
|
2758
|
+
for (const participant of envelope.participants) {
|
|
2759
|
+
const current = await this.requireLocationStore().readAuthority({
|
|
2760
|
+
value: participant.key
|
|
2761
|
+
} as ZLinkAuthorityKey);
|
|
2762
|
+
if (current.kind !== 'snapshot') return { kind: 'other' };
|
|
2763
|
+
if (
|
|
2764
|
+
current.objectGeneration === participant.objectGeneration &&
|
|
2765
|
+
current.ownerId === target.ownerId &&
|
|
2766
|
+
current.ownerLeaseGeneration === target.ownerLeaseGeneration &&
|
|
2767
|
+
current.authorityOwnerGeneration > participant.authorityOwnerGeneration &&
|
|
2768
|
+
String(current.allocation.descriptor.rid) === target.nodeRid &&
|
|
2769
|
+
current.allocation.descriptorLifecycleGeneration === target.nodeGeneration
|
|
2770
|
+
) {
|
|
2771
|
+
committed += 1;
|
|
2772
|
+
}
|
|
2773
|
+
if (participant.key === primaryAuthorityKey) primaryCurrent = current;
|
|
2774
|
+
}
|
|
2775
|
+
if (committed === envelope.participants.length) return { kind: 'target' };
|
|
2776
|
+
if (primaryCurrent !== undefined && isSourceOwner(primaryCurrent, primary)) {
|
|
2777
|
+
return { kind: 'source', current: primaryCurrent };
|
|
2778
|
+
}
|
|
2779
|
+
return { kind: 'other' };
|
|
2415
2780
|
}
|
|
2416
2781
|
|
|
2417
2782
|
/**
|
|
@@ -2455,14 +2820,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2455
2820
|
meshName: string,
|
|
2456
2821
|
stagingId: string,
|
|
2457
2822
|
request: ServiceMaintenanceRelocationPrepare,
|
|
2458
|
-
sourceNodeRid: RoutingId
|
|
2823
|
+
sourceNodeRid: RoutingId,
|
|
2459
2824
|
signal?: AbortSignal
|
|
2460
2825
|
): Promise<ServiceMaintenanceRelocationReady> {
|
|
2461
|
-
if (sourceNodeRid === null || String(sourceNodeRid) !== request.sourceNodeRid) {
|
|
2462
|
-
throw new Error(
|
|
2463
|
-
'Relocation prepare source node fence does not match the authenticated peer.'
|
|
2464
|
-
);
|
|
2465
|
-
}
|
|
2466
2826
|
const targetStatus = this.requireMeshNode(meshName).status();
|
|
2467
2827
|
const targetOwner = this.options.currentOwner();
|
|
2468
2828
|
const targetDescriptor = this.options.localDescriptor?.(meshName);
|
|
@@ -2480,24 +2840,15 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2480
2840
|
const fingerprint = stringifyWire(request);
|
|
2481
2841
|
const existing = this.targetStages.get(stagingId);
|
|
2482
2842
|
if (existing !== undefined) {
|
|
2483
|
-
if (
|
|
2484
|
-
existing.offer.authenticatedSourceNodeRid !== String(sourceNodeRid) ||
|
|
2485
|
-
existing.offer.prepareFingerprint !== fingerprint
|
|
2486
|
-
) {
|
|
2487
|
-
throw new Error('Relocation prepare retry source node changed.');
|
|
2488
|
-
}
|
|
2489
2843
|
return relocationReady(request);
|
|
2490
2844
|
}
|
|
2491
2845
|
const terminal = this.terminalTargets.get(stagingId);
|
|
2492
2846
|
if (terminal !== undefined) {
|
|
2493
|
-
if (terminal !== fingerprint) {
|
|
2494
|
-
throw new Error(`Relocation '${stagingId}' repeated Prepare with different bytes.`);
|
|
2495
|
-
}
|
|
2496
2847
|
this.terminalTargets.touch(stagingId);
|
|
2497
2848
|
return relocationReady(request);
|
|
2498
2849
|
}
|
|
2499
2850
|
const pendingAssembly = this.targetAssemblies.get(stagingId);
|
|
2500
|
-
if (pendingAssembly === undefined
|
|
2851
|
+
if (pendingAssembly === undefined) {
|
|
2501
2852
|
throw new Error(`Relocation '${stagingId}' has no matching payload assembly.`);
|
|
2502
2853
|
}
|
|
2503
2854
|
// The directly transferred payload replaces the shared durable root: the
|
|
@@ -2525,6 +2876,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2525
2876
|
request.nodeInternalBoundSessions
|
|
2526
2877
|
);
|
|
2527
2878
|
validatePrepareEnvelope(request, inventoryEnvelope);
|
|
2879
|
+
const releaseActivation =
|
|
2880
|
+
(await this.options.activationAdmission?.acquire(meshName, signal)) ?? (() => undefined);
|
|
2528
2881
|
let materialized:
|
|
2529
2882
|
(Pick<LocalStage, 'owner' | 'staging'> & { readonly target: LocalTargetPort }) | undefined;
|
|
2530
2883
|
let reservation: TargetRelocationReservation | undefined;
|
|
@@ -2539,7 +2892,6 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2539
2892
|
meshName,
|
|
2540
2893
|
request,
|
|
2541
2894
|
inventoryEnvelope,
|
|
2542
|
-
materialized.target,
|
|
2543
2895
|
signal
|
|
2544
2896
|
);
|
|
2545
2897
|
const offer: TargetRelocationOffer = {
|
|
@@ -2547,7 +2899,6 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2547
2899
|
prepareFingerprint: fingerprint,
|
|
2548
2900
|
authenticatedSourceNodeRid: String(sourceNodeRid),
|
|
2549
2901
|
envelope: inventoryEnvelope,
|
|
2550
|
-
restoreDeadlineAtMs: performance.now() + RELOCATION_OPERATION_RETENTION_MS,
|
|
2551
2902
|
reservation
|
|
2552
2903
|
};
|
|
2553
2904
|
const stage: LocalStage = {
|
|
@@ -2556,10 +2907,12 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2556
2907
|
phase: 'ready',
|
|
2557
2908
|
lane: Promise.resolve(),
|
|
2558
2909
|
cutoverReceived: false,
|
|
2559
|
-
boundaryRelay: []
|
|
2910
|
+
boundaryRelay: [],
|
|
2911
|
+
releaseActivation
|
|
2560
2912
|
};
|
|
2561
2913
|
this.targetStages.set(stagingId, stage);
|
|
2562
2914
|
} catch (error) {
|
|
2915
|
+
releaseActivation();
|
|
2563
2916
|
if (reservation !== undefined) {
|
|
2564
2917
|
await this.abortTargetReservation(reservation, signal).catch(() => undefined);
|
|
2565
2918
|
}
|
|
@@ -2571,11 +2924,23 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2571
2924
|
return relocationReady(request);
|
|
2572
2925
|
}
|
|
2573
2926
|
|
|
2927
|
+
/**
|
|
2928
|
+
* The cutover wait of a Ready stage (spec 28 §4.4) is a Warning only: it
|
|
2929
|
+
* records `cutover_timeout` and never submits the target CAS or opens
|
|
2930
|
+
* dispatch. From then on the target settlement reads the Location Store so
|
|
2931
|
+
* a source Preserve fence or a lost target lease ends the staging; only a
|
|
2932
|
+
* verified cutover lets that settlement submit the CAS.
|
|
2933
|
+
*/
|
|
2574
2934
|
private armTargetCutoverFallback(meshName: string, stagingId: string): void {
|
|
2575
2935
|
const stage = this.targetStages.get(stagingId);
|
|
2576
2936
|
if (stage === undefined || stage.fallback !== undefined || stage.finalize !== undefined) return;
|
|
2577
2937
|
stage.fallback = setTimeout(() => {
|
|
2578
|
-
|
|
2938
|
+
if (stage.finalize !== undefined) return;
|
|
2939
|
+
console.warn('[zlink.runtime.relocation.cutover_timeout]', stagingId);
|
|
2940
|
+
this.options.metrics?.count(METRIC_NAMES.RelocationCutoverTimeout, 1, {
|
|
2941
|
+
mesh_name: meshName
|
|
2942
|
+
});
|
|
2943
|
+
void this.beginTargetFinalize(meshName, stagingId, stage).catch((error) =>
|
|
2579
2944
|
console.error('[zlink.runtime.relocation.location_update_failed]', error)
|
|
2580
2945
|
);
|
|
2581
2946
|
}, this.relocationLimits().relocationCutoverWaitTimeoutMs);
|
|
@@ -2594,7 +2959,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2594
2959
|
const stagingId = relocationStagingId(request);
|
|
2595
2960
|
const stage = this.targetStages.get(stagingId);
|
|
2596
2961
|
if (stage === undefined) {
|
|
2597
|
-
if (this.terminalTargets.get(stagingId) !== undefined) {
|
|
2962
|
+
if (this.disposed || this.terminalTargets.get(stagingId) !== undefined) {
|
|
2598
2963
|
if (request.kind === 'cutover') {
|
|
2599
2964
|
console.warn('[zlink.runtime.relocation.late_cutover]', stagingId);
|
|
2600
2965
|
}
|
|
@@ -2603,19 +2968,29 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2603
2968
|
throw new Error(`Relocation staging '${stagingId}' is missing.`);
|
|
2604
2969
|
}
|
|
2605
2970
|
validateTargetOneWayControl(stage, request, sourceNodeRid);
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2971
|
+
stage.lane = stage.lane.then(() => {
|
|
2972
|
+
// A verified cutover closes the boundary batch. A later record or cutover
|
|
2973
|
+
// is a duplicate retransmission and never changes the staged batch.
|
|
2974
|
+
if (stage.cutoverReceived || (stage.phase !== 'ready' && stage.phase !== 'finalizing')) {
|
|
2975
|
+
if (request.kind === 'cutover') {
|
|
2976
|
+
console.warn('[zlink.runtime.relocation.late_cutover]', stagingId);
|
|
2977
|
+
}
|
|
2978
|
+
return;
|
|
2979
|
+
}
|
|
2980
|
+
if (request.kind === 'data') {
|
|
2981
|
+
return this.stageBoundaryRelay(stage, request);
|
|
2982
|
+
}
|
|
2983
|
+
if (this.targetStageSealed(stage)) return;
|
|
2984
|
+
this.reconcileBoundaryRelay(stage, request, stagingId);
|
|
2985
|
+
stage.cutoverReceived = true;
|
|
2986
|
+
});
|
|
2987
|
+
await stage.lane;
|
|
2988
|
+
if (request.kind === 'data') return;
|
|
2989
|
+
// A settlement started by the cutover wait Warning is still reading the
|
|
2990
|
+
// Store; it submits the CAS on its next round now that cutover verified.
|
|
2991
|
+
if (stage.finalize !== undefined) return;
|
|
2617
2992
|
if (stage.fallback !== undefined) clearTimeout(stage.fallback);
|
|
2618
|
-
await this.beginTargetFinalize(meshName, stagingId, stage,
|
|
2993
|
+
await this.beginTargetFinalize(meshName, stagingId, stage, signal);
|
|
2619
2994
|
}
|
|
2620
2995
|
|
|
2621
2996
|
/** Validates one boundary relay record and buffers it until cutover applies it. */
|
|
@@ -2643,15 +3018,15 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2643
3018
|
}
|
|
2644
3019
|
const expectedCount = Number(request.boundaryRecordCount);
|
|
2645
3020
|
const staged = stage.boundaryRelay;
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
3021
|
+
// The batch is the last `boundaryRecordCount` records: the source sends
|
|
3022
|
+
// the whole batch right before this cutover on the ordered connection,
|
|
3023
|
+
// so a retransmission replaces an earlier partial span as a whole.
|
|
3024
|
+
if (staged.length >= expectedCount) {
|
|
3025
|
+
const batch = staged.slice(staged.length - expectedCount);
|
|
3026
|
+
if (
|
|
3027
|
+
crc32c(Buffer.concat(batch.map((value) => value.frozenRecord.canonicalBytes))) ===
|
|
3028
|
+
request.boundaryChecksumCrc32c
|
|
3029
|
+
) {
|
|
2655
3030
|
staged.splice(0, staged.length - expectedCount);
|
|
2656
3031
|
return;
|
|
2657
3032
|
}
|
|
@@ -2668,21 +3043,16 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2668
3043
|
meshName: string,
|
|
2669
3044
|
stagingId: string,
|
|
2670
3045
|
stage: LocalStage,
|
|
2671
|
-
fallback: boolean,
|
|
2672
3046
|
signal?: AbortSignal
|
|
2673
3047
|
): Promise<void> {
|
|
2674
3048
|
if (stage.finalize !== undefined) return stage.finalize;
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
mesh_name: meshName
|
|
2679
|
-
});
|
|
2680
|
-
}
|
|
3049
|
+
// Records staged before this point finish first. The settlement is not
|
|
3050
|
+
// put on the lane, so a cutover that arrives while it waits for the
|
|
3051
|
+
// authority result is still reconciled.
|
|
2681
3052
|
const finalize = stage.lane.then(() =>
|
|
2682
3053
|
this.finalizeTargetStage(meshName, stagingId, stage, signal)
|
|
2683
3054
|
);
|
|
2684
3055
|
stage.finalize = finalize;
|
|
2685
|
-
stage.lane = finalize;
|
|
2686
3056
|
return finalize;
|
|
2687
3057
|
}
|
|
2688
3058
|
|
|
@@ -2698,14 +3068,14 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2698
3068
|
let authority: ZLinkAuthoritySnapshot | undefined;
|
|
2699
3069
|
let resumeStartedAt: number | undefined;
|
|
2700
3070
|
try {
|
|
2701
|
-
// The boundary relay span is applied to the temporary queue before the
|
|
2702
|
-
// owner CAS so the merged order is saved work, pre-boundary relay, then
|
|
2703
|
-
// post-boundary temporary records (spec 28 §4.6).
|
|
2704
|
-
for (const relay of stage.boundaryRelay.splice(0)) {
|
|
2705
|
-
await this.applyRelocationData(stage, relay, signal);
|
|
2706
|
-
}
|
|
2707
3071
|
authority = await this.commitTargetReservation(stage, stage.offer.reservation, signal);
|
|
3072
|
+
if (authority === undefined) {
|
|
3073
|
+
// The seal owner may have ended staging while a Store read was pending.
|
|
3074
|
+
if ((stage as LocalStage).phase === 'failed') return;
|
|
3075
|
+
return this.discardTargetStage(stagingId, stage, signal);
|
|
3076
|
+
}
|
|
2708
3077
|
authorityCommitted = true;
|
|
3078
|
+
stage.releaseActivation();
|
|
2709
3079
|
resumeStartedAt = performance.now();
|
|
2710
3080
|
stage.phase = 'committed';
|
|
2711
3081
|
await stage.owner.normalize(stage.staging, authority, signal);
|
|
@@ -2720,13 +3090,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2720
3090
|
}
|
|
2721
3091
|
}
|
|
2722
3092
|
await this.finalizeActorJoinProfiles(meshName, stage, signal);
|
|
2723
|
-
//
|
|
2724
|
-
//
|
|
2725
|
-
//
|
|
2726
|
-
//
|
|
2727
|
-
// roll back already committed target ownership or strand its staged
|
|
2728
|
-
// object: the next reservation recognizes the retained journal root and
|
|
2729
|
-
// continues its existing cleanup/retry path.
|
|
3093
|
+
// Clear the final relocation publication before admission becomes
|
|
3094
|
+
// externally visible so a later Join cannot reserve against the
|
|
3095
|
+
// previous root. A bounded CAS conflict must not roll back already
|
|
3096
|
+
// committed target ownership or strand its staged object.
|
|
2730
3097
|
try {
|
|
2731
3098
|
await this.clearTargetRelocationPublication(stage, authority, signal);
|
|
2732
3099
|
} catch (error) {
|
|
@@ -2738,8 +3105,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2738
3105
|
await stage.owner.openAdmission(stage.staging, signal);
|
|
2739
3106
|
// S2→S3: owner confirmation to application dispatch opening.
|
|
2740
3107
|
this.options.metrics?.duration(
|
|
2741
|
-
|
|
2742
|
-
Math.max(0, performance.now() - resumeStartedAt) /
|
|
3108
|
+
METRIC_NAMES.RelocationTargetResume,
|
|
3109
|
+
Math.max(0, performance.now() - resumeStartedAt) / MILLISECONDS_PER_SECOND,
|
|
2743
3110
|
{ mesh_name: meshName }
|
|
2744
3111
|
);
|
|
2745
3112
|
await this.publishSessionRoutes(stage.staging);
|
|
@@ -2754,14 +3121,15 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2754
3121
|
this.targetStages.delete(stagingId);
|
|
2755
3122
|
this.targetAssemblies.delete(stagingId);
|
|
2756
3123
|
} catch (error) {
|
|
2757
|
-
stage.phase = 'failed';
|
|
2758
3124
|
this.terminalTargets.remember(stagingId, stage.offer.prepareFingerprint);
|
|
2759
3125
|
this.targetStages.delete(stagingId);
|
|
2760
3126
|
this.targetAssemblies.delete(stagingId);
|
|
2761
3127
|
if (!authorityCommitted) {
|
|
2762
|
-
|
|
3128
|
+
// abortTargetStage may already have assigned failed before throwing.
|
|
3129
|
+
if ((stage as LocalStage).phase !== 'failed') await this.abortTargetStage(stage);
|
|
2763
3130
|
throw error;
|
|
2764
3131
|
}
|
|
3132
|
+
stage.phase = 'failed';
|
|
2765
3133
|
if (authority !== undefined) {
|
|
2766
3134
|
throw new ServiceRelocationPostCommitError(authority, stage.staging, error);
|
|
2767
3135
|
}
|
|
@@ -2897,19 +3265,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2897
3265
|
}
|
|
2898
3266
|
this.sourceActorJoinProfiles.delete(input.transferId);
|
|
2899
3267
|
this.terminalActorJoinSourceLeaves.remember(input.transferId, input.actorId);
|
|
2900
|
-
void profile
|
|
2901
|
-
.then(async () => {
|
|
2902
|
-
await this.options.actorTransfer.completeRelocationSourceLeave(
|
|
2903
|
-
profile.actor,
|
|
2904
|
-
profile.sourceSpotId
|
|
2905
|
-
);
|
|
2906
|
-
await this.options
|
|
2907
|
-
.actorManager()
|
|
2908
|
-
?.completeRelocationSource(input.actorId, profile.sourceActorRef);
|
|
2909
|
-
})
|
|
2910
|
-
.catch((error) => {
|
|
2911
|
-
console.error('[zlink.runtime.relocation.source_leave_failed]', error);
|
|
2912
|
-
});
|
|
3268
|
+
void this.finalizeActorJoinSource(profile);
|
|
2913
3269
|
return true;
|
|
2914
3270
|
}
|
|
2915
3271
|
|
|
@@ -2941,41 +3297,6 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2941
3297
|
}
|
|
2942
3298
|
}
|
|
2943
3299
|
|
|
2944
|
-
private async waitForTargetAuthorities(
|
|
2945
|
-
envelope: ServiceRelocationEnvelope,
|
|
2946
|
-
target: ServiceWireRelocationTarget,
|
|
2947
|
-
deadlineAtMs: number,
|
|
2948
|
-
signal?: AbortSignal
|
|
2949
|
-
): Promise<void> {
|
|
2950
|
-
for (;;) {
|
|
2951
|
-
signal?.throwIfAborted();
|
|
2952
|
-
let exact = true;
|
|
2953
|
-
for (const participant of envelope.participants) {
|
|
2954
|
-
const current = await this.requireLocationStore().readAuthority(
|
|
2955
|
-
{ value: participant.key } as ZLinkAuthorityKey,
|
|
2956
|
-
signal
|
|
2957
|
-
);
|
|
2958
|
-
if (
|
|
2959
|
-
current.kind !== 'snapshot' ||
|
|
2960
|
-
current.objectGeneration !== participant.objectGeneration ||
|
|
2961
|
-
current.ownerId !== target.ownerId ||
|
|
2962
|
-
current.ownerLeaseGeneration !== target.ownerLeaseGeneration ||
|
|
2963
|
-
current.authorityOwnerGeneration <= participant.authorityOwnerGeneration ||
|
|
2964
|
-
String(current.allocation.descriptor.rid) !== target.nodeRid ||
|
|
2965
|
-
current.allocation.descriptorLifecycleGeneration !== target.nodeGeneration
|
|
2966
|
-
) {
|
|
2967
|
-
exact = false;
|
|
2968
|
-
break;
|
|
2969
|
-
}
|
|
2970
|
-
}
|
|
2971
|
-
if (exact) return;
|
|
2972
|
-
if (performance.now() >= deadlineAtMs) {
|
|
2973
|
-
throw new Error('Relocation target owner was not confirmed before its original deadline.');
|
|
2974
|
-
}
|
|
2975
|
-
await new Promise<void>((resolve) => setTimeout(resolve, 10));
|
|
2976
|
-
}
|
|
2977
|
-
}
|
|
2978
|
-
|
|
2979
3300
|
private async clearTargetRelocationPublication(
|
|
2980
3301
|
stage: LocalStage,
|
|
2981
3302
|
primary: ZLinkAuthoritySnapshot,
|
|
@@ -2986,36 +3307,25 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2986
3307
|
stage.staging.primaryAuthorityKey.value,
|
|
2987
3308
|
signal
|
|
2988
3309
|
);
|
|
2989
|
-
// The
|
|
2990
|
-
//
|
|
2991
|
-
//
|
|
2992
|
-
//
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
},
|
|
3008
|
-
signal
|
|
3009
|
-
);
|
|
3010
|
-
if (result.kind === 'stored') return;
|
|
3011
|
-
const read = await this.requireLocationStore().readAuthority(
|
|
3012
|
-
stage.staging.primaryAuthorityKey,
|
|
3013
|
-
signal
|
|
3014
|
-
);
|
|
3015
|
-
if (read.kind !== 'snapshot') break;
|
|
3016
|
-
current = read;
|
|
3310
|
+
// The target owner is the only writer of the committed primary
|
|
3311
|
+
// authority between its commit and this clear, so one CAS against the
|
|
3312
|
+
// committed snapshot decides. A conflict means another owner changed the
|
|
3313
|
+
// row; the caller records it and leaves the committed ownership intact.
|
|
3314
|
+
const publication = this.codec.read(primary.payload);
|
|
3315
|
+
if (publication === undefined) return;
|
|
3316
|
+
const result = await this.requireLocationStore().compareExchangeAuthority(
|
|
3317
|
+
stage.staging.primaryAuthorityKey,
|
|
3318
|
+
primary.storeVersion,
|
|
3319
|
+
{
|
|
3320
|
+
kind: 'put',
|
|
3321
|
+
generationTransition: 'preserve',
|
|
3322
|
+
payload: this.codec.clear(primary.payload, publication.reference)
|
|
3323
|
+
},
|
|
3324
|
+
signal
|
|
3325
|
+
);
|
|
3326
|
+
if (result.kind !== 'stored') {
|
|
3327
|
+
throw new Error('Relocation target authority normalization CAS failed.');
|
|
3017
3328
|
}
|
|
3018
|
-
throw new Error('Relocation target authority normalization CAS failed.');
|
|
3019
3329
|
}
|
|
3020
3330
|
|
|
3021
3331
|
private async handleReplyRelay(
|
|
@@ -3129,7 +3439,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3129
3439
|
pending.resolve('sourceLeaseExpired');
|
|
3130
3440
|
return;
|
|
3131
3441
|
}
|
|
3132
|
-
if (++attempts >
|
|
3442
|
+
if (++attempts > RELOCATION_MAX_SEND_ATTEMPTS) {
|
|
3133
3443
|
pending.reject(new Error(`Relocation reply relay ACK '${key}' timed out.`));
|
|
3134
3444
|
return;
|
|
3135
3445
|
}
|
|
@@ -3139,7 +3449,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3139
3449
|
encodeMaintenanceReplyRelay(request)
|
|
3140
3450
|
);
|
|
3141
3451
|
if (this.pendingReplyRelays.get(key) === pending) {
|
|
3142
|
-
pending.timer = setTimeout(() => void send(),
|
|
3452
|
+
pending.timer = setTimeout(() => void send(), RELOCATION_RESEND_DELAY_MS);
|
|
3143
3453
|
}
|
|
3144
3454
|
} catch (error) {
|
|
3145
3455
|
pending.reject(error);
|
|
@@ -3269,7 +3579,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3269
3579
|
}
|
|
3270
3580
|
|
|
3271
3581
|
private async exactSourceLeaseExpired(
|
|
3272
|
-
source: ServiceWireRequestSourceFence,
|
|
3582
|
+
source: Pick<ServiceWireRequestSourceFence, 'ownerId' | 'leaseGeneration'>,
|
|
3273
3583
|
signal?: AbortSignal
|
|
3274
3584
|
): Promise<boolean> {
|
|
3275
3585
|
const lease = await this.requireLocationStore().readOwnerLease(source.ownerId, signal);
|
|
@@ -3284,10 +3594,14 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3284
3594
|
private async publishSessionRoutes(
|
|
3285
3595
|
staging: ServiceObjectRelocationStaging<LocalHidden>
|
|
3286
3596
|
): Promise<void> {
|
|
3287
|
-
for (const hidden of staging.hidden
|
|
3288
|
-
if (hidden.actor
|
|
3289
|
-
|
|
3290
|
-
|
|
3597
|
+
for (const [key, hidden] of staging.hidden) {
|
|
3598
|
+
if (hidden.actor === undefined) continue;
|
|
3599
|
+
// The Session seal travels with the relocation envelope, not the Actor.
|
|
3600
|
+
const carried = staging.envelope.participants.find((value) => value.key === key);
|
|
3601
|
+
await this.options.actorTransfer.publishRoutedActorOwnership(
|
|
3602
|
+
hidden.actor,
|
|
3603
|
+
carried === undefined ? undefined : decodeActorSession(carried.boundSessionState)
|
|
3604
|
+
);
|
|
3291
3605
|
}
|
|
3292
3606
|
}
|
|
3293
3607
|
|
|
@@ -3296,7 +3610,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3296
3610
|
targetNodeRid: RoutingId,
|
|
3297
3611
|
request: ZLinkServiceRelocationControlRequest,
|
|
3298
3612
|
signal?: AbortSignal,
|
|
3299
|
-
deadlineAtMs = performance.now() +
|
|
3613
|
+
deadlineAtMs: number = performance.now() + RELOCATION_OPERATION_TIMEOUT_MS,
|
|
3300
3614
|
sideband: readonly Uint8Array[] = []
|
|
3301
3615
|
): Promise<ReturnType<typeof decodeServiceRelocationControlResponse>> {
|
|
3302
3616
|
const key = controlAckKey(request);
|
|
@@ -3316,7 +3630,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3316
3630
|
lastRequestError
|
|
3317
3631
|
);
|
|
3318
3632
|
}
|
|
3319
|
-
const attemptTimeoutMs = Math.max(
|
|
3633
|
+
const attemptTimeoutMs = Math.max(
|
|
3634
|
+
1,
|
|
3635
|
+
Math.min(RELOCATION_ATTEMPT_TIMEOUT_MS, Math.ceil(remainingMs))
|
|
3636
|
+
);
|
|
3320
3637
|
const startedAtMs = performance.now();
|
|
3321
3638
|
try {
|
|
3322
3639
|
const replyFrames = await this.requestInfrastructureControl(
|
|
@@ -3342,11 +3659,13 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3342
3659
|
throw error;
|
|
3343
3660
|
}
|
|
3344
3661
|
lastRequestError = error;
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3662
|
+
if (isRelocationDebugEnabled()) {
|
|
3663
|
+
relocationDebug('control.request_retry', {
|
|
3664
|
+
key,
|
|
3665
|
+
kind: request.kind,
|
|
3666
|
+
targetRid: String(targetNodeRid)
|
|
3667
|
+
});
|
|
3668
|
+
}
|
|
3350
3669
|
const retryDelayMs = Math.min(
|
|
3351
3670
|
Math.max(0, attemptTimeoutMs - (performance.now() - startedAtMs)),
|
|
3352
3671
|
Math.max(0, deadlineAtMs - performance.now())
|
|
@@ -3386,7 +3705,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3386
3705
|
peer.routingId !== null &&
|
|
3387
3706
|
String(peer.routingId) === String(descriptor.rid) &&
|
|
3388
3707
|
peer.lifecycleGeneration === descriptor.lifecycleGeneration &&
|
|
3389
|
-
peer.state ===
|
|
3708
|
+
peer.state === MeshPeerRuntimeState.Serving
|
|
3390
3709
|
) &&
|
|
3391
3710
|
relocationTargetSupports(descriptor, requirements)
|
|
3392
3711
|
)
|
|
@@ -3436,51 +3755,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3436
3755
|
meshName: string,
|
|
3437
3756
|
prepare: ServiceMaintenanceRelocationPrepare,
|
|
3438
3757
|
envelope: ServiceRelocationEnvelope,
|
|
3439
|
-
targetPort: LocalTargetPort,
|
|
3440
3758
|
signal?: AbortSignal
|
|
3441
3759
|
): Promise<TargetRelocationReservation> {
|
|
3442
3760
|
const authorities = await this.readTargetParticipantAuthorities(envelope, signal);
|
|
3443
3761
|
const publication = relocationPublication(prepare, envelope);
|
|
3444
|
-
// Carries the exact reference + CRC of a deferred-Join publication the
|
|
3445
|
-
// target reservation must keep connected through the CAS (spec 28 —
|
|
3446
|
-
// clearing/replacing it here would strand the journal's own
|
|
3447
|
-
// prepared->committed transition with nothing left for it to find).
|
|
3448
|
-
const retainedDeferredJoinRoots = new Map<
|
|
3449
|
-
string,
|
|
3450
|
-
{ readonly reference: string; readonly checksumCrc32c: number }
|
|
3451
|
-
>();
|
|
3452
|
-
for (const participant of envelope.participants) {
|
|
3453
|
-
const staged = targetPort.deferredJoinRoot(participant.key);
|
|
3454
|
-
if (staged !== undefined) {
|
|
3455
|
-
retainedDeferredJoinRoots.set(participant.key, {
|
|
3456
|
-
reference: staged.reference.value,
|
|
3457
|
-
checksumCrc32c: staged.checksumCrc32c
|
|
3458
|
-
});
|
|
3459
|
-
continue;
|
|
3460
|
-
}
|
|
3461
|
-
const current = this.codec.read(authorities.get(participant.key)!.payload);
|
|
3462
|
-
if (
|
|
3463
|
-
participant.objectKind === 'actor' &&
|
|
3464
|
-
current?.canonical === true &&
|
|
3465
|
-
(await this.options.actorTransfer.isDeferredJoinAcceptedRootPublication(
|
|
3466
|
-
current.reference,
|
|
3467
|
-
current.checksumCrc32c,
|
|
3468
|
-
{
|
|
3469
|
-
authorityKey: participant.key,
|
|
3470
|
-
objectKind: 'actor',
|
|
3471
|
-
objectGeneration: participant.objectGeneration,
|
|
3472
|
-
aggregateId: current.aggregateId,
|
|
3473
|
-
aggregateGeneration: current.aggregateGeneration
|
|
3474
|
-
},
|
|
3475
|
-
signal
|
|
3476
|
-
))
|
|
3477
|
-
) {
|
|
3478
|
-
retainedDeferredJoinRoots.set(participant.key, {
|
|
3479
|
-
reference: current.reference,
|
|
3480
|
-
checksumCrc32c: current.checksumCrc32c
|
|
3481
|
-
});
|
|
3482
|
-
}
|
|
3483
|
-
}
|
|
3484
3762
|
const participants = envelope.participants.map((participant) => {
|
|
3485
3763
|
const expected = authorities.get(participant.key)!;
|
|
3486
3764
|
const membership = actorMembershipTarget(envelope, participant.key);
|
|
@@ -3495,48 +3773,34 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3495
3773
|
) {
|
|
3496
3774
|
throw new Error(`Relocation target RouteMesh '${meshName}' has no Entry Spot.`);
|
|
3497
3775
|
}
|
|
3498
|
-
const retainedDeferredJoinRoot = retainedDeferredJoinRoots.get(participant.key);
|
|
3499
|
-
const participantPublication =
|
|
3500
|
-
retainedDeferredJoinRoot === undefined
|
|
3501
|
-
? publication
|
|
3502
|
-
: {
|
|
3503
|
-
...publication,
|
|
3504
|
-
reference: retainedDeferredJoinRoot.reference,
|
|
3505
|
-
checksumCrc32c: retainedDeferredJoinRoot.checksumCrc32c
|
|
3506
|
-
};
|
|
3507
3776
|
return {
|
|
3508
3777
|
key: { value: participant.key } as ZLinkAuthorityKey,
|
|
3509
3778
|
expected,
|
|
3510
3779
|
ownerTransition: 'newOwner' as const,
|
|
3511
|
-
authorityPayload: this.authorityPayloadForPublication(
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
owner: {
|
|
3516
|
-
ownerId: prepare.target.ownerId,
|
|
3517
|
-
leaseGeneration: prepare.target.ownerLeaseGeneration
|
|
3518
|
-
},
|
|
3519
|
-
meshName,
|
|
3520
|
-
nodeRid: prepare.target.nodeRid,
|
|
3521
|
-
nodeGeneration: prepare.target.nodeGeneration,
|
|
3522
|
-
objectGeneration: expected.objectGeneration,
|
|
3523
|
-
targetAttemptGeneration: prepare.targetAttemptGeneration,
|
|
3524
|
-
coordinatorExpectedStoreVersion: expected.storeVersion.value,
|
|
3525
|
-
...(membership === undefined
|
|
3526
|
-
? expected.allocation.objectKind === 'actor'
|
|
3527
|
-
? {
|
|
3528
|
-
actorSpotId: entrySpotId!,
|
|
3529
|
-
actorSpotGeneration: prepare.target.nodeGeneration,
|
|
3530
|
-
actorSpotKind: ZLinkSpotKind.Entry as const
|
|
3531
|
-
}
|
|
3532
|
-
: {}
|
|
3533
|
-
: {
|
|
3534
|
-
...membership,
|
|
3535
|
-
actorSpotKind: ZLinkSpotKind.User as const
|
|
3536
|
-
})
|
|
3780
|
+
authorityPayload: this.authorityPayloadForPublication(expected.payload, publication, {
|
|
3781
|
+
owner: {
|
|
3782
|
+
ownerId: prepare.target.ownerId,
|
|
3783
|
+
leaseGeneration: prepare.target.ownerLeaseGeneration
|
|
3537
3784
|
},
|
|
3538
|
-
|
|
3539
|
-
|
|
3785
|
+
meshName,
|
|
3786
|
+
nodeRid: prepare.target.nodeRid,
|
|
3787
|
+
nodeGeneration: prepare.target.nodeGeneration,
|
|
3788
|
+
objectGeneration: expected.objectGeneration,
|
|
3789
|
+
targetAttemptGeneration: prepare.targetAttemptGeneration,
|
|
3790
|
+
coordinatorExpectedStoreVersion: expected.storeVersion.value,
|
|
3791
|
+
...(membership === undefined
|
|
3792
|
+
? expected.allocation.objectKind === 'actor'
|
|
3793
|
+
? {
|
|
3794
|
+
actorSpotId: entrySpotId!,
|
|
3795
|
+
actorSpotGeneration: prepare.target.nodeGeneration,
|
|
3796
|
+
actorSpotKind: ZLinkSpotKind.Entry as const
|
|
3797
|
+
}
|
|
3798
|
+
: {}
|
|
3799
|
+
: {
|
|
3800
|
+
...membership,
|
|
3801
|
+
actorSpotKind: ZLinkSpotKind.User as const
|
|
3802
|
+
})
|
|
3803
|
+
}),
|
|
3540
3804
|
membershipMutation: encodeMembershipMutation(envelope.memberships, participant.key)
|
|
3541
3805
|
};
|
|
3542
3806
|
});
|
|
@@ -3576,24 +3840,16 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3576
3840
|
readonly actorSpotId?: string;
|
|
3577
3841
|
readonly actorSpotGeneration?: bigint;
|
|
3578
3842
|
readonly actorSpotKind?: ZLinkSpotKind.Entry | ZLinkSpotKind.User;
|
|
3579
|
-
}
|
|
3580
|
-
replacesDeferredJoinRoot = false
|
|
3843
|
+
}
|
|
3581
3844
|
): Uint8Array {
|
|
3582
3845
|
const existing = this.codec.read(payload);
|
|
3583
3846
|
const canonicalIdentity = serviceRelocationAuthoritySlotIdentity(payload);
|
|
3584
|
-
const replacesCanonicalJournal = replacesDeferredJoinRoot && existing?.canonical === true;
|
|
3585
3847
|
if (
|
|
3586
3848
|
canonicalIdentity !== undefined &&
|
|
3587
3849
|
(existing === undefined ||
|
|
3588
|
-
(existing.canonical === true &&
|
|
3589
|
-
(existing.aggregateId === publication.aggregateId || replacesCanonicalJournal)))
|
|
3850
|
+
(existing.canonical === true && existing.aggregateId === publication.aggregateId))
|
|
3590
3851
|
) {
|
|
3591
|
-
const published =
|
|
3592
|
-
existing === undefined
|
|
3593
|
-
? this.codec.publish(payload, publication)
|
|
3594
|
-
: replacesCanonicalJournal && existing.aggregateId !== publication.aggregateId
|
|
3595
|
-
? this.codec.publish(this.codec.clear(payload, existing.reference), publication)
|
|
3596
|
-
: payload;
|
|
3852
|
+
const published = existing === undefined ? this.codec.publish(payload, publication) : payload;
|
|
3597
3853
|
if (target === undefined) return published;
|
|
3598
3854
|
return projectServiceRelocationAuthorityTargetReady(
|
|
3599
3855
|
replaceServiceRelocationAuthorityApplicationPayload(
|
|
@@ -3665,6 +3921,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3665
3921
|
|
|
3666
3922
|
private async abortTargetStage(stage: LocalStage, signal?: AbortSignal): Promise<void> {
|
|
3667
3923
|
if (stage.fallback !== undefined) clearTimeout(stage.fallback);
|
|
3924
|
+
stage.releaseActivation();
|
|
3668
3925
|
const failures: unknown[] = [];
|
|
3669
3926
|
try {
|
|
3670
3927
|
await this.abortTargetReservation(stage.offer.reservation, signal);
|
|
@@ -3683,35 +3940,73 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3683
3940
|
}
|
|
3684
3941
|
}
|
|
3685
3942
|
|
|
3943
|
+
private async discardTargetStage(
|
|
3944
|
+
stagingId: string,
|
|
3945
|
+
stage: LocalStage,
|
|
3946
|
+
signal?: AbortSignal
|
|
3947
|
+
): Promise<void> {
|
|
3948
|
+
stage.phase = 'failed';
|
|
3949
|
+
console.warn('[zlink.runtime.relocation.target_staging_discarded]', stagingId);
|
|
3950
|
+
try {
|
|
3951
|
+
await this.abortTargetStage(stage, signal);
|
|
3952
|
+
} finally {
|
|
3953
|
+
this.terminalTargets.remember(stagingId, stage.offer.prepareFingerprint);
|
|
3954
|
+
this.targetStages.delete(stagingId);
|
|
3955
|
+
this.targetAssemblies.delete(stagingId);
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
|
|
3686
3959
|
private async commitTargetReservation(
|
|
3687
3960
|
stage: LocalStage,
|
|
3688
3961
|
reservation: TargetRelocationReservation,
|
|
3689
3962
|
signal?: AbortSignal
|
|
3690
|
-
): Promise<ZLinkAuthoritySnapshot> {
|
|
3963
|
+
): Promise<ZLinkAuthoritySnapshot | undefined> {
|
|
3691
3964
|
return await this.commitTargetAggregate(stage, reservation.prepared, signal);
|
|
3692
3965
|
}
|
|
3693
3966
|
|
|
3967
|
+
/**
|
|
3968
|
+
* The one target authority settlement (Location runtime §10, spec 28
|
|
3969
|
+
* §4.4-§4.5). Only a verified cutover lets the target submit its NewOwner
|
|
3970
|
+
* CAS; a transient or indeterminate result resubmits the same RelocationId
|
|
3971
|
+
* and expected StoreVersion while the target owner lease is valid (no
|
|
3972
|
+
* separate timeout, no fixed count). Before a verified cutover the target
|
|
3973
|
+
* only reads. It returns the committed authority, or undefined when a
|
|
3974
|
+
* definitive Conflict excludes this commit after reconciliation, a source
|
|
3975
|
+
* Preserve fence moved the source row, or the target lease ended; the
|
|
3976
|
+
* caller then discards the staging.
|
|
3977
|
+
*/
|
|
3694
3978
|
private async commitTargetAggregate(
|
|
3695
3979
|
stage: LocalStage,
|
|
3696
3980
|
prepared: ServicePreparedRelocationAggregate,
|
|
3697
3981
|
signal?: AbortSignal
|
|
3698
|
-
): Promise<ZLinkAuthoritySnapshot> {
|
|
3699
|
-
|
|
3982
|
+
): Promise<ZLinkAuthoritySnapshot | undefined> {
|
|
3983
|
+
const target = stage.offer.prepare.target;
|
|
3700
3984
|
for (;;) {
|
|
3985
|
+
if (stage.phase === 'failed') return undefined;
|
|
3701
3986
|
signal?.throwIfAborted();
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
)
|
|
3987
|
+
if (this.targetStageSealed(stage)) return undefined;
|
|
3988
|
+
let conflict = false;
|
|
3989
|
+
if (stage.cutoverReceived) {
|
|
3990
|
+
// The boundary relay span is applied to the temporary queue before
|
|
3991
|
+
// the owner CAS so the merged order is saved work, pre-boundary relay,
|
|
3992
|
+
// then post-boundary temporary records (spec 28 §4.6).
|
|
3993
|
+
for (const relay of stage.boundaryRelay.splice(0)) {
|
|
3994
|
+
await this.applyRelocationData(stage, relay, signal);
|
|
3995
|
+
}
|
|
3996
|
+
// A thrown provider call has an unknown result; the read below decides.
|
|
3997
|
+
const result = await this.requireLocationStore()
|
|
3998
|
+
.commitAggregate(prepared.fence, signal)
|
|
3999
|
+
.catch((error: unknown) => {
|
|
4000
|
+
if (!isIndeterminateLocationStoreFailure(error, signal)) throw error;
|
|
4001
|
+
return undefined;
|
|
4002
|
+
});
|
|
4003
|
+
if (result?.kind === 'generationExhausted') {
|
|
4004
|
+
throw new Error('location_update_failed: relocation aggregate commit exhausted.');
|
|
4005
|
+
}
|
|
4006
|
+
conflict = result?.kind === 'stale';
|
|
3713
4007
|
}
|
|
3714
4008
|
const observed = await this.readAggregateForCommitRetry(prepared, signal);
|
|
4009
|
+
if ((stage as LocalStage).phase === 'failed') return undefined;
|
|
3715
4010
|
if (observed.kind === 'committed') {
|
|
3716
4011
|
const primary = observed.authorities.get(stage.staging.primaryAuthorityKey.value);
|
|
3717
4012
|
if (primary === undefined) {
|
|
@@ -3719,15 +4014,20 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3719
4014
|
}
|
|
3720
4015
|
return primary;
|
|
3721
4016
|
}
|
|
3722
|
-
if (observed.kind === 'stale')
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
)
|
|
4017
|
+
if (observed.kind === 'stale' || (conflict && observed.kind === 'source')) return undefined;
|
|
4018
|
+
// An unreadable lease is an uncertain result, not an expiry.
|
|
4019
|
+
if (
|
|
4020
|
+
await this.exactSourceLeaseExpired(
|
|
4021
|
+
{ ownerId: target.ownerId, leaseGeneration: target.ownerLeaseGeneration },
|
|
4022
|
+
signal
|
|
4023
|
+
).catch((error: unknown) => {
|
|
4024
|
+
if (!isIndeterminateLocationStoreFailure(error, signal)) throw error;
|
|
4025
|
+
return false;
|
|
4026
|
+
})
|
|
4027
|
+
) {
|
|
4028
|
+
return undefined;
|
|
3729
4029
|
}
|
|
3730
|
-
await
|
|
4030
|
+
await waitForRelocationRetry(RELOCATION_AUTHORITY_RETRY_DELAY_MS, signal);
|
|
3731
4031
|
}
|
|
3732
4032
|
}
|
|
3733
4033
|
|
|
@@ -3769,7 +4069,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3769
4069
|
return { kind: 'stale' };
|
|
3770
4070
|
}
|
|
3771
4071
|
}
|
|
3772
|
-
} catch {
|
|
4072
|
+
} catch (error) {
|
|
4073
|
+
if (!isIndeterminateLocationStoreFailure(error, signal)) throw error;
|
|
3773
4074
|
return { kind: 'unknown' };
|
|
3774
4075
|
}
|
|
3775
4076
|
if (committed === prepared.plan.participants.length) {
|
|
@@ -3821,7 +4122,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3821
4122
|
if (request.object.kind === 'userSpot' || request.object.kind === 'instanceSpot') {
|
|
3822
4123
|
let cursor: ZLinkAuthorityScanCursor | undefined;
|
|
3823
4124
|
for (;;) {
|
|
3824
|
-
const page = await store.listAuthorities('', cursor,
|
|
4125
|
+
const page = await store.listAuthorities('', cursor, ZLINK_PROVIDER_MAX_PAGE_SIZE, signal);
|
|
3825
4126
|
if (page.kind === 'scanExpired') {
|
|
3826
4127
|
throw new Error('Location Store authority scan expired during relocation.');
|
|
3827
4128
|
}
|
|
@@ -3966,7 +4267,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3966
4267
|
);
|
|
3967
4268
|
if (captured.byteLength > RELOCATION_PARTICIPANT_STATE_LIMIT_BYTES) {
|
|
3968
4269
|
throw new ZLinkRelocationStateIncompatibleError(
|
|
3969
|
-
|
|
4270
|
+
`Relocation application state exceeds the ${RELOCATION_PARTICIPANT_STATE_LIMIT_BYTES / (1024 * 1024)} MiB participant limit.`
|
|
3970
4271
|
);
|
|
3971
4272
|
}
|
|
3972
4273
|
return captured;
|
|
@@ -3987,13 +4288,13 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3987
4288
|
startedAt: number,
|
|
3988
4289
|
outcome: string
|
|
3989
4290
|
): void {
|
|
3990
|
-
const seconds = Math.max(0, performance.now() - startedAt) /
|
|
4291
|
+
const seconds = Math.max(0, performance.now() - startedAt) / MILLISECONDS_PER_SECOND;
|
|
3991
4292
|
const attributes = {
|
|
3992
4293
|
unit_kind: unitKind,
|
|
3993
4294
|
...(executionMode === undefined ? {} : { execution_mode: executionMode })
|
|
3994
4295
|
};
|
|
3995
|
-
this.options.metrics?.duration(
|
|
3996
|
-
this.options.metrics?.count(
|
|
4296
|
+
this.options.metrics?.duration(METRIC_NAMES.RelocationInterruption, seconds, attributes);
|
|
4297
|
+
this.options.metrics?.count(METRIC_NAMES.RelocationCompleted, 1, {
|
|
3997
4298
|
mesh_name: meshName,
|
|
3998
4299
|
object_kind: unitKind,
|
|
3999
4300
|
policy: unitKind === 'user_spot' && executionMode === 'per_actor' ? 'recreate' : 'configured',
|
|
@@ -4095,6 +4396,26 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
4095
4396
|
return value;
|
|
4096
4397
|
}
|
|
4097
4398
|
|
|
4399
|
+
private afterSpotCloseDecision<T>(
|
|
4400
|
+
meshName: string,
|
|
4401
|
+
spotId: RoutingId,
|
|
4402
|
+
seal: () => T
|
|
4403
|
+
): T | Promise<T> {
|
|
4404
|
+
const manager = this.requireSpotManager();
|
|
4405
|
+
const sealAfterDecision = (): T => {
|
|
4406
|
+
if (manager.isSpotClosing(meshName, spotId)) {
|
|
4407
|
+
throw createInternalFrameworkException(
|
|
4408
|
+
ZLinkFrameworkInternalErrorKind.SpotMoving,
|
|
4409
|
+
`Spot '${String(spotId)}' is closing.`,
|
|
4410
|
+
true
|
|
4411
|
+
);
|
|
4412
|
+
}
|
|
4413
|
+
return seal();
|
|
4414
|
+
};
|
|
4415
|
+
const decision = manager.pendingSpotCloseDecision(meshName, spotId);
|
|
4416
|
+
return decision === undefined ? sealAfterDecision() : decision.then(sealAfterDecision);
|
|
4417
|
+
}
|
|
4418
|
+
|
|
4098
4419
|
private requireActorManager(): DefaultZLinkActorManager {
|
|
4099
4420
|
const value = this.options.actorManager();
|
|
4100
4421
|
if (value === undefined) throw new Error('Host relocation requires the Actor manager.');
|
|
@@ -4231,14 +4552,7 @@ function actorJoinTargetMembership(
|
|
|
4231
4552
|
];
|
|
4232
4553
|
}
|
|
4233
4554
|
|
|
4234
|
-
function relocationDebug(marker: string, detail: Record<string, unknown>): void {
|
|
4235
|
-
if (process.env.ZLINK_DEBUG_FRAMEWORK_RELOCATION !== '1') return;
|
|
4236
|
-
console.error('[zlink.runtime.relocation]', marker, detail);
|
|
4237
|
-
}
|
|
4238
|
-
|
|
4239
4555
|
class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden> {
|
|
4240
|
-
private readonly deferredJoinRoots = new Map<string, ZLinkDeferredJoinAcceptedRoot>();
|
|
4241
|
-
|
|
4242
4556
|
constructor(
|
|
4243
4557
|
private readonly options: ZLinkHostRelocationOptions,
|
|
4244
4558
|
private readonly meshName: string,
|
|
@@ -4266,8 +4580,8 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4266
4580
|
participant.stableType,
|
|
4267
4581
|
participant.objectGeneration,
|
|
4268
4582
|
participant.authorityOwnerGeneration + 1n,
|
|
4269
|
-
|
|
4270
|
-
|
|
4583
|
+
nativeEntrySpot ? undefined : (spotIdentity as RoutingId),
|
|
4584
|
+
membership.spotObjectGeneration,
|
|
4271
4585
|
membership.membershipEpoch,
|
|
4272
4586
|
signal
|
|
4273
4587
|
);
|
|
@@ -4407,11 +4721,22 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4407
4721
|
hidden.participant.objectKind === 'user_spot' &&
|
|
4408
4722
|
registration.options?.relocationCoordinationMode ===
|
|
4409
4723
|
ZLinkSpotRelocationCoordinationMode.ApplicationSignaled;
|
|
4724
|
+
// createHidden could only predict the owner generation; the native Spot
|
|
4725
|
+
// takes the committed authority, as the Actor branch does above.
|
|
4726
|
+
const node = this.options.meshNode(this.meshName);
|
|
4727
|
+
node?.restoreSpotAuthority?.(
|
|
4728
|
+
String(hidden.activation.spotId),
|
|
4729
|
+
// createHidden stages an activation only for a Spot participant.
|
|
4730
|
+
hidden.participant.objectKind as 'user_spot' | 'instance_spot',
|
|
4731
|
+
hidden.participant.stableType,
|
|
4732
|
+
authority.objectGeneration,
|
|
4733
|
+
authority.authorityOwnerGeneration
|
|
4734
|
+
);
|
|
4410
4735
|
await this.requireSpotManager().publishRelocationSpot(
|
|
4411
4736
|
hidden.activation,
|
|
4412
4737
|
applicationSignaled ? () => hidden.activation!.notifyRelocatedBoundary() : undefined
|
|
4413
4738
|
);
|
|
4414
|
-
|
|
4739
|
+
node?.rememberSpotRoute?.({
|
|
4415
4740
|
spot: {
|
|
4416
4741
|
spotId: String(hidden.activation.spotId),
|
|
4417
4742
|
generation: authority.objectGeneration
|
|
@@ -4442,14 +4767,13 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4442
4767
|
}
|
|
4443
4768
|
|
|
4444
4769
|
async restoreBoundSession(hidden: LocalHidden, payload: Uint8Array): Promise<void> {
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
}
|
|
4770
|
+
const carried = decodeActorSession(payload);
|
|
4771
|
+
if (hidden.actor === undefined || carried === undefined) return;
|
|
4772
|
+
// The relocation delivers the binding it carried; the target installs it
|
|
4773
|
+
// through the one install rule (Session–Actor binding §6, §8).
|
|
4774
|
+
this.requireActorManager()
|
|
4775
|
+
.getState(hidden.actor.context.actorId)!
|
|
4776
|
+
.installBoundSessionBinding(carried);
|
|
4453
4777
|
}
|
|
4454
4778
|
|
|
4455
4779
|
async replayQueuedMessage(
|
|
@@ -4464,14 +4788,7 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4464
4788
|
if (recovery.request.actorId !== hidden.actor.context.actorId) {
|
|
4465
4789
|
throw new Error('Canonical Actor Join recovery names a different staged Actor.');
|
|
4466
4790
|
}
|
|
4467
|
-
|
|
4468
|
-
recovery,
|
|
4469
|
-
undefined,
|
|
4470
|
-
inventoryDigest(this.envelope.participants, this.envelope.memberships)
|
|
4471
|
-
);
|
|
4472
|
-
if (deferred !== undefined) {
|
|
4473
|
-
this.deferredJoinRoots.set(hidden.authorityKey, deferred);
|
|
4474
|
-
}
|
|
4791
|
+
await this.requireSpotManager().restoreCanonicalActorJoinRecovery(recovery);
|
|
4475
4792
|
return;
|
|
4476
4793
|
}
|
|
4477
4794
|
const packet = decodeQueuedHandoffPacket(message);
|
|
@@ -4480,10 +4797,6 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4480
4797
|
hidden.replayPackets.push(packet);
|
|
4481
4798
|
}
|
|
4482
4799
|
|
|
4483
|
-
deferredJoinRoot(authorityKey: string): ZLinkDeferredJoinAcceptedRoot | undefined {
|
|
4484
|
-
return this.deferredJoinRoots.get(authorityKey);
|
|
4485
|
-
}
|
|
4486
|
-
|
|
4487
4800
|
async openAdmission(hidden: LocalHidden): Promise<void> {
|
|
4488
4801
|
if (hidden.actor === undefined) return;
|
|
4489
4802
|
this.requireActorManager().publishRelocationActor(hidden.actor.context.actorId);
|
|
@@ -4916,9 +5229,11 @@ export function createServiceRelocationId(
|
|
|
4916
5229
|
entropy: (size: number) => Uint8Array = randomBytes
|
|
4917
5230
|
): string {
|
|
4918
5231
|
for (;;) {
|
|
4919
|
-
const bytes = entropy(
|
|
4920
|
-
if (bytes.byteLength !==
|
|
4921
|
-
throw new Error(
|
|
5232
|
+
const bytes = entropy(OPERATION_IDENTITY_BYTES);
|
|
5233
|
+
if (bytes.byteLength !== OPERATION_IDENTITY_BYTES) {
|
|
5234
|
+
throw new Error(
|
|
5235
|
+
`Relocation identity entropy must return exactly ${OPERATION_IDENTITY_BYTES} bytes.`
|
|
5236
|
+
);
|
|
4922
5237
|
}
|
|
4923
5238
|
let nonZero = false;
|
|
4924
5239
|
for (const byte of bytes) {
|
|
@@ -4959,19 +5274,64 @@ function sameCoordinator(
|
|
|
4959
5274
|
): boolean {
|
|
4960
5275
|
return (
|
|
4961
5276
|
left.ownerId === right.ownerId &&
|
|
4962
|
-
left.leaseGeneration === right.leaseGeneration &&
|
|
5277
|
+
BigInt(left.leaseGeneration) === right.leaseGeneration &&
|
|
4963
5278
|
left.nodeRid === right.nodeRid &&
|
|
4964
|
-
left.nodeGeneration === right.nodeGeneration &&
|
|
5279
|
+
BigInt(left.nodeGeneration) === right.nodeGeneration &&
|
|
4965
5280
|
left.expectedAuthorityStoreVersion === right.expectedAuthorityStoreVersion
|
|
4966
5281
|
);
|
|
4967
5282
|
}
|
|
4968
5283
|
|
|
5284
|
+
function sameRelocationTarget(
|
|
5285
|
+
left: ServiceWireRelocationTarget,
|
|
5286
|
+
right: ServiceWireRelocationTarget
|
|
5287
|
+
): boolean {
|
|
5288
|
+
return (
|
|
5289
|
+
left.nodeRid === right.nodeRid &&
|
|
5290
|
+
BigInt(left.nodeGeneration) === right.nodeGeneration &&
|
|
5291
|
+
left.ownerId === right.ownerId &&
|
|
5292
|
+
BigInt(left.ownerLeaseGeneration) === right.ownerLeaseGeneration
|
|
5293
|
+
);
|
|
5294
|
+
}
|
|
5295
|
+
|
|
5296
|
+
function sameRelocationObject(
|
|
5297
|
+
left: ServiceWireRelocationObject,
|
|
5298
|
+
right: ServiceWireRelocationObject
|
|
5299
|
+
): boolean {
|
|
5300
|
+
if (left.kind !== right.kind || BigInt(left.objectGeneration) !== right.objectGeneration) {
|
|
5301
|
+
return false;
|
|
5302
|
+
}
|
|
5303
|
+
if (left.kind === 'actor' && right.kind === 'actor') {
|
|
5304
|
+
return (
|
|
5305
|
+
left.actorId === right.actorId &&
|
|
5306
|
+
BigInt(left.expectedAuthorityOwnerGeneration) === right.expectedAuthorityOwnerGeneration
|
|
5307
|
+
);
|
|
5308
|
+
}
|
|
5309
|
+
if (left.kind === 'userSpot' && right.kind === 'userSpot') {
|
|
5310
|
+
return (
|
|
5311
|
+
left.spotId === right.spotId &&
|
|
5312
|
+
BigInt(left.expectedAuthorityOwnerGeneration) === right.expectedAuthorityOwnerGeneration
|
|
5313
|
+
);
|
|
5314
|
+
}
|
|
5315
|
+
return (
|
|
5316
|
+
left.kind === 'instanceSpot' &&
|
|
5317
|
+
right.kind === 'instanceSpot' &&
|
|
5318
|
+
left.spotId === right.spotId &&
|
|
5319
|
+
left.stableType === right.stableType
|
|
5320
|
+
);
|
|
5321
|
+
}
|
|
5322
|
+
|
|
4969
5323
|
function isExactSourceAuthority(
|
|
4970
5324
|
current: ZLinkAuthoritySnapshot,
|
|
4971
5325
|
expected: ZLinkAuthoritySnapshot
|
|
4972
5326
|
): boolean {
|
|
4973
5327
|
return (
|
|
4974
|
-
current.storeVersion.value === expected.storeVersion.value &&
|
|
5328
|
+
current.storeVersion.value === expected.storeVersion.value && isSourceOwner(current, expected)
|
|
5329
|
+
);
|
|
5330
|
+
}
|
|
5331
|
+
|
|
5332
|
+
/** The same source tenure owns the row, at any StoreVersion. */
|
|
5333
|
+
function isSourceOwner(current: ZLinkAuthoritySnapshot, expected: ZLinkAuthoritySnapshot): boolean {
|
|
5334
|
+
return (
|
|
4975
5335
|
current.objectGeneration === expected.objectGeneration &&
|
|
4976
5336
|
current.authorityOwnerGeneration === expected.authorityOwnerGeneration &&
|
|
4977
5337
|
current.ownerId === expected.ownerId &&
|
|
@@ -5002,36 +5362,6 @@ function isExactCommittedTarget(
|
|
|
5002
5362
|
);
|
|
5003
5363
|
}
|
|
5004
5364
|
|
|
5005
|
-
async function waitForLocationRetry(
|
|
5006
|
-
deadlineAtMs: number,
|
|
5007
|
-
signal: AbortSignal | undefined,
|
|
5008
|
-
cause: unknown
|
|
5009
|
-
): Promise<void> {
|
|
5010
|
-
signal?.throwIfAborted();
|
|
5011
|
-
if (performance.now() >= deadlineAtMs) {
|
|
5012
|
-
throw new Error(
|
|
5013
|
-
'location_update_failed: relocation Restore validity expired before exact target owner confirmation.',
|
|
5014
|
-
{ cause }
|
|
5015
|
-
);
|
|
5016
|
-
}
|
|
5017
|
-
await new Promise<void>((resolve, reject) => {
|
|
5018
|
-
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
5019
|
-
const aborted = () => {
|
|
5020
|
-
if (timer !== undefined) clearTimeout(timer);
|
|
5021
|
-
reject(signal?.reason);
|
|
5022
|
-
};
|
|
5023
|
-
signal?.addEventListener('abort', aborted, { once: true });
|
|
5024
|
-
timer = setTimeout(
|
|
5025
|
-
() => {
|
|
5026
|
-
signal?.removeEventListener('abort', aborted);
|
|
5027
|
-
resolve();
|
|
5028
|
-
},
|
|
5029
|
-
Math.min(25, Math.max(1, deadlineAtMs - performance.now()))
|
|
5030
|
-
);
|
|
5031
|
-
timer.unref();
|
|
5032
|
-
});
|
|
5033
|
-
}
|
|
5034
|
-
|
|
5035
5365
|
async function waitForRelocationRetry(delayMs: number, signal?: AbortSignal): Promise<void> {
|
|
5036
5366
|
signal?.throwIfAborted();
|
|
5037
5367
|
await new Promise<void>((resolve, reject) => {
|
|
@@ -5045,7 +5375,6 @@ async function waitForRelocationRetry(delayMs: number, signal?: AbortSignal): Pr
|
|
|
5045
5375
|
signal?.removeEventListener('abort', aborted);
|
|
5046
5376
|
resolve();
|
|
5047
5377
|
}, delayMs);
|
|
5048
|
-
timer.unref();
|
|
5049
5378
|
});
|
|
5050
5379
|
}
|
|
5051
5380
|
|
|
@@ -5087,14 +5416,16 @@ function maintenanceReplyRelay(
|
|
|
5087
5416
|
replyRouteId: BigInt(completion.source.replyRouteId),
|
|
5088
5417
|
participantId,
|
|
5089
5418
|
sequence: BigInt(completion.index + 1),
|
|
5090
|
-
terminalResult: completion.result.ok ?
|
|
5091
|
-
failureCode: completion.result.ok
|
|
5419
|
+
terminalResult: completion.result.ok ? RequestResult.Ok : RequestResult.InternalError,
|
|
5420
|
+
failureCode: completion.result.ok
|
|
5421
|
+
? ServiceWireFrameworkErrorCode.none
|
|
5422
|
+
: ServiceWireFrameworkErrorCode.requestFailed,
|
|
5092
5423
|
...(value === undefined
|
|
5093
5424
|
? {}
|
|
5094
5425
|
: {
|
|
5095
5426
|
payload: {
|
|
5096
|
-
packetName:
|
|
5097
|
-
contentType:
|
|
5427
|
+
packetName: SERVICE_RELOCATION_REPLY_PACKET_NAME,
|
|
5428
|
+
contentType: ZlinkStreamContentType.Json,
|
|
5098
5429
|
bytes: Buffer.from(JSON.stringify(value), 'utf8')
|
|
5099
5430
|
}
|
|
5100
5431
|
})
|
|
@@ -5108,16 +5439,16 @@ function handoffResultFromRelay(relay: ServiceMaintenanceReplyRelay): ZLinkActor
|
|
|
5108
5439
|
let value: unknown;
|
|
5109
5440
|
if (relay.payload !== undefined) {
|
|
5110
5441
|
if (
|
|
5111
|
-
relay.terminalResult !==
|
|
5112
|
-
relay.payload.packetName !==
|
|
5113
|
-
relay.payload.contentType !==
|
|
5442
|
+
relay.terminalResult !== RequestResult.Ok ||
|
|
5443
|
+
relay.payload.packetName !== SERVICE_RELOCATION_REPLY_PACKET_NAME ||
|
|
5444
|
+
relay.payload.contentType !== ZlinkStreamContentType.Json
|
|
5114
5445
|
) {
|
|
5115
5446
|
throw new TypeError('Relocation terminal payload boundary is invalid.');
|
|
5116
5447
|
}
|
|
5117
5448
|
value = JSON.parse(Buffer.from(relay.payload.bytes).toString('utf8')) as unknown;
|
|
5118
5449
|
}
|
|
5119
5450
|
const index = Number(relay.sequence - 1n);
|
|
5120
|
-
return relay.terminalResult ===
|
|
5451
|
+
return relay.terminalResult === RequestResult.Ok
|
|
5121
5452
|
? { index, ok: true, ...(value === undefined ? {} : { response: value }) }
|
|
5122
5453
|
: { index, ok: false, error: `Actor handoff failed with framework code ${relay.failureCode}.` };
|
|
5123
5454
|
}
|
|
@@ -5133,76 +5464,15 @@ function wireIdText(value: { readonly high: bigint; readonly low: bigint }): str
|
|
|
5133
5464
|
*/
|
|
5134
5465
|
export class ServiceRelocationDataLostError extends Error {}
|
|
5135
5466
|
|
|
5136
|
-
/**
|
|
5137
|
-
|
|
5138
|
-
* framework-error code the generated schema
|
|
5139
|
-
* ({@link ServiceWireFrameworkErrorCode}) defines — mirrors the java
|
|
5140
|
-
* reference mapping (commit 97fc074058) and the dotnet/cpp ports. The wire
|
|
5141
|
-
* vocabulary predates the framework's typed error kinds and has no
|
|
5142
|
-
* one-to-one code for every kind, so several kinds share the nearest fit
|
|
5143
|
-
* (documented per case below); unresolvable vocabulary gaps belong at the
|
|
5144
|
-
* schema level, not invented here. {@link ServiceRelocationDataLostError}
|
|
5145
|
-
* is this runtime's dedicated tag for a verified checksum/assembly/digest
|
|
5146
|
-
* integrity failure — the case that keeps relocationDataLost(35). An
|
|
5147
|
-
* unclassified error carries no evidence of integrity loss and takes the
|
|
5148
|
-
* generic opaque requestFailed(17). `objectKind` (the Prepare's
|
|
5149
|
-
* ObjectFence.kind, spec 28 §4.2) picks between an Actor- and
|
|
5150
|
-
* Spot-specific code where the schema splits by object kind.
|
|
5151
|
-
*/
|
|
5152
|
-
export function relocationFailedFailureCode(
|
|
5153
|
-
error: unknown,
|
|
5154
|
-
objectKind: ServiceWireRelocationObject['kind']
|
|
5155
|
-
): number {
|
|
5467
|
+
/** Classifies relocation failure with the error model §2.1 mapping. */
|
|
5468
|
+
export function relocationFailedFailureCode(error: unknown): number {
|
|
5156
5469
|
if (error instanceof ServiceRelocationDataLostError) {
|
|
5157
|
-
return
|
|
5470
|
+
return frameworkRelocationFailureCode(ZLinkFrameworkErrorKind.DataLost);
|
|
5158
5471
|
}
|
|
5159
5472
|
if (!(error instanceof ZLinkFrameworkException)) {
|
|
5160
|
-
return
|
|
5161
|
-
}
|
|
5162
|
-
switch (error.kind) {
|
|
5163
|
-
case ZLinkFrameworkErrorKind.DataLost:
|
|
5164
|
-
return ServiceWireFrameworkErrorCode.relocationDataLost;
|
|
5165
|
-
case ZLinkFrameworkErrorKind.Rejected:
|
|
5166
|
-
return ServiceWireFrameworkErrorCode.requestRejected;
|
|
5167
|
-
case ZLinkFrameworkErrorKind.ProtocolError:
|
|
5168
|
-
return ServiceWireFrameworkErrorCode.requestProtocolError;
|
|
5169
|
-
// No dedicated "deadline exceeded" wire code exists; a worker timeout
|
|
5170
|
-
// is the closest timeout-shaped signal.
|
|
5171
|
-
case ZLinkFrameworkErrorKind.DeadlineExceeded:
|
|
5172
|
-
return ServiceWireFrameworkErrorCode.workerTimedOut;
|
|
5173
|
-
// A stale generation/fence is the concrete cause of InvalidOperation
|
|
5174
|
-
// along this path (spec 15 failure table); pick the
|
|
5175
|
-
// object-kind-specific stale code.
|
|
5176
|
-
case ZLinkFrameworkErrorKind.InvalidOperation:
|
|
5177
|
-
return objectKind === 'actor'
|
|
5178
|
-
? ServiceWireFrameworkErrorCode.actorLocationStale
|
|
5179
|
-
: ServiceWireFrameworkErrorCode.spotGenerationStale;
|
|
5180
|
-
// No dedicated generic "unavailable" wire code exists; a disconnected
|
|
5181
|
-
// route is the closest "cannot reach/use the target" signal.
|
|
5182
|
-
case ZLinkFrameworkErrorKind.Unavailable:
|
|
5183
|
-
return ServiceWireFrameworkErrorCode.routeNotConnected;
|
|
5184
|
-
case ZLinkFrameworkErrorKind.NotFound:
|
|
5185
|
-
return ServiceWireFrameworkErrorCode.requestTargetNotFound;
|
|
5186
|
-
// The only "already exists" wire code is Actor-specific; not expected
|
|
5187
|
-
// along this target-failure path, mapped for completeness.
|
|
5188
|
-
case ZLinkFrameworkErrorKind.AlreadyExists:
|
|
5189
|
-
return ServiceWireFrameworkErrorCode.actorAlreadyExists;
|
|
5190
|
-
case ZLinkFrameworkErrorKind.TypeMismatch:
|
|
5191
|
-
return objectKind === 'actor'
|
|
5192
|
-
? ServiceWireFrameworkErrorCode.actorTypeMismatch
|
|
5193
|
-
: ServiceWireFrameworkErrorCode.spotTypeMismatch;
|
|
5194
|
-
// No dedicated "not configured" wire code exists; a missing configured
|
|
5195
|
-
// handler is the closest analog.
|
|
5196
|
-
case ZLinkFrameworkErrorKind.NotConfigured:
|
|
5197
|
-
return ServiceWireFrameworkErrorCode.handlerNotFound;
|
|
5198
|
-
// No dedicated generic "internal failure" or "shutting down" wire code
|
|
5199
|
-
// exists; the generic opaque request-failure code is the closest fit
|
|
5200
|
-
// for both (ShuttingDown included — re-judged 2026-08-19, C-10).
|
|
5201
|
-
case ZLinkFrameworkErrorKind.ShuttingDown:
|
|
5202
|
-
case ZLinkFrameworkErrorKind.InternalFailure:
|
|
5203
|
-
default:
|
|
5204
|
-
return ServiceWireFrameworkErrorCode.requestFailed;
|
|
5473
|
+
return frameworkRelocationFailureCode(ZLinkFrameworkErrorKind.InternalFailure);
|
|
5205
5474
|
}
|
|
5475
|
+
return frameworkRelocationFailureCode(error);
|
|
5206
5476
|
}
|
|
5207
5477
|
|
|
5208
5478
|
function relocationStagingId(value: {
|
|
@@ -5315,7 +5585,7 @@ function validatePrepareEnvelope(
|
|
|
5315
5585
|
if (
|
|
5316
5586
|
!sameWireId(request.relocation, relocationWireId(envelope.aggregateId)) ||
|
|
5317
5587
|
request.applicationVersion !== envelope.applicationVersion ||
|
|
5318
|
-
|
|
5588
|
+
!sameRelocationObject(request.object, relocationObject(envelope))
|
|
5319
5589
|
) {
|
|
5320
5590
|
throw new Error('Relocation Prepare does not match its immutable root identity.');
|
|
5321
5591
|
}
|
|
@@ -5332,17 +5602,10 @@ function validateTargetOneWayControl(
|
|
|
5332
5602
|
!sameWireId(request.relocation, prepare.relocation) ||
|
|
5333
5603
|
request.targetAttemptGeneration !== prepare.targetAttemptGeneration ||
|
|
5334
5604
|
!sameCoordinator(request.coordinator, prepare.coordinator) ||
|
|
5335
|
-
|
|
5336
|
-
stringifyWire(request.kind === 'data' ? request.object : prepare.object)
|
|
5605
|
+
(request.kind === 'cutover' && !sameRelocationObject(request.object, prepare.object))
|
|
5337
5606
|
) {
|
|
5338
5607
|
throw new Error('Relocation one-way control changed its exact Prepare fence.');
|
|
5339
5608
|
}
|
|
5340
|
-
if (
|
|
5341
|
-
request.kind === 'cutover' &&
|
|
5342
|
-
stringifyWire(request.object) !== stringifyWire(prepare.object)
|
|
5343
|
-
) {
|
|
5344
|
-
throw new Error('Relocation Cutover changed its prepared object.');
|
|
5345
|
-
}
|
|
5346
5609
|
}
|
|
5347
5610
|
|
|
5348
5611
|
function relocationPublication(
|
|
@@ -5447,7 +5710,7 @@ function canonicalQueuedFrozenRecord(
|
|
|
5447
5710
|
...(packet.returnResponse ? { replyRouteId: BigInt(packet.source!.replyRouteId) } : {}),
|
|
5448
5711
|
payload: {
|
|
5449
5712
|
packetName: '__zlink.actor.handoff.accepted',
|
|
5450
|
-
contentType:
|
|
5713
|
+
contentType: ZlinkStreamContentType.Json,
|
|
5451
5714
|
bytes: message.payload
|
|
5452
5715
|
}
|
|
5453
5716
|
});
|
|
@@ -5471,6 +5734,20 @@ function stringifyWire(value: unknown): string {
|
|
|
5471
5734
|
return JSON.stringify(value, (_key, item) => (typeof item === 'bigint' ? item.toString() : item));
|
|
5472
5735
|
}
|
|
5473
5736
|
|
|
5737
|
+
function samePrepareIdentity(
|
|
5738
|
+
acceptedFingerprint: string,
|
|
5739
|
+
incoming: ServiceMaintenanceRelocationPrepare
|
|
5740
|
+
): boolean {
|
|
5741
|
+
const accepted = JSON.parse(acceptedFingerprint) as ServiceMaintenanceRelocationPrepare;
|
|
5742
|
+
return (
|
|
5743
|
+
accepted.sourceNodeRid === incoming.sourceNodeRid &&
|
|
5744
|
+
BigInt(accepted.sourceNodeGeneration) === incoming.sourceNodeGeneration &&
|
|
5745
|
+
sameCoordinator(accepted.coordinator, incoming.coordinator) &&
|
|
5746
|
+
sameRelocationTarget(accepted.target, incoming.target) &&
|
|
5747
|
+
sameRelocationObject(accepted.object, incoming.object)
|
|
5748
|
+
);
|
|
5749
|
+
}
|
|
5750
|
+
|
|
5474
5751
|
function validateControlResponse(
|
|
5475
5752
|
request: ZLinkServiceRelocationControlRequest,
|
|
5476
5753
|
response: ZLinkServiceRelocationControlResponse
|
|
@@ -5481,8 +5758,8 @@ function validateControlResponse(
|
|
|
5481
5758
|
!sameWireId(response.relocation, request.relocation) ||
|
|
5482
5759
|
response.targetAttemptGeneration !== request.targetAttemptGeneration ||
|
|
5483
5760
|
!sameCoordinator(response.coordinator, request.coordinator) ||
|
|
5484
|
-
|
|
5485
|
-
|
|
5761
|
+
!sameRelocationTarget(response.target, request.target) ||
|
|
5762
|
+
!sameRelocationObject(response.object, request.object) ||
|
|
5486
5763
|
response.senderRole !== 'target'
|
|
5487
5764
|
) {
|
|
5488
5765
|
throw new Error('Relocation control response does not match the request fence.');
|
|
@@ -5505,82 +5782,19 @@ function validateControlFailureResponse(
|
|
|
5505
5782
|
!sameWireId(response.relocation, request.relocation) ||
|
|
5506
5783
|
response.targetAttemptGeneration !== request.targetAttemptGeneration ||
|
|
5507
5784
|
!sameCoordinator(response.coordinator, request.coordinator) ||
|
|
5508
|
-
|
|
5509
|
-
|
|
5785
|
+
!sameRelocationTarget(response.target, request.target) ||
|
|
5786
|
+
!sameRelocationObject(response.object, request.object) ||
|
|
5510
5787
|
response.senderRole !== 'target'
|
|
5511
5788
|
) {
|
|
5512
5789
|
throw new Error('Relocation control response does not match the request fence.');
|
|
5513
5790
|
}
|
|
5514
5791
|
}
|
|
5515
5792
|
|
|
5516
|
-
/**
|
|
5517
|
-
* Maps a relocationFailed(53) wire failureCode (the generated
|
|
5518
|
-
* ServiceWireFrameworkErrorCode vocabulary) to its internal error kind, so a
|
|
5519
|
-
* source-side reject carries the same classification the target chose
|
|
5520
|
-
* instead of a generic Error. Direct 1:1 correspondence with the generated
|
|
5521
|
-
* names; an unrecognised or absent code falls back to RequestFailed
|
|
5522
|
-
* (InternalFailure) — the closest existing kind (spec 15 §"Failed.Kind").
|
|
5523
|
-
*/
|
|
5524
|
-
function relocationFailureCodeKind(failureCode: number): ZLinkFrameworkInternalErrorKind {
|
|
5525
|
-
switch (failureCode) {
|
|
5526
|
-
case 1:
|
|
5527
|
-
return ZLinkFrameworkInternalErrorKind.ActorRouteNotFound;
|
|
5528
|
-
case 2:
|
|
5529
|
-
return ZLinkFrameworkInternalErrorKind.ActorCreateFailed;
|
|
5530
|
-
case 3:
|
|
5531
|
-
return ZLinkFrameworkInternalErrorKind.ActorAlreadyExists;
|
|
5532
|
-
case 4:
|
|
5533
|
-
return ZLinkFrameworkInternalErrorKind.ActorTypeMismatch;
|
|
5534
|
-
case 5:
|
|
5535
|
-
return ZLinkFrameworkInternalErrorKind.SpotCreateFailed;
|
|
5536
|
-
case 6:
|
|
5537
|
-
return ZLinkFrameworkInternalErrorKind.SpotRouteNotFound;
|
|
5538
|
-
case 7:
|
|
5539
|
-
return ZLinkFrameworkInternalErrorKind.SpotTypeMismatch;
|
|
5540
|
-
case 8:
|
|
5541
|
-
return ZLinkFrameworkInternalErrorKind.ActorSessionNotBound;
|
|
5542
|
-
case 9:
|
|
5543
|
-
return ZLinkFrameworkInternalErrorKind.HandlerNotFound;
|
|
5544
|
-
case 10:
|
|
5545
|
-
return ZLinkFrameworkInternalErrorKind.RouteHandlerNotFound;
|
|
5546
|
-
case 11:
|
|
5547
|
-
return ZLinkFrameworkInternalErrorKind.ActorDispatchHandlerNotFound;
|
|
5548
|
-
case 12:
|
|
5549
|
-
return ZLinkFrameworkInternalErrorKind.PayloadDecodeFailed;
|
|
5550
|
-
case 13:
|
|
5551
|
-
return ZLinkFrameworkInternalErrorKind.RouteNotConnected;
|
|
5552
|
-
case 14:
|
|
5553
|
-
return ZLinkFrameworkInternalErrorKind.RequestTargetNotFound;
|
|
5554
|
-
case 15:
|
|
5555
|
-
return ZLinkFrameworkInternalErrorKind.RequestRejected;
|
|
5556
|
-
case 16:
|
|
5557
|
-
return ZLinkFrameworkInternalErrorKind.RequestProtocolError;
|
|
5558
|
-
case 18:
|
|
5559
|
-
return ZLinkFrameworkInternalErrorKind.WorkerQueueFull;
|
|
5560
|
-
case 19:
|
|
5561
|
-
return ZLinkFrameworkInternalErrorKind.WorkerTimedOut;
|
|
5562
|
-
case 20:
|
|
5563
|
-
return ZLinkFrameworkInternalErrorKind.WorkerFailed;
|
|
5564
|
-
case 21:
|
|
5565
|
-
return ZLinkFrameworkInternalErrorKind.ActorLocationStale;
|
|
5566
|
-
case 22:
|
|
5567
|
-
return ZLinkFrameworkInternalErrorKind.ActorCreateRejected;
|
|
5568
|
-
case 33:
|
|
5569
|
-
return ZLinkFrameworkInternalErrorKind.SpotGenerationStale;
|
|
5570
|
-
case 34:
|
|
5571
|
-
return ZLinkFrameworkInternalErrorKind.SpotMoving;
|
|
5572
|
-
case 35:
|
|
5573
|
-
return ZLinkFrameworkInternalErrorKind.RelocationDataLost;
|
|
5574
|
-
case 17:
|
|
5575
|
-
default:
|
|
5576
|
-
return ZLinkFrameworkInternalErrorKind.RequestFailed;
|
|
5577
|
-
}
|
|
5578
|
-
}
|
|
5579
|
-
|
|
5580
5793
|
/** Builds the typed exception a Prepare waiter rejects with on an explicit Failed(53). */
|
|
5581
5794
|
function relocationFailureException(response: ServiceMaintenanceRelocationFailed) {
|
|
5582
5795
|
return createInternalFrameworkException(
|
|
5583
|
-
|
|
5796
|
+
internalFrameworkErrorKindFromWireFailureCode(response.failureCode) ??
|
|
5797
|
+
ZLinkFrameworkInternalErrorKind.RequestFailed,
|
|
5584
5798
|
`Relocation '${response.relocation.high}:${response.relocation.low}:` +
|
|
5585
5799
|
`${response.targetAttemptGeneration}' failed with wire failureCode ${response.failureCode}.`,
|
|
5586
5800
|
true
|
|
@@ -5760,7 +5974,7 @@ function queuedMessageU64(value: unknown, field: string): bigint {
|
|
|
5760
5974
|
throw new TypeError(`Relocation Actor Message Follow ${field} is invalid.`);
|
|
5761
5975
|
}
|
|
5762
5976
|
const result = BigInt(value);
|
|
5763
|
-
if (result >
|
|
5977
|
+
if (result > UINT64_MAX) {
|
|
5764
5978
|
throw new TypeError(`Relocation Actor Message Follow ${field} is outside u64.`);
|
|
5765
5979
|
}
|
|
5766
5980
|
return result;
|
|
@@ -5784,6 +5998,15 @@ function toServiceTimer(
|
|
|
5784
5998
|
};
|
|
5785
5999
|
}
|
|
5786
6000
|
|
|
6001
|
+
const PREPARE_SIDEBAND_MAGIC = 'ZLNI';
|
|
6002
|
+
const PREPARE_SIDEBAND_VERSION = 1;
|
|
6003
|
+
const PREPARE_SIDEBAND_HEADER_BYTES = 11;
|
|
6004
|
+
const PREPARE_SIDEBAND_VERSION_OFFSET = 4;
|
|
6005
|
+
const PREPARE_SIDEBAND_KEY_LENGTH_OFFSET = 5;
|
|
6006
|
+
const PREPARE_SIDEBAND_PAYLOAD_LENGTH_OFFSET = 7;
|
|
6007
|
+
const ACTOR_SESSION_JOURNAL_VERSION = 1;
|
|
6008
|
+
const SERVICE_RELOCATION_REPLY_PACKET_NAME = 'zlink.relocation.reply';
|
|
6009
|
+
|
|
5787
6010
|
/**
|
|
5788
6011
|
* NODE-INTERNAL Prepare sideband. Frame zero remains the exact canonical
|
|
5789
6012
|
* command-40 schema record; each following ZLNI frame carries one sealed
|
|
@@ -5799,11 +6022,11 @@ function encodePrepareSideband(envelope: ServiceRelocationEnvelope): readonly Bu
|
|
|
5799
6022
|
if (key.byteLength === 0 || key.byteLength > 0xffff || payload.byteLength > 0xffff_ffff) {
|
|
5800
6023
|
throw new TypeError('Bound-session relocation sideband is out of bounds.');
|
|
5801
6024
|
}
|
|
5802
|
-
const header = Buffer.allocUnsafe(
|
|
5803
|
-
header.write(
|
|
5804
|
-
header[
|
|
5805
|
-
header.writeUInt16BE(key.byteLength,
|
|
5806
|
-
header.writeUInt32BE(payload.byteLength,
|
|
6025
|
+
const header = Buffer.allocUnsafe(PREPARE_SIDEBAND_HEADER_BYTES);
|
|
6026
|
+
header.write(PREPARE_SIDEBAND_MAGIC, 0, 'ascii');
|
|
6027
|
+
header[PREPARE_SIDEBAND_VERSION_OFFSET] = PREPARE_SIDEBAND_VERSION;
|
|
6028
|
+
header.writeUInt16BE(key.byteLength, PREPARE_SIDEBAND_KEY_LENGTH_OFFSET);
|
|
6029
|
+
header.writeUInt32BE(payload.byteLength, PREPARE_SIDEBAND_PAYLOAD_LENGTH_OFFSET);
|
|
5807
6030
|
return Buffer.concat([header, key, payload]);
|
|
5808
6031
|
});
|
|
5809
6032
|
}
|
|
@@ -5819,19 +6042,29 @@ function decodePrepareSideband(frames: readonly Uint8Array[]):
|
|
|
5819
6042
|
for (const frame of frames) {
|
|
5820
6043
|
const bytes = Buffer.from(frame);
|
|
5821
6044
|
if (
|
|
5822
|
-
bytes.byteLength <
|
|
5823
|
-
bytes.subarray(0,
|
|
5824
|
-
|
|
6045
|
+
bytes.byteLength < PREPARE_SIDEBAND_HEADER_BYTES ||
|
|
6046
|
+
bytes.subarray(0, PREPARE_SIDEBAND_VERSION_OFFSET).toString('ascii') !==
|
|
6047
|
+
PREPARE_SIDEBAND_MAGIC ||
|
|
6048
|
+
bytes[PREPARE_SIDEBAND_VERSION_OFFSET] !== PREPARE_SIDEBAND_VERSION
|
|
5825
6049
|
) {
|
|
5826
6050
|
return undefined;
|
|
5827
6051
|
}
|
|
5828
|
-
const keyLength = bytes.readUInt16BE(
|
|
5829
|
-
const payloadLength = bytes.readUInt32BE(
|
|
5830
|
-
if (
|
|
5831
|
-
|
|
6052
|
+
const keyLength = bytes.readUInt16BE(PREPARE_SIDEBAND_KEY_LENGTH_OFFSET);
|
|
6053
|
+
const payloadLength = bytes.readUInt32BE(PREPARE_SIDEBAND_PAYLOAD_LENGTH_OFFSET);
|
|
6054
|
+
if (
|
|
6055
|
+
PREPARE_SIDEBAND_HEADER_BYTES + keyLength + payloadLength !== bytes.byteLength ||
|
|
6056
|
+
keyLength === 0
|
|
6057
|
+
)
|
|
6058
|
+
return undefined;
|
|
6059
|
+
const participantKey = bytes
|
|
6060
|
+
.subarray(PREPARE_SIDEBAND_HEADER_BYTES, PREPARE_SIDEBAND_HEADER_BYTES + keyLength)
|
|
6061
|
+
.toString('utf8');
|
|
5832
6062
|
if (keys.has(participantKey)) return undefined;
|
|
5833
6063
|
keys.add(participantKey);
|
|
5834
|
-
result.push({
|
|
6064
|
+
result.push({
|
|
6065
|
+
participantKey,
|
|
6066
|
+
payload: Buffer.from(bytes.subarray(PREPARE_SIDEBAND_HEADER_BYTES + keyLength))
|
|
6067
|
+
});
|
|
5835
6068
|
}
|
|
5836
6069
|
return result;
|
|
5837
6070
|
}
|
|
@@ -5862,13 +6095,16 @@ function encodeActorSession(target: ZLinkRemoteBoundSessionTarget | undefined):
|
|
|
5862
6095
|
if (target === undefined) return Buffer.alloc(0);
|
|
5863
6096
|
return Buffer.from(
|
|
5864
6097
|
JSON.stringify({
|
|
5865
|
-
version:
|
|
6098
|
+
version: ACTOR_SESSION_JOURNAL_VERSION,
|
|
5866
6099
|
routerChannelId: target.routerChannelId,
|
|
5867
6100
|
targetNodeRid: String(target.targetNodeRid),
|
|
5868
6101
|
spotId: String(target.spotId),
|
|
5869
6102
|
sessionNodeRid:
|
|
5870
6103
|
target.sessionNodeRid === undefined ? undefined : String(target.sessionNodeRid),
|
|
5871
6104
|
sessionRid: target.sessionRid === undefined ? undefined : String(target.sessionRid),
|
|
6105
|
+
sessionOwnerNodeGeneration: target.sessionOwnerNodeGeneration?.toString(),
|
|
6106
|
+
sessionOwnerId: target.sessionOwnerId,
|
|
6107
|
+
sessionOwnerLeaseGeneration: target.sessionOwnerLeaseGeneration?.toString(),
|
|
5872
6108
|
bindingGeneration: target.bindingGeneration?.toString(),
|
|
5873
6109
|
previousAuthorityOwnerGeneration: target.previousAuthorityOwnerGeneration?.toString(),
|
|
5874
6110
|
previousOwnerLeaseGeneration: target.previousOwnerLeaseGeneration?.toString(),
|
|
@@ -5901,12 +6137,13 @@ function encodeActorSession(target: ZLinkRemoteBoundSessionTarget | undefined):
|
|
|
5901
6137
|
);
|
|
5902
6138
|
}
|
|
5903
6139
|
|
|
5904
|
-
function decodeActorSession(payload: Uint8Array): ZLinkRemoteBoundSessionTarget {
|
|
6140
|
+
function decodeActorSession(payload: Uint8Array): ZLinkRemoteBoundSessionTarget | undefined {
|
|
6141
|
+
if (payload.byteLength === 0) return undefined;
|
|
5905
6142
|
const value = JSON.parse(Buffer.from(payload).toString('utf8')) as Record<string, unknown>;
|
|
5906
6143
|
const optionalBigInt = (field: string) =>
|
|
5907
6144
|
typeof value[field] === 'string' ? BigInt(value[field] as string) : undefined;
|
|
5908
6145
|
if (
|
|
5909
|
-
value.version !==
|
|
6146
|
+
value.version !== ACTOR_SESSION_JOURNAL_VERSION ||
|
|
5910
6147
|
typeof value.routerChannelId !== 'string' ||
|
|
5911
6148
|
typeof value.targetNodeRid !== 'string' ||
|
|
5912
6149
|
typeof value.spotId !== 'string'
|
|
@@ -5922,6 +6159,13 @@ function decodeActorSession(payload: Uint8Array): ZLinkRemoteBoundSessionTarget
|
|
|
5922
6159
|
? { sessionNodeRid: value.sessionNodeRid as RoutingId }
|
|
5923
6160
|
: {}),
|
|
5924
6161
|
...(typeof value.sessionRid === 'string' ? { sessionRid: value.sessionRid as RoutingId } : {}),
|
|
6162
|
+
...(optionalBigInt('sessionOwnerNodeGeneration') === undefined
|
|
6163
|
+
? {}
|
|
6164
|
+
: { sessionOwnerNodeGeneration: optionalBigInt('sessionOwnerNodeGeneration') }),
|
|
6165
|
+
...(typeof value.sessionOwnerId === 'string' ? { sessionOwnerId: value.sessionOwnerId } : {}),
|
|
6166
|
+
...(optionalBigInt('sessionOwnerLeaseGeneration') === undefined
|
|
6167
|
+
? {}
|
|
6168
|
+
: { sessionOwnerLeaseGeneration: optionalBigInt('sessionOwnerLeaseGeneration') }),
|
|
5925
6169
|
...(optionalBigInt('bindingGeneration') === undefined
|
|
5926
6170
|
? {}
|
|
5927
6171
|
: { bindingGeneration: optionalBigInt('bindingGeneration') }),
|
|
@@ -6034,3 +6278,16 @@ function addCapacity(left: ZLinkCapacityVector, right: ZLinkCapacityVector): ZLi
|
|
|
6034
6278
|
})
|
|
6035
6279
|
};
|
|
6036
6280
|
}
|
|
6281
|
+
|
|
6282
|
+
if (isStructuralGuardEnabled()) {
|
|
6283
|
+
const relocateActorJoin = ZLinkHostServiceRelocationRuntime.prototype.relocateActorJoin;
|
|
6284
|
+
ZLinkHostServiceRelocationRuntime.prototype.relocateActorJoin = function (
|
|
6285
|
+
this: ZLinkHostServiceRelocationRuntime,
|
|
6286
|
+
input: Parameters<ZLinkActorJoinRelocation['relocateActorJoin']>[0]
|
|
6287
|
+
): ReturnType<ZLinkActorJoinRelocation['relocateActorJoin']> {
|
|
6288
|
+
return trackDiagnosticCompletion(
|
|
6289
|
+
relocateActorJoin.call(this, input),
|
|
6290
|
+
`relocation join actor:${input.state.actorId}`
|
|
6291
|
+
);
|
|
6292
|
+
};
|
|
6293
|
+
}
|