{“tags”: [“monitoring”,“observability”,“alerting”,“reliability”,“performance”,“django”,“postgres”,“task-229”,“deuda-274”], “related”: [“crearack—monitoring—alertas”,“crearack—monitoring—que-es-observatory”,“entity—monitoring—consumer—realtime-websocket”,“feature—monitoring—auditoria-suprema-etapa-3-sa4-sa5”,“concept—general—como-funciona-la-sonda-tcp-de-monitoring-introduci”,“feature—monitoring—deuda-274-tanda-a”], “last_updated_by”: “agent-ingest”, “last_verified”: “2026-08-27”, “content”: ”## Resumen\n\nHasta ahora, el motor de alertas de Observatory (el rastreador de umbrales que abre un evento cuando la latencia, la pérdida de paquetes o el estado de un equipo cruzan un límite) solo se evaluaba en los caminos manuales: un ping/SNMP/HTTP check lanzado desde la interfaz. El camino real de producción — el lote de métricas que el Agente local envía cada ciclo (ingesta bulk, ~130 equipos cada ~30 segundos) — escribía las lecturas pero jamás llamaba al evaluador. En el uso normal del producto, las alertas configuradas casi nunca disparaban solas. Task #229 conecta ese cable: cada lote del Agente evalúa ahora las alertas activas de la organización con las lecturas frescas que trae.\n\n## Cómo se comporta ahora\n\n- Cada lote de ingesta evalúa TODAS las alertas activadas de la organización (evaluate_alerts_for_ingest), con coste de una sola query cuando no hay alertas configuradas.\n- Permanencia real: cada alerta exige que la condición se mantenga de forma continua N segundos (threshold_duration_seconds, 0-86400, campo “Sustained for (s)” en la interfaz) antes de disparar. El cronómetro vive en el nuevo modelo AlertConditionState (una fila por combinación alerta+equipo; se borra en cuanto la condición deja de cumplirse). Para la condición “Target down for” la permanencia es directamente el propio umbral, como siempre prometió la etiqueta.\n- Tope anti-inundación: si una alerta global dispara sobre 10 equipos o más en el mismo ciclo (por ejemplo, una LAN entera cayendo), se agrupa en un único evento “N devices affected” en vez de abrir una fila por equipo.\n- Un fallo en la evaluación de alertas nunca tumba la ingesta de métricas — se registra en el log y el lote sigue su curso.\n\n## Cómo se usa\n\nNada cambia en el flujo del usuario: se crea/edita una alerta igual que antes desde el gestor de alertas de Observatory, con el nuevo campo “Sustained for (s)” (deshabilitado cuando la condición es “Down for”, porque ahí la permanencia es el propio umbral). Detalle completo del flujo de usuario: [[crearack—monitoring—alertas]].\n\n## Implementación\n\n- monitoring/services/alert_service.py — nueva función evaluate_alerts_for_ingest(organization_id, samples), punto de entrada de la vía viva; reutiliza los mismos helpers de evaluación (_evaluate_condition) que el camino manual (evaluate_and_trigger_alerts), y añade el cronómetro en lote (_sustained_batch) y el agrupado anti-inundación.\n- monitoring/models.py + monitoring/migrations/0030_alert_condition_state.py — modelo nuevo AlertConditionState (alert × target, unique constraint), sin organization_id propio a propósito: el acceso llega vía alert, que sí tiene RLS.\n- terminal/api/sentinel_ingest.py — el endpoint de ingesta bulk del Agente (receive_bulk_metrics) llama a evaluate_alerts_for_ingest tras escribir las métricas del lote, dentro de un try/except que no interrumpe la ingesta si la evaluación falla.\n- monitoring/api/schemas.py + monitoring/api/alerts.py — AlertIn/AlertOut ganan threshold_duration_seconds (antes existía en el modelo pero la API nunca lo aceptaba).\n- static/js/pages/observatory/ObservatoryAlerts.js + templates/monitoring/observatory.html — campo “Sustained for (s)” en el modal de alertas.\n\n## Tests\n\ntests/api/test_alert_live_path.py (15 tests) fija el contrato de punta a punta: POST real al endpoint del Agente → evento de alerta creado, incluyendo permanencia y agrupado anti-inundación. Área verificada en Docker: 64 tests verdes + mypy + makemigrations --check limpio.\n\n## Evolución — v1.85.9 (deuda #274, B1-#29): la alerta ya suena en el momento con Sentinel\n\nHasta este fix, evaluate_alerts_for_ingest() (la función de esta página) sí guardaba el evento de alerta en la base de datos, pero el resultado nunca llegaba al Observatory por WebSocket — receive_bulk_metrics descartaba lo que devolvía. Con la monitorización por Agente (Sentinel), la campana del Observatory solo sonaba al recargar la lista de alertas a mano; con el sondeo clásico desde el servidor (operations.py) sí sonaba al momento, así que el comportamiento dependía de qué vía monitorizaba el equipo. Ahora receive_bulk_metrics reenvía cada alerta disparada del lote con broadcast_alert_sync — mismo cable que ya usaba operations.py. Detalle completo de la tanda: [[feature—monitoring—deuda-274-tanda-a]].\n\n## Commits relacionados\n\n- dd975874 (2026-08-16) — PR #394, v1.68.0.\n- 6518b2a8 (2026-08-27) — PR #446, v1.85.9 (deuda #274 Tanda A, B1-#29).\n\n## Véase también\n\n- [[crearack—monitoring—alertas]]\n- [[crearack—monitoring—que-es-observatory]]\n- [[entity—monitoring—consumer—realtime-websocket]]\n- [[feature—monitoring—auditoria-suprema-etapa-3-sa4-sa5]]\n- [[concept—general—como-funciona-la-sonda-tcp-de-monitoring-introduci]]\n- [[feature—monitoring—deuda-274-tanda-a]]\n”}
Las alertas del Observatory se conectan a la vía viva del Agente (task #229)
Funcionalidaddraftcreado Sun Aug 16#monitoring#observability#alerting#reliability#performance#django#postgres#task-229