eduardweb.
PostgreSQLAvansat#devops#prisma#postgresql#backend

PgBouncer în producție: Transaction vs Session și cum îl împaci cu Prisma

De Elisabeta Stan, 28 mai 2026 · 9 vizualizări · 3 like-uri

Postat 28 mai 2026
plaintext
# .env
# URL-ul direct către baza de date, folosit doar pentru migrări
DIRECT_DATABASE_URL="postgresql://postgres:parola@db-host:5432/mydb?schema=public"

# URL-ul prin PgBouncer (mod transaction), folosit de aplicație în runtime
DATABASE_URL="postgresql://postgres:parola@pgbouncer-host:6432/mydb?schema=public&pgbouncer=true&connection_limit=3"

# schema.prisma
datasource db {
  provider  = "postgresql"
  url       = env("DATABASE_URL")
  directUrl = env("DIRECT_DATABASE_URL")
}

Dacă folosești Prisma cu PostgreSQL în producție și ai trecut de câteva sute de utilizatori concurenți, probabil ai dat deja de celebra eroare "too many clients". Hai să vorbim direct despre cum configurezi PgBouncer corect ca să nu-ți crape baza de date la primul spike de trafic. Am trecut prin asta pe un proiect cu peste 15.000 de utilizatori activi și am salvat vreo 35% din memoria serverului de DB doar din reglarea corectă a pool-ului.

Session vs. Transaction. Unde e capcana?

PgBouncer are trei moduri de rulare, dar în producție vorbim doar de două: Session și Transaction.

În modul session, PgBouncer se comportă ca un proxy chior. Îți dă o conexiune fizică din pool și o ține blocată pentru clientul tău până când acesta se deconectează complet. E cel mai sigur mod, merg toate funcționalitățile PostgreSQL (tabele temporare, prepared statements), dar nu rezolvă problema de scalabilitate dacă ai zeci de instanțe de serverless (Lambda sau Cloud Run) care stau agățate de DB fără să facă nimic.

Modul transaction e cel care face minuni pentru scalabilitate. Conexiunea fizică la Postgres este eliberată și returnată în pool imediat ce s-a terminat tranzacția curentă. O singură conexiune fizică la DB poate deservi, prin multiplexare, zeci sau sute de clienți activi ai aplicației.

Dar vine cu un mare trade-off: pierzi prepared statements. Dacă aplicația ta încearcă să folosească prepared statements (cum fac majoritatea ORM-urilor implicit), o să primești erori ciudate de tipul prepared statement "s1" already exists sau erori de protocol. Asta se întâmplă pentru că PgBouncer rutează query-urile tale consecutive către conexiuni fizice diferite în mod complet transparent.

Cum împaci Prisma cu PgBouncer

Prisma folosește sub capotă un Query Engine scris în Rust care își gestionează propriul pool de conexiuni. Când pui PgBouncer în modul transaction în fața lui, lucrurile se strică instantaneu dacă nu-i spui explicit ce se întâmplă.

Soluția este să folosești două URL-uri de conexiune diferite în configurația ta. Ai nevoie de un URL direct către baza de date (portul 5432) pe care Prisma să îl folosească doar pentru migrări (unde are nevoie de modul session ca să poată face lock-uri pe tabelele de migrare) și un URL prin PgBouncer (portul 6432) pentru rularea aplicației în sine.

În URL-ul de PgBouncer trebuie neapărat să adaugi parametrul ?pgbouncer=true. Acest parametru îi spune motorului Prisma să nu mai folosească prepared statements și să treacă pe query-uri simple, parametrizate direct.

Cum calculăm dimensiunea pool-ului

Cea mai comună greșeală pe care o văd este setarea unui pool uriaș peste tot. Regula de aur pentru Postgres este: puține conexiuni fizice, dar folosite intens. Formula clasică recomandată de PostgreSQL pentru numărul optim de conexiuni active este ((core_count * 2) + effective_spindle_count). Pe un server cu 4 vCPUs, asta înseamnă cam 9-10 conexiuni active directe către Postgres.

În pgbouncer.ini, setezi max_db_connections aproape de această limită hardware (să zicem 15 ca să ai loc de manevră).

Apoi, în URL-ul Prisma pentru aplicație, setezi connection_limit la o valoare mică, de exemplu connection_limit=3 per instanță. Dacă ai 5 containere de API în producție, 5 containere * 3 conexiuni = 15 conexiuni maxime către PgBouncer, care apoi le multiplexează impecabil în cele 9 conexiuni fizice reale către Postgres.

Voi cum ați rezolvat problema asta în producție? Mergeți pe PgBouncer clasic sau ați trecut deja pe alternative mai moderne ca Supabase 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.