@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
|
@@ -8,11 +8,13 @@ import {
|
|
|
8
8
|
internalFrameworkWireReply,
|
|
9
9
|
translateWireReplyDecodeError
|
|
10
10
|
} from '../framework-errors-internal';
|
|
11
|
+
import { RequestResult, SubmitResult, isZLinkBackendResultError } from '../backend/runtime-values';
|
|
12
|
+
import type { ZLinkDispatchErrorReporter } from '../channels/dispatch-error-reporter';
|
|
11
13
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} from '
|
|
14
|
+
ZLinkRuntimeDispatchErrorAction as ZLinkDispatchErrorAction,
|
|
15
|
+
ZLinkDispatchErrorSurface,
|
|
16
|
+
ZLinkDispatchMessageKind
|
|
17
|
+
} from '../../contracts/Dispatch/ZLinkDispatchOptions';
|
|
16
18
|
import type {
|
|
17
19
|
RawServiceIngressRecord,
|
|
18
20
|
RawServiceMeshRuntime,
|
|
@@ -84,10 +86,7 @@ import {
|
|
|
84
86
|
encodeActorJoin28,
|
|
85
87
|
type ActorJoin28
|
|
86
88
|
} from '../protocol/service_wire_pilot_codec.generated';
|
|
87
|
-
import {
|
|
88
|
-
canonicalActorJoinHandoffId,
|
|
89
|
-
routingIdBytes
|
|
90
|
-
} from './actor-join-recovery-codec';
|
|
89
|
+
import { canonicalActorJoinHandoffId, routingIdBytes } from './actor-join-recovery-codec';
|
|
91
90
|
|
|
92
91
|
import {
|
|
93
92
|
decodeApplicationPayload,
|
|
@@ -95,14 +94,10 @@ import {
|
|
|
95
94
|
type ServiceApplicationPayload,
|
|
96
95
|
ServiceWireProtocolError
|
|
97
96
|
} from './service-wire-m6a-codec';
|
|
98
|
-
import type {
|
|
99
|
-
ServiceInstanceActivationRecoveryEnvelope
|
|
100
|
-
} from './service-instance-activation-recovery-codec';
|
|
97
|
+
import type { ServiceInstanceActivationRecoveryEnvelope } from './service-instance-activation-recovery-codec';
|
|
101
98
|
import { validateServiceMetadataFrame } from './service-metadata-codec';
|
|
102
99
|
import type { ServiceSessionBindingIngressPort } from './service-session-binding-ingress-port';
|
|
103
|
-
import {
|
|
104
|
-
ZLinkFrameworkException
|
|
105
|
-
} from '../../contracts';
|
|
100
|
+
import { ZLinkFrameworkException } from '../../contracts';
|
|
106
101
|
|
|
107
102
|
const ACTOR_ROUTE_STALE = 21;
|
|
108
103
|
const SPOT_MOVING = 34;
|
|
@@ -123,10 +118,7 @@ export interface ServiceBoundSessionSendFence {
|
|
|
123
118
|
|
|
124
119
|
interface ServiceSpotMessageFollowRecord {
|
|
125
120
|
readonly ingress: RawServiceIngressRecord;
|
|
126
|
-
readonly wire: Extract<
|
|
127
|
-
ServiceStatefulWireRecord,
|
|
128
|
-
{ readonly kind: 'spotSend' | 'spotRequest' }
|
|
129
|
-
>;
|
|
121
|
+
readonly wire: Extract<ServiceStatefulWireRecord, { readonly kind: 'spotSend' | 'spotRequest' }>;
|
|
130
122
|
readonly bytes: number;
|
|
131
123
|
}
|
|
132
124
|
|
|
@@ -294,7 +286,10 @@ export interface ServiceInstanceActivationAuthority {
|
|
|
294
286
|
reservation: ServiceInstanceActivationReservation,
|
|
295
287
|
spot: ServiceSpotState
|
|
296
288
|
): { readonly kind: 'committed' | 'lost'; readonly route: ServiceInstanceRouteFence };
|
|
297
|
-
abort(
|
|
289
|
+
abort(
|
|
290
|
+
target: ServiceInstanceActivationTarget,
|
|
291
|
+
reservation: ServiceInstanceActivationReservation
|
|
292
|
+
): void;
|
|
298
293
|
}
|
|
299
294
|
|
|
300
295
|
/**
|
|
@@ -344,9 +339,18 @@ export interface ServiceUserSpotOperationResult {
|
|
|
344
339
|
}
|
|
345
340
|
|
|
346
341
|
export interface ServiceUserSpotOperationHandler {
|
|
347
|
-
create(
|
|
348
|
-
|
|
349
|
-
|
|
342
|
+
create(
|
|
343
|
+
record: ServiceUserSpotCreateRecord,
|
|
344
|
+
signal: AbortSignal
|
|
345
|
+
): Promise<ServiceUserSpotOperationResult>;
|
|
346
|
+
close(
|
|
347
|
+
record: ServiceUserSpotCloseRecord,
|
|
348
|
+
signal: AbortSignal
|
|
349
|
+
): Promise<ServiceUserSpotOperationResult>;
|
|
350
|
+
createActor?(
|
|
351
|
+
record: ServiceActorCreateRecord,
|
|
352
|
+
signal: AbortSignal
|
|
353
|
+
): Promise<ServiceUserSpotOperationResult>;
|
|
350
354
|
}
|
|
351
355
|
|
|
352
356
|
/**
|
|
@@ -403,16 +407,24 @@ export class ServiceStatefulRuntime {
|
|
|
403
407
|
readonly kind: 'spot_multicast' | 'actor_control' | 'actor_binding';
|
|
404
408
|
readonly owner: string;
|
|
405
409
|
}) => void;
|
|
406
|
-
private
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
410
|
+
private dispatchErrors?: ZLinkDispatchErrorReporter;
|
|
411
|
+
private dispatchErrorMeshName?: string;
|
|
412
|
+
private readonly admittedUserSpotOperations = new Map<
|
|
413
|
+
string,
|
|
414
|
+
{
|
|
415
|
+
readonly request: string;
|
|
416
|
+
readonly replayExpiresAtMs: number;
|
|
417
|
+
readonly result: Promise<ServiceUserSpotOperationResult>;
|
|
418
|
+
settled: boolean;
|
|
419
|
+
}
|
|
420
|
+
>();
|
|
421
|
+
private readonly pendingInstanceActivations = new Map<
|
|
422
|
+
string,
|
|
423
|
+
Promise<{
|
|
424
|
+
readonly spot: ServiceSpotState;
|
|
425
|
+
readonly route: ServiceInstanceRouteFence;
|
|
426
|
+
}>
|
|
427
|
+
>();
|
|
416
428
|
private closed = false;
|
|
417
429
|
|
|
418
430
|
constructor(
|
|
@@ -422,17 +434,28 @@ export class ServiceStatefulRuntime {
|
|
|
422
434
|
) {
|
|
423
435
|
this.registry = new ServiceStatefulRegistry(nodeRid, nodeGeneration);
|
|
424
436
|
this.registry.createEntrySpot(nodeRid);
|
|
425
|
-
raw.setServiceIngress(record => this.ingress(record));
|
|
437
|
+
raw.setServiceIngress((record) => this.ingress(record));
|
|
426
438
|
}
|
|
427
439
|
|
|
428
|
-
setMailboxDropHandler(
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
440
|
+
setMailboxDropHandler(
|
|
441
|
+
handler: (record: {
|
|
442
|
+
readonly kind: 'spot_multicast' | 'actor_control' | 'actor_binding';
|
|
443
|
+
readonly owner: string;
|
|
444
|
+
}) => void
|
|
445
|
+
): void {
|
|
432
446
|
this.mailboxDropHandler = handler;
|
|
433
447
|
}
|
|
434
448
|
|
|
435
|
-
|
|
449
|
+
setDispatchErrorReporter(reporter: ZLinkDispatchErrorReporter, meshName: string): void {
|
|
450
|
+
this.dispatchErrors = reporter;
|
|
451
|
+
this.dispatchErrorMeshName = meshName;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
createSpot(
|
|
455
|
+
routingId?: string,
|
|
456
|
+
kind: 'user' | 'instance' = 'user',
|
|
457
|
+
stableType = kind
|
|
458
|
+
): ServiceSpotState {
|
|
436
459
|
this.requireOpen();
|
|
437
460
|
return this.registry.createSpot(
|
|
438
461
|
routingId ?? `spot-${this.nodeRid}-${this.nextSpotId++}`,
|
|
@@ -514,17 +537,17 @@ export class ServiceStatefulRuntime {
|
|
|
514
537
|
this.requireOpen();
|
|
515
538
|
const actor = this.registry.actor(actorRef.actorId);
|
|
516
539
|
if (
|
|
517
|
-
actor === undefined
|
|
518
|
-
|
|
519
|
-
|
|
540
|
+
actor === undefined ||
|
|
541
|
+
actor.ref.generation !== actorRef.generation ||
|
|
542
|
+
actor.ref.nodeRid !== actorRef.nodeRid
|
|
520
543
|
) {
|
|
521
544
|
throw new ServiceStaleGenerationError('actor', actorRef.actorId);
|
|
522
545
|
}
|
|
523
546
|
const current = this.registry.binding(actor.ref);
|
|
524
547
|
if (
|
|
525
|
-
current?.bindingGeneration === bindingGeneration
|
|
526
|
-
|
|
527
|
-
|
|
548
|
+
current?.bindingGeneration === bindingGeneration &&
|
|
549
|
+
current.sessionOwnerNodeRid === sessionNodeRid &&
|
|
550
|
+
current.sessionRid === sessionRid
|
|
528
551
|
) {
|
|
529
552
|
return;
|
|
530
553
|
}
|
|
@@ -554,14 +577,15 @@ export class ServiceStatefulRuntime {
|
|
|
554
577
|
}
|
|
555
578
|
if (current.ref.generation === attempt) {
|
|
556
579
|
return {
|
|
557
|
-
spot:
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
580
|
+
spot:
|
|
581
|
+
current.authorityOwnerGeneration === authorityOwnerGeneration
|
|
582
|
+
? current
|
|
583
|
+
: this.registry.restoreSpot(
|
|
584
|
+
current.ref,
|
|
585
|
+
'instance',
|
|
586
|
+
instanceType,
|
|
587
|
+
authorityOwnerGeneration
|
|
588
|
+
),
|
|
565
589
|
created: false
|
|
566
590
|
};
|
|
567
591
|
}
|
|
@@ -585,7 +609,10 @@ export class ServiceStatefulRuntime {
|
|
|
585
609
|
route: ServiceInstanceRouteFence,
|
|
586
610
|
expectedCurrentRoute?: ServiceInstanceRouteFence | null
|
|
587
611
|
): void {
|
|
588
|
-
if (
|
|
612
|
+
if (
|
|
613
|
+
route.targetNodeRid !== this.nodeRid ||
|
|
614
|
+
route.targetNodeGeneration !== this.nodeGeneration
|
|
615
|
+
) {
|
|
589
616
|
throw new ServiceStaleGenerationError('spot', route.targetSpotId);
|
|
590
617
|
}
|
|
591
618
|
const current = this.instanceIntents.get(route.targetSpotId);
|
|
@@ -595,7 +622,10 @@ export class ServiceStatefulRuntime {
|
|
|
595
622
|
}
|
|
596
623
|
if (!matchesExpectedInstanceRoute(current.route, expectedCurrentRoute)) return;
|
|
597
624
|
if (current.route.objectGeneration < route.objectGeneration) {
|
|
598
|
-
this.instanceIntents.set(
|
|
625
|
+
this.instanceIntents.set(
|
|
626
|
+
route.targetSpotId,
|
|
627
|
+
Object.freeze({ instanceType, route: { ...route } })
|
|
628
|
+
);
|
|
599
629
|
return;
|
|
600
630
|
}
|
|
601
631
|
if (current.route.objectGeneration > route.objectGeneration) {
|
|
@@ -615,7 +645,10 @@ export class ServiceStatefulRuntime {
|
|
|
615
645
|
} else if (!matchesExpectedInstanceRoute(undefined, expectedCurrentRoute)) {
|
|
616
646
|
return;
|
|
617
647
|
}
|
|
618
|
-
this.instanceIntents.set(
|
|
648
|
+
this.instanceIntents.set(
|
|
649
|
+
route.targetSpotId,
|
|
650
|
+
Object.freeze({ instanceType, route: { ...route } })
|
|
651
|
+
);
|
|
619
652
|
}
|
|
620
653
|
|
|
621
654
|
instanceSpotApplicationTarget(
|
|
@@ -624,10 +657,10 @@ export class ServiceStatefulRuntime {
|
|
|
624
657
|
const intent = this.instanceIntents.get(spotId);
|
|
625
658
|
const spot = this.registry.spot(spotId);
|
|
626
659
|
if (
|
|
627
|
-
intent === undefined
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
660
|
+
intent === undefined ||
|
|
661
|
+
spot?.kind !== 'instance' ||
|
|
662
|
+
spot.state !== 'ready' ||
|
|
663
|
+
spot.ref.generation !== intent.route.objectGeneration
|
|
631
664
|
) {
|
|
632
665
|
return undefined;
|
|
633
666
|
}
|
|
@@ -645,9 +678,9 @@ export class ServiceStatefulRuntime {
|
|
|
645
678
|
): void {
|
|
646
679
|
const current = this.instanceIntents.get(spotId);
|
|
647
680
|
if (
|
|
648
|
-
current?.route.objectGeneration === objectGeneration
|
|
649
|
-
|
|
650
|
-
|
|
681
|
+
current?.route.objectGeneration === objectGeneration &&
|
|
682
|
+
current.route.authorityOwnerGeneration === authorityOwnerGeneration &&
|
|
683
|
+
(storeVersion === undefined || current.route.storeVersion === storeVersion)
|
|
651
684
|
) {
|
|
652
685
|
this.instanceIntents.delete(spotId);
|
|
653
686
|
}
|
|
@@ -656,8 +689,8 @@ export class ServiceStatefulRuntime {
|
|
|
656
689
|
registerInstanceActivationAuthority(authority: ServiceInstanceActivationAuthority): void {
|
|
657
690
|
this.requireOpen();
|
|
658
691
|
if (
|
|
659
|
-
this.asyncInstanceAuthority !== undefined
|
|
660
|
-
|
|
692
|
+
this.asyncInstanceAuthority !== undefined ||
|
|
693
|
+
(this.instanceAuthority !== undefined && this.instanceAuthority !== authority)
|
|
661
694
|
) {
|
|
662
695
|
throw new Error('Instance activation authority is already registered.');
|
|
663
696
|
}
|
|
@@ -669,18 +702,15 @@ export class ServiceStatefulRuntime {
|
|
|
669
702
|
): void {
|
|
670
703
|
this.requireOpen();
|
|
671
704
|
if (
|
|
672
|
-
this.instanceAuthority !== undefined
|
|
673
|
-
|
|
674
|
-
&& this.asyncInstanceAuthority !== authority
|
|
705
|
+
this.instanceAuthority !== undefined ||
|
|
706
|
+
(this.asyncInstanceAuthority !== undefined && this.asyncInstanceAuthority !== authority)
|
|
675
707
|
) {
|
|
676
708
|
throw new Error('Instance activation authority is already registered.');
|
|
677
709
|
}
|
|
678
710
|
this.asyncInstanceAuthority = authority;
|
|
679
711
|
}
|
|
680
712
|
|
|
681
|
-
registerInstanceApplicationLifecycle(
|
|
682
|
-
lifecycle: ServiceInstanceApplicationLifecycle
|
|
683
|
-
): void {
|
|
713
|
+
registerInstanceApplicationLifecycle(lifecycle: ServiceInstanceApplicationLifecycle): void {
|
|
684
714
|
if (this.instanceApplicationLifecycle !== undefined) {
|
|
685
715
|
throw new Error('Instance application lifecycle is already registered.');
|
|
686
716
|
}
|
|
@@ -689,10 +719,7 @@ export class ServiceStatefulRuntime {
|
|
|
689
719
|
|
|
690
720
|
registerUserSpotOperationHandler(handler: ServiceUserSpotOperationHandler): void {
|
|
691
721
|
this.requireOpen();
|
|
692
|
-
if (
|
|
693
|
-
this.userSpotOperationHandler !== undefined
|
|
694
|
-
&& this.userSpotOperationHandler !== handler
|
|
695
|
-
) {
|
|
722
|
+
if (this.userSpotOperationHandler !== undefined && this.userSpotOperationHandler !== handler) {
|
|
696
723
|
throw new Error('User Spot operation handler is already registered.');
|
|
697
724
|
}
|
|
698
725
|
this.userSpotOperationHandler = handler;
|
|
@@ -775,16 +802,19 @@ export class ServiceStatefulRuntime {
|
|
|
775
802
|
expectedCurrentRoute?: ServiceInstanceRouteFence | null
|
|
776
803
|
): Promise<boolean> {
|
|
777
804
|
const target = envelope.target;
|
|
778
|
-
if (!matchesExpectedInstanceRoute(
|
|
779
|
-
this.instanceIntents.get(target.targetSpotId)?.route,
|
|
780
|
-
expectedCurrentRoute
|
|
781
|
-
)) return false;
|
|
782
805
|
if (
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
806
|
+
!matchesExpectedInstanceRoute(
|
|
807
|
+
this.instanceIntents.get(target.targetSpotId)?.route,
|
|
808
|
+
expectedCurrentRoute
|
|
809
|
+
)
|
|
810
|
+
)
|
|
811
|
+
return false;
|
|
812
|
+
if (
|
|
813
|
+
target.targetNodeRid !== this.nodeRid ||
|
|
814
|
+
target.targetNodeGeneration !== this.nodeGeneration ||
|
|
815
|
+
target.targetSpotId !== route.targetSpotId ||
|
|
816
|
+
route.targetNodeRid !== this.nodeRid ||
|
|
817
|
+
route.targetNodeGeneration !== this.nodeGeneration
|
|
788
818
|
) {
|
|
789
819
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
790
820
|
}
|
|
@@ -802,8 +832,9 @@ export class ServiceStatefulRuntime {
|
|
|
802
832
|
expectedCurrentRoute?: ServiceInstanceRouteFence | null
|
|
803
833
|
): Promise<boolean> {
|
|
804
834
|
const target = envelope.target;
|
|
805
|
-
const spot =
|
|
806
|
-
|
|
835
|
+
const spot =
|
|
836
|
+
this.registry.spot(target.targetSpotId) ??
|
|
837
|
+
this.registry.restoreSpot(
|
|
807
838
|
{
|
|
808
839
|
spotId: target.targetSpotId,
|
|
809
840
|
generation: route.objectGeneration
|
|
@@ -813,17 +844,20 @@ export class ServiceStatefulRuntime {
|
|
|
813
844
|
route.authorityOwnerGeneration
|
|
814
845
|
);
|
|
815
846
|
if (
|
|
816
|
-
spot.kind !== 'instance'
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
847
|
+
spot.kind !== 'instance' ||
|
|
848
|
+
spot.stableType !== target.stableType ||
|
|
849
|
+
spot.ref.generation !== route.objectGeneration ||
|
|
850
|
+
spot.authorityOwnerGeneration !== route.authorityOwnerGeneration
|
|
820
851
|
) {
|
|
821
852
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
822
853
|
}
|
|
823
|
-
if (
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
854
|
+
if (
|
|
855
|
+
!matchesExpectedInstanceRoute(
|
|
856
|
+
this.instanceIntents.get(target.targetSpotId)?.route,
|
|
857
|
+
expectedCurrentRoute
|
|
858
|
+
)
|
|
859
|
+
)
|
|
860
|
+
return false;
|
|
827
861
|
this.publishCommittedInstanceRoute(target.stableType, route, expectedCurrentRoute);
|
|
828
862
|
const record = {
|
|
829
863
|
kind: 'instanceSpot' as const,
|
|
@@ -831,9 +865,7 @@ export class ServiceStatefulRuntime {
|
|
|
831
865
|
target,
|
|
832
866
|
sourceNodeGeneration: envelope.sourceNodeGeneration,
|
|
833
867
|
sourceNodeRid: envelope.sourceNodeRid,
|
|
834
|
-
...(envelope.sourceSpotId === undefined
|
|
835
|
-
? {}
|
|
836
|
-
: { sourceSpotId: envelope.sourceSpotId }),
|
|
868
|
+
...(envelope.sourceSpotId === undefined ? {} : { sourceSpotId: envelope.sourceSpotId }),
|
|
837
869
|
operationKind: envelope.operationKind,
|
|
838
870
|
operation: envelope.operation,
|
|
839
871
|
deadlineUnixMs: envelope.deadlineUnixMs,
|
|
@@ -843,23 +875,23 @@ export class ServiceStatefulRuntime {
|
|
|
843
875
|
try {
|
|
844
876
|
const admitted = this.enqueueActivatedInstanceSpot(
|
|
845
877
|
{
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
878
|
+
command: M6bServiceWireCommand.instanceSpot,
|
|
879
|
+
flags: envelope.metadataFrame === undefined ? 0 : M6bServiceWireFlag.metadata,
|
|
880
|
+
sourceRoutingId: envelope.sourceNodeRid,
|
|
881
|
+
parts: [
|
|
882
|
+
encodeInstanceSpotActivationHeader(
|
|
883
|
+
target,
|
|
884
|
+
envelope.sourceNodeGeneration,
|
|
885
|
+
envelope.sourceNodeRid,
|
|
886
|
+
envelope.sourceSpotId,
|
|
887
|
+
envelope.operationKind,
|
|
888
|
+
envelope.operation,
|
|
889
|
+
envelope.deadlineUnixMs,
|
|
890
|
+
envelope.replyRouteId,
|
|
891
|
+
envelope.metadataFrame !== undefined
|
|
892
|
+
)
|
|
893
|
+
],
|
|
894
|
+
applicationJobOwner
|
|
863
895
|
},
|
|
864
896
|
record,
|
|
865
897
|
Buffer.from(envelope.applicationPayloadFrame),
|
|
@@ -887,16 +919,19 @@ export class ServiceStatefulRuntime {
|
|
|
887
919
|
expectedCurrentRoute?: ServiceInstanceRouteFence | null
|
|
888
920
|
): Promise<boolean> {
|
|
889
921
|
const target = envelope.target;
|
|
890
|
-
if (!matchesExpectedInstanceRoute(
|
|
891
|
-
this.instanceIntents.get(target.targetSpotId)?.route,
|
|
892
|
-
expectedCurrentRoute
|
|
893
|
-
)) return false;
|
|
894
922
|
if (
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
923
|
+
!matchesExpectedInstanceRoute(
|
|
924
|
+
this.instanceIntents.get(target.targetSpotId)?.route,
|
|
925
|
+
expectedCurrentRoute
|
|
926
|
+
)
|
|
927
|
+
)
|
|
928
|
+
return false;
|
|
929
|
+
if (
|
|
930
|
+
target.targetNodeRid !== this.nodeRid ||
|
|
931
|
+
target.targetNodeGeneration !== this.nodeGeneration ||
|
|
932
|
+
pending.nodeRid !== this.nodeRid ||
|
|
933
|
+
pending.nodeGeneration !== this.nodeGeneration ||
|
|
934
|
+
pending.meshName !== envelope.targetMeshName
|
|
900
935
|
) {
|
|
901
936
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
902
937
|
}
|
|
@@ -928,10 +963,13 @@ export class ServiceStatefulRuntime {
|
|
|
928
963
|
route: ServiceInstanceRouteFence,
|
|
929
964
|
expectedCurrentRoute?: ServiceInstanceRouteFence | null
|
|
930
965
|
): Promise<ServiceInstanceRouteFence | undefined> {
|
|
931
|
-
if (
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
966
|
+
if (
|
|
967
|
+
!matchesExpectedInstanceRoute(
|
|
968
|
+
this.instanceIntents.get(target.targetSpotId)?.route,
|
|
969
|
+
expectedCurrentRoute
|
|
970
|
+
)
|
|
971
|
+
)
|
|
972
|
+
return undefined;
|
|
935
973
|
const authority = this.asyncInstanceAuthority;
|
|
936
974
|
if (authority === undefined) {
|
|
937
975
|
throw new Error('Async Instance activation authority is not registered.');
|
|
@@ -940,17 +978,17 @@ export class ServiceStatefulRuntime {
|
|
|
940
978
|
const currentRoute = this.instanceIntents.get(target.targetSpotId)?.route;
|
|
941
979
|
if (!matchesExpectedInstanceRoute(currentRoute, expectedCurrentRoute)) return undefined;
|
|
942
980
|
if (currentRoute !== undefined && !sameInstanceRoute(currentRoute, route)) return undefined;
|
|
943
|
-
const authorityReleased =
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
?? false;
|
|
981
|
+
const authorityReleased =
|
|
982
|
+
(await this.instanceApplicationLifecycle?.completeTerminal({
|
|
983
|
+
targetSpotId: target.targetSpotId,
|
|
984
|
+
stableType: target.stableType,
|
|
985
|
+
objectGeneration: route.objectGeneration
|
|
986
|
+
})) ?? false;
|
|
949
987
|
if (!authorityReleased) {
|
|
950
988
|
if (currentRoute === undefined) {
|
|
951
989
|
this.publishCommittedInstanceRoute(target.stableType, released, null);
|
|
952
990
|
} else if (!this.replaceCommittedInstanceRoute(target.stableType, route, released)) {
|
|
953
|
-
|
|
991
|
+
return undefined;
|
|
954
992
|
}
|
|
955
993
|
} else {
|
|
956
994
|
this.forgetClosedInstanceRoute(released);
|
|
@@ -958,14 +996,11 @@ export class ServiceStatefulRuntime {
|
|
|
958
996
|
return released;
|
|
959
997
|
}
|
|
960
998
|
|
|
961
|
-
waitForInstanceApplicationQuiescence(
|
|
962
|
-
spotId: string,
|
|
963
|
-
signal?: AbortSignal
|
|
964
|
-
): Promise<void> {
|
|
999
|
+
waitForInstanceApplicationQuiescence(spotId: string, signal?: AbortSignal): Promise<void> {
|
|
965
1000
|
const key = String(spotId);
|
|
966
1001
|
if (
|
|
967
|
-
(this.pendingInstanceTerminals.get(key) ?? 0) === 0
|
|
968
|
-
|
|
1002
|
+
(this.pendingInstanceTerminals.get(key) ?? 0) === 0 &&
|
|
1003
|
+
(this.pendingInstanceAuthorityTerminals.get(key) ?? 0) === 0
|
|
969
1004
|
) {
|
|
970
1005
|
return Promise.resolve();
|
|
971
1006
|
}
|
|
@@ -989,17 +1024,19 @@ export class ServiceStatefulRuntime {
|
|
|
989
1024
|
});
|
|
990
1025
|
}
|
|
991
1026
|
|
|
992
|
-
|
|
993
1027
|
rememberActorRoute(route: ServiceActorRouteFence): void {
|
|
994
1028
|
if (route.actor.nodeRid.length === 0) {
|
|
995
1029
|
throw new TypeError('Actor route requires a target node.');
|
|
996
1030
|
}
|
|
997
|
-
this.actorRoutes.set(
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1031
|
+
this.actorRoutes.set(
|
|
1032
|
+
actorKey(route.actor),
|
|
1033
|
+
Object.freeze({
|
|
1034
|
+
actor: Object.freeze({ ...route.actor }),
|
|
1035
|
+
targetNodeGeneration: route.targetNodeGeneration,
|
|
1036
|
+
authorityOwnerGeneration: route.authorityOwnerGeneration,
|
|
1037
|
+
ownerLeaseGeneration: route.ownerLeaseGeneration
|
|
1038
|
+
})
|
|
1039
|
+
);
|
|
1003
1040
|
}
|
|
1004
1041
|
|
|
1005
1042
|
rememberSpotRoute(
|
|
@@ -1017,9 +1054,10 @@ export class ServiceStatefulRuntime {
|
|
|
1017
1054
|
return;
|
|
1018
1055
|
}
|
|
1019
1056
|
if (
|
|
1020
|
-
expectedCurrentRoute !== undefined
|
|
1021
|
-
|
|
1022
|
-
)
|
|
1057
|
+
expectedCurrentRoute !== undefined &&
|
|
1058
|
+
!matchesExpectedDirectSpotRoute(current, expectedCurrentRoute)
|
|
1059
|
+
)
|
|
1060
|
+
return;
|
|
1023
1061
|
if (current !== undefined) {
|
|
1024
1062
|
if (current.spot.generation < route.spot.generation) {
|
|
1025
1063
|
const next = freezeDirectSpotRoute(route);
|
|
@@ -1061,9 +1099,9 @@ export class ServiceStatefulRuntime {
|
|
|
1061
1099
|
return await this.raw.sendService(targetNodeRid, [encodeMessageFollowHeader(record)]);
|
|
1062
1100
|
}
|
|
1063
1101
|
|
|
1064
|
-
setMessageFollowHandler(
|
|
1065
|
-
record: import('./service-stateful-wire-codec').ServiceMessageFollowRecord
|
|
1066
|
-
)
|
|
1102
|
+
setMessageFollowHandler(
|
|
1103
|
+
handler: (record: import('./service-stateful-wire-codec').ServiceMessageFollowRecord) => void
|
|
1104
|
+
): void {
|
|
1067
1105
|
this.messageFollowHandler = handler;
|
|
1068
1106
|
}
|
|
1069
1107
|
|
|
@@ -1072,8 +1110,8 @@ export class ServiceStatefulRuntime {
|
|
|
1072
1110
|
): ServiceSpotMessageFollowSeal | undefined {
|
|
1073
1111
|
this.requireOpen();
|
|
1074
1112
|
if (
|
|
1075
|
-
source.targetNodeRid !== this.nodeRid
|
|
1076
|
-
|
|
1113
|
+
source.targetNodeRid !== this.nodeRid ||
|
|
1114
|
+
source.targetNodeGeneration !== this.nodeGeneration
|
|
1077
1115
|
) {
|
|
1078
1116
|
return undefined;
|
|
1079
1117
|
}
|
|
@@ -1125,10 +1163,10 @@ export class ServiceStatefulRuntime {
|
|
|
1125
1163
|
throw new RangeError('Message Follow duration must be a non-negative safe integer.');
|
|
1126
1164
|
}
|
|
1127
1165
|
if (
|
|
1128
|
-
target.spot.spotId !== state.source.spot.spotId
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1166
|
+
target.spot.spotId !== state.source.spot.spotId ||
|
|
1167
|
+
target.spot.generation !== state.source.spot.generation ||
|
|
1168
|
+
target.authorityOwnerGeneration <= state.source.authorityOwnerGeneration ||
|
|
1169
|
+
target.targetNodeRid === this.nodeRid
|
|
1132
1170
|
) {
|
|
1133
1171
|
throw new ServiceStaleGenerationError('spot', state.source.spot.spotId);
|
|
1134
1172
|
}
|
|
@@ -1139,9 +1177,7 @@ export class ServiceStatefulRuntime {
|
|
|
1139
1177
|
state.target
|
|
1140
1178
|
);
|
|
1141
1179
|
this.spotMessageFollowSuppression.retainRoute(state.suppressionFence);
|
|
1142
|
-
state.expiresAtMs = durationMs === 0
|
|
1143
|
-
? Number.MAX_SAFE_INTEGER
|
|
1144
|
-
: performance.now() + durationMs;
|
|
1180
|
+
state.expiresAtMs = durationMs === 0 ? Number.MAX_SAFE_INTEGER : performance.now() + durationMs;
|
|
1145
1181
|
await this.drainSpotMessageFollow(state);
|
|
1146
1182
|
if (durationMs === 0) this.removeSpotMessageFollow(state);
|
|
1147
1183
|
return true;
|
|
@@ -1156,16 +1192,16 @@ export class ServiceStatefulRuntime {
|
|
|
1156
1192
|
const exact = this.spotRoutes.get(exactKey);
|
|
1157
1193
|
const current = this.directSpotRoutes.get(directSpotKey(spot.spotId));
|
|
1158
1194
|
if (
|
|
1159
|
-
exact?.authorityOwnerGeneration === authorityOwnerGeneration
|
|
1160
|
-
|
|
1195
|
+
exact?.authorityOwnerGeneration === authorityOwnerGeneration &&
|
|
1196
|
+
(storeVersion === undefined || exact.storeVersion === storeVersion)
|
|
1161
1197
|
) {
|
|
1162
1198
|
this.spotRoutes.delete(exactKey);
|
|
1163
1199
|
}
|
|
1164
1200
|
const key = directSpotKey(spot.spotId);
|
|
1165
1201
|
if (
|
|
1166
|
-
current?.authorityOwnerGeneration === authorityOwnerGeneration
|
|
1167
|
-
|
|
1168
|
-
|
|
1202
|
+
current?.authorityOwnerGeneration === authorityOwnerGeneration &&
|
|
1203
|
+
current.spot.generation === spot.generation &&
|
|
1204
|
+
(storeVersion === undefined || current.storeVersion === storeVersion)
|
|
1169
1205
|
) {
|
|
1170
1206
|
this.directSpotRoutes.delete(key);
|
|
1171
1207
|
}
|
|
@@ -1204,16 +1240,43 @@ export class ServiceStatefulRuntime {
|
|
|
1204
1240
|
sourceSpotId = this.nodeRid
|
|
1205
1241
|
): Promise<void> {
|
|
1206
1242
|
await this.enqueueLogicalMulticast(channelName, topic, sourceSpotId, payload);
|
|
1207
|
-
const targets = this.raw.topology
|
|
1208
|
-
.
|
|
1209
|
-
|
|
1210
|
-
|
|
1243
|
+
const targets = this.raw.topology
|
|
1244
|
+
.peers()
|
|
1245
|
+
.filter(
|
|
1246
|
+
(peer) =>
|
|
1247
|
+
peer.descriptor.channels.some(
|
|
1248
|
+
(channel) => channel.name === channelName && channel.weight > 0
|
|
1249
|
+
) && this.raw.isPeerRouteReady(peer.descriptor.nodeRoutingId)
|
|
1250
|
+
);
|
|
1211
1251
|
const header = encodeLogicalMulticastHeader(channelName, topic, sourceSpotId);
|
|
1212
1252
|
const payloadFrame = encodeApplicationPayload(payload);
|
|
1253
|
+
const reporter = this.dispatchErrors;
|
|
1254
|
+
const captureFailures = reporter?.captureEnabled() === true;
|
|
1213
1255
|
for (const target of targets) {
|
|
1214
1256
|
// Remote admission ends at the source outbound transport queue. The
|
|
1215
1257
|
// receiver's Spot queue and handler completion are not publish results.
|
|
1216
|
-
await this.raw.sendService(target.descriptor.nodeRoutingId, [
|
|
1258
|
+
const accepted = await this.raw.sendService(target.descriptor.nodeRoutingId, [
|
|
1259
|
+
header,
|
|
1260
|
+
payloadFrame
|
|
1261
|
+
]);
|
|
1262
|
+
if (!accepted) {
|
|
1263
|
+
if (captureFailures) {
|
|
1264
|
+
reporter.report({
|
|
1265
|
+
surface: ZLinkDispatchErrorSurface.SpotRoute,
|
|
1266
|
+
messageKind: ZLinkDispatchMessageKind.Send,
|
|
1267
|
+
reason: serviceSpotPublishFailureReason(
|
|
1268
|
+
undefined,
|
|
1269
|
+
this.raw.isPeerRouteReady(target.descriptor.nodeRoutingId),
|
|
1270
|
+
this.closed
|
|
1271
|
+
),
|
|
1272
|
+
action: ZLinkDispatchErrorAction.Drop,
|
|
1273
|
+
meshName: this.dispatchErrorMeshName,
|
|
1274
|
+
channelName,
|
|
1275
|
+
topic,
|
|
1276
|
+
targetRid: target.descriptor.nodeRoutingId
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1217
1280
|
}
|
|
1218
1281
|
}
|
|
1219
1282
|
|
|
@@ -1327,19 +1390,25 @@ export class ServiceStatefulRuntime {
|
|
|
1327
1390
|
sourceSpotId?: string,
|
|
1328
1391
|
metadataFrame?: Uint8Array
|
|
1329
1392
|
): Promise<number> {
|
|
1330
|
-
return this.submitOneWay(
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1393
|
+
return this.submitOneWay(
|
|
1394
|
+
route.targetNodeRid,
|
|
1395
|
+
instanceOperationParts(
|
|
1396
|
+
[
|
|
1397
|
+
encodeInstanceSpotHeader(
|
|
1398
|
+
route,
|
|
1399
|
+
this.nodeGeneration,
|
|
1400
|
+
this.nodeRid,
|
|
1401
|
+
sourceSpotId,
|
|
1402
|
+
'send',
|
|
1403
|
+
{ high: 0n, low: 0n },
|
|
1404
|
+
undefined,
|
|
1405
|
+
metadataFrame !== undefined
|
|
1406
|
+
),
|
|
1407
|
+
encodeApplicationPayload(payload)
|
|
1408
|
+
],
|
|
1409
|
+
metadataFrame
|
|
1410
|
+
)
|
|
1411
|
+
);
|
|
1343
1412
|
}
|
|
1344
1413
|
|
|
1345
1414
|
async sendToMissingInstanceSpot(
|
|
@@ -1366,20 +1435,26 @@ export class ServiceStatefulRuntime {
|
|
|
1366
1435
|
metadataFrame?: Uint8Array
|
|
1367
1436
|
): Promise<number> {
|
|
1368
1437
|
const operation = { high: this.nodeGeneration, low: this.nextInstanceOperation++ };
|
|
1369
|
-
return await this.submitOneWay(
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1438
|
+
return await this.submitOneWay(
|
|
1439
|
+
target.targetNodeRid,
|
|
1440
|
+
instanceOperationParts(
|
|
1441
|
+
[
|
|
1442
|
+
encodeInstanceSpotActivationHeader(
|
|
1443
|
+
target,
|
|
1444
|
+
this.nodeGeneration,
|
|
1445
|
+
this.nodeRid,
|
|
1446
|
+
sourceSpotId,
|
|
1447
|
+
'send',
|
|
1448
|
+
operation,
|
|
1449
|
+
deadlineUnixMs,
|
|
1450
|
+
undefined,
|
|
1451
|
+
metadataFrame !== undefined
|
|
1452
|
+
),
|
|
1453
|
+
payloadFrame
|
|
1454
|
+
],
|
|
1455
|
+
metadataFrame
|
|
1456
|
+
)
|
|
1457
|
+
);
|
|
1383
1458
|
}
|
|
1384
1459
|
|
|
1385
1460
|
requestToInstanceSpot(
|
|
@@ -1393,19 +1468,22 @@ export class ServiceStatefulRuntime {
|
|
|
1393
1468
|
this.submitRequest(
|
|
1394
1469
|
pending,
|
|
1395
1470
|
route.targetNodeRid,
|
|
1396
|
-
instanceOperationParts(
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1471
|
+
instanceOperationParts(
|
|
1472
|
+
[
|
|
1473
|
+
encodeInstanceSpotHeader(
|
|
1474
|
+
route,
|
|
1475
|
+
this.nodeGeneration,
|
|
1476
|
+
this.nodeRid,
|
|
1477
|
+
sourceSpotId,
|
|
1478
|
+
'request',
|
|
1479
|
+
{ high: 2n, low: pending.id },
|
|
1480
|
+
pending.id,
|
|
1481
|
+
metadataFrame !== undefined
|
|
1482
|
+
),
|
|
1483
|
+
encodeApplicationPayload(payload)
|
|
1484
|
+
],
|
|
1485
|
+
metadataFrame
|
|
1486
|
+
),
|
|
1409
1487
|
timeoutMs,
|
|
1410
1488
|
'instanceSpotRequest'
|
|
1411
1489
|
);
|
|
@@ -1442,20 +1520,23 @@ export class ServiceStatefulRuntime {
|
|
|
1442
1520
|
this.submitRequest(
|
|
1443
1521
|
pending,
|
|
1444
1522
|
target.targetNodeRid,
|
|
1445
|
-
instanceOperationParts(
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1523
|
+
instanceOperationParts(
|
|
1524
|
+
[
|
|
1525
|
+
encodeInstanceSpotActivationHeader(
|
|
1526
|
+
target,
|
|
1527
|
+
this.nodeGeneration,
|
|
1528
|
+
this.nodeRid,
|
|
1529
|
+
sourceSpotId,
|
|
1530
|
+
'request',
|
|
1531
|
+
{ high: this.nodeGeneration, low: pending.id },
|
|
1532
|
+
deadlineUnixMs,
|
|
1533
|
+
pending.id,
|
|
1534
|
+
metadataFrame !== undefined
|
|
1535
|
+
),
|
|
1536
|
+
payloadFrame
|
|
1537
|
+
],
|
|
1538
|
+
metadataFrame
|
|
1539
|
+
),
|
|
1459
1540
|
timeoutMs,
|
|
1460
1541
|
'instanceSpotRequest'
|
|
1461
1542
|
);
|
|
@@ -1506,19 +1587,15 @@ export class ServiceStatefulRuntime {
|
|
|
1506
1587
|
timeoutMs: number
|
|
1507
1588
|
): ServiceStatefulPendingOperation {
|
|
1508
1589
|
const deadlineMs = performance.now() + timeoutMs;
|
|
1509
|
-
const pending = this.operations.register(
|
|
1590
|
+
const pending = this.operations.register(
|
|
1591
|
+
timeoutMs,
|
|
1592
|
+
targetNodeRid === this.nodeRid ? 'registry' : 'sender'
|
|
1593
|
+
);
|
|
1510
1594
|
const target = this.trySpotFence(targetNodeRid, {
|
|
1511
1595
|
...targetSpot,
|
|
1512
1596
|
generation: targetSpotGeneration
|
|
1513
1597
|
});
|
|
1514
|
-
this.submitActorJoin(
|
|
1515
|
-
pending,
|
|
1516
|
-
actor,
|
|
1517
|
-
targetNodeRid,
|
|
1518
|
-
target,
|
|
1519
|
-
payload,
|
|
1520
|
-
deadlineMs
|
|
1521
|
-
);
|
|
1598
|
+
this.submitActorJoin(pending, actor, targetNodeRid, target, payload, deadlineMs);
|
|
1522
1599
|
return pending;
|
|
1523
1600
|
}
|
|
1524
1601
|
|
|
@@ -1533,20 +1610,19 @@ export class ServiceStatefulRuntime {
|
|
|
1533
1610
|
timeoutMs: number
|
|
1534
1611
|
): ServiceStatefulPendingOperation {
|
|
1535
1612
|
const deadlineMs = performance.now() + timeoutMs;
|
|
1536
|
-
const pending = this.operations.register(
|
|
1613
|
+
const pending = this.operations.register(
|
|
1614
|
+
timeoutMs,
|
|
1615
|
+
targetNodeRid === this.nodeRid ? 'registry' : 'sender'
|
|
1616
|
+
);
|
|
1537
1617
|
const target = this.trySpotFence(targetNodeRid, {
|
|
1538
1618
|
...targetSpot,
|
|
1539
1619
|
generation: targetSpotGeneration
|
|
1540
1620
|
});
|
|
1541
|
-
this.submitActorJoin(
|
|
1542
|
-
pending,
|
|
1543
|
-
actor,
|
|
1544
|
-
targetNodeRid,
|
|
1545
|
-
target,
|
|
1621
|
+
this.submitActorJoin(pending, actor, targetNodeRid, target, request, deadlineMs, {
|
|
1546
1622
|
request,
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
);
|
|
1623
|
+
actorFence,
|
|
1624
|
+
local
|
|
1625
|
+
});
|
|
1550
1626
|
return pending;
|
|
1551
1627
|
}
|
|
1552
1628
|
|
|
@@ -1557,7 +1633,10 @@ export class ServiceStatefulRuntime {
|
|
|
1557
1633
|
timeoutMs: number
|
|
1558
1634
|
): ServiceStatefulPendingOperation {
|
|
1559
1635
|
const deadlineMs = performance.now() + timeoutMs;
|
|
1560
|
-
const pending = this.operations.register(
|
|
1636
|
+
const pending = this.operations.register(
|
|
1637
|
+
timeoutMs,
|
|
1638
|
+
targetNodeRid === this.nodeRid ? 'registry' : 'sender'
|
|
1639
|
+
);
|
|
1561
1640
|
this.submitActorJoin(
|
|
1562
1641
|
pending,
|
|
1563
1642
|
actor,
|
|
@@ -1578,7 +1657,10 @@ export class ServiceStatefulRuntime {
|
|
|
1578
1657
|
timeoutMs: number
|
|
1579
1658
|
): ServiceStatefulPendingOperation {
|
|
1580
1659
|
const deadlineMs = performance.now() + timeoutMs;
|
|
1581
|
-
const pending = this.operations.register(
|
|
1660
|
+
const pending = this.operations.register(
|
|
1661
|
+
timeoutMs,
|
|
1662
|
+
targetNodeRid === this.nodeRid ? 'registry' : 'sender'
|
|
1663
|
+
);
|
|
1582
1664
|
this.submitActorJoin(
|
|
1583
1665
|
pending,
|
|
1584
1666
|
actor,
|
|
@@ -1656,7 +1738,10 @@ export class ServiceStatefulRuntime {
|
|
|
1656
1738
|
const previous = this.registry.binding(actor);
|
|
1657
1739
|
if (previous !== undefined && sameSessionIdentity(previous, localBinding)) {
|
|
1658
1740
|
const currentDelivery = this.sessionDeliveries.get(deliveryKey);
|
|
1659
|
-
if (
|
|
1741
|
+
if (
|
|
1742
|
+
currentDelivery !== undefined &&
|
|
1743
|
+
sameSessionBinding(currentDelivery.binding, previous)
|
|
1744
|
+
) {
|
|
1660
1745
|
this.sessionDeliveries.set(deliveryKey, {
|
|
1661
1746
|
...delivery,
|
|
1662
1747
|
binding: previous
|
|
@@ -1680,10 +1765,7 @@ export class ServiceStatefulRuntime {
|
|
|
1680
1765
|
this.nodeGeneration
|
|
1681
1766
|
);
|
|
1682
1767
|
if (previousDelivery !== undefined && previous !== undefined) {
|
|
1683
|
-
this.retiredSessionDeliveries.set(
|
|
1684
|
-
sessionDeliveryKey(previous),
|
|
1685
|
-
previousDelivery
|
|
1686
|
-
);
|
|
1768
|
+
this.retiredSessionDeliveries.set(sessionDeliveryKey(previous), previousDelivery);
|
|
1687
1769
|
}
|
|
1688
1770
|
installedDelivery = { ...delivery, binding };
|
|
1689
1771
|
this.sessionDeliveries.set(deliveryKey, installedDelivery);
|
|
@@ -1693,18 +1775,24 @@ export class ServiceStatefulRuntime {
|
|
|
1693
1775
|
kindData: undefined
|
|
1694
1776
|
});
|
|
1695
1777
|
if (previous !== undefined) {
|
|
1696
|
-
this.sendBoundSessionReplacement(
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1778
|
+
this.sendBoundSessionReplacement(
|
|
1779
|
+
previous,
|
|
1780
|
+
actorAuthorityFromRoute(
|
|
1781
|
+
this.tryActorFence(actor) ?? {
|
|
1782
|
+
actor,
|
|
1783
|
+
targetNodeGeneration: this.nodeGeneration,
|
|
1784
|
+
authorityOwnerGeneration:
|
|
1785
|
+
this.registry.actor(actor.actorId)?.authorityOwnerGeneration ?? actor.generation,
|
|
1786
|
+
ownerLeaseGeneration: this.nodeGeneration
|
|
1787
|
+
}
|
|
1788
|
+
)
|
|
1789
|
+
);
|
|
1705
1790
|
}
|
|
1706
1791
|
} catch (error) {
|
|
1707
|
-
if (
|
|
1792
|
+
if (
|
|
1793
|
+
installedDelivery !== undefined &&
|
|
1794
|
+
this.sessionDeliveries.get(deliveryKey) === installedDelivery
|
|
1795
|
+
) {
|
|
1708
1796
|
this.sessionDeliveries.delete(deliveryKey);
|
|
1709
1797
|
}
|
|
1710
1798
|
this.operations.reply(pending.id, failure(error));
|
|
@@ -1715,28 +1803,29 @@ export class ServiceStatefulRuntime {
|
|
|
1715
1803
|
let bindingActorFence: ServiceActorRouteFence;
|
|
1716
1804
|
let header: Buffer;
|
|
1717
1805
|
try {
|
|
1718
|
-
bindingActorFence =
|
|
1719
|
-
? this.actorFence(actor)
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
sessionRid,
|
|
1725
|
-
{ state: 'active', generation }
|
|
1726
|
-
);
|
|
1806
|
+
bindingActorFence =
|
|
1807
|
+
actorAuthority === undefined ? this.actorFence(actor) : { actor, ...actorAuthority };
|
|
1808
|
+
header = encodeBoundSessionBindHeader(pending.id, bindingActorFence, sessionRid, {
|
|
1809
|
+
state: 'active',
|
|
1810
|
+
generation
|
|
1811
|
+
});
|
|
1727
1812
|
} catch (error) {
|
|
1728
1813
|
this.operations.fail(pending.id, error);
|
|
1729
1814
|
return pending;
|
|
1730
1815
|
}
|
|
1731
1816
|
void this.requestDurableOperation(
|
|
1732
|
-
actor.nodeRid,
|
|
1817
|
+
actor.nodeRid,
|
|
1818
|
+
[header],
|
|
1819
|
+
pending.id,
|
|
1820
|
+
'streamBind',
|
|
1821
|
+
deadlineMs
|
|
1733
1822
|
).then(
|
|
1734
|
-
reply => this.completeRemoteReply(pending, actor.nodeRid, 'streamBind', undefined, reply),
|
|
1735
|
-
error => this.operations.fail(pending.id, error)
|
|
1823
|
+
(reply) => this.completeRemoteReply(pending, actor.nodeRid, 'streamBind', undefined, reply),
|
|
1824
|
+
(error) => this.operations.fail(pending.id, error)
|
|
1736
1825
|
);
|
|
1737
1826
|
return {
|
|
1738
1827
|
id: pending.id,
|
|
1739
|
-
promise: pending.promise.then(result => {
|
|
1828
|
+
promise: pending.promise.then((result) => {
|
|
1740
1829
|
if (result.terminalResult === RequestResult.Ok && result.streamBinding !== undefined) {
|
|
1741
1830
|
const committed: ServiceSessionDelivery = {
|
|
1742
1831
|
...delivery,
|
|
@@ -1747,10 +1836,10 @@ export class ServiceStatefulRuntime {
|
|
|
1747
1836
|
};
|
|
1748
1837
|
const current = this.sessionDeliveries.get(deliveryKey);
|
|
1749
1838
|
if (
|
|
1750
|
-
current === previousDelivery
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1839
|
+
current === previousDelivery ||
|
|
1840
|
+
current === undefined ||
|
|
1841
|
+
(sameSessionOwnerIdentity(current.binding, committed.binding) &&
|
|
1842
|
+
current.binding.bindingGeneration < committed.binding.bindingGeneration)
|
|
1754
1843
|
) {
|
|
1755
1844
|
if (current !== undefined && !sameSessionBinding(current.binding, committed.binding)) {
|
|
1756
1845
|
this.retiredSessionDeliveries.set(sessionDeliveryKey(current.binding), current);
|
|
@@ -1776,9 +1865,9 @@ export class ServiceStatefulRuntime {
|
|
|
1776
1865
|
const pending = this.operations.register(timeoutMs);
|
|
1777
1866
|
const delivery = this.sessionDeliveries.get(actorKey(actor));
|
|
1778
1867
|
if (
|
|
1779
|
-
delivery === undefined
|
|
1780
|
-
|
|
1781
|
-
|
|
1868
|
+
delivery === undefined ||
|
|
1869
|
+
delivery.binding.sessionRid !== sessionRid ||
|
|
1870
|
+
delivery.binding.bindingGeneration !== expectedBindingGeneration
|
|
1782
1871
|
) {
|
|
1783
1872
|
this.operations.reply(pending.id, {
|
|
1784
1873
|
terminalResult: RequestResult.NotFound,
|
|
@@ -1796,24 +1885,22 @@ export class ServiceStatefulRuntime {
|
|
|
1796
1885
|
}
|
|
1797
1886
|
return pending;
|
|
1798
1887
|
}
|
|
1799
|
-
const header = encodeBoundSessionBindHeader(
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
{ state: 'tombstone', retiredGeneration: expectedBindingGeneration }
|
|
1804
|
-
);
|
|
1888
|
+
const header = encodeBoundSessionBindHeader(pending.id, this.actorFence(actor), sessionRid, {
|
|
1889
|
+
state: 'tombstone',
|
|
1890
|
+
retiredGeneration: expectedBindingGeneration
|
|
1891
|
+
});
|
|
1805
1892
|
this.submitRequest(pending, actor.nodeRid, [header], timeoutMs, 'streamUnbind');
|
|
1806
1893
|
return pending;
|
|
1807
1894
|
}
|
|
1808
1895
|
|
|
1809
1896
|
sessionBindings(sessionRid: string): readonly ServiceSessionBinding[] {
|
|
1810
1897
|
return [...this.sessionDeliveries.values()]
|
|
1811
|
-
.map(value => value.binding)
|
|
1812
|
-
.filter(binding => binding.sessionRid === sessionRid);
|
|
1898
|
+
.map((value) => value.binding)
|
|
1899
|
+
.filter((binding) => binding.sessionRid === sessionRid);
|
|
1813
1900
|
}
|
|
1814
1901
|
|
|
1815
1902
|
allSessionBindings(): readonly ServiceSessionBinding[] {
|
|
1816
|
-
return [...this.sessionDeliveries.values()].map(value => value.binding);
|
|
1903
|
+
return [...this.sessionDeliveries.values()].map((value) => value.binding);
|
|
1817
1904
|
}
|
|
1818
1905
|
|
|
1819
1906
|
async sendSessionToActor(
|
|
@@ -1852,14 +1939,11 @@ export class ServiceStatefulRuntime {
|
|
|
1852
1939
|
}
|
|
1853
1940
|
const actorFence = this.tryBoundSessionSendFence(actor, senderFence);
|
|
1854
1941
|
if (actorFence === undefined) return SubmitResult.InvalidState;
|
|
1855
|
-
const header = encodeBoundSessionSendHeader(
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
binding.sessionOwnerNodeRid,
|
|
1861
|
-
[header, encodeApplicationPayload(payload)]
|
|
1862
|
-
);
|
|
1942
|
+
const header = encodeBoundSessionSendHeader(actorFence, expectedBindingGeneration);
|
|
1943
|
+
return this.submitOneWay(binding.sessionOwnerNodeRid, [
|
|
1944
|
+
header,
|
|
1945
|
+
encodeApplicationPayload(payload)
|
|
1946
|
+
]);
|
|
1863
1947
|
}
|
|
1864
1948
|
|
|
1865
1949
|
close(): void {
|
|
@@ -1906,23 +1990,21 @@ export class ServiceStatefulRuntime {
|
|
|
1906
1990
|
// fall through to RawServiceMeshRuntime's relocation ingress instead of
|
|
1907
1991
|
// being decoded as stateful actor/spot work.
|
|
1908
1992
|
if (
|
|
1909
|
-
record.command === M6bServiceWireCommand.relocationReady
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1993
|
+
record.command === M6bServiceWireCommand.relocationReady ||
|
|
1994
|
+
record.command === M6bServiceWireCommand.relocationData ||
|
|
1995
|
+
record.command === M6bServiceWireCommand.replyRelay ||
|
|
1996
|
+
record.command === M6bServiceWireCommand.relocationCutover
|
|
1913
1997
|
) {
|
|
1914
1998
|
return undefined;
|
|
1915
1999
|
}
|
|
1916
2000
|
if (
|
|
1917
|
-
record.command < M6bServiceWireCommand.spotSend
|
|
1918
|
-
|
|
1919
|
-
record.command
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
&& record.command !== M6bServiceWireCommand.boundSessionReplaced
|
|
1925
|
-
)
|
|
2001
|
+
record.command < M6bServiceWireCommand.spotSend ||
|
|
2002
|
+
(record.command > M6bServiceWireCommand.instanceSpot &&
|
|
2003
|
+
record.command !== M6bServiceWireCommand.userSpotCreate &&
|
|
2004
|
+
record.command !== M6bServiceWireCommand.userSpotClose &&
|
|
2005
|
+
record.command !== M6bServiceWireCommand.actorCreate &&
|
|
2006
|
+
record.command !== M6bServiceWireCommand.messageFollow &&
|
|
2007
|
+
record.command !== M6bServiceWireCommand.boundSessionReplaced)
|
|
1926
2008
|
) {
|
|
1927
2009
|
return undefined;
|
|
1928
2010
|
}
|
|
@@ -1948,38 +2030,40 @@ export class ServiceStatefulRuntime {
|
|
|
1948
2030
|
'actorJoin',
|
|
1949
2031
|
'boundSessionSend'
|
|
1950
2032
|
].includes(decoded.kind);
|
|
1951
|
-
const instanceMetadata =
|
|
1952
|
-
&& (record.flags & M6bServiceWireFlag.metadata) !== 0;
|
|
1953
|
-
const userSpotInfrastructure =
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
2033
|
+
const instanceMetadata =
|
|
2034
|
+
decoded.kind === 'instanceSpot' && (record.flags & M6bServiceWireFlag.metadata) !== 0;
|
|
2035
|
+
const userSpotInfrastructure =
|
|
2036
|
+
decoded.kind === 'userSpotCreate' ||
|
|
2037
|
+
decoded.kind === 'userSpotClose' ||
|
|
2038
|
+
decoded.kind === 'actorCreate' ||
|
|
2039
|
+
decoded.kind === 'messageFollow' ||
|
|
2040
|
+
decoded.kind === 'boundSessionReplaced';
|
|
1958
2041
|
if (
|
|
1959
|
-
record.parts.length < 1
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
&& record.parts.length !== (instanceMetadata ? 3 : 2)
|
|
1965
|
-
)
|
|
1966
|
-
|| (userSpotInfrastructure && record.parts.length !== 1)
|
|
2042
|
+
record.parts.length < 1 ||
|
|
2043
|
+
record.parts.length > (instanceMetadata ? 3 : 2) ||
|
|
2044
|
+
(hasPayload && decoded.kind !== 'actorJoin' && record.parts.length !== 2) ||
|
|
2045
|
+
(decoded.kind === 'instanceSpot' && record.parts.length !== (instanceMetadata ? 3 : 2)) ||
|
|
2046
|
+
(userSpotInfrastructure && record.parts.length !== 1)
|
|
1967
2047
|
) {
|
|
1968
2048
|
return 'protocolError';
|
|
1969
2049
|
}
|
|
1970
2050
|
const metadataFrame = instanceMetadata
|
|
1971
2051
|
? validateServiceMetadataFrame(record.parts[1]!)
|
|
1972
2052
|
: undefined;
|
|
1973
|
-
const canonicalPayload =
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
2053
|
+
const canonicalPayload =
|
|
2054
|
+
canonicalActorJoin?.payload === undefined
|
|
2055
|
+
? undefined
|
|
2056
|
+
: {
|
|
2057
|
+
packetName: canonicalActorJoin.payload.packetName,
|
|
2058
|
+
contentType: canonicalActorJoin.payload.contentType,
|
|
2059
|
+
payload: Buffer.from(canonicalActorJoin.payload.payload)
|
|
2060
|
+
};
|
|
2061
|
+
const payloadFrame =
|
|
2062
|
+
canonicalPayload === undefined
|
|
2063
|
+
? record.parts.length >= 2
|
|
2064
|
+
? record.parts[record.parts.length - 1]
|
|
2065
|
+
: undefined
|
|
2066
|
+
: encodeApplicationPayload(canonicalPayload);
|
|
1983
2067
|
try {
|
|
1984
2068
|
return await this.handleIngress(
|
|
1985
2069
|
record,
|
|
@@ -2000,9 +2084,9 @@ export class ServiceStatefulRuntime {
|
|
|
2000
2084
|
if (error instanceof ZLinkFrameworkException) {
|
|
2001
2085
|
const kind = internalFrameworkErrorKind(error);
|
|
2002
2086
|
if (
|
|
2003
|
-
kind === ZLinkFrameworkInternalErrorKind.RequestTargetNotFound
|
|
2004
|
-
|
|
2005
|
-
|
|
2087
|
+
kind === ZLinkFrameworkInternalErrorKind.RequestTargetNotFound ||
|
|
2088
|
+
kind === ZLinkFrameworkInternalErrorKind.SpotMoving ||
|
|
2089
|
+
kind === ZLinkFrameworkInternalErrorKind.SpotGenerationStale
|
|
2006
2090
|
) {
|
|
2007
2091
|
return 'protocolError';
|
|
2008
2092
|
}
|
|
@@ -2025,9 +2109,10 @@ export class ServiceStatefulRuntime {
|
|
|
2025
2109
|
): Promise<RawServicePumpResult> {
|
|
2026
2110
|
switch (record.kind) {
|
|
2027
2111
|
case 'messageFollow': {
|
|
2028
|
-
if (
|
|
2029
|
-
|
|
2030
|
-
|
|
2112
|
+
if (
|
|
2113
|
+
record.source.kind !== record.target.kind ||
|
|
2114
|
+
record.target.authorityOwnerGeneration <= record.source.authorityOwnerGeneration
|
|
2115
|
+
) {
|
|
2031
2116
|
throw new ServiceWireProtocolError(
|
|
2032
2117
|
'Message Follow target owner generation must increase at every hop.'
|
|
2033
2118
|
);
|
|
@@ -2043,10 +2128,10 @@ export class ServiceStatefulRuntime {
|
|
|
2043
2128
|
'Message Follow source route is not the current admitted peer.'
|
|
2044
2129
|
);
|
|
2045
2130
|
}
|
|
2046
|
-
const targetGeneration =
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
.lifecycleGeneration;
|
|
2131
|
+
const targetGeneration =
|
|
2132
|
+
record.target.targetNodeRid === this.nodeRid
|
|
2133
|
+
? this.nodeGeneration
|
|
2134
|
+
: this.raw.topology.peer(record.target.targetNodeRid)?.descriptor.lifecycleGeneration;
|
|
2050
2135
|
if (targetGeneration !== record.target.targetNodeGeneration) {
|
|
2051
2136
|
throw new ServiceWireProtocolError(
|
|
2052
2137
|
'Message Follow target route is not the current topology target.'
|
|
@@ -2057,8 +2142,7 @@ export class ServiceStatefulRuntime {
|
|
|
2057
2142
|
} else {
|
|
2058
2143
|
const directKey = directSpotKey(record.source.spot.spotId);
|
|
2059
2144
|
const current = this.directSpotRoutes.get(directKey);
|
|
2060
|
-
if (current !== undefined
|
|
2061
|
-
&& sameMessageFollowSpotSource(current, record.source)) {
|
|
2145
|
+
if (current !== undefined && sameMessageFollowSpotSource(current, record.source)) {
|
|
2062
2146
|
this.directSpotRoutes.delete(directKey);
|
|
2063
2147
|
const exactKey = spotKey(record.source.spot);
|
|
2064
2148
|
const exact = this.spotRoutes.get(exactKey);
|
|
@@ -2079,7 +2163,8 @@ export class ServiceStatefulRuntime {
|
|
|
2079
2163
|
`spot:${record.target.spot.spotId}`,
|
|
2080
2164
|
payloadFrame!,
|
|
2081
2165
|
{
|
|
2082
|
-
receiveKind:
|
|
2166
|
+
receiveKind:
|
|
2167
|
+
record.kind === 'spotSend' ? ReceiveKind.SpotSend : ReceiveKind.SpotRequest,
|
|
2083
2168
|
operationKind: record.kind === 'spotRequest' ? OperationKind.SpotRequest : 0,
|
|
2084
2169
|
...(record.correlation === undefined ? {} : { correlation: record.correlation }),
|
|
2085
2170
|
sourceSpotId: record.sourceSpotId,
|
|
@@ -2092,17 +2177,18 @@ export class ServiceStatefulRuntime {
|
|
|
2092
2177
|
}
|
|
2093
2178
|
case 'actorSend':
|
|
2094
2179
|
case 'actorRequest': {
|
|
2095
|
-
const actor =
|
|
2096
|
-
|
|
2097
|
-
|
|
2180
|
+
const actor =
|
|
2181
|
+
record.boundSession === undefined
|
|
2182
|
+
? this.validateActorMessageFence(record.target)
|
|
2183
|
+
: this.validateActorFence(record.target);
|
|
2098
2184
|
if (record.boundSession !== undefined) {
|
|
2099
2185
|
const binding = this.registry.validateBoundSession(
|
|
2100
2186
|
record.target.actor,
|
|
2101
2187
|
record.boundSession.bindingGeneration
|
|
2102
2188
|
);
|
|
2103
2189
|
if (
|
|
2104
|
-
binding.sessionRid !== record.boundSession.sessionRid
|
|
2105
|
-
|
|
2190
|
+
binding.sessionRid !== record.boundSession.sessionRid ||
|
|
2191
|
+
binding.sessionOwnerNodeRid !== ingress.sourceRoutingId
|
|
2106
2192
|
) {
|
|
2107
2193
|
throw new ServiceStaleGenerationError('binding', record.target.actor.actorId);
|
|
2108
2194
|
}
|
|
@@ -2112,7 +2198,8 @@ export class ServiceStatefulRuntime {
|
|
|
2112
2198
|
`actor:${actor.ref.actorId}\0${actor.ref.generation}`,
|
|
2113
2199
|
payloadFrame!,
|
|
2114
2200
|
{
|
|
2115
|
-
receiveKind:
|
|
2201
|
+
receiveKind:
|
|
2202
|
+
record.kind === 'actorSend' ? ReceiveKind.ActorSend : ReceiveKind.ActorRequest,
|
|
2116
2203
|
operationKind: record.kind === 'actorRequest' ? OperationKind.ActorRequest : 0,
|
|
2117
2204
|
...(record.correlation === undefined ? {} : { correlation: record.correlation }),
|
|
2118
2205
|
...(record.sourceActor === undefined
|
|
@@ -2170,18 +2257,9 @@ export class ServiceStatefulRuntime {
|
|
|
2170
2257
|
case 'boundSessionReplaced':
|
|
2171
2258
|
return this.handleBoundSessionReplaced(ingress, record);
|
|
2172
2259
|
case 'boundSessionSend':
|
|
2173
|
-
return await this.deliverBoundSession(
|
|
2174
|
-
ingress,
|
|
2175
|
-
record,
|
|
2176
|
-
payloadFrame
|
|
2177
|
-
);
|
|
2260
|
+
return await this.deliverBoundSession(ingress, record, payloadFrame);
|
|
2178
2261
|
case 'instanceSpot':
|
|
2179
|
-
return this.enqueueInstanceSpot(
|
|
2180
|
-
ingress,
|
|
2181
|
-
record,
|
|
2182
|
-
payloadFrame!,
|
|
2183
|
-
metadataFrame
|
|
2184
|
-
);
|
|
2262
|
+
return this.enqueueInstanceSpot(ingress, record, payloadFrame!, metadataFrame);
|
|
2185
2263
|
case 'userSpotCreate':
|
|
2186
2264
|
case 'userSpotClose':
|
|
2187
2265
|
case 'actorCreate':
|
|
@@ -2207,8 +2285,8 @@ export class ServiceStatefulRuntime {
|
|
|
2207
2285
|
return 'infrastructure';
|
|
2208
2286
|
}
|
|
2209
2287
|
if (
|
|
2210
|
-
record.activation === 'ready'
|
|
2211
|
-
|
|
2288
|
+
record.activation === 'ready' &&
|
|
2289
|
+
this.needsInstanceApplicationMaterialization(ingress, record)
|
|
2212
2290
|
) {
|
|
2213
2291
|
const ownedIngress = shareIngressOwnership(ingress);
|
|
2214
2292
|
void this.continueReadyInstanceMaterialization(
|
|
@@ -2220,13 +2298,14 @@ export class ServiceStatefulRuntime {
|
|
|
2220
2298
|
return 'infrastructure';
|
|
2221
2299
|
}
|
|
2222
2300
|
const spot = this.requireInstanceActivation(ingress, record);
|
|
2223
|
-
const applicationTarget =
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2301
|
+
const applicationTarget =
|
|
2302
|
+
record.activation === 'missing'
|
|
2303
|
+
? {
|
|
2304
|
+
targetSpotId: record.target.targetSpotId,
|
|
2305
|
+
stableType: record.target.stableType,
|
|
2306
|
+
objectGeneration: spot.ref.generation
|
|
2307
|
+
}
|
|
2308
|
+
: this.instanceApplicationTarget(record);
|
|
2230
2309
|
return this.enqueueActivatedInstanceSpot(
|
|
2231
2310
|
ingress,
|
|
2232
2311
|
record,
|
|
@@ -2248,10 +2327,12 @@ export class ServiceStatefulRuntime {
|
|
|
2248
2327
|
const lifecycle = this.instanceApplicationLifecycle;
|
|
2249
2328
|
if (lifecycle === undefined) return false;
|
|
2250
2329
|
const { intent } = this.requireReadyInstanceApplicationRoute(ingress, record);
|
|
2251
|
-
if (
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2330
|
+
if (
|
|
2331
|
+
this.instanceApplicationLifecycle?.isIdleEvicting?.({
|
|
2332
|
+
targetSpotId: record.route.targetSpotId,
|
|
2333
|
+
stableType: intent.instanceType
|
|
2334
|
+
}) === true
|
|
2335
|
+
) {
|
|
2255
2336
|
throw createInternalFrameworkException(
|
|
2256
2337
|
ZLinkFrameworkInternalErrorKind.SpotMoving,
|
|
2257
2338
|
`Instance Spot '${record.route.targetSpotId}' is entering idle eviction.`
|
|
@@ -2289,15 +2370,13 @@ export class ServiceStatefulRuntime {
|
|
|
2289
2370
|
operationKey = instanceOperationKey(record);
|
|
2290
2371
|
if (this.admittedInstanceOperations.has(operationKey)) return 'application';
|
|
2291
2372
|
}
|
|
2292
|
-
const directReply =
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
const applicationTarget = applicationTargetOverride
|
|
2300
|
-
?? (record.activation === 'missing'
|
|
2373
|
+
const directReply =
|
|
2374
|
+
record.operationKind === 'request'
|
|
2375
|
+
? (localReply ?? this.replyPort(ingress, record.replyRouteId, 'instanceSpotRequest'))
|
|
2376
|
+
: undefined;
|
|
2377
|
+
const applicationTarget =
|
|
2378
|
+
applicationTargetOverride ??
|
|
2379
|
+
(record.activation === 'missing'
|
|
2301
2380
|
? {
|
|
2302
2381
|
targetSpotId: record.target.targetSpotId,
|
|
2303
2382
|
stableType: record.target.stableType,
|
|
@@ -2305,45 +2384,42 @@ export class ServiceStatefulRuntime {
|
|
|
2305
2384
|
}
|
|
2306
2385
|
: this.instanceApplicationTarget(record));
|
|
2307
2386
|
let deferredReply: Parameters<NonNullable<ServiceStatefulMailboxData['reply']>> | undefined;
|
|
2308
|
-
const terminalCompletion =
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2387
|
+
const terminalCompletion =
|
|
2388
|
+
onTerminalCompletion === undefined
|
|
2389
|
+
? undefined
|
|
2390
|
+
: async () => {
|
|
2391
|
+
// The application turn has returned before authority cleanup starts.
|
|
2392
|
+
// Mark it quiescent first so a close requested by that turn can finish
|
|
2393
|
+
// its local cleanup and release the exact authority fence below.
|
|
2394
|
+
this.completeInstanceApplicationOperation(applicationTarget.targetSpotId);
|
|
2395
|
+
try {
|
|
2396
|
+
await onTerminalCompletion();
|
|
2397
|
+
} finally {
|
|
2398
|
+
if (deferredReply !== undefined && directReply !== undefined) {
|
|
2399
|
+
directReply(...deferredReply);
|
|
2400
|
+
}
|
|
2320
2401
|
}
|
|
2321
|
-
}
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
...(terminalCompletion === undefined ? {} : { onTerminalCompletion: terminalCompletion }),
|
|
2335
|
-
...(record.operationKind === 'request'
|
|
2336
|
-
? {
|
|
2337
|
-
reply: onTerminalCompletion === undefined
|
|
2402
|
+
};
|
|
2403
|
+
const result = this.enqueueApplicationFrame(ingress, `spot:${spot.ref.spotId}`, payloadFrame, {
|
|
2404
|
+
receiveKind: ReceiveKind.InstanceSpotActivation,
|
|
2405
|
+
operationKind: record.operationKind === 'request' ? OperationKind.InstanceSpotRequest : 0,
|
|
2406
|
+
...(record.operationKind === 'request' ? { correlation: record.replyRouteId } : {}),
|
|
2407
|
+
...(record.sourceSpotId === undefined ? {} : { sourceSpotId: record.sourceSpotId }),
|
|
2408
|
+
targetSpot: spot.ref,
|
|
2409
|
+
...(metadataFrame === undefined ? {} : { applicationMetadata: metadataFrame }),
|
|
2410
|
+
...(terminalCompletion === undefined ? {} : { onTerminalCompletion: terminalCompletion }),
|
|
2411
|
+
...(record.operationKind === 'request'
|
|
2412
|
+
? {
|
|
2413
|
+
reply:
|
|
2414
|
+
onTerminalCompletion === undefined
|
|
2338
2415
|
? directReply
|
|
2339
|
-
: (
|
|
2416
|
+
: (...reply: Parameters<NonNullable<ServiceStatefulMailboxData['reply']>>) => {
|
|
2340
2417
|
deferredReply = reply;
|
|
2341
2418
|
return true;
|
|
2342
|
-
}
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
);
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
: {})
|
|
2422
|
+
});
|
|
2347
2423
|
if (operationKey !== undefined && result === 'application' && record.activation === 'missing') {
|
|
2348
2424
|
this.admittedInstanceOperations.set(operationKey, record.deadlineUnixMs);
|
|
2349
2425
|
}
|
|
@@ -2369,7 +2445,11 @@ export class ServiceStatefulRuntime {
|
|
|
2369
2445
|
): Promise<void> {
|
|
2370
2446
|
try {
|
|
2371
2447
|
this.validateInstanceIngress(ingress, record);
|
|
2372
|
-
const activation = await this.activateMissingInstanceAsync(
|
|
2448
|
+
const activation = await this.activateMissingInstanceAsync(
|
|
2449
|
+
record,
|
|
2450
|
+
payloadFrame,
|
|
2451
|
+
metadataFrame
|
|
2452
|
+
);
|
|
2373
2453
|
if (this.closed) return;
|
|
2374
2454
|
const admitted = this.enqueueActivatedInstanceSpot(
|
|
2375
2455
|
ingress,
|
|
@@ -2408,12 +2488,7 @@ export class ServiceStatefulRuntime {
|
|
|
2408
2488
|
localReply(result.terminalResult, result.failureCode);
|
|
2409
2489
|
} else {
|
|
2410
2490
|
if (record.replyRouteId === undefined) return;
|
|
2411
|
-
this.replyWire(
|
|
2412
|
-
ingress,
|
|
2413
|
-
record.replyRouteId,
|
|
2414
|
-
result.terminalResult,
|
|
2415
|
-
result.failureCode
|
|
2416
|
-
);
|
|
2491
|
+
this.replyWire(ingress, record.replyRouteId, result.terminalResult, result.failureCode);
|
|
2417
2492
|
}
|
|
2418
2493
|
}
|
|
2419
2494
|
}
|
|
@@ -2445,10 +2520,10 @@ export class ServiceStatefulRuntime {
|
|
|
2445
2520
|
);
|
|
2446
2521
|
if (this.closed) return;
|
|
2447
2522
|
const latestRoute = this.instanceIntents.get(record.route.targetSpotId)?.route;
|
|
2448
|
-
const refreshedRecord =
|
|
2449
|
-
&& sameInstanceApplicationOwner(latestRoute, record.route)
|
|
2450
|
-
|
|
2451
|
-
|
|
2523
|
+
const refreshedRecord =
|
|
2524
|
+
latestRoute !== undefined && sameInstanceApplicationOwner(latestRoute, record.route)
|
|
2525
|
+
? { ...record, route: latestRoute }
|
|
2526
|
+
: record;
|
|
2452
2527
|
const spot = this.requireInstanceActivation(ingress, refreshedRecord);
|
|
2453
2528
|
const admitted = this.enqueueActivatedInstanceSpot(
|
|
2454
2529
|
ingress,
|
|
@@ -2465,12 +2540,7 @@ export class ServiceStatefulRuntime {
|
|
|
2465
2540
|
} catch (error) {
|
|
2466
2541
|
if (record.operationKind !== 'request' || record.replyRouteId === undefined) return;
|
|
2467
2542
|
const result = failure(error);
|
|
2468
|
-
this.replyWire(
|
|
2469
|
-
ingress,
|
|
2470
|
-
record.replyRouteId,
|
|
2471
|
-
result.terminalResult,
|
|
2472
|
-
result.failureCode
|
|
2473
|
-
);
|
|
2543
|
+
this.replyWire(ingress, record.replyRouteId, result.terminalResult, result.failureCode);
|
|
2474
2544
|
}
|
|
2475
2545
|
}
|
|
2476
2546
|
|
|
@@ -2484,40 +2554,42 @@ export class ServiceStatefulRuntime {
|
|
|
2484
2554
|
stableType: target.stableType,
|
|
2485
2555
|
objectGeneration: route.objectGeneration
|
|
2486
2556
|
};
|
|
2487
|
-
return () =>
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
authorityCompletionSignaled = true;
|
|
2493
|
-
if (released !== undefined) {
|
|
2494
|
-
const authorityReleased = await this.instanceApplicationLifecycle?.completeTerminal(applicationTarget)
|
|
2495
|
-
?? false;
|
|
2496
|
-
if (!authorityReleased) {
|
|
2497
|
-
this.replaceCommittedInstanceRoute(target.stableType, route, released);
|
|
2498
|
-
} else {
|
|
2499
|
-
this.forgetClosedInstanceRoute(released);
|
|
2500
|
-
}
|
|
2501
|
-
}
|
|
2502
|
-
} finally {
|
|
2503
|
-
if (!authorityCompletionSignaled) {
|
|
2557
|
+
return () =>
|
|
2558
|
+
(completion ??= (async () => {
|
|
2559
|
+
let authorityCompletionSignaled = false;
|
|
2560
|
+
try {
|
|
2561
|
+
const released = await this.asyncInstanceAuthority?.complete(target, route);
|
|
2504
2562
|
this.completeInstanceAuthorityOperation(target.targetSpotId);
|
|
2563
|
+
authorityCompletionSignaled = true;
|
|
2564
|
+
if (released !== undefined) {
|
|
2565
|
+
const authorityReleased =
|
|
2566
|
+
(await this.instanceApplicationLifecycle?.completeTerminal(applicationTarget)) ??
|
|
2567
|
+
false;
|
|
2568
|
+
if (!authorityReleased) {
|
|
2569
|
+
this.replaceCommittedInstanceRoute(target.stableType, route, released);
|
|
2570
|
+
} else {
|
|
2571
|
+
this.forgetClosedInstanceRoute(released);
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
} finally {
|
|
2575
|
+
if (!authorityCompletionSignaled) {
|
|
2576
|
+
this.completeInstanceAuthorityOperation(target.targetSpotId);
|
|
2577
|
+
}
|
|
2505
2578
|
}
|
|
2506
|
-
}
|
|
2507
|
-
})();
|
|
2579
|
+
})());
|
|
2508
2580
|
}
|
|
2509
2581
|
|
|
2510
2582
|
private instanceApplicationTerminalCompletion(
|
|
2511
2583
|
target: ServiceInstanceApplicationTarget
|
|
2512
2584
|
): () => Promise<void> {
|
|
2513
2585
|
return async () => {
|
|
2514
|
-
const authorityReleased =
|
|
2515
|
-
?? false;
|
|
2586
|
+
const authorityReleased =
|
|
2587
|
+
(await this.instanceApplicationLifecycle?.completeTerminal(target)) ?? false;
|
|
2516
2588
|
if (!authorityReleased) return;
|
|
2517
2589
|
const current = this.instanceIntents.get(target.targetSpotId);
|
|
2518
2590
|
if (
|
|
2519
|
-
current?.instanceType === target.stableType
|
|
2520
|
-
|
|
2591
|
+
current?.instanceType === target.stableType &&
|
|
2592
|
+
current.route.objectGeneration === target.objectGeneration
|
|
2521
2593
|
) {
|
|
2522
2594
|
this.forgetClosedInstanceRoute(current.route);
|
|
2523
2595
|
}
|
|
@@ -2551,10 +2623,7 @@ export class ServiceStatefulRuntime {
|
|
|
2551
2623
|
|
|
2552
2624
|
private beginInstanceApplicationOperation(spotId: string): void {
|
|
2553
2625
|
const key = String(spotId);
|
|
2554
|
-
this.pendingInstanceTerminals.set(
|
|
2555
|
-
key,
|
|
2556
|
-
(this.pendingInstanceTerminals.get(key) ?? 0) + 1
|
|
2557
|
-
);
|
|
2626
|
+
this.pendingInstanceTerminals.set(key, (this.pendingInstanceTerminals.get(key) ?? 0) + 1);
|
|
2558
2627
|
}
|
|
2559
2628
|
|
|
2560
2629
|
private beginInstanceAuthorityOperation(spotId: string): void {
|
|
@@ -2589,9 +2658,10 @@ export class ServiceStatefulRuntime {
|
|
|
2589
2658
|
|
|
2590
2659
|
private completeInstanceApplicationWaitersIfQuiescent(key: string): void {
|
|
2591
2660
|
if (
|
|
2592
|
-
(this.pendingInstanceTerminals.get(key) ?? 0) !== 0
|
|
2593
|
-
|
|
2594
|
-
)
|
|
2661
|
+
(this.pendingInstanceTerminals.get(key) ?? 0) !== 0 ||
|
|
2662
|
+
(this.pendingInstanceAuthorityTerminals.get(key) ?? 0) !== 0
|
|
2663
|
+
)
|
|
2664
|
+
return;
|
|
2595
2665
|
const waiters = this.instanceApplicationWaiters.get(key);
|
|
2596
2666
|
if (waiters === undefined) return;
|
|
2597
2667
|
this.instanceApplicationWaiters.delete(key);
|
|
@@ -2620,20 +2690,26 @@ export class ServiceStatefulRuntime {
|
|
|
2620
2690
|
targetSpotId: route.targetSpotId
|
|
2621
2691
|
};
|
|
2622
2692
|
if (record.operationKind === 'send') {
|
|
2623
|
-
const submitted = await this.submitOneWay(
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2693
|
+
const submitted = await this.submitOneWay(
|
|
2694
|
+
route.targetNodeRid,
|
|
2695
|
+
instanceOperationParts(
|
|
2696
|
+
[
|
|
2697
|
+
encodeInstanceSpotActivationHeader(
|
|
2698
|
+
redirectedTarget,
|
|
2699
|
+
this.nodeGeneration,
|
|
2700
|
+
this.nodeRid,
|
|
2701
|
+
record.sourceSpotId,
|
|
2702
|
+
'send',
|
|
2703
|
+
record.operation,
|
|
2704
|
+
record.deadlineUnixMs,
|
|
2705
|
+
undefined,
|
|
2706
|
+
metadataFrame !== undefined
|
|
2707
|
+
),
|
|
2708
|
+
payloadFrame
|
|
2709
|
+
],
|
|
2710
|
+
metadataFrame
|
|
2711
|
+
)
|
|
2712
|
+
);
|
|
2637
2713
|
if (submitted !== SubmitResult.Ok) {
|
|
2638
2714
|
throw new Error(`Instance activation redirect was not admitted: ${submitted}.`);
|
|
2639
2715
|
}
|
|
@@ -2644,20 +2720,23 @@ export class ServiceStatefulRuntime {
|
|
|
2644
2720
|
this.submitRequest(
|
|
2645
2721
|
pending,
|
|
2646
2722
|
route.targetNodeRid,
|
|
2647
|
-
instanceOperationParts(
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2723
|
+
instanceOperationParts(
|
|
2724
|
+
[
|
|
2725
|
+
encodeInstanceSpotActivationHeader(
|
|
2726
|
+
redirectedTarget,
|
|
2727
|
+
this.nodeGeneration,
|
|
2728
|
+
this.nodeRid,
|
|
2729
|
+
record.sourceSpotId,
|
|
2730
|
+
'request',
|
|
2731
|
+
record.operation,
|
|
2732
|
+
record.deadlineUnixMs,
|
|
2733
|
+
pending.id,
|
|
2734
|
+
metadataFrame !== undefined
|
|
2735
|
+
),
|
|
2736
|
+
payloadFrame
|
|
2737
|
+
],
|
|
2738
|
+
metadataFrame
|
|
2739
|
+
),
|
|
2661
2740
|
remainingMs,
|
|
2662
2741
|
'instanceSpotRequest'
|
|
2663
2742
|
);
|
|
@@ -2682,16 +2761,18 @@ export class ServiceStatefulRuntime {
|
|
|
2682
2761
|
record: Extract<ServiceStatefulWireRecord, { readonly kind: 'instanceSpot' }>
|
|
2683
2762
|
): ServiceSpotState {
|
|
2684
2763
|
const readyRoute = record.activation === 'ready' ? record.route : undefined;
|
|
2685
|
-
const spotId =
|
|
2686
|
-
|
|
2764
|
+
const spotId =
|
|
2765
|
+
readyRoute?.targetSpotId ??
|
|
2766
|
+
(record.activation === 'missing' ? record.target.targetSpotId : undefined);
|
|
2687
2767
|
if (spotId === undefined) throw new ServiceStaleGenerationError('spot', 'unknown');
|
|
2688
2768
|
this.validateInstanceIngress(ingress, record);
|
|
2689
2769
|
if (record.activation === 'missing') {
|
|
2690
2770
|
return this.activateMissingInstance(record);
|
|
2691
2771
|
}
|
|
2692
2772
|
const { intent, route } = this.requireReadyInstanceApplicationRoute(ingress, record);
|
|
2693
|
-
const spot =
|
|
2694
|
-
|
|
2773
|
+
const spot =
|
|
2774
|
+
this.registry.spot(route.targetSpotId) ??
|
|
2775
|
+
this.registry.restoreSpot(
|
|
2695
2776
|
{
|
|
2696
2777
|
spotId: route.targetSpotId,
|
|
2697
2778
|
generation: route.objectGeneration
|
|
@@ -2701,11 +2782,11 @@ export class ServiceStatefulRuntime {
|
|
|
2701
2782
|
route.authorityOwnerGeneration
|
|
2702
2783
|
);
|
|
2703
2784
|
if (
|
|
2704
|
-
spot.state !== 'ready'
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2785
|
+
spot.state !== 'ready' ||
|
|
2786
|
+
spot.kind !== 'instance' ||
|
|
2787
|
+
spot.stableType !== intent.instanceType ||
|
|
2788
|
+
spot.ref.generation !== route.objectGeneration ||
|
|
2789
|
+
spot.authorityOwnerGeneration !== route.authorityOwnerGeneration
|
|
2709
2790
|
) {
|
|
2710
2791
|
throw createInternalFrameworkException(
|
|
2711
2792
|
ZLinkFrameworkInternalErrorKind.SpotMoving,
|
|
@@ -2733,9 +2814,9 @@ export class ServiceStatefulRuntime {
|
|
|
2733
2814
|
// request. A mismatched or absent local projection remains NotFound.
|
|
2734
2815
|
const local = this.registry.spot(record.route.targetSpotId);
|
|
2735
2816
|
if (
|
|
2736
|
-
local?.kind === 'instance'
|
|
2737
|
-
|
|
2738
|
-
|
|
2817
|
+
local?.kind === 'instance' &&
|
|
2818
|
+
local.state === 'ready' &&
|
|
2819
|
+
routeMatchesLocal(record.route, local, this.nodeRid, this.nodeGeneration)
|
|
2739
2820
|
) {
|
|
2740
2821
|
this.publishCommittedInstanceRoute(local.stableType, record.route);
|
|
2741
2822
|
intent = this.instanceIntents.get(record.route.targetSpotId);
|
|
@@ -2761,8 +2842,8 @@ export class ServiceStatefulRuntime {
|
|
|
2761
2842
|
);
|
|
2762
2843
|
}
|
|
2763
2844
|
if (
|
|
2764
|
-
current.objectGeneration === record.route.objectGeneration
|
|
2765
|
-
|
|
2845
|
+
current.objectGeneration === record.route.objectGeneration &&
|
|
2846
|
+
current.authorityOwnerGeneration !== record.route.authorityOwnerGeneration
|
|
2766
2847
|
) {
|
|
2767
2848
|
throw createInternalFrameworkException(
|
|
2768
2849
|
ZLinkFrameworkInternalErrorKind.SpotMoving,
|
|
@@ -2778,8 +2859,8 @@ export class ServiceStatefulRuntime {
|
|
|
2778
2859
|
): void {
|
|
2779
2860
|
const expectedSourceGeneration = this.peerGeneration(record.sourceNodeRid);
|
|
2780
2861
|
if (
|
|
2781
|
-
record.sourceNodeRid !== ingress.sourceRoutingId
|
|
2782
|
-
|
|
2862
|
+
record.sourceNodeRid !== ingress.sourceRoutingId ||
|
|
2863
|
+
record.sourceNodeGeneration !== expectedSourceGeneration
|
|
2783
2864
|
) {
|
|
2784
2865
|
if (record.activation === 'ready') {
|
|
2785
2866
|
throw createInternalFrameworkException(
|
|
@@ -2799,9 +2880,9 @@ export class ServiceStatefulRuntime {
|
|
|
2799
2880
|
): ServiceSpotState {
|
|
2800
2881
|
const target = record.target;
|
|
2801
2882
|
if (
|
|
2802
|
-
target.targetNodeRid !== this.nodeRid
|
|
2803
|
-
|
|
2804
|
-
|
|
2883
|
+
target.targetNodeRid !== this.nodeRid ||
|
|
2884
|
+
target.targetNodeGeneration !== this.nodeGeneration ||
|
|
2885
|
+
record.deadlineUnixMs < BigInt(Date.now())
|
|
2805
2886
|
) {
|
|
2806
2887
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
2807
2888
|
}
|
|
@@ -2817,10 +2898,10 @@ export class ServiceStatefulRuntime {
|
|
|
2817
2898
|
const current = authority.read(target);
|
|
2818
2899
|
if (local !== undefined) {
|
|
2819
2900
|
if (
|
|
2820
|
-
local.kind !== 'instance'
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2901
|
+
local.kind !== 'instance' ||
|
|
2902
|
+
local.stableType !== target.stableType ||
|
|
2903
|
+
current.kind !== 'ready' ||
|
|
2904
|
+
!routeMatchesLocal(current.route, local, this.nodeRid, this.nodeGeneration)
|
|
2824
2905
|
) {
|
|
2825
2906
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
2826
2907
|
}
|
|
@@ -2852,12 +2933,7 @@ export class ServiceStatefulRuntime {
|
|
|
2852
2933
|
if (activation.created) this.registry.closeSpot(activation.spot.ref);
|
|
2853
2934
|
throw new ServiceInstanceActivationRedirectError(committed.route);
|
|
2854
2935
|
}
|
|
2855
|
-
if (!routeMatchesLocal(
|
|
2856
|
-
committed.route,
|
|
2857
|
-
activation.spot,
|
|
2858
|
-
this.nodeRid,
|
|
2859
|
-
this.nodeGeneration
|
|
2860
|
-
)) {
|
|
2936
|
+
if (!routeMatchesLocal(committed.route, activation.spot, this.nodeRid, this.nodeGeneration)) {
|
|
2861
2937
|
if (activation.created) this.registry.closeSpot(activation.spot.ref);
|
|
2862
2938
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
2863
2939
|
}
|
|
@@ -2896,9 +2972,9 @@ export class ServiceStatefulRuntime {
|
|
|
2896
2972
|
): Promise<{ readonly spot: ServiceSpotState; readonly route: ServiceInstanceRouteFence }> {
|
|
2897
2973
|
const target = record.target;
|
|
2898
2974
|
if (
|
|
2899
|
-
target.targetNodeRid !== this.nodeRid
|
|
2900
|
-
|
|
2901
|
-
|
|
2975
|
+
target.targetNodeRid !== this.nodeRid ||
|
|
2976
|
+
target.targetNodeGeneration !== this.nodeGeneration ||
|
|
2977
|
+
record.deadlineUnixMs < BigInt(Date.now())
|
|
2902
2978
|
) {
|
|
2903
2979
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
2904
2980
|
}
|
|
@@ -2925,17 +3001,14 @@ export class ServiceStatefulRuntime {
|
|
|
2925
3001
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
2926
3002
|
}
|
|
2927
3003
|
if (
|
|
2928
|
-
local !== undefined
|
|
2929
|
-
|
|
2930
|
-
local.kind !== 'instance'
|
|
2931
|
-
|| local.stableType !== target.stableType
|
|
2932
|
-
)
|
|
3004
|
+
local !== undefined &&
|
|
3005
|
+
(local.kind !== 'instance' || local.stableType !== target.stableType)
|
|
2933
3006
|
) {
|
|
2934
3007
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
2935
3008
|
}
|
|
2936
3009
|
if (
|
|
2937
|
-
local !== undefined
|
|
2938
|
-
|
|
3010
|
+
local !== undefined &&
|
|
3011
|
+
routeMatchesLocal(current.route, local, this.nodeRid, this.nodeGeneration)
|
|
2939
3012
|
) {
|
|
2940
3013
|
return { spot: local, route: current.route };
|
|
2941
3014
|
}
|
|
@@ -2944,26 +3017,22 @@ export class ServiceStatefulRuntime {
|
|
|
2944
3017
|
if (local !== undefined) {
|
|
2945
3018
|
const lifecycle = this.instanceApplicationLifecycle;
|
|
2946
3019
|
const closing = lifecycle?.isClosing?.(target) === true;
|
|
2947
|
-
let materialized = lifecycle === undefined
|
|
2948
|
-
? undefined
|
|
2949
|
-
: lifecycle.isMaterialized(target);
|
|
3020
|
+
let materialized = lifecycle === undefined ? undefined : lifecycle.isMaterialized(target);
|
|
2950
3021
|
const materializing = lifecycle?.isMaterializing?.(target) === true;
|
|
2951
|
-
if (
|
|
2952
|
-
local.kind !== 'instance'
|
|
2953
|
-
|| local.stableType !== target.stableType
|
|
2954
|
-
) {
|
|
3022
|
+
if (local.kind !== 'instance' || local.stableType !== target.stableType) {
|
|
2955
3023
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
2956
3024
|
}
|
|
2957
|
-
const joinsCreating =
|
|
2958
|
-
|
|
2959
|
-
|
|
3025
|
+
const joinsCreating =
|
|
3026
|
+
current.kind === 'creating' &&
|
|
3027
|
+
local.ref.generation === current.objectGeneration &&
|
|
3028
|
+
local.authorityOwnerGeneration === current.authorityOwnerGeneration;
|
|
2960
3029
|
const replacesClosingProjection = current.kind === 'creating' && closing;
|
|
2961
3030
|
if (
|
|
2962
|
-
current.kind === 'missing'
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
3031
|
+
current.kind === 'missing' &&
|
|
3032
|
+
!closing &&
|
|
3033
|
+
!materializing &&
|
|
3034
|
+
materialized === true &&
|
|
3035
|
+
lifecycle !== undefined
|
|
2967
3036
|
) {
|
|
2968
3037
|
// The durable authority is the ownership source of truth. Its removal
|
|
2969
3038
|
// can become visible before the route reconciler has discarded the
|
|
@@ -2984,8 +3053,8 @@ export class ServiceStatefulRuntime {
|
|
|
2984
3053
|
// If disposal did not remove an orphaned materialization, keep fencing
|
|
2985
3054
|
// it instead of admitting work into two application generations.
|
|
2986
3055
|
if (
|
|
2987
|
-
(!joinsCreating && !replacesClosingProjection && current.kind !== 'missing')
|
|
2988
|
-
|
|
3056
|
+
(!joinsCreating && !replacesClosingProjection && current.kind !== 'missing') ||
|
|
3057
|
+
(!joinsCreating && !closing && materialized !== false && !materializing)
|
|
2989
3058
|
) {
|
|
2990
3059
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
2991
3060
|
}
|
|
@@ -3037,12 +3106,7 @@ export class ServiceStatefulRuntime {
|
|
|
3037
3106
|
await this.instanceApplicationLifecycle?.discard(target);
|
|
3038
3107
|
throw new ServiceInstanceActivationRedirectError(committed.route);
|
|
3039
3108
|
}
|
|
3040
|
-
if (!routeMatchesLocal(
|
|
3041
|
-
committed.route,
|
|
3042
|
-
activation.spot,
|
|
3043
|
-
this.nodeRid,
|
|
3044
|
-
this.nodeGeneration
|
|
3045
|
-
)) {
|
|
3109
|
+
if (!routeMatchesLocal(committed.route, activation.spot, this.nodeRid, this.nodeGeneration)) {
|
|
3046
3110
|
if (activation.created) this.registry.closeSpot(activation.spot.ref);
|
|
3047
3111
|
await this.instanceApplicationLifecycle?.discard(target);
|
|
3048
3112
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
@@ -3059,8 +3123,9 @@ export class ServiceStatefulRuntime {
|
|
|
3059
3123
|
};
|
|
3060
3124
|
};
|
|
3061
3125
|
const descriptor = topology.localDescriptor?.();
|
|
3062
|
-
return
|
|
3063
|
-
|| (descriptor.state === 'serving' && descriptor.placementWeight > 0)
|
|
3126
|
+
return (
|
|
3127
|
+
descriptor === undefined || (descriptor.state === 'serving' && descriptor.placementWeight > 0)
|
|
3128
|
+
);
|
|
3064
3129
|
}
|
|
3065
3130
|
|
|
3066
3131
|
private async reconcileReadyInstanceProjection(
|
|
@@ -3075,12 +3140,7 @@ export class ServiceStatefulRuntime {
|
|
|
3075
3140
|
route.objectGeneration,
|
|
3076
3141
|
route.authorityOwnerGeneration
|
|
3077
3142
|
);
|
|
3078
|
-
if (!routeMatchesLocal(
|
|
3079
|
-
route,
|
|
3080
|
-
activation.spot,
|
|
3081
|
-
this.nodeRid,
|
|
3082
|
-
this.nodeGeneration
|
|
3083
|
-
)) {
|
|
3143
|
+
if (!routeMatchesLocal(route, activation.spot, this.nodeRid, this.nodeGeneration)) {
|
|
3084
3144
|
if (activation.created) this.registry.closeSpot(activation.spot.ref);
|
|
3085
3145
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
3086
3146
|
}
|
|
@@ -3088,18 +3148,15 @@ export class ServiceStatefulRuntime {
|
|
|
3088
3148
|
|
|
3089
3149
|
const confirmed = await authority.read(target);
|
|
3090
3150
|
if (confirmed.kind !== 'ready' || !sameInstanceRoute(confirmed.route, route)) {
|
|
3091
|
-
if (
|
|
3092
|
-
confirmed.kind === 'ready'
|
|
3093
|
-
&& confirmed.route.targetNodeRid !== this.nodeRid
|
|
3094
|
-
) {
|
|
3151
|
+
if (confirmed.kind === 'ready' && confirmed.route.targetNodeRid !== this.nodeRid) {
|
|
3095
3152
|
throw new ServiceInstanceActivationRedirectError(confirmed.route);
|
|
3096
3153
|
}
|
|
3097
3154
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
3098
3155
|
}
|
|
3099
3156
|
const current = this.registry.spot(target.targetSpotId);
|
|
3100
3157
|
if (
|
|
3101
|
-
current === undefined
|
|
3102
|
-
|
|
3158
|
+
current === undefined ||
|
|
3159
|
+
!routeMatchesLocal(confirmed.route, current, this.nodeRid, this.nodeGeneration)
|
|
3103
3160
|
) {
|
|
3104
3161
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
3105
3162
|
}
|
|
@@ -3112,11 +3169,12 @@ export class ServiceStatefulRuntime {
|
|
|
3112
3169
|
expectedCurrentRoute?: ServiceInstanceRouteFence | null
|
|
3113
3170
|
): void {
|
|
3114
3171
|
const directRoute = instanceRouteAsDirectRoute(route);
|
|
3115
|
-
const expectedDirectRoute =
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3172
|
+
const expectedDirectRoute =
|
|
3173
|
+
expectedCurrentRoute === undefined
|
|
3174
|
+
? undefined
|
|
3175
|
+
: expectedCurrentRoute === null
|
|
3176
|
+
? null
|
|
3177
|
+
: instanceRouteAsDirectRoute(expectedCurrentRoute);
|
|
3120
3178
|
this.rememberSpotRoute(directRoute, expectedDirectRoute);
|
|
3121
3179
|
this.registerInstanceIntent(stableType, route, expectedCurrentRoute);
|
|
3122
3180
|
}
|
|
@@ -3150,9 +3208,9 @@ export class ServiceStatefulRuntime {
|
|
|
3150
3208
|
private forgetClosedInstanceRoute(route: ServiceInstanceRouteFence): void {
|
|
3151
3209
|
const local = this.registry.spot(route.targetSpotId);
|
|
3152
3210
|
if (
|
|
3153
|
-
local?.kind === 'instance'
|
|
3154
|
-
|
|
3155
|
-
|
|
3211
|
+
local?.kind === 'instance' &&
|
|
3212
|
+
local.ref.generation === route.objectGeneration &&
|
|
3213
|
+
local.authorityOwnerGeneration === route.authorityOwnerGeneration
|
|
3156
3214
|
) {
|
|
3157
3215
|
this.registry.closeSpot(local.ref);
|
|
3158
3216
|
}
|
|
@@ -3279,8 +3337,8 @@ export class ServiceStatefulRuntime {
|
|
|
3279
3337
|
if (record.binding.state === 'active') {
|
|
3280
3338
|
const actor = this.validateActorFence(record.actor);
|
|
3281
3339
|
const previous = this.registry.binding(actor.ref);
|
|
3282
|
-
const sourceGeneration =
|
|
3283
|
-
?? this.peerGeneration(ingress.sourceRoutingId);
|
|
3340
|
+
const sourceGeneration =
|
|
3341
|
+
ingress.sourceNodeGeneration ?? this.peerGeneration(ingress.sourceRoutingId);
|
|
3284
3342
|
const binding = sessionBindingFromWire(
|
|
3285
3343
|
actor.ref,
|
|
3286
3344
|
record.sessionRid,
|
|
@@ -3337,9 +3395,9 @@ export class ServiceStatefulRuntime {
|
|
|
3337
3395
|
: undefined
|
|
3338
3396
|
);
|
|
3339
3397
|
if (
|
|
3340
|
-
terminalResult === RequestResult.Ok
|
|
3341
|
-
|
|
3342
|
-
|
|
3398
|
+
terminalResult === RequestResult.Ok &&
|
|
3399
|
+
replacedBinding !== undefined &&
|
|
3400
|
+
!sameSessionBinding(replacedBinding, completedBinding)
|
|
3343
3401
|
) {
|
|
3344
3402
|
this.sendBoundSessionReplacement(
|
|
3345
3403
|
replacedBinding,
|
|
@@ -3353,16 +3411,13 @@ export class ServiceStatefulRuntime {
|
|
|
3353
3411
|
}
|
|
3354
3412
|
);
|
|
3355
3413
|
if (!accepted) {
|
|
3356
|
-
const result = failure(
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
ingress,
|
|
3362
|
-
record.correlation,
|
|
3363
|
-
result.terminalResult,
|
|
3364
|
-
result.failureCode
|
|
3414
|
+
const result = failure(
|
|
3415
|
+
createInternalFrameworkException(
|
|
3416
|
+
ZLinkFrameworkInternalErrorKind.SpotMoving,
|
|
3417
|
+
`Actor '${actor.ref.actorId}' no longer accepts session bindings.`
|
|
3418
|
+
)
|
|
3365
3419
|
);
|
|
3420
|
+
this.replyWire(ingress, record.correlation, result.terminalResult, result.failureCode);
|
|
3366
3421
|
}
|
|
3367
3422
|
} else {
|
|
3368
3423
|
this.enqueueSessionBindingTombstone(ingress, record);
|
|
@@ -3388,8 +3443,8 @@ export class ServiceStatefulRuntime {
|
|
|
3388
3443
|
if (authority.actor.nodeRid !== ingress.sourceRoutingId) {
|
|
3389
3444
|
return 'protocolError';
|
|
3390
3445
|
}
|
|
3391
|
-
const sourceGeneration =
|
|
3392
|
-
?? this.tryPeerGeneration(ingress.sourceRoutingId);
|
|
3446
|
+
const sourceGeneration =
|
|
3447
|
+
ingress.sourceNodeGeneration ?? this.tryPeerGeneration(ingress.sourceRoutingId);
|
|
3393
3448
|
if (sourceGeneration === undefined) return 'infrastructure';
|
|
3394
3449
|
if (sourceGeneration !== authority.targetNodeGeneration) {
|
|
3395
3450
|
// A notice from a restarted authority is stale. It must not tear down a
|
|
@@ -3397,10 +3452,10 @@ export class ServiceStatefulRuntime {
|
|
|
3397
3452
|
return 'infrastructure';
|
|
3398
3453
|
}
|
|
3399
3454
|
if (
|
|
3400
|
-
record.retiredSession.sessionOwnerNodeRid !== this.nodeRid
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3455
|
+
record.retiredSession.sessionOwnerNodeRid !== this.nodeRid ||
|
|
3456
|
+
record.retiredSession.sessionOwnerNodeGeneration !== this.nodeGeneration ||
|
|
3457
|
+
record.retiredSession.sessionOwnerId !== this.nodeRid ||
|
|
3458
|
+
record.retiredSession.sessionOwnerLeaseGeneration !== this.nodeGeneration
|
|
3404
3459
|
) {
|
|
3405
3460
|
return 'infrastructure';
|
|
3406
3461
|
}
|
|
@@ -3417,16 +3472,15 @@ export class ServiceStatefulRuntime {
|
|
|
3417
3472
|
sessionOwnerNodeGeneration: record.retiredSession.sessionOwnerNodeGeneration,
|
|
3418
3473
|
sessionOwnerId: record.retiredSession.sessionOwnerId,
|
|
3419
3474
|
sessionOwnerLeaseGeneration: record.retiredSession.sessionOwnerLeaseGeneration,
|
|
3420
|
-
bindingGeneration: record.retiredSession.retiredBindingGeneration
|
|
3475
|
+
bindingGeneration: record.retiredSession.retiredBindingGeneration
|
|
3421
3476
|
})
|
|
3422
3477
|
);
|
|
3423
|
-
const delivery =
|
|
3424
|
-
&& exactRetiredDelivery(current.binding, record.retiredSession)
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
: undefined;
|
|
3478
|
+
const delivery =
|
|
3479
|
+
current !== undefined && exactRetiredDelivery(current.binding, record.retiredSession)
|
|
3480
|
+
? current
|
|
3481
|
+
: retired !== undefined && exactRetiredDelivery(retired.binding, record.retiredSession)
|
|
3482
|
+
? retired
|
|
3483
|
+
: undefined;
|
|
3430
3484
|
if (delivery === undefined) return 'infrastructure';
|
|
3431
3485
|
this.appliedReplacementNotices.add(key);
|
|
3432
3486
|
delivery.onBindingReplaced?.(actor.actorId, record.retiredSession, actor);
|
|
@@ -3440,20 +3494,17 @@ export class ServiceStatefulRuntime {
|
|
|
3440
3494
|
previous: ServiceSessionBinding,
|
|
3441
3495
|
actorAuthority: ServiceBoundSessionActorAuthority
|
|
3442
3496
|
): void {
|
|
3443
|
-
const ownerGeneration =
|
|
3444
|
-
?? this.tryPeerGeneration(previous.sessionOwnerNodeRid);
|
|
3497
|
+
const ownerGeneration =
|
|
3498
|
+
previous.sessionOwnerNodeGeneration ?? this.tryPeerGeneration(previous.sessionOwnerNodeRid);
|
|
3445
3499
|
if (ownerGeneration === undefined) return;
|
|
3446
|
-
const header = encodeBoundSessionReplacedHeader(
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
retiredBindingGeneration: previous.bindingGeneration
|
|
3455
|
-
}
|
|
3456
|
-
);
|
|
3500
|
+
const header = encodeBoundSessionReplacedHeader(actorAuthority, {
|
|
3501
|
+
sessionOwnerNodeRid: previous.sessionOwnerNodeRid,
|
|
3502
|
+
sessionOwnerNodeGeneration: ownerGeneration,
|
|
3503
|
+
sessionOwnerId: previous.sessionOwnerId ?? previous.sessionOwnerNodeRid,
|
|
3504
|
+
sessionOwnerLeaseGeneration: previous.sessionOwnerLeaseGeneration ?? ownerGeneration,
|
|
3505
|
+
sessionRid: previous.sessionRid,
|
|
3506
|
+
retiredBindingGeneration: previous.bindingGeneration
|
|
3507
|
+
});
|
|
3457
3508
|
// submitOneWay owns the normal transport admission timeout. A replacement
|
|
3458
3509
|
// notice is one-way, so an unsuccessful admission has no caller terminal
|
|
3459
3510
|
// to complete and must not delay the already-completed bind.
|
|
@@ -3467,26 +3518,26 @@ export class ServiceStatefulRuntime {
|
|
|
3467
3518
|
if (record.binding.state !== 'tombstone') return;
|
|
3468
3519
|
const actor = record.actor.actor;
|
|
3469
3520
|
const current = this.registry.binding(actor);
|
|
3470
|
-
const sourceGeneration =
|
|
3471
|
-
?? this.tryPeerGeneration(ingress.sourceRoutingId);
|
|
3521
|
+
const sourceGeneration =
|
|
3522
|
+
ingress.sourceNodeGeneration ?? this.tryPeerGeneration(ingress.sourceRoutingId);
|
|
3472
3523
|
if (
|
|
3473
|
-
current === undefined
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3524
|
+
current === undefined ||
|
|
3525
|
+
sourceGeneration === undefined ||
|
|
3526
|
+
current.bindingGeneration !== record.binding.retiredGeneration ||
|
|
3527
|
+
current.sessionRid !== record.sessionRid ||
|
|
3528
|
+
current.sessionOwnerNodeRid !== ingress.sourceRoutingId ||
|
|
3529
|
+
current.sessionOwnerNodeGeneration !== sourceGeneration ||
|
|
3530
|
+
current.sessionOwnerId !== ingress.sourceRoutingId ||
|
|
3531
|
+
current.sessionOwnerLeaseGeneration !== sourceGeneration
|
|
3481
3532
|
) {
|
|
3482
3533
|
this.replyWire(ingress, record.correlation, RequestResult.Ok, 0);
|
|
3483
3534
|
return;
|
|
3484
3535
|
}
|
|
3485
3536
|
const delivery = this.sessionDeliveries.get(actorKey(actor));
|
|
3486
|
-
const currentDelivery =
|
|
3487
|
-
&& sameSessionBinding(delivery.binding, current)
|
|
3488
|
-
|
|
3489
|
-
|
|
3537
|
+
const currentDelivery =
|
|
3538
|
+
delivery !== undefined && sameSessionBinding(delivery.binding, current)
|
|
3539
|
+
? delivery
|
|
3540
|
+
: undefined;
|
|
3490
3541
|
const accepted = this.enqueueSessionBindingRetirement(
|
|
3491
3542
|
ingress,
|
|
3492
3543
|
current,
|
|
@@ -3503,8 +3554,8 @@ export class ServiceStatefulRuntime {
|
|
|
3503
3554
|
);
|
|
3504
3555
|
}
|
|
3505
3556
|
if (
|
|
3506
|
-
currentDelivery !== undefined
|
|
3507
|
-
|
|
3557
|
+
currentDelivery !== undefined &&
|
|
3558
|
+
this.sessionDeliveries.get(actorKey(actor)) === currentDelivery
|
|
3508
3559
|
) {
|
|
3509
3560
|
this.sessionDeliveries.delete(actorKey(actor));
|
|
3510
3561
|
}
|
|
@@ -3514,10 +3565,12 @@ export class ServiceStatefulRuntime {
|
|
|
3514
3565
|
}
|
|
3515
3566
|
);
|
|
3516
3567
|
if (!accepted) {
|
|
3517
|
-
const result = failure(
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3568
|
+
const result = failure(
|
|
3569
|
+
createInternalFrameworkException(
|
|
3570
|
+
ZLinkFrameworkInternalErrorKind.SpotMoving,
|
|
3571
|
+
`Actor '${actor.actorId}' no longer accepts session binding tombstones.`
|
|
3572
|
+
)
|
|
3573
|
+
);
|
|
3521
3574
|
this.replyWire(ingress, record.correlation, result.terminalResult, result.failureCode);
|
|
3522
3575
|
}
|
|
3523
3576
|
}
|
|
@@ -3569,12 +3622,12 @@ export class ServiceStatefulRuntime {
|
|
|
3569
3622
|
): Promise<RawServicePumpResult> {
|
|
3570
3623
|
const delivery = this.sessionDeliveries.get(actorKey(record.actor.actor));
|
|
3571
3624
|
if (
|
|
3572
|
-
delivery === undefined
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3625
|
+
delivery === undefined ||
|
|
3626
|
+
delivery.binding.bindingGeneration !== record.expectedBindingGeneration ||
|
|
3627
|
+
payloadFrame === undefined ||
|
|
3628
|
+
ingress.sourceNodeGeneration === undefined ||
|
|
3629
|
+
!routingIdsEqual(ingress.sourceRoutingId, record.actor.actor.nodeRid) ||
|
|
3630
|
+
ingress.sourceNodeGeneration !== record.actor.targetNodeGeneration
|
|
3578
3631
|
) {
|
|
3579
3632
|
return 'protocolError';
|
|
3580
3633
|
}
|
|
@@ -3598,18 +3651,21 @@ export class ServiceStatefulRuntime {
|
|
|
3598
3651
|
settle();
|
|
3599
3652
|
}
|
|
3600
3653
|
};
|
|
3601
|
-
const decision = await delivery.bindingIngress?.retainOutbound(
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3654
|
+
const decision = await delivery.bindingIngress?.retainOutbound(
|
|
3655
|
+
{
|
|
3656
|
+
actorId: record.actor.actor.actorId,
|
|
3657
|
+
objectGeneration: record.actor.actor.generation,
|
|
3658
|
+
actorNodeRid: record.actor.actor.nodeRid,
|
|
3659
|
+
actorNodeGeneration: record.actor.targetNodeGeneration,
|
|
3660
|
+
authorityOwnerGeneration: record.actor.authorityOwnerGeneration,
|
|
3661
|
+
ownerLeaseGeneration: record.actor.ownerLeaseGeneration,
|
|
3662
|
+
producerNodeRid: ingress.sourceRoutingId,
|
|
3663
|
+
producerNodeGeneration: ingress.sourceNodeGeneration,
|
|
3664
|
+
sessionIdentity: sessionRid,
|
|
3665
|
+
bindingGeneration: record.expectedBindingGeneration
|
|
3666
|
+
},
|
|
3667
|
+
operation
|
|
3668
|
+
);
|
|
3613
3669
|
if (decision === 'retained') return 'application';
|
|
3614
3670
|
if (decision === 'rejected') {
|
|
3615
3671
|
return 'protocolError';
|
|
@@ -3624,9 +3680,9 @@ export class ServiceStatefulRuntime {
|
|
|
3624
3680
|
// already completed, so a later client delivery terminal is local to
|
|
3625
3681
|
// this session owner. Local fast paths still preserve the typed result.
|
|
3626
3682
|
if (
|
|
3627
|
-
ingress.sourceRoutingId !== this.nodeRid
|
|
3628
|
-
|
|
3629
|
-
|
|
3683
|
+
ingress.sourceRoutingId !== this.nodeRid &&
|
|
3684
|
+
isZLinkBackendResultError(error) &&
|
|
3685
|
+
error.operation === 'submit'
|
|
3630
3686
|
) {
|
|
3631
3687
|
return 'application';
|
|
3632
3688
|
}
|
|
@@ -3668,7 +3724,9 @@ export class ServiceStatefulRuntime {
|
|
|
3668
3724
|
sourceRoutingId: ingress.sourceRoutingId,
|
|
3669
3725
|
sourceRoute: ingress.sourceRoute,
|
|
3670
3726
|
...(ingress.reply === undefined ? {} : { reply: ingress.reply }),
|
|
3671
|
-
...(ingress.requestSequence === undefined
|
|
3727
|
+
...(ingress.requestSequence === undefined
|
|
3728
|
+
? {}
|
|
3729
|
+
: { requestSequence: ingress.requestSequence }),
|
|
3672
3730
|
...(stateful.correlation === undefined ? {} : { correlation: stateful.correlation }),
|
|
3673
3731
|
stateful,
|
|
3674
3732
|
applicationJob
|
|
@@ -3676,10 +3734,12 @@ export class ServiceStatefulRuntime {
|
|
|
3676
3734
|
if (accepted) return domain;
|
|
3677
3735
|
applicationJob.close();
|
|
3678
3736
|
if (stateful.reply !== undefined) {
|
|
3679
|
-
const result = failure(
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3737
|
+
const result = failure(
|
|
3738
|
+
createInternalFrameworkException(
|
|
3739
|
+
ZLinkFrameworkInternalErrorKind.SpotMoving,
|
|
3740
|
+
`Mailbox owner '${owner}' no longer accepts work on this runtime.`
|
|
3741
|
+
)
|
|
3742
|
+
);
|
|
3683
3743
|
stateful.reply(result.terminalResult, result.failureCode);
|
|
3684
3744
|
return 'infrastructure';
|
|
3685
3745
|
}
|
|
@@ -3717,11 +3777,15 @@ export class ServiceStatefulRuntime {
|
|
|
3717
3777
|
applicationJobOwner: NonNullable<RawServiceIngressRecord['applicationJobOwner']>
|
|
3718
3778
|
): Promise<void> {
|
|
3719
3779
|
const targets = [...this.subscriptions.entries()]
|
|
3720
|
-
.filter(([, values]) =>
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3780
|
+
.filter(([, values]) =>
|
|
3781
|
+
[...values].some((value) => {
|
|
3782
|
+
const separator = value.indexOf('\0');
|
|
3783
|
+
return (
|
|
3784
|
+
value.slice(0, separator) === channelName &&
|
|
3785
|
+
topicMatches(value.slice(separator + 1), topic)
|
|
3786
|
+
);
|
|
3787
|
+
})
|
|
3788
|
+
)
|
|
3725
3789
|
.map(([spotId]) => spotId)
|
|
3726
3790
|
.sort();
|
|
3727
3791
|
for (const spotId of targets) {
|
|
@@ -3731,10 +3795,7 @@ export class ServiceStatefulRuntime {
|
|
|
3731
3795
|
const accepted = this.raw.mailbox.tryEnqueue({
|
|
3732
3796
|
owner: `spot:${spotId}`,
|
|
3733
3797
|
domain: 'application',
|
|
3734
|
-
parts: [
|
|
3735
|
-
encodeLogicalMulticastHeader(channelName, topic, sourceSpotId),
|
|
3736
|
-
payloadFrame
|
|
3737
|
-
],
|
|
3798
|
+
parts: [encodeLogicalMulticastHeader(channelName, topic, sourceSpotId), payloadFrame],
|
|
3738
3799
|
sourceRoutingId: sourceNodeRid,
|
|
3739
3800
|
stateful: {
|
|
3740
3801
|
receiveKind: ReceiveKind.SpotMulticast,
|
|
@@ -3843,10 +3904,10 @@ export class ServiceStatefulRuntime {
|
|
|
3843
3904
|
private replyPort(
|
|
3844
3905
|
ingress: RawServiceIngressRecord,
|
|
3845
3906
|
correlation: bigint | undefined,
|
|
3846
|
-
operationKind: 'spotRequest' | 'actorRequest' | 'actorJoin'
|
|
3847
|
-
| 'instanceSpotRequest'
|
|
3907
|
+
operationKind: 'spotRequest' | 'actorRequest' | 'actorJoin' | 'instanceSpotRequest'
|
|
3848
3908
|
): NonNullable<ServiceStatefulMailboxData['reply']> {
|
|
3849
|
-
if (correlation === undefined)
|
|
3909
|
+
if (correlation === undefined)
|
|
3910
|
+
throw new ServiceWireProtocolError('Request correlation is missing.');
|
|
3850
3911
|
return (terminalResult, failureCode, payload, tail) => {
|
|
3851
3912
|
this.replyWire(ingress, correlation, terminalResult, failureCode, payload, tail);
|
|
3852
3913
|
void operationKind;
|
|
@@ -3873,26 +3934,22 @@ export class ServiceStatefulRuntime {
|
|
|
3873
3934
|
record: ServiceUserSpotCreateRecord | ServiceUserSpotCloseRecord | ServiceActorCreateRecord
|
|
3874
3935
|
): RawServicePumpResult {
|
|
3875
3936
|
if (
|
|
3876
|
-
ingress.requestSequence === undefined
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3937
|
+
ingress.requestSequence === undefined ||
|
|
3938
|
+
record.sourceNodeRid !== ingress.sourceRoutingId ||
|
|
3939
|
+
record.sourceNodeGeneration !== this.peerGeneration(ingress.sourceRoutingId) ||
|
|
3940
|
+
(record.kind === 'actorCreate' && record.reservation.pendingCapacityDelta !== 1)
|
|
3880
3941
|
) {
|
|
3881
3942
|
return 'protocolError';
|
|
3882
3943
|
}
|
|
3883
|
-
const target =
|
|
3884
|
-
|
|
3885
|
-
|
|
3944
|
+
const target =
|
|
3945
|
+
record.kind === 'userSpotCreate' || record.kind === 'actorCreate'
|
|
3946
|
+
? record.reservation
|
|
3947
|
+
: record.target;
|
|
3886
3948
|
if (
|
|
3887
|
-
target.targetNodeRid !== this.nodeRid
|
|
3888
|
-
|
|
3949
|
+
target.targetNodeRid !== this.nodeRid ||
|
|
3950
|
+
target.targetNodeGeneration !== this.nodeGeneration
|
|
3889
3951
|
) {
|
|
3890
|
-
this.replyWire(
|
|
3891
|
-
ingress,
|
|
3892
|
-
record.correlation,
|
|
3893
|
-
RequestResult.Conflict,
|
|
3894
|
-
SPOT_MOVING
|
|
3895
|
-
);
|
|
3952
|
+
this.replyWire(ingress, record.correlation, RequestResult.Conflict, SPOT_MOVING);
|
|
3896
3953
|
return 'infrastructure';
|
|
3897
3954
|
}
|
|
3898
3955
|
const operationKey = [
|
|
@@ -3903,10 +3960,7 @@ export class ServiceStatefulRuntime {
|
|
|
3903
3960
|
].join('\0');
|
|
3904
3961
|
const encodedRequest = userSpotOperationFingerprint(record);
|
|
3905
3962
|
let admitted = this.admittedUserSpotOperations.get(operationKey);
|
|
3906
|
-
if (
|
|
3907
|
-
admitted?.settled === true
|
|
3908
|
-
&& operationReplayExpired(admitted.replayExpiresAtMs)
|
|
3909
|
-
) {
|
|
3963
|
+
if (admitted?.settled === true && operationReplayExpired(admitted.replayExpiresAtMs)) {
|
|
3910
3964
|
this.admittedUserSpotOperations.delete(operationKey);
|
|
3911
3965
|
admitted = undefined;
|
|
3912
3966
|
}
|
|
@@ -3915,36 +3969,28 @@ export class ServiceStatefulRuntime {
|
|
|
3915
3969
|
}
|
|
3916
3970
|
if (admitted === undefined) {
|
|
3917
3971
|
if (record.deadlineUnixMs < BigInt(Date.now())) {
|
|
3918
|
-
this.replyWire(
|
|
3919
|
-
ingress,
|
|
3920
|
-
record.correlation,
|
|
3921
|
-
RequestResult.TimedOut,
|
|
3922
|
-
0
|
|
3923
|
-
);
|
|
3972
|
+
this.replyWire(ingress, record.correlation, RequestResult.TimedOut, 0);
|
|
3924
3973
|
return 'infrastructure';
|
|
3925
3974
|
}
|
|
3926
3975
|
if (this.admittedUserSpotOperations.size >= USER_SPOT_OPERATION_CAPACITY) {
|
|
3927
3976
|
this.releaseExpiredUserSpotOperations();
|
|
3928
3977
|
}
|
|
3929
3978
|
if (this.admittedUserSpotOperations.size >= USER_SPOT_OPERATION_CAPACITY) {
|
|
3930
|
-
this.replyWire(
|
|
3931
|
-
ingress,
|
|
3932
|
-
record.correlation,
|
|
3933
|
-
RequestResult.Busy,
|
|
3934
|
-
0
|
|
3935
|
-
);
|
|
3979
|
+
this.replyWire(ingress, record.correlation, RequestResult.Busy, 0);
|
|
3936
3980
|
return 'infrastructure';
|
|
3937
3981
|
}
|
|
3938
|
-
const replayExpiresAtMs =
|
|
3939
|
-
|
|
3940
|
-
+
|
|
3982
|
+
const replayExpiresAtMs =
|
|
3983
|
+
performance.now() +
|
|
3984
|
+
Number(record.deadlineUnixMs - BigInt(Date.now())) +
|
|
3985
|
+
USER_SPOT_OPERATION_REPLAY_RETENTION_MS;
|
|
3941
3986
|
const handler = this.userSpotOperationHandler;
|
|
3942
|
-
const result =
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3987
|
+
const result =
|
|
3988
|
+
handler === undefined
|
|
3989
|
+
? Promise.resolve<ServiceUserSpotOperationResult>({
|
|
3990
|
+
terminalResult: RequestResult.InvalidState,
|
|
3991
|
+
failureCode: 0
|
|
3992
|
+
})
|
|
3993
|
+
: this.executeUserSpotOperation(handler, record);
|
|
3948
3994
|
admitted = {
|
|
3949
3995
|
request: encodedRequest,
|
|
3950
3996
|
replayExpiresAtMs,
|
|
@@ -3952,13 +3998,15 @@ export class ServiceStatefulRuntime {
|
|
|
3952
3998
|
settled: false
|
|
3953
3999
|
};
|
|
3954
4000
|
this.admittedUserSpotOperations.set(operationKey, admitted);
|
|
3955
|
-
void result
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
4001
|
+
void result
|
|
4002
|
+
.finally(() => {
|
|
4003
|
+
const current = this.admittedUserSpotOperations.get(operationKey);
|
|
4004
|
+
if (current?.result === result) current.settled = true;
|
|
4005
|
+
})
|
|
4006
|
+
.catch(() => undefined);
|
|
3959
4007
|
}
|
|
3960
4008
|
void admitted.result.then(
|
|
3961
|
-
result => {
|
|
4009
|
+
(result) => {
|
|
3962
4010
|
if (this.closed) return;
|
|
3963
4011
|
this.raw.replyService(ingress, [
|
|
3964
4012
|
encodeStatefulReply(
|
|
@@ -3967,20 +4015,13 @@ export class ServiceStatefulRuntime {
|
|
|
3967
4015
|
result.failureCode,
|
|
3968
4016
|
result.tail
|
|
3969
4017
|
),
|
|
3970
|
-
...(result.payload === undefined
|
|
3971
|
-
? []
|
|
3972
|
-
: [encodeApplicationPayload(result.payload)])
|
|
4018
|
+
...(result.payload === undefined ? [] : [encodeApplicationPayload(result.payload)])
|
|
3973
4019
|
]);
|
|
3974
4020
|
},
|
|
3975
|
-
error => {
|
|
4021
|
+
(error) => {
|
|
3976
4022
|
if (this.closed) return;
|
|
3977
4023
|
const failed = failure(error);
|
|
3978
|
-
this.replyWire(
|
|
3979
|
-
ingress,
|
|
3980
|
-
record.correlation,
|
|
3981
|
-
failed.terminalResult,
|
|
3982
|
-
failed.failureCode
|
|
3983
|
-
);
|
|
4024
|
+
this.replyWire(ingress, record.correlation, failed.terminalResult, failed.failureCode);
|
|
3984
4025
|
}
|
|
3985
4026
|
);
|
|
3986
4027
|
return 'infrastructure';
|
|
@@ -4000,22 +4041,20 @@ export class ServiceStatefulRuntime {
|
|
|
4000
4041
|
): Promise<ServiceUserSpotOperationResult> {
|
|
4001
4042
|
const deadline = userSpotDeadline(record.deadlineUnixMs);
|
|
4002
4043
|
try {
|
|
4003
|
-
const result =
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4044
|
+
const result =
|
|
4045
|
+
record.kind === 'userSpotCreate'
|
|
4046
|
+
? await handler.create(record, deadline.signal)
|
|
4047
|
+
: record.kind === 'userSpotClose'
|
|
4048
|
+
? await handler.close(record, deadline.signal)
|
|
4049
|
+
: handler.createActor === undefined
|
|
4050
|
+
? {
|
|
4051
|
+
terminalResult: RequestResult.InvalidState,
|
|
4052
|
+
failureCode: 0
|
|
4053
|
+
}
|
|
4054
|
+
: await handler.createActor(record, deadline.signal);
|
|
4013
4055
|
if (
|
|
4014
|
-
result.terminalResult === RequestResult.Ok
|
|
4015
|
-
|
|
4016
|
-
result.tail === undefined
|
|
4017
|
-
|| result.tail.kind !== record.kind
|
|
4018
|
-
)
|
|
4056
|
+
result.terminalResult === RequestResult.Ok &&
|
|
4057
|
+
(result.tail === undefined || result.tail.kind !== record.kind)
|
|
4019
4058
|
) {
|
|
4020
4059
|
throw new ServiceWireProtocolError('User Spot success result omits its operation tail.');
|
|
4021
4060
|
}
|
|
@@ -4036,7 +4075,11 @@ export class ServiceStatefulRuntime {
|
|
|
4036
4075
|
timeoutMs: number
|
|
4037
4076
|
): Promise<ServiceUserSpotOperationResult> {
|
|
4038
4077
|
const parts = await this.requestDurableOperation(
|
|
4039
|
-
targetNodeRid,
|
|
4078
|
+
targetNodeRid,
|
|
4079
|
+
[header],
|
|
4080
|
+
correlation,
|
|
4081
|
+
operationKind,
|
|
4082
|
+
performance.now() + Math.max(1, timeoutMs)
|
|
4040
4083
|
);
|
|
4041
4084
|
// A received envelope, including a decode failure, ends sender replay.
|
|
4042
4085
|
try {
|
|
@@ -4044,25 +4087,17 @@ export class ServiceStatefulRuntime {
|
|
|
4044
4087
|
throw new ServiceWireProtocolError('Invalid creation operation reply parts.');
|
|
4045
4088
|
}
|
|
4046
4089
|
const decoded = decodeStatefulReply(parts[0]!, correlation, operationKind, parts.length >= 2);
|
|
4047
|
-
if (
|
|
4048
|
-
decoded.terminalResult !== RequestResult.Ok
|
|
4049
|
-
&& parts.length !== 1
|
|
4050
|
-
) {
|
|
4090
|
+
if (decoded.terminalResult !== RequestResult.Ok && parts.length !== 1) {
|
|
4051
4091
|
throw new ServiceWireProtocolError('Failed creation operation reply carries a payload.');
|
|
4052
4092
|
}
|
|
4053
|
-
if (
|
|
4054
|
-
operationKind === 'userSpotClose'
|
|
4055
|
-
&& parts.length !== 1
|
|
4056
|
-
) {
|
|
4093
|
+
if (operationKind === 'userSpotClose' && parts.length !== 1) {
|
|
4057
4094
|
throw new ServiceWireProtocolError('User Spot close reply carries a payload.');
|
|
4058
4095
|
}
|
|
4059
4096
|
return {
|
|
4060
4097
|
terminalResult: decoded.terminalResult,
|
|
4061
4098
|
failureCode: decoded.failureCode,
|
|
4062
4099
|
...(decoded.tail === undefined ? {} : { tail: decoded.tail as never }),
|
|
4063
|
-
...(parts.length === 2
|
|
4064
|
-
? { payload: decodeApplicationPayload(parts[1]!) }
|
|
4065
|
-
: {})
|
|
4100
|
+
...(parts.length === 2 ? { payload: decodeApplicationPayload(parts[1]!) } : {})
|
|
4066
4101
|
};
|
|
4067
4102
|
} catch (error) {
|
|
4068
4103
|
throw translateWireReplyDecodeError(error);
|
|
@@ -4077,13 +4112,15 @@ export class ServiceStatefulRuntime {
|
|
|
4077
4112
|
deadlineMs: number
|
|
4078
4113
|
): Promise<readonly Buffer[]> {
|
|
4079
4114
|
const stop = new AbortController();
|
|
4080
|
-
const unobserve = this.raw.observePeerConnectionIntentRemoved(nodeRoutingId => {
|
|
4115
|
+
const unobserve = this.raw.observePeerConnectionIntentRemoved((nodeRoutingId) => {
|
|
4081
4116
|
if (nodeRoutingId === targetNodeRid) {
|
|
4082
|
-
stop.abort(
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4117
|
+
stop.abort(
|
|
4118
|
+
createInternalFrameworkException(
|
|
4119
|
+
ZLinkFrameworkInternalErrorKind.RouteNotConnected,
|
|
4120
|
+
`${operationKind} target node lifecycle ended.`,
|
|
4121
|
+
true
|
|
4122
|
+
)
|
|
4123
|
+
);
|
|
4087
4124
|
}
|
|
4088
4125
|
});
|
|
4089
4126
|
let wasAdmitted = false;
|
|
@@ -4112,9 +4149,12 @@ export class ServiceStatefulRuntime {
|
|
|
4112
4149
|
}
|
|
4113
4150
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
4114
4151
|
try {
|
|
4115
|
-
await awaitWithAbort(
|
|
4116
|
-
|
|
4117
|
-
|
|
4152
|
+
await awaitWithAbort(
|
|
4153
|
+
new Promise<void>((resolve) => {
|
|
4154
|
+
timer = setTimeout(resolve, retryDelayMs);
|
|
4155
|
+
}),
|
|
4156
|
+
stop.signal
|
|
4157
|
+
);
|
|
4118
4158
|
} finally {
|
|
4119
4159
|
if (timer !== undefined) clearTimeout(timer);
|
|
4120
4160
|
}
|
|
@@ -4148,7 +4188,7 @@ export class ServiceStatefulRuntime {
|
|
|
4148
4188
|
if (settled) return;
|
|
4149
4189
|
settled = true;
|
|
4150
4190
|
clearTimeout(timer);
|
|
4151
|
-
resolve(parts.map(part => Buffer.from(part)));
|
|
4191
|
+
resolve(parts.map((part) => Buffer.from(part)));
|
|
4152
4192
|
};
|
|
4153
4193
|
void (async () => {
|
|
4154
4194
|
const applicationJobOwner = await this.raw.reserveLocalIngress();
|
|
@@ -4163,9 +4203,11 @@ export class ServiceStatefulRuntime {
|
|
|
4163
4203
|
applicationJobOwner
|
|
4164
4204
|
});
|
|
4165
4205
|
if (result !== 'infrastructure') {
|
|
4166
|
-
fail(
|
|
4167
|
-
|
|
4168
|
-
|
|
4206
|
+
fail(
|
|
4207
|
+
new ServiceWireProtocolError(
|
|
4208
|
+
`Local infrastructure request was rejected as '${result ?? 'unsupported'}'.`
|
|
4209
|
+
)
|
|
4210
|
+
);
|
|
4169
4211
|
}
|
|
4170
4212
|
} finally {
|
|
4171
4213
|
applicationJobOwner.close();
|
|
@@ -4174,10 +4216,7 @@ export class ServiceStatefulRuntime {
|
|
|
4174
4216
|
});
|
|
4175
4217
|
}
|
|
4176
4218
|
|
|
4177
|
-
private async submitOneWay(
|
|
4178
|
-
targetNodeRid: string,
|
|
4179
|
-
parts: readonly Buffer[]
|
|
4180
|
-
): Promise<number> {
|
|
4219
|
+
private async submitOneWay(targetNodeRid: string, parts: readonly Buffer[]): Promise<number> {
|
|
4181
4220
|
this.requireOpen();
|
|
4182
4221
|
if (targetNodeRid === this.nodeRid) {
|
|
4183
4222
|
const applicationJobOwner = await this.raw.reserveLocalIngress();
|
|
@@ -4231,7 +4270,7 @@ export class ServiceStatefulRuntime {
|
|
|
4231
4270
|
applicationJobOwner.close();
|
|
4232
4271
|
}
|
|
4233
4272
|
}
|
|
4234
|
-
return await this.raw.sendService(targetNodeRid, parts)
|
|
4273
|
+
return (await this.raw.sendService(targetNodeRid, parts))
|
|
4235
4274
|
? SubmitResult.Ok
|
|
4236
4275
|
: SubmitResult.NotConnected;
|
|
4237
4276
|
}
|
|
@@ -4241,7 +4280,13 @@ export class ServiceStatefulRuntime {
|
|
|
4241
4280
|
targetNodeRid: string,
|
|
4242
4281
|
parts: readonly Buffer[],
|
|
4243
4282
|
timeoutMs: number,
|
|
4244
|
-
operationKind:
|
|
4283
|
+
operationKind:
|
|
4284
|
+
| 'spotRequest'
|
|
4285
|
+
| 'actorRequest'
|
|
4286
|
+
| 'actorLookup'
|
|
4287
|
+
| 'actorDestroy'
|
|
4288
|
+
| 'actorJoin'
|
|
4289
|
+
| 'streamBind'
|
|
4245
4290
|
| 'streamUnbind'
|
|
4246
4291
|
| 'instanceSpotRequest',
|
|
4247
4292
|
actor?: ServiceActorRef
|
|
@@ -4259,24 +4304,18 @@ export class ServiceStatefulRuntime {
|
|
|
4259
4304
|
applicationJobOwner
|
|
4260
4305
|
};
|
|
4261
4306
|
try {
|
|
4262
|
-
this.submitLocalRequest(
|
|
4263
|
-
localIngress,
|
|
4264
|
-
pending,
|
|
4265
|
-
operationKind,
|
|
4266
|
-
actor,
|
|
4267
|
-
deadlineUnixMs
|
|
4268
|
-
);
|
|
4307
|
+
this.submitLocalRequest(localIngress, pending, operationKind, actor, deadlineUnixMs);
|
|
4269
4308
|
} catch (error) {
|
|
4270
4309
|
this.operations.reply(pending.id, failure(error));
|
|
4271
4310
|
} finally {
|
|
4272
4311
|
applicationJobOwner.close();
|
|
4273
4312
|
}
|
|
4274
|
-
})().catch(error => this.operations.reply(pending.id, failure(error)));
|
|
4313
|
+
})().catch((error) => this.operations.reply(pending.id, failure(error)));
|
|
4275
4314
|
return;
|
|
4276
4315
|
}
|
|
4277
4316
|
void this.raw.requestService(targetNodeRid, parts, timeoutMs).then(
|
|
4278
|
-
reply => this.completeRemoteReply(pending, targetNodeRid, operationKind, actor, reply),
|
|
4279
|
-
error => this.operations.fail(pending.id, error)
|
|
4317
|
+
(reply) => this.completeRemoteReply(pending, targetNodeRid, operationKind, actor, reply),
|
|
4318
|
+
(error) => this.operations.fail(pending.id, error)
|
|
4280
4319
|
);
|
|
4281
4320
|
}
|
|
4282
4321
|
|
|
@@ -4289,9 +4328,10 @@ export class ServiceStatefulRuntime {
|
|
|
4289
4328
|
deadlineMs: number,
|
|
4290
4329
|
canonical?: ServiceCanonicalActorJoinCandidate
|
|
4291
4330
|
): void {
|
|
4292
|
-
const actorRoute =
|
|
4293
|
-
|
|
4294
|
-
|
|
4331
|
+
const actorRoute =
|
|
4332
|
+
canonical === undefined
|
|
4333
|
+
? this.tryActorFence(actor)
|
|
4334
|
+
: this.acceptCanonicalActorFence(actor, canonical.actorFence);
|
|
4295
4335
|
if (target === undefined || actorRoute === undefined) {
|
|
4296
4336
|
this.operations.reply(pending.id, {
|
|
4297
4337
|
terminalResult: RequestResult.NotFound,
|
|
@@ -4300,13 +4340,16 @@ export class ServiceStatefulRuntime {
|
|
|
4300
4340
|
return;
|
|
4301
4341
|
}
|
|
4302
4342
|
if (canonical !== undefined) {
|
|
4303
|
-
this.canonicalActorJoinHandoffs.set(
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4343
|
+
this.canonicalActorJoinHandoffs.set(
|
|
4344
|
+
pending.id,
|
|
4345
|
+
canonicalActorJoinHandoffId({
|
|
4346
|
+
sourceActorNodeRid: routingIdBytes(actor.nodeRid as never),
|
|
4347
|
+
actorId: actor.actorId,
|
|
4348
|
+
actorGeneration: actor.generation,
|
|
4349
|
+
sourceActorNodeGeneration: actorRoute.targetNodeGeneration,
|
|
4350
|
+
correlation: pending.id
|
|
4351
|
+
})
|
|
4352
|
+
);
|
|
4310
4353
|
}
|
|
4311
4354
|
let parts: readonly Buffer[];
|
|
4312
4355
|
try {
|
|
@@ -4330,7 +4373,7 @@ export class ServiceStatefulRuntime {
|
|
|
4330
4373
|
expectedOwnerLeaseGeneration: target.ownerLeaseGeneration
|
|
4331
4374
|
},
|
|
4332
4375
|
...(payload === undefined ? {} : { payload })
|
|
4333
|
-
}).map(frame => Buffer.from(frame));
|
|
4376
|
+
}).map((frame) => Buffer.from(frame));
|
|
4334
4377
|
} catch (error) {
|
|
4335
4378
|
this.canonicalActorJoinHandoffs.delete(pending.id);
|
|
4336
4379
|
this.operations.fail(pending.id, error);
|
|
@@ -4338,14 +4381,24 @@ export class ServiceStatefulRuntime {
|
|
|
4338
4381
|
}
|
|
4339
4382
|
if (targetNodeRid === this.nodeRid) {
|
|
4340
4383
|
this.submitRequest(
|
|
4341
|
-
pending,
|
|
4342
|
-
|
|
4384
|
+
pending,
|
|
4385
|
+
targetNodeRid,
|
|
4386
|
+
parts,
|
|
4387
|
+
Math.max(0, Math.ceil(deadlineMs - performance.now())),
|
|
4388
|
+
'actorJoin',
|
|
4389
|
+
actor
|
|
4343
4390
|
);
|
|
4344
4391
|
return;
|
|
4345
4392
|
}
|
|
4346
|
-
void this.requestDurableOperation(
|
|
4347
|
-
|
|
4348
|
-
|
|
4393
|
+
void this.requestDurableOperation(
|
|
4394
|
+
targetNodeRid,
|
|
4395
|
+
parts,
|
|
4396
|
+
pending.id,
|
|
4397
|
+
'actorJoin',
|
|
4398
|
+
deadlineMs
|
|
4399
|
+
).then(
|
|
4400
|
+
(reply) => this.completeRemoteReply(pending, targetNodeRid, 'actorJoin', actor, reply),
|
|
4401
|
+
(error) => {
|
|
4349
4402
|
this.canonicalActorJoinHandoffs.delete(pending.id);
|
|
4350
4403
|
this.operations.fail(pending.id, error);
|
|
4351
4404
|
}
|
|
@@ -4355,34 +4408,46 @@ export class ServiceStatefulRuntime {
|
|
|
4355
4408
|
private submitLocalRequest(
|
|
4356
4409
|
ingress: RawServiceIngressRecord,
|
|
4357
4410
|
pending: ServiceStatefulPendingOperation,
|
|
4358
|
-
operationKind:
|
|
4411
|
+
operationKind:
|
|
4412
|
+
| 'spotRequest'
|
|
4413
|
+
| 'actorRequest'
|
|
4414
|
+
| 'actorLookup'
|
|
4415
|
+
| 'actorDestroy'
|
|
4416
|
+
| 'actorJoin'
|
|
4417
|
+
| 'streamBind'
|
|
4359
4418
|
| 'streamUnbind'
|
|
4360
4419
|
| 'instanceSpotRequest',
|
|
4361
4420
|
actor?: ServiceActorRef,
|
|
4362
4421
|
deadlineUnixMs?: bigint
|
|
4363
4422
|
): void {
|
|
4364
|
-
const canonicalActorJoin =
|
|
4365
|
-
? decodeActorJoin28(ingress.parts)
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
const payloadFrame =
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4423
|
+
const canonicalActorJoin =
|
|
4424
|
+
operationKind === 'actorJoin' ? decodeActorJoin28(ingress.parts) : undefined;
|
|
4425
|
+
const decoded =
|
|
4426
|
+
canonicalActorJoin === undefined
|
|
4427
|
+
? decodeStatefulHeader(ingress.parts[0]!)
|
|
4428
|
+
: statefulActorJoinFromCanonical(canonicalActorJoin);
|
|
4429
|
+
const payloadFrame =
|
|
4430
|
+
canonicalActorJoin?.payload === undefined
|
|
4431
|
+
? ingress.parts.length < 2
|
|
4432
|
+
? undefined
|
|
4433
|
+
: ingress.parts[1]
|
|
4434
|
+
: encodeApplicationPayload({
|
|
4435
|
+
packetName: canonicalActorJoin.payload.packetName,
|
|
4436
|
+
contentType: canonicalActorJoin.payload.contentType,
|
|
4437
|
+
payload: Buffer.from(canonicalActorJoin.payload.payload)
|
|
4438
|
+
});
|
|
4377
4439
|
if (decoded.kind === 'actorLookup') {
|
|
4378
4440
|
const found = this.registry.actor(decoded.actorId);
|
|
4379
|
-
this.operations.reply(
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4441
|
+
this.operations.reply(
|
|
4442
|
+
pending.id,
|
|
4443
|
+
found === undefined
|
|
4444
|
+
? { terminalResult: RequestResult.NotFound, failureCode: ACTOR_ROUTE_NOT_FOUND }
|
|
4445
|
+
: {
|
|
4446
|
+
terminalResult: RequestResult.Ok,
|
|
4447
|
+
failureCode: 0,
|
|
4448
|
+
kindData: lookupKindData(found)
|
|
4449
|
+
}
|
|
4450
|
+
);
|
|
4386
4451
|
return;
|
|
4387
4452
|
}
|
|
4388
4453
|
if (decoded.kind === 'actorDestroy') {
|
|
@@ -4399,35 +4464,28 @@ export class ServiceStatefulRuntime {
|
|
|
4399
4464
|
failureCode,
|
|
4400
4465
|
replyPayload,
|
|
4401
4466
|
tail
|
|
4402
|
-
) =>
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
replyPayload,
|
|
4406
|
-
|
|
4407
|
-
this.nodeRid,
|
|
4408
|
-
actor
|
|
4409
|
-
));
|
|
4467
|
+
) =>
|
|
4468
|
+
this.operations.reply(
|
|
4469
|
+
pending.id,
|
|
4470
|
+
this.resultFromReply(terminalResult, failureCode, replyPayload, tail, this.nodeRid, actor)
|
|
4471
|
+
);
|
|
4410
4472
|
if (decoded.kind === 'spotRequest') {
|
|
4411
4473
|
this.validateDirectSpotFence(decoded.target);
|
|
4412
|
-
this.enqueueApplicationFrame(
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
sourceSpotId: decoded.sourceSpotId,
|
|
4421
|
-
targetSpot: decoded.target.spot,
|
|
4422
|
-
reply: localReply
|
|
4423
|
-
}
|
|
4424
|
-
);
|
|
4474
|
+
this.enqueueApplicationFrame(ingress, `spot:${decoded.target.spot.spotId}`, payloadFrame!, {
|
|
4475
|
+
receiveKind: ReceiveKind.SpotRequest,
|
|
4476
|
+
operationKind: OperationKind.SpotRequest,
|
|
4477
|
+
correlation: decoded.correlation,
|
|
4478
|
+
sourceSpotId: decoded.sourceSpotId,
|
|
4479
|
+
targetSpot: decoded.target.spot,
|
|
4480
|
+
reply: localReply
|
|
4481
|
+
});
|
|
4425
4482
|
return;
|
|
4426
4483
|
}
|
|
4427
4484
|
if (decoded.kind === 'actorRequest') {
|
|
4428
|
-
const current =
|
|
4429
|
-
|
|
4430
|
-
|
|
4485
|
+
const current =
|
|
4486
|
+
decoded.boundSession === undefined
|
|
4487
|
+
? this.validateActorMessageFence(decoded.target)
|
|
4488
|
+
: this.validateActorFence(decoded.target);
|
|
4431
4489
|
this.enqueueApplicationFrame(
|
|
4432
4490
|
ingress,
|
|
4433
4491
|
`actor:${current.ref.actorId}\0${current.ref.generation}`,
|
|
@@ -4490,9 +4548,10 @@ export class ServiceStatefulRuntime {
|
|
|
4490
4548
|
onTerminalCompletion: () => completeOnJoinDispatchTerminal?.(),
|
|
4491
4549
|
reply: (terminalResult, failureCode, replyPayload, tail) => {
|
|
4492
4550
|
if (actorJoinReplyAccepted) return false;
|
|
4493
|
-
const acceptedJoin =
|
|
4494
|
-
|
|
4495
|
-
|
|
4551
|
+
const acceptedJoin =
|
|
4552
|
+
terminalResult === RequestResult.Ok &&
|
|
4553
|
+
tail?.kind === 'actorJoin' &&
|
|
4554
|
+
tail.joinResult === 0;
|
|
4496
4555
|
if (acceptedJoin && this.operations.isPending(pending.id)) {
|
|
4497
4556
|
// Commit membership now, but keep the source operation behind
|
|
4498
4557
|
// target OnJoinedActor. Entry finishes on this record; User
|
|
@@ -4517,12 +4576,7 @@ export class ServiceStatefulRuntime {
|
|
|
4517
4576
|
}
|
|
4518
4577
|
return true;
|
|
4519
4578
|
}
|
|
4520
|
-
const acceptedReply = localReply(
|
|
4521
|
-
terminalResult,
|
|
4522
|
-
failureCode,
|
|
4523
|
-
replyPayload,
|
|
4524
|
-
tail
|
|
4525
|
-
);
|
|
4579
|
+
const acceptedReply = localReply(terminalResult, failureCode, replyPayload, tail);
|
|
4526
4580
|
if (acceptedReply) actorJoinReplyAccepted = true;
|
|
4527
4581
|
return acceptedReply;
|
|
4528
4582
|
}
|
|
@@ -4534,13 +4588,15 @@ export class ServiceStatefulRuntime {
|
|
|
4534
4588
|
try {
|
|
4535
4589
|
const current = this.validateActorFence(decoded.actor);
|
|
4536
4590
|
if (decoded.binding.state === 'active') {
|
|
4537
|
-
this.registry.installSessionBinding(
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4591
|
+
this.registry.installSessionBinding(
|
|
4592
|
+
sessionBindingFromWire(
|
|
4593
|
+
current.ref,
|
|
4594
|
+
decoded.sessionRid,
|
|
4595
|
+
this.nodeRid,
|
|
4596
|
+
decoded.binding.generation,
|
|
4597
|
+
current.membershipEpoch
|
|
4598
|
+
)
|
|
4599
|
+
);
|
|
4544
4600
|
} else {
|
|
4545
4601
|
this.registry.unbindSession(
|
|
4546
4602
|
current.ref,
|
|
@@ -4557,29 +4613,19 @@ export class ServiceStatefulRuntime {
|
|
|
4557
4613
|
}
|
|
4558
4614
|
if (decoded.kind === 'instanceSpot') {
|
|
4559
4615
|
if (decoded.activation === 'missing' && this.asyncInstanceAuthority !== undefined) {
|
|
4560
|
-
void this.continueMissingInstanceActivation(
|
|
4561
|
-
ingress,
|
|
4562
|
-
decoded,
|
|
4563
|
-
payloadFrame!,
|
|
4564
|
-
localReply
|
|
4565
|
-
);
|
|
4616
|
+
void this.continueMissingInstanceActivation(ingress, decoded, payloadFrame!, localReply);
|
|
4566
4617
|
return;
|
|
4567
4618
|
}
|
|
4568
4619
|
try {
|
|
4569
4620
|
const activation = this.requireInstanceActivation(ingress, decoded);
|
|
4570
|
-
this.enqueueApplicationFrame(
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
{
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
...(decoded.sourceSpotId === undefined ? {} : { sourceSpotId: decoded.sourceSpotId }),
|
|
4579
|
-
targetSpot: activation.ref,
|
|
4580
|
-
reply: localReply
|
|
4581
|
-
}
|
|
4582
|
-
);
|
|
4621
|
+
this.enqueueApplicationFrame(ingress, `spot:${activation.ref.spotId}`, payloadFrame!, {
|
|
4622
|
+
receiveKind: ReceiveKind.InstanceSpotActivation,
|
|
4623
|
+
operationKind: OperationKind.InstanceSpotRequest,
|
|
4624
|
+
correlation: pending.id,
|
|
4625
|
+
...(decoded.sourceSpotId === undefined ? {} : { sourceSpotId: decoded.sourceSpotId }),
|
|
4626
|
+
targetSpot: activation.ref,
|
|
4627
|
+
reply: localReply
|
|
4628
|
+
});
|
|
4583
4629
|
} catch (error) {
|
|
4584
4630
|
this.operations.reply(pending.id, failure(error));
|
|
4585
4631
|
}
|
|
@@ -4595,28 +4641,37 @@ export class ServiceStatefulRuntime {
|
|
|
4595
4641
|
private completeRemoteReply(
|
|
4596
4642
|
pending: ServiceStatefulPendingOperation,
|
|
4597
4643
|
targetNodeRid: string,
|
|
4598
|
-
operationKind:
|
|
4644
|
+
operationKind:
|
|
4645
|
+
| 'spotRequest'
|
|
4646
|
+
| 'actorRequest'
|
|
4647
|
+
| 'actorLookup'
|
|
4648
|
+
| 'actorDestroy'
|
|
4649
|
+
| 'actorJoin'
|
|
4650
|
+
| 'streamBind'
|
|
4599
4651
|
| 'streamUnbind'
|
|
4600
4652
|
| 'instanceSpotRequest',
|
|
4601
4653
|
actor: ServiceActorRef | undefined,
|
|
4602
4654
|
reply: readonly Buffer[]
|
|
4603
4655
|
): void {
|
|
4604
|
-
const canonicalActorJoinHandoffId =
|
|
4605
|
-
? this.canonicalActorJoinHandoffs.get(pending.id)
|
|
4606
|
-
: undefined;
|
|
4656
|
+
const canonicalActorJoinHandoffId =
|
|
4657
|
+
operationKind === 'actorJoin' ? this.canonicalActorJoinHandoffs.get(pending.id) : undefined;
|
|
4607
4658
|
try {
|
|
4608
|
-
if (reply.length < 1 || reply.length > 2)
|
|
4659
|
+
if (reply.length < 1 || reply.length > 2)
|
|
4660
|
+
throw new ServiceWireProtocolError('Invalid M6B reply parts.');
|
|
4609
4661
|
const decoded = decodeStatefulReply(reply[0]!, pending.id, operationKind, reply.length >= 2);
|
|
4610
4662
|
const payload = reply.length < 2 ? undefined : decodeApplicationPayload(reply[1]);
|
|
4611
|
-
this.operations.reply(
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4663
|
+
this.operations.reply(
|
|
4664
|
+
pending.id,
|
|
4665
|
+
this.resultFromReply(
|
|
4666
|
+
decoded.terminalResult,
|
|
4667
|
+
decoded.failureCode,
|
|
4668
|
+
payload,
|
|
4669
|
+
decoded.tail,
|
|
4670
|
+
targetNodeRid,
|
|
4671
|
+
actor,
|
|
4672
|
+
canonicalActorJoinHandoffId
|
|
4673
|
+
)
|
|
4674
|
+
);
|
|
4620
4675
|
} catch (error) {
|
|
4621
4676
|
// Spec 32-framework-error-model:58-60, 91-92 — an unprocessable remote
|
|
4622
4677
|
// reply (decode/shape failure) fails the awaiting operation with a
|
|
@@ -4695,7 +4750,10 @@ export class ServiceStatefulRuntime {
|
|
|
4695
4750
|
}
|
|
4696
4751
|
|
|
4697
4752
|
private validateActorFence(fence: ServiceActorRouteFence): ServiceActorState {
|
|
4698
|
-
if (
|
|
4753
|
+
if (
|
|
4754
|
+
fence.actor.nodeRid !== this.nodeRid ||
|
|
4755
|
+
fence.targetNodeGeneration !== this.nodeGeneration
|
|
4756
|
+
) {
|
|
4699
4757
|
throw new ServiceStaleGenerationError('actor', fence.actor.actorId);
|
|
4700
4758
|
}
|
|
4701
4759
|
const actor = this.registry.requireActor(fence.actor);
|
|
@@ -4706,7 +4764,10 @@ export class ServiceStatefulRuntime {
|
|
|
4706
4764
|
}
|
|
4707
4765
|
|
|
4708
4766
|
private validateActorMessageFence(fence: ServiceActorRouteFence): ServiceActorState {
|
|
4709
|
-
if (
|
|
4767
|
+
if (
|
|
4768
|
+
fence.actor.nodeRid !== this.nodeRid ||
|
|
4769
|
+
fence.targetNodeGeneration !== this.nodeGeneration
|
|
4770
|
+
) {
|
|
4710
4771
|
throw new ServiceStaleGenerationError('actor', fence.actor.actorId);
|
|
4711
4772
|
}
|
|
4712
4773
|
const actor = this.registry.actor(fence.actor.actorId);
|
|
@@ -4720,7 +4781,10 @@ export class ServiceStatefulRuntime {
|
|
|
4720
4781
|
}
|
|
4721
4782
|
|
|
4722
4783
|
private validateSpotFence(fence: ServiceSpotRouteFence): ServiceSpotState {
|
|
4723
|
-
if (
|
|
4784
|
+
if (
|
|
4785
|
+
fence.targetNodeRid !== this.nodeRid ||
|
|
4786
|
+
fence.targetNodeGeneration !== this.nodeGeneration
|
|
4787
|
+
) {
|
|
4724
4788
|
throw new ServiceStaleGenerationError('spot', fence.spot.spotId);
|
|
4725
4789
|
}
|
|
4726
4790
|
const spot = this.registry.requireSpot(fence.spot);
|
|
@@ -4744,7 +4808,10 @@ export class ServiceStatefulRuntime {
|
|
|
4744
4808
|
`Spot '${fence.spot.spotId}' is not Ready.`
|
|
4745
4809
|
);
|
|
4746
4810
|
}
|
|
4747
|
-
if (
|
|
4811
|
+
if (
|
|
4812
|
+
fence.targetNodeRid !== this.nodeRid ||
|
|
4813
|
+
fence.targetNodeGeneration !== this.nodeGeneration
|
|
4814
|
+
) {
|
|
4748
4815
|
throw createInternalFrameworkException(
|
|
4749
4816
|
ZLinkFrameworkInternalErrorKind.SpotMoving,
|
|
4750
4817
|
`Spot '${fence.spot.spotId}' targets an unavailable owner route.`
|
|
@@ -4767,15 +4834,11 @@ export class ServiceStatefulRuntime {
|
|
|
4767
4834
|
);
|
|
4768
4835
|
}
|
|
4769
4836
|
if (
|
|
4770
|
-
current.targetNodeRid !== this.nodeRid
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
current.
|
|
4775
|
-
&& (
|
|
4776
|
-
current.authorityOwnerGeneration !== fence.authorityOwnerGeneration
|
|
4777
|
-
)
|
|
4778
|
-
)
|
|
4837
|
+
current.targetNodeRid !== this.nodeRid ||
|
|
4838
|
+
current.targetNodeGeneration !== this.nodeGeneration ||
|
|
4839
|
+
current.ownerLeaseGeneration !== fence.ownerLeaseGeneration ||
|
|
4840
|
+
(current.spot.generation === fence.spot.generation &&
|
|
4841
|
+
current.authorityOwnerGeneration !== fence.authorityOwnerGeneration)
|
|
4779
4842
|
) {
|
|
4780
4843
|
throw createInternalFrameworkException(
|
|
4781
4844
|
ZLinkFrameworkInternalErrorKind.SpotMoving,
|
|
@@ -4787,10 +4850,7 @@ export class ServiceStatefulRuntime {
|
|
|
4787
4850
|
|
|
4788
4851
|
private holdOrRelaySpotMessage(
|
|
4789
4852
|
ingress: RawServiceIngressRecord,
|
|
4790
|
-
wire: Extract<
|
|
4791
|
-
ServiceStatefulWireRecord,
|
|
4792
|
-
{ readonly kind: 'spotSend' | 'spotRequest' }
|
|
4793
|
-
>
|
|
4853
|
+
wire: Extract<ServiceStatefulWireRecord, { readonly kind: 'spotSend' | 'spotRequest' }>
|
|
4794
4854
|
): RawServicePumpResult | undefined {
|
|
4795
4855
|
const state = this.spotMessageFollow.get(spotKey(wire.target.spot));
|
|
4796
4856
|
if (state === undefined || !sameDirectSpotRoute(state.source, wire.target)) {
|
|
@@ -4855,7 +4915,7 @@ export class ServiceStatefulRuntime {
|
|
|
4855
4915
|
current.ingress.parts[1]!
|
|
4856
4916
|
];
|
|
4857
4917
|
if (current.wire.kind === 'spotSend') {
|
|
4858
|
-
if (!await this.raw.sendService(state.target.targetNodeRid, parts)) continue;
|
|
4918
|
+
if (!(await this.raw.sendService(state.target.targetNodeRid, parts))) continue;
|
|
4859
4919
|
} else {
|
|
4860
4920
|
try {
|
|
4861
4921
|
const remainingMs = Math.max(
|
|
@@ -4900,22 +4960,24 @@ export class ServiceStatefulRuntime {
|
|
|
4900
4960
|
if (target === undefined || suppressionFence === undefined) return;
|
|
4901
4961
|
const claim = this.spotMessageFollowSuppression.begin(suppressionFence);
|
|
4902
4962
|
if (claim === undefined) return;
|
|
4903
|
-
const operationLow =
|
|
4904
|
-
&& current.ingress.requestSequence !== 0n
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
const accepted = await this.raw.sendService(current.ingress.sourceRoutingId, [
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4963
|
+
const operationLow =
|
|
4964
|
+
current.ingress.requestSequence !== undefined && current.ingress.requestSequence !== 0n
|
|
4965
|
+
? current.ingress.requestSequence
|
|
4966
|
+
: this.nextMessageFollowOperation++;
|
|
4967
|
+
const accepted = await this.raw.sendService(current.ingress.sourceRoutingId, [
|
|
4968
|
+
encodeMessageFollowHeader({
|
|
4969
|
+
source: messageFollowSpotRoute(state.source),
|
|
4970
|
+
target: messageFollowSpotRoute(target),
|
|
4971
|
+
hopCount: 1,
|
|
4972
|
+
queuedMessages: Math.min(state.queuedCount, 0xffff_ffff),
|
|
4973
|
+
queuedBytes: Math.min(state.queuedBytes, 0xffff_ffff),
|
|
4974
|
+
originalOperation: {
|
|
4975
|
+
high: this.nodeGeneration,
|
|
4976
|
+
low: operationLow
|
|
4977
|
+
},
|
|
4978
|
+
originalReplyRouteId: current.ingress.requestSequence ?? 0n
|
|
4979
|
+
})
|
|
4980
|
+
]);
|
|
4919
4981
|
if (accepted) {
|
|
4920
4982
|
this.spotMessageFollowSuppression.markSent(claim);
|
|
4921
4983
|
} else {
|
|
@@ -4994,18 +5056,18 @@ export class ServiceStatefulRuntime {
|
|
|
4994
5056
|
senderFence: ServiceBoundSessionSendFence | undefined
|
|
4995
5057
|
): ServiceActorRouteFence | undefined {
|
|
4996
5058
|
if (
|
|
4997
|
-
senderFence === undefined
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5059
|
+
senderFence === undefined ||
|
|
5060
|
+
senderFence.targetNodeGeneration !== this.nodeGeneration ||
|
|
5061
|
+
senderFence.authorityOwnerGeneration <= 0n ||
|
|
5062
|
+
senderFence.ownerLeaseGeneration <= 0n
|
|
5001
5063
|
) {
|
|
5002
5064
|
return undefined;
|
|
5003
5065
|
}
|
|
5004
5066
|
const local = actor.nodeRid === this.nodeRid ? this.registry.actor(actor.actorId) : undefined;
|
|
5005
5067
|
if (
|
|
5006
|
-
local === undefined
|
|
5007
|
-
|
|
5008
|
-
|
|
5068
|
+
local === undefined ||
|
|
5069
|
+
!sameActorRef(local.ref, actor) ||
|
|
5070
|
+
local.authorityOwnerGeneration !== senderFence.authorityOwnerGeneration
|
|
5009
5071
|
) {
|
|
5010
5072
|
return undefined;
|
|
5011
5073
|
}
|
|
@@ -5043,10 +5105,10 @@ export class ServiceStatefulRuntime {
|
|
|
5043
5105
|
// Object/authority/lease are bounded counters. Node lifecycle is a
|
|
5044
5106
|
// full-range opaque token, where only zero represents absence.
|
|
5045
5107
|
if (
|
|
5046
|
-
actor.generation <= 0n
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5108
|
+
actor.generation <= 0n ||
|
|
5109
|
+
fence.targetNodeGeneration === 0n ||
|
|
5110
|
+
fence.authorityOwnerGeneration <= 0n ||
|
|
5111
|
+
fence.ownerLeaseGeneration <= 0n
|
|
5050
5112
|
) {
|
|
5051
5113
|
return undefined;
|
|
5052
5114
|
}
|
|
@@ -5073,17 +5135,18 @@ export class ServiceStatefulRuntime {
|
|
|
5073
5135
|
};
|
|
5074
5136
|
}
|
|
5075
5137
|
const route = this.spotRoutes.get(spotKey(spot));
|
|
5076
|
-
return route !== undefined
|
|
5077
|
-
|
|
5078
|
-
|
|
5138
|
+
return route !== undefined &&
|
|
5139
|
+
route.targetNodeRid === targetNodeRid &&
|
|
5140
|
+
sameSpotRef(route.spot, spot)
|
|
5079
5141
|
? route
|
|
5080
5142
|
: undefined;
|
|
5081
5143
|
}
|
|
5082
5144
|
|
|
5083
5145
|
private tryEntrySpotFence(targetNodeRid: string): ServiceDirectSpotRouteFence | undefined {
|
|
5084
|
-
const targetNodeGeneration =
|
|
5085
|
-
|
|
5086
|
-
|
|
5146
|
+
const targetNodeGeneration =
|
|
5147
|
+
targetNodeRid === this.nodeRid
|
|
5148
|
+
? this.nodeGeneration
|
|
5149
|
+
: this.raw.topology.peer(targetNodeRid)?.descriptor.lifecycleGeneration;
|
|
5087
5150
|
if (targetNodeGeneration === undefined) return undefined;
|
|
5088
5151
|
return {
|
|
5089
5152
|
spot: {
|
|
@@ -5105,28 +5168,24 @@ export class ServiceStatefulRuntime {
|
|
|
5105
5168
|
if (targetNodeRid === this.nodeRid) {
|
|
5106
5169
|
const local = this.registry.spot(spot.spotId);
|
|
5107
5170
|
if (
|
|
5108
|
-
local?.kind === 'entry'
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5171
|
+
local?.kind === 'entry' &&
|
|
5172
|
+
sameSpotRef(local.ref, spot) &&
|
|
5173
|
+
targetNodeGeneration === this.nodeGeneration &&
|
|
5174
|
+
requested.authorityOwnerGeneration === local.authorityOwnerGeneration &&
|
|
5175
|
+
isEntrySpotFence(requested)
|
|
5113
5176
|
) {
|
|
5114
5177
|
return requested;
|
|
5115
5178
|
}
|
|
5116
5179
|
const current = this.directSpotRoutes.get(directSpotKey(spot.spotId));
|
|
5117
|
-
return local !== undefined
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
current.
|
|
5126
|
-
|| (
|
|
5127
|
-
current.authorityOwnerGeneration === requested.authorityOwnerGeneration
|
|
5128
|
-
)
|
|
5129
|
-
)
|
|
5180
|
+
return local !== undefined &&
|
|
5181
|
+
local.state === 'ready' &&
|
|
5182
|
+
targetNodeGeneration === this.nodeGeneration &&
|
|
5183
|
+
current !== undefined &&
|
|
5184
|
+
current.targetNodeRid === this.nodeRid &&
|
|
5185
|
+
current.targetNodeGeneration === this.nodeGeneration &&
|
|
5186
|
+
current.ownerLeaseGeneration === requested.ownerLeaseGeneration &&
|
|
5187
|
+
(current.spot.generation !== requested.spot.generation ||
|
|
5188
|
+
current.authorityOwnerGeneration === requested.authorityOwnerGeneration)
|
|
5130
5189
|
? current
|
|
5131
5190
|
: undefined;
|
|
5132
5191
|
}
|
|
@@ -5160,9 +5219,8 @@ export class ServiceStatefulRuntime {
|
|
|
5160
5219
|
const current = this.registry.actor(actor.actorId);
|
|
5161
5220
|
const previousActor = current?.ref ?? actor;
|
|
5162
5221
|
const previousSpot = current?.spot;
|
|
5163
|
-
const previousSpotState =
|
|
5164
|
-
? undefined
|
|
5165
|
-
: this.registry.spot(previousSpot.spotId);
|
|
5222
|
+
const previousSpotState =
|
|
5223
|
+
previousSpot === undefined ? undefined : this.registry.spot(previousSpot.spotId);
|
|
5166
5224
|
const previousMembershipEpoch = current?.membershipEpoch ?? membershipEpoch - 1n;
|
|
5167
5225
|
if (current === undefined) {
|
|
5168
5226
|
this.registry.restoreActor(
|
|
@@ -5180,26 +5238,30 @@ export class ServiceStatefulRuntime {
|
|
|
5180
5238
|
});
|
|
5181
5239
|
const targetSpot = this.registry.spot(spot.spotId);
|
|
5182
5240
|
if (targetSpot?.kind !== 'entry') {
|
|
5183
|
-
this.enqueueActorControl(
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5241
|
+
this.enqueueActorControl(
|
|
5242
|
+
spot.spotId,
|
|
5243
|
+
{
|
|
5244
|
+
kind: 'actorControl',
|
|
5245
|
+
lifecycleKind: ActorLifecycleKind.Joined,
|
|
5246
|
+
previousActor,
|
|
5247
|
+
currentActor: committed.ref,
|
|
5248
|
+
previousSpotId: previousSpot?.spotId ?? null,
|
|
5249
|
+
currentSpotId: spot.spotId,
|
|
5250
|
+
previousSpotGeneration: previousSpot?.generation ?? 0n,
|
|
5251
|
+
currentSpotGeneration: spot.generation,
|
|
5252
|
+
previousMembershipEpoch,
|
|
5253
|
+
currentMembershipEpoch: committed.membershipEpoch,
|
|
5254
|
+
resultCode: 0
|
|
5255
|
+
},
|
|
5256
|
+
onTargetLifecycleCompleted
|
|
5257
|
+
);
|
|
5196
5258
|
}
|
|
5197
5259
|
if (
|
|
5198
|
-
actor.nodeRid === this.nodeRid
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5260
|
+
actor.nodeRid === this.nodeRid &&
|
|
5261
|
+
previousSpot !== undefined &&
|
|
5262
|
+
previousSpot.spotId !== spot.spotId &&
|
|
5263
|
+
previousSpotState?.ref.generation === previousSpot.generation &&
|
|
5264
|
+
(previousSpotState.kind === 'entry' || previousSpotState.kind === 'user')
|
|
5203
5265
|
) {
|
|
5204
5266
|
// A same-node Core join commits the target membership and publishes
|
|
5205
5267
|
// the source LEFT control separately. Remote joins use the formal
|
|
@@ -5221,18 +5283,41 @@ export class ServiceStatefulRuntime {
|
|
|
5221
5283
|
}
|
|
5222
5284
|
}
|
|
5223
5285
|
|
|
5224
|
-
|
|
5225
5286
|
private requireOpen(): void {
|
|
5226
5287
|
if (this.closed) throw new Error('Stateful runtime is closed.');
|
|
5227
5288
|
}
|
|
5228
5289
|
}
|
|
5229
5290
|
|
|
5291
|
+
function serviceSpotPublishFailureReason(
|
|
5292
|
+
result: number | undefined,
|
|
5293
|
+
routeReady: boolean,
|
|
5294
|
+
closed: boolean
|
|
5295
|
+
): 'stale_target' | 'backpressure' | 'shutdown' {
|
|
5296
|
+
if (closed || result === SubmitResult.Terminated) return 'shutdown';
|
|
5297
|
+
switch (result) {
|
|
5298
|
+
case SubmitResult.Backpressured:
|
|
5299
|
+
return 'backpressure';
|
|
5300
|
+
case SubmitResult.NotAdmitted:
|
|
5301
|
+
case SubmitResult.NotConnected:
|
|
5302
|
+
case SubmitResult.NotFound:
|
|
5303
|
+
case SubmitResult.InvalidHandle:
|
|
5304
|
+
return 'stale_target';
|
|
5305
|
+
default:
|
|
5306
|
+
// RawServiceMeshRuntime exposes failed Core submissions as false. A
|
|
5307
|
+
// ready route therefore identifies queue admission failure; a route
|
|
5308
|
+
// that became unready after selection is a stale target.
|
|
5309
|
+
return routeReady ? 'backpressure' : 'stale_target';
|
|
5310
|
+
}
|
|
5311
|
+
}
|
|
5312
|
+
|
|
5230
5313
|
function isEntrySpotFence(fence: ServiceDirectSpotRouteFence): boolean {
|
|
5231
|
-
return
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5314
|
+
return (
|
|
5315
|
+
fence.spot.spotId === fence.targetNodeRid &&
|
|
5316
|
+
fence.spot.generation === fence.targetNodeGeneration &&
|
|
5317
|
+
fence.authorityOwnerGeneration === fence.targetNodeGeneration &&
|
|
5318
|
+
fence.ownerLeaseGeneration === fence.targetNodeGeneration &&
|
|
5319
|
+
fence.storeVersion === `entry:${fence.targetNodeGeneration}`
|
|
5320
|
+
);
|
|
5236
5321
|
}
|
|
5237
5322
|
|
|
5238
5323
|
function userSpotDeadline(deadlineUnixMs: bigint): {
|
|
@@ -5280,10 +5365,10 @@ function durableRequestCanReplay(error: unknown): boolean {
|
|
|
5280
5365
|
if (!isZLinkBackendResultError(error)) return false;
|
|
5281
5366
|
return error.operation === 'request'
|
|
5282
5367
|
? error.result === RequestResult.NotConnected || error.result === RequestResult.TimedOut
|
|
5283
|
-
: error.result === SubmitResult.NotConnected
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5368
|
+
: error.result === SubmitResult.NotConnected ||
|
|
5369
|
+
error.result === SubmitResult.NotFound ||
|
|
5370
|
+
error.result === SubmitResult.NotAdmitted ||
|
|
5371
|
+
error.result === SubmitResult.Backpressured;
|
|
5287
5372
|
}
|
|
5288
5373
|
|
|
5289
5374
|
function remainingDeadlineMs(deadlineUnixMs: bigint): number {
|
|
@@ -5302,9 +5387,8 @@ function userSpotOperationFingerprint(
|
|
|
5302
5387
|
record: ServiceUserSpotCreateRecord | ServiceUserSpotCloseRecord | ServiceActorCreateRecord
|
|
5303
5388
|
): string {
|
|
5304
5389
|
const { correlation: _correlation, ...semantic } = record;
|
|
5305
|
-
return JSON.stringify(
|
|
5306
|
-
|
|
5307
|
-
(_key, value: unknown) => typeof value === 'bigint' ? `${value}n` : value
|
|
5390
|
+
return JSON.stringify(semantic, (_key, value: unknown) =>
|
|
5391
|
+
typeof value === 'bigint' ? `${value}n` : value
|
|
5308
5392
|
);
|
|
5309
5393
|
}
|
|
5310
5394
|
|
|
@@ -5339,32 +5423,44 @@ function actorKey(actor: ServiceActorRef): string {
|
|
|
5339
5423
|
}
|
|
5340
5424
|
|
|
5341
5425
|
function sameSessionBinding(left: ServiceSessionBinding, right: ServiceSessionBinding): boolean {
|
|
5342
|
-
return
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5426
|
+
return (
|
|
5427
|
+
sameActorRef(left.actor, right.actor) &&
|
|
5428
|
+
left.sessionRid === right.sessionRid &&
|
|
5429
|
+
sameSessionOwnerIdentity(left, right) &&
|
|
5430
|
+
left.bindingGeneration === right.bindingGeneration
|
|
5431
|
+
);
|
|
5346
5432
|
}
|
|
5347
5433
|
|
|
5348
5434
|
function sameSessionIdentity(left: ServiceSessionBinding, right: ServiceSessionBinding): boolean {
|
|
5349
|
-
return
|
|
5350
|
-
|
|
5351
|
-
|
|
5435
|
+
return (
|
|
5436
|
+
sameActorRef(left.actor, right.actor) &&
|
|
5437
|
+
left.sessionRid === right.sessionRid &&
|
|
5438
|
+
sameSessionOwnerIdentity(left, right)
|
|
5439
|
+
);
|
|
5352
5440
|
}
|
|
5353
5441
|
|
|
5354
5442
|
function sameSessionOwnerIdentity(
|
|
5355
5443
|
left: Pick<
|
|
5356
5444
|
ServiceSessionBinding,
|
|
5357
|
-
|
|
5445
|
+
| 'sessionOwnerNodeRid'
|
|
5446
|
+
| 'sessionOwnerNodeGeneration'
|
|
5447
|
+
| 'sessionOwnerId'
|
|
5448
|
+
| 'sessionOwnerLeaseGeneration'
|
|
5358
5449
|
>,
|
|
5359
5450
|
right: Pick<
|
|
5360
5451
|
ServiceSessionBinding,
|
|
5361
|
-
|
|
5452
|
+
| 'sessionOwnerNodeRid'
|
|
5453
|
+
| 'sessionOwnerNodeGeneration'
|
|
5454
|
+
| 'sessionOwnerId'
|
|
5455
|
+
| 'sessionOwnerLeaseGeneration'
|
|
5362
5456
|
>
|
|
5363
5457
|
): boolean {
|
|
5364
|
-
return
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5458
|
+
return (
|
|
5459
|
+
left.sessionOwnerNodeRid === right.sessionOwnerNodeRid &&
|
|
5460
|
+
left.sessionOwnerNodeGeneration === right.sessionOwnerNodeGeneration &&
|
|
5461
|
+
left.sessionOwnerId === right.sessionOwnerId &&
|
|
5462
|
+
left.sessionOwnerLeaseGeneration === right.sessionOwnerLeaseGeneration
|
|
5463
|
+
);
|
|
5368
5464
|
}
|
|
5369
5465
|
|
|
5370
5466
|
function requireSessionOwnerIdentity(binding: ServiceSessionBinding): {
|
|
@@ -5373,9 +5469,9 @@ function requireSessionOwnerIdentity(binding: ServiceSessionBinding): {
|
|
|
5373
5469
|
readonly leaseGeneration: bigint;
|
|
5374
5470
|
} {
|
|
5375
5471
|
if (
|
|
5376
|
-
binding.sessionOwnerNodeGeneration === undefined
|
|
5377
|
-
|
|
5378
|
-
|
|
5472
|
+
binding.sessionOwnerNodeGeneration === undefined ||
|
|
5473
|
+
binding.sessionOwnerId === undefined ||
|
|
5474
|
+
binding.sessionOwnerLeaseGeneration === undefined
|
|
5379
5475
|
) {
|
|
5380
5476
|
throw new ServiceWireProtocolError(
|
|
5381
5477
|
`Session binding '${binding.sessionRid}' omits its owner lifecycle identity.`
|
|
@@ -5388,47 +5484,55 @@ function requireSessionOwnerIdentity(binding: ServiceSessionBinding): {
|
|
|
5388
5484
|
};
|
|
5389
5485
|
}
|
|
5390
5486
|
|
|
5391
|
-
function sessionDeliveryKey(
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5487
|
+
function sessionDeliveryKey(
|
|
5488
|
+
binding: Pick<
|
|
5489
|
+
ServiceSessionBinding,
|
|
5490
|
+
| 'actor'
|
|
5491
|
+
| 'sessionRid'
|
|
5492
|
+
| 'sessionOwnerNodeRid'
|
|
5493
|
+
| 'sessionOwnerNodeGeneration'
|
|
5494
|
+
| 'sessionOwnerId'
|
|
5495
|
+
| 'sessionOwnerLeaseGeneration'
|
|
5496
|
+
| 'bindingGeneration'
|
|
5497
|
+
>
|
|
5498
|
+
): string {
|
|
5499
|
+
return (
|
|
5500
|
+
`${actorKey(binding.actor)}\0${binding.sessionOwnerNodeRid}\0${binding.sessionRid}` +
|
|
5501
|
+
`\0${binding.sessionOwnerNodeGeneration ?? ''}` +
|
|
5502
|
+
`\0${binding.sessionOwnerId ?? ''}` +
|
|
5503
|
+
`\0${binding.sessionOwnerLeaseGeneration ?? ''}` +
|
|
5504
|
+
`\0${binding.bindingGeneration}`
|
|
5505
|
+
);
|
|
5406
5506
|
}
|
|
5407
5507
|
|
|
5408
5508
|
function exactRetiredDelivery(
|
|
5409
5509
|
binding: ServiceSessionBinding,
|
|
5410
5510
|
retired: ServiceRetiredBoundSessionRouteFence
|
|
5411
5511
|
): boolean {
|
|
5412
|
-
return
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5512
|
+
return (
|
|
5513
|
+
binding.sessionRid === retired.sessionRid &&
|
|
5514
|
+
binding.sessionOwnerNodeRid === retired.sessionOwnerNodeRid &&
|
|
5515
|
+
binding.sessionOwnerNodeGeneration === retired.sessionOwnerNodeGeneration &&
|
|
5516
|
+
binding.sessionOwnerId === retired.sessionOwnerId &&
|
|
5517
|
+
binding.sessionOwnerLeaseGeneration === retired.sessionOwnerLeaseGeneration &&
|
|
5518
|
+
binding.bindingGeneration === retired.retiredBindingGeneration
|
|
5519
|
+
);
|
|
5418
5520
|
}
|
|
5419
5521
|
|
|
5420
5522
|
function replacementNoticeKey(
|
|
5421
5523
|
record: Extract<ServiceStatefulWireRecord, { readonly kind: 'boundSessionReplaced' }>
|
|
5422
5524
|
): string {
|
|
5423
|
-
return
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5525
|
+
return (
|
|
5526
|
+
`${actorKey(record.actorAuthority.actor)}\0${record.retiredSession.sessionOwnerNodeRid}` +
|
|
5527
|
+
`\0${record.actorAuthority.targetNodeGeneration}` +
|
|
5528
|
+
`\0${record.actorAuthority.authorityOwnerGeneration}` +
|
|
5529
|
+
`\0${record.actorAuthority.ownerLeaseGeneration}` +
|
|
5530
|
+
`\0${record.retiredSession.sessionOwnerNodeGeneration}` +
|
|
5531
|
+
`\0${record.retiredSession.sessionOwnerId}` +
|
|
5532
|
+
`\0${record.retiredSession.sessionOwnerLeaseGeneration}` +
|
|
5533
|
+
`\0${record.retiredSession.sessionRid}` +
|
|
5534
|
+
`\0${record.retiredSession.retiredBindingGeneration}`
|
|
5535
|
+
);
|
|
5432
5536
|
}
|
|
5433
5537
|
|
|
5434
5538
|
function actorAuthorityFromRoute(route: ServiceActorRouteFence): ServiceBoundSessionActorAuthority {
|
|
@@ -5449,9 +5553,11 @@ function directSpotKey(spotId: string): string {
|
|
|
5449
5553
|
}
|
|
5450
5554
|
|
|
5451
5555
|
function sameActorRef(left: ServiceActorRef, right: ServiceActorRef): boolean {
|
|
5452
|
-
return
|
|
5453
|
-
|
|
5454
|
-
|
|
5556
|
+
return (
|
|
5557
|
+
left.nodeRid === right.nodeRid &&
|
|
5558
|
+
left.actorId === right.actorId &&
|
|
5559
|
+
left.generation === right.generation
|
|
5560
|
+
);
|
|
5455
5561
|
}
|
|
5456
5562
|
|
|
5457
5563
|
function sameSpotRef(left: ServiceSpotRef, right: ServiceSpotRef): boolean {
|
|
@@ -5468,9 +5574,7 @@ function freezeSpotRoute(route: ServiceSpotRouteFence): ServiceSpotRouteFence {
|
|
|
5468
5574
|
});
|
|
5469
5575
|
}
|
|
5470
5576
|
|
|
5471
|
-
function freezeDirectSpotRoute(
|
|
5472
|
-
route: ServiceDirectSpotRouteFence
|
|
5473
|
-
): ServiceDirectSpotRouteFence {
|
|
5577
|
+
function freezeDirectSpotRoute(route: ServiceDirectSpotRouteFence): ServiceDirectSpotRouteFence {
|
|
5474
5578
|
return Object.freeze({
|
|
5475
5579
|
...freezeSpotRoute(route),
|
|
5476
5580
|
ownerLeaseGeneration: route.ownerLeaseGeneration,
|
|
@@ -5511,39 +5615,48 @@ function matchesExpectedInstanceRoute(
|
|
|
5511
5615
|
: current !== undefined && sameInstanceRoute(current, expected);
|
|
5512
5616
|
}
|
|
5513
5617
|
|
|
5514
|
-
function sameInstanceRoute(
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5618
|
+
function sameInstanceRoute(
|
|
5619
|
+
left: ServiceInstanceRouteFence,
|
|
5620
|
+
right: ServiceInstanceRouteFence
|
|
5621
|
+
): boolean {
|
|
5622
|
+
return (
|
|
5623
|
+
left.targetNodeRid === right.targetNodeRid &&
|
|
5624
|
+
left.targetNodeGeneration === right.targetNodeGeneration &&
|
|
5625
|
+
left.targetSpotId === right.targetSpotId &&
|
|
5626
|
+
left.objectGeneration === right.objectGeneration &&
|
|
5627
|
+
left.ownerId === right.ownerId &&
|
|
5628
|
+
left.authorityOwnerGeneration === right.authorityOwnerGeneration &&
|
|
5629
|
+
left.leaseGeneration === right.leaseGeneration &&
|
|
5630
|
+
left.storeVersion === right.storeVersion
|
|
5631
|
+
);
|
|
5523
5632
|
}
|
|
5524
5633
|
|
|
5525
5634
|
function sameInstanceOwnerIdentity(
|
|
5526
5635
|
left: ServiceInstanceRouteFence,
|
|
5527
5636
|
right: ServiceInstanceRouteFence
|
|
5528
5637
|
): boolean {
|
|
5529
|
-
return
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5638
|
+
return (
|
|
5639
|
+
left.targetNodeRid === right.targetNodeRid &&
|
|
5640
|
+
left.targetNodeGeneration === right.targetNodeGeneration &&
|
|
5641
|
+
left.targetSpotId === right.targetSpotId &&
|
|
5642
|
+
left.objectGeneration === right.objectGeneration &&
|
|
5643
|
+
left.ownerId === right.ownerId &&
|
|
5644
|
+
left.authorityOwnerGeneration === right.authorityOwnerGeneration &&
|
|
5645
|
+
left.leaseGeneration === right.leaseGeneration
|
|
5646
|
+
);
|
|
5536
5647
|
}
|
|
5537
5648
|
|
|
5538
5649
|
function sameInstanceApplicationOwner(
|
|
5539
5650
|
left: ServiceInstanceRouteFence,
|
|
5540
5651
|
right: ServiceInstanceRouteFence
|
|
5541
5652
|
): boolean {
|
|
5542
|
-
return
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5653
|
+
return (
|
|
5654
|
+
left.targetNodeRid === right.targetNodeRid &&
|
|
5655
|
+
left.targetNodeGeneration === right.targetNodeGeneration &&
|
|
5656
|
+
left.targetSpotId === right.targetSpotId &&
|
|
5657
|
+
left.ownerId === right.ownerId &&
|
|
5658
|
+
left.leaseGeneration === right.leaseGeneration
|
|
5659
|
+
);
|
|
5547
5660
|
}
|
|
5548
5661
|
|
|
5549
5662
|
function matchesExpectedDirectSpotRoute(
|
|
@@ -5573,8 +5686,7 @@ function sameDirectSpotRoute(
|
|
|
5573
5686
|
left: ServiceDirectSpotRouteFence,
|
|
5574
5687
|
right: ServiceDirectSpotRouteFence
|
|
5575
5688
|
): boolean {
|
|
5576
|
-
return sameDirectSpotRouteIdentity(left, right)
|
|
5577
|
-
&& left.storeVersion === right.storeVersion;
|
|
5689
|
+
return sameDirectSpotRouteIdentity(left, right) && left.storeVersion === right.storeVersion;
|
|
5578
5690
|
}
|
|
5579
5691
|
|
|
5580
5692
|
function messageFollowSpotRoute(
|
|
@@ -5614,31 +5726,34 @@ function sameMessageFollowSpotSource(
|
|
|
5614
5726
|
current: ServiceDirectSpotRouteFence,
|
|
5615
5727
|
source: Extract<ServiceMessageFollowRoute, { readonly kind: 'spot' }>
|
|
5616
5728
|
): boolean {
|
|
5617
|
-
return
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5729
|
+
return (
|
|
5730
|
+
current.spot.spotId === source.spot.spotId &&
|
|
5731
|
+
current.spot.generation === source.spot.generation &&
|
|
5732
|
+
current.targetNodeRid === source.targetNodeRid &&
|
|
5733
|
+
current.targetNodeGeneration === source.targetNodeGeneration &&
|
|
5734
|
+
current.authorityOwnerGeneration === source.authorityOwnerGeneration &&
|
|
5735
|
+
current.ownerLeaseGeneration === source.ownerLeaseGeneration
|
|
5736
|
+
);
|
|
5623
5737
|
}
|
|
5624
5738
|
|
|
5625
5739
|
function sameDirectSpotRouteIdentity(
|
|
5626
5740
|
left: ServiceDirectSpotRouteFence,
|
|
5627
5741
|
right: ServiceDirectSpotRouteFence
|
|
5628
5742
|
): boolean {
|
|
5629
|
-
return sameDirectSpotOwnerIdentity(left, right)
|
|
5630
|
-
&& left.spot.generation === right.spot.generation;
|
|
5743
|
+
return sameDirectSpotOwnerIdentity(left, right) && left.spot.generation === right.spot.generation;
|
|
5631
5744
|
}
|
|
5632
5745
|
|
|
5633
5746
|
function sameDirectSpotOwnerIdentity(
|
|
5634
5747
|
left: ServiceDirectSpotRouteFence,
|
|
5635
5748
|
right: ServiceDirectSpotRouteFence
|
|
5636
5749
|
): boolean {
|
|
5637
|
-
return
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5750
|
+
return (
|
|
5751
|
+
left.spot.spotId === right.spot.spotId &&
|
|
5752
|
+
left.targetNodeRid === right.targetNodeRid &&
|
|
5753
|
+
left.targetNodeGeneration === right.targetNodeGeneration &&
|
|
5754
|
+
left.authorityOwnerGeneration === right.authorityOwnerGeneration &&
|
|
5755
|
+
left.ownerLeaseGeneration === right.ownerLeaseGeneration
|
|
5756
|
+
);
|
|
5642
5757
|
}
|
|
5643
5758
|
|
|
5644
5759
|
function routeMatchesLocal(
|
|
@@ -5647,25 +5762,34 @@ function routeMatchesLocal(
|
|
|
5647
5762
|
nodeRid: string,
|
|
5648
5763
|
nodeGeneration: bigint
|
|
5649
5764
|
): boolean {
|
|
5650
|
-
return
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5765
|
+
return (
|
|
5766
|
+
route.targetNodeRid === nodeRid &&
|
|
5767
|
+
route.targetNodeGeneration === nodeGeneration &&
|
|
5768
|
+
route.targetSpotId === spot.ref.spotId &&
|
|
5769
|
+
route.objectGeneration === spot.ref.generation &&
|
|
5770
|
+
route.authorityOwnerGeneration === spot.authorityOwnerGeneration
|
|
5771
|
+
);
|
|
5655
5772
|
}
|
|
5656
5773
|
|
|
5657
5774
|
function instanceOperationKey(
|
|
5658
5775
|
record: Extract<ServiceStatefulWireRecord, { readonly kind: 'instanceSpot' }>
|
|
5659
5776
|
): string {
|
|
5660
|
-
return
|
|
5661
|
-
|
|
5777
|
+
return (
|
|
5778
|
+
`${record.sourceNodeRid}\0${record.sourceNodeGeneration}\0` +
|
|
5779
|
+
`${record.operation.high}\0${record.operation.low}`
|
|
5780
|
+
);
|
|
5662
5781
|
}
|
|
5663
5782
|
|
|
5664
5783
|
function instanceActivationKey(
|
|
5665
|
-
record: Extract<
|
|
5784
|
+
record: Extract<
|
|
5785
|
+
ServiceStatefulWireRecord,
|
|
5786
|
+
{ readonly kind: 'instanceSpot'; readonly activation: 'missing' }
|
|
5787
|
+
>
|
|
5666
5788
|
): string {
|
|
5667
|
-
return
|
|
5668
|
-
|
|
5789
|
+
return (
|
|
5790
|
+
`${record.target.targetNodeRid}\0${record.target.targetNodeGeneration}\0` +
|
|
5791
|
+
`${record.target.targetSpotId}\0${record.target.stableType}`
|
|
5792
|
+
);
|
|
5669
5793
|
}
|
|
5670
5794
|
|
|
5671
5795
|
function subscriptionKey(channelName: string, topicFilter: string): string {
|
|
@@ -5694,10 +5818,9 @@ function emptyPayload(): ServiceApplicationPayload {
|
|
|
5694
5818
|
* the stateful mailbox's internal record shape as an adapter only; it must
|
|
5695
5819
|
* not re-parse the canonical bytes with the legacy hand-written decoder.
|
|
5696
5820
|
*/
|
|
5697
|
-
function statefulActorJoinFromCanonical(
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
> {
|
|
5821
|
+
function statefulActorJoinFromCanonical(
|
|
5822
|
+
value: ActorJoin28
|
|
5823
|
+
): Extract<ServiceStatefulWireRecord, { readonly kind: 'actorJoin' }> {
|
|
5701
5824
|
return {
|
|
5702
5825
|
kind: 'actorJoin',
|
|
5703
5826
|
correlation: value.correlation,
|
|
@@ -5737,13 +5860,11 @@ function instanceOperationParts(
|
|
|
5737
5860
|
throw new TypeError('Instance operation requires one header and one payload frame.');
|
|
5738
5861
|
}
|
|
5739
5862
|
if (metadataFrame === undefined) return headerAndPayload;
|
|
5740
|
-
return [
|
|
5741
|
-
headerAndPayload[0]!,
|
|
5742
|
-
validateServiceMetadataFrame(metadataFrame),
|
|
5743
|
-
headerAndPayload[1]!
|
|
5744
|
-
];
|
|
5863
|
+
return [headerAndPayload[0]!, validateServiceMetadataFrame(metadataFrame), headerAndPayload[1]!];
|
|
5745
5864
|
}
|
|
5746
5865
|
|
|
5747
|
-
export function statefulMailboxData(
|
|
5866
|
+
export function statefulMailboxData(
|
|
5867
|
+
record: ServiceMailboxRecord
|
|
5868
|
+
): ServiceStatefulMailboxData | undefined {
|
|
5748
5869
|
return record.stateful as ServiceStatefulMailboxData | undefined;
|
|
5749
5870
|
}
|