Repository navigation
Expand file tree
/
Copy pathgateway-shutdown-budget.mjs
More file actions
69 lines (60 loc) 路 3.24 KB
/
Copy pathgateway-shutdown-budget.mjs
File metadata and controls
69 lines (60 loc) 路 3.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// Shared stop policy; v2026.9.5 restart-health.constants.ts fixes the replacement window.
export const GATEWAY_RESTART_REPLACEMENT_TIMEOUT_MS = 60_000;
const GATEWAY_SHUTDOWN_DRAIN_TIMEOUT_MS = 315_000;
export const GATEWAY_SHUTDOWN_RESERVE_MS = 10_000;
export const GATEWAY_SUPERVISOR_EXIT_MARGIN_MS = 5_000;
export const GATEWAY_SHUTDOWN_TIMEOUT_MS =
GATEWAY_SHUTDOWN_DRAIN_TIMEOUT_MS + GATEWAY_SHUTDOWN_RESERVE_MS;
export const GATEWAY_SERVICE_STOP_TIMEOUT_MS =
GATEWAY_SHUTDOWN_TIMEOUT_MS + GATEWAY_SUPERVISOR_EXIT_MARGIN_MS;
export const LAUNCH_AGENT_EXIT_TIMEOUT_SECONDS = GATEWAY_SERVICE_STOP_TIMEOUT_MS / 1_000;
// Keep a positive shutdown budget when a supervisor's deadline is under 20s.
const GATEWAY_SUPERVISOR_EXIT_MARGIN_SHARE = 0.25;
// Preserve a legacy job's 5s drain where possible. Under 20s, trade some cleanup
// reserve for drain; on very short jobs, split the remaining budget in half.
// Inspection time is debited before this allocation, so a slow probe can also
// reduce the drain. The measured upgrade tradeoff is documented in restart-recovery.
const GATEWAY_SHUTDOWN_DRAIN_FLOOR_MS = 5_000;
const GATEWAY_SHUTDOWN_DRAIN_FLOOR_SHARE = 0.5;
/** The exit margin to hold back from a supervisor-enforced stop deadline. */
export const resolveSupervisorExitMarginMs = (stopTimeoutMs) =>
Math.min(
GATEWAY_SUPERVISOR_EXIT_MARGIN_MS,
Math.floor(Math.max(0, stopTimeoutMs) * GATEWAY_SUPERVISOR_EXIT_MARGIN_SHARE),
);
/** The post-drain reserve to hold back from a resolved shutdown budget. */
export const resolveShutdownReserveMs = (shutdownTimeoutMs) => {
const budgetMs = Math.max(0, shutdownTimeoutMs);
const drainFloorMs = Math.min(
GATEWAY_SHUTDOWN_DRAIN_FLOOR_MS,
Math.floor(budgetMs * GATEWAY_SHUTDOWN_DRAIN_FLOOR_SHARE),
);
return Math.min(GATEWAY_SHUTDOWN_RESERVE_MS, budgetMs - drainFloorMs);
};
// Escalation graces the Node recovery launcher applies to a stopping child. Kept
// here rather than in the launcher so both use the same escalation policy.
const RESPAWN_SIGNAL_EXIT_GRACE_MS = 1_000;
export const RESPAWN_SIGNAL_FORCE_KILL_GRACE_MS = 1_000;
const RESPAWN_SIGNAL_HARD_EXIT_GRACE_MS = 1_000;
// Every runRespawnedChild call site stamps one of these. The compile-cache
// marker is also used by a different respawner, but that one refuses foreground
// Gateway runs on darwin. Keep this list with the launcher deadline it authorizes.
const RESPAWN_LAUNCHER_MARKER_ENV_VARS = [
"OPENCLAW_NODE_UPDATE_RESPAWNED",
"OPENCLAW_COMPILE_CACHE_DISABLED_RESPAWNED",
"OPENCLAW_PACKAGED_COMPILE_CACHE_RESPAWNED",
];
/** Whether a `runRespawnedChild` parent started this process. */
export const isRespawnedByLauncher = (env) =>
RESPAWN_LAUNCHER_MARKER_ENV_VARS.some((name) => env[name] === "1");
// New launchers use the shared service policy. A serving Gateway separately
// retains the shorter deadline of an unidentified, already-running old launcher.
export const resolveLauncherStopTimeoutMs = ({ platform, foreground }) => {
const signalExitGraceMs =
platform !== "win32" && foreground
? GATEWAY_SERVICE_STOP_TIMEOUT_MS -
RESPAWN_SIGNAL_FORCE_KILL_GRACE_MS -
RESPAWN_SIGNAL_HARD_EXIT_GRACE_MS
: RESPAWN_SIGNAL_EXIT_GRACE_MS;
return signalExitGraceMs + RESPAWN_SIGNAL_FORCE_KILL_GRACE_MS;
};