codeflash-internal/js/cf-webapp/proof/03-prisma-singleton.md
Kevin Turcios fad39c934d
proof: PrismaClient singleton consolidation (16c5887a) (#2543)
## 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
2026-04-04 11:26:33 -05:00

3.1 KiB
Raw Blame History

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 globalThis during 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:

  1. Greps the codebase for new PrismaClient() — should only appear in src/lib/prisma.ts
  2. Verifies all 5 previously-affected files import from @/lib/prisma
  3. Confirms the singleton has connection pooling configured
  4. Runs tsc --noEmit to verify types check clean

Why This Is Real

  1. 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.
  2. 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.
  3. The singleton pattern is Prisma's official recommendation for Next.js — Prisma docs: Best practice for instantiating PrismaClient.
  4. globalThis caching prevents HMR leaks — without it, each hot reload in development creates a new instance, eventually exhausting connections.