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

Am o eroare Prisma 'Transaction already closed' în prod. Unde să mai caut?

De Răzvan Matei, 20 iun. 2026 · 22 vizualizări · 3 like-uri

Postat 20 iun. 2026
typescript
await prisma.$transaction(async (tx) => {
  const product = await tx.product.findUnique({
    where: { id: productId },
  });

  if (!product || product.stock < quantity) {
    throw new Error('Stoc insuficient');
  }

  await tx.order.create({
    data: { userId, productId, quantity },
  });

  await tx.product.update({
    where: { id: productId },
    data: { stock: { decrement: quantity } },
  });
}, {
  timeout: 15000 // am încercat și fără, tot crapă random
});

Salutare tuturor. Am dat de o belea în producție de care nu mai reușesc să scap și îmi mănâncă nopțile de vreo săptămână. E vorba de clasica, dar extrem de enervanta eroare de la Prisma: Transaction already closed: Transaction is deadlocked or has already been committed or rolled back.

Faza e că apare complet random. Avem un API scris în NestJS, baza de date e un PostgreSQL pe AWS RDS (un db.t3.medium, deci 2 vCPUs și 4GB RAM), iar traficul nu e uriaș, undeva la 12k useri activi pe zi. Totuși, la orele de vârf, când avem cam 50-60 de request-uri concomitente pe endpoint-ul de checkout, începe nebunia. În Sentry îmi apar cam 10-15 astfel de erori pe zi. Pe local? Evident că nu reușesc să o reproduc, oricât am forțat cu autocannon.

Cum arată flow-ul și ce se întâmplă de fapt

Flow-ul de business e destul de clasic. Avem o tranzacție interactivă în care facem trei operațiuni: verificăm dacă stocul e suficient, creăm comanda în sine și apoi scădem stocul produselor. Nimic SF. Doar că, din când în când, una dintre scrieri durează mai mult, iar Prisma decide să închidă tranzacția înainte ca restul operațiunilor să apuce să se execute.

Am mai pățit în trecut chestii similare din cauza unor apeluri de API extern băgate aiurea în blocul de tranzacție, dar aici am curățat tot. Sunt doar query-uri pure de DB.

Ce am încercat deja și nu a funcționat

Am luat la rând cam tot ce scrie pe forumuri și pe GitHub issues, dar fără succes:

  1. Am crescut timeout-ul tranzacției: Implicit e 5 secunde. L-am urcat la 15 secunde și chiar la 30 de secunde ca test. Singurul trade-off a fost că s-au acumulat mai multe conexiuni blocate în pool și a crescut latența generală a aplicației. Eroarea tot apare, doar că acum așteaptă mai mult până crapă.
  2. Tuning pe Connection Pool: Am crezut că e de la epuizarea conexiunilor. Am setat connection_limit=20 în URL-ul de conexiune (înainte era pe default, adică 10). PG-ul duce lejer 100 de conexiuni active, deci nu e de acolo. Utilizarea CPU pe RDS nu sare de 40%.
  3. Optimizare query-uri: Am pus indecși pe toate câmpurile folosite în clauzele where. Timpul mediu de execuție pentru un query individual e de sub 5ms.

Unde bănuiesc că e problema

Bănuiala mea e că PostgreSQL face un lock pe rândurile de stoc (folosim un update clasic) și, când vin request-uri simultane pentru același produs, se creează un blocaj de tip deadlock. Prisma, în loc să aștepte frumos la coadă sau să dea un mesaj clar de conflict, aruncă acest generic "Transaction already closed".

O altă teorie pe care am citit-o este legată de Node.js event loop lag. Dacă procesul Node e ocupat cu altceva (de exemplu, o rută de export rapoarte care rulează în paralel și mănâncă CPU pe instanța de server), Promise-ul din tranzacție nu se rezolvă la timp și Prisma consideră tranzacția expirată în engine-ul ei scris în Rust.

A mai lovit cineva problema asta în producție? Cum ați rezolvat-o? Merită să renunț complet la tranzacția interactivă din Prisma și să merg pe query-uri brute de tipul UPDATE ... WHERE ... cu decrementare directă în baza de date ca să evit lock-urile lungi?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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