@zlink-systems/framework 0.21.0 → 0.23.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/Common/ZLinkMessage.js +5 -5
- package/dist/contracts/Configuration/CodecContentType.js +5 -7
- package/dist/contracts/Configuration/DiagnosticsValidation.js +1 -6
- package/dist/contracts/Configuration/EndpointNotation.js +4 -8
- package/dist/contracts/Configuration/Registration.js +14 -11
- package/dist/contracts/Configuration/RegistrationBuilders.js +32 -25
- package/dist/contracts/Configuration/RegistrationNormalizers.js +25 -26
- package/dist/contracts/Configuration/RegistrationValidators.js +36 -38
- package/dist/contracts/Configuration/SendTimeoutValidation.js +4 -4
- package/dist/contracts/Configuration/TimerRegistrationValidator.js +7 -7
- package/dist/contracts/Dispatch/ZLinkDispatchOptions.d.ts +1 -0
- package/dist/contracts/Handlers/JsonContract.js +23 -11
- package/dist/contracts/Spots/SpotKind.js +16 -8
- package/dist/contracts/Streams/IZLinkSession.d.ts +2 -1
- package/dist/location-store-integration.js +16 -16
- package/dist/nest-integration.js +17 -11
- package/dist/runtime/abort.js +9 -5
- package/dist/runtime/actors/actor-authority-payload-codec.js +12 -16
- package/dist/runtime/actors/actor-authority-publication.js +18 -17
- package/dist/runtime/actors/actor-client.js +83 -63
- package/dist/runtime/actors/actor-context.js +28 -22
- package/dist/runtime/actors/actor-creation.js +4 -3
- package/dist/runtime/actors/actor-handoff.js +89 -92
- package/dist/runtime/actors/actor-join-deferred-scope.d.ts +0 -1
- package/dist/runtime/actors/actor-join-deferred-scope.js +0 -18
- package/dist/runtime/actors/actor-local-native-join.js +18 -19
- package/dist/runtime/actors/actor-mailbox.js +1 -1
- package/dist/runtime/actors/actor-message-follow-context.js +24 -21
- package/dist/runtime/actors/actor-packet-relay-wire.js +1 -3
- package/dist/runtime/actors/actor-remote-joiner.js +20 -17
- package/dist/runtime/actors/actor-remote-wire.js +6 -7
- package/dist/runtime/actors/actor-runtime-state.js +87 -81
- package/dist/runtime/actors/actor-transfer-registry.js +3 -5
- package/dist/runtime/actors/bound-session-wire.js +14 -9
- package/dist/runtime/actors/deferred-join-accepted-journal.js +160 -130
- package/dist/runtime/actors/index.js +2 -5
- package/dist/runtime/actors/post-commit-actor-binder.js +3 -6
- package/dist/runtime/actors/post-commit-actor-location.js +1 -1
- package/dist/runtime/actors/spot-actor-dispatch.js +5 -5
- package/dist/runtime/actors/transferred-actor-rollback.js +4 -4
- package/dist/runtime/admission.js +2 -6
- package/dist/runtime/application-jobs/application-ingress-record-owner.js +2 -6
- package/dist/runtime/application-jobs/receive-flow-controller.js +11 -12
- package/dist/runtime/backend/contracts/index.d.ts +1 -0
- package/dist/runtime/backend/mesh-completion-table.js +1 -3
- package/dist/runtime/backend/mesh-dispatch-pump.js +12 -16
- package/dist/runtime/backend/node/node-backend-adapter-factory.js +1 -3
- package/dist/runtime/backend/node/node-backend-adapter-support.js +26 -17
- package/dist/runtime/backend/node/node-mesh-backend-adapter.js +4 -3
- package/dist/runtime/backend/node/node-raw-binding-port.js +7 -6
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +3 -0
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +134 -118
- package/dist/runtime/backend/node/node-socket-backend-adapter.js +12 -13
- package/dist/runtime/backend/runtime-message.js +3 -3
- package/dist/runtime/backend/runtime-values.d.ts +2 -2
- package/dist/runtime/backend/runtime-values.js +4 -5
- package/dist/runtime/channels/channel-clients.js +27 -24
- package/dist/runtime/channels/channel-dispatch-pipeline.js +12 -20
- package/dist/runtime/channels/channel-dispatch-services.js +7 -4
- package/dist/runtime/channels/channel-dispatchers.js +9 -9
- package/dist/runtime/channels/channel-envelope-inspection.js +16 -13
- package/dist/runtime/channels/channel-envelope.js +83 -47
- package/dist/runtime/channels/channel-outbound-operations.js +8 -8
- package/dist/runtime/channels/channel-receive-loops.js +20 -22
- package/dist/runtime/channels/channel-receive-task-tracker.js +1 -1
- package/dist/runtime/channels/channel-runtime-lifecycle.js +61 -56
- package/dist/runtime/channels/channel-runtime-manager.js +18 -9
- package/dist/runtime/channels/channel-socket-registry.js +126 -123
- package/dist/runtime/channels/channel-transports.js +46 -42
- package/dist/runtime/channels/client-server-location-runtime.js +32 -40
- package/dist/runtime/channels/client-server-service-wire.js +29 -22
- package/dist/runtime/channels/dispatch-error-reporter.d.ts +1 -0
- package/dist/runtime/channels/dispatch-error-reporter.js +9 -39
- package/dist/runtime/channels/fanout-location-runtime.js +34 -47
- package/dist/runtime/channels/fanout-service-wire.js +3 -5
- package/dist/runtime/channels/local-spot-route-transport.js +7 -1
- package/dist/runtime/channels/route-member-snapshot.js +3 -3
- package/dist/runtime/channels/source-spot-router.js +6 -2
- package/dist/runtime/channels/spot-route-bridge-transport.js +5 -2
- package/dist/runtime/channels/spot-route-dispatch-strategy.js +2 -2
- package/dist/runtime/channels/spot-route-target-resolver.js +6 -6
- package/dist/runtime/diagnostics/dispatch-error-details.d.ts +6 -0
- package/dist/runtime/diagnostics/dispatch-error-details.js +55 -0
- package/dist/runtime/diagnostics/flow-context.d.ts +2 -1
- package/dist/runtime/diagnostics/flow-context.js +2 -2
- package/dist/runtime/diagnostics/index.js +38 -10
- package/dist/runtime/diagnostics/message-flow.js +58 -42
- package/dist/runtime/diagnostics/runtime-metrics.js +18 -9
- package/dist/runtime/diagnostics/runtime-observation-queue.js +8 -10
- package/dist/runtime/diagnostics/topology-runtime-projections.js +17 -10
- package/dist/runtime/execution/index.js +7 -6
- package/dist/runtime/execution/serial-execution-queue.js +12 -18
- package/dist/runtime/execution/state-lane.js +2 -2
- package/dist/runtime/foundation/actor-join-recovery-codec.js +31 -27
- package/dist/runtime/foundation/event-loop-resources.js +4 -4
- package/dist/runtime/foundation/message-follow-suppression-registry.js +4 -4
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +4 -2
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +169 -144
- package/dist/runtime/foundation/route-mesh-connection-policy.js +4 -4
- package/dist/runtime/foundation/runtime-state-projections.js +22 -11
- package/dist/runtime/foundation/service-authority-payload-codec.js +13 -15
- package/dist/runtime/foundation/service-discovery-registry.js +35 -33
- package/dist/runtime/foundation/service-instance-activation-recovery-codec.js +20 -22
- package/dist/runtime/foundation/service-liveness-registry.js +16 -12
- package/dist/runtime/foundation/service-maintenance-runtime.js +2 -4
- package/dist/runtime/foundation/service-relocation-aggregate-committer.js +24 -24
- package/dist/runtime/foundation/service-relocation-object-owner.js +6 -6
- package/dist/runtime/foundation/service-relocation-runtime.js +137 -94
- package/dist/runtime/foundation/service-runtime-contracts.js +5 -5
- package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +1 -0
- package/dist/runtime/foundation/service-stateful-registry.js +24 -23
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +4 -0
- package/dist/runtime/foundation/service-stateful-runtime.js +461 -439
- package/dist/runtime/foundation/service-stateful-wire-codec.js +95 -98
- package/dist/runtime/foundation/service-topology-registry.d.ts +1 -0
- package/dist/runtime/foundation/service-topology-registry.js +105 -96
- package/dist/runtime/foundation/service-weighted-selection.js +6 -11
- package/dist/runtime/foundation/service-wire-binary-primitives.js +5 -6
- package/dist/runtime/foundation/service-wire-m6a-codec.js +46 -35
- package/dist/runtime/framework-errors-internal.js +7 -10
- package/dist/runtime/handlers/handler-instance-scope.js +4 -5
- package/dist/runtime/handlers/index.js +3 -1
- package/dist/runtime/host/actor-packet-relay.js +74 -70
- package/dist/runtime/host/actor-placement-coordinator.js +29 -31
- package/dist/runtime/host/actor-runtime-options-factory.js +12 -14
- package/dist/runtime/host/actor-transfer-authority-runtime.js +7 -9
- package/dist/runtime/host/actor-transfer-runtime.js +176 -153
- package/dist/runtime/host/application-job-queue.js +18 -22
- package/dist/runtime/host/bound-session-relay.js +3 -1
- package/dist/runtime/host/channel-runtime-options-factory.js +31 -14
- package/dist/runtime/host/host-capacity-status.js +8 -4
- package/dist/runtime/host/index.js +245 -228
- package/dist/runtime/host/instance-activation-authority.js +93 -96
- package/dist/runtime/host/location-runtime-owner.js +16 -14
- package/dist/runtime/host/mesh-router-resolver.js +11 -13
- package/dist/runtime/host/relocation-direct-transfer.js +10 -9
- package/dist/runtime/host/remote-actor-join-receiver.js +20 -15
- package/dist/runtime/host/remote-actor-packet-target-store.js +18 -22
- package/dist/runtime/host/remote-bound-session-relay.js +97 -73
- package/dist/runtime/host/route-mesh-runtime.js +64 -69
- package/dist/runtime/host/runtime-shutdown.js +1 -2
- package/dist/runtime/host/service-relocation-host-runtime.js +586 -511
- package/dist/runtime/host/spot-address-transport.js +69 -71
- package/dist/runtime/host/spot-runtime-options-factory.js +30 -29
- package/dist/runtime/host/stateful-authority-route-runtime.js +65 -68
- package/dist/runtime/host/user-spot-creation-coordinator.js +61 -67
- package/dist/runtime/locations/actor-location-claims.js +26 -20
- package/dist/runtime/locations/aggregate-inventory-store.js +72 -75
- package/dist/runtime/locations/authority-key-codec.js +7 -7
- package/dist/runtime/locations/auto-connect-loop.js +3 -3
- package/dist/runtime/locations/auto-connect-planner.js +25 -19
- package/dist/runtime/locations/auto-connect-reconciler.js +51 -31
- package/dist/runtime/locations/in-memory-authority-store.js +128 -118
- package/dist/runtime/locations/in-memory-location-store.js +195 -168
- package/dist/runtime/locations/in-memory-provider-location-store.js +8 -10
- package/dist/runtime/locations/key-codec.js +1 -1
- package/dist/runtime/locations/lease-tracker.js +23 -21
- package/dist/runtime/locations/lifecycle.js +4 -5
- package/dist/runtime/locations/location-store-repository.js +368 -324
- package/dist/runtime/locations/opaque-record-key.js +2 -2
- package/dist/runtime/locations/resolvers.js +73 -82
- package/dist/runtime/locations/runtime.js +68 -63
- package/dist/runtime/locations/spot-location-claims.js +39 -35
- package/dist/runtime/messaging/framework-json-v1.js +7 -7
- package/dist/runtime/messaging/packet-name.js +7 -6
- package/dist/runtime/messaging/payload-codec.js +21 -18
- package/dist/runtime/protocol/service_wire_codec.generated.d.ts +30 -2
- package/dist/runtime/protocol/service_wire_codec.generated.js +1671 -234
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +20 -18
- package/dist/runtime/routing-id.js +3 -5
- package/dist/runtime/spots/formal-remote-actor-admission-registry.js +35 -31
- package/dist/runtime/spots/formal-remote-actor-transfer-registry.js +1 -2
- package/dist/runtime/spots/index.js +209 -220
- package/dist/runtime/spots/spot-activation-registry.js +15 -14
- package/dist/runtime/spots/spot-activation-state.js +17 -15
- package/dist/runtime/spots/spot-activation.js +33 -16
- package/dist/runtime/spots/spot-actor-admission-coordinator.js +18 -15
- package/dist/runtime/spots/spot-actor-membership.js +1 -3
- package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +2 -1
- package/dist/runtime/spots/spot-actor-packet-dispatch.js +29 -21
- package/dist/runtime/spots/spot-actor-packet-drain.js +14 -10
- package/dist/runtime/spots/spot-actor-packet-relay-dispatch.js +10 -6
- package/dist/runtime/spots/spot-closing.js +3 -7
- package/dist/runtime/spots/spot-context.js +2 -2
- package/dist/runtime/spots/spot-entry-activation.js +13 -14
- package/dist/runtime/spots/spot-handle.js +6 -3
- package/dist/runtime/spots/spot-handler-registry.js +12 -12
- package/dist/runtime/spots/spot-manager-public.js +5 -6
- package/dist/runtime/spots/spot-native-actor-join-admission.js +4 -3
- package/dist/runtime/spots/spot-node-autoconnect.js +15 -18
- package/dist/runtime/spots/spot-node-runtime-manager.js +79 -86
- package/dist/runtime/spots/spot-outbound.js +10 -14
- package/dist/runtime/spots/spot-remote-codec.js +22 -26
- package/dist/runtime/spots/spot-remote-route-codec.js +5 -7
- package/dist/runtime/spots/spot-route-packet-dispatch.js +17 -15
- package/dist/runtime/spots/spot-route-replies.js +4 -6
- package/dist/runtime/spots/spot-routed-actor-admission.js +9 -7
- package/dist/runtime/spots/spot-routed-bound-session-dispatch.js +2 -3
- package/dist/runtime/spots/spot-routed-frame-dispatch.js +5 -5
- package/dist/runtime/spots/spot-routed-spot-packet-dispatch.js +14 -10
- package/dist/runtime/spots/spot-serial-executor.js +5 -8
- package/dist/runtime/spots/spot-serial-turn-executor.js +4 -6
- package/dist/runtime/spots/spot-subscription-dispatch.js +6 -6
- package/dist/runtime/spots/spot-timer.js +26 -30
- package/dist/runtime/streams/actor-session-binding-registry.d.ts +21 -8
- package/dist/runtime/streams/actor-session-binding-registry.js +168 -148
- package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +1 -0
- package/dist/runtime/streams/actor-session-lifecycle-coordinator.js +3 -1
- package/dist/runtime/streams/bound-actor-relay-sender.js +20 -17
- package/dist/runtime/streams/bound-session-response-target.js +8 -1
- package/dist/runtime/streams/bound-session-service.d.ts +1 -0
- package/dist/runtime/streams/bound-session-service.js +14 -12
- package/dist/runtime/streams/index.js +28 -20
- package/dist/runtime/streams/managed-stream.d.ts +3 -0
- package/dist/runtime/streams/managed-stream.js +52 -31
- package/dist/runtime/streams/native-fallback-bound-session.js +21 -17
- package/dist/runtime/streams/protocol.d.ts +5 -1
- package/dist/runtime/streams/protocol.js +68 -20
- package/dist/runtime/streams/session-actor-coordinator.js +37 -41
- package/dist/runtime/streams/session-context.d.ts +9 -2
- package/dist/runtime/streams/session-context.js +50 -32
- package/dist/runtime/streams/session-local-actors.d.ts +3 -1
- package/dist/runtime/streams/session-local-actors.js +17 -2
- package/dist/runtime/streams/session-provider.js +2 -2
- package/dist/runtime/streams/session-serial-executor.js +1 -2
- package/dist/runtime/streams/stream-frame-factory.d.ts +1 -1
- package/dist/runtime/streams/stream-frame-factory.js +14 -7
- package/dist/runtime/streams/stream-session-runtime.js +112 -78
- package/dist/runtime/workers/index.js +15 -4
- package/package.json +3 -3
- package/src/contracts/Channels/RouteCalls.ts +6 -1
- package/src/contracts/Common/ZLinkMessage.ts +5 -5
- package/src/contracts/Common/ZLinkMessageMetadata.ts +4 -3
- package/src/contracts/Configuration/Builders.ts +14 -14
- package/src/contracts/Configuration/CodecContentType.ts +8 -10
- package/src/contracts/Configuration/DiagnosticsValidation.ts +1 -6
- package/src/contracts/Configuration/EndpointNotation.ts +11 -11
- package/src/contracts/Configuration/ObjectRoles.ts +1 -3
- package/src/contracts/Configuration/Registration.ts +46 -17
- package/src/contracts/Configuration/RegistrationBuilderPolicy.ts +10 -8
- package/src/contracts/Configuration/RegistrationBuilders.ts +107 -96
- package/src/contracts/Configuration/RegistrationCodecRegistry.ts +9 -3
- package/src/contracts/Configuration/RegistrationNormalizers.ts +116 -84
- package/src/contracts/Configuration/RegistrationTypes.ts +21 -25
- package/src/contracts/Configuration/RegistrationValidators.ts +122 -73
- package/src/contracts/Configuration/RouteChannelInternalState.ts +1 -4
- package/src/contracts/Configuration/RuntimeEndpointConnections.ts +4 -1
- package/src/contracts/Configuration/SendTimeoutValidation.ts +6 -4
- package/src/contracts/Configuration/TimerRegistrationValidator.ts +8 -13
- package/src/contracts/Dispatch/ZLinkDispatchOptions.ts +2 -6
- package/src/contracts/Errors/index.ts +1 -4
- package/src/contracts/Handlers/Attributes.ts +15 -9
- package/src/contracts/Handlers/IZLinkChannelHandlers.ts +24 -4
- package/src/contracts/Handlers/JsonContract.ts +53 -19
- package/src/contracts/Locations/ActorTransfer.ts +1 -5
- package/src/contracts/Locations/Authority.ts +4 -11
- package/src/contracts/Locations/Keys.ts +6 -1
- package/src/contracts/Locations/Options.ts +3 -2
- package/src/contracts/Locations/RelocationStore.ts +1 -4
- package/src/contracts/Locations/Resolvers.ts +5 -5
- package/src/contracts/Locations/RuntimeQuery.ts +6 -2
- package/src/contracts/Locations/Stores.ts +1 -2
- package/src/contracts/Locations/index.ts +2 -8
- package/src/contracts/RouteMesh/Contracts.ts +1 -7
- package/src/contracts/RouteMesh/RuntimeTopology.ts +1 -4
- package/src/contracts/Spots/Contracts.ts +2 -8
- package/src/contracts/Spots/SpotKind.ts +16 -8
- package/src/contracts/Spots/ZLinkSpot.ts +8 -11
- package/src/contracts/Streams/IZLinkSession.ts +2 -2
- package/src/contracts/Streams/IZLinkSessionActor.ts +5 -1
- package/src/contracts/Streams/IZLinkSessionPacketHandler.ts +5 -1
- package/src/internal.ts +5 -4
- package/src/location-store-integration.ts +24 -16
- package/src/nest-integration.ts +40 -25
- package/src/runtime/abort.ts +10 -4
- package/src/runtime/actors/actor-authority-payload-codec.ts +20 -29
- package/src/runtime/actors/actor-authority-publication.ts +92 -77
- package/src/runtime/actors/actor-client.ts +162 -133
- package/src/runtime/actors/actor-context.ts +142 -134
- package/src/runtime/actors/actor-creation.ts +47 -44
- package/src/runtime/actors/actor-execution-context.ts +6 -4
- package/src/runtime/actors/actor-handoff.ts +269 -293
- package/src/runtime/actors/actor-join-deferred-scope.ts +1 -32
- package/src/runtime/actors/actor-lifecycle-snapshot.ts +5 -8
- package/src/runtime/actors/actor-local-native-join.ts +131 -159
- package/src/runtime/actors/actor-mailbox.ts +16 -19
- package/src/runtime/actors/actor-message-follow-context.ts +46 -36
- package/src/runtime/actors/actor-packet-relay-wire.ts +32 -21
- package/src/runtime/actors/actor-remote-joiner.ts +50 -54
- package/src/runtime/actors/actor-remote-wire.ts +9 -13
- package/src/runtime/actors/actor-routed-join-transport.ts +10 -2
- package/src/runtime/actors/actor-runtime-contracts.ts +2 -5
- package/src/runtime/actors/actor-runtime-state.ts +127 -124
- package/src/runtime/actors/actor-transfer-registry.ts +9 -14
- package/src/runtime/actors/actor-transport-delivery-gate.ts +4 -6
- package/src/runtime/actors/bound-session-wire.ts +37 -17
- package/src/runtime/actors/deferred-join-accepted-journal.ts +353 -326
- package/src/runtime/actors/index.ts +43 -86
- package/src/runtime/actors/post-commit-actor-binder.ts +10 -10
- package/src/runtime/actors/post-commit-actor-location.ts +30 -23
- package/src/runtime/actors/spot-actor-dispatch.ts +78 -53
- package/src/runtime/actors/transferred-actor-rollback.ts +18 -13
- package/src/runtime/admission.ts +9 -9
- package/src/runtime/application-jobs/application-ingress-record-owner.ts +5 -15
- package/src/runtime/application-jobs/application-job-queue-handler-start-gate.ts +3 -2
- package/src/runtime/application-jobs/receive-flow-controller.ts +17 -14
- package/src/runtime/backend/contracts/index.ts +77 -52
- package/src/runtime/backend/mesh-actor-session-node-adapter.ts +4 -14
- package/src/runtime/backend/mesh-completion-table.ts +6 -9
- package/src/runtime/backend/mesh-dispatch-pump.ts +38 -30
- package/src/runtime/backend/node/node-backend-adapter-factory.ts +23 -21
- package/src/runtime/backend/node/node-backend-adapter-support.ts +36 -22
- package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +59 -58
- package/src/runtime/backend/node/node-event-loop-poller.ts +2 -10
- package/src/runtime/backend/node/node-mesh-backend-adapter.ts +12 -5
- package/src/runtime/backend/node/node-monitor-backend-adapter.ts +4 -3
- package/src/runtime/backend/node/node-raw-binding-port.ts +29 -29
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +370 -390
- package/src/runtime/backend/node/node-socket-backend-adapter.ts +101 -54
- package/src/runtime/backend/runtime-message.ts +5 -3
- package/src/runtime/backend/runtime-values.ts +10 -7
- package/src/runtime/channels/channel-autoconnect.ts +1 -4
- package/src/runtime/channels/channel-clients.ts +93 -73
- package/src/runtime/channels/channel-dispatch-pipeline.ts +44 -44
- package/src/runtime/channels/channel-dispatch-services.ts +51 -49
- package/src/runtime/channels/channel-dispatchers.ts +152 -96
- package/src/runtime/channels/channel-envelope-inspection.ts +20 -16
- package/src/runtime/channels/channel-envelope.ts +256 -150
- package/src/runtime/channels/channel-multipart.ts +13 -5
- package/src/runtime/channels/channel-outbound-operations.ts +37 -41
- package/src/runtime/channels/channel-receive-loops.ts +102 -98
- package/src/runtime/channels/channel-receive-task-tracker.ts +1 -1
- package/src/runtime/channels/channel-runtime-lifecycle.ts +248 -190
- package/src/runtime/channels/channel-runtime-manager.ts +63 -37
- package/src/runtime/channels/channel-socket-options.ts +3 -10
- package/src/runtime/channels/channel-socket-registry.ts +331 -273
- package/src/runtime/channels/channel-transports.ts +218 -206
- package/src/runtime/channels/client-server-location-runtime.ts +116 -100
- package/src/runtime/channels/client-server-service-wire.ts +37 -36
- package/src/runtime/channels/dispatch-error-reporter.ts +12 -47
- package/src/runtime/channels/fanout-location-runtime.ts +91 -94
- package/src/runtime/channels/fanout-service-wire.ts +5 -12
- package/src/runtime/channels/handler-filter-scope.ts +1 -4
- package/src/runtime/channels/index.ts +1 -4
- package/src/runtime/channels/local-spot-route-transport.ts +20 -5
- package/src/runtime/channels/route-member-snapshot.ts +11 -5
- package/src/runtime/channels/router-socket-spot-route-transport.ts +1 -7
- package/src/runtime/channels/source-spot-router.ts +26 -22
- package/src/runtime/channels/spot-direct-envelope.ts +10 -8
- package/src/runtime/channels/spot-node-route-transport.ts +13 -16
- package/src/runtime/channels/spot-route-bridge-transport.ts +26 -9
- package/src/runtime/channels/spot-route-dispatch-strategy.ts +40 -13
- package/src/runtime/channels/spot-route-target-resolver.ts +18 -9
- package/src/runtime/codecs/index.ts +3 -2
- package/src/runtime/diagnostics/dispatch-error-details.ts +62 -0
- package/src/runtime/diagnostics/flow-context.ts +18 -7
- package/src/runtime/diagnostics/index.ts +96 -61
- package/src/runtime/diagnostics/internal-event-contracts.ts +87 -33
- package/src/runtime/diagnostics/message-flow.ts +107 -79
- package/src/runtime/diagnostics/runtime-metrics.ts +46 -24
- package/src/runtime/diagnostics/runtime-observation-queue.ts +17 -28
- package/src/runtime/diagnostics/topology-runtime-projections.ts +62 -43
- package/src/runtime/execution/index.ts +18 -26
- package/src/runtime/execution/serial-execution-queue.ts +31 -40
- package/src/runtime/execution/state-lane.ts +2 -2
- package/src/runtime/foundation/actor-join-recovery-codec.ts +69 -55
- package/src/runtime/foundation/event-loop-resources.ts +8 -4
- package/src/runtime/foundation/message-follow-suppression-registry.ts +7 -14
- package/src/runtime/foundation/operation-identity.ts +1 -2
- package/src/runtime/foundation/operation-registry.ts +8 -2
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +364 -396
- package/src/runtime/foundation/route-mesh-connection-policy.ts +7 -8
- package/src/runtime/foundation/runtime-state-projections.ts +24 -23
- package/src/runtime/foundation/service-authority-payload-codec.ts +54 -57
- package/src/runtime/foundation/service-discovery-registry.ts +67 -59
- package/src/runtime/foundation/service-instance-activation-recovery-codec.ts +28 -29
- package/src/runtime/foundation/service-liveness-registry.ts +18 -13
- package/src/runtime/foundation/service-mailbox.ts +14 -14
- package/src/runtime/foundation/service-maintenance-runtime.ts +22 -21
- package/src/runtime/foundation/service-metadata-codec.ts +2 -9
- package/src/runtime/foundation/service-relocation-aggregate-committer.ts +39 -36
- package/src/runtime/foundation/service-relocation-coordinator.ts +1 -4
- package/src/runtime/foundation/service-relocation-object-owner.ts +40 -58
- package/src/runtime/foundation/service-relocation-runtime.ts +292 -216
- package/src/runtime/foundation/service-runtime-contracts.ts +8 -8
- package/src/runtime/foundation/service-session-binding-ingress-port.ts +2 -4
- package/src/runtime/foundation/service-stateful-registry.ts +69 -59
- package/src/runtime/foundation/service-stateful-runtime.ts +1233 -1112
- package/src/runtime/foundation/service-stateful-wire-codec.ts +318 -270
- package/src/runtime/foundation/service-topology-registry.ts +170 -159
- package/src/runtime/foundation/service-weighted-selection.ts +9 -15
- package/src/runtime/foundation/service-wire-binary-primitives.ts +6 -12
- package/src/runtime/foundation/service-wire-m6a-codec.ts +69 -47
- package/src/runtime/framework-errors-internal.ts +31 -30
- package/src/runtime/handlers/handler-instance-scope.ts +18 -29
- package/src/runtime/handlers/index.ts +18 -8
- package/src/runtime/host/actor-packet-relay.ts +290 -261
- package/src/runtime/host/actor-placement-coordinator.ts +165 -150
- package/src/runtime/host/actor-runtime-options-factory.ts +89 -87
- package/src/runtime/host/actor-transfer-authority-runtime.ts +46 -38
- package/src/runtime/host/actor-transfer-runtime.ts +391 -395
- package/src/runtime/host/application-job-queue.ts +32 -48
- package/src/runtime/host/bound-session-relay.ts +7 -9
- package/src/runtime/host/channel-runtime-options-factory.ts +75 -35
- package/src/runtime/host/host-capacity-status.ts +10 -9
- package/src/runtime/host/index.ts +861 -789
- package/src/runtime/host/instance-activation-authority.ts +125 -143
- package/src/runtime/host/location-runtime-owner.ts +38 -26
- package/src/runtime/host/mesh-router-resolver.ts +30 -23
- package/src/runtime/host/relocation-direct-transfer.ts +15 -15
- package/src/runtime/host/relocation-integrity-fault-gate.ts +3 -2
- package/src/runtime/host/remote-actor-join-receiver.ts +43 -34
- package/src/runtime/host/remote-actor-packet-target-store.ts +54 -52
- package/src/runtime/host/remote-bound-session-relay.ts +274 -247
- package/src/runtime/host/route-mesh-runtime-options.ts +1 -3
- package/src/runtime/host/route-mesh-runtime.ts +227 -181
- package/src/runtime/host/runtime-shutdown.ts +4 -3
- package/src/runtime/host/service-relocation-control.ts +14 -5
- package/src/runtime/host/service-relocation-host-runtime.ts +1388 -1165
- package/src/runtime/host/spot-address-transport.ts +284 -237
- package/src/runtime/host/spot-node-runtime-options-factory.ts +14 -8
- package/src/runtime/host/spot-runtime-options-factory.ts +71 -62
- package/src/runtime/host/stateful-authority-route-runtime.ts +117 -116
- package/src/runtime/host/user-spot-creation-coordinator.ts +225 -220
- package/src/runtime/locations/actor-location-claims.ts +61 -49
- package/src/runtime/locations/actor-session-route-claims.ts +1 -4
- package/src/runtime/locations/aggregate-inventory-store.ts +105 -134
- package/src/runtime/locations/authority-key-codec.ts +13 -10
- package/src/runtime/locations/auto-connect-loop.ts +14 -11
- package/src/runtime/locations/auto-connect-planner.ts +42 -31
- package/src/runtime/locations/auto-connect-reconciler.ts +106 -63
- package/src/runtime/locations/canonical-codec.ts +3 -1
- package/src/runtime/locations/creation-operation-id.ts +4 -3
- package/src/runtime/locations/domain-store-contract.ts +13 -7
- package/src/runtime/locations/in-memory-authority-store.ts +232 -199
- package/src/runtime/locations/in-memory-location-store.ts +432 -250
- package/src/runtime/locations/in-memory-provider-location-store.ts +16 -17
- package/src/runtime/locations/internal-store-contracts.ts +95 -38
- package/src/runtime/locations/key-codec.ts +3 -4
- package/src/runtime/locations/lease-tracker.ts +36 -27
- package/src/runtime/locations/lifecycle-runtime.ts +15 -3
- package/src/runtime/locations/lifecycle.ts +33 -28
- package/src/runtime/locations/location-store-repository.ts +1329 -1185
- package/src/runtime/locations/opaque-record-key.ts +4 -9
- package/src/runtime/locations/resolvers.ts +201 -178
- package/src/runtime/locations/runtime.ts +251 -159
- package/src/runtime/locations/spot-location-claims.ts +110 -112
- package/src/runtime/messaging/creation-payload-codec.ts +4 -1
- package/src/runtime/messaging/framework-json-v1.ts +34 -15
- package/src/runtime/messaging/packet-name.ts +8 -6
- package/src/runtime/messaging/payload-codec.ts +70 -50
- package/src/runtime/messaging/submission-result.ts +6 -9
- package/src/runtime/protocol/service_wire_codec.generated.ts +1376 -159
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +19 -18
- package/src/runtime/routing-id.ts +10 -7
- package/src/runtime/spots/formal-remote-actor-admission-registry.ts +56 -62
- package/src/runtime/spots/formal-remote-actor-transfer-registry.ts +23 -20
- package/src/runtime/spots/index.ts +595 -564
- package/src/runtime/spots/spot-activation-registry.ts +38 -34
- package/src/runtime/spots/spot-activation-state.ts +43 -51
- package/src/runtime/spots/spot-activation.ts +179 -116
- package/src/runtime/spots/spot-actor-admission-coordinator.ts +110 -78
- package/src/runtime/spots/spot-actor-join-dispatch.ts +57 -41
- package/src/runtime/spots/spot-actor-lifecycle-drain.ts +5 -7
- package/src/runtime/spots/spot-actor-membership.ts +25 -27
- package/src/runtime/spots/spot-actor-packet-dispatch.ts +196 -148
- package/src/runtime/spots/spot-actor-packet-drain.ts +56 -42
- package/src/runtime/spots/spot-actor-packet-relay-dispatch.ts +37 -39
- package/src/runtime/spots/spot-closing.ts +11 -12
- package/src/runtime/spots/spot-context.ts +46 -26
- package/src/runtime/spots/spot-entry-activation.ts +84 -65
- package/src/runtime/spots/spot-handle.ts +17 -15
- package/src/runtime/spots/spot-handler-registry.ts +44 -31
- package/src/runtime/spots/spot-location-claim.ts +5 -4
- package/src/runtime/spots/spot-manager-public.ts +62 -82
- package/src/runtime/spots/spot-native-actor-join-admission.ts +20 -16
- package/src/runtime/spots/spot-node-autoconnect.ts +54 -61
- package/src/runtime/spots/spot-node-runtime-manager.ts +235 -222
- package/src/runtime/spots/spot-outbound.ts +47 -59
- package/src/runtime/spots/spot-publisher-transport.ts +5 -4
- package/src/runtime/spots/spot-remote-codec.ts +66 -65
- package/src/runtime/spots/spot-remote-route-codec.ts +61 -41
- package/src/runtime/spots/spot-route-packet-dispatch.ts +129 -95
- package/src/runtime/spots/spot-route-replies.ts +23 -24
- package/src/runtime/spots/spot-route-reply-wire.ts +3 -2
- package/src/runtime/spots/spot-routed-actor-admission.ts +37 -27
- package/src/runtime/spots/spot-routed-bound-session-dispatch.ts +13 -18
- package/src/runtime/spots/spot-routed-frame-dispatch.ts +18 -15
- package/src/runtime/spots/spot-routed-spot-packet-dispatch.ts +35 -26
- package/src/runtime/spots/spot-routing-internal.ts +1 -4
- package/src/runtime/spots/spot-runtime-ports.ts +5 -5
- package/src/runtime/spots/spot-serial-executor.ts +5 -9
- package/src/runtime/spots/spot-serial-turn-executor.ts +19 -26
- package/src/runtime/spots/spot-subscription-dispatch.ts +58 -47
- package/src/runtime/spots/spot-timer.ts +103 -91
- package/src/runtime/streams/actor-session-binding-registry.ts +421 -292
- package/src/runtime/streams/actor-session-binding-runtime-owner.ts +8 -4
- package/src/runtime/streams/actor-session-lifecycle-coordinator.ts +3 -1
- package/src/runtime/streams/bound-actor-relay-sender.ts +60 -50
- package/src/runtime/streams/bound-session-response-target.ts +39 -9
- package/src/runtime/streams/bound-session-service.ts +52 -42
- package/src/runtime/streams/index.ts +183 -100
- package/src/runtime/streams/managed-stream.ts +123 -90
- package/src/runtime/streams/native-fallback-bound-session.ts +57 -52
- package/src/runtime/streams/protocol.ts +115 -46
- package/src/runtime/streams/session-actor-coordinator.ts +109 -93
- package/src/runtime/streams/session-calls.ts +2 -8
- package/src/runtime/streams/session-context.ts +96 -51
- package/src/runtime/streams/session-local-actors.ts +19 -3
- package/src/runtime/streams/session-provider.ts +5 -7
- package/src/runtime/streams/session-serial-executor.ts +7 -6
- package/src/runtime/streams/stream-binding-runtime-ports.ts +79 -29
- package/src/runtime/streams/stream-frame-factory.ts +59 -40
- package/src/runtime/streams/stream-session-runtime.ts +317 -214
- package/src/runtime/workers/index.ts +68 -29
|
@@ -35,21 +35,11 @@ import {
|
|
|
35
35
|
import type { ZLinkDomainLocationStore as ZLinkLocationStore } from '../locations/domain-store-contract';
|
|
36
36
|
import type { ZLinkTrackedInstanceAuthority } from '../locations/spot-location-claims';
|
|
37
37
|
import type { ZLinkProviderResolver } from '../../contracts/Common/ZLinkProviderResolver';
|
|
38
|
-
import type {
|
|
39
|
-
ZLinkRuntimeEventPublisher,
|
|
40
|
-
ZLinkRuntimeMetrics
|
|
41
|
-
} from '../diagnostics';
|
|
38
|
+
import type { ZLinkRuntimeEventPublisher, ZLinkRuntimeMetrics } from '../diagnostics';
|
|
42
39
|
import type { ZLinkFrameworkRegistration } from '../configuration';
|
|
43
|
-
import type {
|
|
44
|
-
ZLinkBackendMeshNode,
|
|
45
|
-
ZLinkMeshCompletionTable
|
|
46
|
-
} from '../backend';
|
|
40
|
+
import type { ZLinkBackendMeshNode, ZLinkMeshCompletionTable } from '../backend';
|
|
47
41
|
import { ReceiveKind, type ReceiveRecord } from '../foundation/service-runtime-contracts';
|
|
48
|
-
import {
|
|
49
|
-
decodeRoutingId,
|
|
50
|
-
encodeRoutingIdStorageHex,
|
|
51
|
-
routingIdsEqual
|
|
52
|
-
} from '../routing-id';
|
|
42
|
+
import { decodeRoutingId, encodeRoutingIdStorageHex, routingIdsEqual } from '../routing-id';
|
|
53
43
|
import { ServiceWireProtocolError } from '../foundation/service-wire-m6a-codec';
|
|
54
44
|
import type { ServiceSpotMessageFollowSeal } from '../foundation/service-stateful-runtime';
|
|
55
45
|
import {
|
|
@@ -82,12 +72,8 @@ import {
|
|
|
82
72
|
restoreRelocationAdapterState,
|
|
83
73
|
type ZLinkRelocationStateAdapterLike
|
|
84
74
|
} from './relocation-state-adapter';
|
|
85
|
-
import {
|
|
86
|
-
|
|
87
|
-
} from '../foundation/service-relocation-coordinator';
|
|
88
|
-
import {
|
|
89
|
-
ServiceMaintenanceRuntime
|
|
90
|
-
} from '../foundation/service-maintenance-runtime';
|
|
75
|
+
import { ServiceRelocationPostCommitError } from '../foundation/service-relocation-coordinator';
|
|
76
|
+
import { ServiceMaintenanceRuntime } from '../foundation/service-maintenance-runtime';
|
|
91
77
|
import {
|
|
92
78
|
ServiceRelocationObjectCaptureOwner,
|
|
93
79
|
ServiceRelocationObjectRestoreOwner,
|
|
@@ -103,10 +89,7 @@ import {
|
|
|
103
89
|
type ServiceRelocationAggregatePlan
|
|
104
90
|
} from '../foundation/service-relocation-aggregate-committer';
|
|
105
91
|
import { createProviderInstance } from '../spots/spot-provider';
|
|
106
|
-
import type {
|
|
107
|
-
DefaultZLinkSpotManager,
|
|
108
|
-
ZLinkSpotNodeRuntimeManager
|
|
109
|
-
} from '../spots';
|
|
92
|
+
import type { DefaultZLinkSpotManager, ZLinkSpotNodeRuntimeManager } from '../spots';
|
|
110
93
|
import type { ZLinkSpotActivation } from '../spots/spot-activation-state';
|
|
111
94
|
import type { DefaultZLinkActorManager } from '../actors';
|
|
112
95
|
import type { ZLinkDeferredJoinAcceptedRoot } from '../actors/deferred-join-accepted-journal';
|
|
@@ -126,10 +109,7 @@ import {
|
|
|
126
109
|
rewriteActorAuthorityRoute
|
|
127
110
|
} from '../actors/actor-authority-publication';
|
|
128
111
|
import { rewriteServiceAuthorityRoute } from '../foundation/service-authority-payload-codec';
|
|
129
|
-
import {
|
|
130
|
-
committedActorOwnerFence,
|
|
131
|
-
type ZLinkActorTransferRuntime
|
|
132
|
-
} from './actor-transfer-runtime';
|
|
112
|
+
import { committedActorOwnerFence, type ZLinkActorTransferRuntime } from './actor-transfer-runtime';
|
|
133
113
|
import { decodeAuthorityKey, encodeAuthorityKey } from '../locations/authority-key-codec';
|
|
134
114
|
import {
|
|
135
115
|
decodeServiceRelocationControlRequest,
|
|
@@ -170,7 +150,7 @@ import {
|
|
|
170
150
|
type ServiceWireRequestSourceFence,
|
|
171
151
|
type ServiceWireRelocationTarget,
|
|
172
152
|
type ServiceWireRelocationCoordinatorFence,
|
|
173
|
-
type ServiceWireRelocationObject
|
|
153
|
+
type ServiceWireRelocationObject
|
|
174
154
|
} from '../foundation/service-stateful-wire-codec';
|
|
175
155
|
import { ServiceWireFrameworkErrorCode } from '../foundation/service-wire-constants.generated';
|
|
176
156
|
import { BoundedReplayMap } from './bounded-replay-map';
|
|
@@ -315,7 +295,9 @@ interface SourceActorJoinProfile {
|
|
|
315
295
|
readonly targetSpotId: RoutingId;
|
|
316
296
|
readonly targetNodeRid: RoutingId;
|
|
317
297
|
readonly relocationContentType: string;
|
|
318
|
-
readonly completionOperationId?: Parameters<
|
|
298
|
+
readonly completionOperationId?: Parameters<
|
|
299
|
+
ZLinkActorJoinRelocation['relocateActorJoin']
|
|
300
|
+
>[0]['completionOperationId'];
|
|
319
301
|
readonly canonicalRecovery?: NonNullable<
|
|
320
302
|
Parameters<ZLinkActorJoinRelocation['relocateActorJoin']>[0]['canonicalRecovery']
|
|
321
303
|
>;
|
|
@@ -361,17 +343,18 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
361
343
|
private readonly targetPrepareOperations = new Map<string, TargetPrepareOperation>();
|
|
362
344
|
private readonly pendingReplyRelays = new Map<string, PendingRelocationReplyRelay>();
|
|
363
345
|
private readonly pendingSessionRelocations = new Map<string, PendingSessionRelocation>();
|
|
364
|
-
private readonly terminalSessionRelocations =
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
346
|
+
private readonly terminalSessionRelocations = new BoundedReplayMap<
|
|
347
|
+
string,
|
|
348
|
+
TerminalSessionRelocationControl
|
|
349
|
+
>(SERVICE_CONTROL_TERMINAL_CAPACITY);
|
|
368
350
|
/**
|
|
369
351
|
* A command 44 submit has no response path. Keep a bounded record of a
|
|
370
352
|
* rejected one-way attempt without turning the committed relocation into a
|
|
371
353
|
* retry loop.
|
|
372
354
|
*/
|
|
373
|
-
private readonly failedSessionRouteSubmits =
|
|
374
|
-
|
|
355
|
+
private readonly failedSessionRouteSubmits = new BoundedReplayMap<string, SubmitResult>(
|
|
356
|
+
SERVICE_CONTROL_TERMINAL_CAPACITY
|
|
357
|
+
);
|
|
375
358
|
private readonly sourceRelocationIds = new Set<string>();
|
|
376
359
|
private readonly targetAssemblies = new Map<string, TargetPayloadAssembly>();
|
|
377
360
|
/**
|
|
@@ -381,21 +364,26 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
381
364
|
* restore; a different exact identity reusing the same RelocationId+attempt
|
|
382
365
|
* key supersedes the stale entry (spec 28 §3, §9).
|
|
383
366
|
*/
|
|
384
|
-
private readonly targetReadyFailures =
|
|
385
|
-
|
|
367
|
+
private readonly targetReadyFailures = new Map<
|
|
368
|
+
string,
|
|
369
|
+
{ readonly fingerprint: string; readonly response: ServiceMaintenanceRelocationFailed }
|
|
370
|
+
>();
|
|
386
371
|
/**
|
|
387
372
|
* Successful restores retained across a Ready delivery retry: an
|
|
388
373
|
* exact-identity Prepare resend re-submits Ready against this retained
|
|
389
374
|
* staging instead of redoing the restore (spec 28).
|
|
390
375
|
*/
|
|
391
|
-
private readonly targetReadyResponses =
|
|
392
|
-
|
|
376
|
+
private readonly targetReadyResponses = new Map<
|
|
377
|
+
string,
|
|
378
|
+
{ readonly fingerprint: string; readonly response: ServiceMaintenanceRelocationReady }
|
|
379
|
+
>();
|
|
393
380
|
private readonly sourceCutoverWindows = new Map<string, SourceCutoverWindow>();
|
|
394
381
|
private readonly peerPayloadBudgets = new Map<string, ZLinkRelocationInFlightBudget>();
|
|
395
382
|
private nodePayloadBudget?: ZLinkRelocationInFlightBudget;
|
|
396
383
|
private readonly sourceActorJoinProfiles = new Map<string, SourceActorJoinProfile>();
|
|
397
|
-
private readonly terminalActorJoinSourceLeaves =
|
|
398
|
-
|
|
384
|
+
private readonly terminalActorJoinSourceLeaves = new BoundedReplayMap<string, string>(
|
|
385
|
+
SERVICE_CONTROL_TERMINAL_CAPACITY
|
|
386
|
+
);
|
|
399
387
|
private readonly codec = new ServiceRelocationAuthorityPayloadCodec();
|
|
400
388
|
/**
|
|
401
389
|
* Set once dispose() starts tearing assemblies down. A payload() rejection
|
|
@@ -414,15 +402,21 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
414
402
|
throw new Error(`Actor '${input.state.actorId}' has no source authority identity.`);
|
|
415
403
|
}
|
|
416
404
|
const descriptors = await this.options.liveDescriptors(input.meshName, input.signal);
|
|
417
|
-
const target = descriptors.find(value =>
|
|
405
|
+
const target = descriptors.find((value) =>
|
|
406
|
+
routingIdsEqual(value.rid, input.target.targetNodeRid)
|
|
407
|
+
);
|
|
418
408
|
if (target === undefined) {
|
|
419
409
|
throw new Error(
|
|
420
410
|
`Actor '${input.state.actorId}' Join target '${String(input.target.targetNodeRid)}' is not live.`
|
|
421
411
|
);
|
|
422
412
|
}
|
|
423
|
-
if (
|
|
424
|
-
|
|
425
|
-
|
|
413
|
+
if (
|
|
414
|
+
input.target.targetNodeGeneration !== undefined &&
|
|
415
|
+
target.lifecycleGeneration !== input.target.targetNodeGeneration
|
|
416
|
+
) {
|
|
417
|
+
throw new Error(
|
|
418
|
+
`Actor '${input.state.actorId}' Join target generation changed after admission.`
|
|
419
|
+
);
|
|
426
420
|
}
|
|
427
421
|
this.reserveExactRelocationId(input.relocationId);
|
|
428
422
|
let resolveReady!: () => void;
|
|
@@ -436,20 +430,18 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
436
430
|
sourceSpotId: input.state.spotId,
|
|
437
431
|
targetSpotId: input.target.spotId,
|
|
438
432
|
targetNodeRid: input.target.targetNodeRid,
|
|
439
|
-
relocationContentType:
|
|
440
|
-
input.state.meshName ?? input.meshName,
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
: 'application/vnd.zlink.actor-relocation.recreate',
|
|
433
|
+
relocationContentType:
|
|
434
|
+
this.actorRegistration(input.state.meshName ?? input.meshName, input.state.actorType ?? '')
|
|
435
|
+
.relocation.kind === 'snapshot'
|
|
436
|
+
? 'application/vnd.zlink.actor-relocation.snapshot'
|
|
437
|
+
: 'application/vnd.zlink.actor-relocation.recreate',
|
|
445
438
|
completionOperationId: input.completionOperationId,
|
|
446
439
|
canonicalRecovery: input.canonicalRecovery,
|
|
447
440
|
ready,
|
|
448
441
|
resolveReady
|
|
449
442
|
});
|
|
450
|
-
const membershipEpoch =
|
|
451
|
-
? input.state.spotMembershipEpoch
|
|
452
|
-
: 1n;
|
|
443
|
+
const membershipEpoch =
|
|
444
|
+
input.state.spotMembershipEpoch > 0n ? input.state.spotMembershipEpoch : 1n;
|
|
453
445
|
const spotGeneration = input.target.targetSpotGeneration ?? target.lifecycleGeneration;
|
|
454
446
|
try {
|
|
455
447
|
await this.relocateStandaloneActor(
|
|
@@ -460,9 +452,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
460
452
|
input.signal,
|
|
461
453
|
{
|
|
462
454
|
spotId: input.target.spotId,
|
|
463
|
-
spotKind:
|
|
464
|
-
|
|
465
|
-
|
|
455
|
+
spotKind:
|
|
456
|
+
input.target.spotKind === ZLinkSpotKind.Entry
|
|
457
|
+
? ZLinkSpotKind.Entry
|
|
458
|
+
: ZLinkSpotKind.User,
|
|
466
459
|
spotObjectGeneration: spotGeneration
|
|
467
460
|
},
|
|
468
461
|
input.relocationId,
|
|
@@ -543,18 +536,19 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
543
536
|
if (profile.state.actorId !== actorId) continue;
|
|
544
537
|
this.sourceActorJoinProfiles.delete(relocationId);
|
|
545
538
|
this.terminalActorJoinSourceLeaves.remember(relocationId, actorId);
|
|
546
|
-
void profile.ready
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
539
|
+
void profile.ready
|
|
540
|
+
.then(async () => {
|
|
541
|
+
await this.options.actorTransfer.completeRelocationSourceLeave(
|
|
542
|
+
profile.actor,
|
|
543
|
+
profile.sourceSpotId
|
|
544
|
+
);
|
|
545
|
+
await this.options
|
|
546
|
+
.actorManager()
|
|
547
|
+
?.completeRelocationSource(actorId, profile.sourceActorRef);
|
|
548
|
+
})
|
|
549
|
+
.catch((error) => {
|
|
550
|
+
console.error('[zlink.runtime.relocation.source_cleanup_failed]', error);
|
|
551
|
+
});
|
|
558
552
|
}
|
|
559
553
|
}
|
|
560
554
|
|
|
@@ -578,12 +572,15 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
578
572
|
for (const activation of spotManager.relocationActivations(meshName)) {
|
|
579
573
|
const kind = this.spotKind(meshName, activation);
|
|
580
574
|
if (kind === undefined) continue;
|
|
581
|
-
const states = actorManager
|
|
582
|
-
|
|
575
|
+
const states = actorManager
|
|
576
|
+
.snapshotStates()
|
|
577
|
+
.filter(
|
|
578
|
+
(state) => state.actor !== undefined && String(state.spotId) === String(activation.spotId)
|
|
579
|
+
);
|
|
583
580
|
for (const state of states) groupedActorIds.add(state.actorId);
|
|
584
581
|
if (
|
|
585
|
-
kind === 'user_spot'
|
|
586
|
-
|
|
582
|
+
kind === 'user_spot' &&
|
|
583
|
+
activation.executionMode === ZLinkUserSpotExecutionMode.PerActor
|
|
587
584
|
) {
|
|
588
585
|
let shellRelocation: Promise<void> | undefined;
|
|
589
586
|
const relocateShell = (relocationSignal: AbortSignal): Promise<void> => {
|
|
@@ -603,7 +600,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
603
600
|
for (const state of states) {
|
|
604
601
|
work.push({
|
|
605
602
|
id: `actor:${state.actorId}`,
|
|
606
|
-
relocate: async relocationSignal => {
|
|
603
|
+
relocate: async (relocationSignal) => {
|
|
607
604
|
await relocateShell(relocationSignal);
|
|
608
605
|
await this.relocateStandaloneActor(
|
|
609
606
|
meshName,
|
|
@@ -630,9 +627,16 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
630
627
|
}
|
|
631
628
|
work.push({
|
|
632
629
|
id: `spot:${String(activation.spotId)}`,
|
|
633
|
-
relocate: relocationSignal =>
|
|
630
|
+
relocate: (relocationSignal) =>
|
|
634
631
|
this.relocateSpotAggregate(
|
|
635
|
-
meshName,
|
|
632
|
+
meshName,
|
|
633
|
+
activation,
|
|
634
|
+
kind,
|
|
635
|
+
states,
|
|
636
|
+
undefined,
|
|
637
|
+
targetApplicationVersion,
|
|
638
|
+
relocationSignal
|
|
639
|
+
)
|
|
636
640
|
});
|
|
637
641
|
}
|
|
638
642
|
|
|
@@ -641,9 +645,14 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
641
645
|
if ((state.meshName ?? meshName) !== meshName) continue;
|
|
642
646
|
work.push({
|
|
643
647
|
id: `actor:${state.actorId}`,
|
|
644
|
-
relocate: relocationSignal =>
|
|
648
|
+
relocate: (relocationSignal) =>
|
|
645
649
|
this.relocateStandaloneActor(
|
|
646
|
-
meshName,
|
|
650
|
+
meshName,
|
|
651
|
+
state,
|
|
652
|
+
undefined,
|
|
653
|
+
targetApplicationVersion,
|
|
654
|
+
relocationSignal
|
|
655
|
+
)
|
|
647
656
|
});
|
|
648
657
|
}
|
|
649
658
|
if (work.length === 0) return;
|
|
@@ -680,15 +689,16 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
680
689
|
if (isServiceWireCommand(payload, M6bServiceWireCommand.replyRelay)) {
|
|
681
690
|
await this.handleReplyRelay(
|
|
682
691
|
meshName,
|
|
683
|
-
decodeMaintenanceReplyRelay(record.parts.map(part => part.data())),
|
|
692
|
+
decodeMaintenanceReplyRelay(record.parts.map((part) => part.data())),
|
|
684
693
|
record.sourceNodeRid,
|
|
685
694
|
signal
|
|
686
695
|
);
|
|
687
696
|
return true;
|
|
688
697
|
}
|
|
689
|
-
const sideband =
|
|
690
|
-
|
|
691
|
-
|
|
698
|
+
const sideband =
|
|
699
|
+
record.parts.length === 1
|
|
700
|
+
? undefined
|
|
701
|
+
: decodePrepareSideband(record.parts.slice(1).map((part) => part.data()));
|
|
692
702
|
if (record.parts.length > 1 && sideband === undefined) return false;
|
|
693
703
|
if (record.kind === ReceiveKind.NodeSend) {
|
|
694
704
|
const sourceLeave = decodeRemoteActorSourceLeaveTerminal(payload);
|
|
@@ -720,12 +730,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
720
730
|
}
|
|
721
731
|
if (isServiceWireCommand(payload, M6bServiceWireCommand.sessionRelocationRoute)) {
|
|
722
732
|
const request = decodeSessionRelocationRoute(payload);
|
|
723
|
-
await this.handleSessionRelocationRoute(
|
|
724
|
-
meshName,
|
|
725
|
-
request,
|
|
726
|
-
record.sourceNodeRid,
|
|
727
|
-
signal
|
|
728
|
-
);
|
|
733
|
+
await this.handleSessionRelocationRoute(meshName, request, record.sourceNodeRid, signal);
|
|
729
734
|
return true;
|
|
730
735
|
}
|
|
731
736
|
if (isServiceWireCommand(payload, M6bServiceWireCommand.replyRelayAck)) {
|
|
@@ -802,19 +807,15 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
802
807
|
);
|
|
803
808
|
operation = { fingerprint, promise };
|
|
804
809
|
this.targetPrepareOperations.set(operationKey, operation);
|
|
805
|
-
void promise
|
|
806
|
-
|
|
807
|
-
this.targetPrepareOperations.
|
|
808
|
-
|
|
809
|
-
|
|
810
|
+
void promise
|
|
811
|
+
.finally(() => {
|
|
812
|
+
if (this.targetPrepareOperations.get(operationKey) === operation) {
|
|
813
|
+
this.targetPrepareOperations.delete(operationKey);
|
|
814
|
+
}
|
|
815
|
+
})
|
|
816
|
+
.catch(() => undefined);
|
|
810
817
|
}
|
|
811
|
-
void this.deliverTargetReady(
|
|
812
|
-
meshName,
|
|
813
|
-
operationKey,
|
|
814
|
-
request,
|
|
815
|
-
record,
|
|
816
|
-
operation.promise
|
|
817
|
-
);
|
|
818
|
+
void this.deliverTargetReady(meshName, operationKey, request, record, operation.promise);
|
|
818
819
|
return true;
|
|
819
820
|
}
|
|
820
821
|
await this.handleOneWayControl(meshName, request, record.sourceNodeRid, signal);
|
|
@@ -838,7 +839,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
838
839
|
// (command 53) once and remember it so an identical Prepare resend
|
|
839
840
|
// replays the same response instead of retrying the restore.
|
|
840
841
|
const failed = relocationFailed(
|
|
841
|
-
request,
|
|
842
|
+
request,
|
|
843
|
+
relocationFailedFailureCode(error, request.object.kind)
|
|
842
844
|
);
|
|
843
845
|
this.targetReadyFailures.set(operationKey, {
|
|
844
846
|
fingerprint: stringifyWire(request),
|
|
@@ -884,7 +886,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
884
886
|
const stage = this.targetStages.get(operationKey);
|
|
885
887
|
if (stage === undefined) return;
|
|
886
888
|
this.targetStages.delete(operationKey);
|
|
887
|
-
void this.abortTargetStage(stage).catch(error => {
|
|
889
|
+
void this.abortTargetStage(stage).catch((error) => {
|
|
888
890
|
console.error('[zlink.runtime.relocation.ready_expired_abort_failed]', operationKey, error);
|
|
889
891
|
});
|
|
890
892
|
}
|
|
@@ -928,12 +930,12 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
928
930
|
const operationKey = relocationStagingId(request);
|
|
929
931
|
const pending = this.targetAssemblies.get(operationKey);
|
|
930
932
|
if (
|
|
931
|
-
pending === undefined
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
933
|
+
pending === undefined ||
|
|
934
|
+
sourceNodeRid === null ||
|
|
935
|
+
String(sourceNodeRid) !== pending.authenticatedSourceNodeRid ||
|
|
936
|
+
request.senderRole !== 'source' ||
|
|
937
|
+
!sameCoordinator(request.coordinator, pending.prepare.coordinator) ||
|
|
938
|
+
stringifyWire(request.object) !== stringifyWire(pending.prepare.object)
|
|
937
939
|
) {
|
|
938
940
|
// Spec 28 §4.3 — a chunk with a different exact identity is never
|
|
939
941
|
// attached to an in-progress assembly; it is discarded.
|
|
@@ -981,10 +983,14 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
981
983
|
if (this.relocationIntegrityFaultGate()?.consumeIdentityConflict() === true) {
|
|
982
984
|
// Route a same-identity, different-manifest delivery through the normal
|
|
983
985
|
// conflict branch so existing staging is neither reused nor overwritten.
|
|
984
|
-
return this.registerTargetAssembly(
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
986
|
+
return this.registerTargetAssembly(
|
|
987
|
+
operationKey,
|
|
988
|
+
{
|
|
989
|
+
...request,
|
|
990
|
+
payloadChecksumCrc32c: request.payloadChecksumCrc32c ^ 1
|
|
991
|
+
},
|
|
992
|
+
authenticatedSourceNodeRid
|
|
993
|
+
);
|
|
988
994
|
}
|
|
989
995
|
return pending;
|
|
990
996
|
}
|
|
@@ -992,8 +998,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
992
998
|
private relocationIntegrityFaultGate(): ZLinkInternalRelocationIntegrityFaultGate | undefined {
|
|
993
999
|
try {
|
|
994
1000
|
return this.options.providerResolver?.get?.(
|
|
995
|
-
ZLINK_INTERNAL_RELOCATION_INTEGRITY_FAULT_GATE as unknown as
|
|
996
|
-
Type<ZLinkInternalRelocationIntegrityFaultGate>
|
|
1001
|
+
ZLINK_INTERNAL_RELOCATION_INTEGRITY_FAULT_GATE as unknown as Type<ZLinkInternalRelocationIntegrityFaultGate>
|
|
997
1002
|
);
|
|
998
1003
|
} catch {
|
|
999
1004
|
return undefined;
|
|
@@ -1006,11 +1011,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1006
1011
|
request: ServiceSessionRelocationSeal,
|
|
1007
1012
|
signal?: AbortSignal
|
|
1008
1013
|
): Promise<ServiceSessionRelocationSealed> {
|
|
1009
|
-
return this.requestSessionRelocationSealControl(
|
|
1010
|
-
meshName,
|
|
1011
|
-
targetNodeRid,
|
|
1012
|
-
request,
|
|
1013
|
-
signal);
|
|
1014
|
+
return this.requestSessionRelocationSealControl(meshName, targetNodeRid, request, signal);
|
|
1014
1015
|
}
|
|
1015
1016
|
|
|
1016
1017
|
async sendSessionRelocationRoute(
|
|
@@ -1024,12 +1025,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1024
1025
|
// control dispatches through the same inbound handler locally.
|
|
1025
1026
|
const localRid = this.requireMeshNode(meshName).status().routingId;
|
|
1026
1027
|
if (String(localRid) === String(targetNodeRid)) {
|
|
1027
|
-
await this.handleSessionRelocationRoute(
|
|
1028
|
-
meshName,
|
|
1029
|
-
request,
|
|
1030
|
-
localRid,
|
|
1031
|
-
signal
|
|
1032
|
-
);
|
|
1028
|
+
await this.handleSessionRelocationRoute(meshName, request, localRid, signal);
|
|
1033
1029
|
return;
|
|
1034
1030
|
}
|
|
1035
1031
|
// Spec 20 §5.1 / config-10 ST-E1C: command 44 is a one-way control with
|
|
@@ -1039,10 +1035,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1039
1035
|
// bind is the only recovery path -- never a second command 44 send.
|
|
1040
1036
|
const bytes = encodeSessionRelocationRoute(request);
|
|
1041
1037
|
signal?.throwIfAborted();
|
|
1042
|
-
const submitted = await this.sendInfrastructureControl(meshName,
|
|
1043
|
-
targetNodeRid,
|
|
1044
|
-
bytes
|
|
1045
|
-
);
|
|
1038
|
+
const submitted = await this.sendInfrastructureControl(meshName, targetNodeRid, bytes);
|
|
1046
1039
|
if (submitted !== SubmitResult.Ok) {
|
|
1047
1040
|
this.failedSessionRouteSubmits.remember(
|
|
1048
1041
|
sessionRelocationRouteSubmitKey(request, targetNodeRid),
|
|
@@ -1064,36 +1057,38 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1064
1057
|
const terminal = this.terminalSessionRelocations.get(key);
|
|
1065
1058
|
if (terminal !== undefined) {
|
|
1066
1059
|
if (
|
|
1067
|
-
terminal.targetNodeRid !== expectedTarget
|
|
1068
|
-
|
|
1060
|
+
terminal.targetNodeRid !== expectedTarget ||
|
|
1061
|
+
terminal.requestFingerprint !== requestFingerprint
|
|
1069
1062
|
) {
|
|
1070
|
-
return Promise.reject(
|
|
1071
|
-
|
|
1072
|
-
|
|
1063
|
+
return Promise.reject(
|
|
1064
|
+
new ServiceWireProtocolError(
|
|
1065
|
+
`Session relocation control '${key}' repeated with different bytes or target.`
|
|
1066
|
+
)
|
|
1067
|
+
);
|
|
1073
1068
|
}
|
|
1074
1069
|
this.terminalSessionRelocations.touch(key);
|
|
1075
1070
|
}
|
|
1076
1071
|
const existing = this.pendingSessionRelocations.get(key);
|
|
1077
1072
|
if (existing !== undefined) {
|
|
1078
1073
|
if (
|
|
1079
|
-
existing.targetNodeRid !== expectedTarget
|
|
1080
|
-
|
|
1074
|
+
existing.targetNodeRid !== expectedTarget ||
|
|
1075
|
+
existing.requestFingerprint !== requestFingerprint
|
|
1081
1076
|
) {
|
|
1082
|
-
return Promise.reject(
|
|
1083
|
-
|
|
1084
|
-
|
|
1077
|
+
return Promise.reject(
|
|
1078
|
+
new ServiceWireProtocolError(
|
|
1079
|
+
`Session relocation control '${key}' repeated with different bytes or target.`
|
|
1080
|
+
)
|
|
1081
|
+
);
|
|
1085
1082
|
}
|
|
1086
1083
|
return existing.promise;
|
|
1087
1084
|
}
|
|
1088
1085
|
|
|
1089
1086
|
let resolvePromise!: (response: ServiceSessionRelocationSealed) => void;
|
|
1090
1087
|
let rejectPromise!: (error: unknown) => void;
|
|
1091
|
-
const promise = new Promise<ServiceSessionRelocationSealed>(
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
}
|
|
1096
|
-
);
|
|
1088
|
+
const promise = new Promise<ServiceSessionRelocationSealed>((resolve, reject) => {
|
|
1089
|
+
resolvePromise = resolve;
|
|
1090
|
+
rejectPromise = reject;
|
|
1091
|
+
});
|
|
1097
1092
|
let abortListener: (() => void) | undefined;
|
|
1098
1093
|
let settled = false;
|
|
1099
1094
|
const finish = (action: () => void) => {
|
|
@@ -1122,9 +1117,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1122
1117
|
const registration = Object.hasOwn(this.options, 'registration')
|
|
1123
1118
|
? this.options.registration
|
|
1124
1119
|
: undefined;
|
|
1125
|
-
const sealTimeoutMs =
|
|
1126
|
-
.sessionRelocationSealTimeoutMs
|
|
1127
|
-
|
|
1120
|
+
const sealTimeoutMs =
|
|
1121
|
+
registration?.locations.options.sessionRelocationSealTimeoutMs ??
|
|
1122
|
+
zlinkRuntimeDefaultLocationOptions.sessionRelocationSealTimeoutMs;
|
|
1128
1123
|
const resendDeadline = performance.now() + sealTimeoutMs;
|
|
1129
1124
|
const send = async (): Promise<void> => {
|
|
1130
1125
|
if (signal?.aborted === true) {
|
|
@@ -1150,34 +1145,39 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1150
1145
|
return;
|
|
1151
1146
|
}
|
|
1152
1147
|
if (performance.now() >= resendDeadline) {
|
|
1153
|
-
pending.reject(
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1148
|
+
pending.reject(
|
|
1149
|
+
createInternalFrameworkException(
|
|
1150
|
+
ZLinkFrameworkInternalErrorKind.DeadlineExceeded,
|
|
1151
|
+
`Session relocation seal '${key}' was not acknowledged within the relocation seal timeout.`,
|
|
1152
|
+
true
|
|
1153
|
+
)
|
|
1154
|
+
);
|
|
1158
1155
|
return;
|
|
1159
1156
|
}
|
|
1160
1157
|
try {
|
|
1161
|
-
const submitted = await this.sendInfrastructureControl(
|
|
1162
|
-
meshName, targetNodeRid, bytes);
|
|
1158
|
+
const submitted = await this.sendInfrastructureControl(meshName, targetNodeRid, bytes);
|
|
1163
1159
|
if (submitted !== SubmitResult.Ok) {
|
|
1164
|
-
pending.reject(
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1160
|
+
pending.reject(
|
|
1161
|
+
createInternalFrameworkException(
|
|
1162
|
+
ZLinkFrameworkInternalErrorKind.RelocationTargetUnavailable,
|
|
1163
|
+
`Session relocation seal '${key}' was not accepted by RouteMesh.`,
|
|
1164
|
+
true
|
|
1165
|
+
)
|
|
1166
|
+
);
|
|
1169
1167
|
return;
|
|
1170
1168
|
}
|
|
1171
1169
|
if (this.pendingSessionRelocations.get(key) === pending) {
|
|
1172
1170
|
pending.retryTimer = setTimeout(() => void send(), 250);
|
|
1173
1171
|
}
|
|
1174
1172
|
} catch (error) {
|
|
1175
|
-
pending.reject(
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1173
|
+
pending.reject(
|
|
1174
|
+
createInternalFrameworkException(
|
|
1175
|
+
ZLinkFrameworkInternalErrorKind.RelocationTargetUnavailable,
|
|
1176
|
+
`Session relocation seal '${key}' could not reach its RouteMesh owner.`,
|
|
1177
|
+
true,
|
|
1178
|
+
error
|
|
1179
|
+
)
|
|
1180
|
+
);
|
|
1181
1181
|
}
|
|
1182
1182
|
};
|
|
1183
1183
|
pending = {
|
|
@@ -1185,33 +1185,40 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1185
1185
|
request,
|
|
1186
1186
|
requestFingerprint,
|
|
1187
1187
|
promise,
|
|
1188
|
-
resolve: response
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1188
|
+
resolve: (response) =>
|
|
1189
|
+
finish(() => {
|
|
1190
|
+
this.terminalSessionRelocations.remember(key, {
|
|
1191
|
+
targetNodeRid: expectedTarget,
|
|
1192
|
+
request,
|
|
1193
|
+
requestFingerprint,
|
|
1194
|
+
responseFingerprint: sessionRelocationResponseFingerprint(response)
|
|
1195
|
+
});
|
|
1196
|
+
resolvePromise(response);
|
|
1197
|
+
}),
|
|
1198
|
+
reject: (error) => finish(() => rejectPromise(error))
|
|
1198
1199
|
};
|
|
1199
1200
|
this.pendingSessionRelocations.set(key, pending);
|
|
1200
|
-
pending.deadlineTimer = setTimeout(
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
ZLinkFrameworkInternalErrorKind.DeadlineExceeded,
|
|
1211
|
-
`Session relocation seal '${key}' was cancelled.`,
|
|
1212
|
-
true
|
|
1213
|
-
)
|
|
1201
|
+
pending.deadlineTimer = setTimeout(
|
|
1202
|
+
() =>
|
|
1203
|
+
pending.reject(
|
|
1204
|
+
createInternalFrameworkException(
|
|
1205
|
+
ZLinkFrameworkInternalErrorKind.DeadlineExceeded,
|
|
1206
|
+
`Session relocation seal '${key}' was not acknowledged within the relocation seal timeout.`,
|
|
1207
|
+
true
|
|
1208
|
+
)
|
|
1209
|
+
),
|
|
1210
|
+
Math.max(0, resendDeadline - performance.now())
|
|
1214
1211
|
);
|
|
1212
|
+
pending.deadlineTimer.unref();
|
|
1213
|
+
abortListener = () =>
|
|
1214
|
+
pending.reject(
|
|
1215
|
+
signal?.reason ??
|
|
1216
|
+
createInternalFrameworkException(
|
|
1217
|
+
ZLinkFrameworkInternalErrorKind.DeadlineExceeded,
|
|
1218
|
+
`Session relocation seal '${key}' was cancelled.`,
|
|
1219
|
+
true
|
|
1220
|
+
)
|
|
1221
|
+
);
|
|
1215
1222
|
if (signal?.aborted === true) {
|
|
1216
1223
|
abortListener();
|
|
1217
1224
|
return promise;
|
|
@@ -1231,9 +1238,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1231
1238
|
const terminal = this.terminalSessionRelocations.get(key);
|
|
1232
1239
|
if (terminal === undefined) return;
|
|
1233
1240
|
if (sourceNodeRid === null || String(sourceNodeRid) !== terminal.targetNodeRid) {
|
|
1234
|
-
throw new ServiceWireProtocolError(
|
|
1235
|
-
`Session relocation ACK '${key}' source node changed.`
|
|
1236
|
-
);
|
|
1241
|
+
throw new ServiceWireProtocolError(`Session relocation ACK '${key}' source node changed.`);
|
|
1237
1242
|
}
|
|
1238
1243
|
validateSessionRelocationResponse(terminal.request, response);
|
|
1239
1244
|
if (sessionRelocationResponseFingerprint(response) !== terminal.responseFingerprint) {
|
|
@@ -1245,9 +1250,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1245
1250
|
return;
|
|
1246
1251
|
}
|
|
1247
1252
|
if (sourceNodeRid === null || String(sourceNodeRid) !== pending.targetNodeRid) {
|
|
1248
|
-
pending.reject(
|
|
1249
|
-
`Session relocation ACK '${key}' source node changed.`
|
|
1250
|
-
)
|
|
1253
|
+
pending.reject(
|
|
1254
|
+
new ServiceWireProtocolError(`Session relocation ACK '${key}' source node changed.`)
|
|
1255
|
+
);
|
|
1251
1256
|
return;
|
|
1252
1257
|
}
|
|
1253
1258
|
try {
|
|
@@ -1271,10 +1276,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1271
1276
|
if (String(status.routingId) === String(sourceNodeRid)) {
|
|
1272
1277
|
return status.lifecycleGeneration;
|
|
1273
1278
|
}
|
|
1274
|
-
return node
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1279
|
+
return node
|
|
1280
|
+
.peers()
|
|
1281
|
+
.find((peer) => peer.routingId !== null && String(peer.routingId) === String(sourceNodeRid))
|
|
1282
|
+
?.lifecycleGeneration;
|
|
1278
1283
|
}
|
|
1279
1284
|
|
|
1280
1285
|
private async handleSessionRelocationSeal(
|
|
@@ -1284,19 +1289,20 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1284
1289
|
signal?: AbortSignal
|
|
1285
1290
|
): Promise<ServiceSessionRelocationSealed> {
|
|
1286
1291
|
if (
|
|
1287
|
-
sourceNodeRid === null
|
|
1288
|
-
|
|
1289
|
-
|
|
1292
|
+
sourceNodeRid === null ||
|
|
1293
|
+
String(sourceNodeRid) !== request.coordinator.nodeRid ||
|
|
1294
|
+
String(sourceNodeRid) !== request.actor.actor.nodeRid
|
|
1290
1295
|
) {
|
|
1291
1296
|
throw new ServiceWireProtocolError(
|
|
1292
1297
|
'Session relocation seal source does not match its coordinator and Actor fence.'
|
|
1293
1298
|
);
|
|
1294
1299
|
}
|
|
1295
1300
|
this.validateSessionOwnerFence(meshName, request.session);
|
|
1296
|
-
const sealSourceGeneration =
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1301
|
+
const sealSourceGeneration = this.sourceLifecycleGeneration(meshName, sourceNodeRid);
|
|
1302
|
+
if (
|
|
1303
|
+
sealSourceGeneration !== request.actor.targetNodeGeneration ||
|
|
1304
|
+
sealSourceGeneration !== request.coordinator.nodeGeneration
|
|
1305
|
+
) {
|
|
1300
1306
|
throw new ServiceWireProtocolError(
|
|
1301
1307
|
'Session relocation seal source lifecycle generation is stale.'
|
|
1302
1308
|
);
|
|
@@ -1315,19 +1321,18 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1315
1321
|
_signal?: AbortSignal
|
|
1316
1322
|
): Promise<void> {
|
|
1317
1323
|
this.validateSessionOwnerFence(meshName, request.session);
|
|
1318
|
-
const expectedSource =
|
|
1319
|
-
? request.route.targetNodeRid
|
|
1320
|
-
: request.coordinator.nodeRid;
|
|
1324
|
+
const expectedSource =
|
|
1325
|
+
request.route.action === 'commit' ? request.route.targetNodeRid : request.coordinator.nodeRid;
|
|
1321
1326
|
if (sourceNodeRid === null || String(sourceNodeRid) !== expectedSource) {
|
|
1322
1327
|
throw new ServiceWireProtocolError(
|
|
1323
1328
|
'Session relocation route source does not match its action fence.'
|
|
1324
1329
|
);
|
|
1325
1330
|
}
|
|
1326
|
-
const routeSourceGeneration =
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
+
const routeSourceGeneration = this.sourceLifecycleGeneration(meshName, sourceNodeRid);
|
|
1332
|
+
const expectedSourceGeneration =
|
|
1333
|
+
request.route.action === 'commit'
|
|
1334
|
+
? request.route.targetNodeGeneration
|
|
1335
|
+
: request.coordinator.nodeGeneration;
|
|
1331
1336
|
if (routeSourceGeneration !== expectedSourceGeneration) {
|
|
1332
1337
|
throw new ServiceWireProtocolError(
|
|
1333
1338
|
'Session relocation route source lifecycle generation is stale.'
|
|
@@ -1347,10 +1352,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1347
1352
|
const status = this.requireMeshNode(meshName).status();
|
|
1348
1353
|
const owner = this.options.currentOwner();
|
|
1349
1354
|
if (
|
|
1350
|
-
String(status.routingId) !== session.sessionOwnerNodeRid
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1355
|
+
String(status.routingId) !== session.sessionOwnerNodeRid ||
|
|
1356
|
+
status.lifecycleGeneration !== session.sessionOwnerNodeGeneration ||
|
|
1357
|
+
owner?.ownerId !== session.sessionOwnerId ||
|
|
1358
|
+
owner.leaseGeneration !== session.sessionOwnerLeaseGeneration
|
|
1354
1359
|
) {
|
|
1355
1360
|
throw new ServiceWireProtocolError(
|
|
1356
1361
|
'Session relocation command does not match the local Session owner fence.'
|
|
@@ -1397,7 +1402,11 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1397
1402
|
let spotSealCommitted = false;
|
|
1398
1403
|
let readinessBoundaryConsumed = false;
|
|
1399
1404
|
let interruptionStartedAt: number | undefined;
|
|
1400
|
-
const spotRegistration = this.spotRegistration(
|
|
1405
|
+
const spotRegistration = this.spotRegistration(
|
|
1406
|
+
meshName,
|
|
1407
|
+
kind,
|
|
1408
|
+
spotAuthority.allocation.stableType
|
|
1409
|
+
);
|
|
1401
1410
|
target ??= await this.selectTarget(
|
|
1402
1411
|
meshName,
|
|
1403
1412
|
String(this.requireMeshNode(meshName).status().routingId),
|
|
@@ -1409,7 +1418,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1409
1418
|
policy: spotRegistration.relocation.kind,
|
|
1410
1419
|
count: 1
|
|
1411
1420
|
},
|
|
1412
|
-
...actorStates.map(state => {
|
|
1421
|
+
...actorStates.map((state) => {
|
|
1413
1422
|
const actorAuthority = actorAuthorities.get(state.actorId)!;
|
|
1414
1423
|
return {
|
|
1415
1424
|
objectKind: 'actor' as const,
|
|
@@ -1431,26 +1440,29 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1431
1440
|
stableType: spotAuthority.allocation.stableType,
|
|
1432
1441
|
objectGeneration: spotAuthority.objectGeneration,
|
|
1433
1442
|
authorityOwnerGeneration: spotAuthority.authorityOwnerGeneration,
|
|
1434
|
-
seal: async captureSignal => {
|
|
1443
|
+
seal: async (captureSignal) => {
|
|
1435
1444
|
interruptionStartedAt = performance.now();
|
|
1436
|
-
spotMessageFollowSeal = this.sealSpotMessageFollow(
|
|
1437
|
-
meshName,
|
|
1438
|
-
spotAuthority,
|
|
1439
|
-
activation
|
|
1440
|
-
);
|
|
1445
|
+
spotMessageFollowSeal = this.sealSpotMessageFollow(meshName, spotAuthority, activation);
|
|
1441
1446
|
// captureRelocation seals synchronously before its first await. Invoke
|
|
1442
1447
|
// it in the same event-loop turn as the wire ingress seal so no
|
|
1443
1448
|
// accepted direct message can enter between the two boundaries.
|
|
1444
1449
|
const spotCaptureOperation = activation.captureRelocation(captureSignal);
|
|
1445
|
-
const preparedSessions = Promise.all(
|
|
1450
|
+
const preparedSessions = Promise.all(
|
|
1451
|
+
actorStates.map(async (state) => ({
|
|
1446
1452
|
state,
|
|
1447
1453
|
actor: state.actor!,
|
|
1448
1454
|
prepared: await this.options.actorTransfer.prepareMaintenanceSession(
|
|
1449
|
-
state.actor!,
|
|
1450
|
-
|
|
1455
|
+
state.actor!,
|
|
1456
|
+
state,
|
|
1457
|
+
captureSignal,
|
|
1458
|
+
false,
|
|
1459
|
+
relocationWireId(aggregateId)
|
|
1460
|
+
)
|
|
1461
|
+
}))
|
|
1462
|
+
);
|
|
1451
1463
|
const preparedSessionsOutcome = preparedSessions.then(
|
|
1452
|
-
value => ({ value }),
|
|
1453
|
-
error => ({ error })
|
|
1464
|
+
(value) => ({ value }),
|
|
1465
|
+
(error) => ({ error })
|
|
1454
1466
|
);
|
|
1455
1467
|
spotCapture = await spotCaptureOperation;
|
|
1456
1468
|
const preparation = await preparedSessionsOutcome;
|
|
@@ -1462,15 +1474,11 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1462
1474
|
timers: spotCapture.timers.map(toServiceTimer)
|
|
1463
1475
|
};
|
|
1464
1476
|
},
|
|
1465
|
-
captureApplicationState: captureSignal =>
|
|
1466
|
-
this.captureApplication(
|
|
1467
|
-
spotRegistration.relocation,
|
|
1468
|
-
activation.spot,
|
|
1469
|
-
captureSignal
|
|
1470
|
-
),
|
|
1477
|
+
captureApplicationState: (captureSignal) =>
|
|
1478
|
+
this.captureApplication(spotRegistration.relocation, activation.spot, captureSignal),
|
|
1471
1479
|
commitSeal: async () => {
|
|
1472
1480
|
if (!spotSealCommitted) {
|
|
1473
|
-
if (spotCapture === undefined || !await activation.commitRelocation(spotCapture)) {
|
|
1481
|
+
if (spotCapture === undefined || !(await activation.commitRelocation(spotCapture))) {
|
|
1474
1482
|
throw new Error(`Spot '${String(activation.spotId)}' relocation seal became stale.`);
|
|
1475
1483
|
}
|
|
1476
1484
|
spotSealCommitted = true;
|
|
@@ -1495,19 +1503,17 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1495
1503
|
meshName,
|
|
1496
1504
|
nodeRid: target.rid
|
|
1497
1505
|
};
|
|
1498
|
-
await session.prepared.commit(
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
? ZLinkSpotKind.User
|
|
1504
|
-
:
|
|
1505
|
-
|
|
1506
|
-
} as never, targetActorRef, committedActorOwnerFence(
|
|
1507
|
-
session.state.actorId,
|
|
1506
|
+
await session.prepared.commit(
|
|
1507
|
+
{
|
|
1508
|
+
routerChannelId: meshName,
|
|
1509
|
+
targetNodeRid: target.rid,
|
|
1510
|
+
spotId: activation.spotId,
|
|
1511
|
+
spotKind: kind === 'user_spot' ? ZLinkSpotKind.User : ZLinkSpotKind.Instance,
|
|
1512
|
+
authorityOwnerGeneration: committedAuthority.authorityOwnerGeneration
|
|
1513
|
+
} as never,
|
|
1508
1514
|
targetActorRef,
|
|
1509
|
-
committedAuthority
|
|
1510
|
-
)
|
|
1515
|
+
committedActorOwnerFence(session.state.actorId, targetActorRef, committedAuthority)
|
|
1516
|
+
);
|
|
1511
1517
|
activation.commitActorDeparture(session.state.actorId);
|
|
1512
1518
|
await this.requireActorManager().completeRelocationSource(session.state.actorId);
|
|
1513
1519
|
}
|
|
@@ -1545,18 +1551,20 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1545
1551
|
}
|
|
1546
1552
|
}
|
|
1547
1553
|
};
|
|
1548
|
-
const actorUnits = actorStates.map(state =>
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1554
|
+
const actorUnits = actorStates.map((state) =>
|
|
1555
|
+
this.actorCaptureUnit(
|
|
1556
|
+
state,
|
|
1557
|
+
actorAuthorities.get(state.actorId)!,
|
|
1558
|
+
sessions,
|
|
1559
|
+
false,
|
|
1560
|
+
undefined,
|
|
1561
|
+
undefined,
|
|
1562
|
+
undefined,
|
|
1563
|
+
undefined,
|
|
1564
|
+
false
|
|
1565
|
+
)
|
|
1566
|
+
);
|
|
1567
|
+
const memberships = actorStates.map((state) => ({
|
|
1560
1568
|
actorKey: encodeAuthorityKey('actor', state.actorId).value,
|
|
1561
1569
|
spotKey: spotKey.value,
|
|
1562
1570
|
spotObjectGeneration: spotAuthority.objectGeneration,
|
|
@@ -1569,39 +1577,56 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1569
1577
|
waitForRelocationBoundary?: (signal?: AbortSignal) => Promise<boolean>;
|
|
1570
1578
|
}
|
|
1571
1579
|
).waitForRelocationBoundary;
|
|
1572
|
-
readinessBoundaryConsumed =
|
|
1573
|
-
|
|
1574
|
-
|
|
1580
|
+
readinessBoundaryConsumed =
|
|
1581
|
+
waitForRelocationBoundary === undefined
|
|
1582
|
+
? false
|
|
1583
|
+
: await waitForRelocationBoundary.call(activation, signal);
|
|
1575
1584
|
// A full in-flight payload budget delays this unit before its seal, so
|
|
1576
1585
|
// the Spot and its Actors keep processing messages while waiting.
|
|
1577
1586
|
await this.waitForRelocationBudgetHeadroom(String(target.rid), signal);
|
|
1578
1587
|
const captureOwner = new ServiceRelocationObjectCaptureOwner();
|
|
1579
|
-
const captured =
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1588
|
+
const captured =
|
|
1589
|
+
kind === 'user_spot'
|
|
1590
|
+
? await captureOwner.captureUserSpotAggregate(
|
|
1591
|
+
aggregateId,
|
|
1592
|
+
1n,
|
|
1593
|
+
spotUnit,
|
|
1594
|
+
actorUnits,
|
|
1595
|
+
memberships,
|
|
1596
|
+
signal
|
|
1597
|
+
)
|
|
1598
|
+
: await captureOwner.captureInstanceSpotAggregate(
|
|
1599
|
+
aggregateId,
|
|
1600
|
+
1n,
|
|
1601
|
+
spotUnit,
|
|
1602
|
+
actorUnits,
|
|
1603
|
+
memberships,
|
|
1604
|
+
signal
|
|
1605
|
+
);
|
|
1584
1606
|
await this.runCoordinator(
|
|
1585
1607
|
meshName,
|
|
1586
1608
|
target,
|
|
1587
1609
|
spotAuthority,
|
|
1588
1610
|
captured,
|
|
1589
|
-
new Map([
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1611
|
+
new Map([
|
|
1612
|
+
[spotKey.value, spotAuthority],
|
|
1613
|
+
...actorStates.map(
|
|
1614
|
+
(state) =>
|
|
1615
|
+
[
|
|
1616
|
+
encodeAuthorityKey('actor', state.actorId).value,
|
|
1617
|
+
actorAuthorities.get(state.actorId)!
|
|
1618
|
+
] as const
|
|
1619
|
+
)
|
|
1620
|
+
]),
|
|
1593
1621
|
signal,
|
|
1594
1622
|
sessions
|
|
1595
1623
|
);
|
|
1596
1624
|
} catch (error) {
|
|
1597
1625
|
outcome = 'failed';
|
|
1598
|
-
if (
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
await activation.completeConsumedRelocationBoundary(
|
|
1603
|
-
ZLinkSpotRelocationReadyOutcome.Continued
|
|
1604
|
-
).catch(() => undefined);
|
|
1626
|
+
if (readinessBoundaryConsumed && !(error instanceof ServiceRelocationPostCommitError)) {
|
|
1627
|
+
await activation
|
|
1628
|
+
.completeConsumedRelocationBoundary(ZLinkSpotRelocationReadyOutcome.Continued)
|
|
1629
|
+
.catch(() => undefined);
|
|
1605
1630
|
}
|
|
1606
1631
|
throw error;
|
|
1607
1632
|
} finally {
|
|
@@ -1644,20 +1669,22 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1644
1669
|
meshName,
|
|
1645
1670
|
String(this.requireMeshNode(meshName).status().routingId),
|
|
1646
1671
|
targetApplicationVersion,
|
|
1647
|
-
[
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1672
|
+
[
|
|
1673
|
+
{
|
|
1674
|
+
objectKind: 'actor',
|
|
1675
|
+
stableType: authority.allocation.stableType,
|
|
1676
|
+
policy: this.actorRegistration(
|
|
1677
|
+
state.meshName ?? meshName,
|
|
1678
|
+
authority.allocation.stableType
|
|
1679
|
+
).relocation.kind,
|
|
1680
|
+
count: 1
|
|
1681
|
+
}
|
|
1682
|
+
],
|
|
1656
1683
|
signal
|
|
1657
1684
|
);
|
|
1658
1685
|
const sessions: SourceActorSession[] = [];
|
|
1659
|
-
const membershipSpotId =
|
|
1660
|
-
?? ((target.entrySpotId ?? String(target.rid)) as RoutingId);
|
|
1686
|
+
const membershipSpotId =
|
|
1687
|
+
targetMembership?.spotId ?? ((target.entrySpotId ?? String(target.rid)) as RoutingId);
|
|
1661
1688
|
const aggregateId = aggregateIdOverride ?? this.reserveRelocationId();
|
|
1662
1689
|
const unit = this.actorCaptureUnit(
|
|
1663
1690
|
state,
|
|
@@ -1676,25 +1703,32 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1676
1703
|
const membership: ServiceRelocationMembership = {
|
|
1677
1704
|
actorKey: encodeAuthorityKey('actor', state.actorId).value,
|
|
1678
1705
|
spotKey: encodeAuthorityKey('user_spot', String(membershipSpotId)).value,
|
|
1679
|
-
spotObjectGeneration:
|
|
1680
|
-
targetMembership?.spotObjectGeneration ?? target.lifecycleGeneration,
|
|
1706
|
+
spotObjectGeneration: targetMembership?.spotObjectGeneration ?? target.lifecycleGeneration,
|
|
1681
1707
|
membershipEpoch: state.spotMembershipEpoch > 0n ? state.spotMembershipEpoch : 1n
|
|
1682
1708
|
};
|
|
1683
1709
|
let interruptionStartedAt: number | undefined;
|
|
1684
1710
|
const measuredUnit: ServiceRelocationCaptureUnit = {
|
|
1685
1711
|
...unit,
|
|
1686
|
-
seal: async captureSignal => {
|
|
1712
|
+
seal: async (captureSignal) => {
|
|
1687
1713
|
interruptionStartedAt = performance.now();
|
|
1688
1714
|
return unit.seal(captureSignal);
|
|
1689
1715
|
}
|
|
1690
1716
|
};
|
|
1691
1717
|
let outcome = 'completed';
|
|
1692
1718
|
try {
|
|
1693
|
-
relocationDebug('standalone_actor.wait_budget', {
|
|
1719
|
+
relocationDebug('standalone_actor.wait_budget', {
|
|
1720
|
+
actorId: state.actorId,
|
|
1721
|
+
targetRid: String(target.rid)
|
|
1722
|
+
});
|
|
1694
1723
|
await this.waitForRelocationBudgetHeadroom(String(target.rid), signal);
|
|
1695
1724
|
relocationDebug('standalone_actor.capture_begin', { actorId: state.actorId });
|
|
1696
1725
|
const captured = await new ServiceRelocationObjectCaptureOwner().captureStandaloneActor(
|
|
1697
|
-
aggregateId,
|
|
1726
|
+
aggregateId,
|
|
1727
|
+
1n,
|
|
1728
|
+
measuredUnit,
|
|
1729
|
+
membership,
|
|
1730
|
+
signal
|
|
1731
|
+
);
|
|
1698
1732
|
relocationDebug('standalone_actor.capture_complete', { actorId: state.actorId });
|
|
1699
1733
|
await this.runCoordinator(
|
|
1700
1734
|
meshName,
|
|
@@ -1731,11 +1765,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1731
1765
|
signal?: AbortSignal
|
|
1732
1766
|
): Promise<void> {
|
|
1733
1767
|
const spotKey = encodeAuthorityKey('user_spot', String(activation.spotId));
|
|
1734
|
-
const spotAuthority = await requireAuthority(
|
|
1735
|
-
this.requireLocationStore(),
|
|
1736
|
-
spotKey,
|
|
1737
|
-
signal
|
|
1738
|
-
);
|
|
1768
|
+
const spotAuthority = await requireAuthority(this.requireLocationStore(), spotKey, signal);
|
|
1739
1769
|
const spotRegistration = this.spotRegistration(
|
|
1740
1770
|
meshName,
|
|
1741
1771
|
'user_spot',
|
|
@@ -1745,16 +1775,17 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1745
1775
|
meshName,
|
|
1746
1776
|
String(this.requireMeshNode(meshName).status().routingId),
|
|
1747
1777
|
targetApplicationVersion,
|
|
1748
|
-
[
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1778
|
+
[
|
|
1779
|
+
{
|
|
1780
|
+
objectKind: 'user_spot',
|
|
1781
|
+
stableType: spotAuthority.allocation.stableType,
|
|
1782
|
+
policy: spotRegistration.relocation.kind,
|
|
1783
|
+
count: 1
|
|
1784
|
+
}
|
|
1785
|
+
],
|
|
1754
1786
|
signal
|
|
1755
1787
|
);
|
|
1756
|
-
let spotCapture:
|
|
1757
|
-
Awaited<ReturnType<ZLinkSpotActivation['captureRelocation']>> | undefined;
|
|
1788
|
+
let spotCapture: Awaited<ReturnType<ZLinkSpotActivation['captureRelocation']>> | undefined;
|
|
1758
1789
|
let spotMessageFollowSeal: ServiceSpotMessageFollowSeal | undefined;
|
|
1759
1790
|
let sealCommitted = false;
|
|
1760
1791
|
let interruptionStartedAt: number | undefined;
|
|
@@ -1764,13 +1795,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1764
1795
|
stableType: spotAuthority.allocation.stableType,
|
|
1765
1796
|
objectGeneration: spotAuthority.objectGeneration,
|
|
1766
1797
|
authorityOwnerGeneration: spotAuthority.authorityOwnerGeneration,
|
|
1767
|
-
seal: async captureSignal => {
|
|
1798
|
+
seal: async (captureSignal) => {
|
|
1768
1799
|
interruptionStartedAt = performance.now();
|
|
1769
|
-
spotMessageFollowSeal = this.sealSpotMessageFollow(
|
|
1770
|
-
meshName,
|
|
1771
|
-
spotAuthority,
|
|
1772
|
-
activation
|
|
1773
|
-
);
|
|
1800
|
+
spotMessageFollowSeal = this.sealSpotMessageFollow(meshName, spotAuthority, activation);
|
|
1774
1801
|
spotCapture = await activation.captureRelocation(captureSignal);
|
|
1775
1802
|
return {
|
|
1776
1803
|
boundSessionState: Buffer.alloc(0),
|
|
@@ -1783,7 +1810,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1783
1810
|
captureApplicationState: async () => Buffer.alloc(0),
|
|
1784
1811
|
commitSeal: async () => {
|
|
1785
1812
|
if (!sealCommitted) {
|
|
1786
|
-
if (spotCapture === undefined || !await activation.commitRelocation(spotCapture)) {
|
|
1813
|
+
if (spotCapture === undefined || !(await activation.commitRelocation(spotCapture))) {
|
|
1787
1814
|
throw new Error(
|
|
1788
1815
|
`PerActor Spot '${String(activation.spotId)}' relocation seal became stale.`
|
|
1789
1816
|
);
|
|
@@ -1864,7 +1891,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1864
1891
|
sessionRelocation?: ServiceWireOperationId,
|
|
1865
1892
|
retainSourceForActorJoin = false
|
|
1866
1893
|
): ServiceRelocationCaptureUnit {
|
|
1867
|
-
const registration = this.actorRegistration(
|
|
1894
|
+
const registration = this.actorRegistration(
|
|
1895
|
+
state.meshName ?? meshName ?? '',
|
|
1896
|
+
state.actorType ?? authority.allocation.stableType
|
|
1897
|
+
);
|
|
1868
1898
|
let ownSession: SourceActorSession | undefined;
|
|
1869
1899
|
return {
|
|
1870
1900
|
authorityKey: encodeAuthorityKey('actor', state.actorId).value,
|
|
@@ -1872,17 +1902,22 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1872
1902
|
stableType: authority.allocation.stableType,
|
|
1873
1903
|
objectGeneration: authority.objectGeneration,
|
|
1874
1904
|
authorityOwnerGeneration: authority.authorityOwnerGeneration,
|
|
1875
|
-
seal: async signal => {
|
|
1905
|
+
seal: async (signal) => {
|
|
1876
1906
|
if (standalone) {
|
|
1877
1907
|
ownSession = {
|
|
1878
1908
|
state,
|
|
1879
1909
|
actor: state.actor!,
|
|
1880
1910
|
prepared: await this.options.actorTransfer.prepareMaintenanceSession(
|
|
1881
|
-
state.actor!,
|
|
1911
|
+
state.actor!,
|
|
1912
|
+
state,
|
|
1913
|
+
signal,
|
|
1914
|
+
true,
|
|
1915
|
+
sessionRelocation
|
|
1916
|
+
)
|
|
1882
1917
|
};
|
|
1883
1918
|
sessions.push(ownSession);
|
|
1884
1919
|
}
|
|
1885
|
-
const prepared = standalone ? ownSession : sessions.find(value => value.state === state);
|
|
1920
|
+
const prepared = standalone ? ownSession : sessions.find((value) => value.state === state);
|
|
1886
1921
|
return {
|
|
1887
1922
|
boundSessionState: encodeActorSession(prepared?.prepared.target),
|
|
1888
1923
|
queuedMessages: encodeHandoffQueuedMessages(prepared?.prepared.handoffBacklog ?? []),
|
|
@@ -1892,15 +1927,18 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1892
1927
|
// The canonical participant body (discriminator 1) is raw application
|
|
1893
1928
|
// state. The sealed bound-session target remains in sealed work and is
|
|
1894
1929
|
// transported only by Prepare's NODE-INTERNAL sideband frames.
|
|
1895
|
-
captureApplicationState: signal =>
|
|
1896
|
-
registration.relocation,
|
|
1897
|
-
state.actor!,
|
|
1898
|
-
signal
|
|
1899
|
-
),
|
|
1930
|
+
captureApplicationState: (signal) =>
|
|
1931
|
+
this.captureApplication(registration.relocation, state.actor!, signal),
|
|
1900
1932
|
commitSeal: async () => {
|
|
1901
|
-
if (
|
|
1902
|
-
|
|
1903
|
-
|
|
1933
|
+
if (
|
|
1934
|
+
!standalone ||
|
|
1935
|
+
ownSession === undefined ||
|
|
1936
|
+
target === undefined ||
|
|
1937
|
+
meshName === undefined
|
|
1938
|
+
)
|
|
1939
|
+
return;
|
|
1940
|
+
const membershipSpotId =
|
|
1941
|
+
targetMembership?.spotId ?? ((target.entrySpotId ?? String(target.rid)) as RoutingId);
|
|
1904
1942
|
const committedAuthority = await requireAuthority(
|
|
1905
1943
|
this.requireLocationStore(),
|
|
1906
1944
|
encodeAuthorityKey('actor', state.actorId)
|
|
@@ -1911,17 +1949,17 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1911
1949
|
meshName,
|
|
1912
1950
|
nodeRid: target.rid
|
|
1913
1951
|
};
|
|
1914
|
-
await ownSession.prepared.commit(
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1952
|
+
await ownSession.prepared.commit(
|
|
1953
|
+
{
|
|
1954
|
+
routerChannelId: meshName,
|
|
1955
|
+
targetNodeRid: target.rid,
|
|
1956
|
+
spotId: membershipSpotId,
|
|
1957
|
+
spotKind: targetMembership?.spotKind ?? ZLinkSpotKind.Entry,
|
|
1958
|
+
authorityOwnerGeneration: committedAuthority.authorityOwnerGeneration
|
|
1959
|
+
} as never,
|
|
1922
1960
|
targetActorRef,
|
|
1923
|
-
committedAuthority
|
|
1924
|
-
)
|
|
1961
|
+
committedActorOwnerFence(state.actorId, targetActorRef, committedAuthority)
|
|
1962
|
+
);
|
|
1925
1963
|
if (!retainSourceForActorJoin) {
|
|
1926
1964
|
await this.requireActorManager().completeRelocationSource(state.actorId);
|
|
1927
1965
|
}
|
|
@@ -1933,11 +1971,11 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1933
1971
|
}
|
|
1934
1972
|
|
|
1935
1973
|
private reserveRelocationId(): string {
|
|
1936
|
-
const id = createServiceRelocationId(candidate => {
|
|
1974
|
+
const id = createServiceRelocationId((candidate) => {
|
|
1937
1975
|
if (this.sourceRelocationIds.has(candidate)) return true;
|
|
1938
1976
|
const wire = relocationWireId(candidate);
|
|
1939
1977
|
const prefix = `${wire.high}:${wire.low}:`;
|
|
1940
|
-
return [...this.targetStages.keys()].some(key => key.startsWith(prefix));
|
|
1978
|
+
return [...this.targetStages.keys()].some((key) => key.startsWith(prefix));
|
|
1941
1979
|
});
|
|
1942
1980
|
this.sourceRelocationIds.add(id);
|
|
1943
1981
|
return id;
|
|
@@ -1946,8 +1984,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1946
1984
|
private reserveExactRelocationId(id: string): void {
|
|
1947
1985
|
const wire = relocationWireId(id);
|
|
1948
1986
|
const prefix = `${wire.high}:${wire.low}:`;
|
|
1949
|
-
if (
|
|
1950
|
-
|
|
1987
|
+
if (
|
|
1988
|
+
this.sourceRelocationIds.has(id) ||
|
|
1989
|
+
[...this.targetStages.keys()].some((key) => key.startsWith(prefix))
|
|
1990
|
+
) {
|
|
1951
1991
|
throw new Error(`Actor Join relocation '${id}' is already active.`);
|
|
1952
1992
|
}
|
|
1953
1993
|
this.sourceRelocationIds.add(id);
|
|
@@ -1964,11 +2004,13 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1964
2004
|
/** Target's advertised relocation state chunk cap (Actor Join only; spec 15 §4.2). */
|
|
1965
2005
|
advertisedReceiveChunkLimitBytes?: number
|
|
1966
2006
|
): Promise<void> {
|
|
1967
|
-
relocationDebug('coordinator.begin', {
|
|
2007
|
+
relocationDebug('coordinator.begin', {
|
|
2008
|
+
aggregateId: captured.envelope.aggregateId,
|
|
2009
|
+
targetRid: String(target.rid)
|
|
2010
|
+
});
|
|
1968
2011
|
const localStatus = this.requireMeshNode(meshName).status();
|
|
1969
|
-
const controlDeadlineAtMs =
|
|
1970
|
-
? performance.now() + 30_000
|
|
1971
|
-
: Number.MAX_SAFE_INTEGER;
|
|
2012
|
+
const controlDeadlineAtMs =
|
|
2013
|
+
signal === undefined ? performance.now() + 30_000 : Number.MAX_SAFE_INTEGER;
|
|
1972
2014
|
const coordinator = {
|
|
1973
2015
|
ownerId: primary.ownerId,
|
|
1974
2016
|
leaseGeneration: primary.ownerLeaseGeneration,
|
|
@@ -1992,7 +2034,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
1992
2034
|
const relayAuthorityId = wireIdText(relocationWireId(captured.envelope.aggregateId));
|
|
1993
2035
|
const relayAuthorityKey = primaryKey(captured.envelope);
|
|
1994
2036
|
if (this.relocationAuthorityKeys.has(relayAuthorityId)) {
|
|
1995
|
-
throw new Error(
|
|
2037
|
+
throw new Error(
|
|
2038
|
+
`Relocation reply relay authority '${relayAuthorityId}' is already registered.`
|
|
2039
|
+
);
|
|
1996
2040
|
}
|
|
1997
2041
|
this.relocationAuthorityKeys.set(relayAuthorityId, relayAuthorityKey);
|
|
1998
2042
|
// The captured payload stays in source memory: this copy is the only
|
|
@@ -2004,18 +2048,16 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2004
2048
|
targetFence
|
|
2005
2049
|
);
|
|
2006
2050
|
const actorJoinProfile = this.sourceActorJoinProfiles.get(captured.envelope.aggregateId);
|
|
2007
|
-
const transportEnvelope =
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
target.applicationVersion
|
|
2018
|
-
);
|
|
2051
|
+
const transportEnvelope =
|
|
2052
|
+
actorJoinProfile?.canonicalRecovery === undefined
|
|
2053
|
+
? canonicalEnvelope
|
|
2054
|
+
: appendCanonicalActorJoinRecovery(
|
|
2055
|
+
canonicalEnvelope,
|
|
2056
|
+
actorJoinProfile,
|
|
2057
|
+
coordinator,
|
|
2058
|
+
primary
|
|
2059
|
+
);
|
|
2060
|
+
const encoded = encodeServiceRelocationEnvelope(transportEnvelope, target.applicationVersion);
|
|
2019
2061
|
const limits = this.relocationLimits();
|
|
2020
2062
|
// Actor Join threads the target's admission-time advertised cap in;
|
|
2021
2063
|
// other relocation paths have no such negotiation and fall back to the
|
|
@@ -2025,9 +2067,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2025
2067
|
advertisedReceiveChunkLimitBytes
|
|
2026
2068
|
);
|
|
2027
2069
|
const plan = planRelocationChunks(encoded, chunkLimitBytes);
|
|
2028
|
-
const convergenceDeadlineAtMs =
|
|
2029
|
-
|
|
2030
|
-
|
|
2070
|
+
const convergenceDeadlineAtMs =
|
|
2071
|
+
signal === undefined
|
|
2072
|
+
? performance.now() + RELOCATION_OPERATION_RETENTION_MS
|
|
2073
|
+
: controlDeadlineAtMs;
|
|
2031
2074
|
let readyReceived = false;
|
|
2032
2075
|
let sourceCommitted = false;
|
|
2033
2076
|
try {
|
|
@@ -2059,15 +2102,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2059
2102
|
encodePrepareSideband(captured.envelope)
|
|
2060
2103
|
);
|
|
2061
2104
|
try {
|
|
2062
|
-
relocationDebug('coordinator.state_transfer_begin', {
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
prepare,
|
|
2067
|
-
encoded,
|
|
2068
|
-
plan,
|
|
2069
|
-
signal
|
|
2070
|
-
);
|
|
2105
|
+
relocationDebug('coordinator.state_transfer_begin', {
|
|
2106
|
+
aggregateId: captured.envelope.aggregateId
|
|
2107
|
+
});
|
|
2108
|
+
await this.sendRelocationStateChunks(meshName, target, prepare, encoded, plan, signal);
|
|
2071
2109
|
} catch (chunkError) {
|
|
2072
2110
|
readyOperation.catch(() => undefined);
|
|
2073
2111
|
throw chunkError;
|
|
@@ -2086,13 +2124,16 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2086
2124
|
readonly canonicalBytes: Buffer;
|
|
2087
2125
|
}> = [];
|
|
2088
2126
|
for (const session of sessions) {
|
|
2089
|
-
const participant = captured.envelope.participants.find(
|
|
2090
|
-
value
|
|
2091
|
-
|
|
2092
|
-
|
|
2127
|
+
const participant = captured.envelope.participants.find(
|
|
2128
|
+
(value) =>
|
|
2129
|
+
value.objectKind === 'actor' &&
|
|
2130
|
+
decodeAuthorityKey({ value: value.key } as ZLinkAuthorityKey).globalId ===
|
|
2131
|
+
session.state.actorId
|
|
2093
2132
|
);
|
|
2094
2133
|
if (participant === undefined) {
|
|
2095
|
-
throw new Error(
|
|
2134
|
+
throw new Error(
|
|
2135
|
+
`Relocation Actor '${session.state.actorId}' is missing from its envelope.`
|
|
2136
|
+
);
|
|
2096
2137
|
}
|
|
2097
2138
|
for (const packet of session.prepared.takeRelocationRelay()) {
|
|
2098
2139
|
const frozenRecord = canonicalRelayFrozenRecord(
|
|
@@ -2125,7 +2166,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2125
2166
|
object,
|
|
2126
2167
|
boundaryRecordCount: BigInt(boundaryBatch.length),
|
|
2127
2168
|
boundaryChecksumCrc32c: crc32c(
|
|
2128
|
-
Buffer.concat(boundaryBatch.map(value => value.canonicalBytes))
|
|
2169
|
+
Buffer.concat(boundaryBatch.map((value) => value.canonicalBytes))
|
|
2129
2170
|
)
|
|
2130
2171
|
});
|
|
2131
2172
|
// The one-way cutover submit reaches a terminal result exactly once;
|
|
@@ -2145,7 +2186,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2145
2186
|
meshName,
|
|
2146
2187
|
target.rid,
|
|
2147
2188
|
relayAuthorityId,
|
|
2148
|
-
[...boundaryBatch.map(value => value.frame), cutoverFrame],
|
|
2189
|
+
[...boundaryBatch.map((value) => value.frame), cutoverFrame],
|
|
2149
2190
|
submitFailure !== undefined,
|
|
2150
2191
|
limits
|
|
2151
2192
|
);
|
|
@@ -2156,7 +2197,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2156
2197
|
convergenceDeadlineAtMs,
|
|
2157
2198
|
signal
|
|
2158
2199
|
);
|
|
2159
|
-
relocationDebug('coordinator.target_authorities_observed', {
|
|
2200
|
+
relocationDebug('coordinator.target_authorities_observed', {
|
|
2201
|
+
aggregateId: captured.envelope.aggregateId
|
|
2202
|
+
});
|
|
2160
2203
|
for (const session of sessions) session.prepared.setReplayResults([]);
|
|
2161
2204
|
await captured.commitSource();
|
|
2162
2205
|
sourceCommitted = true;
|
|
@@ -2288,9 +2331,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2288
2331
|
let budget = this.peerPayloadBudgets.get(targetRid);
|
|
2289
2332
|
if (budget === undefined) {
|
|
2290
2333
|
budget = new ZLinkRelocationInFlightBudget(
|
|
2291
|
-
effectiveRelocationBudgetBytes(
|
|
2292
|
-
this.relocationLimits().relocationInFlightPayloadBudgetBytes
|
|
2293
|
-
)
|
|
2334
|
+
effectiveRelocationBudgetBytes(this.relocationLimits().relocationInFlightPayloadBudgetBytes)
|
|
2294
2335
|
);
|
|
2295
2336
|
this.peerPayloadBudgets.set(targetRid, budget);
|
|
2296
2337
|
}
|
|
@@ -2418,25 +2459,31 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2418
2459
|
signal?: AbortSignal
|
|
2419
2460
|
): Promise<ServiceMaintenanceRelocationReady> {
|
|
2420
2461
|
if (sourceNodeRid === null || String(sourceNodeRid) !== request.sourceNodeRid) {
|
|
2421
|
-
throw new Error(
|
|
2462
|
+
throw new Error(
|
|
2463
|
+
'Relocation prepare source node fence does not match the authenticated peer.'
|
|
2464
|
+
);
|
|
2422
2465
|
}
|
|
2423
2466
|
const targetStatus = this.requireMeshNode(meshName).status();
|
|
2424
2467
|
const targetOwner = this.options.currentOwner();
|
|
2425
2468
|
const targetDescriptor = this.options.localDescriptor?.(meshName);
|
|
2426
|
-
if (
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2469
|
+
if (
|
|
2470
|
+
targetOwner === undefined ||
|
|
2471
|
+
String(targetStatus.routingId) !== request.target.nodeRid ||
|
|
2472
|
+
targetStatus.lifecycleGeneration !== request.target.nodeGeneration ||
|
|
2473
|
+
targetOwner.ownerId !== request.target.ownerId ||
|
|
2474
|
+
targetOwner.leaseGeneration !== request.target.ownerLeaseGeneration ||
|
|
2475
|
+
(targetDescriptor !== undefined &&
|
|
2476
|
+
targetDescriptor.applicationVersion !== request.applicationVersion)
|
|
2477
|
+
) {
|
|
2433
2478
|
throw new Error('Relocation prepare target fence does not match the target owner.');
|
|
2434
2479
|
}
|
|
2435
2480
|
const fingerprint = stringifyWire(request);
|
|
2436
2481
|
const existing = this.targetStages.get(stagingId);
|
|
2437
2482
|
if (existing !== undefined) {
|
|
2438
|
-
if (
|
|
2439
|
-
|
|
2483
|
+
if (
|
|
2484
|
+
existing.offer.authenticatedSourceNodeRid !== String(sourceNodeRid) ||
|
|
2485
|
+
existing.offer.prepareFingerprint !== fingerprint
|
|
2486
|
+
) {
|
|
2440
2487
|
throw new Error('Relocation prepare retry source node changed.');
|
|
2441
2488
|
}
|
|
2442
2489
|
return relocationReady(request);
|
|
@@ -2466,38 +2513,27 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2466
2513
|
// wire code in the vocabulary yet, so it falls through unwrapped to
|
|
2467
2514
|
// the generic InternalFailure code (spec 15 §4).
|
|
2468
2515
|
if (this.disposed) throw error;
|
|
2469
|
-
const message =
|
|
2470
|
-
|
|
2471
|
-
|
|
2516
|
+
const message =
|
|
2517
|
+
error !== null && typeof error === 'object' && 'message' in error
|
|
2518
|
+
? ((error as { readonly message?: unknown }).message ?? error)
|
|
2519
|
+
: error;
|
|
2472
2520
|
throw new ServiceRelocationDataLostError(String(message));
|
|
2473
2521
|
}
|
|
2474
|
-
const envelope = decodeServiceRelocationEnvelope(
|
|
2475
|
-
payload,
|
|
2476
|
-
request.targetAttemptGeneration
|
|
2477
|
-
);
|
|
2522
|
+
const envelope = decodeServiceRelocationEnvelope(payload, request.targetAttemptGeneration);
|
|
2478
2523
|
const inventoryEnvelope = attachPrepareBoundSessions(
|
|
2479
|
-
await this.resolveTargetParticipantInventory(
|
|
2480
|
-
request,
|
|
2481
|
-
envelope,
|
|
2482
|
-
signal
|
|
2483
|
-
),
|
|
2524
|
+
await this.resolveTargetParticipantInventory(request, envelope, signal),
|
|
2484
2525
|
request.nodeInternalBoundSessions
|
|
2485
2526
|
);
|
|
2486
2527
|
validatePrepareEnvelope(request, inventoryEnvelope);
|
|
2487
2528
|
let materialized:
|
|
2488
|
-
|
|
2489
|
-
| undefined;
|
|
2529
|
+
(Pick<LocalStage, 'owner' | 'staging'> & { readonly target: LocalTargetPort }) | undefined;
|
|
2490
2530
|
let reservation: TargetRelocationReservation | undefined;
|
|
2491
2531
|
try {
|
|
2492
2532
|
// Deferred Join completion publishes into the source authority's
|
|
2493
2533
|
// canonical relocation slot. Do that while the row is still CAS-able;
|
|
2494
2534
|
// the aggregate marker installed by reserveTargetForPrepare deliberately
|
|
2495
2535
|
// rejects unrelated authority CAS until commit/abort.
|
|
2496
|
-
materialized = await this.materializeTargetEnvelope(
|
|
2497
|
-
meshName,
|
|
2498
|
-
inventoryEnvelope,
|
|
2499
|
-
signal
|
|
2500
|
-
);
|
|
2536
|
+
materialized = await this.materializeTargetEnvelope(meshName, inventoryEnvelope, signal);
|
|
2501
2537
|
await materialized.owner.restoreSavedWork(materialized.staging, signal);
|
|
2502
2538
|
reservation = await this.reserveTargetForPrepare(
|
|
2503
2539
|
meshName,
|
|
@@ -2539,8 +2575,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2539
2575
|
const stage = this.targetStages.get(stagingId);
|
|
2540
2576
|
if (stage === undefined || stage.fallback !== undefined || stage.finalize !== undefined) return;
|
|
2541
2577
|
stage.fallback = setTimeout(() => {
|
|
2542
|
-
void this.beginTargetFinalize(meshName, stagingId, stage, true)
|
|
2543
|
-
|
|
2578
|
+
void this.beginTargetFinalize(meshName, stagingId, stage, true).catch((error) =>
|
|
2579
|
+
console.error('[zlink.runtime.relocation.location_update_failed]', error)
|
|
2580
|
+
);
|
|
2544
2581
|
}, this.relocationLimits().relocationCutoverWaitTimeoutMs);
|
|
2545
2582
|
stage.fallback.unref();
|
|
2546
2583
|
}
|
|
@@ -2607,9 +2644,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2607
2644
|
const expectedCount = Number(request.boundaryRecordCount);
|
|
2608
2645
|
const staged = stage.boundaryRelay;
|
|
2609
2646
|
const checksumOf = (records: readonly ServiceMaintenanceRelocationData[]) =>
|
|
2610
|
-
crc32c(Buffer.concat(records.map(value => value.frozenRecord.canonicalBytes)));
|
|
2611
|
-
if (staged.length === expectedCount
|
|
2612
|
-
&& checksumOf(staged) === request.boundaryChecksumCrc32c) {
|
|
2647
|
+
crc32c(Buffer.concat(records.map((value) => value.frozenRecord.canonicalBytes)));
|
|
2648
|
+
if (staged.length === expectedCount && checksumOf(staged) === request.boundaryChecksumCrc32c) {
|
|
2613
2649
|
return;
|
|
2614
2650
|
}
|
|
2615
2651
|
if (staged.length > expectedCount) {
|
|
@@ -2623,8 +2659,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2623
2659
|
// Spec 28 §4.4 — on an ordered connection this comparison always holds on
|
|
2624
2660
|
// the normal path; a mismatch signals an implementation defect.
|
|
2625
2661
|
throw new Error(
|
|
2626
|
-
`Relocation cutover boundary confirmation mismatch for '${stagingId}' `
|
|
2627
|
-
|
|
2662
|
+
`Relocation cutover boundary confirmation mismatch for '${stagingId}' ` +
|
|
2663
|
+
`(staged=${staged.length} declared=${expectedCount}).`
|
|
2628
2664
|
);
|
|
2629
2665
|
}
|
|
2630
2666
|
|
|
@@ -2643,7 +2679,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2643
2679
|
});
|
|
2644
2680
|
}
|
|
2645
2681
|
const finalize = stage.lane.then(() =>
|
|
2646
|
-
this.finalizeTargetStage(meshName, stagingId, stage, signal)
|
|
2682
|
+
this.finalizeTargetStage(meshName, stagingId, stage, signal)
|
|
2683
|
+
);
|
|
2647
2684
|
stage.finalize = finalize;
|
|
2648
2685
|
stage.lane = finalize;
|
|
2649
2686
|
return finalize;
|
|
@@ -2667,11 +2704,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2667
2704
|
for (const relay of stage.boundaryRelay.splice(0)) {
|
|
2668
2705
|
await this.applyRelocationData(stage, relay, signal);
|
|
2669
2706
|
}
|
|
2670
|
-
authority = await this.commitTargetReservation(
|
|
2671
|
-
stage,
|
|
2672
|
-
stage.offer.reservation,
|
|
2673
|
-
signal
|
|
2674
|
-
);
|
|
2707
|
+
authority = await this.commitTargetReservation(stage, stage.offer.reservation, signal);
|
|
2675
2708
|
authorityCommitted = true;
|
|
2676
2709
|
resumeStartedAt = performance.now();
|
|
2677
2710
|
stage.phase = 'committed';
|
|
@@ -2744,25 +2777,27 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2744
2777
|
const target = frozen.target;
|
|
2745
2778
|
const payload = frozen.applicationPayload;
|
|
2746
2779
|
const preparedTarget = stage.offer.prepare.target;
|
|
2747
|
-
if (
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2780
|
+
if (
|
|
2781
|
+
request.object.kind !== 'actor' ||
|
|
2782
|
+
target?.kind !== 'actor' ||
|
|
2783
|
+
payload === undefined ||
|
|
2784
|
+
payload.packetName !== '__zlink.actor.handoff.accepted' ||
|
|
2785
|
+
target.actorId !== request.object.actorId ||
|
|
2786
|
+
target.generation !== request.object.objectGeneration ||
|
|
2787
|
+
target.targetNodeRid !== preparedTarget.nodeRid ||
|
|
2788
|
+
target.targetNodeGeneration !== preparedTarget.nodeGeneration ||
|
|
2789
|
+
target.authorityOwnerGeneration !== request.object.expectedAuthorityOwnerGeneration + 1n ||
|
|
2790
|
+
target.ownerLeaseGeneration !== preparedTarget.ownerLeaseGeneration
|
|
2791
|
+
) {
|
|
2758
2792
|
throw new Error('Relocation data does not target the exact temporary Actor queue.');
|
|
2759
2793
|
}
|
|
2760
2794
|
const key = encodeAuthorityKey('actor', request.object.actorId).value;
|
|
2761
|
-
const participant = stage.staging.envelope.participants.find(value => value.key === key);
|
|
2762
|
-
if (
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2795
|
+
const participant = stage.staging.envelope.participants.find((value) => value.key === key);
|
|
2796
|
+
if (
|
|
2797
|
+
participant === undefined ||
|
|
2798
|
+
participant.objectGeneration !== request.object.objectGeneration ||
|
|
2799
|
+
participant.authorityOwnerGeneration !== request.object.expectedAuthorityOwnerGeneration
|
|
2800
|
+
) {
|
|
2766
2801
|
throw new Error('Relocation data Actor is not part of the prepared unit.');
|
|
2767
2802
|
}
|
|
2768
2803
|
}
|
|
@@ -2802,12 +2837,13 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2802
2837
|
actorJoinAdmissionIdentity(stage.staging.envelope),
|
|
2803
2838
|
hidden.actor,
|
|
2804
2839
|
toFrameworkActorRef(nativeRef, meshName),
|
|
2805
|
-
sourceNodeRid =>
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2840
|
+
(sourceNodeRid) =>
|
|
2841
|
+
this.submitActorJoinSourceLeave(
|
|
2842
|
+
meshName,
|
|
2843
|
+
sourceNodeRid,
|
|
2844
|
+
stage.staging.envelope.aggregateId,
|
|
2845
|
+
hidden.actor!.context.actorId
|
|
2846
|
+
),
|
|
2811
2847
|
signal
|
|
2812
2848
|
);
|
|
2813
2849
|
}
|
|
@@ -2822,26 +2858,20 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2822
2858
|
try {
|
|
2823
2859
|
const submitted = await this.requireMeshNode(meshName).sendToNode(
|
|
2824
2860
|
sourceNodeRid,
|
|
2825
|
-
Buffer.from(
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2861
|
+
Buffer.from(
|
|
2862
|
+
JSON.stringify({
|
|
2863
|
+
packetName: ZLINK_REMOTE_ACTOR_SOURCE_LEAVE_TERMINAL,
|
|
2864
|
+
transferId: relocationId,
|
|
2865
|
+
actorId,
|
|
2866
|
+
succeeded: true
|
|
2867
|
+
})
|
|
2868
|
+
)
|
|
2831
2869
|
);
|
|
2832
2870
|
if (submitted !== SubmitResult.Ok) {
|
|
2833
|
-
console.warn(
|
|
2834
|
-
'[zlink.runtime.relocation.source_leave_submit_failed]',
|
|
2835
|
-
actorId,
|
|
2836
|
-
submitted
|
|
2837
|
-
);
|
|
2871
|
+
console.warn('[zlink.runtime.relocation.source_leave_submit_failed]', actorId, submitted);
|
|
2838
2872
|
}
|
|
2839
2873
|
} catch (error) {
|
|
2840
|
-
console.warn(
|
|
2841
|
-
'[zlink.runtime.relocation.source_leave_submit_failed]',
|
|
2842
|
-
actorId,
|
|
2843
|
-
error
|
|
2844
|
-
);
|
|
2874
|
+
console.warn('[zlink.runtime.relocation.source_leave_submit_failed]', actorId, error);
|
|
2845
2875
|
}
|
|
2846
2876
|
}
|
|
2847
2877
|
|
|
@@ -2855,7 +2885,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2855
2885
|
const terminalActorId = this.terminalActorJoinSourceLeaves.get(input.transferId);
|
|
2856
2886
|
if (terminalActorId === undefined) return false;
|
|
2857
2887
|
if (terminalActorId !== input.actorId || !input.succeeded) {
|
|
2858
|
-
throw new Error(
|
|
2888
|
+
throw new Error(
|
|
2889
|
+
`Actor Join relocation '${input.transferId}' source leave identity changed.`
|
|
2890
|
+
);
|
|
2859
2891
|
}
|
|
2860
2892
|
this.terminalActorJoinSourceLeaves.touch(input.transferId);
|
|
2861
2893
|
return true;
|
|
@@ -2865,18 +2897,19 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2865
2897
|
}
|
|
2866
2898
|
this.sourceActorJoinProfiles.delete(input.transferId);
|
|
2867
2899
|
this.terminalActorJoinSourceLeaves.remember(input.transferId, input.actorId);
|
|
2868
|
-
void profile.ready
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2900
|
+
void profile.ready
|
|
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
|
+
});
|
|
2880
2913
|
return true;
|
|
2881
2914
|
}
|
|
2882
2915
|
|
|
@@ -2922,13 +2955,15 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2922
2955
|
{ value: participant.key } as ZLinkAuthorityKey,
|
|
2923
2956
|
signal
|
|
2924
2957
|
);
|
|
2925
|
-
if (
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
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
|
+
) {
|
|
2932
2967
|
exact = false;
|
|
2933
2968
|
break;
|
|
2934
2969
|
}
|
|
@@ -2937,7 +2972,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2937
2972
|
if (performance.now() >= deadlineAtMs) {
|
|
2938
2973
|
throw new Error('Relocation target owner was not confirmed before its original deadline.');
|
|
2939
2974
|
}
|
|
2940
|
-
await new Promise<void>(resolve => setTimeout(resolve, 10));
|
|
2975
|
+
await new Promise<void>((resolve) => setTimeout(resolve, 10));
|
|
2941
2976
|
}
|
|
2942
2977
|
}
|
|
2943
2978
|
|
|
@@ -2992,12 +3027,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
2992
3027
|
if (sourceNodeRid === null) {
|
|
2993
3028
|
throw new Error('Relocation reply relay has no authenticated target node.');
|
|
2994
3029
|
}
|
|
2995
|
-
await this.requireAdmittedPeer(
|
|
2996
|
-
meshName,
|
|
2997
|
-
String(sourceNodeRid),
|
|
2998
|
-
relay.coordinator,
|
|
2999
|
-
signal
|
|
3000
|
-
);
|
|
3030
|
+
await this.requireAdmittedPeer(meshName, String(sourceNodeRid), relay.coordinator, signal);
|
|
3001
3031
|
const relocationId = wireIdText(relay.relocation);
|
|
3002
3032
|
const authorityKey = this.relocationAuthorityKeys.get(relocationId);
|
|
3003
3033
|
if (authorityKey === undefined) {
|
|
@@ -3008,12 +3038,13 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3008
3038
|
{ value: authorityKey } as ZLinkAuthorityKey,
|
|
3009
3039
|
signal
|
|
3010
3040
|
);
|
|
3011
|
-
if (
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3041
|
+
if (
|
|
3042
|
+
authority.storeVersion.value !== relay.coordinator.expectedAuthorityStoreVersion ||
|
|
3043
|
+
authority.ownerId !== relay.coordinator.ownerId ||
|
|
3044
|
+
authority.ownerLeaseGeneration !== relay.coordinator.leaseGeneration ||
|
|
3045
|
+
String(authority.allocation.descriptor.rid) !== relay.coordinator.nodeRid ||
|
|
3046
|
+
authority.allocation.descriptorLifecycleGeneration !== relay.coordinator.nodeGeneration
|
|
3047
|
+
) {
|
|
3017
3048
|
throw new Error('Relocation reply relay coordinator authority fence is stale.');
|
|
3018
3049
|
}
|
|
3019
3050
|
const accepted = this.options.actorTransfer.relayCanonicalMaintenanceTerminal(
|
|
@@ -3022,18 +3053,22 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3022
3053
|
handoffResultFromRelay(relay),
|
|
3023
3054
|
sourceNodeRid
|
|
3024
3055
|
);
|
|
3025
|
-
if (
|
|
3026
|
-
|
|
3056
|
+
if (
|
|
3057
|
+
(accepted.status !== 'terminalReceived' && accepted.status !== 'alreadyTerminal') ||
|
|
3058
|
+
accepted.source === undefined
|
|
3059
|
+
) {
|
|
3027
3060
|
throw new Error('Canonical relocation reply relay collided with another source route.');
|
|
3028
3061
|
}
|
|
3029
3062
|
const requestSource = handoffSourceFence(accepted.source);
|
|
3030
3063
|
const localStatus = this.requireMeshNode(meshName).status();
|
|
3031
3064
|
const localOwner = this.options.currentOwner();
|
|
3032
|
-
if (
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3065
|
+
if (
|
|
3066
|
+
localOwner === undefined ||
|
|
3067
|
+
requestSource.ownerId !== localOwner.ownerId ||
|
|
3068
|
+
requestSource.leaseGeneration !== localOwner.leaseGeneration ||
|
|
3069
|
+
!routingIdsEqual(requestSource.nodeRid, localStatus.routingId) ||
|
|
3070
|
+
requestSource.nodeGeneration !== localStatus.lifecycleGeneration
|
|
3071
|
+
) {
|
|
3037
3072
|
throw new Error('Relocation reply relay request-source fence changed after capture.');
|
|
3038
3073
|
}
|
|
3039
3074
|
const ack: ServiceMaintenanceReplyRelayAck = {
|
|
@@ -3044,10 +3079,13 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3044
3079
|
requestSource,
|
|
3045
3080
|
status: accepted.status
|
|
3046
3081
|
};
|
|
3047
|
-
if (
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3082
|
+
if (
|
|
3083
|
+
(await this.sendInfrastructureControl(
|
|
3084
|
+
meshName,
|
|
3085
|
+
sourceNodeRid,
|
|
3086
|
+
encodeMaintenanceReplyRelayAck(ack)
|
|
3087
|
+
)) !== SubmitResult.Ok
|
|
3088
|
+
) {
|
|
3051
3089
|
throw new Error('Relocation reply relay ACK send was rejected.');
|
|
3052
3090
|
}
|
|
3053
3091
|
}
|
|
@@ -3078,8 +3116,8 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3078
3116
|
identityKey,
|
|
3079
3117
|
request,
|
|
3080
3118
|
expectedRequestSource,
|
|
3081
|
-
resolve: delivery => finish(() => resolve(delivery)),
|
|
3082
|
-
reject: error => finish(() => reject(error))
|
|
3119
|
+
resolve: (delivery) => finish(() => resolve(delivery)),
|
|
3120
|
+
reject: (error) => finish(() => reject(error))
|
|
3083
3121
|
};
|
|
3084
3122
|
const send = async (): Promise<void> => {
|
|
3085
3123
|
try {
|
|
@@ -3096,7 +3134,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3096
3134
|
return;
|
|
3097
3135
|
}
|
|
3098
3136
|
await this.sendInfrastructureControl(
|
|
3099
|
-
meshName,
|
|
3137
|
+
meshName,
|
|
3138
|
+
ackTargetNodeRid,
|
|
3139
|
+
encodeMaintenanceReplyRelay(request)
|
|
3140
|
+
);
|
|
3100
3141
|
if (this.pendingReplyRelays.get(key) === pending) {
|
|
3101
3142
|
pending.timer = setTimeout(() => void send(), 250);
|
|
3102
3143
|
}
|
|
@@ -3116,13 +3157,12 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3116
3157
|
signal?: AbortSignal
|
|
3117
3158
|
): Promise<void> {
|
|
3118
3159
|
const identityKey = replyRelayIdentityKey(ack);
|
|
3119
|
-
const exactKey =
|
|
3120
|
-
? undefined
|
|
3121
|
-
: replyRelayPendingKey(String(sourceNodeRid), ack);
|
|
3160
|
+
const exactKey =
|
|
3161
|
+
sourceNodeRid === null ? undefined : replyRelayPendingKey(String(sourceNodeRid), ack);
|
|
3122
3162
|
const pending = exactKey === undefined ? undefined : this.pendingReplyRelays.get(exactKey);
|
|
3123
3163
|
if (pending === undefined) {
|
|
3124
3164
|
const collision = [...this.pendingReplyRelays.values()].find(
|
|
3125
|
-
value => value.identityKey === identityKey
|
|
3165
|
+
(value) => value.identityKey === identityKey
|
|
3126
3166
|
);
|
|
3127
3167
|
collision?.reject(new Error('Relocation reply relay ACK target source collided.'));
|
|
3128
3168
|
return;
|
|
@@ -3134,11 +3174,13 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3134
3174
|
pending.expectedRequestSource,
|
|
3135
3175
|
signal
|
|
3136
3176
|
);
|
|
3137
|
-
if (
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3177
|
+
if (
|
|
3178
|
+
!sameWireId(ack.relocation, pending.request.relocation) ||
|
|
3179
|
+
!sameWireId(ack.operation, pending.request.operation) ||
|
|
3180
|
+
ack.replyRouteId !== pending.request.replyRouteId ||
|
|
3181
|
+
!sameCoordinator(ack.coordinator, pending.request.coordinator) ||
|
|
3182
|
+
!sameRequestSource(ack.requestSource, pending.expectedRequestSource)
|
|
3183
|
+
) {
|
|
3142
3184
|
throw new Error('Relocation reply relay ACK does not match its durable source fence.');
|
|
3143
3185
|
}
|
|
3144
3186
|
pending.resolve(ack.status);
|
|
@@ -3154,15 +3196,16 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3154
3196
|
signal?: AbortSignal
|
|
3155
3197
|
): Promise<void> {
|
|
3156
3198
|
const descriptor = (await this.options.liveDescriptors(meshName, signal)).find(
|
|
3157
|
-
value => String(value.rid) === authenticatedRid && value.state === 1
|
|
3199
|
+
(value) => String(value.rid) === authenticatedRid && value.state === 1
|
|
3158
3200
|
);
|
|
3159
|
-
if (
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3201
|
+
if (
|
|
3202
|
+
descriptor === undefined ||
|
|
3203
|
+
(expected !== undefined &&
|
|
3204
|
+
(descriptor.ownerId !== expected.ownerId ||
|
|
3205
|
+
descriptor.leaseGeneration !== expected.leaseGeneration ||
|
|
3206
|
+
String(descriptor.rid) !== expected.nodeRid ||
|
|
3207
|
+
descriptor.lifecycleGeneration !== expected.nodeGeneration))
|
|
3208
|
+
) {
|
|
3166
3209
|
throw new Error('Relocation reply relay peer is not the exact admitted source fence.');
|
|
3167
3210
|
}
|
|
3168
3211
|
}
|
|
@@ -3175,11 +3218,13 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3175
3218
|
const status = this.requireMeshNode(meshName).status();
|
|
3176
3219
|
const owner = this.options.currentOwner();
|
|
3177
3220
|
const target = stage.offer.prepare.target;
|
|
3178
|
-
if (
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3221
|
+
if (
|
|
3222
|
+
owner === undefined ||
|
|
3223
|
+
owner.ownerId !== target.ownerId ||
|
|
3224
|
+
owner.leaseGeneration !== target.ownerLeaseGeneration ||
|
|
3225
|
+
String(status.routingId) !== target.nodeRid ||
|
|
3226
|
+
status.lifecycleGeneration !== target.nodeGeneration
|
|
3227
|
+
) {
|
|
3183
3228
|
throw new Error('Relocation reply relay target coordinator fence is stale.');
|
|
3184
3229
|
}
|
|
3185
3230
|
return {
|
|
@@ -3215,12 +3260,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3215
3260
|
await this.sendReplyRelay(
|
|
3216
3261
|
meshName,
|
|
3217
3262
|
packet.source.nodeRid as RoutingId,
|
|
3218
|
-
maintenanceReplyRelay(
|
|
3219
|
-
stage,
|
|
3220
|
-
coordinator,
|
|
3221
|
-
BigInt(participantIndex + 1),
|
|
3222
|
-
completion
|
|
3223
|
-
),
|
|
3263
|
+
maintenanceReplyRelay(stage, coordinator, BigInt(participantIndex + 1), completion),
|
|
3224
3264
|
handoffSourceFence(packet.source),
|
|
3225
3265
|
signal
|
|
3226
3266
|
);
|
|
@@ -3233,10 +3273,12 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3233
3273
|
signal?: AbortSignal
|
|
3234
3274
|
): Promise<boolean> {
|
|
3235
3275
|
const lease = await this.requireLocationStore().readOwnerLease(source.ownerId, signal);
|
|
3236
|
-
return
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3276
|
+
return (
|
|
3277
|
+
lease.kind === 'missing' ||
|
|
3278
|
+
lease.token.ownerId !== source.ownerId ||
|
|
3279
|
+
lease.token.leaseGeneration !== source.leaseGeneration ||
|
|
3280
|
+
lease.leaseExpiresAt.getTime() <= lease.storeNow.getTime()
|
|
3281
|
+
);
|
|
3240
3282
|
}
|
|
3241
3283
|
|
|
3242
3284
|
private async publishSessionRoutes(
|
|
@@ -3258,9 +3300,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3258
3300
|
sideband: readonly Uint8Array[] = []
|
|
3259
3301
|
): Promise<ReturnType<typeof decodeServiceRelocationControlResponse>> {
|
|
3260
3302
|
const key = controlAckKey(request);
|
|
3261
|
-
const frames =
|
|
3262
|
-
|
|
3263
|
-
|
|
3303
|
+
const frames =
|
|
3304
|
+
sideband.length === 0
|
|
3305
|
+
? [encodeServiceRelocationControlRequest(request)]
|
|
3306
|
+
: [encodeServiceRelocationControlRequest(request), ...sideband];
|
|
3264
3307
|
let lastRequestError: unknown;
|
|
3265
3308
|
for (;;) {
|
|
3266
3309
|
signal?.throwIfAborted();
|
|
@@ -3295,8 +3338,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3295
3338
|
validateControlResponse(request, response);
|
|
3296
3339
|
return response;
|
|
3297
3340
|
} catch (error) {
|
|
3298
|
-
if (error instanceof ServiceWireProtocolError
|
|
3299
|
-
|| error instanceof ZLinkFrameworkException) {
|
|
3341
|
+
if (error instanceof ServiceWireProtocolError || error instanceof ZLinkFrameworkException) {
|
|
3300
3342
|
throw error;
|
|
3301
3343
|
}
|
|
3302
3344
|
lastRequestError = error;
|
|
@@ -3324,25 +3366,30 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3324
3366
|
signal?: AbortSignal
|
|
3325
3367
|
): Promise<ZLinkMeshNodeDescriptor> {
|
|
3326
3368
|
const descriptors = await this.options.liveDescriptors(meshName, signal);
|
|
3327
|
-
const local =
|
|
3328
|
-
|
|
3369
|
+
const local =
|
|
3370
|
+
this.options.localDescriptor?.(meshName) ??
|
|
3371
|
+
descriptors.find((descriptor) => String(descriptor.rid) === localRid);
|
|
3329
3372
|
const requiredVersion = targetApplicationVersion ?? local?.applicationVersion;
|
|
3330
3373
|
const peers = this.requireMeshNode(meshName).peers();
|
|
3331
3374
|
const candidates = descriptors
|
|
3332
|
-
.filter(
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|| descriptor.applicationVersion === requiredVersion)
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3375
|
+
.filter(
|
|
3376
|
+
(descriptor) =>
|
|
3377
|
+
String(descriptor.rid) !== localRid &&
|
|
3378
|
+
descriptor.state === ZLinkFrameworkRuntimeState.Serving &&
|
|
3379
|
+
descriptor.objectRole === ZLinkObjectRole.Server &&
|
|
3380
|
+
descriptor.placementWeight > 0 &&
|
|
3381
|
+
(requiredVersion === undefined || descriptor.applicationVersion === requiredVersion) &&
|
|
3382
|
+
(local?.maintenanceWave === undefined ||
|
|
3383
|
+
descriptor.maintenanceWave !== local.maintenanceWave) &&
|
|
3384
|
+
peers.some(
|
|
3385
|
+
(peer) =>
|
|
3386
|
+
peer.routingId !== null &&
|
|
3387
|
+
String(peer.routingId) === String(descriptor.rid) &&
|
|
3388
|
+
peer.lifecycleGeneration === descriptor.lifecycleGeneration &&
|
|
3389
|
+
peer.state === 3
|
|
3390
|
+
) &&
|
|
3391
|
+
relocationTargetSupports(descriptor, requirements)
|
|
3392
|
+
)
|
|
3346
3393
|
.sort((left, right) => String(left.rid).localeCompare(String(right.rid)));
|
|
3347
3394
|
const target = selectWeightedRelocationTarget(candidates);
|
|
3348
3395
|
if (target === undefined) {
|
|
@@ -3369,12 +3416,16 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3369
3416
|
{ value: participant.key } as ZLinkAuthorityKey,
|
|
3370
3417
|
signal
|
|
3371
3418
|
);
|
|
3372
|
-
if (
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3419
|
+
if (
|
|
3420
|
+
current.objectGeneration !== participant.objectGeneration ||
|
|
3421
|
+
current.authorityOwnerGeneration !== participant.authorityOwnerGeneration ||
|
|
3422
|
+
current.allocation.objectKind !== participant.objectKind ||
|
|
3423
|
+
current.allocation.stableType !== participant.stableType ||
|
|
3424
|
+
current.allocation.state !== 'active'
|
|
3425
|
+
) {
|
|
3426
|
+
throw new Error(
|
|
3427
|
+
`Relocation participant '${participant.key}' authority changed before reservation.`
|
|
3428
|
+
);
|
|
3378
3429
|
}
|
|
3379
3430
|
authorities.set(participant.key, current);
|
|
3380
3431
|
}
|
|
@@ -3394,7 +3445,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3394
3445
|
// target reservation must keep connected through the CAS (spec 28 —
|
|
3395
3446
|
// clearing/replacing it here would strand the journal's own
|
|
3396
3447
|
// prepared->committed transition with nothing left for it to find).
|
|
3397
|
-
const retainedDeferredJoinRoots = new Map<
|
|
3448
|
+
const retainedDeferredJoinRoots = new Map<
|
|
3449
|
+
string,
|
|
3450
|
+
{ readonly reference: string; readonly checksumCrc32c: number }
|
|
3451
|
+
>();
|
|
3398
3452
|
for (const participant of envelope.participants) {
|
|
3399
3453
|
const staged = targetPort.deferredJoinRoot(participant.key);
|
|
3400
3454
|
if (staged !== undefined) {
|
|
@@ -3405,9 +3459,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3405
3459
|
continue;
|
|
3406
3460
|
}
|
|
3407
3461
|
const current = this.codec.read(authorities.get(participant.key)!.payload);
|
|
3408
|
-
if (
|
|
3409
|
-
|
|
3410
|
-
|
|
3462
|
+
if (
|
|
3463
|
+
participant.objectKind === 'actor' &&
|
|
3464
|
+
current?.canonical === true &&
|
|
3465
|
+
(await this.options.actorTransfer.isDeferredJoinAcceptedRootPublication(
|
|
3411
3466
|
current.reference,
|
|
3412
3467
|
current.checksumCrc32c,
|
|
3413
3468
|
{
|
|
@@ -3418,63 +3473,70 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3418
3473
|
aggregateGeneration: current.aggregateGeneration
|
|
3419
3474
|
},
|
|
3420
3475
|
signal
|
|
3421
|
-
))
|
|
3476
|
+
))
|
|
3477
|
+
) {
|
|
3422
3478
|
retainedDeferredJoinRoots.set(participant.key, {
|
|
3423
3479
|
reference: current.reference,
|
|
3424
3480
|
checksumCrc32c: current.checksumCrc32c
|
|
3425
3481
|
});
|
|
3426
3482
|
}
|
|
3427
3483
|
}
|
|
3428
|
-
const participants = envelope.participants.map(participant => {
|
|
3484
|
+
const participants = envelope.participants.map((participant) => {
|
|
3429
3485
|
const expected = authorities.get(participant.key)!;
|
|
3430
3486
|
const membership = actorMembershipTarget(envelope, participant.key);
|
|
3431
|
-
const entrySpotId =
|
|
3432
|
-
&& membership === undefined
|
|
3433
|
-
|
|
3434
|
-
|
|
3487
|
+
const entrySpotId =
|
|
3488
|
+
expected.allocation.objectKind === 'actor' && membership === undefined
|
|
3489
|
+
? this.options.spotManager()?.entrySpotIdForMesh(meshName)
|
|
3490
|
+
: undefined;
|
|
3435
3491
|
if (
|
|
3436
|
-
expected.allocation.objectKind === 'actor'
|
|
3437
|
-
|
|
3438
|
-
|
|
3492
|
+
expected.allocation.objectKind === 'actor' &&
|
|
3493
|
+
membership === undefined &&
|
|
3494
|
+
entrySpotId === undefined
|
|
3439
3495
|
) {
|
|
3440
3496
|
throw new Error(`Relocation target RouteMesh '${meshName}' has no Entry Spot.`);
|
|
3441
3497
|
}
|
|
3442
3498
|
const retainedDeferredJoinRoot = retainedDeferredJoinRoots.get(participant.key);
|
|
3443
|
-
const participantPublication =
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3499
|
+
const participantPublication =
|
|
3500
|
+
retainedDeferredJoinRoot === undefined
|
|
3501
|
+
? publication
|
|
3502
|
+
: {
|
|
3503
|
+
...publication,
|
|
3504
|
+
reference: retainedDeferredJoinRoot.reference,
|
|
3505
|
+
checksumCrc32c: retainedDeferredJoinRoot.checksumCrc32c
|
|
3506
|
+
};
|
|
3450
3507
|
return {
|
|
3451
3508
|
key: { value: participant.key } as ZLinkAuthorityKey,
|
|
3452
3509
|
expected,
|
|
3453
3510
|
ownerTransition: 'newOwner' as const,
|
|
3454
|
-
authorityPayload: this.authorityPayloadForPublication(
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3511
|
+
authorityPayload: this.authorityPayloadForPublication(
|
|
3512
|
+
expected.payload,
|
|
3513
|
+
participantPublication,
|
|
3514
|
+
{
|
|
3515
|
+
owner: {
|
|
3516
|
+
ownerId: prepare.target.ownerId,
|
|
3517
|
+
leaseGeneration: prepare.target.ownerLeaseGeneration
|
|
3518
|
+
},
|
|
3519
|
+
meshName,
|
|
3460
3520
|
nodeRid: prepare.target.nodeRid,
|
|
3461
3521
|
nodeGeneration: prepare.target.nodeGeneration,
|
|
3462
3522
|
objectGeneration: expected.objectGeneration,
|
|
3463
3523
|
targetAttemptGeneration: prepare.targetAttemptGeneration,
|
|
3464
3524
|
coordinatorExpectedStoreVersion: expected.storeVersion.value,
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
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
|
+
})
|
|
3537
|
+
},
|
|
3538
|
+
retainedDeferredJoinRoot !== undefined
|
|
3539
|
+
),
|
|
3478
3540
|
membershipMutation: encodeMembershipMutation(envelope.memberships, participant.key)
|
|
3479
3541
|
};
|
|
3480
3542
|
});
|
|
@@ -3483,16 +3545,20 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3483
3545
|
participants,
|
|
3484
3546
|
targetDescriptor: { meshName, rid: prepare.target.nodeRid as RoutingId },
|
|
3485
3547
|
targetDescriptorLifecycleGeneration: prepare.target.nodeGeneration,
|
|
3486
|
-
capacity: participants.reduce(
|
|
3487
|
-
addCapacity(sum, participant.expected.allocation.capacity),
|
|
3548
|
+
capacity: participants.reduce(
|
|
3549
|
+
(sum, participant) => addCapacity(sum, participant.expected.allocation.capacity),
|
|
3550
|
+
{ actors: 0, spots: 0 }
|
|
3551
|
+
),
|
|
3488
3552
|
targetOwner: {
|
|
3489
3553
|
ownerId: prepare.target.ownerId,
|
|
3490
3554
|
leaseGeneration: prepare.target.ownerLeaseGeneration
|
|
3491
3555
|
}
|
|
3492
3556
|
};
|
|
3493
3557
|
return {
|
|
3494
|
-
prepared: await new ServiceRelocationAggregateCommitter(this.requireLocationStore())
|
|
3495
|
-
|
|
3558
|
+
prepared: await new ServiceRelocationAggregateCommitter(this.requireLocationStore()).prepare(
|
|
3559
|
+
plan,
|
|
3560
|
+
signal
|
|
3561
|
+
)
|
|
3496
3562
|
};
|
|
3497
3563
|
}
|
|
3498
3564
|
|
|
@@ -3515,21 +3581,18 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3515
3581
|
): Uint8Array {
|
|
3516
3582
|
const existing = this.codec.read(payload);
|
|
3517
3583
|
const canonicalIdentity = serviceRelocationAuthoritySlotIdentity(payload);
|
|
3518
|
-
const replacesCanonicalJournal = replacesDeferredJoinRoot
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
const published =
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
&& existing.aggregateId !== publication.aggregateId
|
|
3529
|
-
? this.codec.publish(
|
|
3530
|
-
this.codec.clear(payload, existing.reference),
|
|
3531
|
-
publication
|
|
3532
|
-
)
|
|
3584
|
+
const replacesCanonicalJournal = replacesDeferredJoinRoot && existing?.canonical === true;
|
|
3585
|
+
if (
|
|
3586
|
+
canonicalIdentity !== undefined &&
|
|
3587
|
+
(existing === undefined ||
|
|
3588
|
+
(existing.canonical === true &&
|
|
3589
|
+
(existing.aggregateId === publication.aggregateId || replacesCanonicalJournal)))
|
|
3590
|
+
) {
|
|
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)
|
|
3533
3596
|
: payload;
|
|
3534
3597
|
if (target === undefined) return published;
|
|
3535
3598
|
return projectServiceRelocationAuthorityTargetReady(
|
|
@@ -3568,25 +3631,23 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3568
3631
|
}
|
|
3569
3632
|
);
|
|
3570
3633
|
}
|
|
3571
|
-
return this.codec.publish(
|
|
3572
|
-
rewriteAuthorityApplicationRoute(payload, target),
|
|
3573
|
-
publication
|
|
3574
|
-
);
|
|
3634
|
+
return this.codec.publish(rewriteAuthorityApplicationRoute(payload, target), publication);
|
|
3575
3635
|
}
|
|
3576
|
-
if (
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3636
|
+
if (
|
|
3637
|
+
existing.reference !== publication.reference ||
|
|
3638
|
+
existing.checksumCrc32c !== publication.checksumCrc32c ||
|
|
3639
|
+
existing.aggregateId !== publication.aggregateId ||
|
|
3640
|
+
existing.aggregateGeneration !== publication.aggregateGeneration ||
|
|
3641
|
+
existing.inventoryDigest !== publication.inventoryDigest ||
|
|
3642
|
+
existing.targetOwnerId !== publication.targetOwnerId ||
|
|
3643
|
+
existing.targetOwnerLeaseGeneration !== publication.targetOwnerLeaseGeneration
|
|
3644
|
+
) {
|
|
3584
3645
|
throw new Error(
|
|
3585
|
-
'Relocation authority contains another published root '
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3646
|
+
'Relocation authority contains another published root ' +
|
|
3647
|
+
`(current=${existing.aggregateId}/${existing.aggregateGeneration}` +
|
|
3648
|
+
`/${existing.reference}/canonical=${existing.canonical === true}, ` +
|
|
3649
|
+
`requested=${publication.aggregateId}/${publication.aggregateGeneration}` +
|
|
3650
|
+
`/${publication.reference}).`
|
|
3590
3651
|
);
|
|
3591
3652
|
}
|
|
3592
3653
|
return payload;
|
|
@@ -3596,8 +3657,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3596
3657
|
reservation: TargetRelocationReservation,
|
|
3597
3658
|
signal?: AbortSignal
|
|
3598
3659
|
): Promise<void> {
|
|
3599
|
-
await new ServiceRelocationAggregateCommitter(this.requireLocationStore())
|
|
3600
|
-
|
|
3660
|
+
await new ServiceRelocationAggregateCommitter(this.requireLocationStore()).abort(
|
|
3661
|
+
reservation.prepared,
|
|
3662
|
+
signal
|
|
3663
|
+
);
|
|
3601
3664
|
}
|
|
3602
3665
|
|
|
3603
3666
|
private async abortTargetStage(stage: LocalStage, signal?: AbortSignal): Promise<void> {
|
|
@@ -3657,9 +3720,12 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3657
3720
|
return primary;
|
|
3658
3721
|
}
|
|
3659
3722
|
if (observed.kind === 'stale') {
|
|
3660
|
-
throw new Error(
|
|
3661
|
-
|
|
3662
|
-
|
|
3723
|
+
throw new Error(
|
|
3724
|
+
'location_update_failed: relocation aggregate source authority became stale.',
|
|
3725
|
+
{
|
|
3726
|
+
cause: firstError
|
|
3727
|
+
}
|
|
3728
|
+
);
|
|
3663
3729
|
}
|
|
3664
3730
|
await waitForLocationRetry(stage.offer.restoreDeadlineAtMs, signal, firstError);
|
|
3665
3731
|
}
|
|
@@ -3669,7 +3735,10 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3669
3735
|
prepared: ServicePreparedRelocationAggregate,
|
|
3670
3736
|
signal?: AbortSignal
|
|
3671
3737
|
): Promise<
|
|
3672
|
-
| {
|
|
3738
|
+
| {
|
|
3739
|
+
readonly kind: 'committed';
|
|
3740
|
+
readonly authorities: ReadonlyMap<string, ZLinkAuthoritySnapshot>;
|
|
3741
|
+
}
|
|
3673
3742
|
| { readonly kind: 'source' | 'unknown' | 'stale' }
|
|
3674
3743
|
> {
|
|
3675
3744
|
const authorities = new Map<string, ZLinkAuthoritySnapshot>();
|
|
@@ -3679,17 +3748,19 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3679
3748
|
for (const participant of prepared.plan.participants) {
|
|
3680
3749
|
const current = await this.requireLocationStore().readAuthority(participant.key, signal);
|
|
3681
3750
|
if (current.kind !== 'snapshot') return { kind: 'stale' };
|
|
3682
|
-
if (
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3751
|
+
if (
|
|
3752
|
+
isExactCommittedTarget(
|
|
3753
|
+
current,
|
|
3754
|
+
participant.expected,
|
|
3755
|
+
{
|
|
3756
|
+
nodeRid: String(prepared.plan.targetDescriptor.rid),
|
|
3757
|
+
nodeGeneration: prepared.plan.targetDescriptorLifecycleGeneration,
|
|
3758
|
+
ownerId: prepared.plan.targetOwner.ownerId,
|
|
3759
|
+
ownerLeaseGeneration: prepared.plan.targetOwner.leaseGeneration
|
|
3760
|
+
},
|
|
3761
|
+
participant.authorityPayload
|
|
3762
|
+
)
|
|
3763
|
+
) {
|
|
3693
3764
|
committed += 1;
|
|
3694
3765
|
authorities.set(participant.key.value, current);
|
|
3695
3766
|
} else if (isExactSourceAuthority(current, participant.expected)) {
|
|
@@ -3704,9 +3775,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3704
3775
|
if (committed === prepared.plan.participants.length) {
|
|
3705
3776
|
return { kind: 'committed', authorities };
|
|
3706
3777
|
}
|
|
3707
|
-
return source === prepared.plan.participants.length
|
|
3708
|
-
? { kind: 'source' }
|
|
3709
|
-
: { kind: 'stale' };
|
|
3778
|
+
return source === prepared.plan.participants.length ? { kind: 'source' } : { kind: 'stale' };
|
|
3710
3779
|
}
|
|
3711
3780
|
|
|
3712
3781
|
private async materializeTargetEnvelope(
|
|
@@ -3717,7 +3786,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3717
3786
|
const target = new LocalTargetPort(this.options, meshName, envelope);
|
|
3718
3787
|
const owner = new ServiceRelocationObjectRestoreOwner(
|
|
3719
3788
|
target,
|
|
3720
|
-
value => ({ value } as ZLinkAuthorityKey
|
|
3789
|
+
(value) => ({ value }) as ZLinkAuthorityKey
|
|
3721
3790
|
);
|
|
3722
3791
|
const staging = await owner.prepare(envelope, signal);
|
|
3723
3792
|
if (staging.id !== `${envelope.aggregateId}:${envelope.aggregateGeneration}`) {
|
|
@@ -3732,21 +3801,23 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3732
3801
|
envelope: ServiceRelocationEnvelope,
|
|
3733
3802
|
signal?: AbortSignal
|
|
3734
3803
|
): Promise<ServiceRelocationEnvelope> {
|
|
3735
|
-
if (!envelope.participants.some(value => value.rootSpotId !== undefined)) {
|
|
3804
|
+
if (!envelope.participants.some((value) => value.rootSpotId !== undefined)) {
|
|
3736
3805
|
return envelope;
|
|
3737
3806
|
}
|
|
3738
3807
|
const store = this.requireLocationStore();
|
|
3739
3808
|
const manager = this.options.spotManager();
|
|
3740
|
-
const primaryKey =
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3809
|
+
const primaryKey =
|
|
3810
|
+
request.object.kind === 'actor'
|
|
3811
|
+
? encodeAuthorityKey('actor', request.object.actorId)
|
|
3812
|
+
: encodeAuthorityKey(
|
|
3813
|
+
request.object.kind === 'userSpot' ? 'user_spot' : 'instance_spot',
|
|
3814
|
+
request.object.spotId
|
|
3815
|
+
);
|
|
3746
3816
|
const primary = await requireAuthority(store, primaryKey, signal);
|
|
3747
|
-
const entries: Array<{
|
|
3748
|
-
|
|
3749
|
-
|
|
3817
|
+
const entries: Array<{
|
|
3818
|
+
readonly key: ZLinkAuthorityKey;
|
|
3819
|
+
readonly snapshot: ZLinkAuthoritySnapshot;
|
|
3820
|
+
}> = [{ key: primaryKey, snapshot: primary }];
|
|
3750
3821
|
if (request.object.kind === 'userSpot' || request.object.kind === 'instanceSpot') {
|
|
3751
3822
|
let cursor: ZLinkAuthorityScanCursor | undefined;
|
|
3752
3823
|
for (;;) {
|
|
@@ -3755,8 +3826,11 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3755
3826
|
throw new Error('Location Store authority scan expired during relocation.');
|
|
3756
3827
|
}
|
|
3757
3828
|
for (const candidate of page.items) {
|
|
3758
|
-
if (
|
|
3759
|
-
|
|
3829
|
+
if (
|
|
3830
|
+
candidate.key.value === primaryKey.value ||
|
|
3831
|
+
decodeAuthorityKey(candidate.key).kind !== 'actor'
|
|
3832
|
+
)
|
|
3833
|
+
continue;
|
|
3760
3834
|
const actor = decodeRelocatingActorAuthorityIdentity(
|
|
3761
3835
|
candidate.snapshot.payload,
|
|
3762
3836
|
candidate.snapshot.objectGeneration
|
|
@@ -3769,34 +3843,35 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3769
3843
|
cursor = page.nextCursor;
|
|
3770
3844
|
}
|
|
3771
3845
|
}
|
|
3772
|
-
entries.sort((left, right) =>
|
|
3773
|
-
Buffer.from(left.key.value, 'utf8'),
|
|
3774
|
-
|
|
3775
|
-
))
|
|
3776
|
-
if (new Set(entries.map(entry => entry.key.value)).size !== entries.length) {
|
|
3846
|
+
entries.sort((left, right) =>
|
|
3847
|
+
Buffer.compare(Buffer.from(left.key.value, 'utf8'), Buffer.from(right.key.value, 'utf8'))
|
|
3848
|
+
);
|
|
3849
|
+
if (new Set(entries.map((entry) => entry.key.value)).size !== entries.length) {
|
|
3777
3850
|
throw new Error('Location Store relocation inventory has duplicate authority keys.');
|
|
3778
3851
|
}
|
|
3779
|
-
const root = envelope.participants.find(value => value.rootSpotId !== undefined);
|
|
3780
|
-
const expectedRootId =
|
|
3781
|
-
? request.object.actorId
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3852
|
+
const root = envelope.participants.find((value) => value.rootSpotId !== undefined);
|
|
3853
|
+
const expectedRootId =
|
|
3854
|
+
request.object.kind === 'actor' ? request.object.actorId : request.object.spotId;
|
|
3855
|
+
if (
|
|
3856
|
+
root === undefined ||
|
|
3857
|
+
root.rootSpotId !== expectedRootId ||
|
|
3858
|
+
root.rootObjectKind !== primary.allocation.objectKind ||
|
|
3859
|
+
root.rootSpotGeneration !== primary.objectGeneration ||
|
|
3860
|
+
(root.rootObjectKind !== 'instance_spot' &&
|
|
3861
|
+
root.rootOwnerGeneration !== primary.authorityOwnerGeneration)
|
|
3862
|
+
) {
|
|
3789
3863
|
throw new Error('Relocation root object projection does not match Location Store.');
|
|
3790
3864
|
}
|
|
3791
|
-
const transferred = new Map(
|
|
3865
|
+
const transferred = new Map(
|
|
3866
|
+
envelope.participants.map((participant) => [participant.participantId, participant])
|
|
3867
|
+
);
|
|
3792
3868
|
if (transferred.size !== envelope.participants.length || transferred.size !== entries.length) {
|
|
3793
3869
|
throw new Error('Relocation payload participant inventory does not match Location Store.');
|
|
3794
3870
|
}
|
|
3795
3871
|
const participants = entries.map(({ key, snapshot }, index) => {
|
|
3796
3872
|
const participantId = BigInt(index + 1);
|
|
3797
3873
|
const transferredParticipant = transferred.get(participantId);
|
|
3798
|
-
if (transferredParticipant === undefined
|
|
3799
|
-
) {
|
|
3874
|
+
if (transferredParticipant === undefined) {
|
|
3800
3875
|
throw new Error('Relocation payload participant identity does not match Location Store.');
|
|
3801
3876
|
}
|
|
3802
3877
|
return {
|
|
@@ -3813,16 +3888,17 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3813
3888
|
// Location Store still names the source Spot until target cutover. Rebuild
|
|
3814
3889
|
// the Actor lane from the already accepted ActorJoin admission, preserving
|
|
3815
3890
|
// the target queue boundary for decoded saved-work.
|
|
3816
|
-
const memberships =
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3891
|
+
const memberships =
|
|
3892
|
+
request.object.kind === 'actor'
|
|
3893
|
+
? actorJoinTargetMembership(manager, envelope.aggregateId, participants)
|
|
3894
|
+
: entries
|
|
3895
|
+
.filter((entry) => entry.snapshot.allocation.objectKind === 'actor')
|
|
3896
|
+
.map((entry) => ({
|
|
3897
|
+
actorKey: entry.key.value,
|
|
3898
|
+
spotKey: primaryKey.value,
|
|
3899
|
+
spotObjectGeneration: primary.objectGeneration,
|
|
3900
|
+
membershipEpoch: 1n
|
|
3901
|
+
}));
|
|
3826
3902
|
return {
|
|
3827
3903
|
...envelope,
|
|
3828
3904
|
participants,
|
|
@@ -3835,12 +3911,16 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3835
3911
|
activation: ZLinkSpotActivation
|
|
3836
3912
|
): 'user_spot' | 'instance_spot' | undefined {
|
|
3837
3913
|
const node = this.options.registration.spotNodes.get(meshName);
|
|
3838
|
-
const registrations =
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3914
|
+
const registrations =
|
|
3915
|
+
activation.domain.kind === 'user'
|
|
3916
|
+
? node?.spotFactoryRegistrations
|
|
3917
|
+
: node?.instanceSpotFactoryRegistrations;
|
|
3918
|
+
return Object.values(registrations ?? {}).some(
|
|
3919
|
+
(value) => value.implementation === activation.spotType
|
|
3920
|
+
)
|
|
3921
|
+
? activation.domain.kind === 'user'
|
|
3922
|
+
? 'user_spot'
|
|
3923
|
+
: 'instance_spot'
|
|
3844
3924
|
: undefined;
|
|
3845
3925
|
}
|
|
3846
3926
|
|
|
@@ -3850,17 +3930,20 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3850
3930
|
stableType: string
|
|
3851
3931
|
) {
|
|
3852
3932
|
const node = this.options.registration.spotNodes.get(meshName);
|
|
3853
|
-
const value =
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3933
|
+
const value =
|
|
3934
|
+
kind === 'user_spot'
|
|
3935
|
+
? node?.spotFactoryRegistrations?.[stableType]
|
|
3936
|
+
: node?.instanceSpotFactoryRegistrations?.[stableType];
|
|
3937
|
+
if (value === undefined)
|
|
3938
|
+
throw new Error(`Relocation Spot type '${stableType}' is not registered.`);
|
|
3857
3939
|
return value;
|
|
3858
3940
|
}
|
|
3859
3941
|
|
|
3860
3942
|
private actorRegistration(meshName: string, stableType: string) {
|
|
3861
|
-
const value =
|
|
3862
|
-
?.actorFactoryRegistrations?.[stableType];
|
|
3863
|
-
if (value === undefined)
|
|
3943
|
+
const value =
|
|
3944
|
+
this.options.registration.spotNodes.get(meshName)?.actorFactoryRegistrations?.[stableType];
|
|
3945
|
+
if (value === undefined)
|
|
3946
|
+
throw new Error(`Relocation Actor type '${stableType}' is not registered.`);
|
|
3864
3947
|
return value;
|
|
3865
3948
|
}
|
|
3866
3949
|
|
|
@@ -3870,7 +3953,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3870
3953
|
signal?: AbortSignal
|
|
3871
3954
|
): Promise<Uint8Array> {
|
|
3872
3955
|
if (policy.kind === 'disabled') {
|
|
3873
|
-
throw new Error(
|
|
3956
|
+
throw new Error(
|
|
3957
|
+
`Relocation is disabled for '${(value as { constructor?: { name?: string } }).constructor?.name ?? 'object'}'.`
|
|
3958
|
+
);
|
|
3874
3959
|
}
|
|
3875
3960
|
if (policy.kind === 'recreate') return Buffer.alloc(0);
|
|
3876
3961
|
const adapter = await this.createRelocationAdapter(policy.adapterType!);
|
|
@@ -3890,8 +3975,9 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3890
3975
|
private async createRelocationAdapter(
|
|
3891
3976
|
adapterType: Type
|
|
3892
3977
|
): Promise<ZLinkRelocationStateAdapterLike<never>> {
|
|
3893
|
-
return await createProviderInstance(adapterType, this.options.providerResolver) as
|
|
3894
|
-
ZLinkActorRelocationAdapter<ZLinkActor>
|
|
3978
|
+
return (await createProviderInstance(adapterType, this.options.providerResolver)) as
|
|
3979
|
+
| ZLinkActorRelocationAdapter<ZLinkActor>
|
|
3980
|
+
| ZLinkSpotRelocationAdapter<ZLinkSpot | ZLinkInstanceSpot>;
|
|
3895
3981
|
}
|
|
3896
3982
|
|
|
3897
3983
|
private recordRelocationInterruption(
|
|
@@ -3906,23 +3992,17 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3906
3992
|
unit_kind: unitKind,
|
|
3907
3993
|
...(executionMode === undefined ? {} : { execution_mode: executionMode })
|
|
3908
3994
|
};
|
|
3909
|
-
this.options.metrics?.duration(
|
|
3910
|
-
'zlink.relocation.interruption',
|
|
3911
|
-
seconds,
|
|
3912
|
-
attributes
|
|
3913
|
-
);
|
|
3995
|
+
this.options.metrics?.duration('zlink.relocation.interruption', seconds, attributes);
|
|
3914
3996
|
this.options.metrics?.count('zlink.relocation.completed', 1, {
|
|
3915
3997
|
mesh_name: meshName,
|
|
3916
3998
|
object_kind: unitKind,
|
|
3917
|
-
policy: unitKind === 'user_spot' && executionMode === 'per_actor'
|
|
3918
|
-
? 'recreate'
|
|
3919
|
-
: 'configured',
|
|
3999
|
+
policy: unitKind === 'user_spot' && executionMode === 'per_actor' ? 'recreate' : 'configured',
|
|
3920
4000
|
outcome
|
|
3921
4001
|
});
|
|
3922
4002
|
if (seconds <= 1) return;
|
|
3923
4003
|
console.warn(
|
|
3924
4004
|
`[zlink.runtime.relocation.changed] ${unitKind} admission interruption ` +
|
|
3925
|
-
|
|
4005
|
+
`exceeded 1 second (${seconds.toFixed(3)}s).`
|
|
3926
4006
|
);
|
|
3927
4007
|
void this.options.runtimeEventPublisher?.publish({
|
|
3928
4008
|
sourceName: 'zlink.runtime.relocation',
|
|
@@ -3981,25 +4061,25 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
3981
4061
|
if (commit === undefined) {
|
|
3982
4062
|
throw new Error('Spot Message Follow backend support is incomplete.');
|
|
3983
4063
|
}
|
|
3984
|
-
const committed = await requireAuthority(
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
4064
|
+
const committed = await requireAuthority(this.requireLocationStore(), authorityKey);
|
|
4065
|
+
const accepted = await commit.call(
|
|
4066
|
+
node,
|
|
4067
|
+
seal,
|
|
4068
|
+
{
|
|
4069
|
+
spot: {
|
|
4070
|
+
spotId: String(activation.spotId),
|
|
4071
|
+
generation: committed.objectGeneration
|
|
4072
|
+
},
|
|
4073
|
+
targetNodeRid: String(target.rid),
|
|
4074
|
+
targetNodeGeneration: target.lifecycleGeneration,
|
|
4075
|
+
authorityOwnerGeneration: committed.authorityOwnerGeneration,
|
|
4076
|
+
ownerLeaseGeneration: committed.ownerLeaseGeneration,
|
|
4077
|
+
storeVersion: committed.storeVersion.value
|
|
3992
4078
|
},
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
authorityOwnerGeneration: committed.authorityOwnerGeneration,
|
|
3996
|
-
ownerLeaseGeneration: committed.ownerLeaseGeneration,
|
|
3997
|
-
storeVersion: committed.storeVersion.value
|
|
3998
|
-
}, this.relocationLimits().messageFollowDurationMs);
|
|
4079
|
+
this.relocationLimits().messageFollowDurationMs
|
|
4080
|
+
);
|
|
3999
4081
|
if (!accepted) {
|
|
4000
|
-
throw new Error(
|
|
4001
|
-
`Spot '${String(activation.spotId)}' Message Follow route became stale.`
|
|
4002
|
-
);
|
|
4082
|
+
throw new Error(`Spot '${String(activation.spotId)}' Message Follow route became stale.`);
|
|
4003
4083
|
}
|
|
4004
4084
|
}
|
|
4005
4085
|
|
|
@@ -4023,11 +4103,13 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
4023
4103
|
|
|
4024
4104
|
private relocationLimits() {
|
|
4025
4105
|
const configured = (
|
|
4026
|
-
this.options.registration as unknown as
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4106
|
+
this.options.registration as unknown as
|
|
4107
|
+
| {
|
|
4108
|
+
readonly locations?: {
|
|
4109
|
+
readonly options?: Partial<typeof zlinkRuntimeDefaultLocationOptions>;
|
|
4110
|
+
};
|
|
4111
|
+
}
|
|
4112
|
+
| undefined
|
|
4031
4113
|
)?.locations?.options;
|
|
4032
4114
|
return {
|
|
4033
4115
|
...zlinkRuntimeDefaultLocationOptions,
|
|
@@ -4057,7 +4139,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
4057
4139
|
if (Array.isArray(record)) {
|
|
4058
4140
|
return await node.sendToNode(
|
|
4059
4141
|
targetNodeRid,
|
|
4060
|
-
record.map(frame => Buffer.from(frame))
|
|
4142
|
+
record.map((frame) => Buffer.from(frame))
|
|
4061
4143
|
);
|
|
4062
4144
|
}
|
|
4063
4145
|
return await node.sendToNode(targetNodeRid, Buffer.from(record as Uint8Array));
|
|
@@ -4093,10 +4175,12 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
|
|
|
4093
4175
|
function canonicalActorJoinRecovery(
|
|
4094
4176
|
participants: readonly ServiceRelocationParticipant[]
|
|
4095
4177
|
): CanonicalActorJoinRecovery | undefined {
|
|
4096
|
-
const matches = participants.flatMap(participant =>
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4178
|
+
const matches = participants.flatMap((participant) =>
|
|
4179
|
+
participant.queuedMessages.flatMap((message) => {
|
|
4180
|
+
const recovery = decodeCanonicalActorJoinRecoverySavedWork(message.payload);
|
|
4181
|
+
return recovery === undefined ? [] : [recovery];
|
|
4182
|
+
})
|
|
4183
|
+
);
|
|
4100
4184
|
if (matches.length > 1) {
|
|
4101
4185
|
throw new Error('Actor relocation contains duplicate canonical Join recovery records.');
|
|
4102
4186
|
}
|
|
@@ -4104,8 +4188,9 @@ function canonicalActorJoinRecovery(
|
|
|
4104
4188
|
}
|
|
4105
4189
|
|
|
4106
4190
|
function actorJoinAdmissionIdentity(envelope: ServiceRelocationEnvelope): string {
|
|
4107
|
-
return
|
|
4108
|
-
?? envelope.aggregateId
|
|
4191
|
+
return (
|
|
4192
|
+
canonicalActorJoinRecovery(envelope.participants)?.request.handoffId ?? envelope.aggregateId
|
|
4193
|
+
);
|
|
4109
4194
|
}
|
|
4110
4195
|
|
|
4111
4196
|
function actorJoinTargetMembership(
|
|
@@ -4115,27 +4200,35 @@ function actorJoinTargetMembership(
|
|
|
4115
4200
|
): readonly ServiceRelocationMembership[] {
|
|
4116
4201
|
const recovery = canonicalActorJoinRecovery(participants);
|
|
4117
4202
|
const admissionId = recovery?.request.handoffId ?? relocationId;
|
|
4118
|
-
const admission = (
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4203
|
+
const admission = (
|
|
4204
|
+
manager as unknown as
|
|
4205
|
+
| {
|
|
4206
|
+
readonly formalRemoteActorAdmissions?: {
|
|
4207
|
+
get(id: string):
|
|
4208
|
+
| {
|
|
4209
|
+
readonly admission: {
|
|
4210
|
+
readonly spotId: RoutingId;
|
|
4211
|
+
readonly targetSpotGeneration: bigint;
|
|
4212
|
+
readonly expectedMembershipEpoch: bigint;
|
|
4213
|
+
};
|
|
4214
|
+
}
|
|
4215
|
+
| undefined;
|
|
4216
|
+
};
|
|
4217
|
+
}
|
|
4218
|
+
| undefined
|
|
4219
|
+
)?.formalRemoteActorAdmissions?.get(admissionId);
|
|
4220
|
+
const actor = participants.find((value) => value.objectKind === 'actor');
|
|
4130
4221
|
if (admission === undefined || actor === undefined) {
|
|
4131
4222
|
throw new Error(`Actor relocation '${relocationId}' has no accepted target membership.`);
|
|
4132
4223
|
}
|
|
4133
|
-
return [
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4224
|
+
return [
|
|
4225
|
+
{
|
|
4226
|
+
actorKey: actor.key,
|
|
4227
|
+
spotKey: encodeAuthorityKey('user_spot', String(admission.admission.spotId)).value,
|
|
4228
|
+
spotObjectGeneration: admission.admission.targetSpotGeneration,
|
|
4229
|
+
membershipEpoch: admission.admission.expectedMembershipEpoch
|
|
4230
|
+
}
|
|
4231
|
+
];
|
|
4139
4232
|
}
|
|
4140
4233
|
|
|
4141
4234
|
function relocationDebug(marker: string, detail: Record<string, unknown>): void {
|
|
@@ -4158,9 +4251,14 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4158
4251
|
): Promise<LocalHidden> {
|
|
4159
4252
|
const identity = decodeAuthorityKey({ value: participant.key } as ZLinkAuthorityKey).globalId;
|
|
4160
4253
|
if (participant.objectKind === 'actor') {
|
|
4161
|
-
const membership = this.envelope.memberships.find(
|
|
4162
|
-
|
|
4163
|
-
|
|
4254
|
+
const membership = this.envelope.memberships.find(
|
|
4255
|
+
(value) => value.actorKey === participant.key
|
|
4256
|
+
);
|
|
4257
|
+
if (membership === undefined)
|
|
4258
|
+
throw new Error(`Actor '${identity}' relocation membership is missing.`);
|
|
4259
|
+
const spotIdentity = decodeAuthorityKey({
|
|
4260
|
+
value: membership.spotKey
|
|
4261
|
+
} as ZLinkAuthorityKey).globalId;
|
|
4164
4262
|
const localDescriptor = this.options.localDescriptor?.(this.meshName);
|
|
4165
4263
|
const nativeEntrySpot = localDescriptor?.entrySpotId === spotIdentity;
|
|
4166
4264
|
const actor = await this.requireActorManager().prepareRelocationActor(
|
|
@@ -4210,7 +4308,10 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4210
4308
|
payload: Uint8Array,
|
|
4211
4309
|
signal?: AbortSignal
|
|
4212
4310
|
): Promise<void> {
|
|
4213
|
-
const registration = this.registration(
|
|
4311
|
+
const registration = this.registration(
|
|
4312
|
+
hidden.participant.objectKind,
|
|
4313
|
+
hidden.participant.stableType
|
|
4314
|
+
);
|
|
4214
4315
|
if (registration.relocation.kind === 'recreate') {
|
|
4215
4316
|
if (payload.byteLength !== 0) throw new Error('Recreate relocation contains snapshot state.');
|
|
4216
4317
|
return;
|
|
@@ -4218,10 +4319,12 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4218
4319
|
if (registration.relocation.kind !== 'snapshot') {
|
|
4219
4320
|
throw new Error(`Relocation is disabled for '${hidden.participant.stableType}'.`);
|
|
4220
4321
|
}
|
|
4221
|
-
const adapter = await createProviderInstance(
|
|
4322
|
+
const adapter = (await createProviderInstance(
|
|
4222
4323
|
registration.relocation.adapterType,
|
|
4223
4324
|
this.options.providerResolver
|
|
4224
|
-
) as
|
|
4325
|
+
)) as
|
|
4326
|
+
| ZLinkActorRelocationAdapter<ZLinkActor>
|
|
4327
|
+
| ZLinkSpotRelocationAdapter<ZLinkSpot | ZLinkInstanceSpot>;
|
|
4225
4328
|
const instance = (hidden.actor ?? hidden.activation?.spot) as never;
|
|
4226
4329
|
await restoreRelocationAdapterState(
|
|
4227
4330
|
adapter as ZLinkRelocationStateAdapterLike<unknown>,
|
|
@@ -4237,11 +4340,12 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4237
4340
|
): Promise<void> {
|
|
4238
4341
|
for (const membership of memberships) {
|
|
4239
4342
|
const actor = hidden.get(membership.actorKey)?.actor;
|
|
4240
|
-
const spotIdentity = decodeAuthorityKey(
|
|
4241
|
-
|
|
4242
|
-
).globalId;
|
|
4243
|
-
const spot =
|
|
4244
|
-
|
|
4343
|
+
const spotIdentity = decodeAuthorityKey({
|
|
4344
|
+
value: membership.spotKey
|
|
4345
|
+
} as ZLinkAuthorityKey).globalId;
|
|
4346
|
+
const spot =
|
|
4347
|
+
hidden.get(membership.spotKey)?.activation ??
|
|
4348
|
+
this.requireSpotManager().resolveRelocationActivation(
|
|
4245
4349
|
this.meshName,
|
|
4246
4350
|
spotIdentity as RoutingId
|
|
4247
4351
|
);
|
|
@@ -4257,31 +4361,25 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4257
4361
|
// local Entry Spot path after the target commit.
|
|
4258
4362
|
state.clearJoinedSpot();
|
|
4259
4363
|
} else {
|
|
4260
|
-
state.setJoinedSpot(
|
|
4261
|
-
spotIdentity as RoutingId,
|
|
4262
|
-
spot?.spot,
|
|
4263
|
-
membership.membershipEpoch
|
|
4264
|
-
);
|
|
4364
|
+
state.setJoinedSpot(spotIdentity as RoutingId, spot?.spot, membership.membershipEpoch);
|
|
4265
4365
|
}
|
|
4266
4366
|
spot?.commitActorJoin(actor);
|
|
4267
4367
|
}
|
|
4268
4368
|
}
|
|
4269
4369
|
|
|
4270
|
-
async publish(
|
|
4271
|
-
hidden: LocalHidden,
|
|
4272
|
-
authority: ZLinkAuthoritySnapshot
|
|
4273
|
-
): Promise<void> {
|
|
4370
|
+
async publish(hidden: LocalHidden, authority: ZLinkAuthoritySnapshot): Promise<void> {
|
|
4274
4371
|
if (hidden.actor !== undefined) {
|
|
4275
4372
|
const store = this.requireLocationStore();
|
|
4276
|
-
const current = await requireAuthority(
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
);
|
|
4373
|
+
const current = await requireAuthority(store, {
|
|
4374
|
+
value: hidden.authorityKey
|
|
4375
|
+
} as ZLinkAuthorityKey);
|
|
4280
4376
|
const actorId = hidden.actor.context.actorId;
|
|
4281
4377
|
const node = this.options.meshNode(this.meshName);
|
|
4282
4378
|
const restore = node?.restoreActorAuthority;
|
|
4283
4379
|
if (node === undefined || restore === undefined) {
|
|
4284
|
-
throw new Error(
|
|
4380
|
+
throw new Error(
|
|
4381
|
+
`Relocation Actor '${actorId}' native authority publication is unavailable.`
|
|
4382
|
+
);
|
|
4285
4383
|
}
|
|
4286
4384
|
const native = node.actorLookup(actorId);
|
|
4287
4385
|
restore.call(
|
|
@@ -4306,14 +4404,12 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4306
4404
|
hidden.participant.stableType
|
|
4307
4405
|
);
|
|
4308
4406
|
const applicationSignaled =
|
|
4309
|
-
hidden.participant.objectKind === 'user_spot'
|
|
4310
|
-
|
|
4311
|
-
|
|
4407
|
+
hidden.participant.objectKind === 'user_spot' &&
|
|
4408
|
+
registration.options?.relocationCoordinationMode ===
|
|
4409
|
+
ZLinkSpotRelocationCoordinationMode.ApplicationSignaled;
|
|
4312
4410
|
await this.requireSpotManager().publishRelocationSpot(
|
|
4313
4411
|
hidden.activation,
|
|
4314
|
-
applicationSignaled
|
|
4315
|
-
? () => hidden.activation!.notifyRelocatedBoundary()
|
|
4316
|
-
: undefined
|
|
4412
|
+
applicationSignaled ? () => hidden.activation!.notifyRelocatedBoundary() : undefined
|
|
4317
4413
|
);
|
|
4318
4414
|
this.options.meshNode(this.meshName)?.rememberSpotRoute?.({
|
|
4319
4415
|
spot: {
|
|
@@ -4424,22 +4520,25 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4424
4520
|
}
|
|
4425
4521
|
|
|
4426
4522
|
async restoreTimer(hidden: LocalHidden, timer: ServiceRelocationTimer): Promise<void> {
|
|
4427
|
-
if (hidden.activation === undefined)
|
|
4523
|
+
if (hidden.activation === undefined)
|
|
4524
|
+
throw new Error('Actor relocation cannot restore a Spot timer.');
|
|
4428
4525
|
hidden.restoredTimers.push(timer);
|
|
4429
4526
|
if (hidden.restoredTimers.length !== hidden.participant.timers.length) return;
|
|
4430
|
-
hidden.activation.timers.restoreRelocation(
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4527
|
+
hidden.activation.timers.restoreRelocation(
|
|
4528
|
+
hidden.restoredTimers.map((value) => ({
|
|
4529
|
+
name: value.timerId,
|
|
4530
|
+
handlerType: value.handlerType,
|
|
4531
|
+
periodMs: value.intervalMs,
|
|
4532
|
+
overrunPolicy: value.overrunPolicy as never,
|
|
4533
|
+
maxCatchUpTicks: value.maxCatchUpTicks,
|
|
4534
|
+
stopOnUnhandledException: value.stopOnUnhandledException,
|
|
4535
|
+
startedAtUnixMs: value.startedAtUnixMs,
|
|
4536
|
+
deliveryIndex: value.deliveryIndex,
|
|
4537
|
+
lastScheduledIndex: value.lastScheduledIndex,
|
|
4538
|
+
nextDueAtUnixMs: value.dueAtUnixMs,
|
|
4539
|
+
pendingTicks: value.pendingTicks
|
|
4540
|
+
}))
|
|
4541
|
+
);
|
|
4443
4542
|
}
|
|
4444
4543
|
|
|
4445
4544
|
async normalize(_hidden: LocalHidden): Promise<void> {}
|
|
@@ -4454,11 +4553,12 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
|
|
|
4454
4553
|
|
|
4455
4554
|
private registration(kind: ServiceRelocationParticipant['objectKind'], stableType: string): any {
|
|
4456
4555
|
const node = this.options.registration.spotNodes.get(this.meshName);
|
|
4457
|
-
const value =
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4556
|
+
const value =
|
|
4557
|
+
kind === 'actor'
|
|
4558
|
+
? node?.actorFactoryRegistrations?.[stableType]
|
|
4559
|
+
: kind === 'user_spot'
|
|
4560
|
+
? node?.spotFactoryRegistrations?.[stableType]
|
|
4561
|
+
: node?.instanceSpotFactoryRegistrations?.[stableType];
|
|
4462
4562
|
if (value === undefined) throw new Error(`Relocation type '${stableType}' is not registered.`);
|
|
4463
4563
|
return value;
|
|
4464
4564
|
}
|
|
@@ -4506,10 +4606,7 @@ function rewriteAuthorityApplicationRoute(
|
|
|
4506
4606
|
}
|
|
4507
4607
|
): Buffer {
|
|
4508
4608
|
const applicationPayload = serviceRelocationAuthorityApplicationPayload(payload);
|
|
4509
|
-
const actor = decodeRelocatingActorAuthorityIdentity(
|
|
4510
|
-
applicationPayload,
|
|
4511
|
-
target.objectGeneration
|
|
4512
|
-
);
|
|
4609
|
+
const actor = decodeRelocatingActorAuthorityIdentity(applicationPayload, target.objectGeneration);
|
|
4513
4610
|
if (actor !== undefined) {
|
|
4514
4611
|
return rewriteActorAuthorityRoute(
|
|
4515
4612
|
applicationPayload,
|
|
@@ -4526,15 +4623,16 @@ function rewriteAuthorityApplicationRoute(
|
|
|
4526
4623
|
target.owner
|
|
4527
4624
|
);
|
|
4528
4625
|
}
|
|
4529
|
-
return
|
|
4530
|
-
?? applicationPayload
|
|
4626
|
+
return (
|
|
4627
|
+
rewriteServiceAuthorityRoute(applicationPayload, target.owner, target) ?? applicationPayload
|
|
4628
|
+
);
|
|
4531
4629
|
}
|
|
4532
4630
|
|
|
4533
4631
|
function actorMembershipTarget(
|
|
4534
4632
|
envelope: ServiceRelocationEnvelope,
|
|
4535
4633
|
actorKey: string
|
|
4536
4634
|
): { readonly actorSpotId: string; readonly actorSpotGeneration: bigint } | undefined {
|
|
4537
|
-
const membership = envelope.memberships.find(value => value.actorKey === actorKey);
|
|
4635
|
+
const membership = envelope.memberships.find((value) => value.actorKey === actorKey);
|
|
4538
4636
|
if (membership === undefined) return undefined;
|
|
4539
4637
|
return {
|
|
4540
4638
|
actorSpotId: decodeAuthorityKey({ value: membership.spotKey } as ZLinkAuthorityKey).globalId,
|
|
@@ -4543,8 +4641,10 @@ function actorMembershipTarget(
|
|
|
4543
4641
|
}
|
|
4544
4642
|
|
|
4545
4643
|
function primaryKey(envelope: ServiceRelocationEnvelope): string {
|
|
4546
|
-
return
|
|
4547
|
-
|
|
4644
|
+
return (
|
|
4645
|
+
envelope.participants.find((value) => value.objectKind !== 'actor')?.key ??
|
|
4646
|
+
envelope.participants[0]!.key
|
|
4647
|
+
);
|
|
4548
4648
|
}
|
|
4549
4649
|
|
|
4550
4650
|
function relocationTargetRequirements(
|
|
@@ -4554,9 +4654,10 @@ function relocationTargetRequirements(
|
|
|
4554
4654
|
for (const value of values) {
|
|
4555
4655
|
const key = `${value.objectKind}\0${value.stableType}\0${value.policy}`;
|
|
4556
4656
|
const existing = grouped.get(key);
|
|
4557
|
-
grouped.set(
|
|
4558
|
-
|
|
4559
|
-
: { ...existing, count: existing.count + value.count }
|
|
4657
|
+
grouped.set(
|
|
4658
|
+
key,
|
|
4659
|
+
existing === undefined ? value : { ...existing, count: existing.count + value.count }
|
|
4660
|
+
);
|
|
4560
4661
|
}
|
|
4561
4662
|
return [...grouped.values()];
|
|
4562
4663
|
}
|
|
@@ -4565,40 +4666,53 @@ function relocationTargetSupports(
|
|
|
4565
4666
|
descriptor: ZLinkMeshNodeDescriptor,
|
|
4566
4667
|
requirements: readonly RelocationTargetRequirement[]
|
|
4567
4668
|
): boolean {
|
|
4568
|
-
if (
|
|
4569
|
-
|
|
4669
|
+
if (
|
|
4670
|
+
descriptor.activationConcurrency.limit !== 0 &&
|
|
4671
|
+
descriptor.activationConcurrency.active >= descriptor.activationConcurrency.limit
|
|
4672
|
+
) {
|
|
4570
4673
|
return false;
|
|
4571
4674
|
}
|
|
4572
4675
|
const actorCount = requirements
|
|
4573
|
-
.filter(value => value.objectKind === 'actor')
|
|
4676
|
+
.filter((value) => value.objectKind === 'actor')
|
|
4574
4677
|
.reduce((sum, value) => sum + value.count, 0);
|
|
4575
4678
|
const spotCount = requirements
|
|
4576
|
-
.filter(value => value.objectKind !== 'actor')
|
|
4679
|
+
.filter((value) => value.objectKind !== 'actor')
|
|
4577
4680
|
.reduce((sum, value) => sum + value.count, 0);
|
|
4578
|
-
if (
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4681
|
+
if (
|
|
4682
|
+
(descriptor.populationCapacity.actors.limit !== 0 &&
|
|
4683
|
+
descriptor.populationCapacity.actors.active +
|
|
4684
|
+
descriptor.populationCapacity.actors.reserved +
|
|
4685
|
+
actorCount >
|
|
4686
|
+
descriptor.populationCapacity.actors.limit) ||
|
|
4687
|
+
(descriptor.populationCapacity.spots.limit !== 0 &&
|
|
4688
|
+
descriptor.populationCapacity.spots.active +
|
|
4689
|
+
descriptor.populationCapacity.spots.reserved +
|
|
4690
|
+
spotCount >
|
|
4691
|
+
descriptor.populationCapacity.spots.limit)
|
|
4692
|
+
) {
|
|
4586
4693
|
return false;
|
|
4587
4694
|
}
|
|
4588
4695
|
for (const requirement of requirements) {
|
|
4589
|
-
if (
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4696
|
+
if (
|
|
4697
|
+
!descriptor.objectCapabilities.some(
|
|
4698
|
+
(capability) =>
|
|
4699
|
+
capability.objectKind === requirement.objectKind &&
|
|
4700
|
+
capability.stableType === requirement.stableType &&
|
|
4701
|
+
capability.policy === requirement.policy
|
|
4702
|
+
)
|
|
4703
|
+
) {
|
|
4593
4704
|
return false;
|
|
4594
4705
|
}
|
|
4595
4706
|
if (requirement.objectKind !== 'actor') {
|
|
4596
|
-
const capacity = descriptor.populationCapacity.spotTypes.find(
|
|
4597
|
-
value
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4707
|
+
const capacity = descriptor.populationCapacity.spotTypes.find(
|
|
4708
|
+
(value) =>
|
|
4709
|
+
value.objectKind === requirement.objectKind && value.stableType === requirement.stableType
|
|
4710
|
+
);
|
|
4711
|
+
if (
|
|
4712
|
+
capacity === undefined ||
|
|
4713
|
+
(capacity.limit !== 0 &&
|
|
4714
|
+
capacity.active + capacity.reserved + requirement.count > capacity.limit)
|
|
4715
|
+
) {
|
|
4602
4716
|
return false;
|
|
4603
4717
|
}
|
|
4604
4718
|
}
|
|
@@ -4626,10 +4740,7 @@ interface RelocationTerminalCompletion {
|
|
|
4626
4740
|
readonly result: ZLinkActorHandoffResult;
|
|
4627
4741
|
}
|
|
4628
4742
|
|
|
4629
|
-
type RelocationTerminalDelivery =
|
|
4630
|
-
| 'terminalReceived'
|
|
4631
|
-
| 'alreadyTerminal'
|
|
4632
|
-
| 'sourceLeaseExpired';
|
|
4743
|
+
type RelocationTerminalDelivery = 'terminalReceived' | 'alreadyTerminal' | 'sourceLeaseExpired';
|
|
4633
4744
|
|
|
4634
4745
|
export function decodeQueuedHandoffPacket(
|
|
4635
4746
|
message: ServiceRelocationQueuedMessage
|
|
@@ -4638,19 +4749,21 @@ export function decodeQueuedHandoffPacket(
|
|
|
4638
4749
|
const frozen = isCanonicalFrozenRecord(payload)
|
|
4639
4750
|
? decodeServiceWireFrozenRecord(payload)
|
|
4640
4751
|
: undefined;
|
|
4641
|
-
const value = JSON.parse(
|
|
4642
|
-
frozen?.applicationPayload?.bytes ?? payload
|
|
4643
|
-
)
|
|
4752
|
+
const value = JSON.parse(
|
|
4753
|
+
Buffer.from(frozen?.applicationPayload?.bytes ?? payload).toString('utf8')
|
|
4754
|
+
) as Record<string, unknown>;
|
|
4644
4755
|
const index = value.index;
|
|
4645
|
-
if (!Number.isSafeInteger(index) || index as number < 0) {
|
|
4756
|
+
if (!Number.isSafeInteger(index) || (index as number) < 0) {
|
|
4646
4757
|
throw new TypeError('Relocation Actor packet index is invalid.');
|
|
4647
4758
|
}
|
|
4648
4759
|
const messageFollowOrigin = decodeQueuedMessageFollowOrigin(value.messageFollowOrigin);
|
|
4649
|
-
const decoded = decodeHandoffBacklog([
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4760
|
+
const decoded = decodeHandoffBacklog([
|
|
4761
|
+
{
|
|
4762
|
+
...value,
|
|
4763
|
+
index: 0,
|
|
4764
|
+
...(messageFollowOrigin === undefined ? {} : { messageFollowOrigin })
|
|
4765
|
+
}
|
|
4766
|
+
])[0]!;
|
|
4654
4767
|
return { ...decoded, index: index as number };
|
|
4655
4768
|
}
|
|
4656
4769
|
|
|
@@ -4667,20 +4780,18 @@ function canonicalizeCapturedHandoffBacklog(
|
|
|
4667
4780
|
): ServiceRelocationEnvelope {
|
|
4668
4781
|
return {
|
|
4669
4782
|
...envelope,
|
|
4670
|
-
participants: envelope.participants.map(participant =>
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
participant,
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
}))
|
|
4683
|
-
})
|
|
4783
|
+
participants: envelope.participants.map((participant) =>
|
|
4784
|
+
participant.objectKind !== 'actor'
|
|
4785
|
+
? participant
|
|
4786
|
+
: {
|
|
4787
|
+
...participant,
|
|
4788
|
+
queuedMessages: participant.queuedMessages.map((message) => ({
|
|
4789
|
+
...message,
|
|
4790
|
+
payload: canonicalQueuedFrozenRecord(participant, message, coordinator, target)
|
|
4791
|
+
.canonicalBytes
|
|
4792
|
+
}))
|
|
4793
|
+
}
|
|
4794
|
+
)
|
|
4684
4795
|
};
|
|
4685
4796
|
}
|
|
4686
4797
|
|
|
@@ -4697,27 +4808,32 @@ function appendCanonicalActorJoinRecovery(
|
|
|
4697
4808
|
sourceAuthoritySnapshot.objectGeneration
|
|
4698
4809
|
);
|
|
4699
4810
|
if (
|
|
4700
|
-
actorType === undefined
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4811
|
+
actorType === undefined ||
|
|
4812
|
+
sourceAuthority === undefined ||
|
|
4813
|
+
sourceAuthority.actor.actorId !== profile.state.actorId ||
|
|
4814
|
+
sourceAuthority.actor.objectGeneration !== profile.sourceActorRef.generation ||
|
|
4815
|
+
!routingIdsEqual(
|
|
4705
4816
|
sourceAuthority.actor.nodeRid,
|
|
4706
4817
|
profile.sourceActorRef.nodeRid as unknown as RoutingId
|
|
4707
|
-
)
|
|
4708
|
-
|
|
4709
|
-
|
|
4818
|
+
) ||
|
|
4819
|
+
sourceAuthority.ownerNodeGeneration !== recovery.actorNodeGeneration ||
|
|
4820
|
+
sourceAuthoritySnapshot.authorityOwnerGeneration !== profile.state.locationGeneration
|
|
4710
4821
|
) {
|
|
4711
|
-
throw new Error(
|
|
4822
|
+
throw new Error(
|
|
4823
|
+
`Actor '${profile.state.actorId}' canonical Join recovery identity is incomplete.`
|
|
4824
|
+
);
|
|
4712
4825
|
}
|
|
4713
4826
|
const attachment = { attached: false };
|
|
4714
|
-
const participants = envelope.participants.map(participant => {
|
|
4715
|
-
if (
|
|
4716
|
-
|
|
4717
|
-
|
|
4827
|
+
const participants = envelope.participants.map((participant) => {
|
|
4828
|
+
if (
|
|
4829
|
+
participant.objectKind !== 'actor' ||
|
|
4830
|
+
decodeAuthorityKey({ value: participant.key } as ZLinkAuthorityKey).globalId !==
|
|
4831
|
+
profile.state.actorId
|
|
4832
|
+
) {
|
|
4718
4833
|
return participant;
|
|
4719
4834
|
}
|
|
4720
|
-
if (attachment.attached)
|
|
4835
|
+
if (attachment.attached)
|
|
4836
|
+
throw new Error('Canonical Actor Join recovery has duplicate Actor participants.');
|
|
4721
4837
|
attachment.attached = true;
|
|
4722
4838
|
const encoded = encodeCanonicalActorJoinRecoverySavedWork({
|
|
4723
4839
|
actorId: profile.state.actorId,
|
|
@@ -4759,14 +4875,15 @@ function appendCanonicalActorJoinRecovery(
|
|
|
4759
4875
|
...participant,
|
|
4760
4876
|
queuedMessages: [
|
|
4761
4877
|
{ sequence: 1n, payload: encoded },
|
|
4762
|
-
...participant.queuedMessages.map(message => ({
|
|
4878
|
+
...participant.queuedMessages.map((message) => ({
|
|
4763
4879
|
...message,
|
|
4764
4880
|
sequence: message.sequence + 1n
|
|
4765
4881
|
}))
|
|
4766
4882
|
]
|
|
4767
4883
|
};
|
|
4768
4884
|
});
|
|
4769
|
-
if (!attachment.attached)
|
|
4885
|
+
if (!attachment.attached)
|
|
4886
|
+
throw new Error('Canonical Actor Join recovery Actor participant is missing.');
|
|
4770
4887
|
return { ...envelope, participants };
|
|
4771
4888
|
}
|
|
4772
4889
|
|
|
@@ -4812,8 +4929,9 @@ export function createServiceRelocationId(
|
|
|
4812
4929
|
}
|
|
4813
4930
|
if (!nonZero) continue;
|
|
4814
4931
|
const hex = Buffer.from(bytes).toString('hex');
|
|
4815
|
-
const candidate =
|
|
4816
|
-
|
|
4932
|
+
const candidate =
|
|
4933
|
+
`${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-` +
|
|
4934
|
+
`${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
4817
4935
|
if (!isInUse(candidate)) return candidate;
|
|
4818
4936
|
}
|
|
4819
4937
|
}
|
|
@@ -4839,29 +4957,32 @@ function sameCoordinator(
|
|
|
4839
4957
|
left: ServiceWireRelocationCoordinatorFence,
|
|
4840
4958
|
right: ServiceWireRelocationCoordinatorFence
|
|
4841
4959
|
): boolean {
|
|
4842
|
-
return
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4960
|
+
return (
|
|
4961
|
+
left.ownerId === right.ownerId &&
|
|
4962
|
+
left.leaseGeneration === right.leaseGeneration &&
|
|
4963
|
+
left.nodeRid === right.nodeRid &&
|
|
4964
|
+
left.nodeGeneration === right.nodeGeneration &&
|
|
4965
|
+
left.expectedAuthorityStoreVersion === right.expectedAuthorityStoreVersion
|
|
4966
|
+
);
|
|
4847
4967
|
}
|
|
4848
4968
|
|
|
4849
4969
|
function isExactSourceAuthority(
|
|
4850
4970
|
current: ZLinkAuthoritySnapshot,
|
|
4851
4971
|
expected: ZLinkAuthoritySnapshot
|
|
4852
4972
|
): boolean {
|
|
4853
|
-
return
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4973
|
+
return (
|
|
4974
|
+
current.storeVersion.value === expected.storeVersion.value &&
|
|
4975
|
+
current.objectGeneration === expected.objectGeneration &&
|
|
4976
|
+
current.authorityOwnerGeneration === expected.authorityOwnerGeneration &&
|
|
4977
|
+
current.ownerId === expected.ownerId &&
|
|
4978
|
+
current.ownerLeaseGeneration === expected.ownerLeaseGeneration &&
|
|
4979
|
+
current.allocation.state === expected.allocation.state &&
|
|
4980
|
+
current.allocation.objectKind === expected.allocation.objectKind &&
|
|
4981
|
+
current.allocation.stableType === expected.allocation.stableType &&
|
|
4982
|
+
String(current.allocation.descriptor.rid) === String(expected.allocation.descriptor.rid) &&
|
|
4983
|
+
current.allocation.descriptorLifecycleGeneration ===
|
|
4984
|
+
expected.allocation.descriptorLifecycleGeneration
|
|
4985
|
+
);
|
|
4865
4986
|
}
|
|
4866
4987
|
|
|
4867
4988
|
function isExactCommittedTarget(
|
|
@@ -4870,13 +4991,15 @@ function isExactCommittedTarget(
|
|
|
4870
4991
|
target: ServiceWireRelocationTarget,
|
|
4871
4992
|
payload: Uint8Array
|
|
4872
4993
|
): boolean {
|
|
4873
|
-
return
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4994
|
+
return (
|
|
4995
|
+
current.objectGeneration === expected.objectGeneration &&
|
|
4996
|
+
current.authorityOwnerGeneration > expected.authorityOwnerGeneration &&
|
|
4997
|
+
current.ownerId === target.ownerId &&
|
|
4998
|
+
current.ownerLeaseGeneration === target.ownerLeaseGeneration &&
|
|
4999
|
+
String(current.allocation.descriptor.rid) === target.nodeRid &&
|
|
5000
|
+
current.allocation.descriptorLifecycleGeneration === target.nodeGeneration &&
|
|
5001
|
+
Buffer.from(current.payload).equals(Buffer.from(payload))
|
|
5002
|
+
);
|
|
4880
5003
|
}
|
|
4881
5004
|
|
|
4882
5005
|
async function waitForLocationRetry(
|
|
@@ -4898,18 +5021,18 @@ async function waitForLocationRetry(
|
|
|
4898
5021
|
reject(signal?.reason);
|
|
4899
5022
|
};
|
|
4900
5023
|
signal?.addEventListener('abort', aborted, { once: true });
|
|
4901
|
-
timer = setTimeout(
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
5024
|
+
timer = setTimeout(
|
|
5025
|
+
() => {
|
|
5026
|
+
signal?.removeEventListener('abort', aborted);
|
|
5027
|
+
resolve();
|
|
5028
|
+
},
|
|
5029
|
+
Math.min(25, Math.max(1, deadlineAtMs - performance.now()))
|
|
5030
|
+
);
|
|
4905
5031
|
timer.unref();
|
|
4906
5032
|
});
|
|
4907
5033
|
}
|
|
4908
5034
|
|
|
4909
|
-
async function waitForRelocationRetry(
|
|
4910
|
-
delayMs: number,
|
|
4911
|
-
signal?: AbortSignal
|
|
4912
|
-
): Promise<void> {
|
|
5035
|
+
async function waitForRelocationRetry(delayMs: number, signal?: AbortSignal): Promise<void> {
|
|
4913
5036
|
signal?.throwIfAborted();
|
|
4914
5037
|
await new Promise<void>((resolve, reject) => {
|
|
4915
5038
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
@@ -4930,10 +5053,12 @@ function sameRequestSource(
|
|
|
4930
5053
|
left: ServiceWireRequestSourceFence,
|
|
4931
5054
|
right: ServiceWireRequestSourceFence
|
|
4932
5055
|
): boolean {
|
|
4933
|
-
return
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
5056
|
+
return (
|
|
5057
|
+
left.ownerId === right.ownerId &&
|
|
5058
|
+
left.leaseGeneration === right.leaseGeneration &&
|
|
5059
|
+
left.nodeRid === right.nodeRid &&
|
|
5060
|
+
left.nodeGeneration === right.nodeGeneration
|
|
5061
|
+
);
|
|
4937
5062
|
}
|
|
4938
5063
|
|
|
4939
5064
|
function handoffSourceFence(
|
|
@@ -4964,11 +5089,15 @@ function maintenanceReplyRelay(
|
|
|
4964
5089
|
sequence: BigInt(completion.index + 1),
|
|
4965
5090
|
terminalResult: completion.result.ok ? 0 : 105,
|
|
4966
5091
|
failureCode: completion.result.ok ? 0 : 17,
|
|
4967
|
-
...(value === undefined
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
5092
|
+
...(value === undefined
|
|
5093
|
+
? {}
|
|
5094
|
+
: {
|
|
5095
|
+
payload: {
|
|
5096
|
+
packetName: 'zlink.relocation.reply',
|
|
5097
|
+
contentType: 'application/json',
|
|
5098
|
+
bytes: Buffer.from(JSON.stringify(value), 'utf8')
|
|
5099
|
+
}
|
|
5100
|
+
})
|
|
4972
5101
|
};
|
|
4973
5102
|
}
|
|
4974
5103
|
|
|
@@ -4978,9 +5107,11 @@ function handoffResultFromRelay(relay: ServiceMaintenanceReplyRelay): ZLinkActor
|
|
|
4978
5107
|
}
|
|
4979
5108
|
let value: unknown;
|
|
4980
5109
|
if (relay.payload !== undefined) {
|
|
4981
|
-
if (
|
|
4982
|
-
|
|
4983
|
-
|
|
5110
|
+
if (
|
|
5111
|
+
relay.terminalResult !== 0 ||
|
|
5112
|
+
relay.payload.packetName !== 'zlink.relocation.reply' ||
|
|
5113
|
+
relay.payload.contentType !== 'application/json'
|
|
5114
|
+
) {
|
|
4984
5115
|
throw new TypeError('Relocation terminal payload boundary is invalid.');
|
|
4985
5116
|
}
|
|
4986
5117
|
value = JSON.parse(Buffer.from(relay.payload.bytes).toString('utf8')) as unknown;
|
|
@@ -5082,32 +5213,47 @@ function relocationStagingId(value: {
|
|
|
5082
5213
|
}
|
|
5083
5214
|
|
|
5084
5215
|
function coordinatorSource(value: ServiceWireRelocationCoordinatorFence) {
|
|
5085
|
-
return {
|
|
5086
|
-
|
|
5216
|
+
return {
|
|
5217
|
+
ownerId: value.ownerId,
|
|
5218
|
+
leaseGeneration: value.leaseGeneration,
|
|
5219
|
+
nodeRid: value.nodeRid,
|
|
5220
|
+
nodeGeneration: value.nodeGeneration
|
|
5221
|
+
};
|
|
5087
5222
|
}
|
|
5088
5223
|
|
|
5089
5224
|
function relocationObject(envelope: ServiceRelocationEnvelope): ServiceWireRelocationObject {
|
|
5090
|
-
const participant =
|
|
5091
|
-
|
|
5225
|
+
const participant =
|
|
5226
|
+
envelope.participants.find((value) => value.objectKind !== 'actor') ??
|
|
5227
|
+
envelope.participants[0]!;
|
|
5092
5228
|
const identity = decodeAuthorityKey({ value: participant.key } as ZLinkAuthorityKey).globalId;
|
|
5093
5229
|
if (participant.objectKind === 'actor') {
|
|
5094
|
-
return {
|
|
5095
|
-
|
|
5230
|
+
return {
|
|
5231
|
+
kind: 'actor',
|
|
5232
|
+
actorId: identity,
|
|
5233
|
+
objectGeneration: participant.objectGeneration,
|
|
5234
|
+
expectedAuthorityOwnerGeneration: participant.authorityOwnerGeneration
|
|
5235
|
+
};
|
|
5096
5236
|
}
|
|
5097
5237
|
if (participant.objectKind === 'user_spot') {
|
|
5098
|
-
return {
|
|
5099
|
-
|
|
5238
|
+
return {
|
|
5239
|
+
kind: 'userSpot',
|
|
5240
|
+
spotId: identity,
|
|
5241
|
+
objectGeneration: participant.objectGeneration,
|
|
5242
|
+
expectedAuthorityOwnerGeneration: participant.authorityOwnerGeneration
|
|
5243
|
+
};
|
|
5100
5244
|
}
|
|
5101
|
-
return {
|
|
5102
|
-
|
|
5245
|
+
return {
|
|
5246
|
+
kind: 'instanceSpot',
|
|
5247
|
+
stableType: participant.stableType,
|
|
5248
|
+
spotId: identity,
|
|
5249
|
+
objectGeneration: participant.objectGeneration
|
|
5250
|
+
};
|
|
5103
5251
|
}
|
|
5104
5252
|
|
|
5105
5253
|
function relocationObjectForParticipant(
|
|
5106
5254
|
participant: ServiceRelocationParticipant
|
|
5107
5255
|
): ServiceWireRelocationObject {
|
|
5108
|
-
const identity = decodeAuthorityKey(
|
|
5109
|
-
{ value: participant.key } as ZLinkAuthorityKey
|
|
5110
|
-
).globalId;
|
|
5256
|
+
const identity = decodeAuthorityKey({ value: participant.key } as ZLinkAuthorityKey).globalId;
|
|
5111
5257
|
if (participant.objectKind === 'actor') {
|
|
5112
5258
|
return {
|
|
5113
5259
|
kind: 'actor',
|
|
@@ -5166,9 +5312,11 @@ function validatePrepareEnvelope(
|
|
|
5166
5312
|
request: ServiceMaintenanceRelocationPrepare,
|
|
5167
5313
|
envelope: ServiceRelocationEnvelope
|
|
5168
5314
|
): void {
|
|
5169
|
-
if (
|
|
5170
|
-
|
|
5171
|
-
|
|
5315
|
+
if (
|
|
5316
|
+
!sameWireId(request.relocation, relocationWireId(envelope.aggregateId)) ||
|
|
5317
|
+
request.applicationVersion !== envelope.applicationVersion ||
|
|
5318
|
+
stringifyWire(request.object) !== stringifyWire(relocationObject(envelope))
|
|
5319
|
+
) {
|
|
5172
5320
|
throw new Error('Relocation Prepare does not match its immutable root identity.');
|
|
5173
5321
|
}
|
|
5174
5322
|
}
|
|
@@ -5179,17 +5327,20 @@ function validateTargetOneWayControl(
|
|
|
5179
5327
|
sourceNodeRid: RoutingId
|
|
5180
5328
|
): void {
|
|
5181
5329
|
const prepare = stage.offer.prepare;
|
|
5182
|
-
if (
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5330
|
+
if (
|
|
5331
|
+
String(sourceNodeRid) !== stage.offer.authenticatedSourceNodeRid ||
|
|
5332
|
+
!sameWireId(request.relocation, prepare.relocation) ||
|
|
5333
|
+
request.targetAttemptGeneration !== prepare.targetAttemptGeneration ||
|
|
5334
|
+
!sameCoordinator(request.coordinator, prepare.coordinator) ||
|
|
5335
|
+
stringifyWire(request.object) !==
|
|
5336
|
+
stringifyWire(request.kind === 'data' ? request.object : prepare.object)
|
|
5337
|
+
) {
|
|
5189
5338
|
throw new Error('Relocation one-way control changed its exact Prepare fence.');
|
|
5190
5339
|
}
|
|
5191
|
-
if (
|
|
5192
|
-
|
|
5340
|
+
if (
|
|
5341
|
+
request.kind === 'cutover' &&
|
|
5342
|
+
stringifyWire(request.object) !== stringifyWire(prepare.object)
|
|
5343
|
+
) {
|
|
5193
5344
|
throw new Error('Relocation Cutover changed its prepared object.');
|
|
5194
5345
|
}
|
|
5195
5346
|
}
|
|
@@ -5236,11 +5387,11 @@ function raceAbort<T>(operation: Promise<T>, signal: AbortSignal): Promise<T> {
|
|
|
5236
5387
|
const aborted = () => reject(signal.reason);
|
|
5237
5388
|
signal.addEventListener('abort', aborted, { once: true });
|
|
5238
5389
|
operation.then(
|
|
5239
|
-
value => {
|
|
5390
|
+
(value) => {
|
|
5240
5391
|
signal.removeEventListener('abort', aborted);
|
|
5241
5392
|
resolve(value);
|
|
5242
5393
|
},
|
|
5243
|
-
error => {
|
|
5394
|
+
(error) => {
|
|
5244
5395
|
signal.removeEventListener('abort', aborted);
|
|
5245
5396
|
reject(error);
|
|
5246
5397
|
}
|
|
@@ -5259,22 +5410,24 @@ function canonicalQueuedFrozenRecord(
|
|
|
5259
5410
|
}
|
|
5260
5411
|
const packet = decodeQueuedHandoffPacket(message);
|
|
5261
5412
|
const identity = decodeAuthorityKey({ value: participant.key } as ZLinkAuthorityKey).globalId;
|
|
5262
|
-
const source =
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
);
|
|
5413
|
+
const source =
|
|
5414
|
+
packet.source === undefined
|
|
5415
|
+
? coordinatorSource(coordinator)
|
|
5416
|
+
: {
|
|
5417
|
+
ownerId: packet.source.ownerId,
|
|
5418
|
+
leaseGeneration: BigInt(packet.source.ownerLeaseGeneration),
|
|
5419
|
+
nodeRid: packet.source.nodeRid,
|
|
5420
|
+
nodeGeneration: BigInt(packet.source.nodeGeneration)
|
|
5421
|
+
};
|
|
5422
|
+
const operationId = parseHandoffOperationId(packet.messageFollowContext.operationId);
|
|
5273
5423
|
if (packet.returnResponse && packet.source === undefined) {
|
|
5274
5424
|
throw new Error('Captured Actor request has no exact request-source fence.');
|
|
5275
5425
|
}
|
|
5276
|
-
if (
|
|
5277
|
-
|
|
5426
|
+
if (
|
|
5427
|
+
!packet.returnResponse &&
|
|
5428
|
+
packet.source === undefined &&
|
|
5429
|
+
packet.remoteBoundSessionTarget !== undefined
|
|
5430
|
+
) {
|
|
5278
5431
|
// A durable frozen record is permitted only for a lease-backed source.
|
|
5279
5432
|
// Work tied to a Session connection's lifetime drains before Captured,
|
|
5280
5433
|
// so freezing it under the coordinator's fence is a protocol error.
|
|
@@ -5315,21 +5468,23 @@ function parseHandoffOperationId(value: string): { readonly high: bigint; readon
|
|
|
5315
5468
|
}
|
|
5316
5469
|
|
|
5317
5470
|
function stringifyWire(value: unknown): string {
|
|
5318
|
-
return JSON.stringify(value, (_key, item) => typeof item === 'bigint' ? item.toString() : item);
|
|
5471
|
+
return JSON.stringify(value, (_key, item) => (typeof item === 'bigint' ? item.toString() : item));
|
|
5319
5472
|
}
|
|
5320
5473
|
|
|
5321
5474
|
function validateControlResponse(
|
|
5322
5475
|
request: ZLinkServiceRelocationControlRequest,
|
|
5323
5476
|
response: ZLinkServiceRelocationControlResponse
|
|
5324
5477
|
): void {
|
|
5325
|
-
if (
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5478
|
+
if (
|
|
5479
|
+
request.kind !== 'prepare' ||
|
|
5480
|
+
response.kind !== 'ready' ||
|
|
5481
|
+
!sameWireId(response.relocation, request.relocation) ||
|
|
5482
|
+
response.targetAttemptGeneration !== request.targetAttemptGeneration ||
|
|
5483
|
+
!sameCoordinator(response.coordinator, request.coordinator) ||
|
|
5484
|
+
stringifyWire(response.target) !== stringifyWire(request.target) ||
|
|
5485
|
+
stringifyWire(response.object) !== stringifyWire(request.object) ||
|
|
5486
|
+
response.senderRole !== 'target'
|
|
5487
|
+
) {
|
|
5333
5488
|
throw new Error('Relocation control response does not match the request fence.');
|
|
5334
5489
|
}
|
|
5335
5490
|
}
|
|
@@ -5344,14 +5499,16 @@ function validateControlFailureResponse(
|
|
|
5344
5499
|
request: ZLinkServiceRelocationControlRequest,
|
|
5345
5500
|
response: ZLinkServiceRelocationControlResponse
|
|
5346
5501
|
): asserts response is ServiceMaintenanceRelocationFailed {
|
|
5347
|
-
if (
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5502
|
+
if (
|
|
5503
|
+
request.kind !== 'prepare' ||
|
|
5504
|
+
response.kind !== 'failed' ||
|
|
5505
|
+
!sameWireId(response.relocation, request.relocation) ||
|
|
5506
|
+
response.targetAttemptGeneration !== request.targetAttemptGeneration ||
|
|
5507
|
+
!sameCoordinator(response.coordinator, request.coordinator) ||
|
|
5508
|
+
stringifyWire(response.target) !== stringifyWire(request.target) ||
|
|
5509
|
+
stringifyWire(response.object) !== stringifyWire(request.object) ||
|
|
5510
|
+
response.senderRole !== 'target'
|
|
5511
|
+
) {
|
|
5355
5512
|
throw new Error('Relocation control response does not match the request fence.');
|
|
5356
5513
|
}
|
|
5357
5514
|
}
|
|
@@ -5366,30 +5523,54 @@ function validateControlFailureResponse(
|
|
|
5366
5523
|
*/
|
|
5367
5524
|
function relocationFailureCodeKind(failureCode: number): ZLinkFrameworkInternalErrorKind {
|
|
5368
5525
|
switch (failureCode) {
|
|
5369
|
-
case 1:
|
|
5370
|
-
|
|
5371
|
-
case
|
|
5372
|
-
|
|
5373
|
-
case
|
|
5374
|
-
|
|
5375
|
-
case
|
|
5376
|
-
|
|
5377
|
-
case
|
|
5378
|
-
|
|
5379
|
-
case
|
|
5380
|
-
|
|
5381
|
-
case
|
|
5382
|
-
|
|
5383
|
-
case
|
|
5384
|
-
|
|
5385
|
-
case
|
|
5386
|
-
|
|
5387
|
-
case
|
|
5388
|
-
|
|
5389
|
-
case
|
|
5390
|
-
|
|
5391
|
-
case
|
|
5392
|
-
|
|
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;
|
|
5393
5574
|
case 17:
|
|
5394
5575
|
default:
|
|
5395
5576
|
return ZLinkFrameworkInternalErrorKind.RequestFailed;
|
|
@@ -5400,8 +5581,8 @@ function relocationFailureCodeKind(failureCode: number): ZLinkFrameworkInternalE
|
|
|
5400
5581
|
function relocationFailureException(response: ServiceMaintenanceRelocationFailed) {
|
|
5401
5582
|
return createInternalFrameworkException(
|
|
5402
5583
|
relocationFailureCodeKind(response.failureCode),
|
|
5403
|
-
`Relocation '${response.relocation.high}:${response.relocation.low}:`
|
|
5404
|
-
|
|
5584
|
+
`Relocation '${response.relocation.high}:${response.relocation.low}:` +
|
|
5585
|
+
`${response.targetAttemptGeneration}' failed with wire failureCode ${response.failureCode}.`,
|
|
5405
5586
|
true
|
|
5406
5587
|
);
|
|
5407
5588
|
}
|
|
@@ -5410,8 +5591,10 @@ function controlAckKey(request: ZLinkServiceRelocationControlRequest): string {
|
|
|
5410
5591
|
if (request.kind !== 'prepare') {
|
|
5411
5592
|
throw new Error(`Relocation ${request.kind} is one-way and has no response key.`);
|
|
5412
5593
|
}
|
|
5413
|
-
return
|
|
5414
|
-
|
|
5594
|
+
return (
|
|
5595
|
+
`relocation:${request.relocation.high}:${request.relocation.low}:` +
|
|
5596
|
+
`${request.targetAttemptGeneration}:ready`
|
|
5597
|
+
);
|
|
5415
5598
|
}
|
|
5416
5599
|
|
|
5417
5600
|
function sessionRelocationPendingKey(request: ServiceSessionRelocationSeal): string {
|
|
@@ -5434,7 +5617,8 @@ function sessionRelocationResponseFingerprint(response: ServiceSessionRelocation
|
|
|
5434
5617
|
}
|
|
5435
5618
|
|
|
5436
5619
|
function sessionRelocationIdentityKey(
|
|
5437
|
-
value:
|
|
5620
|
+
value:
|
|
5621
|
+
ServiceSessionRelocationSeal | ServiceSessionRelocationSealed | ServiceSessionRelocationRoute
|
|
5438
5622
|
): string {
|
|
5439
5623
|
return `session-relocation:${serviceSessionRelocationIdentityKey(value)}:sealed`;
|
|
5440
5624
|
}
|
|
@@ -5444,36 +5628,34 @@ function validateSessionRelocationResponse(
|
|
|
5444
5628
|
response: ServiceSessionRelocationSealed
|
|
5445
5629
|
): void {
|
|
5446
5630
|
const sameCoordinator =
|
|
5447
|
-
request.coordinator.ownerId === response.coordinator.ownerId
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5631
|
+
request.coordinator.ownerId === response.coordinator.ownerId &&
|
|
5632
|
+
request.coordinator.leaseGeneration === response.coordinator.leaseGeneration &&
|
|
5633
|
+
request.coordinator.nodeRid === response.coordinator.nodeRid &&
|
|
5634
|
+
request.coordinator.nodeGeneration === response.coordinator.nodeGeneration &&
|
|
5635
|
+
request.coordinator.expectedAuthorityStoreVersion ===
|
|
5636
|
+
response.coordinator.expectedAuthorityStoreVersion;
|
|
5453
5637
|
const sameSession =
|
|
5454
|
-
request.session.sessionOwnerNodeRid === response.session.sessionOwnerNodeRid
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
&& request.session.sessionRid === response.session.sessionRid
|
|
5461
|
-
&& request.session.bindingGeneration === response.session.bindingGeneration;
|
|
5638
|
+
request.session.sessionOwnerNodeRid === response.session.sessionOwnerNodeRid &&
|
|
5639
|
+
request.session.sessionOwnerNodeGeneration === response.session.sessionOwnerNodeGeneration &&
|
|
5640
|
+
request.session.sessionOwnerId === response.session.sessionOwnerId &&
|
|
5641
|
+
request.session.sessionOwnerLeaseGeneration === response.session.sessionOwnerLeaseGeneration &&
|
|
5642
|
+
request.session.sessionRid === response.session.sessionRid &&
|
|
5643
|
+
request.session.bindingGeneration === response.session.bindingGeneration;
|
|
5462
5644
|
if (
|
|
5463
|
-
request.relocation.high !== response.relocation.high
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5645
|
+
request.relocation.high !== response.relocation.high ||
|
|
5646
|
+
request.relocation.low !== response.relocation.low ||
|
|
5647
|
+
!sameCoordinator ||
|
|
5648
|
+
!sameSession
|
|
5467
5649
|
) {
|
|
5468
5650
|
throw new ServiceWireProtocolError('Session relocation ACK changed its exact fence.');
|
|
5469
5651
|
}
|
|
5470
5652
|
if (
|
|
5471
|
-
request.actor.actor.actorId !== response.actor.actor.actorId
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5653
|
+
request.actor.actor.actorId !== response.actor.actor.actorId ||
|
|
5654
|
+
request.actor.actor.generation !== response.actor.actor.generation ||
|
|
5655
|
+
request.actor.actor.nodeRid !== response.actor.actor.nodeRid ||
|
|
5656
|
+
request.actor.targetNodeGeneration !== response.actor.targetNodeGeneration ||
|
|
5657
|
+
request.actor.authorityOwnerGeneration !== response.actor.authorityOwnerGeneration ||
|
|
5658
|
+
request.actor.ownerLeaseGeneration !== response.actor.ownerLeaseGeneration
|
|
5477
5659
|
) {
|
|
5478
5660
|
throw new ServiceWireProtocolError('Command 43 ACK does not echo command 42.');
|
|
5479
5661
|
}
|
|
@@ -5507,33 +5689,41 @@ function replyRelayPendingKey(
|
|
|
5507
5689
|
}
|
|
5508
5690
|
|
|
5509
5691
|
function isServiceWireCommand(payload: Uint8Array, command: number): boolean {
|
|
5510
|
-
return
|
|
5511
|
-
|
|
5512
|
-
|
|
5692
|
+
return (
|
|
5693
|
+
payload.byteLength >= 5 &&
|
|
5694
|
+
payload[0] === 0x5a &&
|
|
5695
|
+
payload[1] === 0x4d &&
|
|
5696
|
+
payload[2] === 1 &&
|
|
5697
|
+
payload[3] === command
|
|
5698
|
+
);
|
|
5513
5699
|
}
|
|
5514
5700
|
|
|
5515
5701
|
export function encodeHandoffQueuedMessages(
|
|
5516
5702
|
backlog: readonly ZLinkActorHandoffPacket[]
|
|
5517
5703
|
): readonly ServiceRelocationQueuedMessage[] {
|
|
5518
|
-
return backlog.map(packet => ({
|
|
5704
|
+
return backlog.map((packet) => ({
|
|
5519
5705
|
sequence: BigInt(packet.index) + 1n,
|
|
5520
|
-
payload: Buffer.from(
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
messageFollowOrigin
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5706
|
+
payload: Buffer.from(
|
|
5707
|
+
JSON.stringify({
|
|
5708
|
+
...packet,
|
|
5709
|
+
...(packet.messageFollowOrigin === undefined
|
|
5710
|
+
? {}
|
|
5711
|
+
: {
|
|
5712
|
+
messageFollowOrigin: {
|
|
5713
|
+
sourceNodeRid: String(packet.messageFollowOrigin.sourceNodeRid),
|
|
5714
|
+
sourceNodeRidHex: encodeRoutingIdStorageHex(
|
|
5715
|
+
packet.messageFollowOrigin.sourceNodeRid
|
|
5716
|
+
),
|
|
5717
|
+
originalOperation: {
|
|
5718
|
+
high: packet.messageFollowOrigin.originalOperation.high.toString(),
|
|
5719
|
+
low: packet.messageFollowOrigin.originalOperation.low.toString()
|
|
5720
|
+
},
|
|
5721
|
+
originalReplyRouteId: packet.messageFollowOrigin.originalReplyRouteId.toString()
|
|
5722
|
+
}
|
|
5723
|
+
})
|
|
5724
|
+
}),
|
|
5725
|
+
'utf8'
|
|
5726
|
+
)
|
|
5537
5727
|
}));
|
|
5538
5728
|
}
|
|
5539
5729
|
|
|
@@ -5545,10 +5735,10 @@ function decodeQueuedMessageFollowOrigin(value: unknown) {
|
|
|
5545
5735
|
const origin = value as Record<string, unknown>;
|
|
5546
5736
|
const operation = origin.originalOperation;
|
|
5547
5737
|
if (
|
|
5548
|
-
typeof origin.sourceNodeRid !== 'string'
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5738
|
+
typeof origin.sourceNodeRid !== 'string' ||
|
|
5739
|
+
origin.sourceNodeRid.length === 0 ||
|
|
5740
|
+
typeof operation !== 'object' ||
|
|
5741
|
+
operation === null
|
|
5552
5742
|
) {
|
|
5553
5743
|
throw new TypeError('Relocation Actor Message Follow origin is invalid.');
|
|
5554
5744
|
}
|
|
@@ -5561,10 +5751,7 @@ function decodeQueuedMessageFollowOrigin(value: unknown) {
|
|
|
5561
5751
|
return {
|
|
5562
5752
|
sourceNodeRid: decodeRoutingId(origin.sourceNodeRid, origin.sourceNodeRidHex),
|
|
5563
5753
|
originalOperation: { high, low },
|
|
5564
|
-
originalReplyRouteId: queuedMessageU64(
|
|
5565
|
-
origin.originalReplyRouteId,
|
|
5566
|
-
'originalReplyRouteId'
|
|
5567
|
-
)
|
|
5754
|
+
originalReplyRouteId: queuedMessageU64(origin.originalReplyRouteId, 'originalReplyRouteId')
|
|
5568
5755
|
};
|
|
5569
5756
|
}
|
|
5570
5757
|
|
|
@@ -5579,7 +5766,9 @@ function queuedMessageU64(value: unknown, field: string): bigint {
|
|
|
5579
5766
|
return result;
|
|
5580
5767
|
}
|
|
5581
5768
|
|
|
5582
|
-
function toServiceTimer(
|
|
5769
|
+
function toServiceTimer(
|
|
5770
|
+
value: import('../spots/spot-timer').ZLinkTimerRelocationState
|
|
5771
|
+
): ServiceRelocationTimer {
|
|
5583
5772
|
return {
|
|
5584
5773
|
timerId: value.name,
|
|
5585
5774
|
handlerType: value.handlerType,
|
|
@@ -5603,8 +5792,8 @@ function toServiceTimer(value: import('../spots/spot-timer').ZLinkTimerRelocatio
|
|
|
5603
5792
|
*/
|
|
5604
5793
|
function encodePrepareSideband(envelope: ServiceRelocationEnvelope): readonly Buffer[] {
|
|
5605
5794
|
return envelope.participants
|
|
5606
|
-
.filter(participant => participant.boundSessionState.byteLength !== 0)
|
|
5607
|
-
.map(participant => {
|
|
5795
|
+
.filter((participant) => participant.boundSessionState.byteLength !== 0)
|
|
5796
|
+
.map((participant) => {
|
|
5608
5797
|
const key = Buffer.from(participant.key, 'utf8');
|
|
5609
5798
|
const payload = Buffer.from(participant.boundSessionState);
|
|
5610
5799
|
if (key.byteLength === 0 || key.byteLength > 0xffff || payload.byteLength > 0xffff_ffff) {
|
|
@@ -5619,15 +5808,21 @@ function encodePrepareSideband(envelope: ServiceRelocationEnvelope): readonly Bu
|
|
|
5619
5808
|
});
|
|
5620
5809
|
}
|
|
5621
5810
|
|
|
5622
|
-
function decodePrepareSideband(frames: readonly Uint8Array[]):
|
|
5623
|
-
readonly
|
|
5624
|
-
|
|
5625
|
-
|
|
5811
|
+
function decodePrepareSideband(frames: readonly Uint8Array[]):
|
|
5812
|
+
| readonly {
|
|
5813
|
+
readonly participantKey: string;
|
|
5814
|
+
readonly payload: Buffer;
|
|
5815
|
+
}[]
|
|
5816
|
+
| undefined {
|
|
5626
5817
|
const result: Array<{ readonly participantKey: string; readonly payload: Buffer }> = [];
|
|
5627
5818
|
const keys = new Set<string>();
|
|
5628
5819
|
for (const frame of frames) {
|
|
5629
5820
|
const bytes = Buffer.from(frame);
|
|
5630
|
-
if (
|
|
5821
|
+
if (
|
|
5822
|
+
bytes.byteLength < 11 ||
|
|
5823
|
+
bytes.subarray(0, 4).toString('ascii') !== 'ZLNI' ||
|
|
5824
|
+
bytes[4] !== 1
|
|
5825
|
+
) {
|
|
5631
5826
|
return undefined;
|
|
5632
5827
|
}
|
|
5633
5828
|
const keyLength = bytes.readUInt16BE(5);
|
|
@@ -5646,8 +5841,10 @@ function attachPrepareBoundSessions(
|
|
|
5646
5841
|
sideband: ServiceMaintenanceRelocationPrepare['nodeInternalBoundSessions']
|
|
5647
5842
|
): ServiceRelocationEnvelope {
|
|
5648
5843
|
if (sideband === undefined || sideband.length === 0) return envelope;
|
|
5649
|
-
const byKey = new Map(
|
|
5650
|
-
|
|
5844
|
+
const byKey = new Map(
|
|
5845
|
+
sideband.map((value) => [value.participantKey, Buffer.from(value.payload)])
|
|
5846
|
+
);
|
|
5847
|
+
const participants = envelope.participants.map((participant) => {
|
|
5651
5848
|
const payload = byKey.get(participant.key);
|
|
5652
5849
|
if (payload === undefined) return participant;
|
|
5653
5850
|
if (participant.objectKind !== 'actor') {
|
|
@@ -5656,56 +5853,64 @@ function attachPrepareBoundSessions(
|
|
|
5656
5853
|
byKey.delete(participant.key);
|
|
5657
5854
|
return { ...participant, boundSessionState: payload };
|
|
5658
5855
|
});
|
|
5659
|
-
if (byKey.size !== 0)
|
|
5856
|
+
if (byKey.size !== 0)
|
|
5857
|
+
throw new TypeError('Bound-session relocation sideband names an unknown participant.');
|
|
5660
5858
|
return { ...envelope, participants };
|
|
5661
5859
|
}
|
|
5662
5860
|
|
|
5663
5861
|
function encodeActorSession(target: ZLinkRemoteBoundSessionTarget | undefined): Buffer {
|
|
5664
5862
|
if (target === undefined) return Buffer.alloc(0);
|
|
5665
|
-
return Buffer.from(
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
:
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5863
|
+
return Buffer.from(
|
|
5864
|
+
JSON.stringify({
|
|
5865
|
+
version: 1,
|
|
5866
|
+
routerChannelId: target.routerChannelId,
|
|
5867
|
+
targetNodeRid: String(target.targetNodeRid),
|
|
5868
|
+
spotId: String(target.spotId),
|
|
5869
|
+
sessionNodeRid:
|
|
5870
|
+
target.sessionNodeRid === undefined ? undefined : String(target.sessionNodeRid),
|
|
5871
|
+
sessionRid: target.sessionRid === undefined ? undefined : String(target.sessionRid),
|
|
5872
|
+
bindingGeneration: target.bindingGeneration?.toString(),
|
|
5873
|
+
previousAuthorityOwnerGeneration: target.previousAuthorityOwnerGeneration?.toString(),
|
|
5874
|
+
previousOwnerLeaseGeneration: target.previousOwnerLeaseGeneration?.toString(),
|
|
5875
|
+
relocationSealId: target.relocationSealId,
|
|
5876
|
+
serviceWireRelocation:
|
|
5877
|
+
target.serviceWireRelocation === undefined
|
|
5878
|
+
? undefined
|
|
5879
|
+
: {
|
|
5880
|
+
relocationHigh: target.serviceWireRelocation.relocation.high.toString(),
|
|
5881
|
+
relocationLow: target.serviceWireRelocation.relocation.low.toString(),
|
|
5882
|
+
coordinatorOwnerId: target.serviceWireRelocation.coordinator.ownerId,
|
|
5883
|
+
coordinatorLeaseGeneration:
|
|
5884
|
+
target.serviceWireRelocation.coordinator.leaseGeneration.toString(),
|
|
5885
|
+
coordinatorNodeRid: target.serviceWireRelocation.coordinator.nodeRid,
|
|
5886
|
+
coordinatorNodeGeneration:
|
|
5887
|
+
target.serviceWireRelocation.coordinator.nodeGeneration.toString(),
|
|
5888
|
+
expectedAuthorityStoreVersion:
|
|
5889
|
+
target.serviceWireRelocation.coordinator.expectedAuthorityStoreVersion,
|
|
5890
|
+
sessionOwnerNodeRid: target.serviceWireRelocation.session.sessionOwnerNodeRid,
|
|
5891
|
+
sessionOwnerNodeGeneration:
|
|
5892
|
+
target.serviceWireRelocation.session.sessionOwnerNodeGeneration.toString(),
|
|
5893
|
+
sessionOwnerId: target.serviceWireRelocation.session.sessionOwnerId,
|
|
5894
|
+
sessionOwnerLeaseGeneration:
|
|
5895
|
+
target.serviceWireRelocation.session.sessionOwnerLeaseGeneration.toString(),
|
|
5896
|
+
sessionRid: target.serviceWireRelocation.session.sessionRid,
|
|
5897
|
+
bindingGeneration: target.serviceWireRelocation.session.bindingGeneration.toString()
|
|
5898
|
+
}
|
|
5899
|
+
}),
|
|
5900
|
+
'utf8'
|
|
5901
|
+
);
|
|
5700
5902
|
}
|
|
5701
5903
|
|
|
5702
5904
|
function decodeActorSession(payload: Uint8Array): ZLinkRemoteBoundSessionTarget {
|
|
5703
5905
|
const value = JSON.parse(Buffer.from(payload).toString('utf8')) as Record<string, unknown>;
|
|
5704
|
-
const optionalBigInt = (field: string) =>
|
|
5705
|
-
? BigInt(value[field] as string)
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5906
|
+
const optionalBigInt = (field: string) =>
|
|
5907
|
+
typeof value[field] === 'string' ? BigInt(value[field] as string) : undefined;
|
|
5908
|
+
if (
|
|
5909
|
+
value.version !== 1 ||
|
|
5910
|
+
typeof value.routerChannelId !== 'string' ||
|
|
5911
|
+
typeof value.targetNodeRid !== 'string' ||
|
|
5912
|
+
typeof value.spotId !== 'string'
|
|
5913
|
+
) {
|
|
5709
5914
|
throw new TypeError('Actor relocation Session journal is invalid.');
|
|
5710
5915
|
}
|
|
5711
5916
|
const serviceWire = decodeActorSessionServiceWireFence(value.serviceWireRelocation);
|
|
@@ -5713,12 +5918,22 @@ function decodeActorSession(payload: Uint8Array): ZLinkRemoteBoundSessionTarget
|
|
|
5713
5918
|
routerChannelId: value.routerChannelId,
|
|
5714
5919
|
targetNodeRid: value.targetNodeRid as RoutingId,
|
|
5715
5920
|
spotId: value.spotId as RoutingId,
|
|
5716
|
-
...(typeof value.sessionNodeRid === 'string'
|
|
5921
|
+
...(typeof value.sessionNodeRid === 'string'
|
|
5922
|
+
? { sessionNodeRid: value.sessionNodeRid as RoutingId }
|
|
5923
|
+
: {}),
|
|
5717
5924
|
...(typeof value.sessionRid === 'string' ? { sessionRid: value.sessionRid as RoutingId } : {}),
|
|
5718
|
-
...(optionalBigInt('bindingGeneration') === undefined
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
...(
|
|
5925
|
+
...(optionalBigInt('bindingGeneration') === undefined
|
|
5926
|
+
? {}
|
|
5927
|
+
: { bindingGeneration: optionalBigInt('bindingGeneration') }),
|
|
5928
|
+
...(optionalBigInt('previousAuthorityOwnerGeneration') === undefined
|
|
5929
|
+
? {}
|
|
5930
|
+
: { previousAuthorityOwnerGeneration: optionalBigInt('previousAuthorityOwnerGeneration') }),
|
|
5931
|
+
...(optionalBigInt('previousOwnerLeaseGeneration') === undefined
|
|
5932
|
+
? {}
|
|
5933
|
+
: { previousOwnerLeaseGeneration: optionalBigInt('previousOwnerLeaseGeneration') }),
|
|
5934
|
+
...(typeof value.relocationSealId === 'string'
|
|
5935
|
+
? { relocationSealId: value.relocationSealId }
|
|
5936
|
+
: {}),
|
|
5722
5937
|
...(serviceWire === undefined ? {} : { serviceWireRelocation: serviceWire })
|
|
5723
5938
|
};
|
|
5724
5939
|
}
|
|
@@ -5782,32 +5997,40 @@ function encodeMembershipMutation(
|
|
|
5782
5997
|
memberships: readonly ServiceRelocationMembership[],
|
|
5783
5998
|
participantKey: string
|
|
5784
5999
|
): Buffer {
|
|
5785
|
-
return Buffer.from(
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
6000
|
+
return Buffer.from(
|
|
6001
|
+
JSON.stringify(
|
|
6002
|
+
memberships
|
|
6003
|
+
.filter((value) => value.actorKey === participantKey || value.spotKey === participantKey)
|
|
6004
|
+
.map((value) => ({
|
|
6005
|
+
...value,
|
|
6006
|
+
spotObjectGeneration: value.spotObjectGeneration.toString(),
|
|
6007
|
+
membershipEpoch: value.membershipEpoch.toString()
|
|
6008
|
+
}))
|
|
6009
|
+
),
|
|
6010
|
+
'utf8'
|
|
6011
|
+
);
|
|
5791
6012
|
}
|
|
5792
6013
|
|
|
5793
|
-
function addCapacity(
|
|
5794
|
-
left: ZLinkCapacityVector,
|
|
5795
|
-
right: ZLinkCapacityVector
|
|
5796
|
-
): ZLinkCapacityVector {
|
|
6014
|
+
function addCapacity(left: ZLinkCapacityVector, right: ZLinkCapacityVector): ZLinkCapacityVector {
|
|
5797
6015
|
const spotType = left.spotType ?? right.spotType;
|
|
5798
|
-
if (
|
|
5799
|
-
|
|
5800
|
-
|
|
6016
|
+
if (
|
|
6017
|
+
left.spotType !== undefined &&
|
|
6018
|
+
right.spotType !== undefined &&
|
|
6019
|
+
(left.spotType.objectKind !== right.spotType.objectKind ||
|
|
6020
|
+
left.spotType.stableType !== right.spotType.stableType)
|
|
6021
|
+
) {
|
|
5801
6022
|
throw new Error('Relocation aggregate contains more than one Spot stable type.');
|
|
5802
6023
|
}
|
|
5803
6024
|
return {
|
|
5804
6025
|
actors: left.actors + right.actors,
|
|
5805
6026
|
spots: left.spots + right.spots,
|
|
5806
|
-
...(spotType === undefined
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
6027
|
+
...(spotType === undefined
|
|
6028
|
+
? {}
|
|
6029
|
+
: {
|
|
6030
|
+
spotType: {
|
|
6031
|
+
...spotType,
|
|
6032
|
+
count: (left.spotType?.count ?? 0) + (right.spotType?.count ?? 0)
|
|
6033
|
+
}
|
|
6034
|
+
})
|
|
5812
6035
|
};
|
|
5813
6036
|
}
|