Skip to content

Commit 04ba7d3

Browse files
committed
fix(qa): avoid startup prewarm contention
1 parent c37961f commit 04ba7d3

6 files changed

Lines changed: 81 additions & 3 deletions

File tree

extensions/qa-lab/src/gateway-child.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ describe("buildQaRuntimeEnv", () => {
167167
});
168168

169169
expect(env.OPENCLAW_TEST_FAST).toBe("1");
170+
expect(env.OPENCLAW_SKIP_STARTUP_MODEL_PREWARM).toBe("1");
171+
expect(env.OPENCLAW_SKIP_PROVIDER_AUTH_PREWARM).toBe("1");
170172
expect(env.OPENCLAW_EMBEDDED_ABORT_SETTLE_TIMEOUT_MS).toBe("2000");
171173
expect(env.OPENCLAW_QA_PARENT_PID).toBe(String(process.pid));
172174
expect(env.OPENCLAW_QA_TEMP_ROOT).toBe("/tmp/openclaw-qa");

extensions/qa-lab/src/gateway-child.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,8 @@ export function buildQaRuntimeEnv(params: {
228228
OPENCLAW_SKIP_BROWSER_CONTROL_SERVER: "1",
229229
OPENCLAW_SKIP_GMAIL_WATCHER: "1",
230230
OPENCLAW_SKIP_CANVAS_HOST: "1",
231+
OPENCLAW_SKIP_STARTUP_MODEL_PREWARM: "1",
232+
OPENCLAW_SKIP_PROVIDER_AUTH_PREWARM: "1",
231233
OPENCLAW_NO_RESPAWN: "1",
232234
OPENCLAW_TEST_FAST: "1",
233235
OPENCLAW_EMBEDDED_ABORT_SETTLE_TIMEOUT_MS: "2000",

src/gateway/server-startup-post-attach.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,40 @@ describe("startGatewayPostAttachRuntime", () => {
10451045
}
10461046
});
10471047

1048+
it("skips provider auth startup prewarm without disabling failure rewarm", async () => {
1049+
vi.useFakeTimers();
1050+
const onGatewayLifetimeSidecars = vi.fn();
1051+
1052+
try {
1053+
await startGatewayPostAttachRuntime({
1054+
...createPostAttachParams(),
1055+
deferSidecars: true,
1056+
providerAuthPrewarm: { startupEnabled: false },
1057+
onGatewayLifetimeSidecars,
1058+
});
1059+
1060+
await vi.dynamicImportSettled();
1061+
await vi.waitFor(() => {
1062+
expect(hoisted.setAuthProfileFailureHook).toHaveBeenCalledTimes(1);
1063+
});
1064+
expect(onGatewayLifetimeSidecars.mock.calls[0]?.[0]).toHaveLength(2);
1065+
1066+
await vi.advanceTimersByTimeAsync(10_000);
1067+
expect(hoisted.warmCurrentProviderAuthStateOffMainThread).not.toHaveBeenCalled();
1068+
1069+
const hook = hoisted.setAuthProfileFailureHook.mock.calls[0]?.[0] as (() => void) | undefined;
1070+
hook?.();
1071+
expect(hoisted.clearCurrentProviderAuthState).toHaveBeenCalledTimes(1);
1072+
1073+
await vi.advanceTimersByTimeAsync(1_000);
1074+
await vi.waitFor(() => {
1075+
expect(hoisted.warmCurrentProviderAuthStateOffMainThread).toHaveBeenCalledTimes(1);
1076+
});
1077+
} finally {
1078+
vi.useRealTimers();
1079+
}
1080+
});
1081+
10481082
it("uses current config when agent runtime plugin prewarm runs", async () => {
10491083
const startupConfig = { marker: "startup" } as never;
10501084
const currentConfig = { marker: "current" } as never;

src/gateway/server-startup-post-attach.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const AGENT_RUNTIME_PLUGIN_PREWARM_START_DELAY_MS = 10_000;
3636
const DEFERRED_SIDECAR_START_DELAY_MS = 100;
3737
const SESSION_LOCK_CLEANUP_CONCURRENCY = 4;
3838
const SKIP_STARTUP_MODEL_PREWARM_ENV = "OPENCLAW_SKIP_STARTUP_MODEL_PREWARM";
39+
const SKIP_PROVIDER_AUTH_PREWARM_ENV = "OPENCLAW_SKIP_PROVIDER_AUTH_PREWARM";
3940
const QMD_STARTUP_IDLE_DELAY_MS = 120_000;
4041

4142
type Awaitable<T> = T | Promise<T>;
@@ -128,6 +129,12 @@ function shouldSkipStartupModelPrewarm(env: NodeJS.ProcessEnv = process.env): bo
128129
return raw === "1" || raw === "true" || raw === "yes" || raw === "on";
129130
}
130131

132+
export function shouldSkipProviderAuthStartupPrewarm(
133+
env: NodeJS.ProcessEnv = process.env,
134+
): boolean {
135+
return isTruthyEnvValue(env[SKIP_PROVIDER_AUTH_PREWARM_ENV]);
136+
}
137+
131138
function resolveGatewayMemoryStartupPolicy(cfg: OpenClawConfig): GatewayMemoryStartupPolicy {
132139
if (cfg.memory?.backend !== "qmd") {
133140
return { mode: "off" };
@@ -200,6 +207,7 @@ function scheduleProviderAuthStatePrewarm(params: {
200207
warn: (msg: string) => void;
201208
};
202209
delayMs?: number;
210+
startupEnabled?: boolean;
203211
}): GatewayPostReadySidecarHandle {
204212
let stopped = false;
205213
let startupTimer: ReturnType<typeof setTimeout> | undefined;
@@ -270,6 +278,9 @@ function scheduleProviderAuthStatePrewarm(params: {
270278
clearCurrentProviderAuthState();
271279
scheduleAuthMapRewarm("auth-profile-failure");
272280
});
281+
if (params.startupEnabled === false) {
282+
return;
283+
}
273284
startupTimer = setTimeout(
274285
() => {
275286
void (async () => {
@@ -1109,6 +1120,7 @@ export async function startGatewayPostAttachRuntime(
11091120
logReadyOnSidecars?: boolean;
11101121
providerAuthPrewarm?: {
11111122
enabled?: boolean;
1123+
startupEnabled?: boolean;
11121124
delayMs?: number;
11131125
getConfig?: () => OpenClawConfig;
11141126
};
@@ -1288,6 +1300,7 @@ export async function startGatewayPostAttachRuntime(
12881300
getConfig: params.providerAuthPrewarm?.getConfig ?? (() => params.cfgAtStart),
12891301
log: params.log,
12901302
delayMs: params.providerAuthPrewarm?.delayMs,
1303+
startupEnabled: params.providerAuthPrewarm?.startupEnabled,
12911304
}),
12921305
);
12931306
}
@@ -1391,6 +1404,7 @@ export const testing = {
13911404
cleanupStaleSessionLocks,
13921405
scheduleProviderAuthStatePrewarm,
13931406
schedulePrimaryModelPrewarm,
1407+
shouldSkipProviderAuthStartupPrewarm,
13941408
shouldSkipStartupModelPrewarm,
13951409
stopPostReadySidecarsAfterCloseStarted,
13961410
};

src/gateway/server-startup.test.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ vi.mock("../agents/embedded-agent-runner/model.js", () => ({
2929
}));
3030

3131
let prewarmConfiguredPrimaryModel: typeof import("./server-startup-post-attach.js").testing.prewarmConfiguredPrimaryModel;
32+
let shouldSkipProviderAuthStartupPrewarm: typeof import("./server-startup-post-attach.js").testing.shouldSkipProviderAuthStartupPrewarm;
3233
let shouldSkipStartupModelPrewarm: typeof import("./server-startup-post-attach.js").testing.shouldSkipStartupModelPrewarm;
3334

3435
function expectModelsJsonPrewarmCall(cfg: OpenClawConfig) {
@@ -47,7 +48,11 @@ function expectModelsJsonPrewarmCall(cfg: OpenClawConfig) {
4748
describe("gateway startup primary model warmup", () => {
4849
beforeAll(async () => {
4950
({
50-
testing: { prewarmConfiguredPrimaryModel, shouldSkipStartupModelPrewarm },
51+
testing: {
52+
prewarmConfiguredPrimaryModel,
53+
shouldSkipProviderAuthStartupPrewarm,
54+
shouldSkipStartupModelPrewarm,
55+
},
5156
} = await import("./server-startup-post-attach.js"));
5257
});
5358

@@ -100,6 +105,20 @@ describe("gateway startup primary model warmup", () => {
100105
).toBe(true);
101106
});
102107

108+
it("honors the provider auth prewarm skip env", () => {
109+
expect(shouldSkipProviderAuthStartupPrewarm({})).toBe(false);
110+
expect(
111+
shouldSkipProviderAuthStartupPrewarm({
112+
OPENCLAW_SKIP_PROVIDER_AUTH_PREWARM: "1",
113+
}),
114+
).toBe(true);
115+
expect(
116+
shouldSkipProviderAuthStartupPrewarm({
117+
OPENCLAW_SKIP_PROVIDER_AUTH_PREWARM: "true",
118+
}),
119+
).toBe(true);
120+
});
121+
103122
it("skips static warmup for configured CLI backends", async () => {
104123
await prewarmConfiguredPrimaryModel({
105124
cfg: {

src/gateway/server.impl.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,7 +1599,11 @@ export async function startGatewayServer(
15991599
pluginServices: runtimeState.pluginServices,
16001600
} = await startupTrace.measure("runtime.post-attach", () =>
16011601
loadGatewayStartupPostAttachModule().then(
1602-
({ startGatewayPostAttachRuntime, stopPostReadySidecarsAfterCloseStarted }) =>
1602+
({
1603+
shouldSkipProviderAuthStartupPrewarm,
1604+
startGatewayPostAttachRuntime,
1605+
stopPostReadySidecarsAfterCloseStarted,
1606+
}) =>
16031607
startGatewayPostAttachRuntime({
16041608
minimalTestGateway,
16051609
cfgAtStart,
@@ -1687,7 +1691,10 @@ export async function startGatewayServer(
16871691
startupTrace,
16881692
deferSidecars: deferStartupSidecars,
16891693
logReadyOnSidecars: !deferStartupSidecars,
1690-
providerAuthPrewarm: { getConfig: getRuntimeConfig },
1694+
providerAuthPrewarm: {
1695+
startupEnabled: !shouldSkipProviderAuthStartupPrewarm(),
1696+
getConfig: getRuntimeConfig,
1697+
},
16911698
}),
16921699
),
16931700
));

0 commit comments

Comments
 (0)