Entidadactivecreado Wed Jul 22#agent#terminal#monitoring#performance#websocket#python#sqlite#optimization
Propósito
El módulo sync.py del Local Agent gestiona la sincronización de métricas histórico/unsynced con el servidor SaaS. Desde v2.16.0, implementa un throttle inteligente del backfill histórico para evitar amplificación de datos en caso de reconexiones frecuentes.
Funciones principales
on_connected()
- Llamada tras establecer la conexión WebSocket.
- Evalúa si es necesario un resync histórico basándose en
_disconnected_at. - Antes (v2.15.1): siempre llamaba
push_historical(24h). - Desde v2.16.0: compara downtime con
DISCONNECT_GAP_MINUTES(600 s = 10 min):- Si downtime < 10 min: solo push unsynced (flujo normal).
- Si downtime ≥ 10 min: push histórico del hueco real (
downtime + 15 min margen, cap 24h). - Respeta cooldown de 15 min entre resyncs completados.
push_unsynced()
- Envía métricas pendientes (filas con
synced=False) al servidor. - Operación de bajo costo (típicamente <100 métricas, <1 s).
- Ejecutada en CADA reconexión breve (<10 min) en lugar del backfill completo.
push_historical(hours=24)
- Consulta BD (metrics.db) por la ventana temporal especificada.
- Agrupa en batches de ~1000 métricas (límite
MAX_BATCH_PER_REQUEST). - Envía cada batch por POST al servidor.
- Duración típica: 33-39 s por 5.000 métricas (5 batches × ~7s + ramp-up).
- Punto de throttle: desde v2.16.0, solo se ejecuta si downtime ≥ 10 min.
on_disconnected()
- Registra
_disconnected_at = time.time()(timestamp UTC). - Permite a
on_connected()calcular downtime en la próxima reconexión. - Novedad v2.16.0: integración con sleep-detection (si el agent entra en standby,
_disconnected_atse fija correctamente).
Parámetros de configuración
| Parámetro | Valor | Significado |
|---|---|---|
DISCONNECT_GAP_MINUTES | 600 (10 min) | Umbral para considerar un corte “largo” |
MAX_BATCH_PER_REQUEST | 1000 | Máximo de métricas por POST al servidor |
HISTORICAL_RESYNC_COOLDOWN_MIN | 15 (min) | Mínimo de tiempo entre resyncs completados |
HISTORICAL_RESYNC_CAP_HOURS | 24 | Techo máximo de ventana histórica a recuperar |
Diagrama de estado
[CONECTADO]
↓ (on_disconnected)
[DESCONECTADO] → registra _disconnected_at
↓ (reconexión)
[VALIDANDO DOWNTIME]
├─ downtime < 10 min → push_unsynced() [bajo costo]
└─ downtime ≥ 10 min → (cooldown expired?)
├─ SÍ → push_historical(downtime + 15min) [caro, ~30-40s]
└─ NO → push_unsynced() [bajo costo]
[CONECTADO]
Fuente de datos
-
Tabla
metrics.db(SQLite en%APPDATA%/CreaRack/agent/metrics.db):- Schema:
timestamp,target_id,metric_name,value,synced(bool). - Consultada por ventana temporal en
push_historical(hours). - Actualizada por los collectors (SNMP, ping, HTTP) en tiempo real.
- Marcada
synced=Truetras POST exitoso al servidor.
- Schema:
-
Servidor SaaS (
/api/agent/metrics):- Endpoint que consume los POSTs.
- Valida tenant, target_id, timestamps.
- Persiste en
monitoring.Metric(PostgreSQL).
Impacto v2.16.0
Problema resuelto
- Antes: cada reconexión re-enviaba 24h completas (~5.000 métricas), independientemente de cuánto tiempo llevara fuera. Con WS flapeando (~6,6s de vida por conexión), entraba en loop infinito de resync.
- Amplificación: 22× de overhead documentado (YogaEdu, 21-07-2026).
- Síntomas: saturación de ancho de banda, CPU high, event loop bloqueado.
Solución implementada
- Throttle basado en duración real del downtime.
- Backfill solo del hueco faltante (no 24h completas).
- Cooldown de 15 min entre resyncs para evitar resonancia.
- Flujo unsynced sigue siendo completo (cubre micro-cortes <10 min).
- Primer resync tras arranque es aún completo (alimenta Observatory).
Verificación post-deploy
- KPI: reconexiones/h en YogaEdu deben caer a baseline (~0, solo background noise).
- Si WS sigue cayendo → diagnostico en
close_codelogs (segunda línea:connector.py).
Tests
- Test de throttle:
test_sync_throttle_downtime_<10min(no resync). - Test de resync:
test_sync_throttle_downtime_>=600s(resync del hueco). - Test de cooldown:
test_sync_throttle_cooldown_15min(segundo resync bloqueado). - Coverage: 8 tests nuevos en v2.16.0, 106 tests agente en total (verdes en Docker).
Referencias de código
- Archivo:
terminal/agent/core/sync.py - Última actualización: 2026-07-22 11:04:33 UTC (commit 9679f97).
- Función pública:
class SyncManager(BaseModel) → on_connected(), on_disconnected(), push_unsynced(), push_historical().
Véase también
- [[feature—agent—resync-throttle-v2160]]
- [[entity—terminal—service—connector-ws-diagnostics]]
- [[concept—infra—agent-health-monitoring]]
- [[entity—terminal—model—metric]]
- [[concept—performance—data-amplification-avoidance]]
Referenciado desde
- Agent 2.16.0 — Throttle del resync histórico + diagnóstico del flapping WS + aviso de portátil
- Agente 2.23.0 — restos de la auditoría #261 + sensor "Agente vivo pero mudo"
- CCIB 07-09-2026 — un deep discovery contra un equipo mudo bloqueó el websocket y tiró al Agente 3 minutos
- Servicio Connector WebSocket — Diagnóstico del Flapping (terminal/agent/core/connector.py)
- Servicio SaaS-Commands del Local Agent (terminal/agent/core/saas_commands.py)
- Servicio Sentinel-Runtime del Local Agent (terminal/agent/core/sentinel_runtime.py)