diff --git a/consumer-prices-core/src/acquisition/playwright.ts b/consumer-prices-core/src/acquisition/playwright.ts index 2af34f201..1a5053171 100644 --- a/consumer-prices-core/src/acquisition/playwright.ts +++ b/consumer-prices-core/src/acquisition/playwright.ts @@ -65,8 +65,11 @@ export class PlaywrightProvider implements AcquisitionProvider { } async teardown(): Promise { - await this.context?.close(); - await this.browser?.close(); + const timeout = new Promise(r => setTimeout(r, 5000)); + await Promise.race([ + Promise.allSettled([this.context?.close(), this.browser?.close()]), + timeout, + ]); this.context = null; this.browser = null; } diff --git a/consumer-prices-core/src/jobs/scrape.ts b/consumer-prices-core/src/jobs/scrape.ts index b891d6021..a0d9884dd 100644 --- a/consumer-prices-core/src/jobs/scrape.ts +++ b/consumer-prices-core/src/jobs/scrape.ts @@ -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(r => setTimeout(r, 5000)); + await Promise.race([closePool().catch(() => {}), poolTimeout]); } }