async function getMetrics() {
const metrics = await prisma.$metrics.json();
const queries = metrics.counters.find(c => c.name === 'prisma_client_queries_total');
return queries?.value || 0;
}
// Exemplu de utilizare în dev
const start = await getMetrics();
const data = await prisma.post.findMany({
take: 20,
include: { author: true } // Rezolvă N+1
});
const end = await getMetrics();
console.log(`Interogări executate: ${end - start}`);Cum m-a păcălit Prisma prima dată
M-am obișnuit cu ORM-urile care promit că "rezolvă ele tot". Prisma are un stil foarte curat de a scrie cod, dar fix asta te face să uiți ce se întâmplă sub capotă. La un proiect cu vreo 12.000 de înregistrări, am observat că timpul de răspuns pe un endpoint de listing sărise de 800ms. Mi s-a părut enorm pentru un query banal.
Problema clasică: iteram printr-o listă de entități și, pentru fiecare, mai făceam un apel separat să iau niște relații. În log-uri totul părea ok dacă te uitai repede, dar la o analiză atentă, baza de date primea o rafală de query-uri scurte. Am pățit-o la un dashboard unde, pentru 50 de postări, făceam de fapt 51 de query-uri la bază pentru că trăgeam autorii într-un map() după fetch-ul principal.
Detectarea cu $metrics: nu e doar pentru grafice
Mulți cred că $metrics e util doar dacă ai Grafana sau Prometheus. Total greșit. Eu am început să-l folosesc în development ca un fel de unit test pentru performanță. Poți să scoți numărul total de query-uri executate într-un bloc de cod foarte simplu.
La un proiect recent cu 8k useri activi, am făcut un mic utilitar care loga prisma_client_queries_total înainte și după un request suspect. Surpriză: pentru 20 de rânduri afișate, aveam 21 de query-uri. Asta e definiția de manual pentru N+1. Dacă ai 100 de itemi, ai 101 query-uri. Postgres-ul rezistă, dar latența de rețea te omoară încet și sigur. Am economisit vreo 300ms pe request doar fiind atent la chestia asta.
Rezolvarea prin include și trade-off-urile ei
Soluția evidentă e să folosești include. Prisma e destul de deșteaptă să facă un JOIN sau un query cu IN, depinzând de versiune și de cum e structurată relația. Totuși, am învățat un lucru important: include nu e mereu "free".
Dacă ai o tabelă cu 50 de coloane și tu ai nevoie doar de un nume din relația respectivă, include o să-ți tragă tot obiectul în memorie. Aici intervine select-ul imbricat. Am economisit cam 30% din RAM-ul consumat de procesul de Node.js pe un worker de raportare doar trecând de la include: { user: true } la un select specific. E mai mult de scris, dar la volum mare se simte.
Un trade-off sincer? Uneori, dacă relațiile sunt foarte complexe și tabelele sunt gigantice, un JOIN generat de ORM poate fi mai lent decât două query-uri separate bine indexate. Prisma încearcă să optimizeze asta prin Query Engine, dar nu strică să verifici planul de execuție din când în când.
Cum verifici rapid în local
Nu trebuie să instalezi nimic extra. Poți să arunci un console.table pe ce returnează $metrics într-un middleware de development sau chiar într-un test de integrare. Dacă vezi că numărul de query-uri crește liniar cu numărul de elemente din listă, ai o problemă de design.
Eu am ajuns să pun o alertă simplă în log-urile de dev dacă un singur request HTTP declanșează mai mult de 10 query-uri la bază. E o limită arbitrară, dar m-a salvat de multe ori de la a împinge cod ineficient în producție. Prisma e un tool excelent, dar abstractizarea vine cu un cost pe care trebuie să-l monitorizezi activ.
Voi cum monitorizați numărul de query-uri în Prisma? Vă bazați pe log-urile de query brute sau folosiți ceva mai structurat în pipeline-ul de CI/CD?