@zlink-systems/framework 0.30.0 → 0.30.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +150 -68
- package/dist/.tsbuildinfo +1 -1
- package/dist/contracts/Actors/ZLinkActorClient.d.ts +2 -1
- package/dist/contracts/Channels/Calls.d.ts +2 -1
- package/dist/contracts/Common/ZLinkEncodedPayload.d.ts +6 -0
- package/dist/contracts/Common/ZLinkEncodedPayload.js +18 -3
- package/dist/contracts/Common/ZLinkMessageMetadata.js +11 -5
- package/dist/contracts/Common/encoded-payload-storage.d.ts +1 -1
- package/dist/contracts/Common/encoded-payload-storage.js +5 -2
- package/dist/contracts/Configuration/Builders.d.ts +1 -1
- package/dist/contracts/Configuration/RegistrationBuilderPolicy.d.ts +2 -1
- package/dist/contracts/Configuration/RegistrationBuilderPolicy.js +5 -1
- package/dist/contracts/Configuration/RegistrationBuilders.js +2 -2
- package/dist/contracts/Configuration/RegistrationTypes.d.ts +1 -0
- package/dist/contracts/Dispatch/ZLinkDispatchOptions.d.ts +2 -2
- package/dist/contracts/Handlers/Attributes.d.ts +5 -13
- package/dist/contracts/Handlers/Attributes.js +7 -11
- package/dist/contracts/Handlers/JsonContract.d.ts +2 -46
- package/dist/contracts/Handlers/JsonContract.js +4 -153
- package/dist/contracts/Locations/Rows.d.ts +2 -0
- package/dist/contracts/Spots/Contracts.d.ts +6 -1
- package/dist/contracts/Spots/ZLinkSpot.d.ts +3 -3
- package/dist/nest-integration.d.ts +1 -1
- package/dist/nest-integration.js +2 -1
- package/dist/runtime/abort.d.ts +5 -0
- package/dist/runtime/abort.js +10 -0
- package/dist/runtime/actors/actor-authority-publication.js +3 -2
- package/dist/runtime/actors/actor-client.d.ts +1 -2
- package/dist/runtime/actors/actor-client.js +52 -57
- package/dist/runtime/actors/actor-context.d.ts +2 -0
- package/dist/runtime/actors/actor-context.js +46 -9
- package/dist/runtime/actors/actor-handoff.d.ts +8 -4
- package/dist/runtime/actors/actor-handoff.js +219 -92
- package/dist/runtime/actors/actor-join-relocation.d.ts +3 -0
- package/dist/runtime/actors/actor-local-native-join.js +13 -12
- package/dist/runtime/actors/actor-mailbox.js +27 -1
- package/dist/runtime/actors/actor-remote-joiner.js +3 -1
- package/dist/runtime/actors/actor-remote-wire.js +15 -0
- package/dist/runtime/actors/actor-reply.d.ts +4 -0
- package/dist/runtime/actors/actor-reply.js +29 -0
- package/dist/runtime/actors/actor-routed-join-transport.d.ts +6 -2
- package/dist/runtime/actors/actor-routed-json-request.d.ts +2 -0
- package/dist/runtime/actors/actor-runtime-contracts.d.ts +2 -0
- package/dist/runtime/actors/actor-runtime-state.js +2 -20
- package/dist/runtime/actors/actor-source-transfer.d.ts +1 -17
- package/dist/runtime/actors/index.d.ts +8 -3
- package/dist/runtime/actors/index.js +55 -27
- package/dist/runtime/actors/session-disconnected-wire.d.ts +8 -0
- package/dist/runtime/actors/session-disconnected-wire.js +48 -0
- package/dist/runtime/actors/spot-actor-dispatch.js +2 -2
- package/dist/runtime/admission.d.ts +9 -1
- package/dist/runtime/admission.js +56 -23
- package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +6 -8
- package/dist/runtime/application-jobs/application-ingress-record-owner.js +39 -22
- package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +15 -1
- package/dist/runtime/application-jobs/application-job-queue-scope.js +61 -20
- package/dist/runtime/application-jobs/contracts.d.ts +12 -1
- package/dist/runtime/backend/contracts/index.d.ts +37 -12
- package/dist/runtime/backend/mesh-actor-session-node-adapter.js +2 -2
- package/dist/runtime/backend/mesh-completion-table.d.ts +9 -1
- package/dist/runtime/backend/mesh-completion-table.js +154 -49
- package/dist/runtime/backend/mesh-dispatch-pump.d.ts +28 -1
- package/dist/runtime/backend/mesh-dispatch-pump.js +208 -101
- package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +2 -2
- package/dist/runtime/backend/node/node-backend-adapter-support.js +16 -9
- package/dist/runtime/backend/node/node-dealer-channel-client-transport.d.ts +1 -1
- package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +5 -2
- package/dist/runtime/backend/node/node-raw-binding-port.js +124 -72
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +46 -13
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +181 -164
- package/dist/runtime/backend/node/node-socket-backend-adapter.d.ts +1 -1
- package/dist/runtime/backend/node/node-socket-backend-adapter.js +11 -11
- package/dist/runtime/backend/raw-binding-port.d.ts +2 -2
- package/dist/runtime/backend/runtime-message.d.ts +1 -0
- package/dist/runtime/backend/runtime-message.js +5 -0
- package/dist/runtime/backend/runtime-values.d.ts +1 -0
- package/dist/runtime/backend/runtime-values.js +2 -1
- package/dist/runtime/channels/channel-clients.d.ts +2 -0
- package/dist/runtime/channels/channel-clients.js +75 -23
- package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +25 -5
- package/dist/runtime/channels/channel-dispatch-pipeline.js +111 -56
- package/dist/runtime/channels/channel-dispatch-services.d.ts +1 -0
- package/dist/runtime/channels/channel-dispatch-services.js +45 -0
- package/dist/runtime/channels/channel-dispatchers.d.ts +4 -3
- package/dist/runtime/channels/channel-dispatchers.js +62 -54
- package/dist/runtime/channels/channel-envelope-inspection.d.ts +1 -2
- package/dist/runtime/channels/channel-envelope-inspection.js +1 -16
- package/dist/runtime/channels/channel-envelope.d.ts +2 -0
- package/dist/runtime/channels/channel-envelope.js +146 -28
- package/dist/runtime/channels/channel-outbound-operations.d.ts +2 -2
- package/dist/runtime/channels/channel-outbound-operations.js +8 -8
- package/dist/runtime/channels/channel-runtime-lifecycle.d.ts +4 -3
- package/dist/runtime/channels/channel-runtime-lifecycle.js +19 -24
- package/dist/runtime/channels/channel-runtime-manager.d.ts +9 -8
- package/dist/runtime/channels/channel-runtime-manager.js +16 -16
- package/dist/runtime/channels/channel-transports.d.ts +18 -16
- package/dist/runtime/channels/channel-transports.js +77 -58
- package/dist/runtime/channels/client-server-location-runtime.js +5 -4
- package/dist/runtime/channels/dispatch-error-reporter.d.ts +2 -1
- package/dist/runtime/channels/dispatch-error-reporter.js +2 -1
- package/dist/runtime/channels/fanout-location-runtime.d.ts +1 -1
- package/dist/runtime/channels/fanout-location-runtime.js +3 -3
- package/dist/runtime/channels/local-spot-route-transport.d.ts +1 -1
- package/dist/runtime/channels/local-spot-route-transport.js +2 -1
- package/dist/runtime/channels/router-socket-spot-route-transport.d.ts +2 -2
- package/dist/runtime/channels/router-socket-spot-route-transport.js +4 -4
- package/dist/runtime/channels/source-spot-router.d.ts +2 -2
- package/dist/runtime/channels/source-spot-router.js +4 -4
- package/dist/runtime/channels/spot-node-route-transport.d.ts +2 -2
- package/dist/runtime/channels/spot-node-route-transport.js +4 -4
- package/dist/runtime/channels/spot-route-bridge-transport.d.ts +2 -2
- package/dist/runtime/channels/spot-route-bridge-transport.js +4 -4
- package/dist/runtime/channels/spot-route-dispatch-strategy.d.ts +5 -4
- package/dist/runtime/channels/spot-route-dispatch-strategy.js +18 -18
- package/dist/runtime/diagnostics/flow-context.js +19 -7
- package/dist/runtime/diagnostics/index.js +4 -9
- package/dist/runtime/diagnostics/message-flow.d.ts +8 -0
- package/dist/runtime/diagnostics/message-flow.js +35 -5
- package/dist/runtime/diagnostics/runtime-metrics.d.ts +2 -2
- package/dist/runtime/diagnostics/runtime-metrics.js +10 -8
- package/dist/runtime/execution/index.d.ts +4 -2
- package/dist/runtime/execution/index.js +33 -10
- package/dist/runtime/execution/serial-execution-queue.d.ts +5 -0
- package/dist/runtime/execution/serial-execution-queue.js +22 -2
- package/dist/runtime/execution/state-lane.d.ts +2 -0
- package/dist/runtime/execution/state-lane.js +5 -0
- package/dist/runtime/foundation/actor-join-recovery-codec.js +2 -2
- package/dist/runtime/foundation/durable-service-request.d.ts +14 -0
- package/dist/runtime/foundation/durable-service-request.js +98 -0
- package/dist/runtime/foundation/operation-identity.d.ts +1 -0
- package/dist/runtime/foundation/operation-identity.js +5 -1
- package/dist/runtime/foundation/operation-registry.d.ts +25 -1
- package/dist/runtime/foundation/operation-registry.js +86 -18
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +61 -8
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +486 -266
- package/dist/runtime/foundation/service-liveness-registry.d.ts +12 -1
- package/dist/runtime/foundation/service-liveness-registry.js +38 -0
- package/dist/runtime/foundation/service-mailbox.d.ts +7 -0
- package/dist/runtime/foundation/service-maintenance-runtime.js +1 -6
- package/dist/runtime/foundation/service-relocation-object-owner.d.ts +3 -3
- package/dist/runtime/foundation/service-relocation-object-owner.js +48 -34
- package/dist/runtime/foundation/service-relocation-runtime.d.ts +2 -0
- package/dist/runtime/foundation/service-relocation-runtime.js +20 -4
- package/dist/runtime/foundation/service-runtime-contracts.d.ts +10 -1
- package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +2 -1
- package/dist/runtime/foundation/service-stateful-registry.d.ts +5 -4
- package/dist/runtime/foundation/service-stateful-registry.js +24 -26
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +75 -9
- package/dist/runtime/foundation/service-stateful-runtime.js +486 -218
- package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +14 -6
- package/dist/runtime/foundation/service-stateful-wire-codec.js +48 -27
- package/dist/runtime/foundation/service-topology-registry.d.ts +12 -2
- package/dist/runtime/foundation/service-topology-registry.js +61 -27
- package/dist/runtime/foundation/service-wire-binary-primitives.d.ts +6 -1
- package/dist/runtime/foundation/service-wire-binary-primitives.js +27 -11
- package/dist/runtime/foundation/service-wire-constants.generated.d.ts +1 -0
- package/dist/runtime/foundation/service-wire-constants.generated.js +1 -0
- package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +13 -3
- package/dist/runtime/foundation/service-wire-m6a-codec.js +189 -78
- package/dist/runtime/handlers/handler-instance-scope.d.ts +8 -0
- package/dist/runtime/handlers/handler-instance-scope.js +54 -11
- package/dist/runtime/host/actor-packet-relay.d.ts +6 -4
- package/dist/runtime/host/actor-packet-relay.js +59 -72
- package/dist/runtime/host/actor-placement-coordinator.js +25 -8
- package/dist/runtime/host/actor-runtime-options-factory.js +2 -2
- package/dist/runtime/host/actor-transfer-runtime.d.ts +7 -31
- package/dist/runtime/host/actor-transfer-runtime.js +85 -331
- package/dist/runtime/host/application-job-queue.d.ts +40 -7
- package/dist/runtime/host/application-job-queue.js +137 -42
- package/dist/runtime/host/entry-actor-runtime.d.ts +1 -0
- package/dist/runtime/host/entry-actor-runtime.js +3 -0
- package/dist/runtime/host/index.d.ts +12 -2
- package/dist/runtime/host/index.js +300 -202
- package/dist/runtime/host/location-runtime-owner.js +2 -6
- package/dist/runtime/host/mesh-router-resolver.d.ts +4 -0
- package/dist/runtime/host/mesh-router-resolver.js +14 -6
- package/dist/runtime/host/remote-bound-session-relay.d.ts +2 -4
- package/dist/runtime/host/remote-bound-session-relay.js +73 -208
- package/dist/runtime/host/route-mesh-runtime.d.ts +2 -2
- package/dist/runtime/host/route-mesh-runtime.js +6 -4
- package/dist/runtime/host/runtime-shutdown.d.ts +1 -0
- package/dist/runtime/host/runtime-shutdown.js +3 -2
- package/dist/runtime/host/service-relocation-control.d.ts +1 -0
- package/dist/runtime/host/service-relocation-control.js +10 -4
- package/dist/runtime/host/service-relocation-host-runtime.d.ts +22 -14
- package/dist/runtime/host/service-relocation-host-runtime.js +754 -599
- package/dist/runtime/host/spot-address-transport.js +3 -2
- package/dist/runtime/host/spot-runtime-options-factory.js +16 -7
- package/dist/runtime/host/user-spot-creation-coordinator.js +33 -84
- package/dist/runtime/locations/actor-location-claims.d.ts +2 -2
- package/dist/runtime/locations/actor-location-claims.js +34 -19
- package/dist/runtime/locations/auto-connect-loop.d.ts +2 -0
- package/dist/runtime/locations/auto-connect-loop.js +22 -14
- package/dist/runtime/locations/auto-connect-reconciler.js +0 -6
- package/dist/runtime/locations/auto-connect-types.d.ts +0 -1
- package/dist/runtime/locations/descriptor-publication.d.ts +4 -0
- package/dist/runtime/locations/descriptor-publication.js +11 -0
- package/dist/runtime/locations/domain-store-contract.d.ts +1 -0
- package/dist/runtime/locations/in-memory-authority-store.d.ts +7 -1
- package/dist/runtime/locations/in-memory-authority-store.js +56 -19
- package/dist/runtime/locations/in-memory-location-store.js +1 -10
- package/dist/runtime/locations/index.d.ts +1 -0
- package/dist/runtime/locations/index.js +1 -0
- package/dist/runtime/locations/lease-tracker.js +1 -6
- package/dist/runtime/locations/lifecycle.d.ts +1 -2
- package/dist/runtime/locations/lifecycle.js +2 -5
- package/dist/runtime/locations/location-store-repository.d.ts +51 -1
- package/dist/runtime/locations/location-store-repository.js +363 -139
- package/dist/runtime/locations/relocation-blob.d.ts +18 -1
- package/dist/runtime/locations/relocation-blob.js +52 -1
- package/dist/runtime/locations/request-source-resolver.d.ts +37 -0
- package/dist/runtime/locations/request-source-resolver.js +80 -0
- package/dist/runtime/locations/resolvers.js +1 -1
- package/dist/runtime/locations/runtime.d.ts +7 -3
- package/dist/runtime/locations/runtime.js +61 -53
- package/dist/runtime/messaging/framework-json-v1.d.ts +4 -6
- package/dist/runtime/messaging/framework-json-v1.js +38 -294
- package/dist/runtime/messaging/packet-name.d.ts +1 -1
- package/dist/runtime/messaging/packet-name.js +8 -24
- package/dist/runtime/messaging/payload-codec.d.ts +7 -1
- package/dist/runtime/messaging/payload-codec.js +7 -6
- package/dist/runtime/messaging/request-result.d.ts +2 -0
- package/dist/runtime/messaging/request-result.js +57 -0
- package/dist/runtime/messaging/submission-result.d.ts +1 -0
- package/dist/runtime/messaging/submission-result.js +5 -2
- package/dist/runtime/protocol/service_wire_codec.generated.d.ts +34 -1
- package/dist/runtime/protocol/service_wire_codec.generated.js +229 -0
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.d.ts +16 -1
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +37 -8
- package/dist/runtime/spots/formal-remote-actor-admission-registry.d.ts +7 -2
- package/dist/runtime/spots/formal-remote-actor-admission-registry.js +36 -21
- package/dist/runtime/spots/index.d.ts +21 -11
- package/dist/runtime/spots/index.js +243 -178
- package/dist/runtime/spots/spot-activation-registry.d.ts +4 -2
- package/dist/runtime/spots/spot-activation-registry.js +21 -11
- package/dist/runtime/spots/spot-activation-state.d.ts +1 -1
- package/dist/runtime/spots/spot-activation-state.js +7 -7
- package/dist/runtime/spots/spot-activation.d.ts +7 -3
- package/dist/runtime/spots/spot-activation.js +140 -52
- package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +1 -0
- package/dist/runtime/spots/spot-actor-admission-coordinator.js +13 -22
- package/dist/runtime/spots/spot-actor-membership.d.ts +0 -1
- package/dist/runtime/spots/spot-actor-membership.js +0 -8
- package/dist/runtime/spots/spot-actor-packet-dispatch.js +33 -8
- package/dist/runtime/spots/spot-actor-packet-drain.js +5 -33
- package/dist/runtime/spots/spot-entry-activation.d.ts +2 -0
- package/dist/runtime/spots/spot-entry-activation.js +20 -11
- package/dist/runtime/spots/spot-handler-registry.d.ts +7 -4
- package/dist/runtime/spots/spot-handler-registry.js +24 -10
- package/dist/runtime/spots/spot-manager-internal-contracts.d.ts +2 -2
- package/dist/runtime/spots/spot-manager-public.js +2 -2
- package/dist/runtime/spots/spot-node-autoconnect.d.ts +2 -1
- package/dist/runtime/spots/spot-node-autoconnect.js +22 -42
- package/dist/runtime/spots/spot-node-runtime-manager.d.ts +19 -2
- package/dist/runtime/spots/spot-node-runtime-manager.js +115 -50
- package/dist/runtime/spots/spot-outbound.d.ts +3 -0
- package/dist/runtime/spots/spot-outbound.js +27 -18
- package/dist/runtime/spots/spot-remote-codec.js +5 -1
- package/dist/runtime/spots/spot-routed-spot-packet-dispatch.d.ts +1 -0
- package/dist/runtime/spots/spot-routed-spot-packet-dispatch.js +1 -1
- package/dist/runtime/spots/spot-runtime-ports.d.ts +1 -1
- package/dist/runtime/spots/spot-serial-executor.d.ts +10 -0
- package/dist/runtime/spots/spot-serial-executor.js +61 -8
- package/dist/runtime/spots/spot-serial-turn-executor.js +19 -2
- package/dist/runtime/spots/spot-subscription-dispatch.js +1 -4
- package/dist/runtime/spots/spot-timer.d.ts +57 -5
- package/dist/runtime/spots/spot-timer.js +288 -103
- package/dist/runtime/streams/actor-session-binding-registry.d.ts +42 -11
- package/dist/runtime/streams/actor-session-binding-registry.js +165 -48
- package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +8 -4
- package/dist/runtime/streams/bound-actor-relay-sender.d.ts +2 -1
- package/dist/runtime/streams/bound-actor-relay-sender.js +15 -8
- package/dist/runtime/streams/bound-session-service.d.ts +1 -1
- package/dist/runtime/streams/bound-session-service.js +6 -11
- package/dist/runtime/streams/index.d.ts +3 -2
- package/dist/runtime/streams/index.js +6 -4
- package/dist/runtime/streams/managed-stream.js +3 -2
- package/dist/runtime/streams/native-fallback-bound-session.js +8 -73
- package/dist/runtime/streams/session-actor-coordinator.d.ts +2 -8
- package/dist/runtime/streams/session-actor-coordinator.js +15 -32
- package/dist/runtime/streams/stream-binding-runtime-ports.d.ts +3 -10
- package/dist/runtime/streams/stream-session-runtime.d.ts +1 -1
- package/dist/runtime/streams/stream-session-runtime.js +14 -31
- package/dist/runtime/workers/index.js +1 -1
- package/package.json +3 -3
- package/src/contracts/Actors/ZLinkActorClient.ts +2 -1
- package/src/contracts/Channels/Calls.ts +2 -1
- package/src/contracts/Common/ZLinkEncodedPayload.ts +24 -4
- package/src/contracts/Common/ZLinkMessageMetadata.ts +13 -8
- package/src/contracts/Common/encoded-payload-storage.ts +6 -3
- package/src/contracts/Configuration/Builders.ts +4 -1
- package/src/contracts/Configuration/RegistrationBuilderPolicy.ts +7 -2
- package/src/contracts/Configuration/RegistrationBuilders.ts +6 -2
- package/src/contracts/Configuration/RegistrationTypes.ts +1 -0
- package/src/contracts/Dispatch/ZLinkDispatchOptions.ts +2 -1
- package/src/contracts/Handlers/Attributes.ts +14 -21
- package/src/contracts/Handlers/JsonContract.ts +8 -216
- package/src/contracts/Locations/Rows.ts +2 -0
- package/src/contracts/Spots/Contracts.ts +10 -1
- package/src/contracts/Spots/ZLinkSpot.ts +3 -3
- package/src/nest-integration.ts +1 -0
- package/src/runtime/abort.ts +12 -0
- package/src/runtime/actors/actor-authority-publication.ts +3 -2
- package/src/runtime/actors/actor-client.ts +100 -85
- package/src/runtime/actors/actor-context.ts +49 -9
- package/src/runtime/actors/actor-handoff.ts +291 -110
- package/src/runtime/actors/actor-join-relocation.ts +3 -0
- package/src/runtime/actors/actor-local-native-join.ts +21 -26
- package/src/runtime/actors/actor-mailbox.ts +40 -2
- package/src/runtime/actors/actor-remote-joiner.ts +6 -1
- package/src/runtime/actors/actor-remote-wire.ts +17 -0
- package/src/runtime/actors/actor-reply.ts +42 -0
- package/src/runtime/actors/actor-routed-join-transport.ts +14 -4
- package/src/runtime/actors/actor-routed-json-request.ts +10 -2
- package/src/runtime/actors/actor-runtime-contracts.ts +2 -0
- package/src/runtime/actors/actor-runtime-state.ts +2 -20
- package/src/runtime/actors/actor-source-transfer.ts +1 -34
- package/src/runtime/actors/index.ts +60 -32
- package/src/runtime/actors/session-disconnected-wire.ts +66 -0
- package/src/runtime/actors/spot-actor-dispatch.ts +4 -2
- package/src/runtime/admission.ts +69 -21
- package/src/runtime/application-jobs/application-ingress-record-owner.ts +41 -25
- package/src/runtime/application-jobs/application-job-queue-scope.ts +83 -22
- package/src/runtime/application-jobs/contracts.ts +16 -1
- package/src/runtime/backend/contracts/index.ts +92 -18
- package/src/runtime/backend/mesh-actor-session-node-adapter.ts +5 -3
- package/src/runtime/backend/mesh-completion-table.ts +160 -57
- package/src/runtime/backend/mesh-dispatch-pump.ts +237 -108
- package/src/runtime/backend/node/node-backend-adapter-support.ts +17 -8
- package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +12 -2
- package/src/runtime/backend/node/node-raw-binding-port.ts +148 -85
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +315 -201
- package/src/runtime/backend/node/node-socket-backend-adapter.ts +26 -11
- package/src/runtime/backend/raw-binding-port.ts +7 -2
- package/src/runtime/backend/runtime-message.ts +6 -0
- package/src/runtime/backend/runtime-values.ts +1 -0
- package/src/runtime/channels/channel-clients.ts +132 -58
- package/src/runtime/channels/channel-dispatch-pipeline.ts +198 -114
- package/src/runtime/channels/channel-dispatch-services.ts +69 -1
- package/src/runtime/channels/channel-dispatchers.ts +136 -90
- package/src/runtime/channels/channel-envelope-inspection.ts +2 -16
- package/src/runtime/channels/channel-envelope.ts +225 -31
- package/src/runtime/channels/channel-outbound-operations.ts +22 -8
- package/src/runtime/channels/channel-runtime-lifecycle.ts +13 -13
- package/src/runtime/channels/channel-runtime-manager.ts +41 -17
- package/src/runtime/channels/channel-transports.ts +171 -112
- package/src/runtime/channels/client-server-location-runtime.ts +11 -10
- package/src/runtime/channels/dispatch-error-reporter.ts +4 -2
- package/src/runtime/channels/fanout-location-runtime.ts +17 -9
- package/src/runtime/channels/local-spot-route-transport.ts +3 -1
- package/src/runtime/channels/router-socket-spot-route-transport.ts +13 -4
- package/src/runtime/channels/source-spot-router.ts +8 -4
- package/src/runtime/channels/spot-node-route-transport.ts +8 -4
- package/src/runtime/channels/spot-route-bridge-transport.ts +6 -4
- package/src/runtime/channels/spot-route-dispatch-strategy.ts +47 -18
- package/src/runtime/diagnostics/flow-context.ts +20 -7
- package/src/runtime/diagnostics/index.ts +1 -7
- package/src/runtime/diagnostics/message-flow.ts +47 -9
- package/src/runtime/diagnostics/runtime-metrics.ts +12 -10
- package/src/runtime/execution/index.ts +40 -14
- package/src/runtime/execution/serial-execution-queue.ts +33 -4
- package/src/runtime/execution/state-lane.ts +7 -0
- package/src/runtime/foundation/actor-join-recovery-codec.ts +2 -2
- package/src/runtime/foundation/durable-service-request.ts +137 -0
- package/src/runtime/foundation/operation-identity.ts +5 -1
- package/src/runtime/foundation/operation-registry.ts +98 -20
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +704 -336
- package/src/runtime/foundation/service-liveness-registry.ts +39 -0
- package/src/runtime/foundation/service-mailbox.ts +8 -0
- package/src/runtime/foundation/service-maintenance-runtime.ts +1 -8
- package/src/runtime/foundation/service-relocation-object-owner.ts +55 -36
- package/src/runtime/foundation/service-relocation-runtime.ts +25 -4
- package/src/runtime/foundation/service-runtime-contracts.ts +19 -2
- package/src/runtime/foundation/service-session-binding-ingress-port.ts +2 -1
- package/src/runtime/foundation/service-stateful-registry.ts +38 -35
- package/src/runtime/foundation/service-stateful-runtime.ts +732 -275
- package/src/runtime/foundation/service-stateful-wire-codec.ts +49 -16
- package/src/runtime/foundation/service-topology-registry.ts +91 -36
- package/src/runtime/foundation/service-wire-binary-primitives.ts +35 -12
- package/src/runtime/foundation/service-wire-constants.generated.ts +1 -0
- package/src/runtime/foundation/service-wire-m6a-codec.ts +231 -76
- package/src/runtime/handlers/handler-instance-scope.ts +67 -16
- package/src/runtime/host/actor-packet-relay.ts +113 -94
- package/src/runtime/host/actor-placement-coordinator.ts +31 -8
- package/src/runtime/host/actor-runtime-options-factory.ts +13 -2
- package/src/runtime/host/actor-transfer-runtime.ts +104 -430
- package/src/runtime/host/application-job-queue.ts +160 -41
- package/src/runtime/host/entry-actor-runtime.ts +4 -0
- package/src/runtime/host/index.ts +473 -271
- package/src/runtime/host/location-runtime-owner.ts +2 -6
- package/src/runtime/host/mesh-router-resolver.ts +13 -5
- package/src/runtime/host/remote-bound-session-relay.ts +88 -271
- package/src/runtime/host/route-mesh-runtime.ts +12 -6
- package/src/runtime/host/runtime-shutdown.ts +15 -4
- package/src/runtime/host/service-relocation-control.ts +11 -5
- package/src/runtime/host/service-relocation-host-runtime.ts +1092 -759
- package/src/runtime/host/spot-address-transport.ts +4 -2
- package/src/runtime/host/spot-runtime-options-factory.ts +20 -7
- package/src/runtime/host/user-spot-creation-coordinator.ts +41 -100
- package/src/runtime/locations/actor-location-claims.ts +52 -30
- package/src/runtime/locations/auto-connect-loop.ts +22 -14
- package/src/runtime/locations/auto-connect-reconciler.ts +0 -6
- package/src/runtime/locations/auto-connect-types.ts +0 -1
- package/src/runtime/locations/descriptor-publication.ts +7 -0
- package/src/runtime/locations/domain-store-contract.ts +5 -0
- package/src/runtime/locations/in-memory-authority-store.ts +85 -23
- package/src/runtime/locations/in-memory-location-store.ts +1 -12
- package/src/runtime/locations/index.ts +1 -0
- package/src/runtime/locations/lease-tracker.ts +2 -9
- package/src/runtime/locations/lifecycle.ts +8 -26
- package/src/runtime/locations/location-store-repository.ts +628 -152
- package/src/runtime/locations/relocation-blob.ts +75 -1
- package/src/runtime/locations/request-source-resolver.ts +138 -0
- package/src/runtime/locations/resolvers.ts +2 -2
- package/src/runtime/locations/runtime.ts +98 -64
- package/src/runtime/messaging/framework-json-v1.ts +5 -325
- package/src/runtime/messaging/packet-name.ts +5 -25
- package/src/runtime/messaging/payload-codec.ts +8 -11
- package/src/runtime/messaging/request-result.ts +62 -0
- package/src/runtime/messaging/submission-result.ts +5 -1
- package/src/runtime/protocol/service_wire_codec.generated.ts +218 -5
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +16 -13
- package/src/runtime/spots/formal-remote-actor-admission-registry.ts +46 -25
- package/src/runtime/spots/index.ts +389 -283
- package/src/runtime/spots/spot-activation-registry.ts +25 -13
- package/src/runtime/spots/spot-activation-state.ts +12 -19
- package/src/runtime/spots/spot-activation.ts +198 -96
- package/src/runtime/spots/spot-actor-admission-coordinator.ts +25 -55
- package/src/runtime/spots/spot-actor-membership.ts +0 -15
- package/src/runtime/spots/spot-actor-packet-dispatch.ts +56 -11
- package/src/runtime/spots/spot-actor-packet-drain.ts +9 -52
- package/src/runtime/spots/spot-entry-activation.ts +33 -21
- package/src/runtime/spots/spot-handler-registry.ts +56 -12
- package/src/runtime/spots/spot-manager-internal-contracts.ts +2 -2
- package/src/runtime/spots/spot-manager-public.ts +2 -2
- package/src/runtime/spots/spot-node-autoconnect.ts +28 -47
- package/src/runtime/spots/spot-node-runtime-manager.ts +197 -67
- package/src/runtime/spots/spot-outbound.ts +39 -19
- package/src/runtime/spots/spot-remote-codec.ts +9 -2
- package/src/runtime/spots/spot-routed-spot-packet-dispatch.ts +2 -1
- package/src/runtime/spots/spot-runtime-ports.ts +1 -1
- package/src/runtime/spots/spot-serial-executor.ts +97 -25
- package/src/runtime/spots/spot-serial-turn-executor.ts +22 -3
- package/src/runtime/spots/spot-subscription-dispatch.ts +1 -4
- package/src/runtime/spots/spot-timer.ts +418 -158
- package/src/runtime/streams/actor-session-binding-registry.ts +236 -64
- package/src/runtime/streams/actor-session-binding-runtime-owner.ts +16 -8
- package/src/runtime/streams/bound-actor-relay-sender.ts +23 -9
- package/src/runtime/streams/bound-session-service.ts +7 -10
- package/src/runtime/streams/index.ts +12 -6
- package/src/runtime/streams/managed-stream.ts +4 -2
- package/src/runtime/streams/native-fallback-bound-session.ts +14 -103
- package/src/runtime/streams/session-actor-coordinator.ts +39 -88
- package/src/runtime/streams/stream-binding-runtime-ports.ts +3 -9
- package/src/runtime/streams/stream-session-runtime.ts +13 -34
- package/src/runtime/workers/index.ts +1 -1
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import { awaitWithAbort, isAbortError } from '../abort';
|
|
2
|
+
import { decodeChannelHeader } from '../channels/channel-envelope';
|
|
3
|
+
import { ZLinkBufferMessage } from '../backend/runtime-message';
|
|
4
|
+
import {
|
|
5
|
+
dispatchErrorDetails,
|
|
6
|
+
dispatchReasonFromError
|
|
7
|
+
} from '../diagnostics/dispatch-error-details';
|
|
8
|
+
import { encodeBoundSessionCloseCommand } from '../protocol/service_wire_codec.generated';
|
|
9
|
+
import { decodeRoutingId, encodeRoutingIdStorageHex, routingIdsEqual } from '../routing-id';
|
|
1
10
|
import { ZlinkStreamContentType } from '@zlink-systems/stream-wire';
|
|
2
11
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
3
12
|
import { ZLinkFrameworkErrorKind, ZLinkFrameworkException } from '../../contracts';
|
|
@@ -10,11 +19,11 @@ import {
|
|
|
10
19
|
ZLinkDispatchMessageKind,
|
|
11
20
|
ZLinkRuntimeMessageFlowOutcome as ZLinkMessageFlowOutcome
|
|
12
21
|
} from '../../contracts/Dispatch/ZLinkDispatchOptions';
|
|
13
|
-
import {
|
|
22
|
+
import { requestDurableService } from './durable-service-request';
|
|
14
23
|
import { shouldCompactBackingArray } from '../admission';
|
|
15
24
|
import { RequestResult, SubmitResult, isZLinkBackendResultError } from '../backend/runtime-values';
|
|
16
25
|
import type { ZLinkDispatchErrorReporter } from '../channels/dispatch-error-reporter';
|
|
17
|
-
import { flowIfEnabled } from '../diagnostics/message-flow';
|
|
26
|
+
import { flowIfEnabled, traceSessionBindingControlDrop } from '../diagnostics/message-flow';
|
|
18
27
|
import { captureZLinkExecutionTurn } from '../execution';
|
|
19
28
|
import {
|
|
20
29
|
ZLinkFrameworkInternalErrorKind,
|
|
@@ -58,6 +67,7 @@ import {
|
|
|
58
67
|
ServiceStaleGenerationError,
|
|
59
68
|
ServiceStatefulRegistry,
|
|
60
69
|
ServiceTerminalOperationRegistry,
|
|
70
|
+
RELOCATION_REPLY_RETENTION_MS,
|
|
61
71
|
type ServiceActorRef,
|
|
62
72
|
type ServiceActorState,
|
|
63
73
|
type ServiceSessionBinding,
|
|
@@ -104,15 +114,19 @@ import {
|
|
|
104
114
|
SERVICE_WIRE_COMMAND_OFFSET,
|
|
105
115
|
SERVICE_WIRE_FLAGS_OFFSET
|
|
106
116
|
} from './service-wire-binary-primitives';
|
|
107
|
-
import {
|
|
117
|
+
import {
|
|
118
|
+
SERVICE_WIRE_MAGIC,
|
|
119
|
+
SERVICE_WIRE_MAJOR,
|
|
120
|
+
SERVICE_FRAMEWORK_MULTIPART_CONTENT_TYPE
|
|
121
|
+
} from './service-wire-constants.generated';
|
|
108
122
|
import {
|
|
109
123
|
ServiceWireProtocolError,
|
|
110
124
|
decodeApplicationPayload,
|
|
111
125
|
decodeApplicationPayloadView,
|
|
126
|
+
decodeMultipartApplicationParts,
|
|
112
127
|
encodeApplicationPayload,
|
|
113
128
|
type ServiceApplicationPayload
|
|
114
129
|
} from './service-wire-m6a-codec';
|
|
115
|
-
const STATEFUL_OPERATION_RETRY_TICK_MS = 20;
|
|
116
130
|
|
|
117
131
|
const MAX_NODE_TIMER_DELAY_MS = 2_147_483_647;
|
|
118
132
|
|
|
@@ -206,6 +220,58 @@ export interface ServiceCanonicalActorJoinCandidate {
|
|
|
206
220
|
readonly local: { readonly phase: 'admission'; readonly transferId: string };
|
|
207
221
|
}
|
|
208
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Session–Actor binding §4: the owner ID and owner lease generation of a
|
|
225
|
+
* Session owner identity are the host's `(OwnerId, LeaseGeneration)` published
|
|
226
|
+
* in its MeshNode descriptor.
|
|
227
|
+
*/
|
|
228
|
+
export interface ServiceSessionOwnerToken {
|
|
229
|
+
readonly ownerId: string;
|
|
230
|
+
readonly leaseGeneration: bigint;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** The descriptor source of the Session owner identity (§4). */
|
|
234
|
+
export interface ServiceSessionOwnerDescriptors {
|
|
235
|
+
/** This host's published token, or undefined while it has no live descriptor. */
|
|
236
|
+
local(): ServiceSessionOwnerToken | undefined;
|
|
237
|
+
/**
|
|
238
|
+
* The token of the admitted peer descriptor with this exact RID and
|
|
239
|
+
* lifecycle generation that passed the Location runtime §4.1 owner lease
|
|
240
|
+
* check, or undefined when no such descriptor exists.
|
|
241
|
+
*/
|
|
242
|
+
peer(nodeRid: string, nodeGeneration: bigint): Promise<ServiceSessionOwnerToken | undefined>;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** The four values of one Session owner identity (§4). */
|
|
246
|
+
export interface ServiceSessionOwnerIdentity {
|
|
247
|
+
readonly nodeRid: string;
|
|
248
|
+
readonly nodeGeneration: bigint;
|
|
249
|
+
readonly ownerId: string;
|
|
250
|
+
readonly leaseGeneration: bigint;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* The receiving Session owner compares the identity carried by command 42,
|
|
255
|
+
* 51 or 54 with its current values exactly (§4).
|
|
256
|
+
*/
|
|
257
|
+
export function sessionOwnerFenceMatches(
|
|
258
|
+
local: ServiceSessionOwnerIdentity,
|
|
259
|
+
fence: Pick<
|
|
260
|
+
ServiceRetiredBoundSessionRouteFence,
|
|
261
|
+
| 'sessionOwnerNodeRid'
|
|
262
|
+
| 'sessionOwnerNodeGeneration'
|
|
263
|
+
| 'sessionOwnerId'
|
|
264
|
+
| 'sessionOwnerLeaseGeneration'
|
|
265
|
+
>
|
|
266
|
+
): boolean {
|
|
267
|
+
return (
|
|
268
|
+
routingIdsEqual(fence.sessionOwnerNodeRid, local.nodeRid) &&
|
|
269
|
+
fence.sessionOwnerNodeGeneration === local.nodeGeneration &&
|
|
270
|
+
fence.sessionOwnerId === local.ownerId &&
|
|
271
|
+
fence.sessionOwnerLeaseGeneration === local.leaseGeneration
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
209
275
|
export interface ServiceSessionDelivery {
|
|
210
276
|
readonly binding: ServiceSessionBinding;
|
|
211
277
|
/**
|
|
@@ -213,6 +279,7 @@ export interface ServiceSessionDelivery {
|
|
|
213
279
|
* been validated. The stream adapter owns multipart decoding at that point.
|
|
214
280
|
*/
|
|
215
281
|
readonly deliver: (sessionRid: string, payloadFrame: Uint8Array) => Promise<boolean>;
|
|
282
|
+
readonly close?: () => Promise<void>;
|
|
216
283
|
/** Enqueues the replacement lifecycle callback on the owning STREAM session. */
|
|
217
284
|
readonly onBindingReplaced?: (
|
|
218
285
|
actorId: string,
|
|
@@ -247,6 +314,7 @@ export interface ServiceInstanceApplicationLifecycle {
|
|
|
247
314
|
isIdleEvicting?(target: ServiceInstanceApplicationIdentity): boolean;
|
|
248
315
|
beginIdleEviction?(target: ServiceInstanceApplicationIdentity): boolean;
|
|
249
316
|
materialize(target: ServiceInstanceApplicationIdentity, objectGeneration: bigint): Promise<void>;
|
|
317
|
+
publish?(target: ServiceInstanceApplicationTarget): void;
|
|
250
318
|
discard(target: ServiceInstanceActivationTarget): Promise<void>;
|
|
251
319
|
beginTerminal(target: ServiceInstanceApplicationTarget): void;
|
|
252
320
|
completeTerminal(target: ServiceInstanceApplicationTarget): Promise<boolean>;
|
|
@@ -382,7 +450,7 @@ export class ServiceStatefulRuntime {
|
|
|
382
450
|
|
|
383
451
|
readonly registry: ServiceStatefulRegistry;
|
|
384
452
|
|
|
385
|
-
private readonly operations: ServiceTerminalOperationRegistry<ServiceStatefulResult>;
|
|
453
|
+
private readonly operations: ServiceTerminalOperationRegistry<ServiceStatefulResult, bigint>;
|
|
386
454
|
private readonly sessionDeliveries = new Map<string, ServiceSessionDelivery>();
|
|
387
455
|
/**
|
|
388
456
|
* A local Actor owner can replace its own session binding before the
|
|
@@ -415,6 +483,7 @@ export class ServiceStatefulRuntime {
|
|
|
415
483
|
private asyncInstanceAuthority?: ServiceAsyncInstanceActivationAuthority;
|
|
416
484
|
private instanceApplicationLifecycle?: ServiceInstanceApplicationLifecycle;
|
|
417
485
|
private userSpotOperationHandler?: ServiceUserSpotOperationHandler;
|
|
486
|
+
private requestSourceAcceptance?: import('../backend/contracts').ZLinkBackendRequestSourceAcceptance;
|
|
418
487
|
private messageFollowHandler?: (
|
|
419
488
|
record: import('./service-stateful-wire-codec').ServiceMessageFollowRecord
|
|
420
489
|
) => void;
|
|
@@ -423,6 +492,7 @@ export class ServiceStatefulRuntime {
|
|
|
423
492
|
readonly owner: string;
|
|
424
493
|
}) => void;
|
|
425
494
|
private dispatchErrors?: ZLinkDispatchErrorReporter;
|
|
495
|
+
private sessionOwnerDescriptors?: ServiceSessionOwnerDescriptors;
|
|
426
496
|
private dispatchErrorMeshName?: string;
|
|
427
497
|
private readonly admittedUserSpotOperations = new Map<
|
|
428
498
|
string,
|
|
@@ -454,7 +524,7 @@ export class ServiceStatefulRuntime {
|
|
|
454
524
|
);
|
|
455
525
|
this.registry = new ServiceStatefulRegistry(nodeRid, nodeGeneration);
|
|
456
526
|
this.registry.createEntrySpot(nodeRid);
|
|
457
|
-
raw.setServiceIngress((record) => this.ingress(record));
|
|
527
|
+
raw.setServiceIngress((record) => this.ingress(record), acceptsStatefulIngress);
|
|
458
528
|
}
|
|
459
529
|
|
|
460
530
|
setMailboxDropHandler(
|
|
@@ -466,6 +536,52 @@ export class ServiceStatefulRuntime {
|
|
|
466
536
|
this.mailboxDropHandler = handler;
|
|
467
537
|
}
|
|
468
538
|
|
|
539
|
+
setSessionOwnerDescriptors(source: ServiceSessionOwnerDescriptors): void {
|
|
540
|
+
this.sessionOwnerDescriptors = source;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/** §4: this host's Session owner identity, taken from its own descriptor. */
|
|
544
|
+
private localSessionOwner(): ServiceSessionOwnerIdentity | undefined {
|
|
545
|
+
const token = this.sessionOwnerDescriptors?.local();
|
|
546
|
+
return token === undefined
|
|
547
|
+
? undefined
|
|
548
|
+
: {
|
|
549
|
+
nodeRid: this.nodeRid,
|
|
550
|
+
nodeGeneration: this.nodeGeneration,
|
|
551
|
+
ownerId: token.ownerId,
|
|
552
|
+
leaseGeneration: token.leaseGeneration
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* §4: the identity of the Session owner that requested a bind. It is this
|
|
558
|
+
* host's own descriptor, or the admitted peer descriptor of that exact
|
|
559
|
+
* lifecycle that passed the owner lease check. Without one the bind is
|
|
560
|
+
* rejected instead of storing a guessed identity.
|
|
561
|
+
*/
|
|
562
|
+
private async sessionOwnerOf(
|
|
563
|
+
nodeRid: string,
|
|
564
|
+
nodeGeneration: bigint
|
|
565
|
+
): Promise<ServiceSessionOwnerIdentity> {
|
|
566
|
+
const token = routingIdsEqual(nodeRid, this.nodeRid)
|
|
567
|
+
? nodeGeneration === this.nodeGeneration
|
|
568
|
+
? this.sessionOwnerDescriptors?.local()
|
|
569
|
+
: undefined
|
|
570
|
+
: await this.sessionOwnerDescriptors?.peer(nodeRid, nodeGeneration);
|
|
571
|
+
if (token === undefined) {
|
|
572
|
+
throw createInternalFrameworkException(
|
|
573
|
+
ZLinkFrameworkInternalErrorKind.RouteNotConnected,
|
|
574
|
+
`Session owner '${nodeRid}' has no live MeshNode descriptor for its lifecycle.`
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
return {
|
|
578
|
+
nodeRid,
|
|
579
|
+
nodeGeneration,
|
|
580
|
+
ownerId: token.ownerId,
|
|
581
|
+
leaseGeneration: token.leaseGeneration
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
|
|
469
585
|
setDispatchErrorReporter(reporter: ZLinkDispatchErrorReporter, meshName: string): void {
|
|
470
586
|
this.dispatchErrors = reporter;
|
|
471
587
|
this.dispatchErrorMeshName = meshName;
|
|
@@ -567,7 +683,7 @@ export class ServiceStatefulRuntime {
|
|
|
567
683
|
if (
|
|
568
684
|
current?.bindingGeneration === bindingGeneration &&
|
|
569
685
|
current.sessionOwnerNodeRid === sessionNodeRid &&
|
|
570
|
-
current.sessionRid
|
|
686
|
+
routingIdsEqual(current.sessionRid, sessionRid)
|
|
571
687
|
) {
|
|
572
688
|
return;
|
|
573
689
|
}
|
|
@@ -1131,6 +1247,78 @@ export class ServiceStatefulRuntime {
|
|
|
1131
1247
|
this.messageFollowHandler = handler;
|
|
1132
1248
|
}
|
|
1133
1249
|
|
|
1250
|
+
setRequestSourceAcceptance(
|
|
1251
|
+
acceptance: import('../backend/contracts').ZLinkBackendRequestSourceAcceptance
|
|
1252
|
+
): void {
|
|
1253
|
+
this.requestSourceAcceptance = acceptance;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
/**
|
|
1257
|
+
* Location runtime §9.3: the first accepting owner reads the sender's owner values once. A
|
|
1258
|
+
* sender without a live descriptor ends the request Unavailable (Session·Actor binding §4). The
|
|
1259
|
+
* polled descriptors answer without a promise; only a Store read returns one.
|
|
1260
|
+
*/
|
|
1261
|
+
private acceptRequestSource(
|
|
1262
|
+
ingress: RawServiceIngressRecord,
|
|
1263
|
+
signal?: AbortSignal
|
|
1264
|
+
): Promise<void> | undefined {
|
|
1265
|
+
if (ingress.requestSource !== undefined) return undefined;
|
|
1266
|
+
const acceptance = this.requestSourceAcceptance;
|
|
1267
|
+
if (acceptance === undefined) return undefined;
|
|
1268
|
+
const generation =
|
|
1269
|
+
ingress.sourceNodeGeneration ?? this.tryPeerGeneration(ingress.sourceRoutingId);
|
|
1270
|
+
const sourceRouteHex =
|
|
1271
|
+
ingress.sourceRoute === undefined || ingress.sourceRoute.length === 0
|
|
1272
|
+
? undefined
|
|
1273
|
+
: Buffer.from(ingress.sourceRoute).toString('hex');
|
|
1274
|
+
const sourceRid =
|
|
1275
|
+
sourceRouteHex === undefined
|
|
1276
|
+
? ingress.sourceRoutingId
|
|
1277
|
+
: decodeRoutingId(ingress.sourceRoutingId, sourceRouteHex);
|
|
1278
|
+
const source =
|
|
1279
|
+
generation === undefined
|
|
1280
|
+
? undefined
|
|
1281
|
+
: acceptance(
|
|
1282
|
+
sourceRid,
|
|
1283
|
+
generation,
|
|
1284
|
+
sourceRouteHex ?? encodeRoutingIdStorageHex(sourceRid),
|
|
1285
|
+
signal
|
|
1286
|
+
);
|
|
1287
|
+
if (source instanceof Promise) {
|
|
1288
|
+
return source.then(
|
|
1289
|
+
(read) => this.recordRequestSource(ingress, read),
|
|
1290
|
+
(error: unknown) => {
|
|
1291
|
+
if (
|
|
1292
|
+
error instanceof ZLinkFrameworkException &&
|
|
1293
|
+
error.kind === ZLinkFrameworkErrorKind.Unavailable &&
|
|
1294
|
+
this.dispatchErrors?.captureEnabled() === true
|
|
1295
|
+
) {
|
|
1296
|
+
this.dispatchErrors.report({
|
|
1297
|
+
surface: ZLinkDispatchErrorSurface.Node,
|
|
1298
|
+
messageKind: ZLinkDispatchMessageKind.Request,
|
|
1299
|
+
reason: ZLinkDispatchErrorReason.StaleTarget,
|
|
1300
|
+
action: ZLinkDispatchErrorAction.ReplyError,
|
|
1301
|
+
meshName: this.dispatchErrorMeshName,
|
|
1302
|
+
sourceRid: ingress.sourceRoutingId,
|
|
1303
|
+
error
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
throw error;
|
|
1307
|
+
}
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
this.recordRequestSource(ingress, source);
|
|
1311
|
+
return undefined;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
private recordRequestSource(
|
|
1315
|
+
ingress: RawServiceIngressRecord,
|
|
1316
|
+
source: import('../locations/request-source-resolver').ZLinkRequestSourceFence | undefined
|
|
1317
|
+
): void {
|
|
1318
|
+
if (source === undefined) throw requestSourceUnavailable(ingress.sourceRoutingId);
|
|
1319
|
+
ingress.requestSource = source;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1134
1322
|
sealSpotMessageFollowIngress(
|
|
1135
1323
|
source: ServiceDirectSpotRouteFence
|
|
1136
1324
|
): ServiceSpotMessageFollowSeal | undefined {
|
|
@@ -1180,11 +1368,15 @@ export class ServiceStatefulRuntime {
|
|
|
1180
1368
|
|
|
1181
1369
|
async commitSpotMessageFollowIngress(
|
|
1182
1370
|
seal: ServiceSpotMessageFollowSeal,
|
|
1183
|
-
target: ServiceDirectSpotRouteFence,
|
|
1371
|
+
target: ServiceDirectSpotRouteFence | null,
|
|
1184
1372
|
durationMs: number
|
|
1185
1373
|
): Promise<boolean> {
|
|
1186
1374
|
const state = this.exactSpotMessageFollow(seal);
|
|
1187
1375
|
if (state === undefined || state.target !== undefined) return false;
|
|
1376
|
+
if (target === null) {
|
|
1377
|
+
this.failExpiredSpotMessageFollow(state);
|
|
1378
|
+
return true;
|
|
1379
|
+
}
|
|
1188
1380
|
if (!Number.isSafeInteger(durationMs) || durationMs < 0) {
|
|
1189
1381
|
throw new RangeError('Message Follow duration must be a non-negative safe integer.');
|
|
1190
1382
|
}
|
|
@@ -1326,7 +1518,8 @@ export class ServiceStatefulRuntime {
|
|
|
1326
1518
|
sourceSpotId: string,
|
|
1327
1519
|
requested: ServiceDirectSpotRouteFence,
|
|
1328
1520
|
payload: ServiceApplicationPayload,
|
|
1329
|
-
timeoutMs: number
|
|
1521
|
+
timeoutMs: number,
|
|
1522
|
+
onAdmitted?: () => void
|
|
1330
1523
|
): ServiceStatefulPendingOperation {
|
|
1331
1524
|
const pending = this.operations.register(timeoutMs);
|
|
1332
1525
|
const target = this.acceptSpotAuthority(requested);
|
|
@@ -1346,20 +1539,21 @@ export class ServiceStatefulRuntime {
|
|
|
1346
1539
|
target.targetNodeRid,
|
|
1347
1540
|
[header, encodeApplicationPayload(payload)],
|
|
1348
1541
|
timeoutMs,
|
|
1349
|
-
'spotRequest'
|
|
1542
|
+
'spotRequest',
|
|
1543
|
+
undefined,
|
|
1544
|
+
onAdmitted
|
|
1350
1545
|
);
|
|
1351
1546
|
return pending;
|
|
1352
1547
|
}
|
|
1353
1548
|
|
|
1354
1549
|
async sendToActor(
|
|
1355
1550
|
target: ServiceActorRef,
|
|
1356
|
-
_targetNodeGeneration: bigint,
|
|
1357
|
-
_authorityOwnerGeneration: bigint,
|
|
1358
1551
|
payload: ServiceApplicationPayload,
|
|
1359
1552
|
sourceActor?: ServiceActorRef,
|
|
1360
|
-
boundSession?: { readonly sessionRid: string; readonly bindingGeneration: bigint }
|
|
1553
|
+
boundSession?: { readonly sessionRid: string; readonly bindingGeneration: bigint },
|
|
1554
|
+
routeFence?: ServiceActorRouteFence
|
|
1361
1555
|
): Promise<number> {
|
|
1362
|
-
const route = this.tryActorFence(target);
|
|
1556
|
+
const route = routeFence ?? this.tryActorFence(target);
|
|
1363
1557
|
if (route === undefined) return SubmitResult.NotFound;
|
|
1364
1558
|
const header = encodeActorHeader(
|
|
1365
1559
|
'actorSend',
|
|
@@ -1379,15 +1573,16 @@ export class ServiceStatefulRuntime {
|
|
|
1379
1573
|
|
|
1380
1574
|
requestToActor(
|
|
1381
1575
|
target: ServiceActorRef,
|
|
1382
|
-
_targetNodeGeneration: bigint,
|
|
1383
|
-
_authorityOwnerGeneration: bigint,
|
|
1384
1576
|
payload: ServiceApplicationPayload,
|
|
1385
1577
|
timeoutMs: number,
|
|
1386
1578
|
sourceActor?: ServiceActorRef,
|
|
1387
|
-
boundSession?: { readonly sessionRid: string; readonly bindingGeneration: bigint }
|
|
1579
|
+
boundSession?: { readonly sessionRid: string; readonly bindingGeneration: bigint },
|
|
1580
|
+
onAdmitted?: () => void,
|
|
1581
|
+
routeFence?: ServiceActorRouteFence
|
|
1388
1582
|
): ServiceStatefulPendingOperation {
|
|
1389
|
-
const
|
|
1390
|
-
const
|
|
1583
|
+
const operationLow = this.nextMessageFollowOperation++;
|
|
1584
|
+
const pending = this.operations.register(timeoutMs, 'registry', operationLow);
|
|
1585
|
+
const route = routeFence ?? this.tryActorFence(target);
|
|
1391
1586
|
if (route === undefined) {
|
|
1392
1587
|
this.operations.reply(
|
|
1393
1588
|
pending.id,
|
|
@@ -1406,18 +1601,35 @@ export class ServiceStatefulRuntime {
|
|
|
1406
1601
|
...boundSession,
|
|
1407
1602
|
sequence: this.nextSessionSequence++
|
|
1408
1603
|
},
|
|
1409
|
-
{ high: this.nodeGeneration, low:
|
|
1604
|
+
{ high: this.nodeGeneration, low: operationLow }
|
|
1410
1605
|
);
|
|
1411
1606
|
this.submitRequest(
|
|
1412
1607
|
pending,
|
|
1413
1608
|
target.nodeRid,
|
|
1414
1609
|
[header, encodeApplicationPayload(payload)],
|
|
1415
1610
|
timeoutMs,
|
|
1416
|
-
'actorRequest'
|
|
1611
|
+
'actorRequest',
|
|
1612
|
+
undefined,
|
|
1613
|
+
onAdmitted
|
|
1417
1614
|
);
|
|
1418
1615
|
return pending;
|
|
1419
1616
|
}
|
|
1420
1617
|
|
|
1618
|
+
acceptRelocatedActorTerminal(
|
|
1619
|
+
operation: { readonly high: bigint; readonly low: bigint },
|
|
1620
|
+
replyRouteId: bigint,
|
|
1621
|
+
result: ServiceStatefulResult
|
|
1622
|
+
): 'terminalReceived' | 'alreadyTerminal' | undefined {
|
|
1623
|
+
if (
|
|
1624
|
+
operation.high !== this.nodeGeneration ||
|
|
1625
|
+
this.operations.context(replyRouteId) !== operation.low
|
|
1626
|
+
)
|
|
1627
|
+
return undefined;
|
|
1628
|
+
return this.operations.reply(replyRouteId, result, RELOCATION_REPLY_RETENTION_MS)
|
|
1629
|
+
? 'terminalReceived'
|
|
1630
|
+
: 'alreadyTerminal';
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1421
1633
|
async sendToInstanceSpot(
|
|
1422
1634
|
route: ServiceInstanceRouteFence,
|
|
1423
1635
|
payload: ServiceApplicationPayload,
|
|
@@ -1499,7 +1711,8 @@ export class ServiceStatefulRuntime {
|
|
|
1499
1711
|
timeoutMs: number,
|
|
1500
1712
|
sourceSpotId?: string,
|
|
1501
1713
|
metadataFrame?: Uint8Array,
|
|
1502
|
-
instanceIntent = false
|
|
1714
|
+
instanceIntent = false,
|
|
1715
|
+
onAdmitted?: () => void
|
|
1503
1716
|
): ServiceStatefulPendingOperation {
|
|
1504
1717
|
const pending = this.operations.register(timeoutMs);
|
|
1505
1718
|
this.submitRequest(
|
|
@@ -1523,7 +1736,9 @@ export class ServiceStatefulRuntime {
|
|
|
1523
1736
|
metadataFrame
|
|
1524
1737
|
),
|
|
1525
1738
|
timeoutMs,
|
|
1526
|
-
'instanceSpotRequest'
|
|
1739
|
+
'instanceSpotRequest',
|
|
1740
|
+
undefined,
|
|
1741
|
+
onAdmitted
|
|
1527
1742
|
);
|
|
1528
1743
|
return pending;
|
|
1529
1744
|
}
|
|
@@ -1551,7 +1766,8 @@ export class ServiceStatefulRuntime {
|
|
|
1551
1766
|
payloadFrame: Buffer,
|
|
1552
1767
|
deadlineUnixMs: bigint,
|
|
1553
1768
|
sourceSpotId?: string,
|
|
1554
|
-
metadataFrame?: Uint8Array
|
|
1769
|
+
metadataFrame?: Uint8Array,
|
|
1770
|
+
onAdmitted?: () => void
|
|
1555
1771
|
): ServiceStatefulPendingOperation {
|
|
1556
1772
|
const timeoutMs = remainingDeadlineMs(deadlineUnixMs);
|
|
1557
1773
|
const pending = this.operations.register(timeoutMs);
|
|
@@ -1576,7 +1792,9 @@ export class ServiceStatefulRuntime {
|
|
|
1576
1792
|
metadataFrame
|
|
1577
1793
|
),
|
|
1578
1794
|
timeoutMs,
|
|
1579
|
-
'instanceSpotRequest'
|
|
1795
|
+
'instanceSpotRequest',
|
|
1796
|
+
undefined,
|
|
1797
|
+
onAdmitted
|
|
1580
1798
|
);
|
|
1581
1799
|
return pending;
|
|
1582
1800
|
}
|
|
@@ -1747,27 +1965,51 @@ export class ServiceStatefulRuntime {
|
|
|
1747
1965
|
timeoutMs: number,
|
|
1748
1966
|
deliver: ServiceSessionDelivery['deliver'],
|
|
1749
1967
|
onBindingReplaced?: ServiceSessionDelivery['onBindingReplaced'],
|
|
1750
|
-
actorAuthority?: StreamSessionActorAuthorityFence
|
|
1968
|
+
actorAuthority?: StreamSessionActorAuthorityFence,
|
|
1969
|
+
close?: () => Promise<void>
|
|
1751
1970
|
): ServiceStatefulPendingOperation {
|
|
1752
1971
|
const deadlineMs = performance.now() + timeoutMs;
|
|
1753
1972
|
const pending = this.operations.register(timeoutMs, 'sender');
|
|
1754
|
-
const
|
|
1973
|
+
const sessionOwner = this.localSessionOwner();
|
|
1974
|
+
if (sessionOwner === undefined) {
|
|
1975
|
+
this.operations.reply(
|
|
1976
|
+
pending.id,
|
|
1977
|
+
failure(
|
|
1978
|
+
createInternalFrameworkException(
|
|
1979
|
+
ZLinkFrameworkInternalErrorKind.RouteNotConnected,
|
|
1980
|
+
`Session owner '${this.nodeRid}' has no live MeshNode descriptor.`
|
|
1981
|
+
)
|
|
1982
|
+
)
|
|
1983
|
+
);
|
|
1984
|
+
return pending;
|
|
1985
|
+
}
|
|
1986
|
+
const deliveryKey = actorKey(actor);
|
|
1987
|
+
const previousDelivery = this.sessionDeliveries.get(deliveryKey);
|
|
1988
|
+
const previousBinding = previousDelivery?.binding;
|
|
1989
|
+
const generation =
|
|
1990
|
+
previousBinding !== undefined &&
|
|
1991
|
+
sameActorRef(previousBinding.actor, actor) &&
|
|
1992
|
+
previousBinding.sessionRid === sessionRid &&
|
|
1993
|
+
previousBinding.sessionOwnerNodeRid === sessionOwner.nodeRid &&
|
|
1994
|
+
previousBinding.sessionOwnerNodeGeneration === sessionOwner.nodeGeneration
|
|
1995
|
+
? previousBinding.bindingGeneration
|
|
1996
|
+
: this.nextSessionSequence++;
|
|
1755
1997
|
const localBinding: ServiceSessionBinding = {
|
|
1756
1998
|
actor,
|
|
1757
1999
|
sessionRid,
|
|
1758
|
-
sessionOwnerNodeRid:
|
|
1759
|
-
sessionOwnerNodeGeneration:
|
|
1760
|
-
sessionOwnerId:
|
|
1761
|
-
sessionOwnerLeaseGeneration:
|
|
2000
|
+
sessionOwnerNodeRid: sessionOwner.nodeRid,
|
|
2001
|
+
sessionOwnerNodeGeneration: sessionOwner.nodeGeneration,
|
|
2002
|
+
sessionOwnerId: sessionOwner.ownerId,
|
|
2003
|
+
sessionOwnerLeaseGeneration: sessionOwner.leaseGeneration,
|
|
1762
2004
|
bindingGeneration: generation,
|
|
1763
2005
|
membershipEpoch: this.registry.actor(actor.actorId)?.membershipEpoch ?? 1n
|
|
1764
2006
|
};
|
|
1765
2007
|
const delivery: ServiceSessionDelivery = {
|
|
1766
2008
|
binding: localBinding,
|
|
1767
2009
|
deliver,
|
|
2010
|
+
...(close === undefined ? {} : { close }),
|
|
1768
2011
|
...(onBindingReplaced === undefined ? {} : { onBindingReplaced })
|
|
1769
2012
|
};
|
|
1770
|
-
const deliveryKey = actorKey(actor);
|
|
1771
2013
|
if (actor.nodeRid === this.nodeRid) {
|
|
1772
2014
|
let installedDelivery: ServiceSessionDelivery | undefined;
|
|
1773
2015
|
try {
|
|
@@ -1791,18 +2033,7 @@ export class ServiceStatefulRuntime {
|
|
|
1791
2033
|
});
|
|
1792
2034
|
return pending;
|
|
1793
2035
|
}
|
|
1794
|
-
const
|
|
1795
|
-
const binding = this.registry.bindSession(
|
|
1796
|
-
actor,
|
|
1797
|
-
sessionRid,
|
|
1798
|
-
this.nodeRid,
|
|
1799
|
-
this.nodeGeneration,
|
|
1800
|
-
this.nodeRid,
|
|
1801
|
-
this.nodeGeneration
|
|
1802
|
-
);
|
|
1803
|
-
if (previousDelivery !== undefined && previous !== undefined) {
|
|
1804
|
-
this.retiredSessionDeliveries.set(sessionDeliveryKey(previous), previousDelivery);
|
|
1805
|
-
}
|
|
2036
|
+
const binding = this.registry.installSessionBinding(localBinding);
|
|
1806
2037
|
installedDelivery = { ...delivery, binding };
|
|
1807
2038
|
this.sessionDeliveries.set(deliveryKey, installedDelivery);
|
|
1808
2039
|
this.operations.reply(pending.id, {
|
|
@@ -1810,7 +2041,10 @@ export class ServiceStatefulRuntime {
|
|
|
1810
2041
|
failureCode: 0,
|
|
1811
2042
|
kindData: undefined
|
|
1812
2043
|
});
|
|
1813
|
-
if (previous !== undefined) {
|
|
2044
|
+
if (previous !== undefined && previous !== binding) {
|
|
2045
|
+
if (previousDelivery !== undefined) {
|
|
2046
|
+
this.retiredSessionDeliveries.set(sessionDeliveryKey(previous), previousDelivery);
|
|
2047
|
+
}
|
|
1814
2048
|
this.sendBoundSessionReplacement(
|
|
1815
2049
|
previous,
|
|
1816
2050
|
actorAuthorityFromRoute(
|
|
@@ -1835,7 +2069,6 @@ export class ServiceStatefulRuntime {
|
|
|
1835
2069
|
}
|
|
1836
2070
|
return pending;
|
|
1837
2071
|
}
|
|
1838
|
-
const previousDelivery = this.sessionDeliveries.get(deliveryKey);
|
|
1839
2072
|
let bindingActorFence: ServiceActorRouteFence;
|
|
1840
2073
|
let header: Buffer;
|
|
1841
2074
|
try {
|
|
@@ -1883,7 +2116,10 @@ export class ServiceStatefulRuntime {
|
|
|
1883
2116
|
// Publish the exact Location authority used by command 38 in the
|
|
1884
2117
|
// same Session-owner completion turn as the binding route. Actor
|
|
1885
2118
|
// lookup only carries a peer-lifecycle fallback for owner lease.
|
|
1886
|
-
this.rememberActorRoute(
|
|
2119
|
+
this.rememberActorRoute({
|
|
2120
|
+
...bindingActorFence,
|
|
2121
|
+
authorityOwnerGeneration: result.streamBinding.authorityOwnerGeneration
|
|
2122
|
+
});
|
|
1887
2123
|
this.sessionDeliveries.set(deliveryKey, committed);
|
|
1888
2124
|
}
|
|
1889
2125
|
}
|
|
@@ -1892,6 +2128,59 @@ export class ServiceStatefulRuntime {
|
|
|
1892
2128
|
};
|
|
1893
2129
|
}
|
|
1894
2130
|
|
|
2131
|
+
closeBoundSession(
|
|
2132
|
+
actor: ServiceActorRef,
|
|
2133
|
+
expectedBindingGeneration: bigint,
|
|
2134
|
+
timeoutMs: number
|
|
2135
|
+
): ServiceStatefulPendingOperation {
|
|
2136
|
+
const pending = this.operations.register(timeoutMs);
|
|
2137
|
+
const binding = this.registry.binding(actor);
|
|
2138
|
+
if (binding === undefined) {
|
|
2139
|
+
this.operations.reply(
|
|
2140
|
+
pending.id,
|
|
2141
|
+
internalFrameworkWireReply(ZLinkFrameworkInternalErrorKind.ActorSessionNotBound)
|
|
2142
|
+
);
|
|
2143
|
+
return pending;
|
|
2144
|
+
}
|
|
2145
|
+
const header = Buffer.from(
|
|
2146
|
+
encodeBoundSessionCloseCommand(
|
|
2147
|
+
{
|
|
2148
|
+
command: 'boundSessionClose',
|
|
2149
|
+
flags: 0,
|
|
2150
|
+
correlation: pending.id,
|
|
2151
|
+
actor: { actorId: actor.actorId, objectGeneration: actor.generation },
|
|
2152
|
+
session: {
|
|
2153
|
+
sessionOwnerNodeRid: Buffer.from(
|
|
2154
|
+
encodeRoutingIdStorageHex(binding.sessionOwnerNodeRid),
|
|
2155
|
+
'hex'
|
|
2156
|
+
),
|
|
2157
|
+
sessionOwnerNodeGeneration: binding.sessionOwnerNodeGeneration!,
|
|
2158
|
+
sessionOwnerId: binding.sessionOwnerId!,
|
|
2159
|
+
sessionOwnerLeaseGeneration: binding.sessionOwnerLeaseGeneration!,
|
|
2160
|
+
sessionRid: Buffer.from(encodeRoutingIdStorageHex(binding.sessionRid), 'hex'),
|
|
2161
|
+
retiredBindingGeneration: expectedBindingGeneration
|
|
2162
|
+
}
|
|
2163
|
+
},
|
|
2164
|
+
{}
|
|
2165
|
+
)[0]!
|
|
2166
|
+
);
|
|
2167
|
+
const reply = routingIdsEqual(binding.sessionOwnerNodeRid, this.nodeRid)
|
|
2168
|
+
? this.requestLocalInfrastructure(header, pending.id, timeoutMs)
|
|
2169
|
+
: this.raw.requestService(binding.sessionOwnerNodeRid, [header], timeoutMs);
|
|
2170
|
+
void reply.then(
|
|
2171
|
+
(parts) =>
|
|
2172
|
+
this.completeRemoteReply(
|
|
2173
|
+
pending,
|
|
2174
|
+
binding.sessionOwnerNodeRid,
|
|
2175
|
+
'boundSessionClose',
|
|
2176
|
+
undefined,
|
|
2177
|
+
parts
|
|
2178
|
+
),
|
|
2179
|
+
(error) => this.operations.fail(pending.id, error)
|
|
2180
|
+
);
|
|
2181
|
+
return pending;
|
|
2182
|
+
}
|
|
2183
|
+
|
|
1895
2184
|
unbindSession(
|
|
1896
2185
|
sessionRid: string,
|
|
1897
2186
|
actor: ServiceActorRef,
|
|
@@ -1902,7 +2191,7 @@ export class ServiceStatefulRuntime {
|
|
|
1902
2191
|
const delivery = this.sessionDeliveries.get(actorKey(actor));
|
|
1903
2192
|
if (
|
|
1904
2193
|
delivery === undefined ||
|
|
1905
|
-
delivery.binding.sessionRid
|
|
2194
|
+
!routingIdsEqual(delivery.binding.sessionRid, sessionRid) ||
|
|
1906
2195
|
delivery.binding.bindingGeneration !== expectedBindingGeneration
|
|
1907
2196
|
) {
|
|
1908
2197
|
this.operations.reply(
|
|
@@ -1932,7 +2221,7 @@ export class ServiceStatefulRuntime {
|
|
|
1932
2221
|
sessionBindings(sessionRid: string): readonly ServiceStreamSessionBinding[] {
|
|
1933
2222
|
return [...this.sessionDeliveries.values()]
|
|
1934
2223
|
.map((value) => value.binding)
|
|
1935
|
-
.filter((binding) => binding.sessionRid
|
|
2224
|
+
.filter((binding) => routingIdsEqual(binding.sessionRid, sessionRid))
|
|
1936
2225
|
.map((binding) => {
|
|
1937
2226
|
const owner = requireSessionOwnerIdentity(binding);
|
|
1938
2227
|
return {
|
|
@@ -1954,20 +2243,13 @@ export class ServiceStatefulRuntime {
|
|
|
1954
2243
|
payload: ServiceApplicationPayload
|
|
1955
2244
|
): Promise<number> {
|
|
1956
2245
|
const delivery = this.sessionDeliveries.get(actorKey(actor));
|
|
1957
|
-
if (delivery === undefined || delivery.binding.sessionRid
|
|
2246
|
+
if (delivery === undefined || !routingIdsEqual(delivery.binding.sessionRid, sessionRid)) {
|
|
1958
2247
|
return SubmitResult.NotFound;
|
|
1959
2248
|
}
|
|
1960
|
-
return this.sendToActor(
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
payload,
|
|
1965
|
-
undefined,
|
|
1966
|
-
{
|
|
1967
|
-
sessionRid,
|
|
1968
|
-
bindingGeneration: delivery.binding.bindingGeneration
|
|
1969
|
-
}
|
|
1970
|
-
);
|
|
2249
|
+
return this.sendToActor(actor, payload, undefined, {
|
|
2250
|
+
sessionRid,
|
|
2251
|
+
bindingGeneration: delivery.binding.bindingGeneration
|
|
2252
|
+
});
|
|
1971
2253
|
}
|
|
1972
2254
|
|
|
1973
2255
|
async sendBoundSession(
|
|
@@ -2003,6 +2285,11 @@ export class ServiceStatefulRuntime {
|
|
|
2003
2285
|
return this.operations.size;
|
|
2004
2286
|
}
|
|
2005
2287
|
|
|
2288
|
+
/** The next stateful request expiry, as a lower bound. */
|
|
2289
|
+
nextMaintenanceDeadlineMs(): number {
|
|
2290
|
+
return this.operations.nextDeadlineMs();
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2006
2293
|
close(): void {
|
|
2007
2294
|
if (this.closed) return;
|
|
2008
2295
|
this.closed = true;
|
|
@@ -2026,32 +2313,13 @@ export class ServiceStatefulRuntime {
|
|
|
2026
2313
|
this.spotMessageFollow.clear();
|
|
2027
2314
|
}
|
|
2028
2315
|
|
|
2316
|
+
cancelPendingOperations(reason: unknown): void {
|
|
2317
|
+
this.operations.failAll(reason);
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2029
2320
|
private async ingress(
|
|
2030
2321
|
record: RawServiceIngressRecord
|
|
2031
2322
|
): Promise<RawServicePumpResult | undefined> {
|
|
2032
|
-
// These frozen relocation controls overlap the legacy stateful command
|
|
2033
|
-
// number range (30-34). They are bare infrastructure records and must
|
|
2034
|
-
// fall through to RawServiceMeshRuntime's relocation ingress instead of
|
|
2035
|
-
// being decoded as stateful actor/spot work.
|
|
2036
|
-
if (
|
|
2037
|
-
record.command === M6bServiceWireCommand.relocationReady ||
|
|
2038
|
-
record.command === M6bServiceWireCommand.relocationData ||
|
|
2039
|
-
record.command === M6bServiceWireCommand.replyRelay ||
|
|
2040
|
-
record.command === M6bServiceWireCommand.relocationCutover
|
|
2041
|
-
) {
|
|
2042
|
-
return undefined;
|
|
2043
|
-
}
|
|
2044
|
-
if (
|
|
2045
|
-
record.command < M6bServiceWireCommand.spotSend ||
|
|
2046
|
-
(record.command > M6bServiceWireCommand.instanceSpot &&
|
|
2047
|
-
record.command !== M6bServiceWireCommand.userSpotCreate &&
|
|
2048
|
-
record.command !== M6bServiceWireCommand.userSpotClose &&
|
|
2049
|
-
record.command !== M6bServiceWireCommand.actorCreate &&
|
|
2050
|
-
record.command !== M6bServiceWireCommand.messageFollow &&
|
|
2051
|
-
record.command !== M6bServiceWireCommand.boundSessionReplaced)
|
|
2052
|
-
) {
|
|
2053
|
-
return undefined;
|
|
2054
|
-
}
|
|
2055
2323
|
let decoded: ServiceStatefulWireRecord;
|
|
2056
2324
|
let canonicalActorJoin: ActorJoin28 | undefined;
|
|
2057
2325
|
try {
|
|
@@ -2098,7 +2366,8 @@ export class ServiceStatefulRuntime {
|
|
|
2098
2366
|
decoded.kind === 'userSpotClose' ||
|
|
2099
2367
|
decoded.kind === 'actorCreate' ||
|
|
2100
2368
|
decoded.kind === 'messageFollow' ||
|
|
2101
|
-
decoded.kind === 'boundSessionReplaced'
|
|
2369
|
+
decoded.kind === 'boundSessionReplaced' ||
|
|
2370
|
+
decoded.kind === 'boundSessionClose';
|
|
2102
2371
|
if (
|
|
2103
2372
|
record.parts.length < 1 ||
|
|
2104
2373
|
record.parts.length > (instanceMetadata || statefulMetadata ? 3 : 2) ||
|
|
@@ -2143,7 +2412,13 @@ export class ServiceStatefulRuntime {
|
|
|
2143
2412
|
decoded.activation === 'missing' &&
|
|
2144
2413
|
(error instanceof ZLinkFrameworkException || error instanceof ServiceStaleGenerationError)
|
|
2145
2414
|
) {
|
|
2146
|
-
this.finishMissingInstanceActivationFailure(
|
|
2415
|
+
this.finishMissingInstanceActivationFailure(
|
|
2416
|
+
record,
|
|
2417
|
+
decoded,
|
|
2418
|
+
error,
|
|
2419
|
+
undefined,
|
|
2420
|
+
payloadFrame
|
|
2421
|
+
);
|
|
2147
2422
|
return 'infrastructure';
|
|
2148
2423
|
}
|
|
2149
2424
|
const correlation = statefulCorrelation(decoded);
|
|
@@ -2275,7 +2550,7 @@ export class ServiceStatefulRuntime {
|
|
|
2275
2550
|
record.boundSession.bindingGeneration
|
|
2276
2551
|
);
|
|
2277
2552
|
if (
|
|
2278
|
-
binding.sessionRid
|
|
2553
|
+
!routingIdsEqual(binding.sessionRid, record.boundSession.sessionRid) ||
|
|
2279
2554
|
binding.sessionOwnerNodeRid !== ingress.sourceRoutingId
|
|
2280
2555
|
) {
|
|
2281
2556
|
throw new ServiceStaleGenerationError('binding', record.target.actor.actorId);
|
|
@@ -2298,12 +2573,13 @@ export class ServiceStatefulRuntime {
|
|
|
2298
2573
|
: { sourceBindingGeneration: record.boundSession.bindingGeneration }),
|
|
2299
2574
|
targetActor: actor.ref,
|
|
2300
2575
|
messageFollowOrigin: {
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
high: this.peerGeneration(ingress.sourceRoutingId),
|
|
2304
|
-
low: record.correlation ?? this.nextMessageFollowOperation++
|
|
2576
|
+
get requestSource() {
|
|
2577
|
+
return ingress.requestSource;
|
|
2305
2578
|
},
|
|
2306
|
-
|
|
2579
|
+
target: record.target,
|
|
2580
|
+
sourceNodeRid: ingress.sourceRoutingId,
|
|
2581
|
+
originalOperation: record.operation,
|
|
2582
|
+
originalReplyRouteId: record.correlation ?? 0n
|
|
2307
2583
|
},
|
|
2308
2584
|
...(record.kind === 'actorRequest'
|
|
2309
2585
|
? { reply: this.replyPort(ingress, record.correlation) }
|
|
@@ -2341,7 +2617,9 @@ export class ServiceStatefulRuntime {
|
|
|
2341
2617
|
canonicalActorJoinPayload
|
|
2342
2618
|
);
|
|
2343
2619
|
case 'boundSessionBind':
|
|
2344
|
-
return this.replySessionBind(ingress, record);
|
|
2620
|
+
return await this.replySessionBind(ingress, record);
|
|
2621
|
+
case 'boundSessionClose':
|
|
2622
|
+
return await this.handleBoundSessionClose(ingress, record);
|
|
2345
2623
|
case 'boundSessionReplaced':
|
|
2346
2624
|
return this.handleBoundSessionReplaced(ingress, record);
|
|
2347
2625
|
case 'boundSessionSend':
|
|
@@ -2371,6 +2649,7 @@ export class ServiceStatefulRuntime {
|
|
|
2371
2649
|
localReply,
|
|
2372
2650
|
metadataFrame
|
|
2373
2651
|
).finally(() => ownedIngress.applicationJobOwner?.close());
|
|
2652
|
+
ingress.onAdmitted?.();
|
|
2374
2653
|
return 'infrastructure';
|
|
2375
2654
|
}
|
|
2376
2655
|
if (
|
|
@@ -2384,6 +2663,7 @@ export class ServiceStatefulRuntime {
|
|
|
2384
2663
|
payloadFrame,
|
|
2385
2664
|
metadataFrame
|
|
2386
2665
|
).finally(() => ownedIngress.applicationJobOwner?.close());
|
|
2666
|
+
ingress.onAdmitted?.();
|
|
2387
2667
|
return 'infrastructure';
|
|
2388
2668
|
}
|
|
2389
2669
|
const spot = this.requireInstanceActivation(ingress, record);
|
|
@@ -2546,7 +2826,7 @@ export class ServiceStatefulRuntime {
|
|
|
2546
2826
|
}
|
|
2547
2827
|
});
|
|
2548
2828
|
} catch (error) {
|
|
2549
|
-
this.finishMissingInstanceActivationFailure(ingress, record, error, localReply);
|
|
2829
|
+
this.finishMissingInstanceActivationFailure(ingress, record, error, localReply, payloadFrame);
|
|
2550
2830
|
}
|
|
2551
2831
|
}
|
|
2552
2832
|
|
|
@@ -2581,6 +2861,7 @@ export class ServiceStatefulRuntime {
|
|
|
2581
2861
|
latestRoute !== undefined && sameInstanceApplicationOwner(latestRoute, record.route)
|
|
2582
2862
|
? { ...record, route: latestRoute }
|
|
2583
2863
|
: record;
|
|
2864
|
+
this.publishCommittedInstanceRoute(intent.instanceType, refreshedRecord.route);
|
|
2584
2865
|
const spot = this.requireInstanceActivation(ingress, refreshedRecord);
|
|
2585
2866
|
const admitted = this.enqueueActivatedInstanceSpot(
|
|
2586
2867
|
ingress,
|
|
@@ -2737,7 +3018,8 @@ export class ServiceStatefulRuntime {
|
|
|
2737
3018
|
{ readonly kind: 'instanceSpot'; readonly activation: 'missing' }
|
|
2738
3019
|
>,
|
|
2739
3020
|
error: unknown,
|
|
2740
|
-
localReply?: NonNullable<ServiceStatefulMailboxData['reply']
|
|
3021
|
+
localReply?: NonNullable<ServiceStatefulMailboxData['reply']>,
|
|
3022
|
+
payloadFrame?: Buffer
|
|
2741
3023
|
): void {
|
|
2742
3024
|
if (record.operationKind === 'request') {
|
|
2743
3025
|
if (record.deadlineUnixMs <= BigInt(Date.now())) return;
|
|
@@ -2750,17 +3032,51 @@ export class ServiceStatefulRuntime {
|
|
|
2750
3032
|
return;
|
|
2751
3033
|
}
|
|
2752
3034
|
const reporter = this.dispatchErrors;
|
|
3035
|
+
const tracePoint = flowIfEnabled(reporter?.flow, ZLinkMessageFlowOutcome.Dropped);
|
|
3036
|
+
if (tracePoint === undefined && reporter?.captureEnabled() !== true) return;
|
|
3037
|
+
const reason = isAbortError(error)
|
|
3038
|
+
? ZLinkDispatchErrorReason.Shutdown
|
|
3039
|
+
: dispatchReasonFromError(error);
|
|
3040
|
+
// Decode the retained first message only after diagnostics accepts the terminal.
|
|
3041
|
+
// Detached local and remote activation share this owner and its explicit flow.
|
|
3042
|
+
const payload =
|
|
3043
|
+
payloadFrame === undefined ? undefined : decodeApplicationPayloadView(payloadFrame);
|
|
3044
|
+
const header =
|
|
3045
|
+
payload?.contentType === SERVICE_FRAMEWORK_MULTIPART_CONTENT_TYPE
|
|
3046
|
+
? decodeChannelHeader(
|
|
3047
|
+
decodeMultipartApplicationParts(payloadFrame!, ZLinkBufferMessage.fromOwned),
|
|
3048
|
+
reporter?.flow.flowCreationEnabled()
|
|
3049
|
+
)
|
|
3050
|
+
: undefined;
|
|
3051
|
+
const context = {
|
|
3052
|
+
surface: ZLinkDispatchErrorSurface.InstanceSpot,
|
|
3053
|
+
messageKind: ZLinkDispatchMessageKind.Send,
|
|
3054
|
+
meshName: this.dispatchErrorMeshName,
|
|
3055
|
+
sourceRid: record.sourceNodeRid,
|
|
3056
|
+
spotId: record.target.targetSpotId,
|
|
3057
|
+
instanceSpotType: record.target.stableType,
|
|
3058
|
+
activationState: 'activating' as const,
|
|
3059
|
+
packetName: header?.messageName ?? payload?.packetName,
|
|
3060
|
+
flowId: header?.flowId,
|
|
3061
|
+
flowOrigin: header?.flowOrigin
|
|
3062
|
+
};
|
|
3063
|
+
tracePoint?.trace({
|
|
3064
|
+
...context,
|
|
3065
|
+
outcome: ZLinkMessageFlowOutcome.Dropped,
|
|
3066
|
+
errorReason:
|
|
3067
|
+
reason === ZLinkDispatchErrorReason.Shutdown
|
|
3068
|
+
? reason
|
|
3069
|
+
: error instanceof ZLinkFrameworkException &&
|
|
3070
|
+
error.kind === ZLinkFrameworkErrorKind.DeadlineExceeded
|
|
3071
|
+
? 'activation_timeout'
|
|
3072
|
+
: 'activation_rejected',
|
|
3073
|
+
...dispatchErrorDetails(error)
|
|
3074
|
+
});
|
|
2753
3075
|
if (reporter?.captureEnabled() === true) {
|
|
2754
3076
|
reporter.report({
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
reason: ZLinkDispatchErrorReason.StaleTarget,
|
|
3077
|
+
...context,
|
|
3078
|
+
reason,
|
|
2758
3079
|
action: ZLinkDispatchErrorAction.Drop,
|
|
2759
|
-
meshName: this.dispatchErrorMeshName,
|
|
2760
|
-
sourceRid: record.sourceNodeRid,
|
|
2761
|
-
spotId: record.target.targetSpotId,
|
|
2762
|
-
instanceSpotType: record.target.stableType,
|
|
2763
|
-
activationState: 'activating',
|
|
2764
3080
|
error
|
|
2765
3081
|
});
|
|
2766
3082
|
}
|
|
@@ -2979,12 +3295,25 @@ export class ServiceStatefulRuntime {
|
|
|
2979
3295
|
): Promise<unknown> {
|
|
2980
3296
|
const key = record.target.targetSpotId;
|
|
2981
3297
|
const pending = this.pendingInstanceActivations.get(key);
|
|
3298
|
+
const metric =
|
|
3299
|
+
pending === undefined
|
|
3300
|
+
? this.dispatchErrors?.metrics?.startInstanceSpotActivation(
|
|
3301
|
+
record.target.targetMeshName,
|
|
3302
|
+
record.target.stableType
|
|
3303
|
+
)
|
|
3304
|
+
: undefined;
|
|
2982
3305
|
const ready =
|
|
2983
3306
|
pending ??
|
|
2984
|
-
this.runMissingInstanceActivation(record, payloadFrame, metadataFrame).then(
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
3307
|
+
this.runMissingInstanceActivation(record, payloadFrame, metadataFrame).then(
|
|
3308
|
+
(activation) => {
|
|
3309
|
+
metric?.complete('ready');
|
|
3310
|
+
return { ...activation, stableType: record.target.stableType };
|
|
3311
|
+
},
|
|
3312
|
+
(error) => {
|
|
3313
|
+
metric?.complete(instanceActivationOutcome(error));
|
|
3314
|
+
throw error;
|
|
3315
|
+
}
|
|
3316
|
+
);
|
|
2988
3317
|
const operation = ready.then(async (value) => {
|
|
2989
3318
|
if (pending !== undefined) {
|
|
2990
3319
|
await this.asyncInstanceAuthority?.read(
|
|
@@ -3033,12 +3362,38 @@ export class ServiceStatefulRuntime {
|
|
|
3033
3362
|
): Promise<{
|
|
3034
3363
|
readonly spot: Pick<ServiceSpotState, 'ref'>;
|
|
3035
3364
|
readonly route: ServiceInstanceRouteFence;
|
|
3365
|
+
}> {
|
|
3366
|
+
const deadline = operationDeadline(record.deadlineUnixMs);
|
|
3367
|
+
try {
|
|
3368
|
+
deadline.signal.throwIfAborted();
|
|
3369
|
+
return await awaitWithAbort(
|
|
3370
|
+
this.runMissingInstanceActivationCore(record, payloadFrame, metadataFrame, deadline.signal),
|
|
3371
|
+
deadline.signal
|
|
3372
|
+
);
|
|
3373
|
+
} catch (error) {
|
|
3374
|
+
if (isAbortError(error) && deadline.signal.aborted) throw deadline.signal.reason;
|
|
3375
|
+
throw error;
|
|
3376
|
+
} finally {
|
|
3377
|
+
deadline.close();
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
|
|
3381
|
+
private async runMissingInstanceActivationCore(
|
|
3382
|
+
record: Extract<
|
|
3383
|
+
ServiceStatefulWireRecord,
|
|
3384
|
+
{ readonly kind: 'instanceSpot'; readonly activation: 'missing' }
|
|
3385
|
+
>,
|
|
3386
|
+
payloadFrame: Buffer,
|
|
3387
|
+
metadataFrame: Buffer | undefined,
|
|
3388
|
+
signal: AbortSignal
|
|
3389
|
+
): Promise<{
|
|
3390
|
+
readonly spot: Pick<ServiceSpotState, 'ref'>;
|
|
3391
|
+
readonly route: ServiceInstanceRouteFence;
|
|
3036
3392
|
}> {
|
|
3037
3393
|
const target = record.target;
|
|
3038
3394
|
if (
|
|
3039
3395
|
target.targetNodeRid !== this.nodeRid ||
|
|
3040
|
-
target.targetNodeGeneration !== this.nodeGeneration
|
|
3041
|
-
record.deadlineUnixMs < BigInt(Date.now())
|
|
3396
|
+
target.targetNodeGeneration !== this.nodeGeneration
|
|
3042
3397
|
) {
|
|
3043
3398
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
3044
3399
|
}
|
|
@@ -3150,21 +3505,22 @@ export class ServiceStatefulRuntime {
|
|
|
3150
3505
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
3151
3506
|
}
|
|
3152
3507
|
}
|
|
3153
|
-
const
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3508
|
+
const reserved = await authority.reserve(activationRequest, signal);
|
|
3509
|
+
if (this.closed || signal.aborted) {
|
|
3510
|
+
const error = new ZLinkFrameworkException(
|
|
3511
|
+
this.closed
|
|
3512
|
+
? ZLinkFrameworkErrorKind.ShuttingDown
|
|
3513
|
+
: ZLinkFrameworkErrorKind.DeadlineExceeded,
|
|
3514
|
+
this.closed ? 'Runtime stopped during Instance activation.' : 'Operation deadline exceeded.'
|
|
3515
|
+
);
|
|
3161
3516
|
await authority.abort(target, reserved.reservation);
|
|
3162
|
-
throw
|
|
3517
|
+
throw error;
|
|
3163
3518
|
}
|
|
3164
3519
|
|
|
3165
3520
|
let activation: { readonly spot: ServiceSpotState; readonly created: boolean };
|
|
3166
3521
|
try {
|
|
3167
3522
|
await this.instanceApplicationLifecycle?.materialize(target, reserved.reservation.attempt);
|
|
3523
|
+
signal.throwIfAborted();
|
|
3168
3524
|
activation = this.activateInstanceSpot(
|
|
3169
3525
|
target.targetSpotId,
|
|
3170
3526
|
target.stableType,
|
|
@@ -3232,8 +3588,6 @@ export class ServiceStatefulRuntime {
|
|
|
3232
3588
|
if (activation.created) this.registry.closeSpot(activation.spot.ref);
|
|
3233
3589
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
3234
3590
|
}
|
|
3235
|
-
this.publishCommittedInstanceRoute(target.stableType, route);
|
|
3236
|
-
|
|
3237
3591
|
const confirmed = await authority.read(target);
|
|
3238
3592
|
if (confirmed.kind !== 'ready' || !sameInstanceRoute(confirmed.route, route)) {
|
|
3239
3593
|
if (confirmed.kind === 'ready' && confirmed.route.targetNodeRid !== this.nodeRid) {
|
|
@@ -3248,6 +3602,7 @@ export class ServiceStatefulRuntime {
|
|
|
3248
3602
|
) {
|
|
3249
3603
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
3250
3604
|
}
|
|
3605
|
+
this.publishCommittedInstanceRoute(target.stableType, confirmed.route);
|
|
3251
3606
|
return { spot: current, route: confirmed.route };
|
|
3252
3607
|
}
|
|
3253
3608
|
|
|
@@ -3265,6 +3620,9 @@ export class ServiceStatefulRuntime {
|
|
|
3265
3620
|
: instanceRouteAsDirectRoute(expectedCurrentRoute);
|
|
3266
3621
|
this.rememberSpotRoute(directRoute, expectedDirectRoute);
|
|
3267
3622
|
this.registerInstanceIntent(stableType, route, expectedCurrentRoute);
|
|
3623
|
+
const target = this.instanceSpotApplicationTarget(route.targetSpotId);
|
|
3624
|
+
if (target !== undefined)
|
|
3625
|
+
this.instanceApplicationLifecycle?.publish?.({ targetSpotId: route.targetSpotId, ...target });
|
|
3268
3626
|
}
|
|
3269
3627
|
|
|
3270
3628
|
private replaceCommittedInstanceRoute(
|
|
@@ -3417,55 +3775,52 @@ export class ServiceStatefulRuntime {
|
|
|
3417
3775
|
return 'infrastructure';
|
|
3418
3776
|
}
|
|
3419
3777
|
|
|
3420
|
-
private replySessionBind(
|
|
3778
|
+
private async replySessionBind(
|
|
3421
3779
|
ingress: RawServiceIngressRecord,
|
|
3422
3780
|
record: Extract<ServiceStatefulWireRecord, { readonly kind: 'boundSessionBind' }>
|
|
3423
|
-
): RawServicePumpResult {
|
|
3781
|
+
): Promise<RawServicePumpResult> {
|
|
3424
3782
|
try {
|
|
3425
3783
|
if (record.binding.state === 'active') {
|
|
3784
|
+
const sessionOwner = await this.sessionOwnerOf(
|
|
3785
|
+
ingress.sourceRoutingId,
|
|
3786
|
+
ingress.sourceNodeGeneration ?? this.peerGeneration(ingress.sourceRoutingId)
|
|
3787
|
+
);
|
|
3426
3788
|
const actor = this.validateActorFence(record.actor);
|
|
3427
|
-
const previous = this.registry.binding(actor.ref);
|
|
3428
|
-
const sourceGeneration =
|
|
3429
|
-
ingress.sourceNodeGeneration ?? this.peerGeneration(ingress.sourceRoutingId);
|
|
3430
3789
|
const binding = sessionBindingFromWire(
|
|
3431
3790
|
actor.ref,
|
|
3432
3791
|
record.sessionRid,
|
|
3433
|
-
|
|
3792
|
+
sessionOwner.nodeRid,
|
|
3434
3793
|
record.binding.generation,
|
|
3435
3794
|
actor.membershipEpoch,
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3795
|
+
sessionOwner.nodeGeneration,
|
|
3796
|
+
sessionOwner.ownerId,
|
|
3797
|
+
sessionOwner.leaseGeneration
|
|
3439
3798
|
);
|
|
3440
|
-
|
|
3441
|
-
// Registry publication is the last step of the first binding owner
|
|
3442
|
-
// turn, so an observable same-Session binding already has every
|
|
3443
|
-
// Actor route projection installed and can complete idempotently.
|
|
3444
|
-
this.replyWire(ingress, record.correlation, RequestResult.Ok, 0, undefined, {
|
|
3445
|
-
kind: 'streamBind',
|
|
3446
|
-
bindingGeneration: previous.bindingGeneration,
|
|
3447
|
-
authorityOwnerGeneration: actor.authorityOwnerGeneration
|
|
3448
|
-
});
|
|
3449
|
-
return 'infrastructure';
|
|
3450
|
-
}
|
|
3799
|
+
let completedBinding = binding;
|
|
3451
3800
|
const accepted = this.enqueueActorBindingControl(
|
|
3452
3801
|
ingress,
|
|
3453
3802
|
binding,
|
|
3454
3803
|
record.actor,
|
|
3455
3804
|
(terminalResult, failureCode) => {
|
|
3456
|
-
let completedBinding = binding;
|
|
3457
3805
|
let replacedBinding: ServiceSessionBinding | undefined;
|
|
3458
3806
|
if (terminalResult === RequestResult.Ok) {
|
|
3459
3807
|
// This callback runs inside the Actor binding control turn,
|
|
3460
|
-
//
|
|
3808
|
+
// before the Host copies the accepted Actor-to-session projection.
|
|
3461
3809
|
// Publish the authority-owned registry view in that same turn
|
|
3462
3810
|
// before making the terminal reply observable.
|
|
3463
3811
|
const turnCurrent = this.registry.binding(actor.ref);
|
|
3464
|
-
|
|
3465
|
-
completedBinding =
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3812
|
+
try {
|
|
3813
|
+
completedBinding = this.registry.installSessionBinding(binding);
|
|
3814
|
+
if (turnCurrent !== completedBinding) replacedBinding = turnCurrent;
|
|
3815
|
+
} catch (error) {
|
|
3816
|
+
const result = failure(error);
|
|
3817
|
+
this.replyWire(
|
|
3818
|
+
ingress,
|
|
3819
|
+
record.correlation,
|
|
3820
|
+
result.terminalResult,
|
|
3821
|
+
result.failureCode
|
|
3822
|
+
);
|
|
3823
|
+
return false;
|
|
3469
3824
|
}
|
|
3470
3825
|
}
|
|
3471
3826
|
this.replyWire(
|
|
@@ -3496,7 +3851,8 @@ export class ServiceStatefulRuntime {
|
|
|
3496
3851
|
);
|
|
3497
3852
|
}
|
|
3498
3853
|
return true;
|
|
3499
|
-
}
|
|
3854
|
+
},
|
|
3855
|
+
() => completedBinding
|
|
3500
3856
|
);
|
|
3501
3857
|
if (!accepted) {
|
|
3502
3858
|
const result = failure(
|
|
@@ -3517,6 +3873,36 @@ export class ServiceStatefulRuntime {
|
|
|
3517
3873
|
return 'infrastructure';
|
|
3518
3874
|
}
|
|
3519
3875
|
|
|
3876
|
+
private async handleBoundSessionClose(
|
|
3877
|
+
ingress: RawServiceIngressRecord,
|
|
3878
|
+
record: Extract<ServiceStatefulWireRecord, { readonly kind: 'boundSessionClose' }>
|
|
3879
|
+
): Promise<RawServicePumpResult> {
|
|
3880
|
+
if (ingress.requestSequence === undefined) return 'protocolError';
|
|
3881
|
+
try {
|
|
3882
|
+
const delivery = this.sessionDeliveries.get(actorKey(record.actor));
|
|
3883
|
+
const sessionOwner = this.localSessionOwner();
|
|
3884
|
+
if (
|
|
3885
|
+
sessionOwner === undefined ||
|
|
3886
|
+
!sessionOwnerFenceMatches(sessionOwner, record.session) ||
|
|
3887
|
+
delivery === undefined ||
|
|
3888
|
+
delivery.binding.actor.generation !== record.actor.generation ||
|
|
3889
|
+
!exactRetiredDelivery(delivery.binding, record.session) ||
|
|
3890
|
+
delivery.close === undefined
|
|
3891
|
+
) {
|
|
3892
|
+
throw createInternalFrameworkException(
|
|
3893
|
+
ZLinkFrameworkInternalErrorKind.RouteNotConnected,
|
|
3894
|
+
'The requested session binding is no longer current.'
|
|
3895
|
+
);
|
|
3896
|
+
}
|
|
3897
|
+
await delivery.close();
|
|
3898
|
+
this.replyWire(ingress, record.correlation, RequestResult.Ok, 0);
|
|
3899
|
+
} catch (error) {
|
|
3900
|
+
const result = failure(error);
|
|
3901
|
+
this.replyWire(ingress, record.correlation, result.terminalResult, result.failureCode);
|
|
3902
|
+
}
|
|
3903
|
+
return 'infrastructure';
|
|
3904
|
+
}
|
|
3905
|
+
|
|
3520
3906
|
/**
|
|
3521
3907
|
* Handles the replacement notice at the retired session owner. This path
|
|
3522
3908
|
* intentionally never calls validateActorFence(): the notice is authorized
|
|
@@ -3539,12 +3925,25 @@ export class ServiceStatefulRuntime {
|
|
|
3539
3925
|
// session that belongs to the new lifecycle.
|
|
3540
3926
|
return 'infrastructure';
|
|
3541
3927
|
}
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3928
|
+
const sessionOwner = this.localSessionOwner();
|
|
3929
|
+
if (sessionOwner === undefined) {
|
|
3930
|
+
traceSessionBindingControlDrop(
|
|
3931
|
+
this.dispatchErrors?.flow,
|
|
3932
|
+
ingress.sourceRoutingId,
|
|
3933
|
+
authority.actor.actorId,
|
|
3934
|
+
'boundSessionReplaced',
|
|
3935
|
+
'location_unavailable'
|
|
3936
|
+
);
|
|
3937
|
+
return 'infrastructure';
|
|
3938
|
+
}
|
|
3939
|
+
if (!sessionOwnerFenceMatches(sessionOwner, record.retiredSession)) {
|
|
3940
|
+
traceSessionBindingControlDrop(
|
|
3941
|
+
this.dispatchErrors?.flow,
|
|
3942
|
+
ingress.sourceRoutingId,
|
|
3943
|
+
authority.actor.actorId,
|
|
3944
|
+
'boundSessionReplaced',
|
|
3945
|
+
'stale_target'
|
|
3946
|
+
);
|
|
3548
3947
|
return 'infrastructure';
|
|
3549
3948
|
}
|
|
3550
3949
|
const actor = authority.actor;
|
|
@@ -3571,6 +3970,9 @@ export class ServiceStatefulRuntime {
|
|
|
3571
3970
|
: undefined;
|
|
3572
3971
|
if (delivery === undefined) return 'infrastructure';
|
|
3573
3972
|
this.appliedReplacementNotices.add(key);
|
|
3973
|
+
if (delivery === current) {
|
|
3974
|
+
this.sessionDeliveries.delete(deliveryKey);
|
|
3975
|
+
}
|
|
3574
3976
|
delivery.onBindingReplaced?.(actor.actorId, record.retiredSession, actor);
|
|
3575
3977
|
if (retired !== undefined) {
|
|
3576
3978
|
this.retiredSessionDeliveries.delete(sessionDeliveryKey(retired.binding));
|
|
@@ -3582,14 +3984,20 @@ export class ServiceStatefulRuntime {
|
|
|
3582
3984
|
previous: ServiceSessionBinding,
|
|
3583
3985
|
actorAuthority: ServiceBoundSessionActorAuthority
|
|
3584
3986
|
): void {
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
if (
|
|
3987
|
+
// Command 51 carries the retired binding's stored identity unchanged; a
|
|
3988
|
+
// binding without one has nothing to notify.
|
|
3989
|
+
if (
|
|
3990
|
+
previous.sessionOwnerNodeGeneration === undefined ||
|
|
3991
|
+
previous.sessionOwnerId === undefined ||
|
|
3992
|
+
previous.sessionOwnerLeaseGeneration === undefined
|
|
3993
|
+
) {
|
|
3994
|
+
return;
|
|
3995
|
+
}
|
|
3588
3996
|
const header = encodeBoundSessionReplacedHeader(actorAuthority, {
|
|
3589
3997
|
sessionOwnerNodeRid: previous.sessionOwnerNodeRid,
|
|
3590
|
-
sessionOwnerNodeGeneration:
|
|
3591
|
-
sessionOwnerId: previous.sessionOwnerId
|
|
3592
|
-
sessionOwnerLeaseGeneration: previous.sessionOwnerLeaseGeneration
|
|
3998
|
+
sessionOwnerNodeGeneration: previous.sessionOwnerNodeGeneration,
|
|
3999
|
+
sessionOwnerId: previous.sessionOwnerId,
|
|
4000
|
+
sessionOwnerLeaseGeneration: previous.sessionOwnerLeaseGeneration,
|
|
3593
4001
|
sessionRid: previous.sessionRid,
|
|
3594
4002
|
retiredBindingGeneration: previous.bindingGeneration
|
|
3595
4003
|
});
|
|
@@ -3612,11 +4020,9 @@ export class ServiceStatefulRuntime {
|
|
|
3612
4020
|
current === undefined ||
|
|
3613
4021
|
sourceGeneration === undefined ||
|
|
3614
4022
|
current.bindingGeneration !== record.binding.retiredGeneration ||
|
|
3615
|
-
current.sessionRid
|
|
4023
|
+
!routingIdsEqual(current.sessionRid, record.sessionRid) ||
|
|
3616
4024
|
current.sessionOwnerNodeRid !== ingress.sourceRoutingId ||
|
|
3617
|
-
current.sessionOwnerNodeGeneration !== sourceGeneration
|
|
3618
|
-
current.sessionOwnerId !== ingress.sourceRoutingId ||
|
|
3619
|
-
current.sessionOwnerLeaseGeneration !== sourceGeneration
|
|
4025
|
+
current.sessionOwnerNodeGeneration !== sourceGeneration
|
|
3620
4026
|
) {
|
|
3621
4027
|
this.replyWire(ingress, record.correlation, RequestResult.Ok, 0);
|
|
3622
4028
|
return;
|
|
@@ -3781,6 +4187,37 @@ export class ServiceStatefulRuntime {
|
|
|
3781
4187
|
});
|
|
3782
4188
|
}
|
|
3783
4189
|
|
|
4190
|
+
private prepareRequestSource(
|
|
4191
|
+
ingress: RawServiceIngressRecord,
|
|
4192
|
+
stateful: ServiceStatefulMailboxData,
|
|
4193
|
+
signal: AbortSignal
|
|
4194
|
+
): boolean | Promise<boolean> {
|
|
4195
|
+
const reject = (error: unknown): false => {
|
|
4196
|
+
if (signal.aborted) return false;
|
|
4197
|
+
if (!(error instanceof ZLinkFrameworkException)) throw error;
|
|
4198
|
+
const result = failure(error);
|
|
4199
|
+
stateful.reply?.(result.terminalResult, result.failureCode);
|
|
4200
|
+
return false;
|
|
4201
|
+
};
|
|
4202
|
+
if (signal.aborted) return false;
|
|
4203
|
+
try {
|
|
4204
|
+
const pending = this.acceptRequestSource(ingress, signal);
|
|
4205
|
+
return pending === undefined
|
|
4206
|
+
? true
|
|
4207
|
+
: awaitWithAbort(pending, signal).then(() => true, reject);
|
|
4208
|
+
} catch (error) {
|
|
4209
|
+
return reject(error);
|
|
4210
|
+
}
|
|
4211
|
+
}
|
|
4212
|
+
|
|
4213
|
+
prepareDispatch(record: ServiceMailboxRecord, signal: AbortSignal): boolean | Promise<boolean> {
|
|
4214
|
+
return this.prepareRequestSource(
|
|
4215
|
+
record.ingress!,
|
|
4216
|
+
record.stateful as ServiceStatefulMailboxData,
|
|
4217
|
+
signal
|
|
4218
|
+
);
|
|
4219
|
+
}
|
|
4220
|
+
|
|
3784
4221
|
private enqueueApplicationFrame(
|
|
3785
4222
|
ingress: RawServiceIngressRecord,
|
|
3786
4223
|
owner: string,
|
|
@@ -3812,6 +4249,10 @@ export class ServiceStatefulRuntime {
|
|
|
3812
4249
|
domain,
|
|
3813
4250
|
parts: [ingress.parts[0]!, payloadFrame],
|
|
3814
4251
|
sourceRoutingId: ingress.sourceRoutingId,
|
|
4252
|
+
ingress,
|
|
4253
|
+
get requestSource() {
|
|
4254
|
+
return ingress.requestSource;
|
|
4255
|
+
},
|
|
3815
4256
|
sourceRoute: ingress.sourceRoute,
|
|
3816
4257
|
...(ingress.reply === undefined ? {} : { reply: ingress.reply }),
|
|
3817
4258
|
...(ingress.requestSequence === undefined
|
|
@@ -3819,9 +4260,21 @@ export class ServiceStatefulRuntime {
|
|
|
3819
4260
|
: { requestSequence: ingress.requestSequence }),
|
|
3820
4261
|
...(stateful.correlation === undefined ? {} : { correlation: stateful.correlation }),
|
|
3821
4262
|
stateful,
|
|
4263
|
+
sourcePreparationOwner:
|
|
4264
|
+
domain === 'application' &&
|
|
4265
|
+
this.requestSourceAcceptance !== undefined &&
|
|
4266
|
+
(stateful.receiveKind === ReceiveKind.SpotSend ||
|
|
4267
|
+
stateful.receiveKind === ReceiveKind.SpotRequest ||
|
|
4268
|
+
stateful.receiveKind === ReceiveKind.ActorSend ||
|
|
4269
|
+
stateful.receiveKind === ReceiveKind.ActorRequest)
|
|
4270
|
+
? this
|
|
4271
|
+
: undefined,
|
|
3822
4272
|
applicationJob
|
|
3823
4273
|
});
|
|
3824
|
-
if (accepted)
|
|
4274
|
+
if (accepted) {
|
|
4275
|
+
ingress.onAdmitted?.();
|
|
4276
|
+
return domain;
|
|
4277
|
+
}
|
|
3825
4278
|
applicationJob.close();
|
|
3826
4279
|
if (stateful.reply !== undefined) {
|
|
3827
4280
|
const result = failure(
|
|
@@ -3957,10 +4410,11 @@ export class ServiceStatefulRuntime {
|
|
|
3957
4410
|
ingress: RawServiceIngressRecord,
|
|
3958
4411
|
binding: ServiceSessionBinding,
|
|
3959
4412
|
actorAuthority: ServiceBoundSessionActorAuthority,
|
|
3960
|
-
reply: NonNullable<ServiceStatefulMailboxData['reply']
|
|
4413
|
+
reply: NonNullable<ServiceStatefulMailboxData['reply']>,
|
|
4414
|
+
acceptedBinding: () => ServiceSessionBinding
|
|
3961
4415
|
): boolean {
|
|
3962
4416
|
const actor = binding.actor;
|
|
3963
|
-
|
|
4417
|
+
requireSessionOwnerIdentity(binding);
|
|
3964
4418
|
const header = Buffer.from([
|
|
3965
4419
|
SERVICE_WIRE_MAGIC[0],
|
|
3966
4420
|
SERVICE_WIRE_MAGIC[1],
|
|
@@ -3985,12 +4439,27 @@ export class ServiceStatefulRuntime {
|
|
|
3985
4439
|
actorNodeGeneration: actorAuthority.targetNodeGeneration,
|
|
3986
4440
|
authorityOwnerGeneration: actorAuthority.authorityOwnerGeneration,
|
|
3987
4441
|
ownerLeaseGeneration: actorAuthority.ownerLeaseGeneration,
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
4442
|
+
// The Host reads only Session identity fields before `reply` installs the
|
|
4443
|
+
// binding; installation keeps that identity. Generation and owner fields
|
|
4444
|
+
// are read after `reply`, so they always come from the accepted binding.
|
|
4445
|
+
get bindingGeneration() {
|
|
4446
|
+
return acceptedBinding().bindingGeneration;
|
|
4447
|
+
},
|
|
4448
|
+
get sessionNodeRid() {
|
|
4449
|
+
return acceptedBinding().sessionOwnerNodeRid as never;
|
|
4450
|
+
},
|
|
4451
|
+
get sessionRid() {
|
|
4452
|
+
return acceptedBinding().sessionRid as never;
|
|
4453
|
+
},
|
|
4454
|
+
get sessionOwnerNodeGeneration() {
|
|
4455
|
+
return acceptedBinding().sessionOwnerNodeGeneration!;
|
|
4456
|
+
},
|
|
4457
|
+
get sessionOwnerId() {
|
|
4458
|
+
return acceptedBinding().sessionOwnerId!;
|
|
4459
|
+
},
|
|
4460
|
+
get sessionOwnerLeaseGeneration() {
|
|
4461
|
+
return acceptedBinding().sessionOwnerLeaseGeneration!;
|
|
4462
|
+
}
|
|
3994
4463
|
},
|
|
3995
4464
|
reply
|
|
3996
4465
|
} satisfies ServiceStatefulMailboxData,
|
|
@@ -4212,64 +4681,15 @@ export class ServiceStatefulRuntime {
|
|
|
4212
4681
|
operationKind: 'userSpotCreate' | 'userSpotClose' | 'actorCreate' | 'streamBind' | 'actorJoin',
|
|
4213
4682
|
deadlineMs: number
|
|
4214
4683
|
): Promise<readonly Buffer[]> {
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
true
|
|
4223
|
-
)
|
|
4224
|
-
);
|
|
4225
|
-
}
|
|
4226
|
-
});
|
|
4227
|
-
let wasAdmitted = false;
|
|
4228
|
-
try {
|
|
4229
|
-
// Only durable lifecycle operations replay these frozen frames. Received
|
|
4230
|
-
// envelopes leave this loop before decoding, including failure terminals.
|
|
4231
|
-
for (;;) {
|
|
4232
|
-
this.requireOpen();
|
|
4233
|
-
stop.signal.throwIfAborted();
|
|
4234
|
-
const remainingMs = Math.ceil(deadlineMs - performance.now());
|
|
4235
|
-
if (remainingMs <= 0) throw durableOperationExhausted(operationKind, wasAdmitted);
|
|
4236
|
-
try {
|
|
4237
|
-
return await awaitWithAbort(
|
|
4238
|
-
targetNodeRid === this.nodeRid
|
|
4239
|
-
? this.requestLocalInfrastructure(parts[0]!, correlation, remainingMs)
|
|
4240
|
-
: this.raw.requestService(targetNodeRid, parts, remainingMs),
|
|
4241
|
-
stop.signal
|
|
4242
|
-
);
|
|
4243
|
-
} catch (error) {
|
|
4244
|
-
stop.signal.throwIfAborted();
|
|
4245
|
-
if (!durableRequestCanReplay(error)) throw error;
|
|
4246
|
-
if (durableRequestWasAdmitted(error)) wasAdmitted = true;
|
|
4247
|
-
const retryDelayMs = Math.min(
|
|
4248
|
-
STATEFUL_OPERATION_RETRY_TICK_MS,
|
|
4249
|
-
deadlineMs - performance.now()
|
|
4250
|
-
);
|
|
4251
|
-
if (retryDelayMs <= 0) {
|
|
4252
|
-
throw durableOperationExhausted(operationKind, wasAdmitted, error);
|
|
4253
|
-
}
|
|
4254
|
-
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
4255
|
-
try {
|
|
4256
|
-
await awaitWithAbort(
|
|
4257
|
-
new Promise<void>((resolve) => {
|
|
4258
|
-
timer = setTimeout(resolve, retryDelayMs);
|
|
4259
|
-
}),
|
|
4260
|
-
stop.signal
|
|
4261
|
-
);
|
|
4262
|
-
} finally {
|
|
4263
|
-
if (timer !== undefined) clearTimeout(timer);
|
|
4264
|
-
}
|
|
4265
|
-
}
|
|
4266
|
-
}
|
|
4267
|
-
} catch (error) {
|
|
4268
|
-
stop.signal.throwIfAborted();
|
|
4269
|
-
throw error;
|
|
4270
|
-
} finally {
|
|
4271
|
-
unobserve();
|
|
4684
|
+
this.requireOpen();
|
|
4685
|
+
if (targetNodeRid === this.nodeRid) {
|
|
4686
|
+
return this.requestLocalInfrastructure(
|
|
4687
|
+
parts[0]!,
|
|
4688
|
+
correlation,
|
|
4689
|
+
Math.ceil(deadlineMs - performance.now())
|
|
4690
|
+
);
|
|
4272
4691
|
}
|
|
4692
|
+
return requestDurableService(this.raw, targetNodeRid, parts, deadlineMs, operationKind);
|
|
4273
4693
|
}
|
|
4274
4694
|
|
|
4275
4695
|
private requestLocalInfrastructure(
|
|
@@ -4393,7 +4813,8 @@ export class ServiceStatefulRuntime {
|
|
|
4393
4813
|
| 'streamBind'
|
|
4394
4814
|
| 'streamUnbind'
|
|
4395
4815
|
| 'instanceSpotRequest',
|
|
4396
|
-
actor?: ServiceActorRef
|
|
4816
|
+
actor?: ServiceActorRef,
|
|
4817
|
+
onAdmitted?: () => void
|
|
4397
4818
|
): void {
|
|
4398
4819
|
if (targetNodeRid === this.nodeRid) {
|
|
4399
4820
|
const deadlineUnixMs = BigInt(Date.now() + Math.max(0, timeoutMs));
|
|
@@ -4411,6 +4832,7 @@ export class ServiceStatefulRuntime {
|
|
|
4411
4832
|
flags: parts[0]![SERVICE_WIRE_FLAGS_OFFSET]!,
|
|
4412
4833
|
sourceRoutingId: this.nodeRid,
|
|
4413
4834
|
requestSequence: pending.id,
|
|
4835
|
+
onAdmitted,
|
|
4414
4836
|
parts,
|
|
4415
4837
|
applicationJobOwner
|
|
4416
4838
|
};
|
|
@@ -4429,7 +4851,7 @@ export class ServiceStatefulRuntime {
|
|
|
4429
4851
|
})().catch((error) => this.operations.reply(pending.id, failure(error)));
|
|
4430
4852
|
return;
|
|
4431
4853
|
}
|
|
4432
|
-
void this.raw.requestService(targetNodeRid, parts, timeoutMs).then(
|
|
4854
|
+
void this.raw.requestService(targetNodeRid, parts, timeoutMs, onAdmitted).then(
|
|
4433
4855
|
(reply) => this.completeRemoteReply(pending, targetNodeRid, operationKind, actor, reply),
|
|
4434
4856
|
(error) => this.operations.fail(pending.id, error)
|
|
4435
4857
|
);
|
|
@@ -4611,6 +5033,15 @@ export class ServiceStatefulRuntime {
|
|
|
4611
5033
|
operationKind: OperationKind.ActorRequest,
|
|
4612
5034
|
correlation: decoded.correlation,
|
|
4613
5035
|
targetActor: current.ref,
|
|
5036
|
+
messageFollowOrigin: {
|
|
5037
|
+
get requestSource() {
|
|
5038
|
+
return ingress.requestSource;
|
|
5039
|
+
},
|
|
5040
|
+
target: decoded.target,
|
|
5041
|
+
sourceNodeRid: ingress.sourceRoutingId,
|
|
5042
|
+
originalOperation: decoded.operation,
|
|
5043
|
+
originalReplyRouteId: decoded.correlation ?? 0n
|
|
5044
|
+
},
|
|
4614
5045
|
...(decoded.sourceActor === undefined
|
|
4615
5046
|
? {}
|
|
4616
5047
|
: { sourceActor: { ...decoded.sourceActor, nodeRid: ingress.sourceRoutingId } }),
|
|
@@ -4706,13 +5137,17 @@ export class ServiceStatefulRuntime {
|
|
|
4706
5137
|
try {
|
|
4707
5138
|
const current = this.validateActorFence(decoded.actor);
|
|
4708
5139
|
if (decoded.binding.state === 'active') {
|
|
5140
|
+
const sessionOwner = await this.sessionOwnerOf(this.nodeRid, this.nodeGeneration);
|
|
4709
5141
|
this.registry.installSessionBinding(
|
|
4710
5142
|
sessionBindingFromWire(
|
|
4711
5143
|
current.ref,
|
|
4712
5144
|
decoded.sessionRid,
|
|
4713
|
-
|
|
5145
|
+
sessionOwner.nodeRid,
|
|
4714
5146
|
decoded.binding.generation,
|
|
4715
|
-
current.membershipEpoch
|
|
5147
|
+
current.membershipEpoch,
|
|
5148
|
+
sessionOwner.nodeGeneration,
|
|
5149
|
+
sessionOwner.ownerId,
|
|
5150
|
+
sessionOwner.leaseGeneration
|
|
4716
5151
|
)
|
|
4717
5152
|
);
|
|
4718
5153
|
} else {
|
|
@@ -4761,6 +5196,7 @@ export class ServiceStatefulRuntime {
|
|
|
4761
5196
|
| 'actorJoin'
|
|
4762
5197
|
| 'streamBind'
|
|
4763
5198
|
| 'streamUnbind'
|
|
5199
|
+
| 'boundSessionClose'
|
|
4764
5200
|
| 'instanceSpotRequest',
|
|
4765
5201
|
actor: ServiceActorRef | undefined,
|
|
4766
5202
|
reply: readonly Buffer[]
|
|
@@ -5387,6 +5823,25 @@ function isEntrySpotFence(fence: ServiceSpotRouteFence): boolean {
|
|
|
5387
5823
|
);
|
|
5388
5824
|
}
|
|
5389
5825
|
|
|
5826
|
+
function instanceActivationOutcome(error: unknown): string {
|
|
5827
|
+
if (isAbortError(error)) return 'shutdown';
|
|
5828
|
+
if (error instanceof ZLinkFrameworkException) {
|
|
5829
|
+
switch (error.kind) {
|
|
5830
|
+
case ZLinkFrameworkErrorKind.DeadlineExceeded:
|
|
5831
|
+
return 'timed_out';
|
|
5832
|
+
case ZLinkFrameworkErrorKind.ShuttingDown:
|
|
5833
|
+
return 'shutdown';
|
|
5834
|
+
case ZLinkFrameworkErrorKind.AlreadyExists:
|
|
5835
|
+
return 'conflict';
|
|
5836
|
+
case ZLinkFrameworkErrorKind.InvalidOperation:
|
|
5837
|
+
return 'fenced';
|
|
5838
|
+
case ZLinkFrameworkErrorKind.Unavailable:
|
|
5839
|
+
return 'store_failure';
|
|
5840
|
+
}
|
|
5841
|
+
}
|
|
5842
|
+
return 'rejected';
|
|
5843
|
+
}
|
|
5844
|
+
|
|
5390
5845
|
function operationDeadline(deadlineUnixMs: bigint): {
|
|
5391
5846
|
readonly signal: AbortSignal;
|
|
5392
5847
|
close(): void;
|
|
@@ -5414,37 +5869,6 @@ function operationReplayExpired(replayExpiresAtMs: number): boolean {
|
|
|
5414
5869
|
return performance.now() > replayExpiresAtMs;
|
|
5415
5870
|
}
|
|
5416
5871
|
|
|
5417
|
-
function durableRequestWasAdmitted(error: unknown): boolean {
|
|
5418
|
-
// Request NotConnected includes Core handover of an admitted request. The
|
|
5419
|
-
// submit/request phase, rather than the result number, owns this distinction.
|
|
5420
|
-
return isZLinkBackendResultError(error) && error.operation === 'request';
|
|
5421
|
-
}
|
|
5422
|
-
|
|
5423
|
-
function durableOperationExhausted(
|
|
5424
|
-
operationKind: 'userSpotCreate' | 'userSpotClose' | 'actorCreate' | 'streamBind' | 'actorJoin',
|
|
5425
|
-
wasAdmitted: boolean,
|
|
5426
|
-
cause?: unknown
|
|
5427
|
-
): ZLinkFrameworkException {
|
|
5428
|
-
return createInternalFrameworkException(
|
|
5429
|
-
wasAdmitted
|
|
5430
|
-
? ZLinkFrameworkInternalErrorKind.DeadlineExceeded
|
|
5431
|
-
: ZLinkFrameworkInternalErrorKind.RouteNotConnected,
|
|
5432
|
-
`${operationKind} exhausted its end-to-end deadline.`,
|
|
5433
|
-
true,
|
|
5434
|
-
cause
|
|
5435
|
-
);
|
|
5436
|
-
}
|
|
5437
|
-
|
|
5438
|
-
function durableRequestCanReplay(error: unknown): boolean {
|
|
5439
|
-
if (!isZLinkBackendResultError(error)) return false;
|
|
5440
|
-
return error.operation === 'request'
|
|
5441
|
-
? error.result === RequestResult.NotConnected || error.result === RequestResult.TimedOut
|
|
5442
|
-
: error.result === SubmitResult.NotConnected ||
|
|
5443
|
-
error.result === SubmitResult.NotFound ||
|
|
5444
|
-
error.result === SubmitResult.NotAdmitted ||
|
|
5445
|
-
error.result === SubmitResult.Backpressured;
|
|
5446
|
-
}
|
|
5447
|
-
|
|
5448
5872
|
function remainingDeadlineMs(deadlineUnixMs: bigint): number {
|
|
5449
5873
|
const remainingMs = deadlineUnixMs - BigInt(Date.now());
|
|
5450
5874
|
if (remainingMs <= 0n) {
|
|
@@ -5482,6 +5906,13 @@ function actorLocation(actor: ServiceActorState): ActorLocation {
|
|
|
5482
5906
|
};
|
|
5483
5907
|
}
|
|
5484
5908
|
|
|
5909
|
+
function requestSourceUnavailable(nodeRid: string): ZLinkFrameworkException {
|
|
5910
|
+
return new ZLinkFrameworkException(
|
|
5911
|
+
ZLinkFrameworkErrorKind.Unavailable,
|
|
5912
|
+
`Request source '${nodeRid}' has no live MeshNode descriptor.`
|
|
5913
|
+
);
|
|
5914
|
+
}
|
|
5915
|
+
|
|
5485
5916
|
function activationRefused(): ZLinkFrameworkException {
|
|
5486
5917
|
return new ZLinkFrameworkException(
|
|
5487
5918
|
ZLinkFrameworkErrorKind.Unavailable,
|
|
@@ -5499,14 +5930,14 @@ function failure(error: unknown): ServiceStatefulResult {
|
|
|
5499
5930
|
return internalFrameworkWireReply(ZLinkFrameworkInternalErrorKind.RequestFailed);
|
|
5500
5931
|
}
|
|
5501
5932
|
|
|
5502
|
-
function actorKey(actor: ServiceActorRef): string {
|
|
5933
|
+
function actorKey(actor: Pick<ServiceActorRef, 'actorId' | 'generation'>): string {
|
|
5503
5934
|
return `${actor.actorId}\0${actor.generation}`;
|
|
5504
5935
|
}
|
|
5505
5936
|
|
|
5506
5937
|
function sameSessionBinding(left: ServiceSessionBinding, right: ServiceSessionBinding): boolean {
|
|
5507
5938
|
return (
|
|
5508
5939
|
sameActorRef(left.actor, right.actor) &&
|
|
5509
|
-
left.sessionRid
|
|
5940
|
+
routingIdsEqual(left.sessionRid, right.sessionRid) &&
|
|
5510
5941
|
sameSessionOwnerIdentity(left, right) &&
|
|
5511
5942
|
left.bindingGeneration === right.bindingGeneration
|
|
5512
5943
|
);
|
|
@@ -5515,7 +5946,7 @@ function sameSessionBinding(left: ServiceSessionBinding, right: ServiceSessionBi
|
|
|
5515
5946
|
function sameSessionIdentity(left: ServiceSessionBinding, right: ServiceSessionBinding): boolean {
|
|
5516
5947
|
return (
|
|
5517
5948
|
sameActorRef(left.actor, right.actor) &&
|
|
5518
|
-
left.sessionRid
|
|
5949
|
+
routingIdsEqual(left.sessionRid, right.sessionRid) &&
|
|
5519
5950
|
sameSessionOwnerIdentity(left, right)
|
|
5520
5951
|
);
|
|
5521
5952
|
}
|
|
@@ -5538,9 +5969,7 @@ function sameSessionOwnerIdentity(
|
|
|
5538
5969
|
): boolean {
|
|
5539
5970
|
return (
|
|
5540
5971
|
left.sessionOwnerNodeRid === right.sessionOwnerNodeRid &&
|
|
5541
|
-
left.sessionOwnerNodeGeneration === right.sessionOwnerNodeGeneration
|
|
5542
|
-
left.sessionOwnerId === right.sessionOwnerId &&
|
|
5543
|
-
left.sessionOwnerLeaseGeneration === right.sessionOwnerLeaseGeneration
|
|
5972
|
+
left.sessionOwnerNodeGeneration === right.sessionOwnerNodeGeneration
|
|
5544
5973
|
);
|
|
5545
5974
|
}
|
|
5546
5975
|
|
|
@@ -5578,7 +6007,7 @@ function sessionDeliveryKey(
|
|
|
5578
6007
|
>
|
|
5579
6008
|
): string {
|
|
5580
6009
|
return (
|
|
5581
|
-
`${actorKey(binding.actor)}\0${binding.sessionOwnerNodeRid}\0${binding.sessionRid}` +
|
|
6010
|
+
`${actorKey(binding.actor)}\0${binding.sessionOwnerNodeRid}\0${encodeRoutingIdStorageHex(binding.sessionRid)}` +
|
|
5582
6011
|
`\0${binding.sessionOwnerNodeGeneration ?? ''}` +
|
|
5583
6012
|
`\0${binding.sessionOwnerId ?? ''}` +
|
|
5584
6013
|
`\0${binding.sessionOwnerLeaseGeneration ?? ''}` +
|
|
@@ -5591,8 +6020,8 @@ function exactRetiredDelivery(
|
|
|
5591
6020
|
retired: ServiceRetiredBoundSessionRouteFence
|
|
5592
6021
|
): boolean {
|
|
5593
6022
|
return (
|
|
5594
|
-
binding.sessionRid
|
|
5595
|
-
binding.sessionOwnerNodeRid
|
|
6023
|
+
routingIdsEqual(binding.sessionRid, retired.sessionRid) &&
|
|
6024
|
+
routingIdsEqual(binding.sessionOwnerNodeRid, retired.sessionOwnerNodeRid) &&
|
|
5596
6025
|
binding.sessionOwnerNodeGeneration === retired.sessionOwnerNodeGeneration &&
|
|
5597
6026
|
binding.sessionOwnerId === retired.sessionOwnerId &&
|
|
5598
6027
|
binding.sessionOwnerLeaseGeneration === retired.sessionOwnerLeaseGeneration &&
|
|
@@ -5611,7 +6040,7 @@ function replacementNoticeKey(
|
|
|
5611
6040
|
`\0${record.retiredSession.sessionOwnerNodeGeneration}` +
|
|
5612
6041
|
`\0${record.retiredSession.sessionOwnerId}` +
|
|
5613
6042
|
`\0${record.retiredSession.sessionOwnerLeaseGeneration}` +
|
|
5614
|
-
`\0${record.retiredSession.sessionRid}` +
|
|
6043
|
+
`\0${encodeRoutingIdStorageHex(record.retiredSession.sessionRid)}` +
|
|
5615
6044
|
`\0${record.retiredSession.retiredBindingGeneration}`
|
|
5616
6045
|
);
|
|
5617
6046
|
}
|
|
@@ -5937,3 +6366,31 @@ export function statefulMailboxData(
|
|
|
5937
6366
|
): ServiceStatefulMailboxData | undefined {
|
|
5938
6367
|
return record.stateful as ServiceStatefulMailboxData | undefined;
|
|
5939
6368
|
}
|
|
6369
|
+
|
|
6370
|
+
function acceptsStatefulIngress(command: number): boolean {
|
|
6371
|
+
// These frozen relocation controls overlap the legacy stateful command
|
|
6372
|
+
// number range (30-34). They are bare infrastructure records and must
|
|
6373
|
+
// fall through to RawServiceMeshRuntime's relocation ingress instead of
|
|
6374
|
+
// being decoded as stateful actor/spot work.
|
|
6375
|
+
if (
|
|
6376
|
+
command === M6bServiceWireCommand.relocationReady ||
|
|
6377
|
+
command === M6bServiceWireCommand.relocationData ||
|
|
6378
|
+
command === M6bServiceWireCommand.replyRelay ||
|
|
6379
|
+
command === M6bServiceWireCommand.relocationCutover
|
|
6380
|
+
) {
|
|
6381
|
+
return false;
|
|
6382
|
+
}
|
|
6383
|
+
if (
|
|
6384
|
+
command < M6bServiceWireCommand.spotSend ||
|
|
6385
|
+
(command > M6bServiceWireCommand.instanceSpot &&
|
|
6386
|
+
command !== M6bServiceWireCommand.userSpotCreate &&
|
|
6387
|
+
command !== M6bServiceWireCommand.userSpotClose &&
|
|
6388
|
+
command !== M6bServiceWireCommand.actorCreate &&
|
|
6389
|
+
command !== M6bServiceWireCommand.messageFollow &&
|
|
6390
|
+
command !== M6bServiceWireCommand.boundSessionReplaced &&
|
|
6391
|
+
command !== M6bServiceWireCommand.boundSessionClose)
|
|
6392
|
+
) {
|
|
6393
|
+
return false;
|
|
6394
|
+
}
|
|
6395
|
+
return true;
|
|
6396
|
+
}
|