eduardweb.
PostgreSQLAvansat#devops#prisma#backend#postgres#pgbouncer

De ce îți crapă Prisma în producție cu PgBouncer și cum rezolvi conexiunile

De Ștefan Iliescu, 8 iul. 2026 · 17 vizualizări · 3 like-uri

Postat 8 iul. 2026
typescript
datasource db {
  provider  = "postgresql"
  // Conexiunea prin PgBouncer (portul 6432) în transaction mode
  url       = env("DATABASE_URL_PGBOUNCER")
  // Conexiunea directă la Postgres (portul 5432) pentru migrări
  directUrl = env("DATABASE_URL_DIRECT")
}

Dacă folosești Prisma în producție și ai trafic serios, probabil ai dat deja de celebra eroare 'Too many clients'. Postgres deschide câte un proces per conexiune și consumă RAM ca la balamuc, așa că pui PgBouncer în față. Dar dacă nu ești atent la detalii, mai rău strici totul.

Am pățit asta acum doi ani la un proiect cu vreo 12.000 de utilizatori activi concurenți. Serverul de Postgres pur și simplu îngenunchea din cauza overhead-ului de conexiuni active. Trecerea la PgBouncer ne-a salvat, reducând consumul de memorie cu aproape 40%, dar configurarea a fost un mic iad din cauza modului în care Prisma interacționează cu baza de date.

Session vs Transaction Mode

Aici se face prima greșeală majoră. PgBouncer are trei moduri, dar în producție doar două contează cu adevărat: session și transaction.

În session mode, PgBouncer îți oferă o conexiune fizică din pool când clientul se conectează și o ține blocată până când clientul se deconectează complet. Pentru serverless sau microservicii care se scalează rapid, modul ăsta e aproape inutil. Nu rezolvă problema de fond a conexiunilor irosite în perioadele de inactivitate.

În transaction mode, conexiunea fizică este returnată în pool imediat ce tranzacția curentă s-a terminat. Este modul ideal pentru scalare masivă, dar vine cu un trade-off sincer: pierzi proprietățile la nivel de sesiune. Nu mai poți folosi tabele temporare (TEMPORARY TABLE), instrucțiuni LISTEN/NOTIFY sau prepared statements clasice, deoarece următorul tău query din aceeași sesiune poate rula pe o conexiune fizică complet diferită.

Problema cu Prisma și Prepared Statements

Prisma folosește implicit prepared statements pentru a optimiza performanța query-urilor. În transaction mode, dacă PgBouncer trimite query-ul tău pe o altă conexiune fizică unde acel statement nu a fost pregătit anterior, totul crapă cu erori de protocol destul de greu de debugat.

Soluția este să îi spui explicit Prismei că vorbește cu un proxy de conexiuni. Adăugăm parametrul ?pgbouncer=true în string-ul de conexiune. Acesta forțează Prisma să folosească query-uri simple pentru operațiunile normale.

Dar cum facem cu migrările? prisma migrate are nevoie absolută de session mode sau de o conexiune directă ca să poată pune lock pe baza de date în timpul modificărilor de schemă. Soluția curată este să folosești două URL-uri diferite în configurarea ta din schema.prisma.

Cum calculăm Pool Sizes ca să nu blocăm baza

O altă greșeală clasică este să lași default_pool_size din PgBouncer la valoarea implicită de 20 și să speri că va fi bine.

Dacă ai 10 instanțe de microservicii și fiecare are un pool intern în Prisma de 10 conexiuni, ai nevoie de minimum 100 de conexiuni în PgBouncer. În Postgres însă, max_connections nu ar trebui să fie setat la valori astronomice. Formula mea empirică pentru servere medii este: (număr de nuclee CPU * 2) + 2 pentru conexiunile fizice active.

În PgBouncer, setează default_pool_size astfel încât suma tuturor conexiunilor deschise către Postgres să nu depășească 80% din max_connections configurat în baza de date. Restul de 20% trebuie lăsat liber pentru conexiuni administrative, scripturi de backup sau migrații directe.

Voi cum gestionați conexiunile când folosiți serverless sau containere efemere? Ați rămas pe PgBouncer sau ați trecut pe alternative mai noi ca Supavisor ori AWS RDS Proxy?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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