Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
IT Security NachrichtenEU-Gesetz zur Cyber-Resilienz ist in Kraft - Netzpalaver(21.09.2026 um 20:29 Uhr)
Malware / Trojaner / VirenMeta Muse AI app flaw lets local malware redirect dictation traffic(21.09.2026 um 21:59 Uhr)
IT Security NachrichteniPhone 18 Pro (Max): Nutzer melden plötzliche Abstürze durch Face ID(21.09.2026 um 21:18 Uhr)
IT Security DownloadsEs wird Zeit, Word zu löschen(21.09.2026 um 21:53 Uhr)
IT NachrichtenAI can't outprompt a shortage of power, water, and land(21.09.2026 um 18:35 Uhr)
IT NachrichtenLondon neocloud Nscale takes its $1B loss to Wall Street(21.09.2026 um 19:15 Uhr)
IT Security NachrichtenEU-Gesetz zur Cyber-Resilienz ist in Kraft - Netzpalaver(21.09.2026 um 20:29 Uhr)
Malware / Trojaner / VirenMeta Muse AI app flaw lets local malware redirect dictation traffic(21.09.2026 um 21:59 Uhr)
IT Security NachrichteniPhone 18 Pro (Max): Nutzer melden plötzliche Abstürze durch Face ID(21.09.2026 um 21:18 Uhr)
IT Security DownloadsEs wird Zeit, Word zu löschen(21.09.2026 um 21:53 Uhr)
IT NachrichtenAI can't outprompt a shortage of power, water, and land(21.09.2026 um 18:35 Uhr)
IT NachrichtenLondon neocloud Nscale takes its $1B loss to Wall Street(21.09.2026 um 19:15 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Alarmas que despiertan por causa, no por un número

El escenario es un trabajo en segundo plano en una plataforma de comunidad: un matcher nocturno que empareja items nuevos contra un pool de usuarios elegibles y escribe filas a una tabla matches. Pero la lección es general: cualquier …

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

El escenario es un trabajo en segundo plano en una plataforma de

comunidad: un matcher nocturno que empareja items nuevos contra un pool de usuarios elegibles y escribe filas a una tabla matches. Pero la lección es general: cualquier alarma sobre un conteo de salida (items emparejados, correos enviados, registros procesados, recomendaciones generadas) tiene el mismo fallo.







GitHub logo

elchesco
/
blog-cause-aware-alarms-code



Code companion — alarms that page on cause, not on a number






Code companion — alarms that page on cause, not on a number



00-output-threshold-alarm/   the naive version: emitter + alarm that page on a COUNT
01-diagnose/ read-only SQL that falsifies the alarm's hypothesis
02-instrument-the-emitter/ the pure classifier + the instrumented job
03-cause-aware-alarm/ same threshold, cause-accurate line + a benign metric
04-test-the-gate/ test the classification, not the plumbing


Reading order





  1. 00-output-threshold-alarm/ — what pages on a coincidence. Note the
    3/3 streak hedge: a workaround for an ambiguous metric, not a fix.


  2. 01-diagnose/funnel.sql — before touching a threshold, confirm the
    alarm caught its stated failure. Here it hadn't: input + similarities
    were healthy; the pool was quota-capped.


  3. 02-instrument-the-emitter/ — move the intelligence to the emitter
    classify.py is the whole idea in one pure function; matcher.py shows
    the two counters that feed it.


  4. 03-cause-aware-alarm/ — the threshold is UNCHANGED. Only the log line
    got smarter. Plus an unalarmed metric for the benign nights.


  5. 04-test-the-gate/










TL;DR











































Alarma por umbral de salida Alarma consciente de la causa
Qué vigila "¿el conteo llegó a 0?" "¿el conteo llegó a 0 por una razón rota?"
Dónde vive la lógica el umbral en el CDK el emisor (el trabajo mismo)
Despertadas falsas cada mes, en agenda ninguna observada
Noches-0 benignas indistinguibles de una falla registradas como una métrica aparte, sin alarma
Arreglo cuando dispara en falso subir el umbral → quedar ciego ya está correcto
Costo nuevo de AWS $0 (derivado de logs) $0 (derivado de logs)


Tres ideas hacen el trabajo:





  1. Una alarma por conteo de salida confunde causas. count == 0 puede significar "roto" o "correctamente callado". Un solo umbral no los distingue.


  2. Diagnostica antes de ajustar. Cuando una alarma dispara, primero pregunta si atrapó la falla que dice atrapar. Razona desde los datos, no desde la descripción de la alarma.


  3. Mueve la inteligencia al emisor. Instrumenta el pipeline para que emita por qué el conteo fue 0. Deja la alarma tonta; haz lista la línea de log.






La trampa: un conteo no es un diagnóstico



La alarma original era una métrica derivada de logs de manual. El trabajo loguea una línea cuando termina con 0 resultados; un metric filter la cuenta; la alarma despierta ante una racha sostenida:




# 00-output-threshold-alarm/emit.py (la versión ingenua)
if scanned_items and created == 0:
logger.warning("matcher_zero_results", items=len(scanned_items))









// 00-output-threshold-alarm/alarm.ts
new logs.MetricFilter(this, "ZeroResultsFilter", {
logGroup, filterPattern: logs.FilterPattern.literal('"matcher_zero_results"'),
metricNamespace: "myapp/Jobs", metricName: "MatcherZeroResults",
metricValue: "1", defaultValue: 0,
});
new cw.Alarm(this, "ZeroResultsAlarm", {
metric: zeroResultsMetric, threshold: 1,
evaluationPeriods: 3, datapointsToAlarm: 3, // 3 noches seguidas
});






El evaluationPeriods: 3 ya es una cobertura: alguien sabía que una sola noche 0 era varianza normal y exigió una racha antes de despertar. Esa cobertura es la pista. Compra tiempo; no arregla la ambigüedad. Si el conteo puede legítimamente quedarse en 0 por un rato, entonces "3 noches seguidas" no es "roto", es "ya esperamos tres noches". La alarma dispara de todos modos. Nada más te entrenaste a esperar más para la despertada falsa.



El problema real: created == 0 tiene (al menos) dos causas.





  • Roto. Un umbral o un embedding regresó y nada pasa la barra ya, la clásica falla de "regresa 0 para siempre". Esto debería despertar, fuerte, rápido.


  • Correctamente callado. El pool elegible es chico y cada candidato que podía emparejar ya lo hizo, o pegó contra una cuota mensual por usuario. El trabajo se está portando perfecto. Esto nunca debería despertar.






Paso 1: diagnostica antes de ajustar



El instinto cuando dispara una alarma "inestable" es subir el umbral o ensanchar la ventana. Resístelo. Primero responde una pregunta: ¿la alarma atrapó lo que dice que atrapó?



La propia descripción de la alarma culpaba a una regresión. Así que revisé las precondiciones de esa regresión directo, desde datos de producción, antes de tocar cualquier cosa:




-- 01-diagnose/funnel.sql: ¿está sano el input? (abreviado)
-- 1. ¿Hay items frescos para emparejar siquiera?
SELECT count(*) FROM items
WHERE status='ACTIVE' AND embedding IS NOT NULL
AND created_at > now() - interval '7 days'; -- sano: cientos

-- 2. ¿Los candidatos pasan el piso de similitud? (la barra "regresada")
-- Puntúa el item más nuevo contra el pool elegible.
SELECT max(1 - (u.embedding <=> :item_vec)) AS best_similarity
FROM eligible_users u; -- sano: bien arriba del piso

-- 3. ¿Los usuarios elegibles ya fueron servidos este periodo?
SELECT tier, used, cap, count(*) n
FROM usage_counters WHERE feature='matcher' AND period=:this_month
GROUP BY tier, used, cap ORDER BY tier; -- la respuesta






Los datos contaban una historia clara: input sano, similitudes sanas, y 9 de 11 usuarios servibles ya estaban en su tope mensual. Una ráfaga más temprano en el mes había consumido legítimamente la cuota del pool; las "0 noches" eran el pool sentado en su tope hasta el siguiente reinicio.

Ninguna regresión. La alarma había disparado sobre un sistema correcto.



Este es el paso de carga estructural y el que más fácil se salta. La alarma me entregó una hipótesis ("regresión"); los datos la falsearon. Todo lo que viene después solo vale la pena hacerlo porque me detuve a revisar.




Regla de dedo: la descripción de una alarma es una hipótesis, no un

diagnóstico. El primer trabajo de un humano de guardia (o de un agente)

es confirmar que la alarma atrapó su falla declarada, no silenciar el

síntoma.






Paso 2: nombra la falla con precisión



Antes de escribir código, escribe la oración que la alarma debería

codificar. La mía:




Despierta cuando el trabajo escaneó input real y candidatos elegibles

llegaron a la compuerta final con cuota de sobra, y aun así produjo 0

resultados.




Todo lo que esa oración excluye es, por construcción, un no-incidente:




  • 0 porque ningún candidato llegó a la compuerta final → el pool ya estaba
    servido o inactivo. Callado.

  • 0 porque cada candidato que llegó a la compuerta estaba bloqueado por
    cuota → demanda real, correctamente limitada por tasa. Callado.

  • 0 porque candidatos llegaron a la compuerta, tenían cuota, y nada
    pasó
    → la barra está rota. Despierta.



Nota que esto es una afirmación sobre los estados internos del pipeline,

no sobre el conteo de salida. Lo que significa que la métrica de salida

nunca lo puede expresar. El emisor tiene que hacerlo.





Paso 3: mueve la inteligencia al emisor



La alarma se queda tonta, un umbral sobre un conteo. Hacemos lista la

línea de log haciendo que el trabajo clasifique su propia corrida de 0 resultados. Dos contadores baratos, acumulados conforme corre el pipeline, capturan la distinción:




# 02-instrument-the-emitter/matcher.py (forma)
class NightlyMatcher:
def __init__(self, ...):
self._reached_gate = 0 # candidatos que pasaron los pre-filtros
self._quota_blocked = 0 # descartados SOLO por el tope por usuario

async def _process_item(self, item):
candidates = await self._prefilter(item) # similitud, dedup, ...
self._reached_gate += len(candidates) # llegaron a la compuerta
created = 0
for c in candidates:
if c.score < THRESHOLD:
continue # encaje débil
if not await self._has_quota(c.user_id):
self._quota_blocked += 1 # demanda real, en tope
continue
await self._create_match(item, c)
created += 1
return created






El clasificador es una función pura, trivial de razonar y de probar:




# 02-instrument-the-emitter/classify.py
def classify_zero_result(reached_gate: int, quota_blocked: int) -> str:
"""¿Por qué una corrida que escaneó input real creó 0 resultados?"""
if reached_gate == 0 or quota_blocked > 0:
return "quiet" # pool servido/inactivo, o la demanda pegó el tope
return "regression" # candidatos + cuota de sobra, nada pasó






Y la compuerta de emisión enruta a dos eventos de log distintos:




# 02-instrument-the-emitter/emit.py
if scanned_items and created_total == 0:
if classify_zero_result(self._reached_gate, self._quota_blocked) == "quiet":
logger.info("matcher_quiet_night", # solo observ., sin despertar
reached_gate=self._reached_gate,
quota_blocked=self._quota_blocked)
else:
logger.warning("matcher_zero_results", # la regresión real
reached_gate=self._reached_gate)






El metric filter de la alarma no cambia: sigue haciendo match a

"matcher_zero_results". Pero esa línea ahora solo aparece para la falla genuina. En una noche callada el trabajo emite matcher_quiet_night en su lugar, que no hace match al patrón de la alarma, así que la métrica publica su defaultValue de 0 y la racha se rompe. La despertada falsa desapareció no porque subimos un umbral, sino porque el emisor dejó de mentir sobre la causa.





Paso 4: mantén observable el camino benigno



"No despertar" no es "no registrar". Las noches calladas son una señal real: te dicen que el pool servible se está topando, lo cual es un dato de crecimiento, no un incidente. Así que el evento benigno recibe su propia métrica, sin alarma:




// 03-cause-aware-alarm/quiet-metric.ts: visibilidad, no una despertada
new logs.MetricFilter(this, "QuietNightFilter", {
logGroup, filterPattern: logs.FilterPattern.literal('"matcher_quiet_night"'),
metricNamespace: "myapp/Jobs", metricName: "MatcherQuietNight",
metricValue: "1", defaultValue: 0,
});






Ahora el dashboard muestra las dos líneas: regresiones (que despiertan) y noches calladas (que no). Cuando alguien pregunta después "¿por qué cayeron

los matches en la segunda mitad del mes?", la respuesta es una gráfica, no una sesión forense de SQL. Y si las noches calladas suben de manera sostenida, esa es la señal para subir los topes o ensanchar la elegibilidad, una decisión de producto que la alarma vieja sepultaba bajo un incidente falso.






Las lecciones del umbral





  • No subas el umbral para silenciar una despertada falsa. Es el
    reflejo, y está al revés. Subirlo (o ensanchar la ventana) hace la alarma
    más lenta para atrapar la falla real mientras de todos modos termina disparando por la benigna. Cambias un falso negativo por un falso positivo demorado. Arregla la ambigüedad en su lugar; deja el umbral apretado.


  • Una cobertura en la config de la alarma es un olor. datapointsToAlarm: 3, evaluationPeriods: N, un sospechosamente redondo > 10: cada uno es a menudo un humano dándole la vuelta a una métrica ambigua. A veces es la decisión correcta (un solo throttle está bien; alarma sobre la ráfaga). Pero si la causa es ambigua, ninguna cantidad de contar rachas la resuelve. Pregúntate cuál de las dos tienes.


  • Alarma sobre la falla, mide el resto. Exactamente una señal debería despertar: el estado por el que despertarías a un humano. Todo lo de al lado, el cero benigno, el parpadeo reintentado, la demanda limitada por tasa, es una métrica sin acción de alarma. Despertar sobre estados ambiguos es como las alarmas terminan silenciadas.






Trampas





  • Los metric filters literales hacen match a subcadenas. Este me
    mordió. El FilterPattern.literal('"matcher_zero_results"') de
    CloudWatch hace match a cualquier evento de log que contenga esa
    cadena. Nombra el evento benigno matcher_quiet_night, no
    matcher_zero_results_ok: el segundo contiene el patrón de la alarma como subcadena y la dispararía de todos modos, derrotando en silencio todo el arreglo. Elige un token sin traslape.


  • defaultValue: 0 es lo que rompe la racha. El filter emite 0 por
    cada periodo sin línea que haga match. Ese es el mecanismo que le permite
    a una noche callada "publicar un 0" y reiniciar una alarma de datapoints
    consecutivos. Sin él, los datapoints faltantes dependen de
    treatMissingData y la lógica de racha se vuelve turbia. Consérvalo.


  • Prueba la compuerta, no la plomería. La prueba valiosa no es "¿sirve
    el metric filter?" (eso es trabajo de AWS). Es "¿el trabajo emite el
    evento correcto para cada estado interno?". Maneja el emisor y afirma
    la línea de log:




# 04-test-the-gate/test_emit.py
async def test_pages_only_on_regression():
m = NightlyMatcher(...)
m._process_item = fake(reached_gate=+3, quota_blocked=+0, created=0)
with structlog.testing.capture_logs() as logs:
await m.run()
events = [e["event"] for e in logs]
assert "matcher_zero_results" in events # regresión → despierta
assert "matcher_quiet_night" not in events

async def test_quiet_when_quota_capped():
m = NightlyMatcher(...)
m._process_item = fake(reached_gate=+3, quota_blocked=+3, created=0)
with structlog.testing.capture_logs() as logs:
await m.run()
events = [e["event"] for e in logs]
assert "matcher_quiet_night" in events # demanda en tope → callado
assert "matcher_zero_results" not in events









Una checklist reutilizable



Cuando una alarma dispara y sospechas que es ruido, antes de tocar un

umbral:





  1. Confirma la falla declarada. ¿Los datos muestran la condición exacta
    que la alarma dice? Si no, la alarma está mal categorizando: un bug de
    diseño, no un problema de ajuste.


  2. Enumera las causas del valor de la métrica. 0, 5xx, lento,
    profundidad de cola: lista cada razón distinta por la que llega al
    valor de la alarma. Si hay más de una, y difieren en si son incidentes,
    la métrica es ambigua.


  3. Escribe la falla en una oración. En términos del estado interno del
    sistema, no de su salida. Si la métrica no puede expresar esa oración,
    el emisor debe hacerlo.


  4. Mueve la lógica al emisor. Emite una señal distinta por causa.
    Despierta sobre la del incidente; mide el resto.


  5. Cuida las colisiones de subcadena y el defaultValue. (Ve las
    trampas.)


  6. Prueba la clasificación, no la alarma. Afirma que el emisor produce
    el evento correcto para cada estado.






La forma de todo esto






ANTES:  count == 0  ─────────────────────────► alarma ──► despierta (a veces mal)

DESPUÉS: corrida termina en 0 ─► clasifica(estado interno) ─┬─ "regression" ─► línea warn ─► alarma ─► despierta
└─ "quiet" ─► línea info ─► métrica (sin despertar)






Un principio: una alarma debería disparar por una causa, no por una

coincidencia.
Cuando un solo valor de salida tiene múltiples causas, no

ajustes el umbral: empuja el diagnóstico río arriba, al código que ya sabe

por qué el número es lo que es, y deja que lo diga en la línea de log. La

alarma se queda como un umbral tonto. La inteligencia vive donde está el

contexto. Y el pager suena solo cuando algo de verdad anda mal.

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Alarmas que despiertan por causa, no por un número

Thematisch verwandte Begriffe: Alarmas, despiertan, causa, número · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-94497 | jshERP through 3.6 fails to validate object ownership in by-id info, upd…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick