Problema
El Agente Local re-envía automáticamente 24 horas de métricas históricas en cada reconexión del WebSocket para asegurar que las gráficas (VictoriaMetrics) tengan historia completa. Esto es correcto para deduplicación temporal en VM, pero causaba que los replays viejos sobrescribieran el estado actual de los targets en la UI.
Incidente capturado (2026-07-17, organización 1):
- WS con reconexiones frecuentes → replays de métricas viejas cada pocos segundos.
- Métricas de la mañana (loss=100%, latency=0) re-llegaban y pisaban el estado actual.
- 130 targets clavados en “down” aunque estuvieran vivos (87 realmente arriba).
- 12.4M métricas históricas re-empujadas en 10h vs 547k producidas en el mismo período.
Root cause: receive_bulk_metrics() aplicaba last_status/last_check de CUALQUIER lote sin validar timestamp.
Decisión
Implementar un guard de frescura en el servidor que separa escrituras a gráficas (VM) de escrituras a estado vivo (MonitoringTarget):
- Todo entra a VictoriaMetrics — incluyendo métricas de >2 min. Así dedup funciona y las gráficas son completas.
- Solo métricas <120s tocan MonitoringTarget (
last_status,last_check,last_packet_loss).
Constante: _TARGET_STATE_FRESHNESS = 120 segundos.
Por qué 120s: Cubre la cadencia mínima de ping real (15-30s interval) + margen de re-entrega WS (~60-90s en peor caso). Una métrica >2min es claramente histórica.
Ventajas
- ✅ Server-side: no requiere cambios en el Agente (.exe). Se activa inmediatamente en prod.
- ✅ Gráficas completas: VM sigue recibiendo todo. Los dashboards no pierden historia.
- ✅ Sin falsos positivos: solo afecta a la UI de estado (lista de targets, widgets). Los datos crudos quedan intactos.
- ✅ Recuperable: si el WS flapea de verdad, el Agente re-reconecta y envía la realidad actual en el siguiente lote fresco.
Limitaciones
- No arregla el root cause en el Agente: throttle de
push_historical+ diagnóstico de flapping WS van en el siguiente release del .exe (#196/#197). - Métrica de 121s puede verse como “sin chequear”: si
last_checkqueda vacío, la UI no finge frescura. Esto es correcto.
Implementación
Archivo: terminal/api/sentinel_ingest.py
_TARGET_STATE_FRESHNESS = 120 # segundos
def receive_bulk_metrics(request, payload: BulkMetricsIn):
now = int(time.time())
state_freshness_floor = now - _TARGET_STATE_FRESHNESS
latest_ping = {}
for m in payload.metrics:
if m.timestamp < state_freshness_floor:
continue # VictoriaMetrics SÍ, MonitoringTarget NO
# ... actualizar target
Test: test_stale_replay_does_not_touch_live_state — verifica que un replay de 1h atrás entra a VM pero NO cambia el estado del target.
Timeline
- 2026-07-17 18:04: Merge a main (v1.61.2).
- 2026-07-17: Deploy automático a prod (sin cambios en Agente).
- Próximo release del .exe: throttle del
push_historical+ diagnóstico de flapping (#196/#197).
Monitoreo
Métricas a seguir en alertas:
monitor_target_state_update_latency_ms— verificar que 99p < 500ms (el guard no debe añadir overhead).agent_metrics_bulk_stale_count_total— contador de métricas rechazadas por frescura. Si es alto (>10% del volumen), indica WS flapeando.
Referencias
- Commit:
a1a2dcf(2026-07-17) - PR: #334 (fix(monitoring): guard de frescura del ingest)
- CHANGELOG: v1.61.2
- Agente tasks futuras: #196, #197 (throttle + diagnóstico).
Véase también
- [[entity—terminal—endpoint—agent-metrics-bulk]]
- [[entity—monitoring—model—monitoring-target]]
- [[concept—monitoring—agent-architecture]]
- [[concept—monitoring—victoriadb-deduplication]]