mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 11:25:50 -06:00
fix(gateway): keep tearing down listeners when cron.stopAndDrain() rejects on shutdown (#114848)
The close handler ran cron.stopAndDrain() and heartbeatRunner.stop() bare, while every sibling teardown uses shutdownStep() (catch/warn/continue). stopAndDrain() re-throws stream-watcher stop failures by design, so the rejection skipped the remaining teardown -- wss.close(), httpServer.close(), client closes, interval clears -- leaving the port bound and timers live; the next listen() then hits EADDRINUSE. Wrap both calls in shutdownStep. Adds a regression test. Co-authored-by: MatthewSynthia <matthewsynthia@users.noreply.github.com>
This commit is contained in:
@@ -182,6 +182,26 @@ describe("createGatewayCloseHandler", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("still runs later teardown when cron.stopAndDrain() rejects (no listener strand)", async () => {
|
||||
const stopAndDrain = vi.fn().mockRejectedValue(new Error("stream watcher stop failed"));
|
||||
const httpClose = vi.fn((cb: (err?: Error | null) => void) => cb(null));
|
||||
const deps = createGatewayCloseTestDeps({
|
||||
cron: { stop: vi.fn(), stopAndDrain } as never,
|
||||
httpServer: { close: httpClose, closeIdleConnections: vi.fn() } as never,
|
||||
});
|
||||
const close = createGatewayCloseHandler(deps);
|
||||
|
||||
const result = await close({ reason: "test" });
|
||||
|
||||
// A rejecting stopAndDrain must be swallowed (recorded as a warning) and must NOT skip the
|
||||
// remaining teardown -- otherwise the HTTP/WS listeners and timers strand and the next
|
||||
// start hits EADDRINUSE.
|
||||
expect(stopAndDrain).toHaveBeenCalledTimes(1);
|
||||
expect(deps.heartbeatRunner.stop).toHaveBeenCalledTimes(1);
|
||||
expect(httpClose).toHaveBeenCalled();
|
||||
expect(result.warnings.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("completes a clean shutdown with a ShutdownResult", async () => {
|
||||
const deps = createGatewayCloseTestDeps();
|
||||
const close = createGatewayCloseHandler(deps);
|
||||
|
||||
@@ -883,12 +883,12 @@ export function createGatewayCloseHandler(
|
||||
await measureCloseStep("gmail-watcher", () =>
|
||||
shutdownStep("gmail-watcher", () => stopGmailWatcherOnDemand(), warnings),
|
||||
);
|
||||
if (params.cron.stopAndDrain) {
|
||||
await params.cron.stopAndDrain();
|
||||
} else {
|
||||
params.cron.stop();
|
||||
}
|
||||
params.heartbeatRunner.stop();
|
||||
await shutdownStep(
|
||||
"cron",
|
||||
() => (params.cron.stopAndDrain ? params.cron.stopAndDrain() : params.cron.stop()),
|
||||
warnings,
|
||||
);
|
||||
await shutdownStep("heartbeat-runner", () => params.heartbeatRunner.stop(), warnings);
|
||||
await shutdownStep(
|
||||
"task-registry-maintenance",
|
||||
() => params.stopTaskRegistryMaintenance?.(),
|
||||
|
||||
Reference in New Issue
Block a user