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

Mă bate eroarea „Transaction already closed” în Prisma și nu-i dau de cap

De Maria Vasilescu, 29 apr. 2026 · 2 vizualizări · 3 like-uri

Postat acum 2 zile

M-am lovit săptămâna asta de o problemă care îmi mănâncă zilele în producție și, sincer, am ajuns la capătul răbdării cu modul în care Prisma gestionează tranzacțiile interactive. E vorba de eroarea aia clasică Transaction already closed, care apare total aleatoriu. Nu e constantă, nu e predictibilă. Apare cam de 5-10 ori la fiecare 1.000 de request-uri pe un endpoint critic de checkout, exact unde te doare mai tare.

Contextul e un proiect cu vreo 12k useri activi lunar, stack-ul fiind Next.js, Prisma și un PostgreSQL hostat pe RDS. Nu e trafic masiv care să justifice blocaje de conexiuni sau resurse epuizate la maximum, dar totuși ceva crapă sub capotă.

Ce am încercat până acum

Prima chestie la care m-am gândit a fost timeout-ul. Am urcat itx_timeout în opțiunile de tranzacție de la default-ul de 5 secunde la 30 de secunde. Ziceam că gata, am rezolvat-o, sigur e un query care se lungește din cauza unor lock-uri pe tabele. Ei bine, surpriză: eroarea continuă să apară chiar și la operații care, conform logurilor de monitorizare, durează mai puțin de 300ms. Deci timeout-ul tranzacției pare să fie o pistă falsă în cazul ăsta.

Apoi, m-am apucat să fac audit pe cod pentru celebrul „unawaited promise”. Știți cum e în Prisma: dacă uiți un await în interiorul callback-ului de $transaction, execuția merge mai departe, callback-ul se termină, Prisma închide tranzacția, iar când promise-ul ăla uitat se trezește la viață și vrea să execute ceva, găsește ușa încuiată. Am luat la mână tot flow-ul de vreo trei ori, cap-coadă. Totul pare legat corect cu await, nicio funcție async apelată „fire and forget”.

Am umblat și la connection_limit. Am zis că poate sub presiune, pool-ul de conexiuni face ceva ciudat și reciclează conexiunea prea repede. Am setat ?connection_limit=20 în URL-ul de bază de date (avem un t3.medium, deci duce), dar comportamentul a rămas identic. Partea cea mai frustrantă e că în mediul de staging sau local, oricât aș trage de el cu scripte de load testing, nu reușesc să reproduc eroarea. Totul merge „ceas”.

Comportamentul e total ilogic

Eroarea care vine în loguri e P2028: Transaction API error: Transaction already closed. Uneori se întâmplă imediat după un upsert care pare să fi avut succes în baza de date. Adică datele se scriu, dar Prisma aruncă eroarea asta la finalul blocului de tranzacție, când încearcă probabil să facă commit-ul final.

Am început să suspectez ceva legat de motorul de Rust al Prismei. Am citit pe niște thread-uri vechi de GitHub că ar fi existat probleme cu managementul conexiunilor în versiunile 4.x, dar noi suntem pe 5.12.0. Să fie oare o regresie? Sau poate e ceva legat de latența rețelei între serverul de aplicație și RDS care face ca engine-ul să creadă că tranzacția a expirat, deși timpul scurs e mic?

Flow-ul nostru e destul de standard: verificăm stocul, scădem cantitatea, creăm comanda și logăm totul într-o tabelă de audit. Totul e înfășurat într-un $transaction. Dacă scot tranzacția, totul merge „brici”, dar evident că nu pot lăsa așa pentru că riscăm inconsistențe majore de date dacă moare ceva la jumătatea procesului.

Sincer, am ajuns într-un punct în care mă gândesc serios să rescriu logica aia în SQL brut sau să folosesc alt query builder pentru bucata asta de cod, dar parca e păcat de restul proiectului. Voi ați mai dat de mizeria asta în producție? Există vreo setare obscură în schema.prisma sau vreun parametru de kernel la baza de date care ar putea influența cum „vede” Prisma starea conexiunii?

Orice idee, oricât de nebunească, e binevenită, că eu am rămas fără opțiuni.

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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