fix(gateway): raise and make configurable the core config-acceptance timeout (#1761)

This commit is contained in:
ggbond 2026-09-13 22:12:14 +08:00
parent afba89fa37
commit 35207da958
2 changed files with 51 additions and 3 deletions

View file

@ -23,7 +23,10 @@ import type { CoreGatewayHealth, ManagedGatewayRuntimeMarker } from "@ccr/core/g
import { delay } from "@ccr/core/gateway/internal/clock";
const gatewayRuntimeStateKey = "gateway";
const gatewayConfigAcceptanceTimeoutMs = 5_000;
// The config-acceptance budget spans child spawn, the gateway module
// require(), and the IPC round-trip through the parent event loop, so a
// loaded host legitimately needs well more than the old hardcoded 5s.
const defaultGatewayConfigAcceptanceTimeoutMs = 30_000;
const gatewayStartupTimeoutMs = 15_000;
const gatewayChildOutputLimit = 4000;
const privateDirMode = 0o700;
@ -98,6 +101,19 @@ export function spawnGatewayProcess(
};
}
// CCR_GATEWAY_CONFIG_TIMEOUT_MS tunes the startup budget above for hosts
// whose process spawn / module load times exceed the default under load.
export function gatewayConfigAcceptanceTimeoutMs(env: NodeJS.ProcessEnv = process.env): number {
const raw = env.CCR_GATEWAY_CONFIG_TIMEOUT_MS?.trim();
if (raw) {
const parsed = Number(raw);
if (Number.isFinite(parsed) && parsed >= 1_000) {
return Math.trunc(parsed);
}
}
return defaultGatewayConfigAcceptanceTimeoutMs;
}
function monitorGatewayConfigAcceptance(child: ChildProcess): {
promise: Promise<void>;
reject: (error: Error) => void;
@ -134,9 +150,10 @@ function monitorGatewayConfigAcceptance(child: ChildProcess): {
const onExit = (code: number | null, signal: NodeJS.Signals | null) => {
finish(new Error(formatCoreGatewayChildExit(child, code, signal, "before accepting runtime config")));
};
const acceptanceTimeoutMs = gatewayConfigAcceptanceTimeoutMs();
const timer = setTimeout(() => {
finish(new Error(`Core gateway did not accept runtime config within ${gatewayConfigAcceptanceTimeoutMs}ms.`));
}, gatewayConfigAcceptanceTimeoutMs);
finish(new Error(`Core gateway did not accept runtime config within ${acceptanceTimeoutMs}ms. Set CCR_GATEWAY_CONFIG_TIMEOUT_MS to raise this limit.`));
}, acceptanceTimeoutMs);
rejectAcceptance = (error) => finish(error);
child.on("message", onMessage);
child.once("error", onError);

View file

@ -0,0 +1,31 @@
import assert from "node:assert/strict";
import test from "node:test";
import { gatewayConfigAcceptanceTimeoutMs } from "@ccr/core/gateway/core-runtime/supervisor.ts";
test("gateway config acceptance timeout defaults to 30s when env is unset", () => {
assert.equal(gatewayConfigAcceptanceTimeoutMs({}), 30_000);
});
test("gateway config acceptance timeout honors CCR_GATEWAY_CONFIG_TIMEOUT_MS", () => {
assert.equal(
gatewayConfigAcceptanceTimeoutMs({ CCR_GATEWAY_CONFIG_TIMEOUT_MS: "60000" }),
60_000
);
});
test("gateway config acceptance timeout trims whitespace and truncates fractions", () => {
assert.equal(
gatewayConfigAcceptanceTimeoutMs({ CCR_GATEWAY_CONFIG_TIMEOUT_MS: " 12345.9 " }),
12_345
);
});
test("gateway config acceptance timeout rejects invalid or too-small overrides", () => {
for (const raw of ["", " ", "abc", "0", "-5000", "999", "NaN", "Infinity"]) {
assert.equal(
gatewayConfigAcceptanceTimeoutMs({ CCR_GATEWAY_CONFIG_TIMEOUT_MS: raw }),
30_000,
`expected default for ${JSON.stringify(raw)}`
);
}
});