## Proof of Correctness — Commit 3/22 **Optimization:** Replace 5 separate `new PrismaClient()` instances with a shared singleton at `@/lib/prisma`. **Claim:** Eliminates 5 independent connection pools → 1 shared pool with `connection_limit=10`, `pool_timeout=20`. Prevents connection pool exhaustion under concurrent requests. ### Evidence Each `new PrismaClient()` creates its own query engine and PostgreSQL connection pool (default 5 connections). With 5 instances, the app could hold 25 connections simultaneously — a real risk against PostgreSQL's hard limit (typically 100, often lower on Azure). Files that had their own `new PrismaClient()`: - `src/app/(dashboard)/apikeys/page.tsx` - `src/app/(dashboard)/apikeys/tokenfuncs.ts` - `src/app/api/traces/[trace_id]/save-modified-code/route.ts` - `src/app/trace/[trace_id]/page.tsx` - `src/lib/modified-code-utils.ts` The singleton pattern is [Prisma's official recommendation for Next.js](https://www.prisma.io/docs/orm/more/help-and-troubleshooting/help-articles/nextjs-prisma-client-dev-practices). ### Reproducer ```bash cd js/cf-webapp bash proof/reproducers/03-prisma-singleton.sh ``` Verifies: 1. No `new PrismaClient()` outside `src/lib/prisma.ts` 2. All 5 affected files import from `@/lib/prisma` 3. Singleton has connection pooling + globalThis caching 4. TypeScript compiles cleanly ### Files - `js/cf-webapp/proof/03-prisma-singleton.md` — detailed proof - `js/cf-webapp/proof/reproducers/03-prisma-singleton.sh` — reproducer ### Reference - Source commit: 16c5887a from PR #2536
3.1 KiB
3.1 KiB
Proof: PrismaClient Singleton (16c5887a)
Optimization
Replace 5 separate new PrismaClient() calls with the shared singleton at @/lib/prisma.
Claim
Eliminates 5 independent connection pools in favor of 1 shared pool with connection_limit=10 and pool_timeout=20. Prevents connection pool exhaustion under concurrent requests.
Root Cause
Each new PrismaClient() creates its own:
- Query engine instance (Rust binary via WASM/native)
- Connection pool to PostgreSQL (default: 5 connections per pool)
- Event listeners and logging infrastructure
With 5 independent instances across 5 files, the app could hold up to 25 connections to PostgreSQL simultaneously (5 pools × 5 default connections). PostgreSQL has a hard limit (typically 100 connections), and Azure-hosted instances often have lower limits.
Before (5 files, each with their own instance)
// apikeys/page.tsx
const prisma = new PrismaClient()
// apikeys/tokenfuncs.ts
const prisma = new PrismaClient()
// api/traces/[trace_id]/save-modified-code/route.ts
const prisma = new PrismaClient()
// trace/[trace_id]/page.tsx
const prisma = new PrismaClient()
// lib/modified-code-utils.ts
const prisma = new PrismaClient()
After (all use shared singleton)
import { prisma } from "@/lib/prisma"
The singleton at src/lib/prisma.ts:
- Creates one PrismaClient with
connection_limit=10,pool_timeout=20 - Caches in
globalThisduring development (survives Next.js HMR reloads) - Logs slow queries (>500ms) in development
- Forwards Prisma errors to Sentry
Files Changed
| File | Change |
|---|---|
src/app/(dashboard)/apikeys/page.tsx |
new PrismaClient() → import { prisma } from "@/lib/prisma" |
src/app/(dashboard)/apikeys/tokenfuncs.ts |
Same |
src/app/api/traces/[trace_id]/save-modified-code/route.ts |
Same |
src/app/trace/[trace_id]/page.tsx |
Same |
src/lib/modified-code-utils.ts |
Same |
How to Verify
cd js/cf-webapp
bash proof/reproducers/03-prisma-singleton.sh
The reproducer:
- Greps the codebase for
new PrismaClient()— should only appear insrc/lib/prisma.ts - Verifies all 5 previously-affected files import from
@/lib/prisma - Confirms the singleton has connection pooling configured
- Runs
tsc --noEmitto verify types check clean
Why This Is Real
- Each
new PrismaClient()is a new connection pool — this is documented Prisma behavior, not speculation. The Prisma query engine is a separate process that maintains its own PostgreSQL connections. - Connection pool exhaustion is a production risk — with 5 pools × default 5 connections = 25 connections from a single Next.js process. Under serverless/edge deployments with multiple instances, this multiplies further.
- The singleton pattern is Prisma's official recommendation for Next.js — Prisma docs: Best practice for instantiating PrismaClient.
globalThiscaching prevents HMR leaks — without it, each hot reload in development creates a new instance, eventually exhausting connections.