eduardweb.
Ajutor & ÎntrebăriIntermediar#nodejs#prisma#postgresql#backend#ajutor

Mă bate o eroare de tranzacții în Prisma (Transaction already closed). Ceva idei?

De Adrian Voicu, 4 iun. 2026 · 1 vizualizări · 3 like-uri

Postat acum 5 zile

Salutare tuturor. Apelez și eu la comunitate pentru că sunt pe cale să îmi pierd mințile cu o problemă de producție care ne bântuie de vreo două săptămâni.

Aveam un microserviciu scris în Node.js cu TypeScript, baza de date e un PostgreSQL în AWS RDS (instanță db.t4g.medium, destul de lejeră pentru ce facem noi), iar ca ORM am mers pe Prisma. Toate bune și frumoase până când am început să avem trafic mai serios, undeva la peste 50-60 de request-uri concurente pe secundă în momentele de vârf. De atunci, Sentry a început să se umple de erori de tipul: Transaction already closed: A query in the transaction encountered an error, or the transaction timed out.

Partea frustrantă este că eroarea apare complet aleatoriu. Am încercat să o reproduc local cu un script de load testing în k6, dar pe mașina mea totul rulează perfect, chiar și la 200 de conexiuni simultane.

Ce am încercat deja și n-a mers

Am luat la mână documentația Prisma și toate thread-urile de pe GitHub. Primul instinct a fost să măresc timeout-urile pentru tranzacțiile interactive.

Implicit, Prisma închide tranzacția după 5 secunde. Am configurat explicit setările astea, urcând destul de mult ștacheta: timeout: 20000 (20 de secunde pentru tranzacție) și maxWait: 5000 (5 secunde pentru a obține o conexiune din pool). Rezultatul? Eroarea tot apare, doar că acum unele request-uri agață mai mult în API gateway înainte să dea crash.

Apoi am verificat event loop-ul și conexiunile. Am crezut că poate baza de date e gâtuită și nu mai are conexiuni libere în pool. Am mărit connection_limit la 25 în connection string-ul de PostgreSQL. CPU-ul pe instanța de RDS nu trece de 15% în momentele de vârf, iar numărul de conexiuni active raportat de AWS e undeva la jumătate din limită. Deci baza de date efectiv șade degeaba, nu acolo e blocajul.

În final, am făcut refactoring la cod. Știu că în tranzacțiile interactive Prisma folosește un client special pe care îl primim ca argument (instanța tx). Dacă folosești din greșeală clientul global prisma în interiorul funcției, totul se dă peste cap. Am verificat fiecare linie. Toate query-urile folosesc corect instanța tx și toate au await în față. Nu avem niciun Promise.all ascuns sau vreo execuție asincronă lăsată în background fără await.

Care e flow-ul de business și trade-off-ul nostru

Logica e destul de clasică pentru un sistem de credite:

  1. Citim balanța curentă a utilizatorului (cu un select)
  2. Verificăm dacă are destule credite pentru operațiune
  3. Creăm o înregistrare de audit în tabela de tranzacții
  4. Scădem creditele din contul lui

Marele nostru trade-off e că avem nevoie de pașii ăștia intermediari de validare în codul de Node.js. Dacă am fi putut folosi o tranzacție secvențială simplă (adică array-ul clasic de promisiuni trimis către prisma.$transaction([query1, query2])), probabil nu am fi avut problema asta. Dar logica de business ne obligă să mergem pe varianta interactivă, unde rulăm funcții JS între query-uri.

Ne gândim serios dacă nu cumva Prisma are un overhead masiv pe partea de management al tranzacțiilor în engine-ul lor scris în Rust. Am citit că generatorul de query-uri face un du-te-vino destul de costisitor între Node.js și engine-ul nativ pentru fiecare pas din tranzacția interactivă.

A mai lovit cineva problema asta în condiții de load real? Să fie o problemă de event loop block în Node care face ca engine-ul Prisma să creadă că tranzacția a expirat? Sau poate ar trebui să trimitem direct un query brut cu BEGIN și COMMIT scris manual? Sincer, parcă nu aș vrea să renunțăm la maparea de tipuri din Prisma pentru jumătate din aplicație, dar dacă nu îi dăm de cap, probabil Kysely sau Drizzle e următorul pas.

Voi cum gestionați tranzacțiile complexe sub trafic mare în Prisma?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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