diff --git a/src/lib/__tests__/db.test.ts b/src/lib/__tests__/db.test.ts index ec872c51f..73f395e39 100644 --- a/src/lib/__tests__/db.test.ts +++ b/src/lib/__tests__/db.test.ts @@ -80,8 +80,8 @@ describe("getStatementTimeoutMs", () => { } }); - it("defaults to 15000ms when the env var is unset", () => { - expect(getStatementTimeoutMs()).toBe(15_000); + it("defaults to 60000ms when the env var is unset", () => { + expect(getStatementTimeoutMs()).toBe(60_000); }); it("honours a positive override", () => { @@ -96,9 +96,9 @@ describe("getStatementTimeoutMs", () => { it("falls back to the default on a malformed or negative value", () => { process.env.DATABASE_STATEMENT_TIMEOUT_MS = "not-a-number"; - expect(getStatementTimeoutMs()).toBe(15_000); + expect(getStatementTimeoutMs()).toBe(60_000); process.env.DATABASE_STATEMENT_TIMEOUT_MS = "-5"; - expect(getStatementTimeoutMs()).toBe(15_000); + expect(getStatementTimeoutMs()).toBe(60_000); }); }); @@ -116,8 +116,8 @@ describe("buildSessionOptions", () => { it("emits both session timeouts as libpq startup options by default", () => { delete process.env.DATABASE_STATEMENT_TIMEOUT_MS; const opts = buildSessionOptions(); - expect(opts).toContain("-c statement_timeout=15000"); - expect(opts).toContain("-c idle_in_transaction_session_timeout=15000"); + expect(opts).toContain("-c statement_timeout=60000"); + expect(opts).toContain("-c idle_in_transaction_session_timeout=60000"); }); it("returns undefined (no options) when the timeout is disabled", () => { diff --git a/src/lib/db.ts b/src/lib/db.ts index 01ab1607f..0c3b8472f 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -48,10 +48,11 @@ export function getPoolMax(): number { * pooled session inherits them. * * Env-overridable via `DATABASE_STATEMENT_TIMEOUT_MS`. Set to `0` to disable - * (legacy unbounded behaviour). Default 15 s — generous for the rollup-backed - * read paths while still bounding a runaway query well before it can starve the - * pool. Heavy admin one-shots (drain/backfill) run on the worker, not the web - * pool, and can dial their own timeout if needed. + * (legacy unbounded behaviour). Default 60 s — bounds a genuinely runaway query + * (the A-1 goal: never hold a pool slot forever) while leaving ample headroom + * for the heaviest legitimate read, the live-aggregate analytics fallback on a + * coverage miss over a large account, which can run well past 15 s. Heavy admin + * one-shots (drain/backfill) can still dial their own timeout if needed. */ export function getStatementTimeoutMs(): number { const raw = process.env.DATABASE_STATEMENT_TIMEOUT_MS; @@ -59,7 +60,7 @@ export function getStatementTimeoutMs(): number { const parsed = Number.parseInt(raw, 10); if (Number.isFinite(parsed) && parsed >= 0) return parsed; } - return 15_000; + return 60_000; } /**