feat(alertas): el motor llega al datalake — obsolescencia y ancho de banda
Rebanada 3 del plan. El motor sólo hablaba con la BD del portal, así que las dos familias que faltaban de la fase 1 —obsolescencia de activos y saturación de enlaces— eran inalcanzables: viven en serving_ti, otra conexión y otro rol.
Cada tabla declara su fuente (_RULE_FUENTE); lo que no aparece ahí es del
portal, así que las siete tablas existentes no cambian de conducta.
La guarda que exigía el plan, en _medir: un cero de verdad y un "no pude leer"
se ven idénticos y no se parecen en nada. Los endpoints de TI devuelven vacío
cuando el datalake no responde; si el motor heredara eso, una alarma como "menos
de N activos vigentes" dispararía CRÍTICO justo cuando lo caído es el pipeline o
la VPN — un incidente inventado que además tapa el real. Ahora devuelve
sin_datos y la alarma se calla, en tres casos: conexión muerta, consulta rota,
y tabla de origen vacía. El tercero es el traicionero, porque la consulta
funciona y el 0 es legítimo; una tabla de origen sin una sola fila no significa
"no hay activos", significa que el DAG no cargó. Sólo aplica al datalake: en el
portal una tabla vacía sí puede ser un cero real.
Columnas calculadas (_RULE_EXPR): la saturación de un enlace es
pico/velocidad_contratada y no existe como columna. Sin ella los umbrales
ti_ancho_banda_util_rojo/amarillo, que están en porcentaje, serían inservibles —
habría que alertar en bits por segundo. La expresión sale siempre del
diccionario, nunca del cliente.
_valor_indicador pasa por el mismo _medir que el cron, para que la pantalla
y la alarma no puedan discrepar, y muestra "no hay dato" donde antes habría
puesto un 0 engañoso.
Verificado contra el datalake real (955 activos, 7,772 filas de ancho de banda): mediciones correctas con umbral vivo, y los tres modos de sin_datos simulados uno por uno — en los tres la alarma no dispara. Probar esto destapó que la apertura de la sesión estaba fuera del try, así que una fuente caída podía tumbar el job entero; corregido.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01Dy1oTZc5vwiws7EFFQwnHF