@kozminski/dbhealth (1.0.1)
Installation
@kozminski:registry=https://git.kozminski.net.pl/api/packages/kozminski/npm/npm install @kozminski/dbhealth@1.0.1"@kozminski/dbhealth": "1.0.1"About this package
@kozminski/dbhealth
Aktywny monitor kondycji połączenia z bazą dla usług backendu. Daje handler /health
zgodny z express oraz callback pozwalający usłudze naprawić się samodzielnie
(np. process.exit(1), gdy kontener ma restart: unless-stopped).
Po co to jest
11.09.2026 o 15:30 w replica secie solis_set odbyły się wybory nowego węzła głównego.
Sterownik Mongo wewnątrz działającego procesu orders-service nie odbudował topologii —
przez dwie godziny każde zapytanie kończyło się MongoServerSelectionError, a mimo to
HEALTHCHECK kontenera (odpytujący GET /) raportował usługę jako sprawną, więc nginx
dalej kierował tam ruch. Naprawił to dopiero ręczny restart kontenera.
Dlatego monitor nie sprawdza readyState — przy tej awarii połączenie było formalnie
otwarte, tylko bezużyteczne, i w logach nie było żadnego zdarzenia rozłączenia. Zamiast tego
wykonuje realne zapytanie (ping) z własnym limitem czasu, co wykrywa zarówno zerwane
połączenie, jak i połączenie żywe, lecz niezdolne wybrać serwera.
Paczka nie zależy od mongoose ani od express (działa z mongoose 5.x, 6.x i 7.x) i nie ma żadnych zależności.
Użycie
const mongoose = require('mongoose');
const { createMonitor } = require('@kozminski/dbhealth');
const logger = require('./src/Logger.js');
const monitor = createMonitor({
connection: mongoose.connection,
logger,
onUnhealthy({ downMs, error }) {
logger.error('orders.main.db_health.exiting', { downMs, error: error.message });
process.exit(1); // Docker (unless-stopped) podniesie kontener z czystym stanem
}
});
module.exports = monitor;
Trasę montujemy przed middleware'ami autoryzacji, inaczej healthcheck dostanie 401:
app.get('/health', require('../src/DbHealth.js').handler);
W Dockerfile:
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
CMD node -e "fetch('http://127.0.0.1:'+(process.env.APP_PORT||3000)+'/health').then(r=>process.exit(r.status===200?0:1)).catch(()=>process.exit(1))"
Opcje
| Opcja | Domyślnie | Opis |
|---|---|---|
connection |
— | Wymagane. Połączenie mongoose (mongoose.connection). |
logger |
brak logów | Obiekt { info, warn, error }, np. winston. |
probeIntervalMs |
15000 |
Co ile milisekund sondować bazę. |
probeTimeoutMs |
5000 |
Limit czasu jednej sondy. Bez niego ping czekałby 30 s (mongoose 6) albo w nieskończoność (mongoose 5). |
gracePeriodMs |
90000 |
Po ilu ms nieprzerwanej awarii wołamy onUnhealthy. |
onUnhealthy |
brak | Callback ({ downMs, error }) wołany dokładnie raz na awarię (kolejny raz dopiero po odzyskaniu połączenia). |
API monitora
monitor.handler—(req, res), handler zgodny z express (używa wyłącznie API node'owegores). Zwraca200 { status: 'ok', lastOkAt }albo503 { status: 'db-unavailable', lastOkAt, downMs, error }.monitor.isHealthy()—true/false.monitor.stop()— zatrzymuje sondę.
Zachowanie
- Pierwsza sonda odpala się od razu przy tworzeniu monitora, a
lastOkAtstartuje zDate.now(), dzięki czemu okno karencji obejmuje też pierwsze łączenie po starcie usługi. - Brak
connection.db(jeszcze nie połączono) liczy się jak nieudana sonda. - Timer interwału jest
unref(), więc monitor nie trzyma procesu przy życiu. - Logi:
dbhealth.probe_failed(warn) przy każdej nieudanej sondzie,dbhealth.recovered(info) przy powrocie do zdrowia.