// Self-termination for the e2e fake binaries: no fixture should be able to // outlive the run that spawned it, whatever happens to the server above it. // // The app-side fixes (cancelling live jobs before the registry is wiped, and on // graceful shutdown) cover every case where something is still alive to send a // signal. This covers the case where nothing is: a SIGKILLed dev server can't // run shutdown code, so its children reparent and keep spinning. A full suite // once left 27 of these alive at ~29% of a core each — enough to saturate the // box, push load to 46, and turn a 30-minute suite with 1 failure into a // 1.3-hour suite with 13 failures that all looked like real regressions. // // Two independent triggers, because neither alone is sufficient: // // 1. Reparenting — our parent died. Checked as "ppid changed since startup", // NOT "ppid === 1": on a systemd-user box (this one) a child subreaper // adopts orphans, so the pid-1 test would silently never fire. // 2. A max-lifetime backstop, for the case where the parent is alive but // wedged. The longest legitimate fixture life is 30s // (fake-parakeet-stitch's completion backstop, and fake-ytdlp --test-slow, // which jobs-retry/jobs-reorder/queues specs rely on), so the default 120s // leaves 4x margin. Override with E2E_FIXTURE_MAX_LIFETIME_MS if a future // fixture legitimately needs longer. // // BOTH timers are unref'd. That is the load-bearing detail: a ref'd timer would // hold the event loop open and keep an otherwise-finished fixture alive — this // module would then create exactly the leak it exists to prevent. const POLL_MS = 1_000; const DEFAULT_MAX_LIFETIME_MS = 120_000; export function installFixtureWatchdog() { const label = process.argv[1]?.split("/").pop() ?? "fixture"; const initialPpid = process.ppid; const poll = setInterval(() => { // process.ppid is re-read from the OS on each access, so this observes the // reparent rather than a value cached at startup. if (process.ppid !== initialPpid) { process.stderr.write( `[watchdog] ${label}: parent ${initialPpid} died (now ${process.ppid}) — exiting rather than orphaning\n`, ); process.exit(1); } }, POLL_MS); poll.unref(); const maxLifetimeMs = Number(process.env.E2E_FIXTURE_MAX_LIFETIME_MS) || DEFAULT_MAX_LIFETIME_MS; const backstop = setTimeout(() => { process.stderr.write( `[watchdog] ${label}: exceeded ${maxLifetimeMs}ms lifetime — exiting\n`, ); process.exit(1); }, maxLifetimeMs); backstop.unref(); }