target-supabase-sdk 0.2.12 → 0.2.14
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/browser.d.ts +1 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +1 -1
- package/dist/chunks/enable-C7lFooTG.js +874 -0
- package/dist/chunks/enable-C7lFooTG.js.map +1 -0
- package/dist/chunks/list.api-BzbTJBsM.js.map +1 -1
- package/dist/chunks/{task-queue-H4mpSW8R.js → task-queue-qSnsfiIg.js} +111 -4
- package/dist/chunks/task-queue-qSnsfiIg.js.map +1 -0
- package/dist/node/env/index.d.ts +1 -1
- package/dist/node/env/index.d.ts.map +1 -1
- package/dist/node/node-runtime.base.d.ts +14 -1
- package/dist/node/node-runtime.base.d.ts.map +1 -1
- package/dist/node/node.api.d.ts.map +1 -1
- package/dist/node/node.constant.d.ts +3 -0
- package/dist/node/node.constant.d.ts.map +1 -0
- package/dist/node/node.utils.d.ts +0 -2
- package/dist/node/node.utils.d.ts.map +1 -1
- package/dist/node/preload/run-service-preload.d.ts.map +1 -1
- package/dist/node/preload/service-preload.interface.d.ts +3 -1
- package/dist/node/preload/service-preload.interface.d.ts.map +1 -1
- package/dist/node/process/managed-child-processes.d.ts.map +1 -1
- package/dist/node/readiness/index.d.ts +2 -0
- package/dist/node/readiness/index.d.ts.map +1 -1
- package/dist/node/{service-guard → readiness}/run-readiness-gate.d.ts +1 -1
- package/dist/node/readiness/run-readiness-gate.d.ts.map +1 -0
- package/dist/node/readiness/service-ready-gate.d.ts.map +1 -1
- package/dist/node/runtime-state/create-service-runtime-state-store.d.ts.map +1 -1
- package/dist/node/runtime-state/service-runtime-state.types.d.ts +16 -0
- package/dist/node/runtime-state/service-runtime-state.types.d.ts.map +1 -1
- package/dist/node/service-guard/index.d.ts +4 -4
- package/dist/node/service-guard/index.d.ts.map +1 -1
- package/dist/node/service-guard/register-service-guard-runner.d.ts +1 -2
- package/dist/node/service-guard/register-service-guard-runner.d.ts.map +1 -1
- package/dist/node/service-guard/registry-slot-guard-step.d.ts.map +1 -0
- package/dist/node/service-guard/run-service-guard-tick.d.ts.map +1 -1
- package/dist/node/service-guard/service-guard-node.d.ts +25 -2
- package/dist/node/service-guard/service-guard-node.d.ts.map +1 -1
- package/dist/node/service-guard/service-guard.interface.d.ts +25 -10
- package/dist/node/service-guard/service-guard.interface.d.ts.map +1 -1
- package/dist/node/service-host/claim-registry-slot.d.ts +15 -0
- package/dist/node/service-host/claim-registry-slot.d.ts.map +1 -0
- package/dist/node/service-host/create-l3-child-launcher.d.ts +38 -0
- package/dist/node/service-host/create-l3-child-launcher.d.ts.map +1 -0
- package/dist/node/service-host/index.d.ts +2 -4
- package/dist/node/service-host/index.d.ts.map +1 -1
- package/dist/node/service-host/service-host.d.ts +8 -4
- package/dist/node/service-host/service-host.d.ts.map +1 -1
- package/dist/node/service-host/single-process-service.d.ts +3 -3
- package/dist/node/service-host/single-process-service.d.ts.map +1 -1
- package/dist/node.d.ts +17 -10
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +927 -119
- package/dist/node.js.map +1 -1
- package/dist/preload.js +9 -6
- package/dist/preload.js.map +1 -1
- package/dist/service/index.d.ts +2 -2
- package/dist/service/index.d.ts.map +1 -1
- package/dist/service/registry.service.d.ts +19 -0
- package/dist/service/registry.service.d.ts.map +1 -1
- package/dist/shared/log/core/create-logger.d.ts.map +1 -0
- package/dist/shared/log/{log-batch.d.ts → core/log-batch.d.ts} +4 -4
- package/dist/shared/log/core/log-batch.d.ts.map +1 -0
- package/dist/shared/log/core/log-manager.d.ts.map +1 -0
- package/dist/shared/log/core/log-min-level.d.ts.map +1 -0
- package/dist/shared/log/core/log-scope.d.ts.map +1 -0
- package/dist/shared/log/index.d.ts +13 -13
- package/dist/shared/log/index.d.ts.map +1 -1
- package/dist/shared/log/spool/collector.d.ts +21 -0
- package/dist/shared/log/spool/collector.d.ts.map +1 -0
- package/dist/shared/log/spool/config.d.ts +7 -0
- package/dist/shared/log/spool/config.d.ts.map +1 -0
- package/dist/shared/log/spool/coordinator.d.ts +18 -0
- package/dist/shared/log/spool/coordinator.d.ts.map +1 -0
- package/dist/shared/log/spool/enable.d.ts +23 -0
- package/dist/shared/log/spool/enable.d.ts.map +1 -0
- package/dist/shared/log/spool/file.d.ts +21 -0
- package/dist/shared/log/spool/file.d.ts.map +1 -0
- package/dist/shared/log/spool/interface.d.ts +55 -0
- package/dist/shared/log/spool/interface.d.ts.map +1 -0
- package/dist/shared/log/spool/paths.d.ts +6 -0
- package/dist/shared/log/spool/paths.d.ts.map +1 -0
- package/dist/shared/log/spool/process-roles.d.ts +17 -0
- package/dist/shared/log/spool/process-roles.d.ts.map +1 -0
- package/dist/shared/log/spool/register-collect-log-runner.d.ts +15 -0
- package/dist/shared/log/spool/register-collect-log-runner.d.ts.map +1 -0
- package/dist/shared/log/spool/service-lifecycle.d.ts +13 -0
- package/dist/shared/log/spool/service-lifecycle.d.ts.map +1 -0
- package/dist/shared/log/spool/writer.d.ts +32 -0
- package/dist/shared/log/spool/writer.d.ts.map +1 -0
- package/dist/shared/log/{log-persist-batch-id.d.ts → upload/batch-id.d.ts} +3 -3
- package/dist/shared/log/upload/batch-id.d.ts.map +1 -0
- package/dist/shared/log/upload/env.d.ts +7 -0
- package/dist/shared/log/upload/env.d.ts.map +1 -0
- package/dist/shared/log/{log-persist-flush.d.ts → upload/flush.d.ts} +3 -3
- package/dist/shared/log/upload/flush.d.ts.map +1 -0
- package/dist/shared/log/{log-persist-hook.d.ts → upload/hook.d.ts} +2 -2
- package/dist/shared/log/upload/hook.d.ts.map +1 -0
- package/dist/shared/log/upload/interface.d.ts +33 -0
- package/dist/shared/log/upload/interface.d.ts.map +1 -0
- package/dist/shared/log/upload/logger.d.ts +22 -0
- package/dist/shared/log/upload/logger.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/browser.ts +1 -0
- package/src/node/env/index.ts +0 -4
- package/src/node/node-runtime.base.ts +33 -5
- package/src/node/node.api.ts +1 -1
- package/src/node/node.constant.ts +2 -0
- package/src/node/node.utils.ts +2 -2
- package/src/node/preload/run-service-preload.ts +11 -6
- package/src/node/preload/service-preload.interface.ts +3 -1
- package/src/node/process/create-managed-child-processes.ts +1 -1
- package/src/node/process/managed-child-processes.ts +8 -1
- package/src/node/readiness/index.ts +2 -0
- package/src/node/{service-guard → readiness}/run-readiness-gate.ts +2 -2
- package/src/node/readiness/service-ready-gate.ts +27 -23
- package/src/node/runtime-state/create-service-runtime-state-store.ts +11 -0
- package/src/node/runtime-state/service-runtime-state.types.ts +12 -0
- package/src/node/service-guard/index.ts +7 -8
- package/src/node/service-guard/register-service-guard-runner.ts +0 -2
- package/src/node/service-guard/run-service-guard-tick.ts +5 -2
- package/src/node/service-guard/service-guard-node.ts +234 -6
- package/src/node/service-guard/service-guard.interface.ts +35 -10
- package/src/node/service-host/claim-registry-slot.ts +33 -0
- package/src/node/service-host/create-l3-child-launcher.ts +157 -0
- package/src/node/service-host/index.ts +5 -20
- package/src/node/service-host/service-host.ts +33 -21
- package/src/node/service-host/single-process-service.ts +10 -20
- package/src/node.ts +45 -35
- package/src/service/index.ts +3 -0
- package/src/service/registry.service.ts +142 -0
- package/src/shared/log/README.md +206 -0
- package/src/shared/log/{log-batch.ts → core/log-batch.ts} +4 -4
- package/src/shared/log/{log-manager.ts → core/log-manager.ts} +3 -3
- package/src/shared/log/{log-min-level.ts → core/log-min-level.ts} +1 -1
- package/src/shared/log/{log-scope.ts → core/log-scope.ts} +1 -1
- package/src/shared/log/index.ts +13 -18
- package/src/shared/log/spool/collector.ts +330 -0
- package/src/shared/log/spool/config.ts +56 -0
- package/src/shared/log/spool/coordinator.ts +55 -0
- package/src/shared/log/spool/enable.ts +136 -0
- package/src/shared/log/spool/file.ts +75 -0
- package/src/shared/log/spool/interface.ts +62 -0
- package/src/shared/log/spool/paths.ts +30 -0
- package/src/shared/log/spool/process-roles.ts +75 -0
- package/src/shared/log/spool/register-collect-log-runner.ts +63 -0
- package/src/shared/log/spool/service-lifecycle.ts +35 -0
- package/src/shared/log/spool/writer.ts +273 -0
- package/src/shared/log/{log-persist-batch-id.ts → upload/batch-id.ts} +2 -2
- package/src/shared/log/upload/env.ts +26 -0
- package/src/shared/log/{log-persist-flush.ts → upload/flush.ts} +8 -7
- package/src/shared/log/{log-persist-hook.ts → upload/hook.ts} +1 -1
- package/src/shared/log/upload/interface.ts +38 -0
- package/src/shared/log/upload/logger.ts +37 -0
- package/dist/chunks/enable-log-persist-Dl55_mv2.js +0 -1674
- package/dist/chunks/enable-log-persist-Dl55_mv2.js.map +0 -1
- package/dist/chunks/task-queue-H4mpSW8R.js.map +0 -1
- package/dist/node/service-guard/run-readiness-gate.d.ts.map +0 -1
- package/dist/node/service-host/registry-slot-guard-step.d.ts.map +0 -1
- package/dist/shared/log/create-logger.d.ts.map +0 -1
- package/dist/shared/log/enable-log-persist.d.ts +0 -28
- package/dist/shared/log/enable-log-persist.d.ts.map +0 -1
- package/dist/shared/log/log-batch.d.ts.map +0 -1
- package/dist/shared/log/log-manager.d.ts.map +0 -1
- package/dist/shared/log/log-min-level.d.ts.map +0 -1
- package/dist/shared/log/log-persist-batch-id.d.ts.map +0 -1
- package/dist/shared/log/log-persist-coordinator.d.ts +0 -37
- package/dist/shared/log/log-persist-coordinator.d.ts.map +0 -1
- package/dist/shared/log/log-persist-errors.d.ts +0 -4
- package/dist/shared/log/log-persist-errors.d.ts.map +0 -1
- package/dist/shared/log/log-persist-flush.d.ts.map +0 -1
- package/dist/shared/log/log-persist-hook.d.ts.map +0 -1
- package/dist/shared/log/log-persist-logger.d.ts +0 -16
- package/dist/shared/log/log-persist-logger.d.ts.map +0 -1
- package/dist/shared/log/log-persist-registry.d.ts +0 -18
- package/dist/shared/log/log-persist-registry.d.ts.map +0 -1
- package/dist/shared/log/log-persist.config.d.ts +0 -9
- package/dist/shared/log/log-persist.config.d.ts.map +0 -1
- package/dist/shared/log/log-persist.d.ts +0 -62
- package/dist/shared/log/log-persist.d.ts.map +0 -1
- package/dist/shared/log/log-persist.interface.d.ts +0 -132
- package/dist/shared/log/log-persist.interface.d.ts.map +0 -1
- package/dist/shared/log/log-scope.d.ts.map +0 -1
- package/src/shared/log/enable-log-persist.ts +0 -225
- package/src/shared/log/log-persist-coordinator.ts +0 -107
- package/src/shared/log/log-persist-errors.ts +0 -16
- package/src/shared/log/log-persist-logger.ts +0 -61
- package/src/shared/log/log-persist-registry.ts +0 -252
- package/src/shared/log/log-persist.config.ts +0 -115
- package/src/shared/log/log-persist.interface.ts +0 -150
- package/src/shared/log/log-persist.ts +0 -822
- /package/dist/node/{service-host → service-guard}/registry-slot-guard-step.d.ts +0 -0
- /package/dist/shared/log/{create-logger.d.ts → core/create-logger.d.ts} +0 -0
- /package/dist/shared/log/{log-manager.d.ts → core/log-manager.d.ts} +0 -0
- /package/dist/shared/log/{log-min-level.d.ts → core/log-min-level.d.ts} +0 -0
- /package/dist/shared/log/{log-scope.d.ts → core/log-scope.d.ts} +0 -0
- /package/src/node/{service-host → service-guard}/registry-slot-guard-step.ts +0 -0
- /package/src/shared/log/{create-logger.ts → core/create-logger.ts} +0 -0
|
@@ -1,17 +1,35 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { createLogger, type LoggerWithScope } from "../../shared/log";
|
|
2
|
+
import { registerCollectLogRunner } from "../../shared/log/spool/register-collect-log-runner";
|
|
3
|
+
import { getErrorMessage } from "../../shared/utils/error.utils";
|
|
2
4
|
import { TriggerNode } from "../../trigger/trigger-node";
|
|
5
|
+
import type { NodeLoopContext } from "../node-runtime.base";
|
|
6
|
+
import { pollUntil } from "../readiness/poll-until";
|
|
7
|
+
import { runReadinessGate } from "../readiness/run-readiness-gate";
|
|
8
|
+
import type { GuardRuntimeSlice } from "../runtime-state/service-runtime-state.types";
|
|
3
9
|
import { registerServiceGuardRunner } from "./register-service-guard-runner";
|
|
4
|
-
import { runReadinessGate } from "./run-readiness-gate";
|
|
5
10
|
import type { ServiceGuardNodeOptions } from "./service-guard.interface";
|
|
11
|
+
import { markWorkerSpawned } from "./worker-spawn-cooldown";
|
|
12
|
+
|
|
13
|
+
const SILENT_BACKOFF_MS = [15_000, 30_000, 60_000, 120_000, 240_000, 300_000] as const;
|
|
14
|
+
const SILENT_EVENT_LIMIT = 32;
|
|
15
|
+
const DEFAULT_BUSINESS_READY_TIMEOUT_MS = 180_000;
|
|
6
16
|
|
|
7
17
|
/**
|
|
8
|
-
* L3 guard process TriggerNode: readiness gate,
|
|
18
|
+
* L3 guard process TriggerNode: readiness gate, business-node spawn owner,
|
|
9
19
|
* and {@link registerServiceGuardRunner} tick (registry slot + liveness + runtime).
|
|
10
20
|
*
|
|
21
|
+
* Consecutive heartbeat failure enters silent mode (no process exit). Recovery
|
|
22
|
+
* respawns business nodes; leaving silent requires heartbeat + all nodes ready.
|
|
23
|
+
*
|
|
11
24
|
* Use {@link ServiceGuardNode.create} so the guard runner is registered before `start()`.
|
|
12
25
|
*/
|
|
13
26
|
class ServiceGuardNode extends TriggerNode {
|
|
14
27
|
private readonly options: ServiceGuardNodeOptions;
|
|
28
|
+
private readonly businessReadyTimeoutMs: number;
|
|
29
|
+
private mode: GuardRuntimeSlice["mode"] = "healthy";
|
|
30
|
+
private silentBackoffIndex = 0;
|
|
31
|
+
private silentEvents: GuardRuntimeSlice["silentEvents"] = [];
|
|
32
|
+
private recoverPromise: Promise<void> | null = null;
|
|
15
33
|
|
|
16
34
|
private constructor(options: ServiceGuardNodeOptions) {
|
|
17
35
|
super({
|
|
@@ -19,18 +37,56 @@ class ServiceGuardNode extends TriggerNode {
|
|
|
19
37
|
beforeProcessExit: options.beforeProcessExit,
|
|
20
38
|
});
|
|
21
39
|
this.options = options;
|
|
40
|
+
this.businessReadyTimeoutMs = options.businessReadyTimeoutMs ?? DEFAULT_BUSINESS_READY_TIMEOUT_MS;
|
|
22
41
|
}
|
|
23
42
|
|
|
24
43
|
/** Register the guard runner and return a ready-to-start node. */
|
|
25
44
|
static create(options: ServiceGuardNodeOptions): ServiceGuardNode {
|
|
26
45
|
registerServiceGuardRunner({
|
|
46
|
+
...options.guardRunner,
|
|
27
47
|
serviceValue: options.serviceValue,
|
|
28
48
|
logTopic: options.logTopic,
|
|
29
|
-
|
|
49
|
+
spawnWorker: options.spawnBusinessNodes,
|
|
50
|
+
});
|
|
51
|
+
registerCollectLogRunner({
|
|
52
|
+
serviceValue: options.serviceValue,
|
|
53
|
+
getServiceId: options.guardRunner.getServiceId,
|
|
30
54
|
});
|
|
31
55
|
return new ServiceGuardNode(options);
|
|
32
56
|
}
|
|
33
57
|
|
|
58
|
+
protected isSilenced(): boolean {
|
|
59
|
+
return this.mode !== "healthy";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
protected getLoopIntervalMs(): number {
|
|
63
|
+
if (this.mode === "silent") {
|
|
64
|
+
return SILENT_BACKOFF_MS[this.silentBackoffIndex] ?? 300_000;
|
|
65
|
+
}
|
|
66
|
+
return super.getLoopIntervalMs();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
protected async onHealthCheckFailed(_ctx: NodeLoopContext): Promise<void> {
|
|
70
|
+
if (this.mode !== "healthy") {
|
|
71
|
+
this.bumpSilentBackoff();
|
|
72
|
+
await this.persistGuard({
|
|
73
|
+
lastDecision: "silent_heartbeat_fail",
|
|
74
|
+
silentConsecutiveFailures: this.silentConsecutiveFailures(),
|
|
75
|
+
silentBackoffMs: this.getLoopIntervalMs(),
|
|
76
|
+
silentLastHeartbeatAt: new Date().toISOString(),
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
await this.enterSilent("heartbeat:consecutive-failures");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
protected async onHeartbeatRecovered(ctx: NodeLoopContext): Promise<void> {
|
|
84
|
+
if (this.mode !== "silent") {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
await this.recoverFromSilent(ctx);
|
|
88
|
+
}
|
|
89
|
+
|
|
34
90
|
protected async onBeforeRegisterNode(nodeLogger: LoggerWithScope): Promise<void> {
|
|
35
91
|
await runReadinessGate({
|
|
36
92
|
checks: this.options.readinessChecks,
|
|
@@ -41,11 +97,183 @@ class ServiceGuardNode extends TriggerNode {
|
|
|
41
97
|
|
|
42
98
|
await super.onBeforeRegisterNode(nodeLogger);
|
|
43
99
|
|
|
44
|
-
nodeLogger.info("Spawning
|
|
100
|
+
nodeLogger.info("Spawning business nodes", {
|
|
45
101
|
topic: this.options.logTopic ?? "guard",
|
|
46
102
|
});
|
|
47
|
-
await this.options.
|
|
103
|
+
await this.options.spawnBusinessNodes("guard:readiness-passed");
|
|
104
|
+
markWorkerSpawned();
|
|
48
105
|
this.options.onWorkerSpawned?.();
|
|
106
|
+
|
|
107
|
+
await this.waitUntilBusinessReady();
|
|
108
|
+
await this.persistGuard({
|
|
109
|
+
mode: "healthy",
|
|
110
|
+
lastDecision: "bootstrap_ready",
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
protected async runLoopSteps(ctx: NodeLoopContext, heartbeatOk: boolean): Promise<void> {
|
|
115
|
+
await super.runLoopSteps(ctx, heartbeatOk);
|
|
116
|
+
if (this.mode !== "healthy" || !heartbeatOk) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
// Idempotent — respawn exited children without entering silent (worker stale is the runner's job).
|
|
120
|
+
try {
|
|
121
|
+
await this.options.spawnBusinessNodes("guard:ensure-business");
|
|
122
|
+
} catch (error) {
|
|
123
|
+
this.createGuardLogger().warn("ensure-business spawn failed", {
|
|
124
|
+
topic: this.options.logTopic ?? "guard",
|
|
125
|
+
data: { error: getErrorMessage(error) },
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private silentConsecutiveFailures(): number {
|
|
131
|
+
return this.silentBackoffIndex + 1;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private bumpSilentBackoff(): void {
|
|
135
|
+
if (this.silentBackoffIndex < SILENT_BACKOFF_MS.length - 1) {
|
|
136
|
+
this.silentBackoffIndex += 1;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private async enterSilent(reason: string): Promise<void> {
|
|
141
|
+
this.mode = "silent";
|
|
142
|
+
this.silentBackoffIndex = 0;
|
|
143
|
+
const enteredAt = new Date().toISOString();
|
|
144
|
+
this.pushEvent("enter", reason);
|
|
145
|
+
await this.persistGuard({
|
|
146
|
+
mode: "silent",
|
|
147
|
+
lastDecision: `silent:${reason}`,
|
|
148
|
+
silentEnteredAt: enteredAt,
|
|
149
|
+
silentLastHeartbeatAt: enteredAt,
|
|
150
|
+
silentBackoffMs: SILENT_BACKOFF_MS[0],
|
|
151
|
+
silentConsecutiveFailures: 1,
|
|
152
|
+
silentRecoveryAttempt: 0,
|
|
153
|
+
silentEvents: this.silentEvents,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const logger = this.createGuardLogger();
|
|
157
|
+
logger.warn("Entering silent mode — service unavailable until heartbeat + business nodes recover", {
|
|
158
|
+
topic: this.options.logTopic ?? "guard",
|
|
159
|
+
data: { reason },
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
await this.options.stopBusinessNodes();
|
|
164
|
+
} catch (error) {
|
|
165
|
+
logger.warn("stopBusinessNodes failed (best-effort)", {
|
|
166
|
+
topic: this.options.logTopic ?? "guard",
|
|
167
|
+
data: { error: getErrorMessage(error) },
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private async recoverFromSilent(ctx: NodeLoopContext): Promise<void> {
|
|
173
|
+
if (this.recoverPromise != null) {
|
|
174
|
+
return this.recoverPromise;
|
|
175
|
+
}
|
|
176
|
+
this.recoverPromise = this.runRecover(ctx).finally(() => {
|
|
177
|
+
this.recoverPromise = null;
|
|
178
|
+
});
|
|
179
|
+
return this.recoverPromise;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private async runRecover(ctx: NodeLoopContext): Promise<void> {
|
|
183
|
+
this.mode = "recovering";
|
|
184
|
+
this.pushEvent("recover_start", ctx.loopTraceId);
|
|
185
|
+
await this.persistGuard({
|
|
186
|
+
mode: "recovering",
|
|
187
|
+
lastDecision: "recovering",
|
|
188
|
+
silentLastHeartbeatAt: new Date().toISOString(),
|
|
189
|
+
silentRecoveryAttempt: this.silentEvents.filter((event) => event.type === "recover_start").length,
|
|
190
|
+
silentEvents: this.silentEvents,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const logger = this.createGuardLogger();
|
|
194
|
+
logger.info("Heartbeat restored — recovering business nodes", {
|
|
195
|
+
topic: this.options.logTopic ?? "guard",
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
await this.options.stopBusinessNodes();
|
|
200
|
+
} catch (error) {
|
|
201
|
+
logger.warn("stopBusinessNodes failed before respawn", {
|
|
202
|
+
topic: this.options.logTopic ?? "guard",
|
|
203
|
+
data: { error: getErrorMessage(error) },
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
await this.options.spawnBusinessNodes("guard:silent-recover");
|
|
209
|
+
markWorkerSpawned();
|
|
210
|
+
await this.waitUntilBusinessReady();
|
|
211
|
+
} catch (error) {
|
|
212
|
+
this.pushEvent("recover_fail", getErrorMessage(error));
|
|
213
|
+
this.mode = "silent";
|
|
214
|
+
this.bumpSilentBackoff();
|
|
215
|
+
await this.persistGuard({
|
|
216
|
+
mode: "silent",
|
|
217
|
+
lastDecision: "recover_fail",
|
|
218
|
+
silentBackoffMs: this.getLoopIntervalMs(),
|
|
219
|
+
silentEvents: this.silentEvents,
|
|
220
|
+
});
|
|
221
|
+
logger.warn("Recovery failed — returning to silent", {
|
|
222
|
+
topic: this.options.logTopic ?? "guard",
|
|
223
|
+
data: { error: getErrorMessage(error) },
|
|
224
|
+
});
|
|
225
|
+
try {
|
|
226
|
+
await this.options.stopBusinessNodes();
|
|
227
|
+
} catch {
|
|
228
|
+
// best-effort — leftover children are stopped again on the next recover
|
|
229
|
+
}
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
this.mode = "healthy";
|
|
234
|
+
this.silentBackoffIndex = 0;
|
|
235
|
+
this.pushEvent("recover_ok");
|
|
236
|
+
await this.persistGuard({
|
|
237
|
+
mode: "healthy",
|
|
238
|
+
lastDecision: "recover_ok",
|
|
239
|
+
silentEnteredAt: null,
|
|
240
|
+
silentBackoffMs: 0,
|
|
241
|
+
silentConsecutiveFailures: 0,
|
|
242
|
+
silentEvents: this.silentEvents,
|
|
243
|
+
});
|
|
244
|
+
logger.success("Left silent mode — business nodes ready", {
|
|
245
|
+
topic: this.options.logTopic ?? "guard",
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private async waitUntilBusinessReady(): Promise<void> {
|
|
250
|
+
try {
|
|
251
|
+
await pollUntil({
|
|
252
|
+
timeoutMs: this.businessReadyTimeoutMs,
|
|
253
|
+
intervalMs: 500,
|
|
254
|
+
until: () => this.options.isBusinessReady(),
|
|
255
|
+
});
|
|
256
|
+
} catch {
|
|
257
|
+
throw new Error(`Business nodes not ready within ${this.businessReadyTimeoutMs}ms`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private pushEvent(type: string, detail?: string): void {
|
|
262
|
+
this.silentEvents = [...this.silentEvents, { at: new Date().toISOString(), type, detail }].slice(
|
|
263
|
+
-SILENT_EVENT_LIMIT,
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
private async persistGuard(patch: Partial<GuardRuntimeSlice>): Promise<void> {
|
|
268
|
+
try {
|
|
269
|
+
await this.options.guardRunner.onGuardPatch(patch);
|
|
270
|
+
} catch {
|
|
271
|
+
// best-effort — disk/runtime state must not take down the guard loop
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
private createGuardLogger(): LoggerWithScope {
|
|
276
|
+
return createLogger({ module: "service-guard" });
|
|
49
277
|
}
|
|
50
278
|
}
|
|
51
279
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { Node } from "../../node/node.interface";
|
|
2
1
|
import type { RegistrySlotRuntimeState } from "../../service/registry-lifecycle";
|
|
3
2
|
import type { LoggerWithScope } from "../../shared/log";
|
|
4
|
-
import type {
|
|
3
|
+
import type { Node } from "../node.interface";
|
|
5
4
|
import type { ReadinessCheck, ReadinessReport } from "../readiness/readiness.types";
|
|
5
|
+
import type { GuardRuntimeSlice } from "../runtime-state/service-runtime-state.types";
|
|
6
6
|
|
|
7
7
|
/** Default TriggerManager runner key for {@link registerServiceGuardRunner}. */
|
|
8
8
|
export const SERVICE_GUARD_RUNNER_KEY = "service-guard";
|
|
@@ -45,20 +45,45 @@ export interface RegisterServiceGuardRunnerOptions {
|
|
|
45
45
|
onRegistryPatch: (
|
|
46
46
|
patch: Pick<RegistrySlotRuntimeState, "slotOwned" | "lastSlotCheckAt" | "lastSlotCheckError">,
|
|
47
47
|
) => Promise<void>;
|
|
48
|
-
onGuardPatch: (patch:
|
|
48
|
+
onGuardPatch: (patch: Partial<GuardRuntimeSlice>) => Promise<void>;
|
|
49
49
|
onDecision?: (decision: string) => Promise<void>;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
export interface ServiceGuardNodeOptions
|
|
52
|
+
export interface ServiceGuardNodeOptions {
|
|
53
53
|
serviceValue: string;
|
|
54
54
|
logTopic?: string;
|
|
55
55
|
readinessChecks: ReadinessCheck[];
|
|
56
56
|
onReadinessReport: (report: ReadinessReport) => Promise<void>;
|
|
57
|
-
guardRunner: Omit<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
guardRunner: Omit<RegisterServiceGuardRunnerOptions, "serviceValue" | "logTopic" | "runnerKey" | "spawnWorker">;
|
|
58
|
+
/**
|
|
59
|
+
* Idempotent spawn of all business nodes (scheduler + worker, etc.).
|
|
60
|
+
* Used at bootstrap, silent recovery, and healthy-mode TaskNode respawn.
|
|
61
|
+
*/
|
|
62
|
+
spawnBusinessNodes: (reason: string) => Promise<void>;
|
|
63
|
+
/** Stop business nodes only (never the guard process). */
|
|
64
|
+
stopBusinessNodes: () => Promise<void>;
|
|
65
|
+
/** Local runtime gate: all business nodes registered and ready. */
|
|
66
|
+
isBusinessReady: () => Promise<boolean>;
|
|
67
|
+
/** Timeout for {@link isBusinessReady} during bootstrap and recovery. Default 180s. */
|
|
68
|
+
businessReadyTimeoutMs?: number;
|
|
69
|
+
/** Extra hook after the initial business spawn (cooldown is recorded automatically). */
|
|
63
70
|
onWorkerSpawned?: () => void;
|
|
71
|
+
beforeProcessExit?: () => void | Promise<void>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** True when the guard slice is serving traffic (`mode` omitted = legacy healthy). */
|
|
75
|
+
export function isGuardAvailable(guard: Pick<GuardRuntimeSlice, "mode">): boolean {
|
|
76
|
+
return guard.mode == null || guard.mode === "healthy";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Seconds for `Retry-After` when the guard is not serving traffic.
|
|
81
|
+
* `null` when {@link isGuardAvailable} is true.
|
|
82
|
+
*/
|
|
83
|
+
export function guardRetryAfterSec(guard: Pick<GuardRuntimeSlice, "mode" | "silentBackoffMs">): number | null {
|
|
84
|
+
if (isGuardAvailable(guard)) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const backoffMs = guard.silentBackoffMs > 0 ? guard.silentBackoffMs : 15_000;
|
|
88
|
+
return Math.max(1, Math.ceil(backoffMs / 1000));
|
|
64
89
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ServiceRegistryError } from "../../service/registry.service";
|
|
2
|
+
import { claimServiceRegistrySlot, type ServiceRegistrySession } from "../../service/registry-lifecycle";
|
|
3
|
+
import type { Service } from "../../service/service.interface";
|
|
4
|
+
import type { LoggerWithScope } from "../../shared/log";
|
|
5
|
+
|
|
6
|
+
export interface ClaimRegistrySlotOrExitInput {
|
|
7
|
+
serviceValue: string;
|
|
8
|
+
createInstance: () => Promise<Service>;
|
|
9
|
+
logger: LoggerWithScope;
|
|
10
|
+
logTopic: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Claim a registry slot, or `process.exit(1)` when no EMPTY slot remains.
|
|
15
|
+
* Other errors propagate.
|
|
16
|
+
*/
|
|
17
|
+
export async function claimRegistrySlotOrExit(input: ClaimRegistrySlotOrExitInput): Promise<ServiceRegistrySession> {
|
|
18
|
+
try {
|
|
19
|
+
return await claimServiceRegistrySlot({
|
|
20
|
+
serviceValue: input.serviceValue,
|
|
21
|
+
createInstance: input.createInstance,
|
|
22
|
+
});
|
|
23
|
+
} catch (error) {
|
|
24
|
+
if (error instanceof ServiceRegistryError && error.code === "SERVICE_SLOTS_FULL") {
|
|
25
|
+
input.logger.critical("No EMPTY registry slot — refusing to start", {
|
|
26
|
+
topic: input.logTopic,
|
|
27
|
+
data: { serviceValue: input.serviceValue, code: error.code },
|
|
28
|
+
});
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import type { ChildProcess } from "node:child_process";
|
|
2
|
+
import type { ManagedChildProcesses } from "../process/managed-child-processes";
|
|
3
|
+
import type {
|
|
4
|
+
GuardRuntimeSlice,
|
|
5
|
+
SchedulerRuntimeSlice,
|
|
6
|
+
WorkerRuntimeSlice,
|
|
7
|
+
} from "../runtime-state/service-runtime-state.types";
|
|
8
|
+
|
|
9
|
+
const WORKER_SCRIPT = "./dist/processes/worker.js";
|
|
10
|
+
const GUARD_SCRIPT = "./dist/processes/guard.js";
|
|
11
|
+
const SCHEDULER_SCRIPT = "./dist/processes/scheduler.js";
|
|
12
|
+
|
|
13
|
+
export interface L3ChildLauncherRuntimeStore {
|
|
14
|
+
readRuntimeState(): Promise<{
|
|
15
|
+
worker: WorkerRuntimeSlice;
|
|
16
|
+
scheduler: SchedulerRuntimeSlice;
|
|
17
|
+
guard: GuardRuntimeSlice;
|
|
18
|
+
}>;
|
|
19
|
+
writeRuntimeState(patch: {
|
|
20
|
+
worker?: Partial<WorkerRuntimeSlice>;
|
|
21
|
+
scheduler?: Partial<SchedulerRuntimeSlice>;
|
|
22
|
+
guard?: Partial<GuardRuntimeSlice>;
|
|
23
|
+
}): Promise<unknown>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface CreateL3ChildLauncherOptions {
|
|
27
|
+
childProcesses: ManagedChildProcesses;
|
|
28
|
+
readRuntimeState: L3ChildLauncherRuntimeStore["readRuntimeState"];
|
|
29
|
+
writeRuntimeState: L3ChildLauncherRuntimeStore["writeRuntimeState"];
|
|
30
|
+
/** Merged onto the child env. Default: `{ LOG_PERSIST_PROCESS: label }`. */
|
|
31
|
+
spawnEnv?: (label: string) => Record<string, string>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface L3ChildLauncher {
|
|
35
|
+
spawnGuard: () => ChildProcess;
|
|
36
|
+
spawnTaskWorker: (reason: string) => Promise<ChildProcess>;
|
|
37
|
+
spawnScheduler: (reason: string) => Promise<ChildProcess>;
|
|
38
|
+
spawnBusinessNodes: (reason: string) => Promise<void>;
|
|
39
|
+
stopBusinessNodes: () => Promise<void>;
|
|
40
|
+
isBusinessReady: () => Promise<boolean>;
|
|
41
|
+
stopChildProcesses: () => Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function defaultSpawnEnv(label: string): Record<string, string> {
|
|
45
|
+
return { LOG_PERSIST_PROCESS: label };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Guard-owned scheduler + worker spawn/stop, plus main-owned Guard spawn.
|
|
50
|
+
* `stopBusinessNodes` must run in the Guard process (that instance's children only).
|
|
51
|
+
* `stopChildProcesses` is for main shutdown (`extraPids` for Guard-spawned PIDs).
|
|
52
|
+
*/
|
|
53
|
+
export function createL3ChildLauncher(options: CreateL3ChildLauncherOptions): L3ChildLauncher {
|
|
54
|
+
const { childProcesses, readRuntimeState, writeRuntimeState } = options;
|
|
55
|
+
const spawnEnv = options.spawnEnv ?? defaultSpawnEnv;
|
|
56
|
+
|
|
57
|
+
function nowIso(): string {
|
|
58
|
+
return new Date().toISOString();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function spawnTaskWorker(reason: string): Promise<ChildProcess> {
|
|
62
|
+
const { child, created } = childProcesses.spawn("worker", WORKER_SCRIPT, {
|
|
63
|
+
env: spawnEnv("worker"),
|
|
64
|
+
});
|
|
65
|
+
if (!created) {
|
|
66
|
+
return child;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const current = await readRuntimeState();
|
|
70
|
+
await writeRuntimeState({
|
|
71
|
+
worker: {
|
|
72
|
+
pid: child.pid ?? null,
|
|
73
|
+
spawnedAt: nowIso(),
|
|
74
|
+
ready: false,
|
|
75
|
+
registeredTasks: [],
|
|
76
|
+
readyAt: null,
|
|
77
|
+
},
|
|
78
|
+
guard: {
|
|
79
|
+
lastSpawnAt: nowIso(),
|
|
80
|
+
lastDecision: reason,
|
|
81
|
+
spawnCount: current.guard.spawnCount + 1,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return child;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function spawnGuard(): ChildProcess {
|
|
89
|
+
return childProcesses.spawn("guard", GUARD_SCRIPT, { env: spawnEnv("guard") }).child;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function spawnScheduler(reason: string): Promise<ChildProcess> {
|
|
93
|
+
const { child, created } = childProcesses.spawn("scheduler", SCHEDULER_SCRIPT, {
|
|
94
|
+
env: spawnEnv("scheduler"),
|
|
95
|
+
});
|
|
96
|
+
if (!created) {
|
|
97
|
+
return child;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
await writeRuntimeState({
|
|
101
|
+
scheduler: {
|
|
102
|
+
pid: child.pid ?? null,
|
|
103
|
+
spawnedAt: nowIso(),
|
|
104
|
+
ready: false,
|
|
105
|
+
readyAt: null,
|
|
106
|
+
},
|
|
107
|
+
guard: {
|
|
108
|
+
lastDecision: reason,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return child;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function spawnBusinessNodes(reason: string): Promise<void> {
|
|
116
|
+
await spawnScheduler(reason);
|
|
117
|
+
await spawnTaskWorker(reason);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function stopBusinessNodes(): Promise<void> {
|
|
121
|
+
await childProcesses.stopAll();
|
|
122
|
+
await writeRuntimeState({
|
|
123
|
+
worker: {
|
|
124
|
+
pid: null,
|
|
125
|
+
ready: false,
|
|
126
|
+
readyAt: null,
|
|
127
|
+
registeredTasks: [],
|
|
128
|
+
},
|
|
129
|
+
scheduler: {
|
|
130
|
+
pid: null,
|
|
131
|
+
ready: false,
|
|
132
|
+
readyAt: null,
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function isBusinessReady(): Promise<boolean> {
|
|
138
|
+
const state = await readRuntimeState();
|
|
139
|
+
return state.worker.ready && state.scheduler.ready;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function stopChildProcesses(): Promise<void> {
|
|
143
|
+
const state = await readRuntimeState();
|
|
144
|
+
const extraPids = [state.worker.pid, state.scheduler.pid].filter((pid): pid is number => pid != null);
|
|
145
|
+
await childProcesses.stopAll({ extraPids });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
spawnGuard,
|
|
150
|
+
spawnTaskWorker,
|
|
151
|
+
spawnScheduler,
|
|
152
|
+
spawnBusinessNodes,
|
|
153
|
+
stopBusinessNodes,
|
|
154
|
+
isBusinessReady,
|
|
155
|
+
stopChildProcesses,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
@@ -1,24 +1,9 @@
|
|
|
1
1
|
export type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "../service-guard";
|
|
8
|
-
export {
|
|
9
|
-
getWorkerSpawnCooldownLastAt,
|
|
10
|
-
markWorkerSpawned,
|
|
11
|
-
registerServiceGuardRunner,
|
|
12
|
-
runReadinessGate,
|
|
13
|
-
runServiceGuardTick,
|
|
14
|
-
SERVICE_GUARD_RUNNER_KEY,
|
|
15
|
-
ServiceGuardNode,
|
|
16
|
-
} from "../service-guard";
|
|
17
|
-
export type {
|
|
18
|
-
ApplyRegistrySlotGuardInput,
|
|
19
|
-
ApplyRegistrySlotGuardResult,
|
|
20
|
-
} from "./registry-slot-guard-step";
|
|
21
|
-
export { applyRegistrySlotGuardStep } from "./registry-slot-guard-step";
|
|
2
|
+
CreateL3ChildLauncherOptions,
|
|
3
|
+
L3ChildLauncher,
|
|
4
|
+
L3ChildLauncherRuntimeStore,
|
|
5
|
+
} from "./create-l3-child-launcher";
|
|
6
|
+
export { createL3ChildLauncher } from "./create-l3-child-launcher";
|
|
22
7
|
export type {
|
|
23
8
|
ServiceHost,
|
|
24
9
|
ServiceHostClosable,
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import type {
|
|
1
|
+
import { ServiceRegistryError } from "../../service/registry.service";
|
|
2
|
+
import type { ServiceRegistrySession } from "../../service/registry-lifecycle";
|
|
3
|
+
import type { Service } from "../../service/service.interface";
|
|
4
|
+
import { createLogger, type LoggerWithScope } from "../../shared/log";
|
|
5
|
+
import { LOG_SPOOL_SERVICE_ID_ENV } from "../../shared/log/spool/config";
|
|
6
|
+
import { ensureLogSpoolFromEnv } from "../../shared/log/spool/enable";
|
|
7
|
+
import { logSpoolEnabledFromEnv } from "../../shared/log/upload/env";
|
|
4
8
|
import type { ManagedChildProcesses } from "../process/managed-child-processes";
|
|
9
|
+
import { claimRegistrySlotOrExit } from "./claim-registry-slot";
|
|
5
10
|
|
|
6
11
|
export interface ServiceHostClosable {
|
|
7
12
|
close: () => Promise<void>;
|
|
@@ -10,6 +15,8 @@ export interface ServiceHostClosable {
|
|
|
10
15
|
export interface ServiceHostContext {
|
|
11
16
|
service: Service;
|
|
12
17
|
session: ServiceRegistrySession;
|
|
18
|
+
/** Registry instance id — same as `service.id`; children inherit via spawn env injection. */
|
|
19
|
+
serviceId: string;
|
|
13
20
|
}
|
|
14
21
|
|
|
15
22
|
export interface ServiceHostOptions {
|
|
@@ -23,9 +30,11 @@ export interface ServiceHostOptions {
|
|
|
23
30
|
prepare?: () => Promise<void>;
|
|
24
31
|
createInstance: () => Promise<Service | { service: Service; baseUrl?: string }>;
|
|
25
32
|
onRegistryClaimed?: (ctx: ServiceHostContext) => Promise<void>;
|
|
26
|
-
startSupervisors?: () => void | Promise<void>;
|
|
33
|
+
startSupervisors?: (ctx: ServiceHostContext) => void | Promise<void>;
|
|
27
34
|
waitUntilReady?: () => Promise<void>;
|
|
28
35
|
startServer?: () => Promise<ServiceHostClosable>;
|
|
36
|
+
/** When true (default), enable file log spool for main after registry claim. */
|
|
37
|
+
enableLogSpoolAfterClaim?: boolean;
|
|
29
38
|
/** Stop child processes and other local resources (not registry release). */
|
|
30
39
|
onShutdown?: (signal: string) => Promise<void>;
|
|
31
40
|
}
|
|
@@ -105,28 +114,31 @@ export function createServiceHost(options: ServiceHostOptions): ServiceHost {
|
|
|
105
114
|
await options.prepare();
|
|
106
115
|
}
|
|
107
116
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
topic: logTopic,
|
|
117
|
-
data: { serviceValue: options.serviceValue, code: error.code },
|
|
118
|
-
});
|
|
119
|
-
process.exit(1);
|
|
120
|
-
}
|
|
121
|
-
throw error;
|
|
122
|
-
}
|
|
117
|
+
session = await claimRegistrySlotOrExit({
|
|
118
|
+
serviceValue: options.serviceValue,
|
|
119
|
+
createInstance: async () => unwrapService(await options.createInstance()),
|
|
120
|
+
logger,
|
|
121
|
+
logTopic,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const hostCtx: ServiceHostContext = { service: session.service, session, serviceId: session.service.id };
|
|
123
125
|
|
|
124
126
|
if (options.onRegistryClaimed != null) {
|
|
125
|
-
await options.onRegistryClaimed(
|
|
127
|
+
await options.onRegistryClaimed(hostCtx);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const enableSpool = options.enableLogSpoolAfterClaim ?? true;
|
|
131
|
+
if (enableSpool && logSpoolEnabledFromEnv()) {
|
|
132
|
+
process.env[LOG_SPOOL_SERVICE_ID_ENV] = session.service.id;
|
|
133
|
+
await ensureLogSpoolFromEnv({
|
|
134
|
+
serviceId: session.service.id,
|
|
135
|
+
serviceValue: options.serviceValue,
|
|
136
|
+
processRole: "main",
|
|
137
|
+
});
|
|
126
138
|
}
|
|
127
139
|
|
|
128
140
|
if (options.startSupervisors != null) {
|
|
129
|
-
await options.startSupervisors();
|
|
141
|
+
await options.startSupervisors(hostCtx);
|
|
130
142
|
}
|
|
131
143
|
|
|
132
144
|
if (options.waitUntilReady != null) {
|