refactor: move resetAllLanes() from heartbeat-wake to restart coordinators

Addresses review concern that setHeartbeatWakeHandler() had a surprising
cross-cutting side effect by calling resetAllLanes(), coupling heartbeat
handler registration to command-queue global state.

The lane reset now lives in the restart loop (run-loop.ts and
gateway-daemon.ts), which is the correct abstraction level — only
in-process restart coordinators need to know about stale lane state.

setHeartbeatWakeHandler() still resets its own module-level state
(running, scheduled, timer) which is properly scoped.
This commit is contained in:
Joey Krug
2026-02-13 04:59:31 +00:00
committed by Gustavo Madeira Santana
parent 4b2ed10df3
commit 8eb80b6d0b
3 changed files with 28 additions and 9 deletions

View File

@@ -6,7 +6,11 @@ import {
isGatewaySigusr1RestartExternallyAllowed,
} from "../../infra/restart.js";
import { createSubsystemLogger } from "../../logging/subsystem.js";
import { getActiveTaskCount, waitForActiveTasks } from "../../process/command-queue.js";
import {
getActiveTaskCount,
resetAllLanes,
waitForActiveTasks,
} from "../../process/command-queue.js";
const gatewayLog = createSubsystemLogger("gateway");
@@ -114,7 +118,18 @@ export async function runGatewayLoop(params: {
// Keep process alive; SIGUSR1 triggers an in-process restart (no supervisor required).
// SIGTERM/SIGINT still exit after a graceful shutdown.
// eslint-disable-next-line no-constant-condition
let isFirstIteration = true;
while (true) {
if (!isFirstIteration) {
// After an in-process restart (SIGUSR1), reset command-queue lane state.
// Interrupted tasks from the previous lifecycle may have left `active`
// counts elevated (their finally blocks never ran), permanently blocking
// new work from draining. This must happen here — at the restart
// coordinator level — rather than inside individual subsystem init
// functions, to avoid surprising cross-cutting side effects.
resetAllLanes();
}
isFirstIteration = false;
server = await params.start();
await new Promise<void>((resolve) => {
restartResolver = resolve;