- 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
593 lines
18 KiB
Markdown
593 lines
18 KiB
Markdown
# Guide de migration : TR-069 Exchange Viewer
|
|
|
|
## Résumé
|
|
|
|
Ce guide décrit la migration du système de trace TR-069 depuis le stockage fichier (logs `.log` rotatifs) vers le stockage structuré MongoDB (`cwmp_exchanges`). La migration suit une approche en 3 phases pour garantir zéro perte de données et un rollback possible à chaque étape.
|
|
|
|
| Phase | Durée | Description | Risque |
|
|
|-------|-------|-------------|--------|
|
|
| Phase 1 — Dual-Write | Semaines 1-2 | Écriture simultanée fichiers + MongoDB | Faible |
|
|
| Phase 2 — Read from MongoDB | Semaines 3-4 | API lit depuis MongoDB, fichiers en backup | Moyen |
|
|
| Phase 3 — Deprecate Files | Semaine 5+ | Suppression du système fichier | Irréversible |
|
|
|
|
---
|
|
|
|
## Prérequis
|
|
|
|
### Infrastructure
|
|
|
|
- MongoDB 5.0+ (pas besoin de replica set — le système utilise un EventEmitter in-process, pas de change streams)
|
|
- Espace disque MongoDB : prévoir ~500 Mo pour 10 000 devices actifs avec 5 000 messages max chacun
|
|
- L'application adam-acs doit être redémarrée après la migration de base
|
|
|
|
### Variables d'environnement
|
|
|
|
```bash
|
|
# Feature flags (déjà présents dans config/exchange_store.ts)
|
|
FEATURE_EXCHANGE_PERSISTENCE=true # Active les écritures MongoDB
|
|
FEATURE_SSE_ENABLED=true # Active le streaming SSE
|
|
FEATURE_EXCHANGE_API_SOURCE=mongodb # Source des données API (mongodb | file)
|
|
|
|
# Configuration optionnelle
|
|
EXCHANGE_STORE_MAX_MESSAGES=5000 # Cap messages par device
|
|
EXCHANGE_STORE_TTL_SECONDS=86400 # TTL 24h
|
|
EXCHANGE_STORE_READ_TIMEOUT_MS=5000 # Timeout requêtes API
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 1 : Dual-Write (Semaines 1-2)
|
|
|
|
### Objectif
|
|
|
|
Écrire simultanément dans les deux systèmes (fichiers ET MongoDB) pour valider la fiabilité du nouveau système sans risque de perte.
|
|
|
|
### 1.1 Exécuter la migration MongoDB
|
|
|
|
La migration crée la collection `cwmp_exchanges` et ses index :
|
|
|
|
```bash
|
|
# Exécuter la migration
|
|
node ace migration:run --connection=mongodb
|
|
```
|
|
|
|
Cela exécute `database/mongo_migrations/011_create_cwmp_exchanges_collection.ts` qui crée :
|
|
- Collection `cwmp_exchanges` avec 3 index :
|
|
- `{ deviceId: 1, timestamp: 1 }` — requêtes par device
|
|
- `{ deviceId: 1, sequenceNumber: 1 }` (unique) — numérotation séquentielle et reconnexion SSE
|
|
- `{ createdAt: 1 }` (TTL: 86400s) — nettoyage automatique après 24h
|
|
- Collection `cwmp_exchange_counters` — compteurs atomiques de séquence
|
|
|
|
### 1.2 Vérifier les index
|
|
|
|
```bash
|
|
# Via mongosh
|
|
mongosh --eval "
|
|
db.cwmp_exchanges.getIndexes().forEach(idx => {
|
|
print(idx.name + ': ' + JSON.stringify(idx.key))
|
|
})
|
|
"
|
|
```
|
|
|
|
Résultat attendu :
|
|
```
|
|
_id_: {"_id":1}
|
|
idx_deviceId_timestamp: {"deviceId":1,"timestamp":1}
|
|
idx_deviceId_sequenceNumber: {"deviceId":1,"sequenceNumber":1}
|
|
idx_createdAt_ttl: {"createdAt":1}
|
|
```
|
|
|
|
### 1.3 Activer le dual-write
|
|
|
|
Le dual-write est déjà implémenté dans `CwmpLoggerMiddleware`. Vérifier les feature flags :
|
|
|
|
```bash
|
|
# .env — Phase 1
|
|
FEATURE_EXCHANGE_PERSISTENCE=true
|
|
FEATURE_SSE_ENABLED=true
|
|
FEATURE_EXCHANGE_API_SOURCE=file # GARDER 'file' en Phase 1
|
|
```
|
|
|
|
Le middleware écrit dans les deux systèmes :
|
|
|
|
```typescript
|
|
// app/middleware/cwmp_logger_middleware.ts (extrait)
|
|
if (isTraced && deviceId) {
|
|
// Nouveau système : MongoDB (fire-and-forget)
|
|
exchangeStore.persistExchange({ deviceId, direction, sessionId, rawXml })
|
|
.catch(() => {})
|
|
|
|
// Ancien système : fichiers (gardé pour rollback)
|
|
traceService.writeExchange(deviceId, direction, sessionId, prettyXml)
|
|
}
|
|
```
|
|
|
|
### 1.4 Redémarrer l'application
|
|
|
|
```bash
|
|
docker compose build app
|
|
docker compose up -d app
|
|
```
|
|
|
|
### 1.5 Vérifier le fonctionnement
|
|
|
|
Activer le trace sur un device et déclencher un Inform :
|
|
|
|
```bash
|
|
# Vérifier que MongoDB reçoit les données
|
|
mongosh --eval "db.cwmp_exchanges.countDocuments({})"
|
|
|
|
# Vérifier les derniers échanges
|
|
mongosh --eval "
|
|
db.cwmp_exchanges.find({}).sort({timestamp: -1}).limit(3).forEach(doc => {
|
|
print(doc.deviceId + ' | ' + doc.direction + ' | ' + doc.cwmpMethod)
|
|
})
|
|
"
|
|
```
|
|
|
|
### 1.6 Vérification de cohérence (Dual-Write)
|
|
|
|
Pendant la Phase 1, vérifier régulièrement que les deux systèmes restent synchronisés :
|
|
|
|
#### Script de comparaison
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# scripts/verify_dual_write.sh
|
|
# Compare le nombre d'échanges entre fichiers et MongoDB pour chaque device tracé
|
|
|
|
DEVICE_ID=$1
|
|
|
|
if [ -z "$DEVICE_ID" ]; then
|
|
echo "Usage: $0 <deviceId>"
|
|
exit 1
|
|
fi
|
|
|
|
# Compteur fichier (nombre de séparateurs '═' = nombre d'échanges)
|
|
FILE_COUNT=$(grep -c '═════' "logs/tr069/${DEVICE_ID}.log" 2>/dev/null || echo "0")
|
|
|
|
# Compteur MongoDB
|
|
MONGO_COUNT=$(mongosh --quiet --eval "db.cwmp_exchanges.countDocuments({deviceId: '${DEVICE_ID}'})")
|
|
|
|
echo "Device: ${DEVICE_ID}"
|
|
echo " Fichiers : ${FILE_COUNT} échanges"
|
|
echo " MongoDB : ${MONGO_COUNT} échanges"
|
|
|
|
if [ "$FILE_COUNT" != "$MONGO_COUNT" ]; then
|
|
DIFF=$((FILE_COUNT - MONGO_COUNT))
|
|
echo " ⚠️ Écart : ${DIFF} messages"
|
|
if [ "${DIFF#-}" -gt $((FILE_COUNT / 20)) ]; then
|
|
echo " 🚨 Écart > 5% — Investigation requise !"
|
|
fi
|
|
else
|
|
echo " ✅ Cohérent"
|
|
fi
|
|
```
|
|
|
|
#### Critères d'acceptation Phase 1
|
|
|
|
| Critère | Seuil | Action si dépassé |
|
|
|---------|-------|-------------------|
|
|
| Écart fichier/MongoDB | < 5% | Vérifier les logs d'erreur ExchangeStore |
|
|
| Erreurs de persistance | < 0.1% | Vérifier la connectivité MongoDB |
|
|
| Latence MongoDB p95 | < 100ms | Vérifier la charge et les index |
|
|
| Messages perdus (critiques) | 0 après retries | Vérifier les logs de retry exhaustion |
|
|
|
|
#### Durée recommandée
|
|
|
|
Maintenir le dual-write pendant **minimum 1 semaine** avec des vérifications quotidiennes. Passer en Phase 2 uniquement si les critères ci-dessus sont respectés de manière constante.
|
|
|
|
---
|
|
|
|
## Phase 2 : Read from MongoDB (Semaines 3-4)
|
|
|
|
### Objectif
|
|
|
|
Basculer la lecture des données vers MongoDB tout en gardant les fichiers en écriture backup.
|
|
|
|
### 2.1 Changer la source de lecture API
|
|
|
|
```bash
|
|
# .env — Phase 2
|
|
FEATURE_EXCHANGE_PERSISTENCE=true
|
|
FEATURE_SSE_ENABLED=true
|
|
FEATURE_EXCHANGE_API_SOURCE=mongodb # ← Changer de 'file' à 'mongodb'
|
|
```
|
|
|
|
### 2.2 Redémarrer l'application
|
|
|
|
```bash
|
|
docker compose restart app
|
|
```
|
|
|
|
### 2.3 Vérifier le fonctionnement
|
|
|
|
```bash
|
|
# Vérifier que l'API retourne des données depuis MongoDB
|
|
curl -s "http://localhost:8080/api/v1/tr069-trace/<DEVICE_ID>/exchanges?limit=5" | \
|
|
python3 -c "
|
|
import sys, json
|
|
data = json.load(sys.stdin)
|
|
print(f\"Source: MongoDB\")
|
|
print(f\"Exchanges: {len(data.get('exchanges', []))} messages\")
|
|
print(f\"Metadata: {data.get('metadata', {})}\")
|
|
"
|
|
```
|
|
|
|
### 2.4 Vérifier le SSE
|
|
|
|
```bash
|
|
# Ouvrir une connexion SSE (ctrl+C pour arrêter)
|
|
curl -N -H "Accept: text/event-stream" \
|
|
"http://localhost:8080/api/v1/tr069-trace/<DEVICE_ID>/exchanges/stream"
|
|
```
|
|
|
|
Déclencher un Inform sur le device tracé et vérifier qu'un événement SSE arrive en temps réel.
|
|
|
|
### 2.5 Vérifier le fallback
|
|
|
|
Si MongoDB est indisponible, le frontend doit retomber sur les fichiers :
|
|
|
|
```typescript
|
|
// Logique dans DeviceDetail.vue
|
|
const exchanges = await api.getExchanges(deviceId)
|
|
.catch(() => legacyService.readExchanges(deviceId))
|
|
```
|
|
|
|
### 2.6 Critères de passage Phase 2 → Phase 3
|
|
|
|
| Critère | Seuil | Vérifié |
|
|
|---------|-------|---------|
|
|
| API MongoDB fonctionne sans erreur | 48h continues | ☐ |
|
|
| SSE pousse les événements en < 500ms | p95 vérifié | ☐ |
|
|
| Aucune perte de message critique | 0 sur 48h | ☐ |
|
|
| Reconnexion SSE (Last-Event-ID) valide | Testé manuellement | ☐ |
|
|
| Purge (DELETE) fonctionne | 204 retourné | ☐ |
|
|
|
|
---
|
|
|
|
## Phase 3 : Deprecate Old System (Semaine 5+)
|
|
|
|
### Objectif
|
|
|
|
Supprimer le système fichier une fois que MongoDB est validé en production.
|
|
|
|
### 3.1 Supprimer le dual-write du middleware
|
|
|
|
Dans `app/middleware/cwmp_logger_middleware.ts`, retirer les appels au système fichier :
|
|
|
|
```typescript
|
|
// AVANT (dual-write)
|
|
if (isTraced && deviceId) {
|
|
exchangeStore.persistExchange({ deviceId, direction, sessionId, rawXml }).catch(() => {})
|
|
traceService.writeExchange(deviceId, direction, sessionId, prettyXml) // ← SUPPRIMER
|
|
}
|
|
|
|
// APRÈS (MongoDB uniquement)
|
|
if (isTraced && deviceId) {
|
|
exchangeStore.persistExchange({ deviceId, direction, sessionId, rawXml }).catch(() => {})
|
|
}
|
|
```
|
|
|
|
### 3.2 Supprimer les dépendances obsolètes
|
|
|
|
1. Retirer l'import `xml-formatter` du middleware (le formatage n'est plus nécessaire côté persistance)
|
|
2. Supprimer les appels à `Tr069TraceService.writeExchange()` dans le middleware
|
|
3. **NE PAS** supprimer `Tr069TraceService` lui-même — il est encore utilisé pour gérer l'état trace (start/stop)
|
|
|
|
### 3.3 Nettoyer les fichiers de logs
|
|
|
|
```bash
|
|
# Archiver les anciens logs avant suppression
|
|
tar -czf logs/tr069-archive-$(date +%Y%m%d).tar.gz logs/tr069/
|
|
rm -rf logs/tr069/*.log*
|
|
```
|
|
|
|
### 3.4 Mise à jour de la configuration
|
|
|
|
```bash
|
|
# .env — Phase 3 (état final)
|
|
FEATURE_EXCHANGE_PERSISTENCE=true
|
|
FEATURE_SSE_ENABLED=true
|
|
FEATURE_EXCHANGE_API_SOURCE=mongodb
|
|
```
|
|
|
|
### 3.5 Vérification finale
|
|
|
|
```bash
|
|
# Vérifier qu'aucun fichier .log n'est créé
|
|
ls -la logs/tr069/
|
|
|
|
# Vérifier que le système fonctionne uniquement via MongoDB
|
|
curl -s "http://localhost:8080/api/v1/health/tr069-exchanges" | python3 -m json.tool
|
|
```
|
|
|
|
---
|
|
|
|
## Procédures de Rollback
|
|
|
|
### Rollback Phase 1 → Système fichier seul
|
|
|
|
**Situation :** MongoDB pose problème, il faut revenir au système fichier uniquement.
|
|
|
|
```bash
|
|
# 1. Désactiver la persistance MongoDB
|
|
FEATURE_EXCHANGE_PERSISTENCE=false
|
|
|
|
# 2. Redémarrer
|
|
docker compose restart app
|
|
|
|
# 3. Vérifier que les fichiers continuent d'être écrits
|
|
ls -la logs/tr069/
|
|
```
|
|
|
|
**Impact :** Les données déjà en MongoDB restent accessibles mais ne sont plus alimentées. Le SSE ne fonctionne plus. L'UI retombe sur les fichiers.
|
|
|
|
**Temps de rollback :** < 2 minutes (changement d'env + restart)
|
|
|
|
### Rollback Phase 2 → Phase 1 (Dual-Write)
|
|
|
|
**Situation :** L'API MongoDB a des problèmes, revenir au dual-write avec lecture fichier.
|
|
|
|
```bash
|
|
# 1. Changer la source API
|
|
FEATURE_EXCHANGE_API_SOURCE=file
|
|
|
|
# 2. Redémarrer
|
|
docker compose restart app
|
|
```
|
|
|
|
**Impact :** L'API lit les fichiers au lieu de MongoDB. Le dual-write continue. Aucune perte de données.
|
|
|
|
**Temps de rollback :** < 2 minutes
|
|
|
|
### Rollback Phase 3 → Phase 2
|
|
|
|
**Situation :** Après suppression du dual-write, MongoDB pose problème.
|
|
|
|
**⚠️ ATTENTION : Ce rollback nécessite un redéploiement du code.**
|
|
|
|
1. Revert du commit qui a supprimé le dual-write dans `CwmpLoggerMiddleware`
|
|
2. Redéployer l'application
|
|
3. Vérifier que les fichiers sont à nouveau écrits
|
|
|
|
```bash
|
|
# Identifier le commit de suppression du dual-write
|
|
git log --oneline -- app/middleware/cwmp_logger_middleware.ts
|
|
|
|
# Revert
|
|
git revert <commit-hash>
|
|
docker compose build app
|
|
docker compose up -d app
|
|
```
|
|
|
|
**Impact :** Nécessite un redéploiement (~5 minutes). Les échanges entre la suppression et le rollback ne sont PAS dans les fichiers.
|
|
|
|
**Temps de rollback :** 5-10 minutes
|
|
|
|
### Rollback SSE uniquement
|
|
|
|
**Situation :** Le SSE cause des problèmes (connexions, mémoire) mais MongoDB est stable.
|
|
|
|
```bash
|
|
# Désactiver uniquement le SSE
|
|
FEATURE_SSE_ENABLED=false
|
|
|
|
# Redémarrer
|
|
docker compose restart app
|
|
```
|
|
|
|
**Impact :** Le frontend bascule automatiquement en REST polling (toutes les 5 secondes). Les données MongoDB sont toujours disponibles. Pas de perte.
|
|
|
|
### Rollback complet (retour système fichier)
|
|
|
|
**Situation :** Problème majeur, tout désactiver.
|
|
|
|
```bash
|
|
# Tout désactiver
|
|
FEATURE_EXCHANGE_PERSISTENCE=false
|
|
FEATURE_SSE_ENABLED=false
|
|
FEATURE_EXCHANGE_API_SOURCE=file
|
|
|
|
# Redémarrer
|
|
docker compose restart app
|
|
```
|
|
|
|
**Impact :** Retour complet au système fichier. MongoDB n'est plus utilisé. L'UI affiche les logs comme avant la migration.
|
|
|
|
---
|
|
|
|
## Migration des données historiques (optionnel)
|
|
|
|
Si vous souhaitez importer les logs fichier existants dans MongoDB, utilisez le script ci-dessous. Cette étape est **optionnelle** car les anciens logs disparaîtront naturellement via le TTL 24h.
|
|
|
|
### Script d'import
|
|
|
|
```typescript
|
|
// scripts/migrate_file_logs_to_mongodb.ts
|
|
import { readFileSync, readdirSync } from 'node:fs'
|
|
import { join } from 'node:path'
|
|
import { getMongoDb } from '#providers/mongodb_provider'
|
|
|
|
const LOG_DIR = join(process.cwd(), 'logs', 'tr069')
|
|
const SEPARATOR = '═'.repeat(80)
|
|
|
|
interface ParsedEntry {
|
|
deviceId: string
|
|
timestamp: string
|
|
direction: 'CPE→ACS' | 'ACS→CPE'
|
|
sessionId: string
|
|
rawXml: string
|
|
}
|
|
|
|
function parseLogFile(deviceId: string, content: string): ParsedEntry[] {
|
|
const entries: ParsedEntry[] = []
|
|
const blocks = content.split(SEPARATOR).filter((b) => b.trim())
|
|
|
|
for (let i = 0; i < blocks.length - 1; i += 2) {
|
|
const header = blocks[i].trim()
|
|
const xml = blocks[i + 1]?.trim() || ''
|
|
|
|
// Parse header: [2025-01-15T10:30:00.123Z] CPE→ACS | 10.0.0.1:54321->192.168.1.1:8080
|
|
const headerMatch = header.match(
|
|
/\[(.+?)\]\s+(CPE→ACS|ACS→CPE)\s+\|\s+(.+)/
|
|
)
|
|
if (!headerMatch) continue
|
|
|
|
entries.push({
|
|
deviceId,
|
|
timestamp: headerMatch[1],
|
|
direction: headerMatch[2] as 'CPE→ACS' | 'ACS→CPE',
|
|
sessionId: headerMatch[3],
|
|
rawXml: xml,
|
|
})
|
|
}
|
|
|
|
return entries
|
|
}
|
|
|
|
async function migrateAll(): Promise<void> {
|
|
const db = getMongoDb()
|
|
const collection = db.collection('cwmp_exchanges')
|
|
const counters = db.collection('cwmp_exchange_counters')
|
|
|
|
const files = readdirSync(LOG_DIR).filter((f) => f.endsWith('.log'))
|
|
let totalMigrated = 0
|
|
|
|
for (const file of files) {
|
|
const deviceId = file.replace('.log', '')
|
|
const content = readFileSync(join(LOG_DIR, file), 'utf-8')
|
|
const entries = parseLogFile(deviceId, content)
|
|
|
|
if (entries.length === 0) continue
|
|
|
|
// Get or initialize sequence counter
|
|
const counterResult = await counters.findOneAndUpdate(
|
|
{ _id: deviceId as any },
|
|
{ $inc: { seq: entries.length } },
|
|
{ upsert: true, returnDocument: 'after' }
|
|
)
|
|
const startSeq = (counterResult!.seq as number) - entries.length + 1
|
|
|
|
// Build documents
|
|
const docs = entries.map((entry, idx) => ({
|
|
...entry,
|
|
sequenceNumber: startSeq + idx,
|
|
cwmpMethod: extractMethod(entry.rawXml),
|
|
createdAt: new Date(entry.timestamp),
|
|
}))
|
|
|
|
// Bulk insert
|
|
await collection.insertMany(docs, { ordered: false }).catch((err) => {
|
|
console.warn(` ⚠️ Duplicates ignored for ${deviceId}: ${err.message}`)
|
|
})
|
|
|
|
totalMigrated += docs.length
|
|
console.log(` ✅ ${deviceId}: ${docs.length} échanges migrés`)
|
|
}
|
|
|
|
console.log(`\n📊 Total: ${totalMigrated} échanges migrés depuis ${files.length} fichiers`)
|
|
}
|
|
|
|
function extractMethod(xml: string): string {
|
|
if (!xml || xml.trim() === '') return 'Empty'
|
|
const match = xml.match(/<cwmp:(\w+)[\s/>]/)
|
|
return match ? match[1] : 'Unknown'
|
|
}
|
|
|
|
migrateAll().catch(console.error)
|
|
```
|
|
|
|
### Exécution
|
|
|
|
```bash
|
|
# Depuis la racine du projet
|
|
npx ts-node scripts/migrate_file_logs_to_mongodb.ts
|
|
```
|
|
|
|
**Notes :**
|
|
- Le script est idempotent : les doublons sont ignorés (grâce à l'index unique deviceId + sequenceNumber)
|
|
- Les documents importés auront un `createdAt` basé sur le timestamp du log, donc ils seront supprimés par le TTL 24h si ils datent de plus de 24 heures
|
|
- Pour conserver les données historiques plus longtemps, ajustez temporairement `EXCHANGE_STORE_TTL_SECONDS`
|
|
|
|
---
|
|
|
|
## Vérifications post-migration
|
|
|
|
### Checklist
|
|
|
|
| # | Vérification | Commande | Attendu |
|
|
|---|-------------|----------|---------|
|
|
| 1 | Collection existe | `db.cwmp_exchanges.stats()` | Collection présente |
|
|
| 2 | Index créés | `db.cwmp_exchanges.getIndexes()` | 4 index (dont _id) |
|
|
| 3 | TTL actif | `db.cwmp_exchanges.getIndexes()` | `expireAfterSeconds: 86400` |
|
|
| 4 | Compteurs fonctionnent | `db.cwmp_exchange_counters.find()` | Entrées par device |
|
|
| 5 | API accessible | `GET /api/v1/tr069-trace/:id/exchanges` | 200 avec données |
|
|
| 6 | SSE connecte | `GET /api/v1/tr069-trace/:id/exchanges/stream` | Event stream ouvert |
|
|
| 7 | Health check | `GET /api/v1/health/tr069-exchanges` | `status: "healthy"` |
|
|
|
|
### Commandes de diagnostic
|
|
|
|
```bash
|
|
# Statistiques collection
|
|
mongosh --eval "
|
|
const stats = db.cwmp_exchanges.stats()
|
|
print('Documents:', stats.count)
|
|
print('Taille:', (stats.size / 1024 / 1024).toFixed(2), 'Mo')
|
|
print('Index size:', (stats.totalIndexSize / 1024 / 1024).toFixed(2), 'Mo')
|
|
"
|
|
|
|
# Vérifier le TTL monitor
|
|
mongosh --eval "
|
|
db.cwmp_exchanges.aggregate([
|
|
{ \$group: { _id: null, oldest: { \$min: '\$createdAt' }, newest: { \$max: '\$createdAt' } } }
|
|
]).forEach(doc => {
|
|
print('Plus ancien:', doc.oldest)
|
|
print('Plus récent:', doc.newest)
|
|
const ageH = (Date.now() - new Date(doc.oldest).getTime()) / 3600000
|
|
print('Âge max:', ageH.toFixed(1), 'heures (devrait être < 24h)')
|
|
})
|
|
"
|
|
|
|
# Vérifier les compteurs de séquence
|
|
mongosh --eval "
|
|
db.cwmp_exchange_counters.find().sort({seq: -1}).limit(5).forEach(doc => {
|
|
print(doc._id + ': seq=' + doc.seq)
|
|
})
|
|
"
|
|
```
|
|
|
|
---
|
|
|
|
## Résumé des feature flags
|
|
|
|
| Flag | Phase 1 | Phase 2 | Phase 3 | Rollback |
|
|
|------|---------|---------|---------|----------|
|
|
| `FEATURE_EXCHANGE_PERSISTENCE` | `true` | `true` | `true` | `false` |
|
|
| `FEATURE_SSE_ENABLED` | `true` | `true` | `true` | `false` |
|
|
| `FEATURE_EXCHANGE_API_SOURCE` | `file` | `mongodb` | `mongodb` | `file` |
|
|
|
|
---
|
|
|
|
## FAQ
|
|
|
|
### Que se passe-t-il si MongoDB tombe pendant le dual-write ?
|
|
|
|
Les écritures MongoDB échouent silencieusement (fire-and-forget). Le système fichier continue normalement. Les messages critiques sont retentés 3 fois avec backoff exponentiel (1s, 2s, 4s) avant d'être abandonnés. La session CWMP n'est jamais interrompue.
|
|
|
|
### Le TTL va-t-il supprimer des données pendant la migration ?
|
|
|
|
Oui. Le TTL de 24h s'applique dès la création de l'index. Si vous importez des logs historiques de plus de 24h, ils seront supprimés par le TTL monitor MongoDB (qui tourne ~toutes les 60 secondes). Augmentez temporairement `EXCHANGE_STORE_TTL_SECONDS` si nécessaire.
|
|
|
|
### Puis-je migrer les données fichier vers MongoDB ?
|
|
|
|
Oui, voir la section "Migration des données historiques". C'est optionnel car le système fonctionne immédiatement avec les nouveaux échanges. Les anciens logs fichier restent lisibles via `FEATURE_EXCHANGE_API_SOURCE=file`.
|
|
|
|
### Combien d'espace MongoDB est nécessaire ?
|
|
|
|
Estimation : ~1 Ko par échange (métadonnées) + taille du XML brut (typiquement 2-50 Ko). Avec le cap de 5 000 messages par device et le TTL 24h, la croissance est bornée. Pour 100 devices tracés simultanément : ~500 Mo maximum.
|
|
|
|
### Le dual-write a-t-il un impact sur les performances CWMP ?
|
|
|
|
Non. Les écritures MongoDB sont fire-and-forget (`catch(() => {})`) et ne bloquent jamais le traitement CWMP. En cas de lenteur MongoDB, le middleware continue sans attendre.
|