eduardweb.
PostgreSQLAvansat#devops#prisma#postgresql#backend

PgBouncer în producție: Transaction vs Session și integrarea cu Prisma

De Ana Ionescu, 25 mai 2026 · 7 vizualizări · 2 like-uri

Postat 25 mai 2026
prisma
datasource db {
  provider  = "postgresql"
  url       = env("DATABASE_URL") // Port 6543 (PgBouncer) + ?pgbouncer=true
  directUrl = env("DIRECT_URL")   // Port 5432 (Postgres direct)
}

Dacă folosești Prisma în producție și ai trecut de faza de MVP, sigur te-ai lovit de faimoasa eroare cu max connections reached. Prisma are un obicei prost: își face propriul pool de conexiuni pe fiecare instanță de server. Când ai 3 instanțe în autoscaling, plus niște serverless functions, baza de date crapă rapid.

Am pățit asta acum un an la un proiect cu peste 12k utilizatori activi concurenți. Soluția clasică e PgBouncer, dar dacă îl configurezi greșit, mai rău strici. Hai să vedem cum se face corect, fără bullshit teoretic.

Session vs Transaction Mode: Unde e capcana?

PgBouncer are trei moduri, dar în producție discutăm doar de două: session și transaction.

În Session Mode, PgBouncer se comportă ca un proxy chior. Când o instanță se conectează, primește o conexiune la Postgres și o ține ocupată până când se închide clientul. Pentru Prisma, modul ăsta e aproape inutil. Dacă ai 50 de instanțe de Node.js, tot ai nevoie de 50 de conexiuni deschise în Postgres. Am economisit exact 0% resurse așa.

În Transaction Mode, conexiunea la Postgres e eliberată imediat ce s-a terminat tranzacția curentă. Următorul query din alt client poate folosi aceeași conexiune fizică. Aici e salvarea. Am reușit să deservim aceiași 12k useri cu doar 15 conexiuni reale în Postgres, în loc de 150.

Dar există un mare trade-off. În modul transaction, nu poți folosi Prepared Statements (decât cu niște hack-uri din Postgres 12+) și pierzi funcționalități ca LISTEN/NOTIFY sau tabele temporare. Pentru noi, a meritat sacrificiul.

Cum legi Prisma la PgBouncer (Fără să strici migrările)

Prisma are nevoie de două conexiuni diferite în schema.prisma. De ce? Pentru că migrările (prisma migrate deploy) au nevoie de tranzacții lungi și folosesc comenzi de sistem care crapă în modul transaction al PgBouncer.

Configurația corectă folosește url și directUrl în schema ta.

url trimite query-urile normale prin PgBouncer (portul 6543, de obicei) și îi punem parametrul ?pgbouncer=true. Acest parametru îi spune motorului Prisma să nu folosească prepared statements lungi, evitând erorile de protocol din modul transaction.

directUrl se conectează direct la baza de date (portul 5432), ocolind PgBouncer. Asta e folosită doar pentru migrări.

Cum calculezi Pool Size?

Nu lăsa valorile default din tutoriale. Regula mea de aur pe care am aplicat-o la proiectul menționat:

  1. În Postgres (postgresql.conf), setează max_connections = 100 (sau cât duce RAM-ul tău, dar nu exagera).
  2. În PgBouncer (pgbouncer.ini), setează default_pool_size = 20 și max_client_conn = 500.
  3. În conexiunea Prisma din aplicație (în DATABASE_URL), setează connection_limit=3.

Dacă ai 15 instanțe de app, ele vor cere maxim 45 de conexiuni de la PgBouncer (15 * 3). PgBouncer le va rula pe toate prin cele 20 de conexiuni reale deschise către Postgres. Baza de date respiră ușurată, CPU-ul stă la 15%, iar utilizatorii nu simt nicio latență.

Voi ce setup folosiți pentru scaling pe Postgres? Ați trecut la Supabase/Neon sau tot pe clasicul PgBouncer self-hosted mergeți?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.