const express = require('express');
const app = express();
let dbConnected = true; // Actualizat de driverul de DB
let lastEventLoopTick = Date.now();
// Monitorizăm dacă event loop-ul este blocat
setInterval(() => {
lastEventLoopTick = Date.now();
}, 1000);
app.get('/healthz', async (req, res) => {
const lag = Date.now() - lastEventLoopTick;
// Dacă event loop-ul e blocat de mai mult de 5 secunde
if (lag > 5000) {
return res.status(503).send({ status: 'unhealthy', reason: 'Event loop lag too high' });
}
if (!dbConnected) {
return res.status(503).send({ status: 'unhealthy', reason: 'DB connection down' });
}
res.send({ status: 'healthy', lag });
});Am trecut cu toții prin asta: te uiți în pm2 list, vezi frumos verde, status „online”, dar clienții urlă pe Slack că site-ul e jos. Am pățit-o acum doi ani la un proiect cu vreo 12k useri activi pe oră. PM2 zicea că totul e brici, dar event loop-ul era atât de blocat de un query prost încât Node nu mai procesa nicio cerere HTTP.
De ce ne minte PM2?
PM2 face o singură chestie de bază excelent: se asigură că procesul tău de Node rulează în sistemul de operare. Dacă procesul are un PID activ, pentru PM2 înseamnă că e „online”.
Dar în realitate, aplicația ta poate fi un zombie. Poate că baza de date a picat, pool-ul de conexiuni e epuizat, sau ai un memory leak masiv care a înghețat event loop-ul înainte ca OOM (Out Of Memory) killer-ul să apuce să trimită semnalul de kill. În toate aceste cazuri, procesul există, deci PM2 nu va da restart automat.
Soluția: Healthcheck activ, nu pasiv
Ca să dormi liniștit noaptea, ai nevoie de un endpoint de /healthz în aplicație care să testeze pe bune dacă sistemul e funcțional.
Dar atenție la un trade-off major aici. Dacă pui load balancer-ul (sau un script local) să apeleze endpoint-ul ăsta la fiecare 5 secunde și tu interoghezi baza de date de fiecare dată direct, s-ar putea să-ți încarci baza degeaba. Am pățit să avem 5% din CPU-ul bazei de date consumat doar de ping-urile de healthcheck de la cele 4 instanțe de microservicii.
Am rezolvat problema asta cache-uind rezultatul verificărilor grele (bază de date, Redis) timp de 15 secunde. Endpoint-ul returnează instant statusul salvat în memorie, evitând astfel un DDOS auto-provocat.
Cum legăm asta de monitorizare?
Dacă rulezi în AWS sau în spatele unui Nginx local, pui load balancer-ul să facă polling pe /healthz. Dacă pică de 3 ori la rând, scoate instanța din uz.
Pentru un VPS simplu unde PM2 rulează „pe curat”, poți folosi un script de cron care dă un simplu curl local o dată pe minut. Dacă primești status non-200 sau timeout, rulezi un pm2 reload <app_name>. E o soluție puțin brutală, dar pe noi ne-a salvat de la downtime de zeci de minute în miezul nopții.
Voi cum gestionați procesele zombie în PM2? Vă bazați doar pe restart-delay sau aveți un watchdog extern?