- 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
397 lines
15 KiB
Markdown
397 lines
15 KiB
Markdown
# 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
|
|
|
|
```json
|
|
// GET /api/v1/health/tr069-exchanges
|
|
{
|
|
"sse": {
|
|
"active_connections": 142,
|
|
"reconnections_1h": 56
|
|
}
|
|
}
|
|
```
|
|
|
|
Logs structurés :
|
|
```json
|
|
{ "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 :
|
|
```json
|
|
{ "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` :
|
|
```bash
|
|
mongosh --eval "db.cwmp_exchanges.getIndexes()"
|
|
```
|
|
3. **Vérifier** la taille de la collection et le nombre de documents :
|
|
```bash
|
|
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 :
|
|
```bash
|
|
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 :
|
|
```json
|
|
{ "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 :
|
|
```bash
|
|
# 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 :
|
|
```bash
|
|
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é :
|
|
```bash
|
|
mongosh --eval "db.cwmp_exchanges.getIndexes()"
|
|
```
|
|
L'index attendu : `{ "createdAt": 1 }` avec `expireAfterSeconds: 86400`
|
|
|
|
2. **Si l'index TTL est manquant** — le recréer :
|
|
```bash
|
|
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 :
|
|
```bash
|
|
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 :
|
|
```yaml
|
|
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 |
|