Files
adam-acs/docs/monitoring/alerting-rules.md
T
Kevin L'Hereec 0c83a4ccb2 feat: TR-069 Exchange Viewer - structured MongoDB storage with SSE streaming and sequence diagram UI
- 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
2026-06-03 15:52:44 +02:00

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

  1. Vérifier le nombre de connexions actives via le health check endpoint
  2. Identifier les devices avec le plus de connexions (logs : sse.connect events par deviceId)
  3. Vérifier si des connexions zombies existent (connexions ouvertes sans activité > 5 minutes)
  4. Appliquer les timeouts serveur : les connexions inactives sont fermées après 5 min, les absolues après 2h
  5. 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_exchanges trop 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

  1. Vérifier l'état de MongoDB : db.serverStatus(), db.currentOp()
  2. Vérifier les index de cwmp_exchanges :
    mongosh --eval "db.cwmp_exchanges.getIndexes()"
    
  3. Vérifier la taille de la collection et le nombre de documents :
    mongosh --eval "db.cwmp_exchanges.stats()"
    
  4. Vérifier le TTL cleanup (voir Alert 4)
  5. 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})"
    
  6. 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

  1. Vérifier la connectivité MongoDB : health check endpoint, mongosh ping
  2. Vérifier les logs d'erreur pour identifier le pattern de défaillance
  3. Vérifier l'espace disque disponible sur le serveur MongoDB
  4. Vérifier que le TTL cleanup fonctionne (pas d'accumulation illimitée)
  5. Si MongoDB est injoignable : vérifier le réseau, le service mongod, les limites de connexions
  6. 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

  1. Vérifier que l'index TTL existe et est correctement configuré :

    mongosh --eval "db.cwmp_exchanges.getIndexes()"
    

    L'index attendu : { "createdAt": 1 } avec expireAfterSeconds: 86400

  2. Si l'index TTL est manquant — le recréer :

    mongosh --eval "db.cwmp_exchanges.createIndex({createdAt: 1}, {expireAfterSeconds: 86400})"
    
  3. 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);
      "
      
  4. 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 sur level: "error" ou level: "warn" + tags spécifiques
  • Datadog : log-based monitors avec conditions sur les champs JSON
  • Prometheus/Grafana : si le endpoint /metrics est 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