- MongoDB storage: cwmp_exchanges collection with indexes (deviceId+timestamp, deviceId+sequenceNumber unique, TTL 24h) - ExchangeStoreService: CWMP method extraction, per-device queue for ordering, retry logic, message cap (5000/device) - Exchange API: GET/DELETE/SSE stream endpoints with Last-Event-ID reconnection - CwmpLoggerMiddleware: dual-write (MongoDB + legacy files) with fire-and-forget - Frontend: SequenceViewer (CPE↔ACS lifelines, arrows, auto-scroll, virtual scroll) + InspectorPanel (formatted XML, copy, syntax highlighting) - Tr069Trace page: integrated new viewer with col-6/col-6 layout - DeviceDetail page: collapsible exchange section when device is traced - 14 property-based tests, 40+ unit tests, 5 integration tests - Health check includes index verification - Config: feature flags, SSE timeouts, retry policy
15 KiB
Alerting Rules — TR-069 Exchange Viewer
Overview
Ce document décrit les règles d'alerte configurées pour le système TR-069 Exchange Viewer. Chaque règle définit un seuil critique, les conditions de déclenchement, l'impact attendu et la procédure d'escalade associée.
Les métriques sont exposées via le endpoint /api/v1/health/tr069-exchanges et les logs structurés JSON.
Alert 1: SSE Active Connections > 100
Seuil
| Métrique | Condition | Sévérité |
|---|---|---|
sse.active_connections |
> 100 connexions simultanées | Warning |
sse.active_connections |
> 200 connexions simultanées | Critical |
Description
Un nombre élevé de connexions SSE simultanées indique une charge serveur importante. Chaque connexion SSE maintient un socket HTTP ouvert et un listener EventEmitter en mémoire. Au-delà de 100 connexions, le serveur risque une dégradation de performance (latence accrue, consommation mémoire excessive).
Causes possibles
- Pic d'activité : de nombreux opérateurs tracent des devices simultanément
- Fuite de connexions : les clients ne ferment pas correctement les connexions EventSource (navigation sans cleanup)
- Reconnection storm : perte réseau transitoire provoquant la reconnexion simultanée de tous les clients
- Bug client : boucle de reconnexion infinie (EventSource retry sans backoff)
Impact
- Augmentation de la consommation mémoire du processus Node.js
- Latence accrue sur les push SSE (chaque nouveau message est dispatché à N listeners)
- Risque d'épuisement des file descriptors (limite OS)
- Dégradation possible du traitement CWMP si le thread principal est saturé
Détection
// GET /api/v1/health/tr069-exchanges
{
"sse": {
"active_connections": 142,
"reconnections_1h": 56
}
}
Logs structurés :
{ "level": "warn", "msg": "SSE connection count high", "active_connections": 142, "threshold": 100 }
Procédure de résolution
- Vérifier le nombre de connexions actives via le health check endpoint
- Identifier les devices avec le plus de connexions (logs :
sse.connectevents par deviceId) - Vérifier si des connexions zombies existent (connexions ouvertes sans activité > 5 minutes)
- Appliquer les timeouts serveur : les connexions inactives sont fermées après 5 min, les absolues après 2h
- Si le problème persiste : redémarrer le service pour forcer la fermeture de toutes les connexions SSE
Escalade
| Durée | Action |
|---|---|
| 0-5 min | Monitoring automatique, pas d'action |
| 5-15 min (Warning) | Vérifier les logs, identifier la cause |
| > 15 min (Warning) | Notifier l'équipe Dev via Slack #adam-acs-alerts |
| Immédiat (Critical > 200) | Notifier l'équipe Dev + Ops, envisager un restart |
| > 30 min (Critical) | Activer le feature flag FEATURE_SSE_ENABLED=false (fallback REST polling) |
Alert 2: MongoDB Write Latency p95 > 100ms
Seuil
| Métrique | Condition | Sévérité |
|---|---|---|
mongodb.write_latency_p95 |
> 100ms | Warning |
mongodb.write_latency_p95 |
> 500ms | Critical |
mongodb.write_latency_p99 |
> 1000ms | Critical |
Description
La latence d'écriture MongoDB au percentile 95 reflète la performance de persistance des échanges CWMP. Au-delà de 100ms (p95), les écritures commencent à s'accumuler, ce qui peut provoquer un retard dans la diffusion SSE et une pression mémoire sur le processus.
Causes possibles
- Charge MongoDB élevée (autres opérations concurrentes, compaction, journalisation)
- Index manquant ou dégradé sur la collection
cwmp_exchanges - Disque I/O saturé sur le serveur MongoDB
- Collection
cwmp_exchangestrop volumineuse (TTL cleanup en retard) - Locks MongoDB : opérations bulk ou migrations en cours
Impact
- Retard dans la diffusion SSE des nouveaux échanges (latence end-to-end augmentée)
- Activation des retries pour les messages critiques (backoff 1s, 2s, 4s)
- En cas de timeout persistant : perte potentielle de messages non-critiques (Empty, Unknown)
- Le traitement CWMP n'est jamais impacté (fire-and-forget)
Détection
Logs structurés :
{ "level": "warn", "msg": "MongoDB write slow", "collection": "cwmp_exchanges", "duration_ms": 145, "deviceId": "abc123", "cwmpMethod": "Inform" }
Métriques agrégées (via logs ou Prometheus) :
mongodb_write_latency_ms{collection="cwmp_exchanges", quantile="0.95"} 145
mongodb_write_latency_ms{collection="cwmp_exchanges", quantile="0.99"} 320
Procédure de résolution
- Vérifier l'état de MongoDB :
db.serverStatus(),db.currentOp() - Vérifier les index de
cwmp_exchanges:mongosh --eval "db.cwmp_exchanges.getIndexes()" - Vérifier la taille de la collection et le nombre de documents :
mongosh --eval "db.cwmp_exchanges.stats()" - Vérifier le TTL cleanup (voir Alert 4)
- Si les index sont manquants : recréer via la migration ou manuellement :
mongosh --eval "db.cwmp_exchanges.createIndex({deviceId:1, timestamp:1})" mongosh --eval "db.cwmp_exchanges.createIndex({deviceId:1, sequenceNumber:1}, {unique:true})" mongosh --eval "db.cwmp_exchanges.createIndex({createdAt:1}, {expireAfterSeconds:86400})" - Si la charge est transitoire : attendre la stabilisation (compaction terminée, etc.)
Escalade
| Durée | Action |
|---|---|
| 0-5 min | Monitoring automatique |
| 5-15 min (Warning) | Vérifier les index et la charge MongoDB |
| > 15 min (Warning) | Notifier l'équipe Dev |
| Immédiat (Critical > 500ms) | Notifier Dev + DBA |
| > 30 min (Critical) | Envisager FEATURE_EXCHANGE_PERSISTENCE=false pour délester MongoDB |
Alert 3: Message Loss Rate > 0.1%
Seuil
| Métrique | Condition | Sévérité |
|---|---|---|
exchange.loss_rate |
> 0.1% (1 message sur 1000) | Critical |
exchange.loss_rate |
> 1.0% (1 message sur 100) | Urgent |
Description
Le taux de perte de messages représente le pourcentage de messages CWMP capturés par le middleware mais non persistés en MongoDB (après épuisement des retries pour les critiques, ou discard immédiat pour les non-critiques). Un taux > 0.1% indique des défaillances critiques du backend de persistance.
Causes possibles
- Défaillance MongoDB : connexion perdue, timeout, disque plein
- Retry exhaustion : 3 retries échoués consécutivement pour les messages critiques
- Erreur dans le service
ExchangeStoreService(bug, exception non catchée) - Saturation de la collection (cap à 5000 messages avec suppression simultanée)
- Problème réseau entre l'application et MongoDB
Impact
- Trous dans l'historique des échanges visibles dans le Sequence Viewer
- Les opérateurs voient des séquences incomplètes, rendant le debug difficile
- Les messages critiques (Inform, SetParameterValues) perdus représentent une perte d'observabilité sur les actions CPE
Détection
Logs structurés :
{ "level": "error", "msg": "Exchange persistence failed after retries", "deviceId": "abc123", "cwmpMethod": "Inform", "retries": 3, "error": "MongoServerError: connection timeout" }
{ "level": "warn", "msg": "Non-critical exchange discarded", "deviceId": "abc123", "cwmpMethod": "Empty", "error": "MongoServerError: connection timeout" }
Calcul du taux de perte :
loss_rate = (messages_discarded / messages_captured) * 100
Procédure de résolution
- Vérifier la connectivité MongoDB : health check endpoint,
mongoshping - Vérifier les logs d'erreur pour identifier le pattern de défaillance
- Vérifier l'espace disque disponible sur le serveur MongoDB
- Vérifier que le TTL cleanup fonctionne (pas d'accumulation illimitée)
- Si MongoDB est injoignable : vérifier le réseau, le service mongod, les limites de connexions
- Si MongoDB est surchargé : identifier les opérations concurrentes, envisager un throttle
Escalade
| Durée | Action |
|---|---|
| Immédiat (> 0.1%) | Alerte critique — Notifier l'équipe Dev immédiatement |
| 0-5 min | Diagnostic rapide : MongoDB up? Index OK? Disk space? |
| 5-15 min | Notifier Dev + Ops, vérifier infrastructure MongoDB |
| > 15 min | Escalade management, envisager rollback vers fichiers |
| Immédiat (> 1.0%) | Alerte urgente — Activer FEATURE_EXCHANGE_PERSISTENCE=false, diagnostic prioritaire |
Alert 4: TTL Cleanup Lag > 1 Hour
Seuil
| Métrique | Condition | Sévérité |
|---|---|---|
exchange.ttl_cleanup_lag |
> 1 heure | Warning |
exchange.ttl_cleanup_lag |
> 6 heures | Critical |
Description
L'index TTL sur le champ createdAt (expire après 86400 secondes = 24h) est géré par un thread interne MongoDB qui s'exécute toutes les 60 secondes. Si le cleanup prend du retard, des documents expirés s'accumulent, augmentant la taille de la collection et dégradant les performances de requête.
Le "lag" est mesuré comme la différence entre le plus ancien document présent dans la collection et son heure d'expiration théorique (createdAt + 24h).
Causes possibles
- MongoDB TTL monitor surchargé (collection très volumineuse, beaucoup de documents à supprimer)
- Index TTL supprimé ou corrompu
- MongoDB en mode de maintenance (locks, compaction)
- Charge élevée sur le serveur empêchant le thread TTL de s'exécuter
- Bug dans la configuration du TTL (mauvais champ, mauvaise valeur
expireAfterSeconds)
Impact
- Croissance non contrôlée de la collection
cwmp_exchanges - Dégradation progressive des performances de lecture (scans plus larges)
- Consommation disque accrue
- Potentiel dépassement des limites de stockage
Détection
Vérification manuelle du lag :
# Trouver le document le plus ancien
mongosh --eval "
const oldest = db.cwmp_exchanges.find().sort({createdAt: 1}).limit(1).toArray()[0];
if (oldest) {
const expectedExpiry = new Date(oldest.createdAt.getTime() + 86400000);
const lag = Date.now() - expectedExpiry.getTime();
print('Oldest createdAt:', oldest.createdAt);
print('Expected expiry:', expectedExpiry);
print('Lag (ms):', Math.max(0, lag));
print('Lag (hours):', Math.max(0, lag / 3600000).toFixed(2));
} else {
print('Collection empty');
}
"
Vérification de l'index TTL :
mongosh --eval "
const indexes = db.cwmp_exchanges.getIndexes();
const ttlIndex = indexes.find(i => i.expireAfterSeconds !== undefined);
if (ttlIndex) {
print('TTL index found:', JSON.stringify(ttlIndex));
} else {
print('WARNING: No TTL index found!');
}
"
Procédure de résolution
-
Vérifier que l'index TTL existe et est correctement configuré :
mongosh --eval "db.cwmp_exchanges.getIndexes()"L'index attendu :
{ "createdAt": 1 }avecexpireAfterSeconds: 86400 -
Si l'index TTL est manquant — le recréer :
mongosh --eval "db.cwmp_exchanges.createIndex({createdAt: 1}, {expireAfterSeconds: 86400})" -
Si l'index existe mais le cleanup est en retard :
- Vérifier la charge MongoDB :
db.serverStatus().metrics.ttl - Vérifier si un grand nombre de documents doivent être supprimés d'un coup
- Envisager un nettoyage manuel des plus anciens documents :
mongosh --eval " const cutoff = new Date(Date.now() - 86400000); const result = db.cwmp_exchanges.deleteMany({createdAt: {$lt: cutoff}}); print('Deleted:', result.deletedCount); "
- Vérifier la charge MongoDB :
-
Si MongoDB est en maintenance : attendre la fin de l'opération, surveiller la reprise du TTL
Escalade
| Durée | Action |
|---|---|
| 0-1h | Monitoring automatique (TTL thread cycle = 60s, léger retard normal) |
| 1-3h (Warning) | Vérifier l'existence de l'index TTL, vérifier la charge MongoDB |
| 3-6h (Warning) | Notifier l'équipe Dev, envisager un nettoyage manuel |
| > 6h (Critical) | Notifier Dev + DBA, nettoyage manuel obligatoire, diagnostic approfondi |
| > 24h | Escalade management — la collection grandit sans limite |
Résumé des alertes
| # | Alerte | Seuil Warning | Seuil Critical | Métrique |
|---|---|---|---|---|
| 1 | SSE Active Connections | > 100 | > 200 | sse.active_connections |
| 2 | MongoDB Write Latency p95 | > 100ms | > 500ms | mongodb.write_latency_p95 |
| 3 | Message Loss Rate | > 0.1% | > 1.0% | exchange.loss_rate |
| 4 | TTL Cleanup Lag | > 1 heure | > 6 heures | exchange.ttl_cleanup_lag |
Configuration des alertes
Variables d'environnement
| Variable | Défaut | Description |
|---|---|---|
ALERT_SSE_CONNECTIONS_WARNING |
100 |
Seuil warning connexions SSE |
ALERT_SSE_CONNECTIONS_CRITICAL |
200 |
Seuil critical connexions SSE |
ALERT_MONGO_LATENCY_P95_WARNING_MS |
100 |
Seuil warning latence MongoDB p95 (ms) |
ALERT_MONGO_LATENCY_P95_CRITICAL_MS |
500 |
Seuil critical latence MongoDB p95 (ms) |
ALERT_LOSS_RATE_CRITICAL_PCT |
0.1 |
Seuil critical taux de perte (%) |
ALERT_TTL_LAG_WARNING_HOURS |
1 |
Seuil warning lag TTL (heures) |
ALERT_TTL_LAG_CRITICAL_HOURS |
6 |
Seuil critical lag TTL (heures) |
Intégration avec les systèmes d'alerte
Les alertes sont déclenchées via les logs structurés JSON, compatibles avec :
- ELK Stack : index pattern
adam-acs-*, filtres surlevel: "error"oulevel: "warn"+ tags spécifiques - Datadog : log-based monitors avec conditions sur les champs JSON
- Prometheus/Grafana : si le endpoint
/metricsest exposé, utiliser les alerting rules Prometheus
Exemple de règle Prometheus :
groups:
- name: tr069-exchange-viewer
rules:
- alert: HighSSEConnections
expr: adam_acs_sse_active_connections > 100
for: 5m
labels:
severity: warning
annotations:
summary: "High SSE connection count"
description: "{{ $value }} SSE connections active (threshold: 100)"
- alert: MongoWriteLatencyHigh
expr: histogram_quantile(0.95, adam_acs_mongodb_write_duration_seconds_bucket{collection="cwmp_exchanges"}) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "MongoDB write latency p95 > 100ms"
description: "p95 latency: {{ $value }}s for cwmp_exchanges writes"
- alert: ExchangeMessageLoss
expr: rate(adam_acs_exchange_messages_lost_total[5m]) / rate(adam_acs_exchange_messages_captured_total[5m]) > 0.001
for: 1m
labels:
severity: critical
annotations:
summary: "Message loss rate exceeds 0.1%"
description: "{{ $value | humanizePercentage }} messages lost in last 5 minutes"
- alert: TTLCleanupLag
expr: adam_acs_exchange_ttl_lag_seconds > 3600
for: 10m
labels:
severity: warning
annotations:
summary: "TTL cleanup lag exceeds 1 hour"
description: "Oldest expired document is {{ $value | humanizeDuration }} past its TTL"
Contacts d'escalade
| Niveau | Contact | Canal |
|---|---|---|
| L1 — Warning | Équipe Dev adam-acs | Slack #adam-acs-alerts |
| L2 — Critical | Lead Dev + Ops | Slack #adam-acs-alerts + mention @oncall |
| L3 — Urgent/Prolongé | Management technique | Escalade téléphonique |
| DBA (MongoDB) | Équipe DBA | Slack #dba-support |