@zlink-systems/framework 0.30.1 → 0.30.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/contracts/Common/ZLinkEncodedPayload.d.ts +6 -0
- package/dist/contracts/Common/ZLinkEncodedPayload.js +18 -3
- package/dist/contracts/Common/encoded-payload-storage.d.ts +1 -1
- package/dist/contracts/Common/encoded-payload-storage.js +5 -2
- package/dist/contracts/Configuration/Builders.d.ts +1 -1
- package/dist/contracts/Configuration/RegistrationBuilderPolicy.d.ts +2 -1
- package/dist/contracts/Configuration/RegistrationBuilderPolicy.js +5 -1
- package/dist/contracts/Configuration/RegistrationBuilders.js +2 -2
- package/dist/contracts/Configuration/RegistrationTypes.d.ts +1 -0
- package/dist/contracts/Dispatch/ZLinkDispatchOptions.d.ts +2 -2
- package/dist/contracts/Locations/Rows.d.ts +2 -0
- package/dist/contracts/Spots/Contracts.d.ts +4 -0
- package/dist/contracts/Spots/ZLinkSpot.d.ts +3 -3
- package/dist/runtime/abort.d.ts +5 -0
- package/dist/runtime/abort.js +10 -0
- package/dist/runtime/actors/actor-authority-publication.js +3 -2
- package/dist/runtime/actors/actor-client.d.ts +0 -1
- package/dist/runtime/actors/actor-client.js +38 -45
- package/dist/runtime/actors/actor-context.d.ts +2 -0
- package/dist/runtime/actors/actor-context.js +46 -9
- package/dist/runtime/actors/actor-handoff.d.ts +7 -3
- package/dist/runtime/actors/actor-handoff.js +212 -88
- package/dist/runtime/actors/actor-join-relocation.d.ts +2 -0
- package/dist/runtime/actors/actor-local-native-join.js +4 -0
- package/dist/runtime/actors/actor-mailbox.js +27 -1
- package/dist/runtime/actors/actor-remote-joiner.js +3 -1
- package/dist/runtime/actors/actor-reply.d.ts +4 -0
- package/dist/runtime/actors/actor-reply.js +29 -0
- package/dist/runtime/actors/actor-runtime-contracts.d.ts +2 -0
- package/dist/runtime/actors/actor-runtime-state.js +2 -20
- package/dist/runtime/actors/actor-source-transfer.d.ts +1 -17
- package/dist/runtime/actors/index.d.ts +6 -2
- package/dist/runtime/actors/index.js +37 -21
- package/dist/runtime/actors/session-disconnected-wire.d.ts +8 -0
- package/dist/runtime/actors/session-disconnected-wire.js +48 -0
- package/dist/runtime/admission.d.ts +9 -1
- package/dist/runtime/admission.js +55 -32
- package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +13 -0
- package/dist/runtime/application-jobs/application-job-queue-scope.js +27 -1
- package/dist/runtime/application-jobs/contracts.d.ts +2 -0
- package/dist/runtime/backend/contracts/index.d.ts +19 -3
- package/dist/runtime/backend/mesh-actor-session-node-adapter.js +2 -2
- package/dist/runtime/backend/mesh-completion-table.js +5 -0
- package/dist/runtime/backend/mesh-dispatch-pump.d.ts +26 -2
- package/dist/runtime/backend/mesh-dispatch-pump.js +193 -107
- package/dist/runtime/backend/node/node-raw-binding-port.js +90 -58
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +33 -8
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +142 -143
- package/dist/runtime/backend/node/node-socket-backend-adapter.d.ts +1 -1
- package/dist/runtime/backend/node/node-socket-backend-adapter.js +5 -5
- package/dist/runtime/channels/channel-clients.d.ts +1 -0
- package/dist/runtime/channels/channel-clients.js +20 -5
- package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +22 -3
- package/dist/runtime/channels/channel-dispatch-pipeline.js +103 -57
- package/dist/runtime/channels/channel-dispatchers.d.ts +3 -2
- package/dist/runtime/channels/channel-dispatchers.js +44 -37
- package/dist/runtime/channels/channel-envelope.d.ts +1 -0
- package/dist/runtime/channels/channel-envelope.js +74 -31
- package/dist/runtime/channels/channel-runtime-lifecycle.d.ts +4 -3
- package/dist/runtime/channels/channel-runtime-lifecycle.js +19 -24
- package/dist/runtime/channels/channel-runtime-manager.d.ts +3 -2
- package/dist/runtime/channels/channel-runtime-manager.js +4 -4
- package/dist/runtime/channels/channel-transports.d.ts +0 -1
- package/dist/runtime/channels/channel-transports.js +35 -33
- package/dist/runtime/channels/client-server-location-runtime.js +5 -4
- package/dist/runtime/channels/dispatch-error-reporter.d.ts +2 -1
- package/dist/runtime/channels/dispatch-error-reporter.js +2 -1
- package/dist/runtime/channels/fanout-location-runtime.d.ts +1 -1
- package/dist/runtime/channels/fanout-location-runtime.js +3 -3
- package/dist/runtime/diagnostics/flow-context.js +19 -7
- package/dist/runtime/diagnostics/index.js +4 -9
- package/dist/runtime/diagnostics/message-flow.d.ts +8 -0
- package/dist/runtime/diagnostics/message-flow.js +35 -5
- package/dist/runtime/diagnostics/runtime-metrics.d.ts +2 -2
- package/dist/runtime/diagnostics/runtime-metrics.js +10 -8
- package/dist/runtime/execution/index.d.ts +4 -2
- package/dist/runtime/execution/index.js +33 -10
- package/dist/runtime/execution/serial-execution-queue.d.ts +4 -0
- package/dist/runtime/execution/serial-execution-queue.js +21 -2
- package/dist/runtime/execution/state-lane.d.ts +2 -0
- package/dist/runtime/execution/state-lane.js +5 -0
- package/dist/runtime/foundation/operation-registry.d.ts +25 -1
- package/dist/runtime/foundation/operation-registry.js +86 -18
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +39 -3
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +151 -55
- package/dist/runtime/foundation/service-liveness-registry.d.ts +12 -1
- package/dist/runtime/foundation/service-liveness-registry.js +38 -0
- package/dist/runtime/foundation/service-mailbox.d.ts +7 -0
- package/dist/runtime/foundation/service-maintenance-runtime.js +1 -6
- package/dist/runtime/foundation/service-relocation-object-owner.d.ts +2 -3
- package/dist/runtime/foundation/service-relocation-object-owner.js +48 -35
- package/dist/runtime/foundation/service-relocation-runtime.js +7 -3
- package/dist/runtime/foundation/service-runtime-contracts.d.ts +8 -1
- package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +2 -1
- package/dist/runtime/foundation/service-stateful-registry.d.ts +5 -4
- package/dist/runtime/foundation/service-stateful-registry.js +24 -26
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +72 -6
- package/dist/runtime/foundation/service-stateful-runtime.js +444 -117
- package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +14 -1
- package/dist/runtime/foundation/service-stateful-wire-codec.js +48 -27
- package/dist/runtime/foundation/service-wire-binary-primitives.d.ts +6 -1
- package/dist/runtime/foundation/service-wire-binary-primitives.js +27 -11
- package/dist/runtime/foundation/service-wire-constants.generated.d.ts +1 -0
- package/dist/runtime/foundation/service-wire-constants.generated.js +1 -0
- package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +10 -0
- package/dist/runtime/foundation/service-wire-m6a-codec.js +166 -53
- package/dist/runtime/host/actor-packet-relay.d.ts +6 -4
- package/dist/runtime/host/actor-packet-relay.js +59 -72
- package/dist/runtime/host/actor-placement-coordinator.js +25 -8
- package/dist/runtime/host/actor-transfer-runtime.d.ts +7 -31
- package/dist/runtime/host/actor-transfer-runtime.js +85 -331
- package/dist/runtime/host/application-job-queue.d.ts +24 -3
- package/dist/runtime/host/application-job-queue.js +28 -41
- package/dist/runtime/host/index.d.ts +5 -1
- package/dist/runtime/host/index.js +171 -97
- package/dist/runtime/host/location-runtime-owner.js +2 -6
- package/dist/runtime/host/mesh-router-resolver.d.ts +4 -0
- package/dist/runtime/host/mesh-router-resolver.js +14 -6
- package/dist/runtime/host/remote-bound-session-relay.d.ts +2 -4
- package/dist/runtime/host/remote-bound-session-relay.js +71 -190
- package/dist/runtime/host/route-mesh-runtime.d.ts +2 -2
- package/dist/runtime/host/route-mesh-runtime.js +5 -3
- package/dist/runtime/host/runtime-shutdown.d.ts +1 -0
- package/dist/runtime/host/runtime-shutdown.js +3 -2
- package/dist/runtime/host/service-relocation-host-runtime.d.ts +17 -7
- package/dist/runtime/host/service-relocation-host-runtime.js +550 -367
- package/dist/runtime/host/spot-runtime-options-factory.js +15 -7
- package/dist/runtime/host/user-spot-creation-coordinator.js +33 -84
- package/dist/runtime/locations/auto-connect-loop.d.ts +2 -0
- package/dist/runtime/locations/auto-connect-loop.js +22 -14
- package/dist/runtime/locations/auto-connect-reconciler.js +0 -6
- package/dist/runtime/locations/auto-connect-types.d.ts +0 -1
- package/dist/runtime/locations/descriptor-publication.d.ts +4 -0
- package/dist/runtime/locations/descriptor-publication.js +11 -0
- package/dist/runtime/locations/domain-store-contract.d.ts +1 -0
- package/dist/runtime/locations/in-memory-authority-store.d.ts +7 -1
- package/dist/runtime/locations/in-memory-authority-store.js +56 -19
- package/dist/runtime/locations/in-memory-location-store.js +1 -10
- package/dist/runtime/locations/index.d.ts +1 -0
- package/dist/runtime/locations/index.js +1 -0
- package/dist/runtime/locations/lease-tracker.js +1 -6
- package/dist/runtime/locations/location-store-repository.d.ts +51 -1
- package/dist/runtime/locations/location-store-repository.js +363 -139
- package/dist/runtime/locations/relocation-blob.d.ts +18 -1
- package/dist/runtime/locations/relocation-blob.js +52 -1
- package/dist/runtime/locations/request-source-resolver.d.ts +37 -0
- package/dist/runtime/locations/request-source-resolver.js +80 -0
- package/dist/runtime/locations/resolvers.js +1 -1
- package/dist/runtime/locations/runtime.d.ts +6 -2
- package/dist/runtime/locations/runtime.js +37 -30
- package/dist/runtime/messaging/payload-codec.d.ts +7 -1
- package/dist/runtime/messaging/payload-codec.js +7 -6
- package/dist/runtime/messaging/request-result.js +29 -26
- package/dist/runtime/protocol/service_wire_codec.generated.d.ts +19 -1
- package/dist/runtime/protocol/service_wire_codec.generated.js +85 -0
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.d.ts +7 -1
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +23 -7
- package/dist/runtime/spots/formal-remote-actor-admission-registry.js +3 -2
- package/dist/runtime/spots/index.d.ts +18 -11
- package/dist/runtime/spots/index.js +234 -176
- package/dist/runtime/spots/spot-activation-registry.d.ts +4 -2
- package/dist/runtime/spots/spot-activation-registry.js +21 -11
- package/dist/runtime/spots/spot-activation-state.d.ts +1 -1
- package/dist/runtime/spots/spot-activation-state.js +7 -7
- package/dist/runtime/spots/spot-activation.d.ts +6 -3
- package/dist/runtime/spots/spot-activation.js +123 -44
- package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +1 -0
- package/dist/runtime/spots/spot-actor-admission-coordinator.js +13 -22
- package/dist/runtime/spots/spot-actor-membership.d.ts +0 -1
- package/dist/runtime/spots/spot-actor-membership.js +0 -8
- package/dist/runtime/spots/spot-actor-packet-dispatch.js +33 -8
- package/dist/runtime/spots/spot-actor-packet-drain.js +5 -33
- package/dist/runtime/spots/spot-entry-activation.d.ts +2 -0
- package/dist/runtime/spots/spot-entry-activation.js +14 -9
- package/dist/runtime/spots/spot-handler-registry.d.ts +4 -3
- package/dist/runtime/spots/spot-handler-registry.js +6 -2
- package/dist/runtime/spots/spot-manager-internal-contracts.d.ts +2 -2
- package/dist/runtime/spots/spot-manager-public.js +2 -2
- package/dist/runtime/spots/spot-node-autoconnect.d.ts +2 -1
- package/dist/runtime/spots/spot-node-autoconnect.js +18 -39
- package/dist/runtime/spots/spot-node-runtime-manager.d.ts +19 -2
- package/dist/runtime/spots/spot-node-runtime-manager.js +115 -50
- package/dist/runtime/spots/spot-outbound.js +3 -3
- package/dist/runtime/spots/spot-remote-codec.js +5 -1
- package/dist/runtime/spots/spot-runtime-ports.d.ts +0 -1
- package/dist/runtime/spots/spot-serial-executor.d.ts +10 -0
- package/dist/runtime/spots/spot-serial-executor.js +61 -8
- package/dist/runtime/spots/spot-serial-turn-executor.js +19 -2
- package/dist/runtime/spots/spot-subscription-dispatch.js +1 -1
- package/dist/runtime/spots/spot-timer.d.ts +57 -5
- package/dist/runtime/spots/spot-timer.js +287 -103
- package/dist/runtime/streams/actor-session-binding-registry.d.ts +42 -11
- package/dist/runtime/streams/actor-session-binding-registry.js +165 -48
- package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +8 -4
- package/dist/runtime/streams/bound-actor-relay-sender.d.ts +2 -1
- package/dist/runtime/streams/bound-actor-relay-sender.js +15 -8
- package/dist/runtime/streams/bound-session-service.d.ts +1 -1
- package/dist/runtime/streams/bound-session-service.js +6 -11
- package/dist/runtime/streams/index.d.ts +3 -2
- package/dist/runtime/streams/index.js +6 -4
- package/dist/runtime/streams/managed-stream.js +3 -2
- package/dist/runtime/streams/native-fallback-bound-session.js +8 -73
- package/dist/runtime/streams/session-actor-coordinator.d.ts +2 -8
- package/dist/runtime/streams/session-actor-coordinator.js +15 -32
- package/dist/runtime/streams/stream-binding-runtime-ports.d.ts +3 -10
- package/dist/runtime/streams/stream-session-runtime.js +1 -4
- package/dist/runtime/workers/index.js +1 -1
- package/package.json +3 -3
- package/src/contracts/Common/ZLinkEncodedPayload.ts +24 -4
- package/src/contracts/Common/encoded-payload-storage.ts +6 -3
- package/src/contracts/Configuration/Builders.ts +4 -1
- package/src/contracts/Configuration/RegistrationBuilderPolicy.ts +7 -2
- package/src/contracts/Configuration/RegistrationBuilders.ts +6 -2
- package/src/contracts/Configuration/RegistrationTypes.ts +1 -0
- package/src/contracts/Dispatch/ZLinkDispatchOptions.ts +2 -1
- package/src/contracts/Locations/Rows.ts +2 -0
- package/src/contracts/Spots/Contracts.ts +8 -0
- package/src/contracts/Spots/ZLinkSpot.ts +3 -3
- package/src/runtime/abort.ts +12 -0
- package/src/runtime/actors/actor-authority-publication.ts +3 -2
- package/src/runtime/actors/actor-client.ts +61 -70
- package/src/runtime/actors/actor-context.ts +49 -9
- package/src/runtime/actors/actor-handoff.ts +282 -106
- package/src/runtime/actors/actor-join-relocation.ts +2 -0
- package/src/runtime/actors/actor-local-native-join.ts +4 -0
- package/src/runtime/actors/actor-mailbox.ts +40 -2
- package/src/runtime/actors/actor-remote-joiner.ts +6 -1
- package/src/runtime/actors/actor-reply.ts +42 -0
- package/src/runtime/actors/actor-runtime-contracts.ts +2 -0
- package/src/runtime/actors/actor-runtime-state.ts +2 -20
- package/src/runtime/actors/actor-source-transfer.ts +1 -34
- package/src/runtime/actors/index.ts +41 -27
- package/src/runtime/actors/session-disconnected-wire.ts +66 -0
- package/src/runtime/admission.ts +69 -34
- package/src/runtime/application-jobs/application-job-queue-scope.ts +33 -1
- package/src/runtime/application-jobs/contracts.ts +2 -0
- package/src/runtime/backend/contracts/index.ts +39 -4
- package/src/runtime/backend/mesh-actor-session-node-adapter.ts +5 -3
- package/src/runtime/backend/mesh-completion-table.ts +4 -0
- package/src/runtime/backend/mesh-dispatch-pump.ts +223 -124
- package/src/runtime/backend/node/node-raw-binding-port.ts +106 -70
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +221 -170
- package/src/runtime/backend/node/node-socket-backend-adapter.ts +5 -5
- package/src/runtime/channels/channel-clients.ts +49 -24
- package/src/runtime/channels/channel-dispatch-pipeline.ts +173 -124
- package/src/runtime/channels/channel-dispatchers.ts +116 -72
- package/src/runtime/channels/channel-envelope.ts +105 -36
- package/src/runtime/channels/channel-runtime-lifecycle.ts +13 -13
- package/src/runtime/channels/channel-runtime-manager.ts +12 -5
- package/src/runtime/channels/channel-transports.ts +49 -58
- package/src/runtime/channels/client-server-location-runtime.ts +11 -10
- package/src/runtime/channels/dispatch-error-reporter.ts +4 -2
- package/src/runtime/channels/fanout-location-runtime.ts +17 -9
- package/src/runtime/diagnostics/flow-context.ts +20 -7
- package/src/runtime/diagnostics/index.ts +1 -7
- package/src/runtime/diagnostics/message-flow.ts +47 -9
- package/src/runtime/diagnostics/runtime-metrics.ts +12 -10
- package/src/runtime/execution/index.ts +40 -14
- package/src/runtime/execution/serial-execution-queue.ts +31 -4
- package/src/runtime/execution/state-lane.ts +7 -0
- package/src/runtime/foundation/operation-registry.ts +98 -20
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +244 -67
- package/src/runtime/foundation/service-liveness-registry.ts +39 -0
- package/src/runtime/foundation/service-mailbox.ts +8 -0
- package/src/runtime/foundation/service-maintenance-runtime.ts +1 -8
- package/src/runtime/foundation/service-relocation-object-owner.ts +54 -37
- package/src/runtime/foundation/service-relocation-runtime.ts +7 -3
- package/src/runtime/foundation/service-runtime-contracts.ts +12 -1
- package/src/runtime/foundation/service-session-binding-ingress-port.ts +2 -1
- package/src/runtime/foundation/service-stateful-registry.ts +38 -35
- package/src/runtime/foundation/service-stateful-runtime.ts +667 -152
- package/src/runtime/foundation/service-stateful-wire-codec.ts +49 -11
- package/src/runtime/foundation/service-wire-binary-primitives.ts +35 -12
- package/src/runtime/foundation/service-wire-constants.generated.ts +1 -0
- package/src/runtime/foundation/service-wire-m6a-codec.ts +187 -51
- package/src/runtime/host/actor-packet-relay.ts +113 -94
- package/src/runtime/host/actor-placement-coordinator.ts +31 -8
- package/src/runtime/host/actor-transfer-runtime.ts +104 -430
- package/src/runtime/host/application-job-queue.ts +29 -44
- package/src/runtime/host/index.ts +286 -129
- package/src/runtime/host/location-runtime-owner.ts +2 -6
- package/src/runtime/host/mesh-router-resolver.ts +13 -5
- package/src/runtime/host/remote-bound-session-relay.ts +86 -248
- package/src/runtime/host/route-mesh-runtime.ts +11 -5
- package/src/runtime/host/runtime-shutdown.ts +15 -4
- package/src/runtime/host/service-relocation-host-runtime.ts +784 -475
- package/src/runtime/host/spot-runtime-options-factory.ts +18 -7
- package/src/runtime/host/user-spot-creation-coordinator.ts +41 -100
- package/src/runtime/locations/auto-connect-loop.ts +22 -14
- package/src/runtime/locations/auto-connect-reconciler.ts +0 -6
- package/src/runtime/locations/auto-connect-types.ts +0 -1
- package/src/runtime/locations/descriptor-publication.ts +7 -0
- package/src/runtime/locations/domain-store-contract.ts +5 -0
- package/src/runtime/locations/in-memory-authority-store.ts +85 -23
- package/src/runtime/locations/in-memory-location-store.ts +1 -12
- package/src/runtime/locations/index.ts +1 -0
- package/src/runtime/locations/lease-tracker.ts +2 -9
- package/src/runtime/locations/location-store-repository.ts +628 -152
- package/src/runtime/locations/relocation-blob.ts +75 -1
- package/src/runtime/locations/request-source-resolver.ts +138 -0
- package/src/runtime/locations/resolvers.ts +2 -2
- package/src/runtime/locations/runtime.ts +70 -40
- package/src/runtime/messaging/payload-codec.ts +8 -11
- package/src/runtime/messaging/request-result.ts +33 -29
- package/src/runtime/protocol/service_wire_codec.generated.ts +87 -4
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +13 -10
- package/src/runtime/spots/formal-remote-actor-admission-registry.ts +3 -2
- package/src/runtime/spots/index.ts +375 -281
- package/src/runtime/spots/spot-activation-registry.ts +25 -13
- package/src/runtime/spots/spot-activation-state.ts +12 -19
- package/src/runtime/spots/spot-activation.ts +170 -86
- package/src/runtime/spots/spot-actor-admission-coordinator.ts +25 -55
- package/src/runtime/spots/spot-actor-membership.ts +0 -15
- package/src/runtime/spots/spot-actor-packet-dispatch.ts +56 -11
- package/src/runtime/spots/spot-actor-packet-drain.ts +9 -52
- package/src/runtime/spots/spot-entry-activation.ts +24 -19
- package/src/runtime/spots/spot-handler-registry.ts +15 -4
- package/src/runtime/spots/spot-manager-internal-contracts.ts +2 -2
- package/src/runtime/spots/spot-manager-public.ts +2 -2
- package/src/runtime/spots/spot-node-autoconnect.ts +22 -45
- package/src/runtime/spots/spot-node-runtime-manager.ts +197 -67
- package/src/runtime/spots/spot-outbound.ts +3 -3
- package/src/runtime/spots/spot-remote-codec.ts +9 -2
- package/src/runtime/spots/spot-runtime-ports.ts +0 -1
- package/src/runtime/spots/spot-serial-executor.ts +97 -25
- package/src/runtime/spots/spot-serial-turn-executor.ts +22 -3
- package/src/runtime/spots/spot-subscription-dispatch.ts +1 -1
- package/src/runtime/spots/spot-timer.ts +413 -157
- package/src/runtime/streams/actor-session-binding-registry.ts +236 -64
- package/src/runtime/streams/actor-session-binding-runtime-owner.ts +16 -8
- package/src/runtime/streams/bound-actor-relay-sender.ts +23 -9
- package/src/runtime/streams/bound-session-service.ts +7 -10
- package/src/runtime/streams/index.ts +12 -6
- package/src/runtime/streams/managed-stream.ts +4 -2
- package/src/runtime/streams/native-fallback-bound-session.ts +14 -103
- package/src/runtime/streams/session-actor-coordinator.ts +39 -88
- package/src/runtime/streams/stream-binding-runtime-ports.ts +3 -9
- package/src/runtime/streams/stream-session-runtime.ts +1 -5
- package/src/runtime/workers/index.ts +1 -1
|
@@ -1,3 +1,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';
|
|
@@ -14,7 +23,7 @@ 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,11 +114,16 @@ 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';
|
|
@@ -205,6 +220,58 @@ export interface ServiceCanonicalActorJoinCandidate {
|
|
|
205
220
|
readonly local: { readonly phase: 'admission'; readonly transferId: string };
|
|
206
221
|
}
|
|
207
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
|
+
|
|
208
275
|
export interface ServiceSessionDelivery {
|
|
209
276
|
readonly binding: ServiceSessionBinding;
|
|
210
277
|
/**
|
|
@@ -212,6 +279,7 @@ export interface ServiceSessionDelivery {
|
|
|
212
279
|
* been validated. The stream adapter owns multipart decoding at that point.
|
|
213
280
|
*/
|
|
214
281
|
readonly deliver: (sessionRid: string, payloadFrame: Uint8Array) => Promise<boolean>;
|
|
282
|
+
readonly close?: () => Promise<void>;
|
|
215
283
|
/** Enqueues the replacement lifecycle callback on the owning STREAM session. */
|
|
216
284
|
readonly onBindingReplaced?: (
|
|
217
285
|
actorId: string,
|
|
@@ -246,6 +314,7 @@ export interface ServiceInstanceApplicationLifecycle {
|
|
|
246
314
|
isIdleEvicting?(target: ServiceInstanceApplicationIdentity): boolean;
|
|
247
315
|
beginIdleEviction?(target: ServiceInstanceApplicationIdentity): boolean;
|
|
248
316
|
materialize(target: ServiceInstanceApplicationIdentity, objectGeneration: bigint): Promise<void>;
|
|
317
|
+
publish?(target: ServiceInstanceApplicationTarget): void;
|
|
249
318
|
discard(target: ServiceInstanceActivationTarget): Promise<void>;
|
|
250
319
|
beginTerminal(target: ServiceInstanceApplicationTarget): void;
|
|
251
320
|
completeTerminal(target: ServiceInstanceApplicationTarget): Promise<boolean>;
|
|
@@ -381,7 +450,7 @@ export class ServiceStatefulRuntime {
|
|
|
381
450
|
|
|
382
451
|
readonly registry: ServiceStatefulRegistry;
|
|
383
452
|
|
|
384
|
-
private readonly operations: ServiceTerminalOperationRegistry<ServiceStatefulResult>;
|
|
453
|
+
private readonly operations: ServiceTerminalOperationRegistry<ServiceStatefulResult, bigint>;
|
|
385
454
|
private readonly sessionDeliveries = new Map<string, ServiceSessionDelivery>();
|
|
386
455
|
/**
|
|
387
456
|
* A local Actor owner can replace its own session binding before the
|
|
@@ -414,6 +483,7 @@ export class ServiceStatefulRuntime {
|
|
|
414
483
|
private asyncInstanceAuthority?: ServiceAsyncInstanceActivationAuthority;
|
|
415
484
|
private instanceApplicationLifecycle?: ServiceInstanceApplicationLifecycle;
|
|
416
485
|
private userSpotOperationHandler?: ServiceUserSpotOperationHandler;
|
|
486
|
+
private requestSourceAcceptance?: import('../backend/contracts').ZLinkBackendRequestSourceAcceptance;
|
|
417
487
|
private messageFollowHandler?: (
|
|
418
488
|
record: import('./service-stateful-wire-codec').ServiceMessageFollowRecord
|
|
419
489
|
) => void;
|
|
@@ -422,6 +492,7 @@ export class ServiceStatefulRuntime {
|
|
|
422
492
|
readonly owner: string;
|
|
423
493
|
}) => void;
|
|
424
494
|
private dispatchErrors?: ZLinkDispatchErrorReporter;
|
|
495
|
+
private sessionOwnerDescriptors?: ServiceSessionOwnerDescriptors;
|
|
425
496
|
private dispatchErrorMeshName?: string;
|
|
426
497
|
private readonly admittedUserSpotOperations = new Map<
|
|
427
498
|
string,
|
|
@@ -465,6 +536,52 @@ export class ServiceStatefulRuntime {
|
|
|
465
536
|
this.mailboxDropHandler = handler;
|
|
466
537
|
}
|
|
467
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
|
+
|
|
468
585
|
setDispatchErrorReporter(reporter: ZLinkDispatchErrorReporter, meshName: string): void {
|
|
469
586
|
this.dispatchErrors = reporter;
|
|
470
587
|
this.dispatchErrorMeshName = meshName;
|
|
@@ -566,7 +683,7 @@ export class ServiceStatefulRuntime {
|
|
|
566
683
|
if (
|
|
567
684
|
current?.bindingGeneration === bindingGeneration &&
|
|
568
685
|
current.sessionOwnerNodeRid === sessionNodeRid &&
|
|
569
|
-
current.sessionRid
|
|
686
|
+
routingIdsEqual(current.sessionRid, sessionRid)
|
|
570
687
|
) {
|
|
571
688
|
return;
|
|
572
689
|
}
|
|
@@ -1130,6 +1247,78 @@ export class ServiceStatefulRuntime {
|
|
|
1130
1247
|
this.messageFollowHandler = handler;
|
|
1131
1248
|
}
|
|
1132
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
|
+
|
|
1133
1322
|
sealSpotMessageFollowIngress(
|
|
1134
1323
|
source: ServiceDirectSpotRouteFence
|
|
1135
1324
|
): ServiceSpotMessageFollowSeal | undefined {
|
|
@@ -1179,11 +1368,15 @@ export class ServiceStatefulRuntime {
|
|
|
1179
1368
|
|
|
1180
1369
|
async commitSpotMessageFollowIngress(
|
|
1181
1370
|
seal: ServiceSpotMessageFollowSeal,
|
|
1182
|
-
target: ServiceDirectSpotRouteFence,
|
|
1371
|
+
target: ServiceDirectSpotRouteFence | null,
|
|
1183
1372
|
durationMs: number
|
|
1184
1373
|
): Promise<boolean> {
|
|
1185
1374
|
const state = this.exactSpotMessageFollow(seal);
|
|
1186
1375
|
if (state === undefined || state.target !== undefined) return false;
|
|
1376
|
+
if (target === null) {
|
|
1377
|
+
this.failExpiredSpotMessageFollow(state);
|
|
1378
|
+
return true;
|
|
1379
|
+
}
|
|
1187
1380
|
if (!Number.isSafeInteger(durationMs) || durationMs < 0) {
|
|
1188
1381
|
throw new RangeError('Message Follow duration must be a non-negative safe integer.');
|
|
1189
1382
|
}
|
|
@@ -1355,13 +1548,12 @@ export class ServiceStatefulRuntime {
|
|
|
1355
1548
|
|
|
1356
1549
|
async sendToActor(
|
|
1357
1550
|
target: ServiceActorRef,
|
|
1358
|
-
_targetNodeGeneration: bigint,
|
|
1359
|
-
_authorityOwnerGeneration: bigint,
|
|
1360
1551
|
payload: ServiceApplicationPayload,
|
|
1361
1552
|
sourceActor?: ServiceActorRef,
|
|
1362
|
-
boundSession?: { readonly sessionRid: string; readonly bindingGeneration: bigint }
|
|
1553
|
+
boundSession?: { readonly sessionRid: string; readonly bindingGeneration: bigint },
|
|
1554
|
+
routeFence?: ServiceActorRouteFence
|
|
1363
1555
|
): Promise<number> {
|
|
1364
|
-
const route = this.tryActorFence(target);
|
|
1556
|
+
const route = routeFence ?? this.tryActorFence(target);
|
|
1365
1557
|
if (route === undefined) return SubmitResult.NotFound;
|
|
1366
1558
|
const header = encodeActorHeader(
|
|
1367
1559
|
'actorSend',
|
|
@@ -1381,16 +1573,16 @@ export class ServiceStatefulRuntime {
|
|
|
1381
1573
|
|
|
1382
1574
|
requestToActor(
|
|
1383
1575
|
target: ServiceActorRef,
|
|
1384
|
-
_targetNodeGeneration: bigint,
|
|
1385
|
-
_authorityOwnerGeneration: bigint,
|
|
1386
1576
|
payload: ServiceApplicationPayload,
|
|
1387
1577
|
timeoutMs: number,
|
|
1388
1578
|
sourceActor?: ServiceActorRef,
|
|
1389
1579
|
boundSession?: { readonly sessionRid: string; readonly bindingGeneration: bigint },
|
|
1390
|
-
onAdmitted?: () => void
|
|
1580
|
+
onAdmitted?: () => void,
|
|
1581
|
+
routeFence?: ServiceActorRouteFence
|
|
1391
1582
|
): ServiceStatefulPendingOperation {
|
|
1392
|
-
const
|
|
1393
|
-
const
|
|
1583
|
+
const operationLow = this.nextMessageFollowOperation++;
|
|
1584
|
+
const pending = this.operations.register(timeoutMs, 'registry', operationLow);
|
|
1585
|
+
const route = routeFence ?? this.tryActorFence(target);
|
|
1394
1586
|
if (route === undefined) {
|
|
1395
1587
|
this.operations.reply(
|
|
1396
1588
|
pending.id,
|
|
@@ -1409,7 +1601,7 @@ export class ServiceStatefulRuntime {
|
|
|
1409
1601
|
...boundSession,
|
|
1410
1602
|
sequence: this.nextSessionSequence++
|
|
1411
1603
|
},
|
|
1412
|
-
{ high: this.nodeGeneration, low:
|
|
1604
|
+
{ high: this.nodeGeneration, low: operationLow }
|
|
1413
1605
|
);
|
|
1414
1606
|
this.submitRequest(
|
|
1415
1607
|
pending,
|
|
@@ -1423,6 +1615,21 @@ export class ServiceStatefulRuntime {
|
|
|
1423
1615
|
return pending;
|
|
1424
1616
|
}
|
|
1425
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
|
+
|
|
1426
1633
|
async sendToInstanceSpot(
|
|
1427
1634
|
route: ServiceInstanceRouteFence,
|
|
1428
1635
|
payload: ServiceApplicationPayload,
|
|
@@ -1758,27 +1965,51 @@ export class ServiceStatefulRuntime {
|
|
|
1758
1965
|
timeoutMs: number,
|
|
1759
1966
|
deliver: ServiceSessionDelivery['deliver'],
|
|
1760
1967
|
onBindingReplaced?: ServiceSessionDelivery['onBindingReplaced'],
|
|
1761
|
-
actorAuthority?: StreamSessionActorAuthorityFence
|
|
1968
|
+
actorAuthority?: StreamSessionActorAuthorityFence,
|
|
1969
|
+
close?: () => Promise<void>
|
|
1762
1970
|
): ServiceStatefulPendingOperation {
|
|
1763
1971
|
const deadlineMs = performance.now() + timeoutMs;
|
|
1764
1972
|
const pending = this.operations.register(timeoutMs, 'sender');
|
|
1765
|
-
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++;
|
|
1766
1997
|
const localBinding: ServiceSessionBinding = {
|
|
1767
1998
|
actor,
|
|
1768
1999
|
sessionRid,
|
|
1769
|
-
sessionOwnerNodeRid:
|
|
1770
|
-
sessionOwnerNodeGeneration:
|
|
1771
|
-
sessionOwnerId:
|
|
1772
|
-
sessionOwnerLeaseGeneration:
|
|
2000
|
+
sessionOwnerNodeRid: sessionOwner.nodeRid,
|
|
2001
|
+
sessionOwnerNodeGeneration: sessionOwner.nodeGeneration,
|
|
2002
|
+
sessionOwnerId: sessionOwner.ownerId,
|
|
2003
|
+
sessionOwnerLeaseGeneration: sessionOwner.leaseGeneration,
|
|
1773
2004
|
bindingGeneration: generation,
|
|
1774
2005
|
membershipEpoch: this.registry.actor(actor.actorId)?.membershipEpoch ?? 1n
|
|
1775
2006
|
};
|
|
1776
2007
|
const delivery: ServiceSessionDelivery = {
|
|
1777
2008
|
binding: localBinding,
|
|
1778
2009
|
deliver,
|
|
2010
|
+
...(close === undefined ? {} : { close }),
|
|
1779
2011
|
...(onBindingReplaced === undefined ? {} : { onBindingReplaced })
|
|
1780
2012
|
};
|
|
1781
|
-
const deliveryKey = actorKey(actor);
|
|
1782
2013
|
if (actor.nodeRid === this.nodeRid) {
|
|
1783
2014
|
let installedDelivery: ServiceSessionDelivery | undefined;
|
|
1784
2015
|
try {
|
|
@@ -1802,18 +2033,7 @@ export class ServiceStatefulRuntime {
|
|
|
1802
2033
|
});
|
|
1803
2034
|
return pending;
|
|
1804
2035
|
}
|
|
1805
|
-
const
|
|
1806
|
-
const binding = this.registry.bindSession(
|
|
1807
|
-
actor,
|
|
1808
|
-
sessionRid,
|
|
1809
|
-
this.nodeRid,
|
|
1810
|
-
this.nodeGeneration,
|
|
1811
|
-
this.nodeRid,
|
|
1812
|
-
this.nodeGeneration
|
|
1813
|
-
);
|
|
1814
|
-
if (previousDelivery !== undefined && previous !== undefined) {
|
|
1815
|
-
this.retiredSessionDeliveries.set(sessionDeliveryKey(previous), previousDelivery);
|
|
1816
|
-
}
|
|
2036
|
+
const binding = this.registry.installSessionBinding(localBinding);
|
|
1817
2037
|
installedDelivery = { ...delivery, binding };
|
|
1818
2038
|
this.sessionDeliveries.set(deliveryKey, installedDelivery);
|
|
1819
2039
|
this.operations.reply(pending.id, {
|
|
@@ -1821,7 +2041,10 @@ export class ServiceStatefulRuntime {
|
|
|
1821
2041
|
failureCode: 0,
|
|
1822
2042
|
kindData: undefined
|
|
1823
2043
|
});
|
|
1824
|
-
if (previous !== undefined) {
|
|
2044
|
+
if (previous !== undefined && previous !== binding) {
|
|
2045
|
+
if (previousDelivery !== undefined) {
|
|
2046
|
+
this.retiredSessionDeliveries.set(sessionDeliveryKey(previous), previousDelivery);
|
|
2047
|
+
}
|
|
1825
2048
|
this.sendBoundSessionReplacement(
|
|
1826
2049
|
previous,
|
|
1827
2050
|
actorAuthorityFromRoute(
|
|
@@ -1846,7 +2069,6 @@ export class ServiceStatefulRuntime {
|
|
|
1846
2069
|
}
|
|
1847
2070
|
return pending;
|
|
1848
2071
|
}
|
|
1849
|
-
const previousDelivery = this.sessionDeliveries.get(deliveryKey);
|
|
1850
2072
|
let bindingActorFence: ServiceActorRouteFence;
|
|
1851
2073
|
let header: Buffer;
|
|
1852
2074
|
try {
|
|
@@ -1894,7 +2116,10 @@ export class ServiceStatefulRuntime {
|
|
|
1894
2116
|
// Publish the exact Location authority used by command 38 in the
|
|
1895
2117
|
// same Session-owner completion turn as the binding route. Actor
|
|
1896
2118
|
// lookup only carries a peer-lifecycle fallback for owner lease.
|
|
1897
|
-
this.rememberActorRoute(
|
|
2119
|
+
this.rememberActorRoute({
|
|
2120
|
+
...bindingActorFence,
|
|
2121
|
+
authorityOwnerGeneration: result.streamBinding.authorityOwnerGeneration
|
|
2122
|
+
});
|
|
1898
2123
|
this.sessionDeliveries.set(deliveryKey, committed);
|
|
1899
2124
|
}
|
|
1900
2125
|
}
|
|
@@ -1903,6 +2128,59 @@ export class ServiceStatefulRuntime {
|
|
|
1903
2128
|
};
|
|
1904
2129
|
}
|
|
1905
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
|
+
|
|
1906
2184
|
unbindSession(
|
|
1907
2185
|
sessionRid: string,
|
|
1908
2186
|
actor: ServiceActorRef,
|
|
@@ -1913,7 +2191,7 @@ export class ServiceStatefulRuntime {
|
|
|
1913
2191
|
const delivery = this.sessionDeliveries.get(actorKey(actor));
|
|
1914
2192
|
if (
|
|
1915
2193
|
delivery === undefined ||
|
|
1916
|
-
delivery.binding.sessionRid
|
|
2194
|
+
!routingIdsEqual(delivery.binding.sessionRid, sessionRid) ||
|
|
1917
2195
|
delivery.binding.bindingGeneration !== expectedBindingGeneration
|
|
1918
2196
|
) {
|
|
1919
2197
|
this.operations.reply(
|
|
@@ -1943,7 +2221,7 @@ export class ServiceStatefulRuntime {
|
|
|
1943
2221
|
sessionBindings(sessionRid: string): readonly ServiceStreamSessionBinding[] {
|
|
1944
2222
|
return [...this.sessionDeliveries.values()]
|
|
1945
2223
|
.map((value) => value.binding)
|
|
1946
|
-
.filter((binding) => binding.sessionRid
|
|
2224
|
+
.filter((binding) => routingIdsEqual(binding.sessionRid, sessionRid))
|
|
1947
2225
|
.map((binding) => {
|
|
1948
2226
|
const owner = requireSessionOwnerIdentity(binding);
|
|
1949
2227
|
return {
|
|
@@ -1965,20 +2243,13 @@ export class ServiceStatefulRuntime {
|
|
|
1965
2243
|
payload: ServiceApplicationPayload
|
|
1966
2244
|
): Promise<number> {
|
|
1967
2245
|
const delivery = this.sessionDeliveries.get(actorKey(actor));
|
|
1968
|
-
if (delivery === undefined || delivery.binding.sessionRid
|
|
2246
|
+
if (delivery === undefined || !routingIdsEqual(delivery.binding.sessionRid, sessionRid)) {
|
|
1969
2247
|
return SubmitResult.NotFound;
|
|
1970
2248
|
}
|
|
1971
|
-
return this.sendToActor(
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
payload,
|
|
1976
|
-
undefined,
|
|
1977
|
-
{
|
|
1978
|
-
sessionRid,
|
|
1979
|
-
bindingGeneration: delivery.binding.bindingGeneration
|
|
1980
|
-
}
|
|
1981
|
-
);
|
|
2249
|
+
return this.sendToActor(actor, payload, undefined, {
|
|
2250
|
+
sessionRid,
|
|
2251
|
+
bindingGeneration: delivery.binding.bindingGeneration
|
|
2252
|
+
});
|
|
1982
2253
|
}
|
|
1983
2254
|
|
|
1984
2255
|
async sendBoundSession(
|
|
@@ -2014,6 +2285,11 @@ export class ServiceStatefulRuntime {
|
|
|
2014
2285
|
return this.operations.size;
|
|
2015
2286
|
}
|
|
2016
2287
|
|
|
2288
|
+
/** The next stateful request expiry, as a lower bound. */
|
|
2289
|
+
nextMaintenanceDeadlineMs(): number {
|
|
2290
|
+
return this.operations.nextDeadlineMs();
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2017
2293
|
close(): void {
|
|
2018
2294
|
if (this.closed) return;
|
|
2019
2295
|
this.closed = true;
|
|
@@ -2037,6 +2313,10 @@ export class ServiceStatefulRuntime {
|
|
|
2037
2313
|
this.spotMessageFollow.clear();
|
|
2038
2314
|
}
|
|
2039
2315
|
|
|
2316
|
+
cancelPendingOperations(reason: unknown): void {
|
|
2317
|
+
this.operations.failAll(reason);
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2040
2320
|
private async ingress(
|
|
2041
2321
|
record: RawServiceIngressRecord
|
|
2042
2322
|
): Promise<RawServicePumpResult | undefined> {
|
|
@@ -2086,7 +2366,8 @@ export class ServiceStatefulRuntime {
|
|
|
2086
2366
|
decoded.kind === 'userSpotClose' ||
|
|
2087
2367
|
decoded.kind === 'actorCreate' ||
|
|
2088
2368
|
decoded.kind === 'messageFollow' ||
|
|
2089
|
-
decoded.kind === 'boundSessionReplaced'
|
|
2369
|
+
decoded.kind === 'boundSessionReplaced' ||
|
|
2370
|
+
decoded.kind === 'boundSessionClose';
|
|
2090
2371
|
if (
|
|
2091
2372
|
record.parts.length < 1 ||
|
|
2092
2373
|
record.parts.length > (instanceMetadata || statefulMetadata ? 3 : 2) ||
|
|
@@ -2131,7 +2412,13 @@ export class ServiceStatefulRuntime {
|
|
|
2131
2412
|
decoded.activation === 'missing' &&
|
|
2132
2413
|
(error instanceof ZLinkFrameworkException || error instanceof ServiceStaleGenerationError)
|
|
2133
2414
|
) {
|
|
2134
|
-
this.finishMissingInstanceActivationFailure(
|
|
2415
|
+
this.finishMissingInstanceActivationFailure(
|
|
2416
|
+
record,
|
|
2417
|
+
decoded,
|
|
2418
|
+
error,
|
|
2419
|
+
undefined,
|
|
2420
|
+
payloadFrame
|
|
2421
|
+
);
|
|
2135
2422
|
return 'infrastructure';
|
|
2136
2423
|
}
|
|
2137
2424
|
const correlation = statefulCorrelation(decoded);
|
|
@@ -2263,7 +2550,7 @@ export class ServiceStatefulRuntime {
|
|
|
2263
2550
|
record.boundSession.bindingGeneration
|
|
2264
2551
|
);
|
|
2265
2552
|
if (
|
|
2266
|
-
binding.sessionRid
|
|
2553
|
+
!routingIdsEqual(binding.sessionRid, record.boundSession.sessionRid) ||
|
|
2267
2554
|
binding.sessionOwnerNodeRid !== ingress.sourceRoutingId
|
|
2268
2555
|
) {
|
|
2269
2556
|
throw new ServiceStaleGenerationError('binding', record.target.actor.actorId);
|
|
@@ -2286,12 +2573,13 @@ export class ServiceStatefulRuntime {
|
|
|
2286
2573
|
: { sourceBindingGeneration: record.boundSession.bindingGeneration }),
|
|
2287
2574
|
targetActor: actor.ref,
|
|
2288
2575
|
messageFollowOrigin: {
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
high: this.peerGeneration(ingress.sourceRoutingId),
|
|
2292
|
-
low: record.correlation ?? this.nextMessageFollowOperation++
|
|
2576
|
+
get requestSource() {
|
|
2577
|
+
return ingress.requestSource;
|
|
2293
2578
|
},
|
|
2294
|
-
|
|
2579
|
+
target: record.target,
|
|
2580
|
+
sourceNodeRid: ingress.sourceRoutingId,
|
|
2581
|
+
originalOperation: record.operation,
|
|
2582
|
+
originalReplyRouteId: record.correlation ?? 0n
|
|
2295
2583
|
},
|
|
2296
2584
|
...(record.kind === 'actorRequest'
|
|
2297
2585
|
? { reply: this.replyPort(ingress, record.correlation) }
|
|
@@ -2329,7 +2617,9 @@ export class ServiceStatefulRuntime {
|
|
|
2329
2617
|
canonicalActorJoinPayload
|
|
2330
2618
|
);
|
|
2331
2619
|
case 'boundSessionBind':
|
|
2332
|
-
return this.replySessionBind(ingress, record);
|
|
2620
|
+
return await this.replySessionBind(ingress, record);
|
|
2621
|
+
case 'boundSessionClose':
|
|
2622
|
+
return await this.handleBoundSessionClose(ingress, record);
|
|
2333
2623
|
case 'boundSessionReplaced':
|
|
2334
2624
|
return this.handleBoundSessionReplaced(ingress, record);
|
|
2335
2625
|
case 'boundSessionSend':
|
|
@@ -2536,7 +2826,7 @@ export class ServiceStatefulRuntime {
|
|
|
2536
2826
|
}
|
|
2537
2827
|
});
|
|
2538
2828
|
} catch (error) {
|
|
2539
|
-
this.finishMissingInstanceActivationFailure(ingress, record, error, localReply);
|
|
2829
|
+
this.finishMissingInstanceActivationFailure(ingress, record, error, localReply, payloadFrame);
|
|
2540
2830
|
}
|
|
2541
2831
|
}
|
|
2542
2832
|
|
|
@@ -2571,6 +2861,7 @@ export class ServiceStatefulRuntime {
|
|
|
2571
2861
|
latestRoute !== undefined && sameInstanceApplicationOwner(latestRoute, record.route)
|
|
2572
2862
|
? { ...record, route: latestRoute }
|
|
2573
2863
|
: record;
|
|
2864
|
+
this.publishCommittedInstanceRoute(intent.instanceType, refreshedRecord.route);
|
|
2574
2865
|
const spot = this.requireInstanceActivation(ingress, refreshedRecord);
|
|
2575
2866
|
const admitted = this.enqueueActivatedInstanceSpot(
|
|
2576
2867
|
ingress,
|
|
@@ -2727,7 +3018,8 @@ export class ServiceStatefulRuntime {
|
|
|
2727
3018
|
{ readonly kind: 'instanceSpot'; readonly activation: 'missing' }
|
|
2728
3019
|
>,
|
|
2729
3020
|
error: unknown,
|
|
2730
|
-
localReply?: NonNullable<ServiceStatefulMailboxData['reply']
|
|
3021
|
+
localReply?: NonNullable<ServiceStatefulMailboxData['reply']>,
|
|
3022
|
+
payloadFrame?: Buffer
|
|
2731
3023
|
): void {
|
|
2732
3024
|
if (record.operationKind === 'request') {
|
|
2733
3025
|
if (record.deadlineUnixMs <= BigInt(Date.now())) return;
|
|
@@ -2740,17 +3032,51 @@ export class ServiceStatefulRuntime {
|
|
|
2740
3032
|
return;
|
|
2741
3033
|
}
|
|
2742
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
|
+
});
|
|
2743
3075
|
if (reporter?.captureEnabled() === true) {
|
|
2744
3076
|
reporter.report({
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
reason: ZLinkDispatchErrorReason.StaleTarget,
|
|
3077
|
+
...context,
|
|
3078
|
+
reason,
|
|
2748
3079
|
action: ZLinkDispatchErrorAction.Drop,
|
|
2749
|
-
meshName: this.dispatchErrorMeshName,
|
|
2750
|
-
sourceRid: record.sourceNodeRid,
|
|
2751
|
-
spotId: record.target.targetSpotId,
|
|
2752
|
-
instanceSpotType: record.target.stableType,
|
|
2753
|
-
activationState: 'activating',
|
|
2754
3080
|
error
|
|
2755
3081
|
});
|
|
2756
3082
|
}
|
|
@@ -2969,12 +3295,25 @@ export class ServiceStatefulRuntime {
|
|
|
2969
3295
|
): Promise<unknown> {
|
|
2970
3296
|
const key = record.target.targetSpotId;
|
|
2971
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;
|
|
2972
3305
|
const ready =
|
|
2973
3306
|
pending ??
|
|
2974
|
-
this.runMissingInstanceActivation(record, payloadFrame, metadataFrame).then(
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
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
|
+
);
|
|
2978
3317
|
const operation = ready.then(async (value) => {
|
|
2979
3318
|
if (pending !== undefined) {
|
|
2980
3319
|
await this.asyncInstanceAuthority?.read(
|
|
@@ -3023,12 +3362,38 @@ export class ServiceStatefulRuntime {
|
|
|
3023
3362
|
): Promise<{
|
|
3024
3363
|
readonly spot: Pick<ServiceSpotState, 'ref'>;
|
|
3025
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;
|
|
3026
3392
|
}> {
|
|
3027
3393
|
const target = record.target;
|
|
3028
3394
|
if (
|
|
3029
3395
|
target.targetNodeRid !== this.nodeRid ||
|
|
3030
|
-
target.targetNodeGeneration !== this.nodeGeneration
|
|
3031
|
-
record.deadlineUnixMs < BigInt(Date.now())
|
|
3396
|
+
target.targetNodeGeneration !== this.nodeGeneration
|
|
3032
3397
|
) {
|
|
3033
3398
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
3034
3399
|
}
|
|
@@ -3140,21 +3505,22 @@ export class ServiceStatefulRuntime {
|
|
|
3140
3505
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
3141
3506
|
}
|
|
3142
3507
|
}
|
|
3143
|
-
const
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
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
|
+
);
|
|
3151
3516
|
await authority.abort(target, reserved.reservation);
|
|
3152
|
-
throw
|
|
3517
|
+
throw error;
|
|
3153
3518
|
}
|
|
3154
3519
|
|
|
3155
3520
|
let activation: { readonly spot: ServiceSpotState; readonly created: boolean };
|
|
3156
3521
|
try {
|
|
3157
3522
|
await this.instanceApplicationLifecycle?.materialize(target, reserved.reservation.attempt);
|
|
3523
|
+
signal.throwIfAborted();
|
|
3158
3524
|
activation = this.activateInstanceSpot(
|
|
3159
3525
|
target.targetSpotId,
|
|
3160
3526
|
target.stableType,
|
|
@@ -3222,8 +3588,6 @@ export class ServiceStatefulRuntime {
|
|
|
3222
3588
|
if (activation.created) this.registry.closeSpot(activation.spot.ref);
|
|
3223
3589
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
3224
3590
|
}
|
|
3225
|
-
this.publishCommittedInstanceRoute(target.stableType, route);
|
|
3226
|
-
|
|
3227
3591
|
const confirmed = await authority.read(target);
|
|
3228
3592
|
if (confirmed.kind !== 'ready' || !sameInstanceRoute(confirmed.route, route)) {
|
|
3229
3593
|
if (confirmed.kind === 'ready' && confirmed.route.targetNodeRid !== this.nodeRid) {
|
|
@@ -3238,6 +3602,7 @@ export class ServiceStatefulRuntime {
|
|
|
3238
3602
|
) {
|
|
3239
3603
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
3240
3604
|
}
|
|
3605
|
+
this.publishCommittedInstanceRoute(target.stableType, confirmed.route);
|
|
3241
3606
|
return { spot: current, route: confirmed.route };
|
|
3242
3607
|
}
|
|
3243
3608
|
|
|
@@ -3255,6 +3620,9 @@ export class ServiceStatefulRuntime {
|
|
|
3255
3620
|
: instanceRouteAsDirectRoute(expectedCurrentRoute);
|
|
3256
3621
|
this.rememberSpotRoute(directRoute, expectedDirectRoute);
|
|
3257
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 });
|
|
3258
3626
|
}
|
|
3259
3627
|
|
|
3260
3628
|
private replaceCommittedInstanceRoute(
|
|
@@ -3407,55 +3775,52 @@ export class ServiceStatefulRuntime {
|
|
|
3407
3775
|
return 'infrastructure';
|
|
3408
3776
|
}
|
|
3409
3777
|
|
|
3410
|
-
private replySessionBind(
|
|
3778
|
+
private async replySessionBind(
|
|
3411
3779
|
ingress: RawServiceIngressRecord,
|
|
3412
3780
|
record: Extract<ServiceStatefulWireRecord, { readonly kind: 'boundSessionBind' }>
|
|
3413
|
-
): RawServicePumpResult {
|
|
3781
|
+
): Promise<RawServicePumpResult> {
|
|
3414
3782
|
try {
|
|
3415
3783
|
if (record.binding.state === 'active') {
|
|
3784
|
+
const sessionOwner = await this.sessionOwnerOf(
|
|
3785
|
+
ingress.sourceRoutingId,
|
|
3786
|
+
ingress.sourceNodeGeneration ?? this.peerGeneration(ingress.sourceRoutingId)
|
|
3787
|
+
);
|
|
3416
3788
|
const actor = this.validateActorFence(record.actor);
|
|
3417
|
-
const previous = this.registry.binding(actor.ref);
|
|
3418
|
-
const sourceGeneration =
|
|
3419
|
-
ingress.sourceNodeGeneration ?? this.peerGeneration(ingress.sourceRoutingId);
|
|
3420
3789
|
const binding = sessionBindingFromWire(
|
|
3421
3790
|
actor.ref,
|
|
3422
3791
|
record.sessionRid,
|
|
3423
|
-
|
|
3792
|
+
sessionOwner.nodeRid,
|
|
3424
3793
|
record.binding.generation,
|
|
3425
3794
|
actor.membershipEpoch,
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3795
|
+
sessionOwner.nodeGeneration,
|
|
3796
|
+
sessionOwner.ownerId,
|
|
3797
|
+
sessionOwner.leaseGeneration
|
|
3429
3798
|
);
|
|
3430
|
-
|
|
3431
|
-
// Registry publication is the last step of the first binding owner
|
|
3432
|
-
// turn, so an observable same-Session binding already has every
|
|
3433
|
-
// Actor route projection installed and can complete idempotently.
|
|
3434
|
-
this.replyWire(ingress, record.correlation, RequestResult.Ok, 0, undefined, {
|
|
3435
|
-
kind: 'streamBind',
|
|
3436
|
-
bindingGeneration: previous.bindingGeneration,
|
|
3437
|
-
authorityOwnerGeneration: actor.authorityOwnerGeneration
|
|
3438
|
-
});
|
|
3439
|
-
return 'infrastructure';
|
|
3440
|
-
}
|
|
3799
|
+
let completedBinding = binding;
|
|
3441
3800
|
const accepted = this.enqueueActorBindingControl(
|
|
3442
3801
|
ingress,
|
|
3443
3802
|
binding,
|
|
3444
3803
|
record.actor,
|
|
3445
3804
|
(terminalResult, failureCode) => {
|
|
3446
|
-
let completedBinding = binding;
|
|
3447
3805
|
let replacedBinding: ServiceSessionBinding | undefined;
|
|
3448
3806
|
if (terminalResult === RequestResult.Ok) {
|
|
3449
3807
|
// This callback runs inside the Actor binding control turn,
|
|
3450
|
-
//
|
|
3808
|
+
// before the Host copies the accepted Actor-to-session projection.
|
|
3451
3809
|
// Publish the authority-owned registry view in that same turn
|
|
3452
3810
|
// before making the terminal reply observable.
|
|
3453
3811
|
const turnCurrent = this.registry.binding(actor.ref);
|
|
3454
|
-
|
|
3455
|
-
completedBinding =
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
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;
|
|
3459
3824
|
}
|
|
3460
3825
|
}
|
|
3461
3826
|
this.replyWire(
|
|
@@ -3486,7 +3851,8 @@ export class ServiceStatefulRuntime {
|
|
|
3486
3851
|
);
|
|
3487
3852
|
}
|
|
3488
3853
|
return true;
|
|
3489
|
-
}
|
|
3854
|
+
},
|
|
3855
|
+
() => completedBinding
|
|
3490
3856
|
);
|
|
3491
3857
|
if (!accepted) {
|
|
3492
3858
|
const result = failure(
|
|
@@ -3507,6 +3873,36 @@ export class ServiceStatefulRuntime {
|
|
|
3507
3873
|
return 'infrastructure';
|
|
3508
3874
|
}
|
|
3509
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
|
+
|
|
3510
3906
|
/**
|
|
3511
3907
|
* Handles the replacement notice at the retired session owner. This path
|
|
3512
3908
|
* intentionally never calls validateActorFence(): the notice is authorized
|
|
@@ -3529,12 +3925,25 @@ export class ServiceStatefulRuntime {
|
|
|
3529
3925
|
// session that belongs to the new lifecycle.
|
|
3530
3926
|
return 'infrastructure';
|
|
3531
3927
|
}
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
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
|
+
);
|
|
3538
3947
|
return 'infrastructure';
|
|
3539
3948
|
}
|
|
3540
3949
|
const actor = authority.actor;
|
|
@@ -3561,6 +3970,9 @@ export class ServiceStatefulRuntime {
|
|
|
3561
3970
|
: undefined;
|
|
3562
3971
|
if (delivery === undefined) return 'infrastructure';
|
|
3563
3972
|
this.appliedReplacementNotices.add(key);
|
|
3973
|
+
if (delivery === current) {
|
|
3974
|
+
this.sessionDeliveries.delete(deliveryKey);
|
|
3975
|
+
}
|
|
3564
3976
|
delivery.onBindingReplaced?.(actor.actorId, record.retiredSession, actor);
|
|
3565
3977
|
if (retired !== undefined) {
|
|
3566
3978
|
this.retiredSessionDeliveries.delete(sessionDeliveryKey(retired.binding));
|
|
@@ -3572,14 +3984,20 @@ export class ServiceStatefulRuntime {
|
|
|
3572
3984
|
previous: ServiceSessionBinding,
|
|
3573
3985
|
actorAuthority: ServiceBoundSessionActorAuthority
|
|
3574
3986
|
): void {
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
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
|
+
}
|
|
3578
3996
|
const header = encodeBoundSessionReplacedHeader(actorAuthority, {
|
|
3579
3997
|
sessionOwnerNodeRid: previous.sessionOwnerNodeRid,
|
|
3580
|
-
sessionOwnerNodeGeneration:
|
|
3581
|
-
sessionOwnerId: previous.sessionOwnerId
|
|
3582
|
-
sessionOwnerLeaseGeneration: previous.sessionOwnerLeaseGeneration
|
|
3998
|
+
sessionOwnerNodeGeneration: previous.sessionOwnerNodeGeneration,
|
|
3999
|
+
sessionOwnerId: previous.sessionOwnerId,
|
|
4000
|
+
sessionOwnerLeaseGeneration: previous.sessionOwnerLeaseGeneration,
|
|
3583
4001
|
sessionRid: previous.sessionRid,
|
|
3584
4002
|
retiredBindingGeneration: previous.bindingGeneration
|
|
3585
4003
|
});
|
|
@@ -3602,11 +4020,9 @@ export class ServiceStatefulRuntime {
|
|
|
3602
4020
|
current === undefined ||
|
|
3603
4021
|
sourceGeneration === undefined ||
|
|
3604
4022
|
current.bindingGeneration !== record.binding.retiredGeneration ||
|
|
3605
|
-
current.sessionRid
|
|
4023
|
+
!routingIdsEqual(current.sessionRid, record.sessionRid) ||
|
|
3606
4024
|
current.sessionOwnerNodeRid !== ingress.sourceRoutingId ||
|
|
3607
|
-
current.sessionOwnerNodeGeneration !== sourceGeneration
|
|
3608
|
-
current.sessionOwnerId !== ingress.sourceRoutingId ||
|
|
3609
|
-
current.sessionOwnerLeaseGeneration !== sourceGeneration
|
|
4025
|
+
current.sessionOwnerNodeGeneration !== sourceGeneration
|
|
3610
4026
|
) {
|
|
3611
4027
|
this.replyWire(ingress, record.correlation, RequestResult.Ok, 0);
|
|
3612
4028
|
return;
|
|
@@ -3771,6 +4187,37 @@ export class ServiceStatefulRuntime {
|
|
|
3771
4187
|
});
|
|
3772
4188
|
}
|
|
3773
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
|
+
|
|
3774
4221
|
private enqueueApplicationFrame(
|
|
3775
4222
|
ingress: RawServiceIngressRecord,
|
|
3776
4223
|
owner: string,
|
|
@@ -3802,6 +4249,10 @@ export class ServiceStatefulRuntime {
|
|
|
3802
4249
|
domain,
|
|
3803
4250
|
parts: [ingress.parts[0]!, payloadFrame],
|
|
3804
4251
|
sourceRoutingId: ingress.sourceRoutingId,
|
|
4252
|
+
ingress,
|
|
4253
|
+
get requestSource() {
|
|
4254
|
+
return ingress.requestSource;
|
|
4255
|
+
},
|
|
3805
4256
|
sourceRoute: ingress.sourceRoute,
|
|
3806
4257
|
...(ingress.reply === undefined ? {} : { reply: ingress.reply }),
|
|
3807
4258
|
...(ingress.requestSequence === undefined
|
|
@@ -3809,6 +4260,15 @@ export class ServiceStatefulRuntime {
|
|
|
3809
4260
|
: { requestSequence: ingress.requestSequence }),
|
|
3810
4261
|
...(stateful.correlation === undefined ? {} : { correlation: stateful.correlation }),
|
|
3811
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,
|
|
3812
4272
|
applicationJob
|
|
3813
4273
|
});
|
|
3814
4274
|
if (accepted) {
|
|
@@ -3950,10 +4410,11 @@ export class ServiceStatefulRuntime {
|
|
|
3950
4410
|
ingress: RawServiceIngressRecord,
|
|
3951
4411
|
binding: ServiceSessionBinding,
|
|
3952
4412
|
actorAuthority: ServiceBoundSessionActorAuthority,
|
|
3953
|
-
reply: NonNullable<ServiceStatefulMailboxData['reply']
|
|
4413
|
+
reply: NonNullable<ServiceStatefulMailboxData['reply']>,
|
|
4414
|
+
acceptedBinding: () => ServiceSessionBinding
|
|
3954
4415
|
): boolean {
|
|
3955
4416
|
const actor = binding.actor;
|
|
3956
|
-
|
|
4417
|
+
requireSessionOwnerIdentity(binding);
|
|
3957
4418
|
const header = Buffer.from([
|
|
3958
4419
|
SERVICE_WIRE_MAGIC[0],
|
|
3959
4420
|
SERVICE_WIRE_MAGIC[1],
|
|
@@ -3978,12 +4439,27 @@ export class ServiceStatefulRuntime {
|
|
|
3978
4439
|
actorNodeGeneration: actorAuthority.targetNodeGeneration,
|
|
3979
4440
|
authorityOwnerGeneration: actorAuthority.authorityOwnerGeneration,
|
|
3980
4441
|
ownerLeaseGeneration: actorAuthority.ownerLeaseGeneration,
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
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
|
+
}
|
|
3987
4463
|
},
|
|
3988
4464
|
reply
|
|
3989
4465
|
} satisfies ServiceStatefulMailboxData,
|
|
@@ -4557,6 +5033,15 @@ export class ServiceStatefulRuntime {
|
|
|
4557
5033
|
operationKind: OperationKind.ActorRequest,
|
|
4558
5034
|
correlation: decoded.correlation,
|
|
4559
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
|
+
},
|
|
4560
5045
|
...(decoded.sourceActor === undefined
|
|
4561
5046
|
? {}
|
|
4562
5047
|
: { sourceActor: { ...decoded.sourceActor, nodeRid: ingress.sourceRoutingId } }),
|
|
@@ -4652,13 +5137,17 @@ export class ServiceStatefulRuntime {
|
|
|
4652
5137
|
try {
|
|
4653
5138
|
const current = this.validateActorFence(decoded.actor);
|
|
4654
5139
|
if (decoded.binding.state === 'active') {
|
|
5140
|
+
const sessionOwner = await this.sessionOwnerOf(this.nodeRid, this.nodeGeneration);
|
|
4655
5141
|
this.registry.installSessionBinding(
|
|
4656
5142
|
sessionBindingFromWire(
|
|
4657
5143
|
current.ref,
|
|
4658
5144
|
decoded.sessionRid,
|
|
4659
|
-
|
|
5145
|
+
sessionOwner.nodeRid,
|
|
4660
5146
|
decoded.binding.generation,
|
|
4661
|
-
current.membershipEpoch
|
|
5147
|
+
current.membershipEpoch,
|
|
5148
|
+
sessionOwner.nodeGeneration,
|
|
5149
|
+
sessionOwner.ownerId,
|
|
5150
|
+
sessionOwner.leaseGeneration
|
|
4662
5151
|
)
|
|
4663
5152
|
);
|
|
4664
5153
|
} else {
|
|
@@ -4707,6 +5196,7 @@ export class ServiceStatefulRuntime {
|
|
|
4707
5196
|
| 'actorJoin'
|
|
4708
5197
|
| 'streamBind'
|
|
4709
5198
|
| 'streamUnbind'
|
|
5199
|
+
| 'boundSessionClose'
|
|
4710
5200
|
| 'instanceSpotRequest',
|
|
4711
5201
|
actor: ServiceActorRef | undefined,
|
|
4712
5202
|
reply: readonly Buffer[]
|
|
@@ -5333,6 +5823,25 @@ function isEntrySpotFence(fence: ServiceSpotRouteFence): boolean {
|
|
|
5333
5823
|
);
|
|
5334
5824
|
}
|
|
5335
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
|
+
|
|
5336
5845
|
function operationDeadline(deadlineUnixMs: bigint): {
|
|
5337
5846
|
readonly signal: AbortSignal;
|
|
5338
5847
|
close(): void;
|
|
@@ -5397,6 +5906,13 @@ function actorLocation(actor: ServiceActorState): ActorLocation {
|
|
|
5397
5906
|
};
|
|
5398
5907
|
}
|
|
5399
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
|
+
|
|
5400
5916
|
function activationRefused(): ZLinkFrameworkException {
|
|
5401
5917
|
return new ZLinkFrameworkException(
|
|
5402
5918
|
ZLinkFrameworkErrorKind.Unavailable,
|
|
@@ -5414,14 +5930,14 @@ function failure(error: unknown): ServiceStatefulResult {
|
|
|
5414
5930
|
return internalFrameworkWireReply(ZLinkFrameworkInternalErrorKind.RequestFailed);
|
|
5415
5931
|
}
|
|
5416
5932
|
|
|
5417
|
-
function actorKey(actor: ServiceActorRef): string {
|
|
5933
|
+
function actorKey(actor: Pick<ServiceActorRef, 'actorId' | 'generation'>): string {
|
|
5418
5934
|
return `${actor.actorId}\0${actor.generation}`;
|
|
5419
5935
|
}
|
|
5420
5936
|
|
|
5421
5937
|
function sameSessionBinding(left: ServiceSessionBinding, right: ServiceSessionBinding): boolean {
|
|
5422
5938
|
return (
|
|
5423
5939
|
sameActorRef(left.actor, right.actor) &&
|
|
5424
|
-
left.sessionRid
|
|
5940
|
+
routingIdsEqual(left.sessionRid, right.sessionRid) &&
|
|
5425
5941
|
sameSessionOwnerIdentity(left, right) &&
|
|
5426
5942
|
left.bindingGeneration === right.bindingGeneration
|
|
5427
5943
|
);
|
|
@@ -5430,7 +5946,7 @@ function sameSessionBinding(left: ServiceSessionBinding, right: ServiceSessionBi
|
|
|
5430
5946
|
function sameSessionIdentity(left: ServiceSessionBinding, right: ServiceSessionBinding): boolean {
|
|
5431
5947
|
return (
|
|
5432
5948
|
sameActorRef(left.actor, right.actor) &&
|
|
5433
|
-
left.sessionRid
|
|
5949
|
+
routingIdsEqual(left.sessionRid, right.sessionRid) &&
|
|
5434
5950
|
sameSessionOwnerIdentity(left, right)
|
|
5435
5951
|
);
|
|
5436
5952
|
}
|
|
@@ -5453,9 +5969,7 @@ function sameSessionOwnerIdentity(
|
|
|
5453
5969
|
): boolean {
|
|
5454
5970
|
return (
|
|
5455
5971
|
left.sessionOwnerNodeRid === right.sessionOwnerNodeRid &&
|
|
5456
|
-
left.sessionOwnerNodeGeneration === right.sessionOwnerNodeGeneration
|
|
5457
|
-
left.sessionOwnerId === right.sessionOwnerId &&
|
|
5458
|
-
left.sessionOwnerLeaseGeneration === right.sessionOwnerLeaseGeneration
|
|
5972
|
+
left.sessionOwnerNodeGeneration === right.sessionOwnerNodeGeneration
|
|
5459
5973
|
);
|
|
5460
5974
|
}
|
|
5461
5975
|
|
|
@@ -5493,7 +6007,7 @@ function sessionDeliveryKey(
|
|
|
5493
6007
|
>
|
|
5494
6008
|
): string {
|
|
5495
6009
|
return (
|
|
5496
|
-
`${actorKey(binding.actor)}\0${binding.sessionOwnerNodeRid}\0${binding.sessionRid}` +
|
|
6010
|
+
`${actorKey(binding.actor)}\0${binding.sessionOwnerNodeRid}\0${encodeRoutingIdStorageHex(binding.sessionRid)}` +
|
|
5497
6011
|
`\0${binding.sessionOwnerNodeGeneration ?? ''}` +
|
|
5498
6012
|
`\0${binding.sessionOwnerId ?? ''}` +
|
|
5499
6013
|
`\0${binding.sessionOwnerLeaseGeneration ?? ''}` +
|
|
@@ -5506,8 +6020,8 @@ function exactRetiredDelivery(
|
|
|
5506
6020
|
retired: ServiceRetiredBoundSessionRouteFence
|
|
5507
6021
|
): boolean {
|
|
5508
6022
|
return (
|
|
5509
|
-
binding.sessionRid
|
|
5510
|
-
binding.sessionOwnerNodeRid
|
|
6023
|
+
routingIdsEqual(binding.sessionRid, retired.sessionRid) &&
|
|
6024
|
+
routingIdsEqual(binding.sessionOwnerNodeRid, retired.sessionOwnerNodeRid) &&
|
|
5511
6025
|
binding.sessionOwnerNodeGeneration === retired.sessionOwnerNodeGeneration &&
|
|
5512
6026
|
binding.sessionOwnerId === retired.sessionOwnerId &&
|
|
5513
6027
|
binding.sessionOwnerLeaseGeneration === retired.sessionOwnerLeaseGeneration &&
|
|
@@ -5526,7 +6040,7 @@ function replacementNoticeKey(
|
|
|
5526
6040
|
`\0${record.retiredSession.sessionOwnerNodeGeneration}` +
|
|
5527
6041
|
`\0${record.retiredSession.sessionOwnerId}` +
|
|
5528
6042
|
`\0${record.retiredSession.sessionOwnerLeaseGeneration}` +
|
|
5529
|
-
`\0${record.retiredSession.sessionRid}` +
|
|
6043
|
+
`\0${encodeRoutingIdStorageHex(record.retiredSession.sessionRid)}` +
|
|
5530
6044
|
`\0${record.retiredSession.retiredBindingGeneration}`
|
|
5531
6045
|
);
|
|
5532
6046
|
}
|
|
@@ -5873,7 +6387,8 @@ function acceptsStatefulIngress(command: number): boolean {
|
|
|
5873
6387
|
command !== M6bServiceWireCommand.userSpotClose &&
|
|
5874
6388
|
command !== M6bServiceWireCommand.actorCreate &&
|
|
5875
6389
|
command !== M6bServiceWireCommand.messageFollow &&
|
|
5876
|
-
command !== M6bServiceWireCommand.boundSessionReplaced
|
|
6390
|
+
command !== M6bServiceWireCommand.boundSessionReplaced &&
|
|
6391
|
+
command !== M6bServiceWireCommand.boundSessionClose)
|
|
5877
6392
|
) {
|
|
5878
6393
|
return false;
|
|
5879
6394
|
}
|