From 3b8f4111f3acb5c374979ea0704da7a7270f7d42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 21 May 2026 11:37:53 +0200 Subject: [PATCH] perf(db): raise Prisma pg.Pool max to 20 for power-user fan-out headroom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The v1.4.39 empirical cold-mount trace (`.planning/round-v1439-empirical-trace.md` §B2) flagged the library default of 10 concurrent connections as the hard ceiling that thick /api/analytics was monopolising. With the parallel W-POOL p-limit(4) cap on the analytics fan-out, a 20-slot pool leaves ≥16 free slots for every other dashboard query regardless of how many measurement types analytics is mid-walk over. Postgres' stock `max_connections` of 100 gives ample headroom for ≤5 concurrent power-users at this ceiling. Env-overridable via `DATABASE_POOL_MAX` so operators with a smaller server-side `max_connections` can dial down without a code change. --- src/lib/db.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/lib/db.ts b/src/lib/db.ts index fb4f0ca50..588883bc5 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -5,9 +5,40 @@ const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined; }; +/** + * v1.4.40 W-POOL — Prisma `pg.Pool` ceiling raised from the library + * default of 10 → 20. + * + * The v1.4.39 empirical cold-mount trace + * (`.planning/round-v1439-empirical-trace.md` § B2) showed thick + * `/api/analytics` holding ≥ 8 of the 10 default pool slots for + * 6.5 s on Marc's 347k-row tenant, starving every other Wave-B and + * Wave-C useQuery fan-out for the duration. Coolify's Postgres + * container runs the stock `max_connections` 100 ceiling, so a + * 20-slot Node-side pool sits well under the server's hard cap with + * plenty of headroom for concurrent power-users (≤ 5 simultaneous + * tenants × 20 = 100). Complements the W-POOL `p-limit(4)` cap on + * the analytics fan-out itself — the bounded concurrency keeps any + * single analytics call to ≤ 4 slots, so the remaining 16 stay + * available for every other dashboard query. + * + * Env-overridable via `DATABASE_POOL_MAX` so an operator running + * with a smaller Postgres `max_connections` can dial down without a + * code change. + */ +export function getPoolMax(): number { + const raw = process.env.DATABASE_POOL_MAX; + if (raw) { + const parsed = Number.parseInt(raw, 10); + if (Number.isFinite(parsed) && parsed > 0) return parsed; + } + return 20; +} + function createPrismaClient() { const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL!, + max: getPoolMax(), }); return new PrismaClient({ adapter }); }