fix(consumer-prices): prevent Playwright hang from blocking aggregate + publish (#2006)

* fix(consumer-prices): prevent Playwright hang from blocking aggregate + publish jobs

browser.close() can silently hang after scraping completes, keeping the
node process alive indefinitely. This blocked the && chain in the Railway
startCommand, so aggregate.js and publish.js never ran.

Two-layer fix:
- teardown(): race browser.close() against a 5s timeout so Chromium
  unresponsiveness never blocks teardownAll()
- scrape.ts: add a 12-minute hard-kill timer as an ultimate safety net
  in case any other async handle prevents natural exit

* fix(consumer-prices): replace startup watchdog with bounded closePool timeout

The 12-minute hard-kill timer started at process startup, meaning a
legitimate long scrape would be killed mid-job with exit code 0 —
letting aggregate and publish run against partial data.

Replace with a 5s race on closePool() in the finally block, mirroring
the teardown() fix in playwright.ts. With both hang points bounded,
main() always resolves promptly after scraping completes and
process.exit() fires immediately with the correct exit code.
This commit is contained in:
Elie Habib
2026-03-21 20:25:21 +04:00
committed by GitHub
parent 054ee00801
commit 9feefecd1e
2 changed files with 10 additions and 3 deletions

View File

@@ -65,8 +65,11 @@ export class PlaywrightProvider implements AcquisitionProvider {
}
async teardown(): Promise<void> {
await this.context?.close();
await this.browser?.close();
const timeout = new Promise<void>(r => setTimeout(r, 5000));
await Promise.race([
Promise.allSettled([this.context?.close(), this.browser?.close()]),
timeout,
]);
this.context = null;
this.browser = null;
}

View File

@@ -207,7 +207,11 @@ async function main() {
console.error('[scrape] fatal:', err);
process.exitCode = 1;
} finally {
await closePool().catch(() => {});
// Race closePool against a 5s timeout — mirrors the teardown() fix in playwright.ts.
// Without a bound, a hung pg pool would keep main() pending indefinitely,
// delaying process.exit() and stalling the && chain (aggregate, publish).
const poolTimeout = new Promise<void>(r => setTimeout(r, 5000));
await Promise.race([closePool().catch(() => {}), poolTimeout]);
}
}