eduardweb.
PerformanceIntermediar#performance#prisma#postgresql#backend

Cum prinzi și rezolvi interogările N+1 în Prisma folosind $metrics

De Andreea Crăciun, 30 mai 2026 · 4 vizualizări · 3 like-uri

Postat 30 mai 2026
typescript
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()

async function handleRequest() {
  // Resetezi metricile la începutul request-ului dacă e necesar
  
  const posts = await prisma.post.findMany({
    include: {
      author: true, // Rezolvă N+1 pentru autori dintr-un singur join/batch
    }
  })

  const metrics = await prisma.$metrics.json()
  const queryCount = metrics.queries.find(q => q.key === 'prisma_client_queries_total')
  
  console.log(`[Request Performance] Query count: ${queryCount?.value}`)
}

Săptămâna trecută am optimizat un endpoint pe un proiect cu vreo 15.000 de utilizatori activi. Se mișca îngrozitor, cam 4 secunde pentru un simplu feed de postări cu comentarii și autori. Clasica problemă de N+1, mascată frumos sub abstractizările din Prisma. Hai să vedem cum o dibuiești rapid și cum o repari fără să rescrii totul în SQL chior.

Cum apare N+1 în Prisma?

Deși Prisma are un query engine destul de deștept care face batching (un fel de DataLoader intern), acesta funcționează automat doar în anumite scenarii. De exemplu, când folosești findUnique în bucle separate, Prisma le grupează în spate într-un singur IN.

Problema apare când ai un flux unde iei o listă de postări și apoi, într-un map în JavaScript, faci câte un findMany pentru comentariile fiecăreia. Ai dat de dracu'. În loc de 2 interogări curate, baza ta de date va fi bombardată cu 101 interogări pentru 100 de postări.

Detectarea rapidă cu $metrics

Să pui log: ['query'] în consolă e ok pentru development local, dar devine zgomotos la proiecte mari. O metodă mult mai curată este să folosești API-ul de $metrics oferit de Prisma pentru a măsura exact numărul de query-uri rulate pe parcursul unui request.

Am implementat un middleware simplu pe serverul de staging. Dacă numărul de query-uri rulate pentru o singură rută depășește pragul de 15, trimitem o alertă pe Slack. În codul de mai jos vezi cum poți extrage această valoare programatic.

Rezolvarea prin include smart și trade-off-uri

Soluția clasică este să folosești include sau select direct în interogarea principală. Prisma va genera un query mult mai eficient (de obicei un singur query sau un batch foarte mic de JOIN-uri).

Dar atenție mare la trade-off-uri. Dacă faci include pe relații adânci și colectezi postări, comentarii, autori, plus istoricul autorilor, Prisma va aduce toate datele în memorie și le va asambla în JS. Am pățit asta pe un server mic, cu 2GB RAM, unde un include nesăbuit pe o tabelă cu mii de înregistrări a dus la crash-uri de tip Out of Memory (OOM).

În plus, include fără limitare aduce toate coloanele din tabelele relaționate. De cele mai multe ori ai nevoie doar de id și name, nu de tot obiectul de profile. Folosește select imbricat în loc de include generalizat pentru a reduce payload-ul transferat între baza de date și aplicație.

Pentru relații foarte mari, uneori e mai sănătos să faci manual două interogări separate cu in: ids și să le asamblezi singur în cod. Câștigi viteză și nu blochezi memoria serverului.

Voi cum monitorizați query-urile astea ascunse în producție? Folosiți APM-uri dedicate sau vă bazați pe logurile din baza de date?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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