@spine-event-engine/server 2.0.0-snapshot.23 → 2.0.0-snapshot.25
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/README.md +38 -11
- package/REFERENCE.md +278 -141
- package/dist/agent/agent-acceptance-receipts.d.ts +69 -0
- package/dist/agent/agent-acceptance-receipts.d.ts.map +1 -0
- package/dist/agent/agent-acceptance-receipts.js +198 -0
- package/dist/agent/agent-acceptance-receipts.js.map +1 -0
- package/dist/agent/agent-execution-capacity.d.ts +37 -9
- package/dist/agent/agent-execution-capacity.d.ts.map +1 -1
- package/dist/agent/agent-execution-capacity.js +73 -15
- package/dist/agent/agent-execution-capacity.js.map +1 -1
- package/dist/agent/agent-history.d.ts +15 -10
- package/dist/agent/agent-history.d.ts.map +1 -1
- package/dist/agent/agent-history.js +26 -14
- package/dist/agent/agent-history.js.map +1 -1
- package/dist/agent/agent-live-ai-audit.d.ts +95 -0
- package/dist/agent/agent-live-ai-audit.d.ts.map +1 -0
- package/dist/agent/agent-live-ai-audit.js +290 -0
- package/dist/agent/agent-live-ai-audit.js.map +1 -0
- package/dist/agent/agent-live-ai-runtime.d.ts +300 -0
- package/dist/agent/agent-live-ai-runtime.d.ts.map +1 -0
- package/dist/agent/agent-live-ai-runtime.js +611 -0
- package/dist/agent/agent-live-ai-runtime.js.map +1 -0
- package/dist/agent/agent-live-audit.d.ts +67 -0
- package/dist/agent/agent-live-audit.d.ts.map +1 -0
- package/dist/agent/agent-live-audit.js +121 -0
- package/dist/agent/agent-live-audit.js.map +1 -0
- package/dist/agent/agent-live-budget.d.ts +84 -0
- package/dist/agent/agent-live-budget.d.ts.map +1 -0
- package/dist/agent/agent-live-budget.js +161 -0
- package/dist/agent/agent-live-budget.js.map +1 -0
- package/dist/agent/agent-live-fault.d.ts +18 -0
- package/dist/agent/agent-live-fault.d.ts.map +1 -0
- package/dist/agent/{agent-execution-fault.js → agent-live-fault.js} +7 -7
- package/dist/agent/agent-live-fault.js.map +1 -0
- package/dist/agent/agent-live-inputs.d.ts +74 -0
- package/dist/agent/agent-live-inputs.d.ts.map +1 -0
- package/dist/agent/agent-live-inputs.js +15 -0
- package/dist/agent/agent-live-inputs.js.map +1 -0
- package/dist/agent/agent-live-interaction-events.d.ts +46 -0
- package/dist/agent/agent-live-interaction-events.d.ts.map +1 -0
- package/dist/agent/agent-live-interaction-events.js +104 -0
- package/dist/agent/agent-live-interaction-events.js.map +1 -0
- package/dist/agent/agent-live-mcp-controls.d.ts +129 -0
- package/dist/agent/agent-live-mcp-controls.d.ts.map +1 -0
- package/dist/agent/agent-live-mcp-controls.js +228 -0
- package/dist/agent/agent-live-mcp-controls.js.map +1 -0
- package/dist/agent/agent-live-mcp-host.d.ts +88 -0
- package/dist/agent/agent-live-mcp-host.d.ts.map +1 -0
- package/dist/agent/{agent-mcp-host.js → agent-live-mcp-host.js} +1 -1
- package/dist/agent/agent-live-mcp-host.js.map +1 -0
- package/dist/agent/{agent-mcp-runtime.d.ts → agent-live-mcp-runtime.d.ts} +6 -6
- package/dist/agent/agent-live-mcp-runtime.d.ts.map +1 -0
- package/dist/agent/{agent-mcp-runtime.js → agent-live-mcp-runtime.js} +13 -42
- package/dist/agent/agent-live-mcp-runtime.js.map +1 -0
- package/dist/agent/agent-live-read-runtime.d.ts +61 -0
- package/dist/agent/agent-live-read-runtime.d.ts.map +1 -0
- package/dist/agent/agent-live-read-runtime.js +121 -0
- package/dist/agent/agent-live-read-runtime.js.map +1 -0
- package/dist/agent/agent-model-selection.d.ts +21 -34
- package/dist/agent/agent-model-selection.d.ts.map +1 -1
- package/dist/agent/agent-model-selection.js +61 -78
- package/dist/agent/agent-model-selection.js.map +1 -1
- package/dist/bus/command-bus.d.ts +14 -18
- package/dist/bus/command-bus.d.ts.map +1 -1
- package/dist/bus/command-bus.js +31 -48
- package/dist/bus/command-bus.js.map +1 -1
- package/dist/bus/event-bus.d.ts +82 -104
- package/dist/bus/event-bus.d.ts.map +1 -1
- package/dist/bus/event-bus.js +87 -127
- package/dist/bus/event-bus.js.map +1 -1
- package/dist/context/bounded-context.d.ts +143 -91
- package/dist/context/bounded-context.d.ts.map +1 -1
- package/dist/context/bounded-context.js +753 -359
- package/dist/context/bounded-context.js.map +1 -1
- package/dist/context/entity-inbox.d.ts +7 -2
- package/dist/context/entity-inbox.d.ts.map +1 -1
- package/dist/context/entity-inbox.js +43 -20
- package/dist/context/entity-inbox.js.map +1 -1
- package/dist/context/local-inbox-handoff.d.ts +35 -34
- package/dist/context/local-inbox-handoff.d.ts.map +1 -1
- package/dist/context/local-inbox-handoff.js +123 -135
- package/dist/context/local-inbox-handoff.js.map +1 -1
- package/dist/context/projection-handoff.d.ts +2 -2
- package/dist/context/projection-handoff.d.ts.map +1 -1
- package/dist/context/projection-handoff.js +12 -8
- package/dist/context/projection-handoff.js.map +1 -1
- package/dist/context/tenant-index.d.ts +36 -31
- package/dist/context/tenant-index.d.ts.map +1 -1
- package/dist/context/tenant-index.js +123 -125
- package/dist/context/tenant-index.js.map +1 -1
- package/dist/context/tenant-unknown-error.d.ts +33 -0
- package/dist/context/tenant-unknown-error.d.ts.map +1 -0
- package/dist/context/tenant-unknown-error.js +75 -0
- package/dist/context/tenant-unknown-error.js.map +1 -0
- package/dist/delivery/agent-delivery-scope.d.ts +66 -0
- package/dist/delivery/agent-delivery-scope.d.ts.map +1 -0
- package/dist/delivery/agent-delivery-scope.js +44 -0
- package/dist/delivery/agent-delivery-scope.js.map +1 -0
- package/dist/delivery/delivery-builder.d.ts +25 -1
- package/dist/delivery/delivery-builder.d.ts.map +1 -1
- package/dist/delivery/delivery-builder.js +89 -0
- package/dist/delivery/delivery-builder.js.map +1 -1
- package/dist/delivery/delivery-monitor.d.ts +12 -9
- package/dist/delivery/delivery-monitor.d.ts.map +1 -1
- package/dist/delivery/delivery-monitor.js +15 -9
- package/dist/delivery/delivery-monitor.js.map +1 -1
- package/dist/delivery/delivery-ports.d.ts +6 -13
- package/dist/delivery/delivery-ports.d.ts.map +1 -1
- package/dist/delivery/delivery-run-control.d.ts +21 -0
- package/dist/delivery/delivery-run-control.d.ts.map +1 -1
- package/dist/delivery/delivery-run-control.js +10 -0
- package/dist/delivery/delivery-run-control.js.map +1 -1
- package/dist/delivery/delivery-supervisor.d.ts +33 -5
- package/dist/delivery/delivery-supervisor.d.ts.map +1 -1
- package/dist/delivery/delivery-supervisor.js +178 -26
- package/dist/delivery/delivery-supervisor.js.map +1 -1
- package/dist/delivery/delivery-tenant.d.ts +17 -0
- package/dist/delivery/delivery-tenant.d.ts.map +1 -0
- package/dist/delivery/delivery-tenant.js +50 -0
- package/dist/delivery/delivery-tenant.js.map +1 -0
- package/dist/delivery/delivery.d.ts +96 -2
- package/dist/delivery/delivery.d.ts.map +1 -1
- package/dist/delivery/delivery.js +803 -89
- package/dist/delivery/delivery.js.map +1 -1
- package/dist/delivery/inbox-records.d.ts.map +1 -1
- package/dist/delivery/inbox-records.js +9 -16
- package/dist/delivery/inbox-records.js.map +1 -1
- package/dist/delivery/inbox-storage.d.ts +14 -11
- package/dist/delivery/inbox-storage.d.ts.map +1 -1
- package/dist/delivery/inbox-storage.js +72 -78
- package/dist/delivery/inbox-storage.js.map +1 -1
- package/dist/delivery/inbox.d.ts +9 -12
- package/dist/delivery/inbox.d.ts.map +1 -1
- package/dist/delivery/inbox.js +13 -15
- package/dist/delivery/inbox.js.map +1 -1
- package/dist/delivery/reception-observation.d.ts +64 -0
- package/dist/delivery/reception-observation.d.ts.map +1 -0
- package/dist/delivery/reception-observation.js +78 -0
- package/dist/delivery/reception-observation.js.map +1 -0
- package/dist/delivery/sharded-work-registry.d.ts +6 -0
- package/dist/delivery/sharded-work-registry.d.ts.map +1 -1
- package/dist/delivery/sharded-work-registry.js +41 -4
- package/dist/delivery/sharded-work-registry.js.map +1 -1
- package/dist/entity/entity-storage-descriptor.d.ts.map +1 -1
- package/dist/entity/entity-storage-descriptor.js +7 -4
- package/dist/entity/entity-storage-descriptor.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/integration/external-messages.d.ts.map +1 -1
- package/dist/integration/external-messages.js +9 -15
- package/dist/integration/external-messages.js.map +1 -1
- package/dist/integration/integration-broker.d.ts.map +1 -1
- package/dist/integration/integration-broker.js +112 -6
- package/dist/integration/integration-broker.js.map +1 -1
- package/dist/integration/third-party-context.d.ts.map +1 -1
- package/dist/integration/third-party-context.js +7 -10
- package/dist/integration/third-party-context.js.map +1 -1
- package/dist/repository/primitive-id.js +26 -11
- package/dist/repository/primitive-id.js.map +1 -1
- package/dist/repository/repository.d.ts +33 -62
- package/dist/repository/repository.d.ts.map +1 -1
- package/dist/repository/repository.js +1475 -1827
- package/dist/repository/repository.js.map +1 -1
- package/dist/runtime/runtime.d.ts +24 -0
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +31 -0
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/signal-metadata.d.ts.map +1 -1
- package/dist/runtime/signal-metadata.js +8 -15
- package/dist/runtime/signal-metadata.js.map +1 -1
- package/dist/runtime/signal-publisher.d.ts +8 -36
- package/dist/runtime/signal-publisher.d.ts.map +1 -1
- package/dist/runtime/signal-publisher.js +68 -61
- package/dist/runtime/signal-publisher.js.map +1 -1
- package/dist/runtime/standalone-handler-runtime.d.ts +0 -7
- package/dist/runtime/standalone-handler-runtime.d.ts.map +1 -1
- package/dist/runtime/standalone-handler-runtime.js +14 -130
- package/dist/runtime/standalone-handler-runtime.js.map +1 -1
- package/dist/server/managed-delivery-gate.d.ts +34 -0
- package/dist/server/managed-delivery-gate.d.ts.map +1 -0
- package/dist/server/managed-delivery-gate.js +70 -0
- package/dist/server/managed-delivery-gate.js.map +1 -0
- package/dist/server/managed-server-application.d.ts +2 -2
- package/dist/server/managed-server-application.d.ts.map +1 -1
- package/dist/server/managed-server-application.js +471 -178
- package/dist/server/managed-server-application.js.map +1 -1
- package/dist/server/node-coordinator.d.ts +14 -0
- package/dist/server/node-coordinator.d.ts.map +1 -1
- package/dist/server/node-coordinator.js +266 -45
- package/dist/server/node-coordinator.js.map +1 -1
- package/dist/server/server-environment.d.ts +203 -25
- package/dist/server/server-environment.d.ts.map +1 -1
- package/dist/server/server-environment.js +661 -133
- package/dist/server/server-environment.js.map +1 -1
- package/dist/server/server-log.d.ts +7 -0
- package/dist/server/server-log.d.ts.map +1 -1
- package/dist/server/server-log.js +18 -1
- package/dist/server/server-log.js.map +1 -1
- package/dist/server/server.d.ts +18 -32
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +62 -203
- package/dist/server/server.js.map +1 -1
- package/dist/services/spine-services.d.ts +1 -1
- package/dist/services/spine-services.d.ts.map +1 -1
- package/dist/services/spine-services.js +399 -370
- package/dist/services/spine-services.js.map +1 -1
- package/dist/stand/event-origin-filter.d.ts +16 -0
- package/dist/stand/event-origin-filter.d.ts.map +1 -0
- package/dist/stand/event-origin-filter.js +124 -0
- package/dist/stand/event-origin-filter.js.map +1 -0
- package/dist/stand/stand.d.ts +14 -0
- package/dist/stand/stand.d.ts.map +1 -1
- package/dist/stand/stand.js +53 -9
- package/dist/stand/stand.js.map +1 -1
- package/dist/stand/subscription-observer.d.ts.map +1 -1
- package/dist/stand/subscription-observer.js +61 -40
- package/dist/stand/subscription-observer.js.map +1 -1
- package/dist/stand/subscription-registry.d.ts +6 -2
- package/dist/stand/subscription-registry.d.ts.map +1 -1
- package/dist/stand/subscription-registry.js +15 -11
- package/dist/stand/subscription-registry.js.map +1 -1
- package/dist/stand/subscription-runtime.d.ts +23 -4
- package/dist/stand/subscription-runtime.d.ts.map +1 -1
- package/dist/stand/subscription-runtime.js +84 -13
- package/dist/stand/subscription-runtime.js.map +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +3 -2
- package/dist/testing/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +15 -15
- package/dist/agent/agent-admission.d.ts +0 -80
- package/dist/agent/agent-admission.d.ts.map +0 -1
- package/dist/agent/agent-admission.js +0 -127
- package/dist/agent/agent-admission.js.map +0 -1
- package/dist/agent/agent-ai-runtime.d.ts +0 -76
- package/dist/agent/agent-ai-runtime.d.ts.map +0 -1
- package/dist/agent/agent-ai-runtime.js +0 -1269
- package/dist/agent/agent-ai-runtime.js.map +0 -1
- package/dist/agent/agent-execution-fault.d.ts +0 -18
- package/dist/agent/agent-execution-fault.d.ts.map +0 -1
- package/dist/agent/agent-execution-fault.js.map +0 -1
- package/dist/agent/agent-execution-session.d.ts +0 -103
- package/dist/agent/agent-execution-session.d.ts.map +0 -1
- package/dist/agent/agent-execution-session.js +0 -238
- package/dist/agent/agent-execution-session.js.map +0 -1
- package/dist/agent/agent-interaction-audit.d.ts +0 -58
- package/dist/agent/agent-interaction-audit.d.ts.map +0 -1
- package/dist/agent/agent-interaction-audit.js +0 -114
- package/dist/agent/agent-interaction-audit.js.map +0 -1
- package/dist/agent/agent-interaction-events.d.ts +0 -40
- package/dist/agent/agent-interaction-events.d.ts.map +0 -1
- package/dist/agent/agent-interaction-events.js +0 -129
- package/dist/agent/agent-interaction-events.js.map +0 -1
- package/dist/agent/agent-mcp-budget.d.ts +0 -125
- package/dist/agent/agent-mcp-budget.d.ts.map +0 -1
- package/dist/agent/agent-mcp-budget.js +0 -566
- package/dist/agent/agent-mcp-budget.js.map +0 -1
- package/dist/agent/agent-mcp-host.d.ts +0 -125
- package/dist/agent/agent-mcp-host.d.ts.map +0 -1
- package/dist/agent/agent-mcp-host.js.map +0 -1
- package/dist/agent/agent-mcp-runtime.d.ts.map +0 -1
- package/dist/agent/agent-mcp-runtime.js.map +0 -1
- package/dist/agent/agent-read-runtime.d.ts +0 -70
- package/dist/agent/agent-read-runtime.d.ts.map +0 -1
- package/dist/agent/agent-read-runtime.js +0 -320
- package/dist/agent/agent-read-runtime.js.map +0 -1
- package/dist/agent/agent-revisions.d.ts +0 -35
- package/dist/agent/agent-revisions.d.ts.map +0 -1
- package/dist/agent/agent-revisions.js +0 -133
- package/dist/agent/agent-revisions.js.map +0 -1
- package/dist/agent/agent-scheduler.d.ts +0 -99
- package/dist/agent/agent-scheduler.d.ts.map +0 -1
- package/dist/agent/agent-scheduler.js +0 -398
- package/dist/agent/agent-scheduler.js.map +0 -1
- package/dist/bus/saved-dispatcher-binding.d.ts +0 -96
- package/dist/bus/saved-dispatcher-binding.d.ts.map +0 -1
- package/dist/bus/saved-dispatcher-binding.js +0 -35
- package/dist/bus/saved-dispatcher-binding.js.map +0 -1
- package/dist/delivery/delivery-run-coordinator.d.ts +0 -218
- package/dist/delivery/delivery-run-coordinator.d.ts.map +0 -1
- package/dist/delivery/delivery-run-coordinator.js +0 -609
- package/dist/delivery/delivery-run-coordinator.js.map +0 -1
- package/dist/delivery/parked-delivery-obligations.d.ts +0 -182
- package/dist/delivery/parked-delivery-obligations.d.ts.map +0 -1
- package/dist/delivery/parked-delivery-obligations.js +0 -390
- package/dist/delivery/parked-delivery-obligations.js.map +0 -1
- package/dist/server/environment-attachment.d.ts +0 -356
- package/dist/server/environment-attachment.d.ts.map +0 -1
- package/dist/server/environment-attachment.js +0 -2062
- package/dist/server/environment-attachment.js.map +0 -1
- package/dist/server/environment-delivery-records.d.ts +0 -77
- package/dist/server/environment-delivery-records.d.ts.map +0 -1
- package/dist/server/environment-delivery-records.js +0 -282
- package/dist/server/environment-delivery-records.js.map +0 -1
- package/dist/server/environment-delivery-worker.d.ts +0 -161
- package/dist/server/environment-delivery-worker.d.ts.map +0 -1
- package/dist/server/environment-delivery-worker.js +0 -673
- package/dist/server/environment-delivery-worker.js.map +0 -1
|
@@ -19,7 +19,7 @@ import { LogLayer, StructuredTransport } from "loglayer";
|
|
|
19
19
|
import { runningServerAccess } from "./server.js";
|
|
20
20
|
import { emitServerWarning } from "./server-log.js";
|
|
21
21
|
import { NodeCoordinator } from "./node-coordinator.js";
|
|
22
|
-
import {
|
|
22
|
+
import { managedDeliveryGate } from "./managed-delivery-gate.js";
|
|
23
23
|
import { managedChildSubscriptionAccess } from "./managed-child-subscription.js";
|
|
24
24
|
import { InMemorySubscriptionRegistry } from "../stand/subscription-registry.js";
|
|
25
25
|
const childMarker = "SPINE_MANAGED_SERVER_CHILD";
|
|
@@ -106,115 +106,7 @@ const ManagedServerValues = Object.freeze({
|
|
|
106
106
|
}
|
|
107
107
|
throw error;
|
|
108
108
|
}
|
|
109
|
-
|
|
110
|
-
// disconnect handler below handles shutdown; this listener prevents that
|
|
111
|
-
// transport detail from becoming an unhandled child-process exception.
|
|
112
|
-
const containIpcError = () => undefined;
|
|
113
|
-
process.on("error", containIpcError);
|
|
114
|
-
managedChildSubscriptionAccess.install((subscriptionId) => {
|
|
115
|
-
const notification = ManagedServerValues.send({
|
|
116
|
-
type: "subscription-installed",
|
|
117
|
-
slot: process.env[slotMarker],
|
|
118
|
-
incarnation: process.env[incarnationMarker],
|
|
119
|
-
subscriptionId,
|
|
120
|
-
});
|
|
121
|
-
// spine-log-boundary: server.managed_replica_subscription_ipc
|
|
122
|
-
void notification.catch(() => undefined);
|
|
123
|
-
});
|
|
124
|
-
let activated = false;
|
|
125
|
-
let releaseActivation;
|
|
126
|
-
const activation = new Promise((resolve) => {
|
|
127
|
-
releaseActivation = resolve;
|
|
128
|
-
});
|
|
129
|
-
let closing;
|
|
130
|
-
const close = () => {
|
|
131
|
-
if (closing !== undefined)
|
|
132
|
-
return closing;
|
|
133
|
-
const draining = ManagedServerValues.send({
|
|
134
|
-
type: "draining",
|
|
135
|
-
slot: process.env[slotMarker],
|
|
136
|
-
incarnation: process.env[incarnationMarker],
|
|
137
|
-
});
|
|
138
|
-
// spine-log-boundary: server.managed_replica_drain_ipc
|
|
139
|
-
const attempt = draining
|
|
140
|
-
.catch(() => undefined)
|
|
141
|
-
.then(() => {
|
|
142
|
-
environmentDeliveryWorkerAccess.cancelManagedChild();
|
|
143
|
-
managedChildSubscriptionAccess.clear();
|
|
144
|
-
releaseActivation();
|
|
145
|
-
return runningServerAccess.drainDelivery(server);
|
|
146
|
-
})
|
|
147
|
-
.then(() => {
|
|
148
|
-
return server.close();
|
|
149
|
-
})
|
|
150
|
-
.then(() => {
|
|
151
|
-
const closed = ManagedServerValues.send({
|
|
152
|
-
type: "closed",
|
|
153
|
-
slot: process.env[slotMarker],
|
|
154
|
-
incarnation: process.env[incarnationMarker],
|
|
155
|
-
});
|
|
156
|
-
// spine-log-boundary: server.managed_replica_closed_ipc
|
|
157
|
-
return closed.catch(() => undefined);
|
|
158
|
-
}, (error) => {
|
|
159
|
-
const failed = ManagedServerValues.send({
|
|
160
|
-
type: "close-failed",
|
|
161
|
-
slot: process.env[slotMarker],
|
|
162
|
-
incarnation: process.env[incarnationMarker],
|
|
163
|
-
});
|
|
164
|
-
// spine-log-boundary: server.managed_replica_close_failed_ipc
|
|
165
|
-
void failed.catch(() => undefined);
|
|
166
|
-
if (closing === attempt)
|
|
167
|
-
closing = undefined;
|
|
168
|
-
throw error;
|
|
169
|
-
})
|
|
170
|
-
.then(() => {
|
|
171
|
-
process.off("disconnect", onDisconnect);
|
|
172
|
-
if (process.connected && typeof process.disconnect === "function")
|
|
173
|
-
process.disconnect();
|
|
174
|
-
process.off("error", containIpcError);
|
|
175
|
-
process.off("message", onMessage);
|
|
176
|
-
});
|
|
177
|
-
closing = attempt;
|
|
178
|
-
return attempt;
|
|
179
|
-
};
|
|
180
|
-
const onMessage = (message) => {
|
|
181
|
-
if (message.type === "activate" && !activated) {
|
|
182
|
-
activated = true;
|
|
183
|
-
environmentDeliveryWorkerAccess.activateManagedChild();
|
|
184
|
-
releaseActivation();
|
|
185
|
-
}
|
|
186
|
-
else if (message.type === "close") {
|
|
187
|
-
ManagedServerValues.closeFromEvent(close);
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
const onDisconnect = () => {
|
|
191
|
-
ManagedServerValues.closeFromEvent(close);
|
|
192
|
-
};
|
|
193
|
-
process.on("message", onMessage);
|
|
194
|
-
process.once("disconnect", onDisconnect);
|
|
195
|
-
try {
|
|
196
|
-
await ManagedServerValues.send({
|
|
197
|
-
type: "synchronizing",
|
|
198
|
-
slot: process.env[slotMarker],
|
|
199
|
-
incarnation: process.env[incarnationMarker],
|
|
200
|
-
endpoint: server.baseUrl,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
catch (error) {
|
|
204
|
-
await close();
|
|
205
|
-
throw error;
|
|
206
|
-
}
|
|
207
|
-
await activation;
|
|
208
|
-
await ManagedServerValues.send({
|
|
209
|
-
type: "ready",
|
|
210
|
-
slot: process.env[slotMarker],
|
|
211
|
-
incarnation: process.env[incarnationMarker],
|
|
212
|
-
endpoint: server.baseUrl,
|
|
213
|
-
});
|
|
214
|
-
return {
|
|
215
|
-
ready: true,
|
|
216
|
-
close,
|
|
217
|
-
};
|
|
109
|
+
return new ManagedChildLifecycle(server).start();
|
|
218
110
|
},
|
|
219
111
|
/**
|
|
220
112
|
* Starts a coordinator for the configured complete-replica cohort.
|
|
@@ -277,6 +169,177 @@ const ManagedServerValues = Object.freeze({
|
|
|
277
169
|
});
|
|
278
170
|
},
|
|
279
171
|
});
|
|
172
|
+
/**
|
|
173
|
+
* Runs the private child IPC lifecycle while keeping its listener open for relay cleanup.
|
|
174
|
+
*/
|
|
175
|
+
class ManagedChildLifecycle {
|
|
176
|
+
#activation = ManagedServerCoordinatorValues.deferred();
|
|
177
|
+
#serverCloseRelease = ManagedServerCoordinatorValues.deferred();
|
|
178
|
+
#server;
|
|
179
|
+
#activated = false;
|
|
180
|
+
#waitForParentClose = false;
|
|
181
|
+
#closing;
|
|
182
|
+
/**
|
|
183
|
+
* Creates the lifecycle around an assembled child server.
|
|
184
|
+
*
|
|
185
|
+
* @param server The already assembled and synchronized child server.
|
|
186
|
+
*/
|
|
187
|
+
constructor(server) {
|
|
188
|
+
this.#server = server;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
*
|
|
192
|
+
* Starts the child and waits for Coordinator activation.
|
|
193
|
+
*
|
|
194
|
+
* @returns A child handle after the private READY notification.
|
|
195
|
+
*/
|
|
196
|
+
async start() {
|
|
197
|
+
process.on("error", this.#containIpcError);
|
|
198
|
+
managedChildSubscriptionAccess.install((subscriptionId) => {
|
|
199
|
+
const notification = ManagedServerValues.send({
|
|
200
|
+
type: "subscription-installed",
|
|
201
|
+
slot: process.env[slotMarker],
|
|
202
|
+
incarnation: process.env[incarnationMarker],
|
|
203
|
+
subscriptionId,
|
|
204
|
+
});
|
|
205
|
+
// spine-log-boundary: server.managed_replica_subscription_ipc
|
|
206
|
+
void notification.catch(() => undefined);
|
|
207
|
+
});
|
|
208
|
+
process.on("message", this.#onMessage);
|
|
209
|
+
process.once("disconnect", this.#onDisconnect);
|
|
210
|
+
try {
|
|
211
|
+
await this.#notify("synchronizing", this.#server.baseUrl);
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
this.#serverCloseRelease.resolve(undefined);
|
|
215
|
+
await this.close();
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
await this.#activation.promise;
|
|
219
|
+
await this.#notify("ready", this.#server.baseUrl);
|
|
220
|
+
return { ready: true, close: () => this.close() };
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
*
|
|
224
|
+
* Closes Delivery before closing the child listener and its server.
|
|
225
|
+
*
|
|
226
|
+
* @returns The shared close attempt; a failed attempt may be retried.
|
|
227
|
+
*/
|
|
228
|
+
close() {
|
|
229
|
+
this.#closing ??= this.#closeOnce().catch((error) => {
|
|
230
|
+
this.#closing = undefined;
|
|
231
|
+
throw error;
|
|
232
|
+
});
|
|
233
|
+
return this.#closing;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
*
|
|
237
|
+
* Performs child shutdown and observes a failed completion notification.
|
|
238
|
+
*
|
|
239
|
+
* @returns Completion after the child server closes.
|
|
240
|
+
*/
|
|
241
|
+
async #closeOnce() {
|
|
242
|
+
try {
|
|
243
|
+
await this.#drainDelivery();
|
|
244
|
+
await this.#closeServerAfterRelayCleanup();
|
|
245
|
+
// spine-log-boundary: server.managed_replica_closed_ipc
|
|
246
|
+
await this.#notify("closed").catch(() => undefined);
|
|
247
|
+
this.#disconnect();
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
// spine-log-boundary: server.managed_replica_close_failed_ipc
|
|
251
|
+
await this.#notify("close-failed").catch(() => undefined);
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
*
|
|
257
|
+
* Stops new child Delivery work and awaits admitted effects.
|
|
258
|
+
*
|
|
259
|
+
* @returns Completion when admitted work drains.
|
|
260
|
+
*/
|
|
261
|
+
async #drainDelivery() {
|
|
262
|
+
managedDeliveryGate.cancel();
|
|
263
|
+
managedChildSubscriptionAccess.clear();
|
|
264
|
+
this.#activation.resolve(undefined);
|
|
265
|
+
// spine-log-boundary: server.managed_replica_drain_ipc
|
|
266
|
+
void this.#notify("draining").catch(() => undefined);
|
|
267
|
+
await runningServerAccess.drainDelivery(this.#server);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
*
|
|
271
|
+
* Waits for parent relay cancellation before closing the child listener.
|
|
272
|
+
*
|
|
273
|
+
* @returns Completion after final server close.
|
|
274
|
+
*/
|
|
275
|
+
async #closeServerAfterRelayCleanup() {
|
|
276
|
+
// spine-log-boundary: server.managed_replica_delivery_drained_ipc
|
|
277
|
+
void this.#notify("delivery-drained").catch(() => undefined);
|
|
278
|
+
if (this.#waitForParentClose)
|
|
279
|
+
await this.#serverCloseRelease.promise;
|
|
280
|
+
await this.#server.close();
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
*
|
|
284
|
+
* Sends one child lifecycle notification on the private IPC channel.
|
|
285
|
+
*
|
|
286
|
+
* @param type The lifecycle phase to report.
|
|
287
|
+
* @param endpoint The listening endpoint for readiness messages.
|
|
288
|
+
* @returns Completion after the parent accepts the frame.
|
|
289
|
+
*/
|
|
290
|
+
#notify(type, endpoint) {
|
|
291
|
+
return ManagedServerValues.send({
|
|
292
|
+
type,
|
|
293
|
+
slot: process.env[slotMarker],
|
|
294
|
+
incarnation: process.env[incarnationMarker],
|
|
295
|
+
...(endpoint === undefined ? {} : { endpoint }),
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
*
|
|
300
|
+
* Releases child startup and close gates for matching parent messages.
|
|
301
|
+
*
|
|
302
|
+
* @param message The private parent frame.
|
|
303
|
+
*/
|
|
304
|
+
#onMessage = (message) => {
|
|
305
|
+
if (message.type === "activate" && !this.#activated) {
|
|
306
|
+
this.#activated = true;
|
|
307
|
+
void managedDeliveryGate.activate().then(() => {
|
|
308
|
+
this.#activation.resolve(undefined);
|
|
309
|
+
}, (error) => {
|
|
310
|
+
this.#activation.reject(error);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
else if (message.type === "close") {
|
|
314
|
+
this.#waitForParentClose = true;
|
|
315
|
+
ManagedServerValues.closeFromEvent(() => this.close());
|
|
316
|
+
}
|
|
317
|
+
else if (message.type === "finish-close") {
|
|
318
|
+
this.#serverCloseRelease.resolve(undefined);
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
/**
|
|
322
|
+
* Releases the final close phase if the parent disappears.
|
|
323
|
+
*/
|
|
324
|
+
#onDisconnect = () => {
|
|
325
|
+
this.#serverCloseRelease.resolve(undefined);
|
|
326
|
+
ManagedServerValues.closeFromEvent(() => this.close());
|
|
327
|
+
};
|
|
328
|
+
/**
|
|
329
|
+
* Contains IPC EPIPE after an already disappeared parent.
|
|
330
|
+
*/
|
|
331
|
+
#containIpcError = () => undefined;
|
|
332
|
+
/**
|
|
333
|
+
* Removes private listeners once the child server has closed.
|
|
334
|
+
*/
|
|
335
|
+
#disconnect() {
|
|
336
|
+
process.off("disconnect", this.#onDisconnect);
|
|
337
|
+
if (process.connected && typeof process.disconnect === "function")
|
|
338
|
+
process.disconnect();
|
|
339
|
+
process.off("error", this.#containIpcError);
|
|
340
|
+
process.off("message", this.#onMessage);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
280
343
|
/**
|
|
281
344
|
*
|
|
282
345
|
* Coordinates the parent-only lifecycle of managed application replicas.
|
|
@@ -288,6 +351,8 @@ export class ManagedServerCoordinator {
|
|
|
288
351
|
#restart;
|
|
289
352
|
#options;
|
|
290
353
|
#dependencies;
|
|
354
|
+
#terminalChildFailure;
|
|
355
|
+
#drainStart;
|
|
291
356
|
#closing = false;
|
|
292
357
|
#starts = 0;
|
|
293
358
|
#ready;
|
|
@@ -394,6 +459,16 @@ export class ManagedServerCoordinator {
|
|
|
394
459
|
this.#drainStarts();
|
|
395
460
|
return;
|
|
396
461
|
}
|
|
462
|
+
this.#installReplica(slot, incarnation, child);
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Records a spawned child and observes its private lifecycle messages.
|
|
466
|
+
*
|
|
467
|
+
* @param slot The configured replica slot.
|
|
468
|
+
* @param incarnation The current child incarnation identifier.
|
|
469
|
+
* @param child The spawned process handle.
|
|
470
|
+
*/
|
|
471
|
+
#installReplica(slot, incarnation, child) {
|
|
397
472
|
const replica = {
|
|
398
473
|
slot: slot.slot,
|
|
399
474
|
incarnation,
|
|
@@ -404,6 +479,9 @@ export class ManagedServerCoordinator {
|
|
|
404
479
|
draining: false,
|
|
405
480
|
subscriptionWaiters: new Map(),
|
|
406
481
|
closeAttempt: undefined,
|
|
482
|
+
closePromise: undefined,
|
|
483
|
+
closeFailure: undefined,
|
|
484
|
+
forcedFailure: undefined,
|
|
407
485
|
expectedExit: false,
|
|
408
486
|
terminal: false,
|
|
409
487
|
};
|
|
@@ -428,43 +506,86 @@ export class ManagedServerCoordinator {
|
|
|
428
506
|
* @param message The untrusted child IPC notification.
|
|
429
507
|
*/
|
|
430
508
|
#onMessage(slot, replica, message) {
|
|
509
|
+
if (this.#onCloseMessage(slot, replica, message))
|
|
510
|
+
return;
|
|
511
|
+
if (ManagedServerCoordinatorValues.subscriptionInstalledMessage(message, slot.slot, replica.incarnation)) {
|
|
512
|
+
if (slot.replica === replica)
|
|
513
|
+
this.#settleSubscriptionWaiter(replica, message.subscriptionId);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
if (this.#onTransitionMessage(slot, replica, message))
|
|
517
|
+
return;
|
|
518
|
+
this.#onReadyMessage(slot, replica, message);
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Settles the private child shutdown notifications.
|
|
522
|
+
*
|
|
523
|
+
* @param slot The reported replica slot.
|
|
524
|
+
* @param replica The matching child incarnation.
|
|
525
|
+
* @param message The candidate notification.
|
|
526
|
+
* @returns Whether a close-phase notification was consumed.
|
|
527
|
+
*/
|
|
528
|
+
#onCloseMessage(slot, replica, message) {
|
|
431
529
|
const closeResult = ManagedServerCoordinatorValues.closeResultMessage(message, slot.slot, replica.incarnation);
|
|
432
530
|
if (closeResult !== undefined) {
|
|
433
531
|
if (slot.replica !== replica || !replica.draining || replica.closeAttempt === undefined)
|
|
434
|
-
return;
|
|
532
|
+
return true;
|
|
435
533
|
if (closeResult === "closed")
|
|
436
534
|
replica.closeAttempt.outcome.resolve(undefined);
|
|
437
|
-
else
|
|
535
|
+
else {
|
|
536
|
+
replica.closeAttempt.drained.reject(new Error("Managed child Delivery drain failed."));
|
|
438
537
|
replica.closeAttempt.outcome.reject(new Error("Managed child server close failed."));
|
|
439
|
-
|
|
538
|
+
}
|
|
539
|
+
return true;
|
|
440
540
|
}
|
|
441
|
-
if (ManagedServerCoordinatorValues.
|
|
442
|
-
if (
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
return;
|
|
541
|
+
if (ManagedServerCoordinatorValues.deliveryDrainedMessage(message, slot.slot, replica.incarnation)) {
|
|
542
|
+
if (replica.closeAttempt !== undefined)
|
|
543
|
+
replica.closeAttempt.drainedAcknowledged = true;
|
|
544
|
+
replica.closeAttempt?.drained.resolve(undefined);
|
|
545
|
+
return true;
|
|
446
546
|
}
|
|
547
|
+
return false;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Advances a synchronizing or draining child without completing public readiness.
|
|
551
|
+
*
|
|
552
|
+
* @param slot The reported replica slot.
|
|
553
|
+
* @param replica The matching child incarnation.
|
|
554
|
+
* @param message The candidate notification.
|
|
555
|
+
* @returns Whether a transition notification was consumed.
|
|
556
|
+
*/
|
|
557
|
+
#onTransitionMessage(slot, replica, message) {
|
|
447
558
|
if (ManagedServerCoordinatorValues.synchronizingMessage(message, slot.slot, replica.incarnation)) {
|
|
448
559
|
if (slot.replica !== replica || replica.endpoint !== undefined)
|
|
449
|
-
return;
|
|
560
|
+
return true;
|
|
450
561
|
replica.endpoint = message.endpoint;
|
|
451
562
|
replica.synchronizing = true;
|
|
452
563
|
this.#notifyReadyMembers();
|
|
453
564
|
if (this.#nodeCoordinator === undefined)
|
|
454
565
|
replica.child.send({ type: "activate" }, () => undefined);
|
|
455
|
-
return;
|
|
566
|
+
return true;
|
|
456
567
|
}
|
|
457
568
|
if (ManagedServerCoordinatorValues.drainingMessage(message, slot.slot, replica.incarnation)) {
|
|
458
569
|
if (slot.replica !== replica)
|
|
459
|
-
return;
|
|
570
|
+
return true;
|
|
460
571
|
replica.closeAttempt?.acknowledged.resolve(undefined);
|
|
461
572
|
if (replica.draining)
|
|
462
|
-
return;
|
|
573
|
+
return true;
|
|
463
574
|
replica.draining = true;
|
|
464
575
|
replica.expectedExit = true;
|
|
465
576
|
this.#notifyReadyMembers();
|
|
466
|
-
return;
|
|
577
|
+
return true;
|
|
467
578
|
}
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Publishes one fully ready child after its synchronization phase.
|
|
583
|
+
*
|
|
584
|
+
* @param slot The reported replica slot.
|
|
585
|
+
* @param replica The matching child incarnation.
|
|
586
|
+
* @param message The candidate notification.
|
|
587
|
+
*/
|
|
588
|
+
#onReadyMessage(slot, replica, message) {
|
|
468
589
|
if (!ManagedServerCoordinatorValues.readyMessage(message, slot.slot, replica.incarnation))
|
|
469
590
|
return;
|
|
470
591
|
if (slot.replica !== replica || replica.readyAt !== undefined || this.#closing)
|
|
@@ -490,6 +611,9 @@ export class ManagedServerCoordinator {
|
|
|
490
611
|
if (slot.replica !== replica || replica.terminal)
|
|
491
612
|
return;
|
|
492
613
|
replica.terminal = true;
|
|
614
|
+
if (this.#closing && replica.closeFailure !== undefined)
|
|
615
|
+
this.#terminalChildFailure ??= replica.closeFailure;
|
|
616
|
+
this.#failIncompleteDrain(replica.closeAttempt);
|
|
493
617
|
this.#clearSubscriptionWaiters(replica, new Error("Managed child exited before subscription installed."));
|
|
494
618
|
slot.replica = undefined;
|
|
495
619
|
this.#notifyReadyMembers();
|
|
@@ -499,10 +623,10 @@ export class ManagedServerCoordinator {
|
|
|
499
623
|
}
|
|
500
624
|
if (this.#closing || replica.expectedExit)
|
|
501
625
|
return;
|
|
502
|
-
|
|
503
|
-
this.#dependencies.clock.now() - replica.readyAt >= this.#restart.healthyReadyMs
|
|
626
|
+
const healthy = replica.readyAt !== undefined &&
|
|
627
|
+
this.#dependencies.clock.now() - replica.readyAt >= this.#restart.healthyReadyMs;
|
|
628
|
+
if (healthy)
|
|
504
629
|
slot.failures = 0;
|
|
505
|
-
}
|
|
506
630
|
// spine-log-boundary: server.managed_replica_exit
|
|
507
631
|
emitServerWarning(lifecycleLogger, "Managed replica exited unexpectedly.", {
|
|
508
632
|
operation: "managed_replica.exit",
|
|
@@ -514,6 +638,20 @@ export class ManagedServerCoordinator {
|
|
|
514
638
|
this.#scheduleReplacement(slot);
|
|
515
639
|
this.#drainStarts();
|
|
516
640
|
}
|
|
641
|
+
/**
|
|
642
|
+
* Retains an exited child's missing drain acknowledgement across close attempts.
|
|
643
|
+
*
|
|
644
|
+
* @param closeAttempt The pending close attempt, if any.
|
|
645
|
+
*/
|
|
646
|
+
#failIncompleteDrain(closeAttempt) {
|
|
647
|
+
if (closeAttempt === undefined || closeAttempt.drainedAcknowledged)
|
|
648
|
+
return;
|
|
649
|
+
const failure = new Error("Managed child exited before Delivery drained.");
|
|
650
|
+
closeAttempt.exitFailure = failure;
|
|
651
|
+
if (this.#closing)
|
|
652
|
+
this.#terminalChildFailure ??= failure;
|
|
653
|
+
closeAttempt.drained.reject(failure);
|
|
654
|
+
}
|
|
517
655
|
/**
|
|
518
656
|
* Retires a child after a process error and starts bounded termination.
|
|
519
657
|
*
|
|
@@ -571,37 +709,7 @@ export class ManagedServerCoordinator {
|
|
|
571
709
|
const closing = this.#close;
|
|
572
710
|
if (closing !== undefined)
|
|
573
711
|
return closing;
|
|
574
|
-
const close = (
|
|
575
|
-
this.#closing = true;
|
|
576
|
-
this.#removeSignalHandlers();
|
|
577
|
-
for (const slot of this.#slots) {
|
|
578
|
-
if (slot.replacementTimer !== undefined)
|
|
579
|
-
this.#dependencies.clock.clearTimeout(slot.replacementTimer);
|
|
580
|
-
slot.replacementTimer = undefined;
|
|
581
|
-
}
|
|
582
|
-
// `beginDrain()` makes the unary snapshot empty synchronously. Start every
|
|
583
|
-
// child quiescence attempt without waiting for its reconciliation promise:
|
|
584
|
-
// child close IPC must be issued in this turn, while the Coordinator keeps
|
|
585
|
-
// subscription relays alive until every child has settled.
|
|
586
|
-
const draining = this.#nodeCoordinator?.beginDrain();
|
|
587
|
-
for (const slot of this.#slots)
|
|
588
|
-
if (slot.replica !== undefined)
|
|
589
|
-
this.#clearSubscriptionWaiters(slot.replica, new Error("Managed application is closing before subscription installed."));
|
|
590
|
-
const childCloses = [
|
|
591
|
-
...this.#slots.map((slot) => this.#closeReplica(slot.replica)),
|
|
592
|
-
...[...this.#retired].map((replica) => this.#closeRetired(replica)),
|
|
593
|
-
];
|
|
594
|
-
const results = await Promise.allSettled([draining, ...childCloses]);
|
|
595
|
-
const failures = [];
|
|
596
|
-
for (const result of results)
|
|
597
|
-
if (result.status === "rejected")
|
|
598
|
-
failures.push(result.reason);
|
|
599
|
-
if (failures.length === 1)
|
|
600
|
-
throw failures[0];
|
|
601
|
-
if (failures.length > 1)
|
|
602
|
-
throw new AggregateError(failures, "Managed child quiescence failed during drain.");
|
|
603
|
-
await this.#nodeCoordinator?.close();
|
|
604
|
-
})();
|
|
712
|
+
const close = this.#closeGracefully();
|
|
605
713
|
this.#close = close;
|
|
606
714
|
void close.then(undefined, () => {
|
|
607
715
|
if (this.#close === close)
|
|
@@ -609,6 +717,91 @@ export class ManagedServerCoordinator {
|
|
|
609
717
|
});
|
|
610
718
|
return close;
|
|
611
719
|
}
|
|
720
|
+
/**
|
|
721
|
+
* Stops replacement starts and awaits all cleanup phases.
|
|
722
|
+
*/
|
|
723
|
+
async #closeGracefully() {
|
|
724
|
+
this.#closing = true;
|
|
725
|
+
this.#removeSignalHandlers();
|
|
726
|
+
for (const slot of this.#slots) {
|
|
727
|
+
if (slot.replacementTimer !== undefined)
|
|
728
|
+
this.#dependencies.clock.clearTimeout(slot.replacementTimer);
|
|
729
|
+
slot.replacementTimer = undefined;
|
|
730
|
+
}
|
|
731
|
+
await this.#drainAndCloseReplicas();
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Drains admitted child work, removes relays while listeners live, then closes children.
|
|
735
|
+
*
|
|
736
|
+
* @returns Completion after Coordinator and child cleanup attempts settle.
|
|
737
|
+
*/
|
|
738
|
+
async #drainAndCloseReplicas() {
|
|
739
|
+
this.#drainStart ??= this.#nodeCoordinator?.beginDrain() ?? Promise.resolve();
|
|
740
|
+
const closing = new Error("Managed application is closing before subscription installed.");
|
|
741
|
+
for (const slot of this.#slots)
|
|
742
|
+
if (slot.replica !== undefined)
|
|
743
|
+
this.#clearSubscriptionWaiters(slot.replica, closing);
|
|
744
|
+
const replicas = this.#slots.flatMap((slot) => slot.replica === undefined ? [] : [slot.replica]);
|
|
745
|
+
const childCloses = [
|
|
746
|
+
...replicas.map((replica) => this.#closeReplica(replica)),
|
|
747
|
+
...[...this.#retired].map((replica) => this.#closeRetired(replica)),
|
|
748
|
+
];
|
|
749
|
+
const children = Promise.allSettled(childCloses);
|
|
750
|
+
const failures = await this.#awaitDeliveryDrains(replicas);
|
|
751
|
+
await this.#finishClose(replicas, children, failures);
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Cancels relays, releases children, and closes the Coordinator after a drain attempt.
|
|
755
|
+
* @param replicas The active children being closed.
|
|
756
|
+
* @param children The already-running bounded child close attempts.
|
|
757
|
+
* @param failures Errors collected from the drain phase.
|
|
758
|
+
*/
|
|
759
|
+
async #finishClose(replicas, children, failures) {
|
|
760
|
+
try {
|
|
761
|
+
await this.#nodeCoordinator?.closeSubscriptions();
|
|
762
|
+
}
|
|
763
|
+
catch (error) {
|
|
764
|
+
failures.push(error);
|
|
765
|
+
}
|
|
766
|
+
for (const replica of replicas) {
|
|
767
|
+
try {
|
|
768
|
+
if (replica.child.connected)
|
|
769
|
+
replica.child.send({ type: "finish-close" }, () => undefined);
|
|
770
|
+
}
|
|
771
|
+
catch (error) {
|
|
772
|
+
failures.push(error);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
const childResults = await children;
|
|
776
|
+
const coordinator = Promise.resolve().then(() => this.#nodeCoordinator?.close());
|
|
777
|
+
const [coordinatorResult] = await Promise.allSettled([coordinator]);
|
|
778
|
+
for (const result of childResults)
|
|
779
|
+
if (result.status === "rejected" && !failures.includes(result.reason))
|
|
780
|
+
failures.push(result.reason);
|
|
781
|
+
if (coordinatorResult.status === "rejected")
|
|
782
|
+
failures.push(coordinatorResult.reason);
|
|
783
|
+
if (this.#terminalChildFailure !== undefined && !failures.includes(this.#terminalChildFailure))
|
|
784
|
+
failures.push(this.#terminalChildFailure);
|
|
785
|
+
if (failures.length === 1)
|
|
786
|
+
throw failures[0];
|
|
787
|
+
if (failures.length > 1)
|
|
788
|
+
throw new AggregateError(failures, "Managed child quiescence failed during drain.");
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Waits for actual child Delivery drains without awaiting final child exits.
|
|
792
|
+
*
|
|
793
|
+
* @param replicas The active child incarnations.
|
|
794
|
+
* @returns Drain failures to report after child and Coordinator cleanup.
|
|
795
|
+
*/
|
|
796
|
+
async #awaitDeliveryDrains(replicas) {
|
|
797
|
+
const deliveryDrains = replicas.map((replica) => replica.closeAttempt?.drained.promise ?? Promise.resolve());
|
|
798
|
+
const results = await Promise.allSettled([this.#drainStart, ...deliveryDrains]);
|
|
799
|
+
const failures = [];
|
|
800
|
+
for (const result of results)
|
|
801
|
+
if (result.status === "rejected")
|
|
802
|
+
failures.push(result.reason);
|
|
803
|
+
return failures;
|
|
804
|
+
}
|
|
612
805
|
/**
|
|
613
806
|
* Closes the current replica through the bounded child shutdown procedure.
|
|
614
807
|
*
|
|
@@ -616,7 +809,28 @@ export class ManagedServerCoordinator {
|
|
|
616
809
|
* @returns Completion after child shutdown.
|
|
617
810
|
*/
|
|
618
811
|
#closeReplica(replica) {
|
|
619
|
-
|
|
812
|
+
if (replica === undefined)
|
|
813
|
+
return Promise.resolve();
|
|
814
|
+
if (replica.closePromise !== undefined)
|
|
815
|
+
return replica.closePromise;
|
|
816
|
+
replica.closeFailure = undefined;
|
|
817
|
+
const close = ManagedServerCoordinatorValues.close(replica, this.#dependencies.clock);
|
|
818
|
+
replica.closePromise = close;
|
|
819
|
+
void close.then(() => {
|
|
820
|
+
replica.closeFailure = undefined;
|
|
821
|
+
}, (error) => {
|
|
822
|
+
replica.closeFailure = error;
|
|
823
|
+
if (replica.forcedFailure !== undefined || replica.terminal)
|
|
824
|
+
this.#terminalChildFailure ??= replica.forcedFailure ?? error;
|
|
825
|
+
});
|
|
826
|
+
// spine-log-boundary: server.managed_replica_close_finally_observer
|
|
827
|
+
void close
|
|
828
|
+
.finally(() => {
|
|
829
|
+
if (replica.closePromise === close)
|
|
830
|
+
replica.closePromise = undefined;
|
|
831
|
+
})
|
|
832
|
+
.catch(() => undefined);
|
|
833
|
+
return close;
|
|
620
834
|
}
|
|
621
835
|
/**
|
|
622
836
|
* Terminates a retired replica and removes it from the pending set.
|
|
@@ -928,6 +1142,8 @@ const ManagedServerCoordinatorValues = Object.freeze({
|
|
|
928
1142
|
resolve = next;
|
|
929
1143
|
reject = fail;
|
|
930
1144
|
});
|
|
1145
|
+
// spine-log-boundary: server.managed_deferred_rejection_observer
|
|
1146
|
+
void promise.catch(() => undefined);
|
|
931
1147
|
return { promise, resolve, reject };
|
|
932
1148
|
},
|
|
933
1149
|
/**
|
|
@@ -943,7 +1159,7 @@ const ManagedServerCoordinatorValues = Object.freeze({
|
|
|
943
1159
|
},
|
|
944
1160
|
dependencies: {
|
|
945
1161
|
clock: {
|
|
946
|
-
now: () => Time.
|
|
1162
|
+
now: () => Time.monotonicTime(),
|
|
947
1163
|
setTimeout: (onTimeout, delay) => setTimeout(onTimeout, delay),
|
|
948
1164
|
clearTimeout: (timer) => {
|
|
949
1165
|
clearTimeout(timer);
|
|
@@ -1053,6 +1269,22 @@ const ManagedServerCoordinatorValues = Object.freeze({
|
|
|
1053
1269
|
candidate.slot === String(slot) &&
|
|
1054
1270
|
candidate.incarnation === incarnation);
|
|
1055
1271
|
},
|
|
1272
|
+
/**
|
|
1273
|
+
* Matches the child's completed Delivery drain before its listener closes.
|
|
1274
|
+
*
|
|
1275
|
+
* @param message The candidate child IPC notification.
|
|
1276
|
+
* @param slot The expected replica slot.
|
|
1277
|
+
* @param incarnation The expected child incarnation token.
|
|
1278
|
+
* @returns Whether the notification belongs to that child.
|
|
1279
|
+
*/
|
|
1280
|
+
deliveryDrainedMessage(message, slot, incarnation) {
|
|
1281
|
+
if (typeof message !== "object" || message === null)
|
|
1282
|
+
return false;
|
|
1283
|
+
const candidate = message;
|
|
1284
|
+
return (candidate.type === "delivery-drained" &&
|
|
1285
|
+
candidate.slot === String(slot) &&
|
|
1286
|
+
candidate.incarnation === incarnation);
|
|
1287
|
+
},
|
|
1056
1288
|
/**
|
|
1057
1289
|
* Matches a CLOSED or CLOSE-FAILED notification from the expected child.
|
|
1058
1290
|
*
|
|
@@ -1090,35 +1322,93 @@ const ManagedServerCoordinatorValues = Object.freeze({
|
|
|
1090
1322
|
}));
|
|
1091
1323
|
const closeAttempt = {
|
|
1092
1324
|
acknowledged: ManagedServerCoordinatorValues.deferred(),
|
|
1325
|
+
drained: ManagedServerCoordinatorValues.deferred(),
|
|
1093
1326
|
outcome: ManagedServerCoordinatorValues.deferred(),
|
|
1094
1327
|
};
|
|
1095
1328
|
replica.closeAttempt = closeAttempt;
|
|
1096
1329
|
try {
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
]);
|
|
1103
|
-
if (await ManagedServerCoordinatorValues.within(observation, closeGraceMs, clock)) {
|
|
1104
|
-
if ((await observation) === "exited")
|
|
1105
|
-
return;
|
|
1106
|
-
await Promise.race([
|
|
1107
|
-
closeAttempt.outcome.promise,
|
|
1108
|
-
exited.then(() => {
|
|
1109
|
-
throw new Error("Managed child exited before graceful close completed.");
|
|
1110
|
-
}),
|
|
1111
|
-
]);
|
|
1112
|
-
await ManagedServerCoordinatorValues.terminate(child, clock, exited);
|
|
1113
|
-
return;
|
|
1114
|
-
}
|
|
1115
|
-
await ManagedServerCoordinatorValues.terminate(child, clock, exited, false);
|
|
1330
|
+
await ManagedServerCoordinatorValues.awaitChildClose(child, closeAttempt, exited, clock);
|
|
1331
|
+
}
|
|
1332
|
+
catch (error) {
|
|
1333
|
+
replica.forcedFailure = closeAttempt.forcedFailure;
|
|
1334
|
+
throw error;
|
|
1116
1335
|
}
|
|
1117
1336
|
finally {
|
|
1118
1337
|
if (replica.closeAttempt === closeAttempt)
|
|
1119
1338
|
replica.closeAttempt = undefined;
|
|
1120
1339
|
}
|
|
1121
1340
|
},
|
|
1341
|
+
/**
|
|
1342
|
+
* Waits for child drain and final close, escalating only after bounded acknowledgement.
|
|
1343
|
+
*
|
|
1344
|
+
* @param child The child process to close.
|
|
1345
|
+
* @param closeAttempt Tracks the child's IPC phase acknowledgements.
|
|
1346
|
+
* @param exited Resolves when the child exits.
|
|
1347
|
+
* @param clock Supplies bounded shutdown timers.
|
|
1348
|
+
* @returns Completion after the child has exited or close failed.
|
|
1349
|
+
*/
|
|
1350
|
+
async awaitChildClose(child, closeAttempt, exited, clock) {
|
|
1351
|
+
if (child.connected)
|
|
1352
|
+
child.send({ type: "close" }, () => undefined);
|
|
1353
|
+
const observation = Promise.race([
|
|
1354
|
+
closeAttempt.acknowledged.promise.then(() => "acknowledged"),
|
|
1355
|
+
exited.then(() => "exited"),
|
|
1356
|
+
]);
|
|
1357
|
+
if (!(await ManagedServerCoordinatorValues.within(observation, closeGraceMs, clock))) {
|
|
1358
|
+
await this.missingDrainAck(child, closeAttempt, exited, clock);
|
|
1359
|
+
}
|
|
1360
|
+
if ((await observation) === "exited") {
|
|
1361
|
+
this.verifyExitedDrain(closeAttempt);
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
await ManagedServerCoordinatorValues.awaitChildOutcome(closeAttempt, exited);
|
|
1365
|
+
await ManagedServerCoordinatorValues.terminate(child, clock, exited);
|
|
1366
|
+
},
|
|
1367
|
+
/**
|
|
1368
|
+
* Rejects an unacknowledged Delivery drain after bounded child termination.
|
|
1369
|
+
*
|
|
1370
|
+
* @param child The unresponsive child process.
|
|
1371
|
+
* @param closeAttempt The current close attempt.
|
|
1372
|
+
* @param exited Resolves when the child exits.
|
|
1373
|
+
* @param clock Supplies bounded shutdown timers.
|
|
1374
|
+
* @returns No value; the missing acknowledgement always rejects.
|
|
1375
|
+
*/
|
|
1376
|
+
async missingDrainAck(child, closeAttempt, exited, clock) {
|
|
1377
|
+
const failure = new Error("Managed child did not acknowledge Delivery drain.");
|
|
1378
|
+
closeAttempt.forcedFailure = failure;
|
|
1379
|
+
closeAttempt.drained.reject(failure);
|
|
1380
|
+
try {
|
|
1381
|
+
await this.terminate(child, clock, exited, false);
|
|
1382
|
+
}
|
|
1383
|
+
catch (termination) {
|
|
1384
|
+
throw new AggregateError([failure, termination], "Managed child shutdown failed.");
|
|
1385
|
+
}
|
|
1386
|
+
throw failure;
|
|
1387
|
+
},
|
|
1388
|
+
/**
|
|
1389
|
+
* Rejects a child exit that preceded its Delivery drain acknowledgement.
|
|
1390
|
+
*
|
|
1391
|
+
* @param closeAttempt The observed close attempt.
|
|
1392
|
+
*/
|
|
1393
|
+
verifyExitedDrain(closeAttempt) {
|
|
1394
|
+
if (closeAttempt.exitFailure !== undefined)
|
|
1395
|
+
throw closeAttempt.exitFailure;
|
|
1396
|
+
},
|
|
1397
|
+
/**
|
|
1398
|
+
* Waits for the real child close outcome before process reaping.
|
|
1399
|
+
*
|
|
1400
|
+
* @param closeAttempt Tracks the child's close notifications.
|
|
1401
|
+
* @param exited Resolves when the child process exits.
|
|
1402
|
+
* @returns Completion after the child's final close fact.
|
|
1403
|
+
*/
|
|
1404
|
+
async awaitChildOutcome(closeAttempt, exited) {
|
|
1405
|
+
await Promise.race([
|
|
1406
|
+
closeAttempt.outcome.promise,
|
|
1407
|
+
exited.then(() => {
|
|
1408
|
+
throw new Error("Managed child exited before graceful close completed.");
|
|
1409
|
+
}),
|
|
1410
|
+
]);
|
|
1411
|
+
},
|
|
1122
1412
|
/**
|
|
1123
1413
|
* Waits for child exit, then escalates through SIGTERM and SIGKILL within bounded intervals.
|
|
1124
1414
|
*
|
|
@@ -1161,6 +1451,9 @@ const ManagedServerCoordinatorValues = Object.freeze({
|
|
|
1161
1451
|
void promise.then(() => {
|
|
1162
1452
|
clock.clearTimeout(timer);
|
|
1163
1453
|
resolve(true);
|
|
1454
|
+
}, () => {
|
|
1455
|
+
clock.clearTimeout(timer);
|
|
1456
|
+
resolve(true);
|
|
1164
1457
|
});
|
|
1165
1458
|
});
|
|
1166
1459
|
},
|