@zlink-systems/framework 0.30.3 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/contracts/Actors/ZLinkActor.d.ts +2 -0
- package/dist/contracts/Actors/ZLinkSessionBindingIdentity.d.ts +6 -0
- package/dist/contracts/Actors/ZLinkSessionBindingIdentity.js +2 -0
- package/dist/contracts/Actors/index.d.ts +1 -0
- package/dist/contracts/Actors/index.js +1 -0
- package/dist/contracts/Common/CoreTypes.d.ts +1 -0
- package/dist/contracts/Common/HandlerMessageType.d.ts +8 -0
- package/dist/contracts/Common/HandlerMessageType.js +41 -0
- package/dist/contracts/Common/ZLinkEncodedPayload.d.ts +1 -1
- package/dist/contracts/Common/ZLinkEncodedPayload.js +12 -20
- package/dist/contracts/Common/ZLinkMessage.js +11 -7
- package/dist/contracts/Common/encoded-payload-storage.d.ts +0 -2
- package/dist/contracts/Common/encoded-payload-storage.js +4 -16
- package/dist/contracts/Common/index.d.ts +1 -1
- package/dist/contracts/Common/payload-slab.d.ts +4 -0
- package/dist/contracts/Common/payload-slab.js +30 -0
- package/dist/contracts/Configuration/RegistrationBuilders.js +15 -9
- package/dist/contracts/Configuration/RegistrationTypes.d.ts +19 -0
- package/dist/contracts/Handlers/Contexts.d.ts +1 -0
- package/dist/contracts/Locations/Authority.d.ts +2 -0
- package/dist/internal.d.ts +9 -0
- package/dist/internal.js +23 -1
- package/dist/nest-integration.d.ts +1 -0
- package/dist/nest-integration.js +3 -1
- package/dist/runtime/actors/actor-client.d.ts +6 -2
- package/dist/runtime/actors/actor-client.js +120 -82
- package/dist/runtime/actors/actor-context.js +16 -5
- package/dist/runtime/actors/actor-execution-context.js +2 -2
- package/dist/runtime/actors/actor-handoff.d.ts +12 -1
- package/dist/runtime/actors/actor-handoff.js +79 -34
- package/dist/runtime/actors/actor-join-deferred-scope.js +6 -6
- package/dist/runtime/actors/actor-local-native-join.d.ts +2 -2
- package/dist/runtime/actors/actor-local-native-join.js +19 -17
- package/dist/runtime/actors/actor-mailbox.d.ts +1 -1
- package/dist/runtime/actors/actor-remote-joiner.d.ts +2 -2
- package/dist/runtime/actors/actor-remote-joiner.js +4 -4
- package/dist/runtime/actors/actor-routed-join-transport.d.ts +9 -3
- package/dist/runtime/actors/actor-routed-json-request.d.ts +3 -0
- package/dist/runtime/actors/actor-runtime-contracts.d.ts +2 -2
- package/dist/runtime/actors/actor-runtime-state.d.ts +1 -1
- package/dist/runtime/actors/spot-actor-dispatch.d.ts +9 -5
- package/dist/runtime/actors/spot-actor-dispatch.js +60 -38
- package/dist/runtime/admission.d.ts +6 -4
- package/dist/runtime/admission.js +21 -10
- package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +1 -0
- package/dist/runtime/application-jobs/application-ingress-record-owner.js +3 -0
- package/dist/runtime/backend/contracts/index.d.ts +26 -10
- package/dist/runtime/backend/mesh-completion-table.d.ts +7 -1
- package/dist/runtime/backend/mesh-completion-table.js +84 -20
- package/dist/runtime/backend/mesh-dispatch-pump.d.ts +19 -6
- package/dist/runtime/backend/mesh-dispatch-pump.js +246 -147
- package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +3 -2
- package/dist/runtime/backend/node/node-backend-adapter-support.js +27 -19
- package/dist/runtime/backend/node/node-dealer-channel-client-transport.d.ts +5 -2
- package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +70 -13
- package/dist/runtime/backend/node/node-event-loop-poller.d.ts +2 -2
- package/dist/runtime/backend/node/node-event-loop-poller.js +4 -3
- package/dist/runtime/backend/node/node-mesh-backend-adapter.js +5 -1
- package/dist/runtime/backend/node/node-raw-binding-port.d.ts +7 -2
- package/dist/runtime/backend/node/node-raw-binding-port.js +99 -63
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +34 -16
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +210 -106
- package/dist/runtime/backend/node/node-socket-backend-adapter.js +20 -6
- package/dist/runtime/backend/raw-binding-port.d.ts +6 -3
- package/dist/runtime/channels/channel-clients.js +3 -3
- package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +11 -7
- package/dist/runtime/channels/channel-dispatch-pipeline.js +40 -22
- package/dist/runtime/channels/channel-dispatch-services.d.ts +3 -1
- package/dist/runtime/channels/channel-dispatch-services.js +7 -6
- package/dist/runtime/channels/channel-dispatchers.d.ts +13 -6
- package/dist/runtime/channels/channel-dispatchers.js +57 -36
- package/dist/runtime/channels/channel-envelope.d.ts +2 -2
- package/dist/runtime/channels/channel-envelope.js +609 -83
- package/dist/runtime/channels/channel-multipart.d.ts +1 -0
- package/dist/runtime/channels/channel-multipart.js +10 -6
- package/dist/runtime/channels/channel-outbound-operations.d.ts +6 -9
- package/dist/runtime/channels/channel-outbound-operations.js +107 -105
- package/dist/runtime/channels/channel-receive-loops.d.ts +9 -7
- package/dist/runtime/channels/channel-receive-loops.js +126 -90
- package/dist/runtime/channels/channel-runtime-lifecycle.d.ts +23 -6
- package/dist/runtime/channels/channel-runtime-lifecycle.js +47 -38
- package/dist/runtime/channels/channel-runtime-manager.d.ts +13 -6
- package/dist/runtime/channels/channel-runtime-manager.js +32 -14
- package/dist/runtime/channels/channel-socket-registry.d.ts +28 -4
- package/dist/runtime/channels/channel-socket-registry.js +9 -1
- package/dist/runtime/channels/channel-transports.d.ts +23 -16
- package/dist/runtime/channels/channel-transports.js +139 -119
- package/dist/runtime/channels/dispatch-error-reporter.d.ts +4 -4
- package/dist/runtime/channels/dispatch-error-reporter.js +5 -5
- package/dist/runtime/channels/fanout-location-runtime.d.ts +1 -1
- package/dist/runtime/channels/local-spot-route-transport.d.ts +5 -2
- package/dist/runtime/channels/local-spot-route-transport.js +15 -18
- package/dist/runtime/channels/router-socket-spot-route-transport.d.ts +6 -3
- package/dist/runtime/channels/router-socket-spot-route-transport.js +33 -19
- package/dist/runtime/channels/source-spot-router.d.ts +6 -3
- package/dist/runtime/channels/source-spot-router.js +34 -23
- package/dist/runtime/channels/spot-node-route-transport.d.ts +6 -3
- package/dist/runtime/channels/spot-node-route-transport.js +23 -17
- package/dist/runtime/channels/spot-route-bridge-transport.d.ts +6 -3
- package/dist/runtime/channels/spot-route-bridge-transport.js +38 -24
- package/dist/runtime/channels/spot-route-dispatch-strategy.d.ts +8 -4
- package/dist/runtime/channels/spot-route-dispatch-strategy.js +36 -23
- package/dist/runtime/diagnostics/dispatch-error-details.js +9 -3
- package/dist/runtime/diagnostics/message-flow-bootstrap.d.ts +1 -0
- package/dist/runtime/diagnostics/message-flow-bootstrap.js +8 -0
- package/dist/runtime/diagnostics/message-flow-delivery.d.ts +47 -0
- package/dist/runtime/diagnostics/message-flow-delivery.js +149 -0
- package/dist/runtime/diagnostics/message-flow-record.d.ts +50 -0
- package/dist/runtime/diagnostics/message-flow-record.js +228 -0
- package/dist/runtime/diagnostics/message-flow-worker.d.ts +1 -0
- package/dist/runtime/diagnostics/message-flow-worker.js +118 -0
- package/dist/runtime/diagnostics/message-flow.d.ts +7 -19
- package/dist/runtime/diagnostics/message-flow.js +42 -290
- package/dist/runtime/diagnostics/runtime-metrics.d.ts +4 -3
- package/dist/runtime/diagnostics/runtime-metrics.js +8 -4
- package/dist/runtime/execution/index.d.ts +1 -0
- package/dist/runtime/execution/index.js +6 -3
- package/dist/runtime/execution/serial-execution-queue.d.ts +26 -13
- package/dist/runtime/execution/serial-execution-queue.js +75 -71
- package/dist/runtime/foundation/durable-service-request.d.ts +12 -3
- package/dist/runtime/foundation/durable-service-request.js +158 -73
- package/dist/runtime/foundation/operation-registry.d.ts +33 -3
- package/dist/runtime/foundation/operation-registry.js +272 -33
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +62 -23
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +373 -194
- package/dist/runtime/foundation/request-runtime.d.ts +17 -0
- package/dist/runtime/foundation/request-runtime.js +163 -0
- package/dist/runtime/foundation/service-discovery-registry.d.ts +4 -0
- package/dist/runtime/foundation/service-discovery-registry.js +6 -0
- package/dist/runtime/foundation/service-liveness-registry.d.ts +1 -1
- package/dist/runtime/foundation/service-mailbox.d.ts +4 -2
- package/dist/runtime/foundation/service-mailbox.js +4 -1
- package/dist/runtime/foundation/service-relocation-object-owner.d.ts +2 -2
- package/dist/runtime/foundation/service-relocation-object-owner.js +12 -5
- package/dist/runtime/foundation/service-runtime-contracts.d.ts +7 -2
- package/dist/runtime/foundation/service-stateful-registry.d.ts +6 -2
- package/dist/runtime/foundation/service-stateful-registry.js +38 -17
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +91 -26
- package/dist/runtime/foundation/service-stateful-runtime.js +245 -120
- package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +3 -0
- package/dist/runtime/foundation/service-stateful-wire-codec.js +15 -0
- package/dist/runtime/foundation/service-topology-registry.d.ts +2 -0
- package/dist/runtime/foundation/service-topology-registry.js +13 -3
- package/dist/runtime/foundation/service-wire-constants.generated.d.ts +1 -0
- package/dist/runtime/foundation/service-wire-constants.generated.js +12 -0
- package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +5 -1
- package/dist/runtime/foundation/service-wire-m6a-codec.js +50 -15
- package/dist/runtime/host/actor-packet-relay.d.ts +5 -6
- package/dist/runtime/host/actor-packet-relay.js +58 -57
- package/dist/runtime/host/actor-placement-coordinator.d.ts +1 -1
- package/dist/runtime/host/actor-runtime-options-factory.d.ts +2 -0
- package/dist/runtime/host/actor-runtime-options-factory.js +3 -2
- package/dist/runtime/host/actor-transfer-runtime.d.ts +3 -3
- package/dist/runtime/host/application-job-queue.d.ts +7 -5
- package/dist/runtime/host/application-job-queue.js +33 -21
- package/dist/runtime/host/bound-session-relay.js +0 -1
- package/dist/runtime/host/channel-runtime-options-factory.d.ts +2 -0
- package/dist/runtime/host/channel-runtime-options-factory.js +1 -0
- package/dist/runtime/host/entry-actor-runtime.d.ts +5 -2
- package/dist/runtime/host/entry-actor-runtime.js +2 -2
- package/dist/runtime/host/index.d.ts +106 -37
- package/dist/runtime/host/index.js +303 -151
- package/dist/runtime/host/location-runtime-owner.d.ts +2 -2
- package/dist/runtime/host/location-runtime-owner.js +1 -1
- package/dist/runtime/host/service-relocation-host-runtime.d.ts +195 -45
- package/dist/runtime/host/service-relocation-host-runtime.js +257 -144
- package/dist/runtime/host/spot-address-transport.d.ts +3 -0
- package/dist/runtime/host/spot-address-transport.js +73 -76
- package/dist/runtime/host/spot-runtime-options-factory.js +2 -0
- package/dist/runtime/host/user-spot-creation-coordinator.d.ts +3 -1
- package/dist/runtime/host/user-spot-creation-coordinator.js +49 -21
- package/dist/runtime/internal-observation.d.ts +1 -0
- package/dist/runtime/internal-observation.js +5 -0
- package/dist/runtime/locations/actor-location-claims.d.ts +1 -1
- package/dist/runtime/locations/actor-location-claims.js +15 -6
- package/dist/runtime/locations/auto-connect-loop.d.ts +0 -1
- package/dist/runtime/locations/auto-connect-loop.js +25 -19
- package/dist/runtime/locations/auto-connect-planner.js +2 -5
- package/dist/runtime/locations/auto-connect-reconciler.d.ts +1 -0
- package/dist/runtime/locations/auto-connect-reconciler.js +35 -23
- package/dist/runtime/locations/in-memory-authority-store.d.ts +1 -0
- package/dist/runtime/locations/in-memory-authority-store.js +15 -5
- package/dist/runtime/locations/in-memory-location-store.d.ts +0 -2
- package/dist/runtime/locations/in-memory-location-store.js +1 -38
- package/dist/runtime/locations/lifecycle.d.ts +1 -1
- package/dist/runtime/locations/lifecycle.js +2 -2
- package/dist/runtime/locations/location-store-repository.d.ts +0 -1
- package/dist/runtime/locations/location-store-repository.js +163 -246
- package/dist/runtime/locations/resolvers.d.ts +8 -1
- package/dist/runtime/locations/resolvers.js +16 -2
- package/dist/runtime/locations/runtime.d.ts +3 -3
- package/dist/runtime/locations/runtime.js +12 -16
- package/dist/runtime/messaging/creation-payload-codec.js +8 -7
- package/dist/runtime/messaging/framework-json-v1.d.ts +1 -1
- package/dist/runtime/messaging/framework-json-v1.js +6 -2
- package/dist/runtime/messaging/payload-codec.js +21 -35
- package/dist/runtime/messaging/request-deadline.d.ts +4 -2
- package/dist/runtime/messaging/request-deadline.js +40 -25
- package/dist/runtime/messaging/request-result.d.ts +14 -1
- package/dist/runtime/messaging/request-result.js +74 -13
- package/dist/runtime/protocol/service_wire_codec.generated.d.ts +4 -0
- package/dist/runtime/protocol/service_wire_codec.generated.js +17 -7
- package/dist/runtime/spots/index.d.ts +28 -7
- package/dist/runtime/spots/index.js +31 -6
- package/dist/runtime/spots/spot-activation-registry.d.ts +3 -2
- package/dist/runtime/spots/spot-activation-registry.js +13 -8
- package/dist/runtime/spots/spot-activation-state.d.ts +2 -0
- package/dist/runtime/spots/spot-activation-state.js +1 -0
- package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +9 -4
- package/dist/runtime/spots/spot-actor-admission-coordinator.js +33 -12
- package/dist/runtime/spots/spot-actor-ingress-admission.d.ts +40 -0
- package/dist/runtime/spots/spot-actor-ingress-admission.js +78 -0
- package/dist/runtime/spots/spot-actor-membership.js +3 -3
- package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +16 -43
- package/dist/runtime/spots/spot-actor-packet-dispatch.js +56 -113
- package/dist/runtime/spots/spot-actor-packet-drain.d.ts +1 -0
- package/dist/runtime/spots/spot-actor-packet-drain.js +12 -5
- package/dist/runtime/spots/spot-entry-activation.d.ts +4 -3
- package/dist/runtime/spots/spot-entry-activation.js +7 -12
- package/dist/runtime/spots/spot-handler-registry.d.ts +5 -1
- package/dist/runtime/spots/spot-handler-registry.js +31 -15
- package/dist/runtime/spots/spot-manager-public.js +3 -2
- package/dist/runtime/spots/spot-node-runtime-manager.d.ts +11 -6
- package/dist/runtime/spots/spot-node-runtime-manager.js +6 -1
- package/dist/runtime/spots/spot-outbound.d.ts +8 -2
- package/dist/runtime/spots/spot-outbound.js +5 -5
- package/dist/runtime/spots/spot-route-packet-dispatch.js +13 -8
- package/dist/runtime/spots/spot-routed-spot-packet-dispatch.d.ts +3 -3
- package/dist/runtime/spots/spot-routed-spot-packet-dispatch.js +9 -5
- package/dist/runtime/spots/spot-runtime-ports.d.ts +2 -2
- package/dist/runtime/spots/spot-serial-executor.d.ts +2 -1
- package/dist/runtime/spots/spot-serial-turn-executor.js +20 -26
- package/dist/runtime/spots/spot-subscription-dispatch.js +5 -2
- package/dist/runtime/spots/spot-timer.d.ts +1 -1
- package/dist/runtime/streams/actor-session-binding-registry.d.ts +11 -3
- package/dist/runtime/streams/actor-session-binding-registry.js +28 -0
- package/dist/runtime/streams/bound-actor-relay-sender.d.ts +2 -2
- package/dist/runtime/streams/bound-actor-relay-sender.js +3 -4
- package/dist/runtime/streams/bound-session-service.js +6 -7
- package/dist/runtime/streams/index.d.ts +3 -3
- package/dist/runtime/streams/index.js +22 -2
- package/dist/runtime/streams/managed-stream.js +2 -1
- package/dist/runtime/streams/session-calls.d.ts +1 -1
- package/dist/runtime/streams/session-calls.js +3 -9
- package/dist/runtime/streams/session-context.d.ts +2 -2
- package/dist/runtime/streams/session-context.js +2 -2
- package/dist/runtime/streams/stream-session-runtime.d.ts +1 -0
- package/dist/runtime/streams/stream-session-runtime.js +64 -33
- package/package.json +3 -3
- package/src/contracts/Actors/ZLinkActor.ts +6 -0
- package/src/contracts/Actors/ZLinkSessionBindingIdentity.ts +7 -0
- package/src/contracts/Actors/index.ts +1 -0
- package/src/contracts/Common/CoreTypes.ts +1 -0
- package/src/contracts/Common/HandlerMessageType.ts +50 -0
- package/src/contracts/Common/ZLinkEncodedPayload.ts +13 -25
- package/src/contracts/Common/ZLinkMessage.ts +11 -7
- package/src/contracts/Common/encoded-payload-storage.ts +4 -16
- package/src/contracts/Common/index.ts +1 -1
- package/src/contracts/Common/payload-slab.ts +29 -0
- package/src/contracts/Configuration/RegistrationBuilders.ts +29 -15
- package/src/contracts/Configuration/RegistrationTypes.ts +19 -0
- package/src/contracts/Handlers/Contexts.ts +1 -0
- package/src/contracts/Locations/Authority.ts +7 -1
- package/src/internal.ts +17 -0
- package/src/nest-integration.ts +1 -0
- package/src/runtime/actors/actor-client.ts +257 -163
- package/src/runtime/actors/actor-context.ts +26 -14
- package/src/runtime/actors/actor-execution-context.ts +2 -2
- package/src/runtime/actors/actor-handoff.ts +86 -51
- package/src/runtime/actors/actor-join-deferred-scope.ts +6 -6
- package/src/runtime/actors/actor-local-native-join.ts +38 -17
- package/src/runtime/actors/actor-mailbox.ts +1 -1
- package/src/runtime/actors/actor-remote-joiner.ts +8 -4
- package/src/runtime/actors/actor-routed-join-transport.ts +13 -5
- package/src/runtime/actors/actor-routed-json-request.ts +3 -0
- package/src/runtime/actors/actor-runtime-contracts.ts +4 -2
- package/src/runtime/actors/actor-runtime-state.ts +1 -1
- package/src/runtime/actors/spot-actor-dispatch.ts +70 -68
- package/src/runtime/admission.ts +42 -15
- package/src/runtime/application-jobs/application-ingress-record-owner.ts +4 -0
- package/src/runtime/backend/contracts/index.ts +77 -10
- package/src/runtime/backend/mesh-completion-table.ts +106 -15
- package/src/runtime/backend/mesh-dispatch-pump.ts +294 -146
- package/src/runtime/backend/node/node-backend-adapter-support.ts +42 -32
- package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +91 -34
- package/src/runtime/backend/node/node-event-loop-poller.ts +14 -5
- package/src/runtime/backend/node/node-mesh-backend-adapter.ts +5 -1
- package/src/runtime/backend/node/node-raw-binding-port.ts +180 -69
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +386 -157
- package/src/runtime/backend/node/node-socket-backend-adapter.ts +56 -11
- package/src/runtime/backend/raw-binding-port.ts +13 -5
- package/src/runtime/channels/channel-clients.ts +26 -11
- package/src/runtime/channels/channel-dispatch-pipeline.ts +77 -65
- package/src/runtime/channels/channel-dispatch-services.ts +7 -12
- package/src/runtime/channels/channel-dispatchers.ts +93 -39
- package/src/runtime/channels/channel-envelope.ts +680 -144
- package/src/runtime/channels/channel-multipart.ts +17 -13
- package/src/runtime/channels/channel-outbound-operations.ts +204 -178
- package/src/runtime/channels/channel-receive-loops.ts +160 -107
- package/src/runtime/channels/channel-runtime-lifecycle.ts +64 -59
- package/src/runtime/channels/channel-runtime-manager.ts +74 -27
- package/src/runtime/channels/channel-socket-registry.ts +20 -5
- package/src/runtime/channels/channel-transports.ts +414 -287
- package/src/runtime/channels/dispatch-error-reporter.ts +4 -9
- package/src/runtime/channels/local-spot-route-transport.ts +27 -29
- package/src/runtime/channels/router-socket-spot-route-transport.ts +63 -32
- package/src/runtime/channels/source-spot-router.ts +75 -38
- package/src/runtime/channels/spot-node-route-transport.ts +67 -37
- package/src/runtime/channels/spot-route-bridge-transport.ts +62 -30
- package/src/runtime/channels/spot-route-dispatch-strategy.ts +105 -34
- package/src/runtime/diagnostics/dispatch-error-details.ts +10 -2
- package/src/runtime/diagnostics/message-flow-bootstrap.ts +6 -0
- package/src/runtime/diagnostics/message-flow-delivery.ts +171 -0
- package/src/runtime/diagnostics/message-flow-record.ts +295 -0
- package/src/runtime/diagnostics/message-flow-worker.ts +146 -0
- package/src/runtime/diagnostics/message-flow.ts +49 -362
- package/src/runtime/diagnostics/runtime-metrics.ts +10 -4
- package/src/runtime/execution/index.ts +6 -0
- package/src/runtime/execution/serial-execution-queue.ts +94 -96
- package/src/runtime/foundation/durable-service-request.ts +212 -89
- package/src/runtime/foundation/operation-registry.ts +287 -38
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +617 -292
- package/src/runtime/foundation/request-runtime.ts +227 -0
- package/src/runtime/foundation/service-discovery-registry.ts +7 -0
- package/src/runtime/foundation/service-liveness-registry.ts +1 -1
- package/src/runtime/foundation/service-mailbox.ts +7 -2
- package/src/runtime/foundation/service-relocation-object-owner.ts +21 -6
- package/src/runtime/foundation/service-runtime-contracts.ts +12 -2
- package/src/runtime/foundation/service-stateful-registry.ts +52 -19
- package/src/runtime/foundation/service-stateful-runtime.ts +365 -198
- package/src/runtime/foundation/service-stateful-wire-codec.ts +18 -0
- package/src/runtime/foundation/service-topology-registry.ts +21 -3
- package/src/runtime/foundation/service-wire-constants.generated.ts +10 -0
- package/src/runtime/foundation/service-wire-m6a-codec.ts +69 -17
- package/src/runtime/host/actor-packet-relay.ts +160 -156
- package/src/runtime/host/actor-placement-coordinator.ts +1 -1
- package/src/runtime/host/actor-runtime-options-factory.ts +7 -2
- package/src/runtime/host/actor-transfer-runtime.ts +3 -3
- package/src/runtime/host/application-job-queue.ts +56 -27
- package/src/runtime/host/bound-session-relay.ts +0 -1
- package/src/runtime/host/channel-runtime-options-factory.ts +3 -0
- package/src/runtime/host/entry-actor-runtime.ts +14 -6
- package/src/runtime/host/index.ts +388 -113
- package/src/runtime/host/location-runtime-owner.ts +3 -3
- package/src/runtime/host/service-relocation-host-runtime.ts +380 -252
- package/src/runtime/host/spot-address-transport.ts +110 -95
- package/src/runtime/host/spot-runtime-options-factory.ts +3 -0
- package/src/runtime/host/user-spot-creation-coordinator.ts +69 -22
- package/src/runtime/internal-observation.ts +2 -0
- package/src/runtime/locations/actor-location-claims.ts +42 -16
- package/src/runtime/locations/auto-connect-loop.ts +24 -16
- package/src/runtime/locations/auto-connect-planner.ts +2 -5
- package/src/runtime/locations/auto-connect-reconciler.ts +37 -24
- package/src/runtime/locations/in-memory-authority-store.ts +27 -7
- package/src/runtime/locations/in-memory-location-store.ts +1 -74
- package/src/runtime/locations/lifecycle.ts +4 -2
- package/src/runtime/locations/location-store-repository.ts +228 -308
- package/src/runtime/locations/resolvers.ts +20 -8
- package/src/runtime/locations/runtime.ts +16 -28
- package/src/runtime/messaging/creation-payload-codec.ts +12 -10
- package/src/runtime/messaging/framework-json-v1.ts +10 -1
- package/src/runtime/messaging/payload-codec.ts +50 -75
- package/src/runtime/messaging/request-deadline.ts +47 -39
- package/src/runtime/messaging/request-result.ts +88 -17
- package/src/runtime/protocol/service_wire_codec.generated.ts +18 -8
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
- package/src/runtime/spots/index.ts +57 -10
- package/src/runtime/spots/spot-activation-registry.ts +12 -8
- package/src/runtime/spots/spot-activation-state.ts +2 -0
- package/src/runtime/spots/spot-actor-admission-coordinator.ts +62 -37
- package/src/runtime/spots/spot-actor-ingress-admission.ts +157 -0
- package/src/runtime/spots/spot-actor-membership.ts +8 -5
- package/src/runtime/spots/spot-actor-packet-dispatch.ts +122 -206
- package/src/runtime/spots/spot-actor-packet-drain.ts +21 -6
- package/src/runtime/spots/spot-entry-activation.ts +25 -35
- package/src/runtime/spots/spot-handler-registry.ts +72 -15
- package/src/runtime/spots/spot-manager-public.ts +4 -2
- package/src/runtime/spots/spot-node-runtime-manager.ts +16 -7
- package/src/runtime/spots/spot-outbound.ts +26 -10
- package/src/runtime/spots/spot-route-packet-dispatch.ts +18 -8
- package/src/runtime/spots/spot-routed-spot-packet-dispatch.ts +14 -8
- package/src/runtime/spots/spot-runtime-ports.ts +5 -2
- package/src/runtime/spots/spot-serial-executor.ts +1 -1
- package/src/runtime/spots/spot-serial-turn-executor.ts +25 -40
- package/src/runtime/spots/spot-subscription-dispatch.ts +9 -2
- package/src/runtime/spots/spot-timer.ts +1 -1
- package/src/runtime/streams/actor-session-binding-registry.ts +46 -3
- package/src/runtime/streams/bound-actor-relay-sender.ts +16 -5
- package/src/runtime/streams/bound-session-service.ts +10 -7
- package/src/runtime/streams/index.ts +30 -4
- package/src/runtime/streams/managed-stream.ts +2 -1
- package/src/runtime/streams/session-calls.ts +9 -14
- package/src/runtime/streams/session-context.ts +6 -11
- package/src/runtime/streams/stream-session-runtime.ts +79 -40
- package/dist/runtime/spots/spot-lifecycle-metrics.d.ts +0 -8
- package/dist/runtime/spots/spot-lifecycle-metrics.js +0 -17
- package/src/runtime/spots/spot-lifecycle-metrics.ts +0 -16
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createInternalFrameworkException,
|
|
3
|
+
ZLinkFrameworkInternalErrorKind
|
|
4
|
+
} from '../framework-errors-internal';
|
|
5
|
+
import type { ConnectionAttempt } from '@zlink-systems/zlink';
|
|
6
|
+
import { admitServiceRequest } from './durable-service-request';
|
|
7
|
+
import { remainingRequestTimeout, requestTerminalError } from '../messaging/request-deadline';
|
|
8
|
+
import type { OperationHandle } from './operation-registry';
|
|
9
|
+
import type { RequestRuntime } from './request-runtime';
|
|
10
|
+
import { inspectRuntime } from '../internal-observation';
|
|
1
11
|
import { constants as osConstants } from 'node:os';
|
|
2
12
|
import { ApplicationIngressRecordOwner } from '../application-jobs/application-ingress-record-owner';
|
|
3
13
|
import type {
|
|
@@ -12,6 +22,7 @@ import type {
|
|
|
12
22
|
ZLinkRawRouterPort
|
|
13
23
|
} from '../backend/raw-binding-port';
|
|
14
24
|
import { submitToRequestResult } from '../messaging/submission-result';
|
|
25
|
+
import { copyPayloadBytes } from '../../contracts/Common/payload-slab';
|
|
15
26
|
import { RequestResult, SubmitResult, isZLinkBackendResultError } from '../backend/runtime-values';
|
|
16
27
|
import { enumWireRejectReason } from '../protocol/service_wire_codec.generated';
|
|
17
28
|
import { SERVICE_WIRE_PREFIX_SIZE } from './service-wire-binary-primitives';
|
|
@@ -36,9 +47,11 @@ import {
|
|
|
36
47
|
SERVICE_WIRE_MAGIC,
|
|
37
48
|
SERVICE_WIRE_MAJOR,
|
|
38
49
|
ServiceWireCommand,
|
|
39
|
-
ServiceWireFrameworkErrorCode
|
|
50
|
+
ServiceWireFrameworkErrorCode,
|
|
51
|
+
recoverRequestCorrelation
|
|
40
52
|
} from './service-wire-constants.generated';
|
|
41
53
|
import {
|
|
54
|
+
decodeApplicationPayload,
|
|
42
55
|
decodeApplicationPayloadView,
|
|
43
56
|
decodeChannelRequestHeader,
|
|
44
57
|
decodeChannelSendHeader,
|
|
@@ -81,10 +94,7 @@ export type RawServiceRequestObserver = OperationObserver<RawServiceRequestResul
|
|
|
81
94
|
* terminal reaches the observer. Local and Promise-contract requests carry
|
|
82
95
|
* the Promise.
|
|
83
96
|
*/
|
|
84
|
-
export interface RawServiceObservedRequest {
|
|
85
|
-
readonly id: bigint;
|
|
86
|
-
readonly promise?: Promise<RawServiceRequestResult>;
|
|
87
|
-
}
|
|
97
|
+
export interface RawServiceObservedRequest extends OperationHandle<RawServiceRequestResult> {}
|
|
88
98
|
|
|
89
99
|
type RawServiceChannelTargetSelection =
|
|
90
100
|
| { readonly kind: 'selected'; readonly peer: AdmittedServicePeer }
|
|
@@ -112,7 +122,7 @@ export interface RawServiceIngressRecord {
|
|
|
112
122
|
readonly sourceNodeGeneration?: bigint;
|
|
113
123
|
readonly sourceRoute?: Uint8Array;
|
|
114
124
|
readonly requestSequence?: bigint;
|
|
115
|
-
readonly reply?:
|
|
125
|
+
readonly reply?: ServiceMailboxRecord['reply'];
|
|
116
126
|
readonly parts: readonly Buffer[];
|
|
117
127
|
readonly applicationJobOwner?: ApplicationIngressRecordOwner;
|
|
118
128
|
}
|
|
@@ -122,6 +132,7 @@ export type RawServiceIngressHandler = (
|
|
|
122
132
|
) => RawServicePumpResult | undefined | Promise<RawServicePumpResult | undefined>;
|
|
123
133
|
|
|
124
134
|
export interface RawServiceMeshRuntimeOptions {
|
|
135
|
+
readonly requests?: RequestRuntime;
|
|
125
136
|
readonly onReceiveReady?: () => void;
|
|
126
137
|
readonly infrastructureTaskRunner?: import('../spots/spot-actor-join-dispatch').ZLinkDetachedTaskRunner;
|
|
127
138
|
readonly onPendingOperationsChanged?: () => void;
|
|
@@ -166,6 +177,7 @@ const livenessCodec = createServiceWireCodec({
|
|
|
166
177
|
interface ControlSendState {
|
|
167
178
|
task?: Promise<boolean>;
|
|
168
179
|
next?: () => boolean | Promise<boolean>;
|
|
180
|
+
// HELLO send eligibility; admission suspension does not end the exchange.
|
|
169
181
|
current?: () => boolean;
|
|
170
182
|
// Admission owns the first HELLO until ADMIT or route termination.
|
|
171
183
|
hello?: Uint8Array;
|
|
@@ -176,12 +188,26 @@ interface ControlSendState {
|
|
|
176
188
|
* It owns protocol, admission, mailbox, request and liveness state.
|
|
177
189
|
*/
|
|
178
190
|
export class RawServiceMeshRuntime {
|
|
191
|
+
[inspectRuntime](nodeRoutingId?: string) {
|
|
192
|
+
return Object.freeze({
|
|
193
|
+
pendingControlSends: this.controlSends.size,
|
|
194
|
+
selectedRouteGeneration:
|
|
195
|
+
nodeRoutingId === undefined ? undefined : this.selectedRoutes.get(nodeRoutingId),
|
|
196
|
+
peerRouteObservers: this.peerRouteChanged.size,
|
|
197
|
+
peerConnectionIntentObservers: this.peerConnectionIntentRemoved.size,
|
|
198
|
+
expectedPeers: this.expectedPeers.size
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
179
202
|
readonly topology: ServiceTopologyRegistry;
|
|
180
203
|
readonly mailbox: ServiceMailbox;
|
|
181
204
|
readonly liveness: ServiceLivenessRegistry;
|
|
182
205
|
|
|
206
|
+
private readonly requestOwner: RequestRuntime | undefined;
|
|
207
|
+
private readonly releaseOperations: () => void;
|
|
208
|
+
|
|
183
209
|
private readonly operations: OperationRegistry<RawServiceRequestResult, number>;
|
|
184
|
-
|
|
210
|
+
readonly expectedPeers = new Map<
|
|
185
211
|
string,
|
|
186
212
|
{
|
|
187
213
|
readonly meshName: string;
|
|
@@ -198,7 +224,8 @@ export class RawServiceMeshRuntime {
|
|
|
198
224
|
* The last observed Core selected route of each RID (Core ROUTER §10.1):
|
|
199
225
|
* RID -> route generation. Only `observeSelectedRoutes` replaces it.
|
|
200
226
|
*/
|
|
201
|
-
|
|
227
|
+
selectedRoutes = new Map<string, bigint>();
|
|
228
|
+
private connectionAttempts: readonly ConnectionAttempt[] = [];
|
|
202
229
|
private readonly bindingPort: ZLinkRawBindingPort;
|
|
203
230
|
private readonly applicationJobQueue: ApplicationJobQueuePort;
|
|
204
231
|
private readonly nodeMailboxOwner: string;
|
|
@@ -207,7 +234,7 @@ export class RawServiceMeshRuntime {
|
|
|
207
234
|
private receiveAcquisition?: Promise<ApplicationJobPermitPort>;
|
|
208
235
|
private receivePermit?: ApplicationJobPermitPort;
|
|
209
236
|
private receiveRotation?: ApplicationJobReceiveRotationPort;
|
|
210
|
-
|
|
237
|
+
readonly controlSends = new Map<string, ControlSendState>();
|
|
211
238
|
private infrastructureFailure?: { readonly error: unknown };
|
|
212
239
|
private readonly onReceiveReady?: () => void;
|
|
213
240
|
private readonly infrastructureTaskRunner?: RawServiceMeshRuntimeOptions['infrastructureTaskRunner'];
|
|
@@ -218,16 +245,25 @@ export class RawServiceMeshRuntime {
|
|
|
218
245
|
private readonly resolveAdvertisedEndpoint?: RawServiceMeshRuntimeOptions['resolveAdvertisedEndpoint'];
|
|
219
246
|
private descriptor: ServiceNodeDescriptor;
|
|
220
247
|
private host?: ZLinkRawHostPort;
|
|
221
|
-
|
|
248
|
+
router?: ZLinkRawRouterPort;
|
|
222
249
|
private nextCorrelation = 1n;
|
|
223
250
|
private serviceIngress?: RawServiceIngressHandler;
|
|
224
251
|
private serviceIngressAccepts?: (command: number) => boolean;
|
|
225
252
|
private closed = false;
|
|
226
253
|
|
|
227
254
|
constructor(options: RawServiceMeshRuntimeOptions) {
|
|
255
|
+
this.requestOwner = options.requests;
|
|
228
256
|
this.onReceiveReady = options.onReceiveReady;
|
|
229
257
|
this.infrastructureTaskRunner = options.infrastructureTaskRunner;
|
|
230
|
-
this.operations =
|
|
258
|
+
this.operations =
|
|
259
|
+
options.requests?.operations<RawServiceRequestResult, number>() ??
|
|
260
|
+
new OperationRegistry(undefined, options.onPendingOperationsChanged, (reason) =>
|
|
261
|
+
requestTerminalError('Request', reason)
|
|
262
|
+
);
|
|
263
|
+
this.releaseOperations =
|
|
264
|
+
options.requests === undefined
|
|
265
|
+
? () => this.operations.close('Raw service runtime closed.')
|
|
266
|
+
: () => {};
|
|
231
267
|
this.descriptor = options.descriptor;
|
|
232
268
|
this.nodeMailboxOwner = `node:${options.descriptor.nodeRoutingId}`;
|
|
233
269
|
this.topology = new ServiceTopologyRegistry(options.descriptor);
|
|
@@ -293,6 +329,7 @@ export class RawServiceMeshRuntime {
|
|
|
293
329
|
lifecycleGeneration: expected.lifecycleGeneration
|
|
294
330
|
})
|
|
295
331
|
);
|
|
332
|
+
this.notifyPeerRouteChanged(expected.nodeRoutingId);
|
|
296
333
|
}
|
|
297
334
|
|
|
298
335
|
connectPeerByRoutingId(
|
|
@@ -312,6 +349,7 @@ export class RawServiceMeshRuntime {
|
|
|
312
349
|
lifecycleGeneration
|
|
313
350
|
})
|
|
314
351
|
);
|
|
352
|
+
this.notifyPeerRouteChanged(nodeRoutingId);
|
|
315
353
|
}
|
|
316
354
|
|
|
317
355
|
expectPeerByRoutingId(
|
|
@@ -330,6 +368,7 @@ export class RawServiceMeshRuntime {
|
|
|
330
368
|
lifecycleGeneration
|
|
331
369
|
})
|
|
332
370
|
);
|
|
371
|
+
this.notifyPeerRouteChanged(nodeRoutingId);
|
|
333
372
|
}
|
|
334
373
|
|
|
335
374
|
connectPeerEndpoint(endpoint: string): void {
|
|
@@ -414,13 +453,27 @@ export class RawServiceMeshRuntime {
|
|
|
414
453
|
nodeRoutingId: string
|
|
415
454
|
): import('./durable-service-request').PeerRouteObservation {
|
|
416
455
|
const selected = this.selectedRoutes.get(nodeRoutingId);
|
|
417
|
-
const
|
|
456
|
+
const peer = this.topology.admittedPeer(nodeRoutingId);
|
|
457
|
+
const expected = this.expectedPeers.get(nodeRoutingId);
|
|
458
|
+
const admitted = peer?.liveness?.connectionId;
|
|
418
459
|
return {
|
|
419
460
|
generation: selected ?? (typeof admitted === 'bigint' ? admitted : undefined),
|
|
420
|
-
ready:
|
|
461
|
+
ready: peer?.remoteAdmitReceived === true,
|
|
462
|
+
selected: selected !== undefined,
|
|
463
|
+
lifecycleGeneration: expected?.lifecycleGeneration ?? peer?.descriptor.lifecycleGeneration,
|
|
464
|
+
endpoint: expected?.endpoint ?? peer?.descriptor.advertisedEndpoint,
|
|
465
|
+
// The immutable socket snapshot is borrowed; only waiting admission filters it.
|
|
466
|
+
connectionAttempts: this.connectionAttempts
|
|
421
467
|
};
|
|
422
468
|
}
|
|
423
469
|
|
|
470
|
+
peerLifecycleGeneration(nodeRoutingId: string): bigint | undefined {
|
|
471
|
+
return (
|
|
472
|
+
this.expectedPeers.get(nodeRoutingId)?.lifecycleGeneration ??
|
|
473
|
+
this.topology.admittedPeer(nodeRoutingId)?.descriptor.lifecycleGeneration
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
|
|
424
477
|
async waitForPeerRoute(
|
|
425
478
|
nodeRoutingId: string,
|
|
426
479
|
lifecycleGeneration: bigint,
|
|
@@ -432,13 +485,13 @@ export class RawServiceMeshRuntime {
|
|
|
432
485
|
const pending = this.operations.register(Number(remaining));
|
|
433
486
|
const check = () => {
|
|
434
487
|
if (this.isPeerRouteReady(nodeRoutingId, lifecycleGeneration)) {
|
|
435
|
-
|
|
488
|
+
pending.reply({ terminalResult: RequestResult.Ok, failureCode: 0 });
|
|
436
489
|
}
|
|
437
490
|
};
|
|
438
491
|
const unsubscribe = this.observePeerRouteChanged((rid) => {
|
|
439
492
|
if (rid === nodeRoutingId) check();
|
|
440
493
|
});
|
|
441
|
-
const abort = () =>
|
|
494
|
+
const abort = () => pending.replyFailure(signal?.reason);
|
|
442
495
|
signal?.addEventListener('abort', abort, { once: true });
|
|
443
496
|
try {
|
|
444
497
|
if (signal?.aborted === true) abort();
|
|
@@ -483,15 +536,19 @@ export class RawServiceMeshRuntime {
|
|
|
483
536
|
if (generation === undefined) return false;
|
|
484
537
|
const key = controlSendKey(nodeRoutingId, M6aServiceWireCommand.hello);
|
|
485
538
|
if (!this.isPeerAnnouncementCurrent(nodeRoutingId, generation)) {
|
|
486
|
-
this.controlSends.delete(key);
|
|
487
539
|
return false;
|
|
488
540
|
}
|
|
489
|
-
const current = () =>
|
|
541
|
+
const current = () =>
|
|
542
|
+
this.isPeerAnnouncementCurrent(nodeRoutingId, generation) &&
|
|
543
|
+
this.peerAdmissionSealed?.() !== true;
|
|
490
544
|
const existing = this.controlSends.get(key);
|
|
545
|
+
if (!current()) {
|
|
546
|
+
if (existing === undefined) this.controlSends.set(key, { current });
|
|
547
|
+
return false;
|
|
548
|
+
}
|
|
491
549
|
const hello =
|
|
492
|
-
existing?.
|
|
493
|
-
|
|
494
|
-
: encodeRouteMeshAdmission(M6aServiceWireCommand.hello, this.topology.localDescriptor());
|
|
550
|
+
existing?.hello ??
|
|
551
|
+
encodeRouteMeshAdmission(M6aServiceWireCommand.hello, this.topology.localDescriptor());
|
|
495
552
|
return this.startControlSend(
|
|
496
553
|
nodeRoutingId,
|
|
497
554
|
M6aServiceWireCommand.hello,
|
|
@@ -502,21 +559,16 @@ export class RawServiceMeshRuntime {
|
|
|
502
559
|
}
|
|
503
560
|
|
|
504
561
|
private isPeerAnnouncementCurrent(nodeRoutingId: string, generation: bigint): boolean {
|
|
505
|
-
const admission = this.topology.routeAdmission(nodeRoutingId);
|
|
506
562
|
return (
|
|
507
563
|
this.selectedRoutes.get(nodeRoutingId) === generation &&
|
|
508
|
-
!(
|
|
509
|
-
admission?.connectionId === routeConnectionId(generation) &&
|
|
510
|
-
admission.remoteAdmitReceived === true
|
|
511
|
-
) &&
|
|
512
|
-
this.peerAdmissionSealed?.() !== true
|
|
564
|
+
!this.topology.hasRemoteAdmit(nodeRoutingId, routeConnectionId(generation))
|
|
513
565
|
);
|
|
514
566
|
}
|
|
515
567
|
|
|
516
568
|
isPeerRouteReady(nodeRoutingId: string, lifecycleGeneration?: bigint): boolean {
|
|
517
569
|
// Admission is bound to the observed selected route: the route observer
|
|
518
570
|
// removes the peer when that route ends or is replaced.
|
|
519
|
-
const peer = this.topology.
|
|
571
|
+
const peer = this.topology.readyPeer(nodeRoutingId);
|
|
520
572
|
return (
|
|
521
573
|
peer !== undefined &&
|
|
522
574
|
(lifecycleGeneration === undefined ||
|
|
@@ -593,20 +645,21 @@ export class RawServiceMeshRuntime {
|
|
|
593
645
|
return this.send(targetNodeRoutingId, [NODE_SEND_HEADER, this.applicationFrame(payload)]);
|
|
594
646
|
}
|
|
595
647
|
|
|
596
|
-
|
|
648
|
+
sendToChannel(
|
|
597
649
|
channelName: string,
|
|
598
650
|
payload: ServiceApplicationPayloadInput
|
|
599
|
-
): Promise<SubmitResult> {
|
|
651
|
+
): SubmitResult | Promise<SubmitResult> {
|
|
600
652
|
const selection = this.selectChannelTarget(channelName);
|
|
601
653
|
if (selection.kind !== 'selected') return selection.submitResult;
|
|
602
654
|
const selected = selection.peer;
|
|
603
655
|
const applicationFrame = this.applicationFrame(payload);
|
|
604
|
-
|
|
656
|
+
const submitted = this.send(selected.descriptor.nodeRoutingId, [
|
|
605
657
|
encodeChannelSendHeader(channelName),
|
|
606
658
|
applicationFrame
|
|
607
|
-
])
|
|
608
|
-
|
|
609
|
-
|
|
659
|
+
]);
|
|
660
|
+
return typeof submitted === 'boolean'
|
|
661
|
+
? channelSendSubmitResult(submitted)
|
|
662
|
+
: submitted.then(channelSendSubmitResult);
|
|
610
663
|
}
|
|
611
664
|
|
|
612
665
|
requestToNode(
|
|
@@ -621,7 +674,8 @@ export class RawServiceMeshRuntime {
|
|
|
621
674
|
timeoutMs: number,
|
|
622
675
|
onAdmitted: (() => void) | undefined,
|
|
623
676
|
observer: RawServiceRequestObserver,
|
|
624
|
-
operationKind: number
|
|
677
|
+
operationKind: number,
|
|
678
|
+
requestOperation?: PendingOperation<unknown>
|
|
625
679
|
): RawServiceObservedRequest;
|
|
626
680
|
requestToNode(
|
|
627
681
|
targetNodeRoutingId: string,
|
|
@@ -629,7 +683,8 @@ export class RawServiceMeshRuntime {
|
|
|
629
683
|
timeoutMs: number,
|
|
630
684
|
onAdmitted?: () => void,
|
|
631
685
|
observer?: RawServiceRequestObserver,
|
|
632
|
-
operationKind?: number
|
|
686
|
+
operationKind?: number,
|
|
687
|
+
requestOperation?: PendingOperation<unknown>
|
|
633
688
|
): RawServiceObservedRequest {
|
|
634
689
|
return this.requestToTarget(
|
|
635
690
|
targetNodeRoutingId,
|
|
@@ -638,7 +693,8 @@ export class RawServiceMeshRuntime {
|
|
|
638
693
|
undefined,
|
|
639
694
|
onAdmitted,
|
|
640
695
|
observer,
|
|
641
|
-
operationKind
|
|
696
|
+
operationKind,
|
|
697
|
+
requestOperation
|
|
642
698
|
);
|
|
643
699
|
}
|
|
644
700
|
|
|
@@ -654,7 +710,8 @@ export class RawServiceMeshRuntime {
|
|
|
654
710
|
timeoutMs: number,
|
|
655
711
|
onAdmitted: (() => void) | undefined,
|
|
656
712
|
observer: RawServiceRequestObserver,
|
|
657
|
-
operationKind: number
|
|
713
|
+
operationKind: number,
|
|
714
|
+
requestOperation?: PendingOperation<unknown>
|
|
658
715
|
): RawServiceObservedRequest;
|
|
659
716
|
requestToChannel(
|
|
660
717
|
channelName: string,
|
|
@@ -662,12 +719,18 @@ export class RawServiceMeshRuntime {
|
|
|
662
719
|
timeoutMs: number,
|
|
663
720
|
onAdmitted?: () => void,
|
|
664
721
|
observer?: RawServiceRequestObserver,
|
|
665
|
-
operationKind?: number
|
|
722
|
+
operationKind?: number,
|
|
723
|
+
requestOperation?: PendingOperation<unknown>
|
|
666
724
|
): RawServiceObservedRequest {
|
|
667
725
|
const selection = this.selectChannelTarget(channelName);
|
|
668
726
|
if (selection.kind !== 'selected') {
|
|
669
|
-
const pending = this.registerRequest(timeoutMs, observer, operationKind);
|
|
670
|
-
this.completeRequest(
|
|
727
|
+
const pending = this.registerRequest(timeoutMs, observer, operationKind, requestOperation);
|
|
728
|
+
this.completeRequest(
|
|
729
|
+
pending,
|
|
730
|
+
selection.requestResult,
|
|
731
|
+
requestOperation === undefined ? undefined : observer,
|
|
732
|
+
operationKind
|
|
733
|
+
);
|
|
671
734
|
return pending;
|
|
672
735
|
}
|
|
673
736
|
return this.requestToTarget(
|
|
@@ -681,6 +744,16 @@ export class RawServiceMeshRuntime {
|
|
|
681
744
|
);
|
|
682
745
|
}
|
|
683
746
|
|
|
747
|
+
beginInfrastructureRequest(
|
|
748
|
+
timeoutMs: number,
|
|
749
|
+
signal?: AbortSignal,
|
|
750
|
+
absoluteDeadlineMs = performance.now() + timeoutMs
|
|
751
|
+
): PendingOperation<readonly Uint8Array[]> {
|
|
752
|
+
const pending = this.operations.register(timeoutMs, 'registry', undefined, absoluteDeadlineMs);
|
|
753
|
+
pending.setSignal(signal);
|
|
754
|
+
return pending as unknown as PendingOperation<readonly Uint8Array[]>;
|
|
755
|
+
}
|
|
756
|
+
|
|
684
757
|
/**
|
|
685
758
|
* One pending entry per request: an observed request reports its terminal
|
|
686
759
|
* to the caller's observer; any other request keeps the Promise contract.
|
|
@@ -688,11 +761,19 @@ export class RawServiceMeshRuntime {
|
|
|
688
761
|
private registerRequest(
|
|
689
762
|
timeoutMs: number,
|
|
690
763
|
observer: RawServiceRequestObserver | undefined,
|
|
691
|
-
operationKind: number | undefined
|
|
764
|
+
operationKind: number | undefined,
|
|
765
|
+
requestOperation?: PendingOperation<unknown>
|
|
692
766
|
): RawServiceObservedRequest {
|
|
767
|
+
if (requestOperation !== undefined) {
|
|
768
|
+
return requestOperation as PendingOperation<RawServiceRequestResult>;
|
|
769
|
+
}
|
|
693
770
|
return observer === undefined
|
|
694
771
|
? this.operations.register(timeoutMs)
|
|
695
|
-
:
|
|
772
|
+
: this.observedRequest(this.operations.registerObserved(timeoutMs, observer, operationKind));
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
private observedRequest(id: bigint): RawServiceObservedRequest {
|
|
776
|
+
return this.operations.handle(id);
|
|
696
777
|
}
|
|
697
778
|
|
|
698
779
|
/**
|
|
@@ -701,23 +782,35 @@ export class RawServiceMeshRuntime {
|
|
|
701
782
|
*/
|
|
702
783
|
private completeRequest(
|
|
703
784
|
pending: RawServiceObservedRequest,
|
|
704
|
-
result: RawServiceRequestResult
|
|
785
|
+
result: RawServiceRequestResult,
|
|
786
|
+
observer?: RawServiceRequestObserver,
|
|
787
|
+
operationKind?: number
|
|
705
788
|
): void {
|
|
706
|
-
if (
|
|
707
|
-
queueMicrotask(() =>
|
|
708
|
-
|
|
789
|
+
if (observer !== undefined) {
|
|
790
|
+
queueMicrotask(() => observer.operationCompleted(pending.id, result, operationKind));
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
if (pending.promise === undefined || pending.hasObserver())
|
|
794
|
+
queueMicrotask(() => pending.reply(result));
|
|
795
|
+
else pending.reply(result);
|
|
709
796
|
}
|
|
710
797
|
|
|
711
|
-
private failRequest(
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
798
|
+
private failRequest(
|
|
799
|
+
pending: RawServiceObservedRequest,
|
|
800
|
+
error: unknown,
|
|
801
|
+
observer?: RawServiceRequestObserver,
|
|
802
|
+
operationKind?: number
|
|
803
|
+
): void {
|
|
804
|
+
if (observer !== undefined) {
|
|
805
|
+
queueMicrotask(() => observer.operationFailed(pending.id, error, operationKind));
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
if (pending.promise === undefined) queueMicrotask(() => pending.replyFailure(error));
|
|
809
|
+
else pending.replyFailure(error);
|
|
715
810
|
}
|
|
716
811
|
|
|
717
812
|
private selectChannelTarget(channelName: string): RawServiceChannelTargetSelection {
|
|
718
|
-
const selected = this.topology.selectChannel(channelName
|
|
719
|
-
this.isPeerRouteReady(peer.descriptor.nodeRoutingId)
|
|
720
|
-
);
|
|
813
|
+
const selected = this.topology.selectChannel(channelName);
|
|
721
814
|
if (selected !== undefined) return { kind: 'selected', peer: selected };
|
|
722
815
|
return this.topology.hasKnownChannelTarget(channelName)
|
|
723
816
|
? {
|
|
@@ -768,16 +861,64 @@ export class RawServiceMeshRuntime {
|
|
|
768
861
|
targetNodeRoutingId: string,
|
|
769
862
|
parts: readonly Uint8Array[],
|
|
770
863
|
timeoutMs: number,
|
|
771
|
-
onAdmitted?: () => void
|
|
772
|
-
|
|
864
|
+
onAdmitted?: () => void,
|
|
865
|
+
existing?: Pick<PendingOperation<unknown>, 'id' | 'deadlineMs'>,
|
|
866
|
+
receiver?: undefined,
|
|
867
|
+
onSubmitted?: () => void
|
|
868
|
+
): Promise<readonly Buffer[]>;
|
|
869
|
+
requestService(
|
|
870
|
+
targetNodeRoutingId: string,
|
|
871
|
+
parts: readonly Uint8Array[],
|
|
872
|
+
timeoutMs: number,
|
|
873
|
+
onAdmitted: (() => void) | undefined,
|
|
874
|
+
existing: Pick<PendingOperation<unknown>, 'id' | 'deadlineMs'> | undefined,
|
|
875
|
+
receiver: import('./operation-registry').OperationObserver<readonly Buffer[], unknown>
|
|
876
|
+
): void;
|
|
877
|
+
requestService(
|
|
878
|
+
targetNodeRoutingId: string,
|
|
879
|
+
parts: readonly Uint8Array[],
|
|
880
|
+
timeoutMs: number,
|
|
881
|
+
onAdmitted?: () => void,
|
|
882
|
+
existing?: Pick<PendingOperation<unknown>, 'id' | 'deadlineMs'>,
|
|
883
|
+
receiver?: import('./operation-registry').OperationObserver<readonly Buffer[], unknown>,
|
|
884
|
+
onSubmitted?: () => void
|
|
885
|
+
): Promise<readonly Buffer[]> | void {
|
|
886
|
+
if (receiver !== undefined) {
|
|
887
|
+
try {
|
|
888
|
+
this.requireStarted().request(
|
|
889
|
+
targetNodeRoutingId,
|
|
890
|
+
parts,
|
|
891
|
+
timeoutMs,
|
|
892
|
+
(reply) => receiver.operationCompleted(existing?.id ?? 0n, reply, undefined),
|
|
893
|
+
(error) => receiver.operationFailed(existing?.id ?? 0n, error, undefined),
|
|
894
|
+
this.topology.admittedPeer(targetNodeRoutingId)?.liveness?.recordReceived,
|
|
895
|
+
onAdmitted,
|
|
896
|
+
existing
|
|
897
|
+
);
|
|
898
|
+
} catch (error) {
|
|
899
|
+
receiver.operationFailed(existing?.id ?? 0n, error, undefined);
|
|
900
|
+
}
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
773
903
|
const admission = this.topology.admittedPeer(targetNodeRoutingId)?.liveness;
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
904
|
+
try {
|
|
905
|
+
const reply = this.requireStarted().request(
|
|
906
|
+
targetNodeRoutingId,
|
|
907
|
+
parts,
|
|
908
|
+
timeoutMs,
|
|
909
|
+
(reply) => reply,
|
|
910
|
+
(error) => {
|
|
911
|
+
throw error;
|
|
912
|
+
},
|
|
913
|
+
admission?.recordReceived,
|
|
914
|
+
onAdmitted,
|
|
915
|
+
existing
|
|
916
|
+
);
|
|
917
|
+
onSubmitted?.();
|
|
918
|
+
return reply;
|
|
919
|
+
} catch (error) {
|
|
920
|
+
return Promise.reject(error);
|
|
921
|
+
}
|
|
781
922
|
}
|
|
782
923
|
|
|
783
924
|
replyService(
|
|
@@ -785,7 +926,7 @@ export class RawServiceMeshRuntime {
|
|
|
785
926
|
RawServiceIngressRecord,
|
|
786
927
|
'sourceRoutingId' | 'sourceRoute' | 'requestSequence' | 'reply'
|
|
787
928
|
>,
|
|
788
|
-
parts: readonly Uint8Array[]
|
|
929
|
+
parts: readonly (Uint8Array | string)[]
|
|
789
930
|
): void {
|
|
790
931
|
if (record.requestSequence === undefined) {
|
|
791
932
|
throw new TypeError('Service reply requires a request sequence.');
|
|
@@ -813,10 +954,8 @@ export class RawServiceMeshRuntime {
|
|
|
813
954
|
if (request.reply === undefined || request.correlation === undefined) {
|
|
814
955
|
throw new TypeError('Reply requires a request mailbox record.');
|
|
815
956
|
}
|
|
816
|
-
request.
|
|
817
|
-
|
|
818
|
-
...(terminalResult === 0 ? [this.applicationFrame(payload)] : [])
|
|
819
|
-
]);
|
|
957
|
+
const header = encodeReplyHeader(request.correlation, terminalResult, failureCode);
|
|
958
|
+
request.reply(terminalResult === 0 ? [header, this.applicationFrame(payload)] : [header]);
|
|
820
959
|
}
|
|
821
960
|
|
|
822
961
|
async pumpOne(
|
|
@@ -838,14 +977,48 @@ export class RawServiceMeshRuntime {
|
|
|
838
977
|
return this.receiveOne(nowMs, observe);
|
|
839
978
|
}
|
|
840
979
|
|
|
841
|
-
setReadableHandler(
|
|
980
|
+
setReadableHandler(
|
|
981
|
+
handler: (receiveReady: boolean, routeReady: boolean, connectReady: boolean) => void
|
|
982
|
+
): void {
|
|
842
983
|
this.requireStarted().setReadableHandler(handler);
|
|
843
984
|
}
|
|
844
985
|
|
|
845
986
|
/** Returns whether a receive budget ended before no-data; idle ticks only maintain peers. */
|
|
846
|
-
|
|
847
|
-
|
|
987
|
+
pumpBatch(
|
|
988
|
+
receiveReady = true,
|
|
989
|
+
routeReady = false,
|
|
990
|
+
connectReady = false
|
|
991
|
+
): boolean | Promise<boolean> {
|
|
992
|
+
try {
|
|
993
|
+
this.observeInfrastructureFailure();
|
|
994
|
+
if (routeReady || connectReady) {
|
|
995
|
+
return this.receiveBatchAfterObservations(receiveReady, routeReady, connectReady);
|
|
996
|
+
}
|
|
997
|
+
return this.receiveBatch(receiveReady);
|
|
998
|
+
} catch (error) {
|
|
999
|
+
return Promise.reject(error);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
private async receiveBatchAfterObservations(
|
|
1004
|
+
receiveReady: boolean,
|
|
1005
|
+
routeReady: boolean,
|
|
1006
|
+
connectReady: boolean
|
|
1007
|
+
): Promise<boolean> {
|
|
1008
|
+
// Both facts are applied before admission observers run in this socket turn.
|
|
1009
|
+
if (connectReady) this.connectionAttempts = this.requireStarted().connectionAttemptsSnapshot();
|
|
848
1010
|
if (routeReady) await this.observeSelectedRoutes();
|
|
1011
|
+
if (connectReady) {
|
|
1012
|
+
for (const target of this.expectedPeers.keys()) this.notifyPeerRouteChanged(target);
|
|
1013
|
+
for (const peer of this.topology.peers()) {
|
|
1014
|
+
if (!this.expectedPeers.has(peer.descriptor.nodeRoutingId))
|
|
1015
|
+
this.notifyPeerRouteChanged(peer.descriptor.nodeRoutingId);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
return this.receiveBatch(receiveReady);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
private receiveBatch(receiveReady: boolean): boolean | Promise<boolean> {
|
|
849
1022
|
const startedAtMs = performance.now();
|
|
850
1023
|
let messages = 0;
|
|
851
1024
|
let bytes = 0;
|
|
@@ -857,30 +1030,49 @@ export class RawServiceMeshRuntime {
|
|
|
857
1030
|
'remote'
|
|
858
1031
|
));
|
|
859
1032
|
if (receiveReady) rotation.reserve();
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
1033
|
+
// The same loop owns both immediate receives and a suspended receive's
|
|
1034
|
+
// continuation; only the latter needs a Promise and retains this frame.
|
|
1035
|
+
const drain = (received?: RawServicePumpResult): boolean | Promise<boolean> => {
|
|
1036
|
+
try {
|
|
1037
|
+
while (
|
|
1038
|
+
received !== undefined ||
|
|
1039
|
+
(receiveReady && messages < RAW_MESH_RECEIVE_RECORD_BUDGET && !this.closed)
|
|
1040
|
+
) {
|
|
1041
|
+
if (received === undefined) {
|
|
1042
|
+
const pending = this.receiveOne(performance.now(), observe, rotation);
|
|
1043
|
+
if (pending instanceof Promise) {
|
|
1044
|
+
return pending.then(drain, (error) => {
|
|
1045
|
+
rotation.releaseUnused();
|
|
1046
|
+
throw error;
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
received = pending;
|
|
1050
|
+
}
|
|
1051
|
+
if (received === 'noData') {
|
|
1052
|
+
receiveReady = false;
|
|
1053
|
+
break;
|
|
1054
|
+
}
|
|
1055
|
+
messages += 1;
|
|
1056
|
+
received = undefined;
|
|
1057
|
+
// Core owns the per-peer fair-queue cursor. The Framework limit bounds
|
|
1058
|
+
// the entire round, so changing peers does not renew its byte/count budget.
|
|
1059
|
+
if (
|
|
1060
|
+
bytes >= RAW_MESH_RECEIVE_BYTE_BUDGET ||
|
|
1061
|
+
performance.now() - startedAtMs >= RAW_MESH_RECEIVE_TIME_BUDGET_MS
|
|
1062
|
+
)
|
|
1063
|
+
break;
|
|
867
1064
|
}
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
if (
|
|
872
|
-
bytes >= RAW_MESH_RECEIVE_BYTE_BUDGET ||
|
|
873
|
-
performance.now() - startedAtMs >= RAW_MESH_RECEIVE_TIME_BUDGET_MS
|
|
874
|
-
)
|
|
875
|
-
break;
|
|
1065
|
+
} catch (error) {
|
|
1066
|
+
rotation.releaseUnused();
|
|
1067
|
+
throw error;
|
|
876
1068
|
}
|
|
877
|
-
} finally {
|
|
878
1069
|
rotation.releaseUnused();
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
1070
|
+
if (!this.closed) {
|
|
1071
|
+
this.tickLiveness();
|
|
1072
|
+
}
|
|
1073
|
+
return receiveReady && messages > 0;
|
|
1074
|
+
};
|
|
1075
|
+
return drain();
|
|
884
1076
|
}
|
|
885
1077
|
|
|
886
1078
|
private receiveOne(
|
|
@@ -894,35 +1086,7 @@ export class RawServiceMeshRuntime {
|
|
|
894
1086
|
this.receivePermit = undefined;
|
|
895
1087
|
permit ??= rotation?.take();
|
|
896
1088
|
if (permit === undefined && this.receiveAcquisition === undefined) {
|
|
897
|
-
|
|
898
|
-
let rejectAcquisition: ((error: unknown) => void) | undefined;
|
|
899
|
-
permit = this.applicationJobQueue.acquireOrResume(
|
|
900
|
-
(permit) => {
|
|
901
|
-
this.receiveAcquisition = undefined;
|
|
902
|
-
if (this.closed) {
|
|
903
|
-
permit.releaseAfterInternalProcessing();
|
|
904
|
-
permit.releaseStorage?.();
|
|
905
|
-
} else {
|
|
906
|
-
this.receivePermit = permit;
|
|
907
|
-
this.onReceiveReady?.();
|
|
908
|
-
}
|
|
909
|
-
resolveAcquisition?.(permit);
|
|
910
|
-
},
|
|
911
|
-
(error) => rejectAcquisition?.(error),
|
|
912
|
-
this.applicationJobStop.signal,
|
|
913
|
-
'remote'
|
|
914
|
-
);
|
|
915
|
-
if (permit === undefined) {
|
|
916
|
-
const acquisition = new Promise<ApplicationJobPermitPort>((resolve, reject) => {
|
|
917
|
-
resolveAcquisition = resolve;
|
|
918
|
-
rejectAcquisition = reject;
|
|
919
|
-
});
|
|
920
|
-
this.receiveAcquisition = acquisition;
|
|
921
|
-
acquisition.catch((error) => {
|
|
922
|
-
this.receiveAcquisition = undefined;
|
|
923
|
-
if (!this.applicationJobStop.signal.aborted) this.reportInfrastructureFailure(error);
|
|
924
|
-
});
|
|
925
|
-
}
|
|
1089
|
+
permit = this.acquireReceivePermit();
|
|
926
1090
|
}
|
|
927
1091
|
if (permit === undefined) return 'noData';
|
|
928
1092
|
if (this.closed) {
|
|
@@ -956,16 +1120,58 @@ export class RawServiceMeshRuntime {
|
|
|
956
1120
|
throw error;
|
|
957
1121
|
}
|
|
958
1122
|
return result instanceof Promise
|
|
959
|
-
?
|
|
960
|
-
(value) => this.finishReceive(received, applicationJobOwner, observe, value),
|
|
961
|
-
(error) => {
|
|
962
|
-
applicationJobOwner.close();
|
|
963
|
-
throw error;
|
|
964
|
-
}
|
|
965
|
-
)
|
|
1123
|
+
? this.finishPendingReceive(result, received, applicationJobOwner, observe)
|
|
966
1124
|
: this.finishReceive(received, applicationJobOwner, observe, result);
|
|
967
1125
|
}
|
|
968
1126
|
|
|
1127
|
+
private acquireReceivePermit(): ApplicationJobPermitPort | undefined {
|
|
1128
|
+
let resolveAcquisition: ((permit: ApplicationJobPermitPort) => void) | undefined;
|
|
1129
|
+
let rejectAcquisition: ((error: unknown) => void) | undefined;
|
|
1130
|
+
const permit = this.applicationJobQueue.acquireOrResume(
|
|
1131
|
+
(permit) => {
|
|
1132
|
+
this.receiveAcquisition = undefined;
|
|
1133
|
+
if (this.closed) {
|
|
1134
|
+
permit.releaseAfterInternalProcessing();
|
|
1135
|
+
permit.releaseStorage?.();
|
|
1136
|
+
} else {
|
|
1137
|
+
this.receivePermit = permit;
|
|
1138
|
+
this.onReceiveReady?.();
|
|
1139
|
+
}
|
|
1140
|
+
resolveAcquisition?.(permit);
|
|
1141
|
+
},
|
|
1142
|
+
(error) => rejectAcquisition?.(error),
|
|
1143
|
+
this.applicationJobStop.signal,
|
|
1144
|
+
'remote'
|
|
1145
|
+
);
|
|
1146
|
+
if (permit === undefined) {
|
|
1147
|
+
const acquisition = new Promise<ApplicationJobPermitPort>((resolve, reject) => {
|
|
1148
|
+
resolveAcquisition = resolve;
|
|
1149
|
+
rejectAcquisition = reject;
|
|
1150
|
+
});
|
|
1151
|
+
this.receiveAcquisition = acquisition;
|
|
1152
|
+
acquisition.catch((error) => {
|
|
1153
|
+
this.receiveAcquisition = undefined;
|
|
1154
|
+
if (!this.applicationJobStop.signal.aborted) this.reportInfrastructureFailure(error);
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
return permit;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
private finishPendingReceive(
|
|
1161
|
+
result: Promise<RawServicePumpResult>,
|
|
1162
|
+
received: ZLinkRawReceivedRecord,
|
|
1163
|
+
owner: ApplicationIngressRecordOwner,
|
|
1164
|
+
observe: RawServicePumpObserver | undefined
|
|
1165
|
+
): Promise<RawServicePumpResult> {
|
|
1166
|
+
return result.then(
|
|
1167
|
+
(value) => this.finishReceive(received, owner, observe, value),
|
|
1168
|
+
(error) => {
|
|
1169
|
+
owner.close();
|
|
1170
|
+
throw error;
|
|
1171
|
+
}
|
|
1172
|
+
);
|
|
1173
|
+
}
|
|
1174
|
+
|
|
969
1175
|
private finishReceive(
|
|
970
1176
|
received: ZLinkRawReceivedRecord,
|
|
971
1177
|
owner: ApplicationIngressRecordOwner,
|
|
@@ -976,7 +1182,8 @@ export class RawServiceMeshRuntime {
|
|
|
976
1182
|
if (result === 'protocolError') this.reportProtocolError(received);
|
|
977
1183
|
if (observe !== undefined) {
|
|
978
1184
|
let bytes = 0;
|
|
979
|
-
for (
|
|
1185
|
+
for (let index = 0; index < received.parts.length; index += 1)
|
|
1186
|
+
bytes += received.parts[index]!.byteLength;
|
|
980
1187
|
observe(received.sourceRid, bytes);
|
|
981
1188
|
}
|
|
982
1189
|
return result;
|
|
@@ -985,7 +1192,7 @@ export class RawServiceMeshRuntime {
|
|
|
985
1192
|
}
|
|
986
1193
|
}
|
|
987
1194
|
|
|
988
|
-
|
|
1195
|
+
reportProtocolError(
|
|
989
1196
|
received: import('../backend/node/node-raw-binding-port').ZLinkRawReceivedRecord
|
|
990
1197
|
): void {
|
|
991
1198
|
const request = received.requestSeq !== undefined;
|
|
@@ -1002,32 +1209,19 @@ export class RawServiceMeshRuntime {
|
|
|
1002
1209
|
received: import('../backend/node/node-raw-binding-port').ZLinkRawReceivedRecord
|
|
1003
1210
|
): boolean {
|
|
1004
1211
|
if (received.requestSeq === undefined || received.parts.length === 0) return false;
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
encodeReplyHeader(
|
|
1016
|
-
correlation,
|
|
1017
|
-
RequestResult.ProtocolError,
|
|
1018
|
-
ServiceWireFrameworkErrorCode.requestProtocolError
|
|
1019
|
-
)
|
|
1020
|
-
];
|
|
1021
|
-
if (received.reply === undefined) return false;
|
|
1022
|
-
received.reply(reply);
|
|
1023
|
-
return true;
|
|
1024
|
-
} catch (error) {
|
|
1025
|
-
if (error instanceof ServiceWireProtocolError) return false;
|
|
1026
|
-
throw error;
|
|
1027
|
-
}
|
|
1212
|
+
const correlation = recoverRequestCorrelation(received.parts[0]!);
|
|
1213
|
+
if (correlation === undefined || received.reply === undefined) return false;
|
|
1214
|
+
received.reply([
|
|
1215
|
+
encodeReplyHeader(
|
|
1216
|
+
correlation,
|
|
1217
|
+
RequestResult.ProtocolError,
|
|
1218
|
+
ServiceWireFrameworkErrorCode.requestProtocolError
|
|
1219
|
+
)
|
|
1220
|
+
]);
|
|
1221
|
+
return true;
|
|
1028
1222
|
}
|
|
1029
1223
|
|
|
1030
|
-
|
|
1224
|
+
processReceived(
|
|
1031
1225
|
received: ZLinkRawReceivedRecord,
|
|
1032
1226
|
nowMs: number,
|
|
1033
1227
|
applicationJobOwner: ApplicationIngressRecordOwner
|
|
@@ -1045,9 +1239,7 @@ export class RawServiceMeshRuntime {
|
|
|
1045
1239
|
header.command === M6aServiceWireCommand.hello ||
|
|
1046
1240
|
header.command === M6aServiceWireCommand.admit
|
|
1047
1241
|
) {
|
|
1048
|
-
return this.processAdmission(received, header, nowMs)
|
|
1049
|
-
this.receiveProtocolFailure(error)
|
|
1050
|
-
);
|
|
1242
|
+
return this.finishPendingProtocolReceive(this.processAdmission(received, header, nowMs));
|
|
1051
1243
|
}
|
|
1052
1244
|
if (header.command === M6aServiceWireCommand.reject) {
|
|
1053
1245
|
if (received.parts.length !== 1) return 'protocolError';
|
|
@@ -1057,17 +1249,13 @@ export class RawServiceMeshRuntime {
|
|
|
1057
1249
|
if (!currentSource) return 'dropped';
|
|
1058
1250
|
this.liveness.recordReceived(peer.liveness, nowMs);
|
|
1059
1251
|
if (header.command === M6aServiceWireCommand.update) {
|
|
1060
|
-
return this.processAdmission(received, header, nowMs)
|
|
1061
|
-
this.receiveProtocolFailure(error)
|
|
1062
|
-
);
|
|
1252
|
+
return this.finishPendingProtocolReceive(this.processAdmission(received, header, nowMs));
|
|
1063
1253
|
}
|
|
1064
1254
|
if (
|
|
1065
1255
|
header.command === M6aServiceWireCommand.livenessProbe ||
|
|
1066
1256
|
header.command === M6aServiceWireCommand.livenessAck
|
|
1067
1257
|
) {
|
|
1068
|
-
return this.processLiveness(received, nowMs)
|
|
1069
|
-
this.receiveProtocolFailure(error)
|
|
1070
|
-
);
|
|
1258
|
+
return this.finishPendingProtocolReceive(this.processLiveness(received, nowMs));
|
|
1071
1259
|
}
|
|
1072
1260
|
const stateful =
|
|
1073
1261
|
this.serviceIngress !== undefined && this.serviceIngressAccepts?.(header.command) !== false
|
|
@@ -1086,11 +1274,7 @@ export class RawServiceMeshRuntime {
|
|
|
1086
1274
|
})
|
|
1087
1275
|
: undefined;
|
|
1088
1276
|
if (stateful instanceof Promise)
|
|
1089
|
-
return
|
|
1090
|
-
(result) =>
|
|
1091
|
-
result ?? this.processApplicationReceived(received, header, applicationJobOwner),
|
|
1092
|
-
(error) => this.receiveProtocolFailure(error)
|
|
1093
|
-
);
|
|
1277
|
+
return this.finishPendingStatefulReceive(stateful, received, header, applicationJobOwner);
|
|
1094
1278
|
if (stateful !== undefined) return stateful;
|
|
1095
1279
|
return this.processApplicationReceived(received, header, applicationJobOwner);
|
|
1096
1280
|
} catch (error) {
|
|
@@ -1099,6 +1283,24 @@ export class RawServiceMeshRuntime {
|
|
|
1099
1283
|
}
|
|
1100
1284
|
}
|
|
1101
1285
|
|
|
1286
|
+
private finishPendingProtocolReceive(
|
|
1287
|
+
pending: Promise<RawServicePumpResult>
|
|
1288
|
+
): Promise<RawServicePumpResult> {
|
|
1289
|
+
return pending.catch((error) => this.receiveProtocolFailure(error));
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
private finishPendingStatefulReceive(
|
|
1293
|
+
pending: Promise<RawServicePumpResult | undefined>,
|
|
1294
|
+
received: ZLinkRawReceivedRecord,
|
|
1295
|
+
header: ReturnType<typeof decodeHeader>,
|
|
1296
|
+
owner: ApplicationIngressRecordOwner
|
|
1297
|
+
): Promise<RawServicePumpResult> {
|
|
1298
|
+
return pending.then(
|
|
1299
|
+
(result) => result ?? this.processApplicationReceived(received, header, owner),
|
|
1300
|
+
(error) => this.receiveProtocolFailure(error)
|
|
1301
|
+
);
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1102
1304
|
private receiveProtocolFailure(error: unknown): RawServicePumpResult {
|
|
1103
1305
|
if (error instanceof ServiceWireProtocolError) return 'protocolError';
|
|
1104
1306
|
throw error;
|
|
@@ -1170,7 +1372,7 @@ export class RawServiceMeshRuntime {
|
|
|
1170
1372
|
]);
|
|
1171
1373
|
} else if (header.command === M6aServiceWireCommand.admit) {
|
|
1172
1374
|
this.controlSends.delete(helloKey);
|
|
1173
|
-
this.
|
|
1375
|
+
this.progressPeerAdmission(nowMs);
|
|
1174
1376
|
if (result === 'notRequired') {
|
|
1175
1377
|
this.retireNotRequiredExpectedPeer(received.sourceRid, descriptor.advertisedEndpoint);
|
|
1176
1378
|
} else if (
|
|
@@ -1274,13 +1476,26 @@ export class RawServiceMeshRuntime {
|
|
|
1274
1476
|
}
|
|
1275
1477
|
const job = applicationJobOwner.acquire('application');
|
|
1276
1478
|
return job instanceof Promise
|
|
1277
|
-
?
|
|
1479
|
+
? this.enqueuePendingOrdinaryRecord(job, received, owner, correlation)
|
|
1278
1480
|
: this.enqueueOrdinaryRecord(received, owner, correlation, job);
|
|
1279
1481
|
} catch (error) {
|
|
1280
1482
|
return this.receiveProtocolFailure(error);
|
|
1281
1483
|
}
|
|
1282
1484
|
}
|
|
1283
1485
|
|
|
1486
|
+
private enqueuePendingOrdinaryRecord(
|
|
1487
|
+
pending: Promise<
|
|
1488
|
+
import('../application-jobs/application-ingress-record-owner').ApplicationJobRecordLease
|
|
1489
|
+
>,
|
|
1490
|
+
received: ZLinkRawReceivedRecord,
|
|
1491
|
+
owner: string,
|
|
1492
|
+
correlation: bigint | undefined
|
|
1493
|
+
): Promise<RawServicePumpResult> {
|
|
1494
|
+
return pending.then((permit) =>
|
|
1495
|
+
this.enqueueOrdinaryRecord(received, owner, correlation, permit)
|
|
1496
|
+
);
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1284
1499
|
private enqueueOrdinaryRecord(
|
|
1285
1500
|
received: ZLinkRawReceivedRecord,
|
|
1286
1501
|
owner: string,
|
|
@@ -1316,7 +1531,9 @@ export class RawServiceMeshRuntime {
|
|
|
1316
1531
|
}
|
|
1317
1532
|
|
|
1318
1533
|
expireOperations(nowMs: number, turnDeadlineMs: number): number {
|
|
1319
|
-
return this.operations.
|
|
1534
|
+
return this.requestOwner === undefined && nowMs >= this.operations.nextDeadlineMs()
|
|
1535
|
+
? this.operations.expire(nowMs, turnDeadlineMs)
|
|
1536
|
+
: 0;
|
|
1320
1537
|
}
|
|
1321
1538
|
|
|
1322
1539
|
get pendingOperationCount(): number {
|
|
@@ -1333,20 +1550,24 @@ export class RawServiceMeshRuntime {
|
|
|
1333
1550
|
* ADMIT, which keeps the existing announce cadence.
|
|
1334
1551
|
*/
|
|
1335
1552
|
nextMaintenanceDeadlineMs(nowMs: number): number {
|
|
1336
|
-
this.
|
|
1337
|
-
return Math.min(
|
|
1553
|
+
this.progressPeerAdmission(nowMs);
|
|
1554
|
+
return Math.min(
|
|
1555
|
+
this.requestOwner === undefined ? this.operations.nextDeadlineMs() : Infinity,
|
|
1556
|
+
this.liveness.nextDeadlineMs()
|
|
1557
|
+
);
|
|
1338
1558
|
}
|
|
1339
1559
|
|
|
1340
|
-
private
|
|
1560
|
+
private progressPeerAdmission(nowMs: number): void {
|
|
1341
1561
|
let pending = false;
|
|
1342
1562
|
if (!this.closed && this.controlSends.size !== 0) {
|
|
1343
1563
|
for (const nodeRoutingId of this.selectedRoutes.keys()) {
|
|
1344
|
-
const
|
|
1345
|
-
|
|
1346
|
-
|
|
1564
|
+
const key = controlSendKey(nodeRoutingId, M6aServiceWireCommand.hello);
|
|
1565
|
+
const exchange = this.controlSends.get(key);
|
|
1566
|
+
// An unsent HELLO intent may resume after the host admission seal opens.
|
|
1567
|
+
// Only retransmission of submitted bytes waits for the management cadence.
|
|
1347
1568
|
if (exchange?.current?.() === true) {
|
|
1569
|
+
if (exchange.hello === undefined) this.announcePeer(nodeRoutingId);
|
|
1348
1570
|
pending = true;
|
|
1349
|
-
break;
|
|
1350
1571
|
}
|
|
1351
1572
|
}
|
|
1352
1573
|
}
|
|
@@ -1354,9 +1575,12 @@ export class RawServiceMeshRuntime {
|
|
|
1354
1575
|
}
|
|
1355
1576
|
|
|
1356
1577
|
tickLiveness(nowMs = performance.now()): ServiceLivenessTick {
|
|
1578
|
+
this.progressPeerAdmission(nowMs);
|
|
1357
1579
|
const admissionDue = this.liveness.tickAdmission(nowMs);
|
|
1358
1580
|
const result = this.liveness.tick(nowMs);
|
|
1359
1581
|
this.requireStarted();
|
|
1582
|
+
if (!admissionDue && result.probes.length === 0 && result.timedOutNodes.length === 0)
|
|
1583
|
+
return result;
|
|
1360
1584
|
if (admissionDue)
|
|
1361
1585
|
for (const nodeRoutingId of this.selectedRoutes.keys()) void this.announcePeer(nodeRoutingId);
|
|
1362
1586
|
for (const probe of result.probes) {
|
|
@@ -1370,12 +1594,24 @@ export class RawServiceMeshRuntime {
|
|
|
1370
1594
|
this.submitControl(probe.nodeRoutingId, parts)
|
|
1371
1595
|
);
|
|
1372
1596
|
}
|
|
1597
|
+
const failures: unknown[] = [];
|
|
1373
1598
|
for (const nodeRoutingId of result.timedOutNodes) {
|
|
1374
1599
|
const peer = this.topology.peer(nodeRoutingId);
|
|
1375
1600
|
if (peer === undefined) continue;
|
|
1376
|
-
|
|
1377
|
-
|
|
1601
|
+
try {
|
|
1602
|
+
this.removePeer(peer, () => {
|
|
1603
|
+
try {
|
|
1604
|
+
this.requireStarted().disconnectRid!(nodeRoutingId);
|
|
1605
|
+
} catch (error) {
|
|
1606
|
+
failures.push(error);
|
|
1607
|
+
}
|
|
1608
|
+
});
|
|
1609
|
+
} catch (error) {
|
|
1610
|
+
failures.push(error);
|
|
1611
|
+
}
|
|
1378
1612
|
}
|
|
1613
|
+
if (failures.length === 1) throw failures[0];
|
|
1614
|
+
if (failures.length > 1) throw new AggregateError(failures, 'Liveness peer cleanup failed.');
|
|
1379
1615
|
return result;
|
|
1380
1616
|
}
|
|
1381
1617
|
|
|
@@ -1410,34 +1646,44 @@ export class RawServiceMeshRuntime {
|
|
|
1410
1646
|
}
|
|
1411
1647
|
this.selectedRoutes = observed;
|
|
1412
1648
|
for (const nodeRoutingId of selected) this.notifyPeerRouteChanged(nodeRoutingId);
|
|
1413
|
-
//
|
|
1649
|
+
// Selected routes begin admission before waiting for the management cadence.
|
|
1414
1650
|
for (const nodeRoutingId of selected) await this.announcePeer(nodeRoutingId);
|
|
1415
|
-
this.
|
|
1651
|
+
this.progressPeerAdmission(performance.now());
|
|
1416
1652
|
return changes;
|
|
1417
1653
|
}
|
|
1418
1654
|
|
|
1419
1655
|
close(): void {
|
|
1420
|
-
if (this.closed) return;
|
|
1421
|
-
this.closed = true;
|
|
1422
|
-
this.applicationJobStop.abort(
|
|
1423
|
-
new Error('Raw service runtime application job admission stopped.')
|
|
1424
|
-
);
|
|
1425
|
-
this.receivePermit?.releaseAfterInternalProcessing();
|
|
1426
|
-
this.receivePermit?.releaseStorage?.();
|
|
1427
|
-
this.receivePermit = undefined;
|
|
1428
|
-
for (const send of this.controlSends.values()) delete send.next;
|
|
1429
|
-
this.controlSends.clear();
|
|
1430
|
-
this.mailbox.close();
|
|
1431
|
-
this.operations.close('Raw service runtime closed.');
|
|
1432
|
-
this.serviceIngress = undefined;
|
|
1433
|
-
const router = this.router;
|
|
1434
|
-
if (router !== undefined) {
|
|
1435
|
-
this.applicationJobQueue.unregisterReceiveFlowTarget?.(router);
|
|
1436
|
-
}
|
|
1437
1656
|
const host = this.host;
|
|
1438
|
-
if (host
|
|
1439
|
-
this.router = undefined;
|
|
1657
|
+
if (this.closed && host === undefined) return;
|
|
1440
1658
|
this.host = undefined;
|
|
1659
|
+
try {
|
|
1660
|
+
if (!this.closed) {
|
|
1661
|
+
this.closed = true;
|
|
1662
|
+
this.applicationJobStop.abort(
|
|
1663
|
+
createInternalFrameworkException(
|
|
1664
|
+
ZLinkFrameworkInternalErrorKind.RuntimeShutdown,
|
|
1665
|
+
'Raw service runtime application job admission stopped.'
|
|
1666
|
+
)
|
|
1667
|
+
);
|
|
1668
|
+
this.receivePermit?.releaseAfterInternalProcessing();
|
|
1669
|
+
this.receivePermit?.releaseStorage?.();
|
|
1670
|
+
this.receivePermit = undefined;
|
|
1671
|
+
for (const send of this.controlSends.values()) delete send.next;
|
|
1672
|
+
this.controlSends.clear();
|
|
1673
|
+
this.mailbox.close();
|
|
1674
|
+
this.releaseOperations();
|
|
1675
|
+
this.serviceIngress = undefined;
|
|
1676
|
+
const router = this.router;
|
|
1677
|
+
if (router !== undefined) {
|
|
1678
|
+
this.applicationJobQueue.unregisterReceiveFlowTarget?.(router);
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
if (host !== undefined) host.close();
|
|
1682
|
+
this.router = undefined;
|
|
1683
|
+
} catch (error) {
|
|
1684
|
+
this.host = host;
|
|
1685
|
+
throw error;
|
|
1686
|
+
}
|
|
1441
1687
|
}
|
|
1442
1688
|
|
|
1443
1689
|
private requestToTarget(
|
|
@@ -1447,14 +1693,15 @@ export class RawServiceMeshRuntime {
|
|
|
1447
1693
|
channelName?: string,
|
|
1448
1694
|
onAdmitted?: () => void,
|
|
1449
1695
|
observer?: RawServiceRequestObserver,
|
|
1450
|
-
operationKind?: number
|
|
1696
|
+
operationKind?: number,
|
|
1697
|
+
requestOperation?: PendingOperation<unknown>
|
|
1451
1698
|
): RawServiceObservedRequest {
|
|
1452
1699
|
const correlation = this.nextCorrelation++;
|
|
1453
1700
|
const header =
|
|
1454
1701
|
channelName === undefined
|
|
1455
1702
|
? encodeNodeRequestHeader(correlation)
|
|
1456
1703
|
: encodeChannelRequestHeader(correlation, channelName);
|
|
1457
|
-
|
|
1704
|
+
let encodedPayload = this.applicationFrame(payload);
|
|
1458
1705
|
let selectedTargetNodeRoutingId = targetNodeRoutingId;
|
|
1459
1706
|
if (
|
|
1460
1707
|
selectedTargetNodeRoutingId !== this.descriptor.nodeRoutingId &&
|
|
@@ -1482,21 +1729,30 @@ export class RawServiceMeshRuntime {
|
|
|
1482
1729
|
}
|
|
1483
1730
|
}
|
|
1484
1731
|
if (selectedTargetNodeRoutingId === this.descriptor.nodeRoutingId) {
|
|
1732
|
+
encodedPayload = copyPayloadBytes(encodedPayload);
|
|
1485
1733
|
// The local admission path aborts its capacity wait on the terminal
|
|
1486
1734
|
// Promise, so a local request always keeps the Promise entry.
|
|
1487
|
-
const pending =
|
|
1735
|
+
const pending =
|
|
1736
|
+
requestOperation === undefined
|
|
1737
|
+
? this.operations.register(timeoutMs)
|
|
1738
|
+
: (this.registerRequest(
|
|
1739
|
+
timeoutMs,
|
|
1740
|
+
observer,
|
|
1741
|
+
operationKind,
|
|
1742
|
+
requestOperation
|
|
1743
|
+
) as PendingOperation<RawServiceRequestResult>);
|
|
1488
1744
|
const parts = [header, encodedPayload];
|
|
1489
1745
|
const capacityStop = new AbortController();
|
|
1490
1746
|
void pending.promise.finally(() => capacityStop.abort()).catch(() => undefined);
|
|
1491
1747
|
void (async () => {
|
|
1492
1748
|
const applicationJobOwner = await this.reserveLocalIngress(capacityStop.signal);
|
|
1493
1749
|
try {
|
|
1494
|
-
if (!
|
|
1750
|
+
if (!pending.isPending()) return;
|
|
1495
1751
|
const applicationJob = await applicationJobOwner.acquire(
|
|
1496
1752
|
'application',
|
|
1497
1753
|
capacityStop.signal
|
|
1498
1754
|
);
|
|
1499
|
-
if (!
|
|
1755
|
+
if (!pending.isPending()) {
|
|
1500
1756
|
applicationJob.close();
|
|
1501
1757
|
return;
|
|
1502
1758
|
}
|
|
@@ -1507,91 +1763,150 @@ export class RawServiceMeshRuntime {
|
|
|
1507
1763
|
sourceRoutingId: this.descriptor.nodeRoutingId,
|
|
1508
1764
|
correlation,
|
|
1509
1765
|
localReply: (terminalResult, failureCode, reply) =>
|
|
1510
|
-
this.
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1766
|
+
this.completeRequest(
|
|
1767
|
+
pending,
|
|
1768
|
+
{
|
|
1769
|
+
terminalResult,
|
|
1770
|
+
failureCode,
|
|
1771
|
+
...(reply === undefined ? {} : { payload: reply })
|
|
1772
|
+
},
|
|
1773
|
+
requestOperation === undefined ? undefined : observer,
|
|
1774
|
+
operationKind
|
|
1775
|
+
),
|
|
1515
1776
|
applicationJob
|
|
1516
1777
|
});
|
|
1517
1778
|
if (!accepted) {
|
|
1518
1779
|
applicationJob.close();
|
|
1519
|
-
this.
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1780
|
+
this.completeRequest(
|
|
1781
|
+
pending,
|
|
1782
|
+
{
|
|
1783
|
+
terminalResult: RequestResult.NotConnected,
|
|
1784
|
+
failureCode: 0
|
|
1785
|
+
},
|
|
1786
|
+
requestOperation === undefined ? undefined : observer,
|
|
1787
|
+
operationKind
|
|
1788
|
+
);
|
|
1523
1789
|
} else {
|
|
1524
1790
|
onAdmitted?.();
|
|
1525
1791
|
}
|
|
1526
1792
|
} finally {
|
|
1527
1793
|
applicationJobOwner.close();
|
|
1528
1794
|
}
|
|
1529
|
-
})().catch((error) =>
|
|
1795
|
+
})().catch((error) =>
|
|
1796
|
+
this.failRequest(
|
|
1797
|
+
pending,
|
|
1798
|
+
error,
|
|
1799
|
+
requestOperation === undefined ? undefined : observer,
|
|
1800
|
+
operationKind
|
|
1801
|
+
)
|
|
1802
|
+
);
|
|
1530
1803
|
return pending;
|
|
1531
1804
|
}
|
|
1532
|
-
const pending = this.registerRequest(timeoutMs, observer, operationKind);
|
|
1533
|
-
const selectedPeer = this.topology.
|
|
1534
|
-
if (
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1805
|
+
const pending = this.registerRequest(timeoutMs, observer, operationKind, requestOperation);
|
|
1806
|
+
const selectedPeer = this.topology.readyPeer(selectedTargetNodeRoutingId);
|
|
1807
|
+
if (
|
|
1808
|
+
selectedPeer === undefined &&
|
|
1809
|
+
!this.expectedPeers.has(selectedTargetNodeRoutingId) &&
|
|
1810
|
+
this.topology.peer(selectedTargetNodeRoutingId) === undefined
|
|
1811
|
+
) {
|
|
1812
|
+
this.completeRequest(
|
|
1813
|
+
pending,
|
|
1814
|
+
{
|
|
1815
|
+
terminalResult: RequestResult.NotFound,
|
|
1816
|
+
failureCode: REQUEST_TARGET_NOT_FOUND_FAILURE_CODE
|
|
1817
|
+
},
|
|
1818
|
+
requestOperation === undefined ? undefined : observer,
|
|
1819
|
+
operationKind
|
|
1820
|
+
);
|
|
1542
1821
|
return pending;
|
|
1543
1822
|
}
|
|
1544
1823
|
const parts = [header, encodedPayload];
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1824
|
+
const completed = (replyParts: readonly Buffer[]) => {
|
|
1825
|
+
try {
|
|
1826
|
+
if (replyParts.length < 1 || replyParts.length > 2) {
|
|
1827
|
+
throw new ServiceWireProtocolError('Invalid reply parts.');
|
|
1828
|
+
}
|
|
1829
|
+
const reply = decodeReplyHeader(replyParts[0]!);
|
|
1830
|
+
if (reply.correlation !== correlation)
|
|
1831
|
+
throw new ServiceWireProtocolError('Reply correlation mismatch.');
|
|
1832
|
+
if (reply.tail.byteLength !== 0) {
|
|
1833
|
+
throw new ServiceWireProtocolError(
|
|
1834
|
+
'Generic node/channel reply carries an operation-specific tail.'
|
|
1835
|
+
);
|
|
1836
|
+
}
|
|
1837
|
+
if (reply.terminalResult === 0 && replyParts.length !== 2) {
|
|
1838
|
+
throw new ServiceWireProtocolError('Successful reply omits its payload.');
|
|
1839
|
+
}
|
|
1840
|
+
if (reply.terminalResult !== 0 && replyParts.length !== 1) {
|
|
1841
|
+
throw new ServiceWireProtocolError('Failed reply carries a payload.');
|
|
1842
|
+
}
|
|
1843
|
+
const result: RawServiceRequestResult = {
|
|
1844
|
+
terminalResult: reply.terminalResult,
|
|
1845
|
+
failureCode: reply.failureCode
|
|
1846
|
+
};
|
|
1847
|
+
const value =
|
|
1848
|
+
reply.terminalResult === 0
|
|
1849
|
+
? { ...result, payload: decodeApplicationPayloadView(replyParts[1]!) }
|
|
1850
|
+
: result;
|
|
1851
|
+
if (requestOperation !== undefined && observer !== undefined)
|
|
1852
|
+
observer.operationCompleted(pending.id, value, operationKind);
|
|
1853
|
+
else pending.reply(value);
|
|
1854
|
+
} catch (error) {
|
|
1855
|
+
this.failRequest(
|
|
1856
|
+
pending,
|
|
1857
|
+
error,
|
|
1858
|
+
requestOperation === undefined ? undefined : observer,
|
|
1859
|
+
operationKind
|
|
1860
|
+
);
|
|
1861
|
+
}
|
|
1862
|
+
};
|
|
1863
|
+
const failed = (error: unknown) =>
|
|
1864
|
+
this.failRequest(
|
|
1865
|
+
pending,
|
|
1866
|
+
error,
|
|
1867
|
+
requestOperation === undefined ? undefined : observer,
|
|
1868
|
+
operationKind
|
|
1553
1869
|
);
|
|
1554
|
-
|
|
1555
|
-
|
|
1870
|
+
if (selectedPeer !== undefined) {
|
|
1871
|
+
try {
|
|
1872
|
+
this.requireStarted().request(
|
|
1873
|
+
selectedTargetNodeRoutingId,
|
|
1874
|
+
parts,
|
|
1875
|
+
remainingRequestTimeout('Request', pending.deadlineMs)!,
|
|
1876
|
+
completed,
|
|
1877
|
+
failed,
|
|
1878
|
+
selectedPeer.liveness?.recordReceived,
|
|
1879
|
+
onAdmitted,
|
|
1880
|
+
pending
|
|
1881
|
+
);
|
|
1882
|
+
} catch (error) {
|
|
1883
|
+
failed(error);
|
|
1884
|
+
}
|
|
1556
1885
|
return pending;
|
|
1557
1886
|
}
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
}
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
terminalResult: reply.terminalResult,
|
|
1580
|
-
failureCode: reply.failureCode
|
|
1581
|
-
};
|
|
1582
|
-
this.operations.complete(
|
|
1583
|
-
pending.id,
|
|
1584
|
-
reply.terminalResult === 0
|
|
1585
|
-
? { ...result, payload: decodeApplicationPayloadView(replyParts[1]!) }
|
|
1586
|
-
: result
|
|
1587
|
-
);
|
|
1588
|
-
} catch (error) {
|
|
1589
|
-
this.operations.fail(pending.id, error);
|
|
1590
|
-
}
|
|
1887
|
+
admitServiceRequest(
|
|
1888
|
+
this,
|
|
1889
|
+
selectedTargetNodeRoutingId,
|
|
1890
|
+
pending,
|
|
1891
|
+
(remaining, finished) => {
|
|
1892
|
+
this.requireStarted().request(
|
|
1893
|
+
selectedTargetNodeRoutingId,
|
|
1894
|
+
parts,
|
|
1895
|
+
remaining,
|
|
1896
|
+
(reply) => {
|
|
1897
|
+
finished();
|
|
1898
|
+
completed(reply);
|
|
1899
|
+
},
|
|
1900
|
+
(error) => {
|
|
1901
|
+
finished();
|
|
1902
|
+
failed(error);
|
|
1903
|
+
},
|
|
1904
|
+
this.topology.admittedPeer(selectedTargetNodeRoutingId)?.liveness?.recordReceived,
|
|
1905
|
+
onAdmitted,
|
|
1906
|
+
pending
|
|
1907
|
+
);
|
|
1591
1908
|
},
|
|
1592
|
-
|
|
1593
|
-
this.operations.fail(pending.id, error);
|
|
1594
|
-
}
|
|
1909
|
+
failed
|
|
1595
1910
|
);
|
|
1596
1911
|
return pending;
|
|
1597
1912
|
}
|
|
@@ -1605,10 +1920,10 @@ export class RawServiceMeshRuntime {
|
|
|
1605
1920
|
}
|
|
1606
1921
|
|
|
1607
1922
|
private applicationPayload(payload: ServiceApplicationPayloadInput): ServiceApplicationPayload {
|
|
1608
|
-
return Buffer.isBuffer(payload) ?
|
|
1923
|
+
return Buffer.isBuffer(payload) ? decodeApplicationPayload(payload) : payload;
|
|
1609
1924
|
}
|
|
1610
1925
|
|
|
1611
|
-
|
|
1926
|
+
admitPeer(
|
|
1612
1927
|
descriptor: ServiceNodeDescriptor,
|
|
1613
1928
|
connectionId: string | bigint,
|
|
1614
1929
|
nowMs: number,
|
|
@@ -1620,7 +1935,7 @@ export class RawServiceMeshRuntime {
|
|
|
1620
1935
|
command: number = M6aServiceWireCommand.hello,
|
|
1621
1936
|
descriptorBytes?: Uint8Array
|
|
1622
1937
|
): PeerAdmissionResult {
|
|
1623
|
-
const previous = this.topology.
|
|
1938
|
+
const previous = this.topology.admittedPeer(descriptor.nodeRoutingId);
|
|
1624
1939
|
const topologyConnectionId =
|
|
1625
1940
|
typeof connectionId === 'bigint' ? routeConnectionId(connectionId) : connectionId;
|
|
1626
1941
|
const result = this.topology.admit(
|
|
@@ -1646,7 +1961,14 @@ export class RawServiceMeshRuntime {
|
|
|
1646
1961
|
previous.liveness?.connectionId ?? previous.connectionId
|
|
1647
1962
|
);
|
|
1648
1963
|
}
|
|
1649
|
-
if (
|
|
1964
|
+
if (
|
|
1965
|
+
result === 'admitted' &&
|
|
1966
|
+
(previous?.connectionId !== topologyConnectionId ||
|
|
1967
|
+
previous.descriptor.lifecycleGeneration !== descriptor.lifecycleGeneration ||
|
|
1968
|
+
previous.remoteAdmitReceived !==
|
|
1969
|
+
this.topology.admittedPeer(descriptor.nodeRoutingId)?.remoteAdmitReceived)
|
|
1970
|
+
)
|
|
1971
|
+
this.notifyPeerRouteChanged(descriptor.nodeRoutingId);
|
|
1650
1972
|
return result;
|
|
1651
1973
|
}
|
|
1652
1974
|
|
|
@@ -1680,17 +2002,22 @@ export class RawServiceMeshRuntime {
|
|
|
1680
2002
|
this.endpointOnlyPeers.delete(endpoint);
|
|
1681
2003
|
}
|
|
1682
2004
|
|
|
1683
|
-
private removePeer(peer: AdmittedServicePeer): void {
|
|
2005
|
+
private removePeer(peer: AdmittedServicePeer, disconnectPhysical?: () => void): void {
|
|
1684
2006
|
this.topology.disconnect(peer.descriptor.nodeRoutingId, peer.connectionId);
|
|
1685
2007
|
this.liveness.disconnect(
|
|
1686
2008
|
peer.descriptor.nodeRoutingId,
|
|
1687
2009
|
peer.liveness?.connectionId ?? peer.connectionId
|
|
1688
2010
|
);
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
2011
|
+
try {
|
|
2012
|
+
disconnectPhysical?.();
|
|
2013
|
+
} finally {
|
|
2014
|
+
// The logical admission has ended even when the physical disconnect reports a failure.
|
|
2015
|
+
this.onPeerDisconnected?.(
|
|
2016
|
+
peer.descriptor.nodeRoutingId,
|
|
2017
|
+
peer.descriptor.advertisedEndpoint,
|
|
2018
|
+
peer.descriptor.lifecycleGeneration
|
|
2019
|
+
);
|
|
2020
|
+
}
|
|
1694
2021
|
}
|
|
1695
2022
|
|
|
1696
2023
|
private requireStarted(): ZLinkRawRouterPort {
|
|
@@ -1698,10 +2025,7 @@ export class RawServiceMeshRuntime {
|
|
|
1698
2025
|
return this.router;
|
|
1699
2026
|
}
|
|
1700
2027
|
|
|
1701
|
-
|
|
1702
|
-
targetNodeRoutingId: string,
|
|
1703
|
-
parts: readonly Uint8Array[]
|
|
1704
|
-
): Promise<boolean> {
|
|
2028
|
+
async sendControl(targetNodeRoutingId: string, parts: readonly Uint8Array[]): Promise<boolean> {
|
|
1705
2029
|
return this.startControlSend(targetNodeRoutingId, decodeHeader(parts[0]!).command, () =>
|
|
1706
2030
|
this.submitControl(targetNodeRoutingId, parts)
|
|
1707
2031
|
);
|
|
@@ -1721,7 +2045,7 @@ export class RawServiceMeshRuntime {
|
|
|
1721
2045
|
);
|
|
1722
2046
|
}
|
|
1723
2047
|
|
|
1724
|
-
|
|
2048
|
+
startControlSend(
|
|
1725
2049
|
target: string,
|
|
1726
2050
|
command: number,
|
|
1727
2051
|
attempt: () => boolean | Promise<boolean>,
|
|
@@ -1732,11 +2056,7 @@ export class RawServiceMeshRuntime {
|
|
|
1732
2056
|
if (command === M6aServiceWireCommand.hello)
|
|
1733
2057
|
this.liveness.setAdmissionPending(true, performance.now());
|
|
1734
2058
|
const existing = this.controlSends.get(key);
|
|
1735
|
-
if (
|
|
1736
|
-
existing !== undefined &&
|
|
1737
|
-
existing.task !== undefined &&
|
|
1738
|
-
(command !== M6aServiceWireCommand.hello || existing.current?.() === true)
|
|
1739
|
-
) {
|
|
2059
|
+
if (existing?.task !== undefined) {
|
|
1740
2060
|
if (command !== M6aServiceWireCommand.hello) {
|
|
1741
2061
|
existing.next = attempt;
|
|
1742
2062
|
existing.current = current;
|
|
@@ -1752,7 +2072,7 @@ export class RawServiceMeshRuntime {
|
|
|
1752
2072
|
const state: ControlSendState = { current, hello };
|
|
1753
2073
|
const completeSend = () => {
|
|
1754
2074
|
if (owner.controlSends.get(key) !== state) return;
|
|
1755
|
-
if (hello !== undefined
|
|
2075
|
+
if (hello !== undefined) delete state.task;
|
|
1756
2076
|
else owner.controlSends.delete(key);
|
|
1757
2077
|
};
|
|
1758
2078
|
const task = first.then(function drain(accepted): boolean | Promise<boolean> {
|
|
@@ -1805,6 +2125,7 @@ export class RawServiceMeshRuntime {
|
|
|
1805
2125
|
targetNodeRoutingId: string,
|
|
1806
2126
|
parts: readonly Uint8Array[]
|
|
1807
2127
|
): boolean | Promise<boolean> {
|
|
2128
|
+
if (!this.isPeerRouteReady(targetNodeRoutingId)) return false;
|
|
1808
2129
|
try {
|
|
1809
2130
|
const submission = this.requireStarted().submitSend(targetNodeRoutingId, parts);
|
|
1810
2131
|
if (submission.result === SubmitResult.Ok) return true;
|
|
@@ -1827,6 +2148,10 @@ export class RawServiceMeshRuntime {
|
|
|
1827
2148
|
}
|
|
1828
2149
|
}
|
|
1829
2150
|
|
|
2151
|
+
function channelSendSubmitResult(accepted: boolean): SubmitResult {
|
|
2152
|
+
return accepted ? SubmitResult.Ok : SubmitResult.NotConnected;
|
|
2153
|
+
}
|
|
2154
|
+
|
|
1830
2155
|
function controlSendKey(target: string, command: number): string {
|
|
1831
2156
|
return `${command}:${target}`;
|
|
1832
2157
|
}
|