Rules engine
El rules engine evalúa una expresión CEL contra cada punto de telemetry validado y despacha acciones cuando coincide. Es la capa reactiva ligera, por punto: lógica simple de condición-a-acción que se ejecuta en menos de 5ms. Para el procesamiento de grafos de múltiples pasos — enriquecimiento, ramificación, connectors, manejo de dead-letter — consulta Rule chains.
Una regla es una condición (una expresión CEL), un grupo de dispositivos al que se aplica, y una o más acciones a disparar cuando la condición es verdadera.
Cómo funciona la evaluación
Sección titulada «Cómo funciona la evaluación»El engine consume el mismo flujo de telemetry enriquecido y validado que tus dispositivos producen al enviar telemetry, y evalúa las reglas coincidentes contra cada punto. La evaluación debe completarse en 5ms por punto.
flowchart TD
point([Validated point<br/>telemetry.validated.T1]) --> scope{Device in the<br/>rule's group?}
scope -->|no| skip([Rule not evaluated])
scope -->|yes| eval{CEL condition true?}
eval -->|no| none([No action])
eval -->|yes| act["Dispatch actions"]
act --> event["Publish rule.triggered.T1.{rule}"]
act --> metric["Increment rules_triggered_total"]
Ámbito de variables de la expresión CEL
Sección titulada «Ámbito de variables de la expresión CEL»Las condiciones se escriben en CEL (Common Expression Language). Solo las variables de abajo están en el ámbito; referenciar cualquier otra cosa es un error en tiempo de compilación, de modo que una regla rota nunca se puede guardar.
| Variable | Tipo | Descripción |
|---|---|---|
data | map(string, double) | Valores numéricos de telemetry, p. ej. data.temperature |
data_str | map(string, string) | Valores de cadena de telemetry, p. ej. data_str.firmware_status |
device.id | string | ID de dispositivo |
device.name | string | Nombre para mostrar |
device.groups | list(string) | Pertenencia a grupos |
device.tags | map(string, string) | Pares clave-valor de tags |
device.firmware | string | Versión de firmware |
device.status | string | online / offline |
device.last_seen | int | Marca de tiempo Unix (segundos) de la última telemetry |
timestamp | int | Marca de tiempo Unix (segundos) del punto actual |
tenant_id | string | Tenant ID |
Validación en tiempo de compilación
Sección titulada «Validación en tiempo de compilación»Cuando se crea o actualiza una regla, la expresión CEL se compila y valida antes de almacenarse. Se detectan dos clases de error por adelantado:
- Errores de sintaxis. Una expresión malformada como
data.temperature >>se rechaza conINVALID_ARGUMENTy un error de parseo legible para humanos. - Referencias no declaradas. Usar una variable que no está en el ámbito — p. ej.
data.temperature > threshold— falla con “undeclared reference to ‘threshold’” eINVALID_ARGUMENT.
Por eso las reglas son rápidas en tiempo de ejecución: para cuando una regla se almacena, ya es un artefacto validado y compilado, no una cadena que parsear por cada punto.
Acotado por grupo
Sección titulada «Acotado por grupo»Cada regla apunta a un grupo de dispositivos. Un punto solo se evalúa contra las
reglas cuyo grupo objetivo contiene el dispositivo de origen. Una regla para el grupo
furnace nunca se evalúa para un dispositivo del grupo hvac — el engine la omite por
completo en lugar de evaluarla y luego descartarla. Esto mantiene el trabajo por punto
proporcional a las reglas que realmente aplican.
Reglas de ejemplo
Sección titulada «Reglas de ejemplo»Alertar cuando cualquier dispositivo de furnace supere los 80 °C:
data.temperature > 80Grupo objetivo: furnace. Acción: webhook a tu endpoint de alertas.
Disparar solo para dispositivos de la región EU por encima del umbral — combinando datos numéricos con un tag de dispositivo:
data.temperature > 80 && device.tags["region"] == "eu"Reaccionar ante una métrica de valor de cadena, p. ej. firmware reportando que necesita una actualización:
data_str.firmware_status == "update_required"Combinar múltiples métricas y el estado del dispositivo:
data.temperature > 75 && data.pressure > 4.0 && device.status == "online"Tipos de acción
Sección titulada «Tipos de acción»Una regla dispara una o más de estas acciones cuando su condición es verdadera:
| Acción | Comportamiento |
|---|---|
| Webhook | HTTP POST que transporta el punto de telemetry más los metadatos de la regla. Se reintenta 3 veces con exponential backoff; en el fallo final se registra con contexto de trazo y rule_action_failures_total{type="webhook"} se incrementa. |
| Comando MQTT | Publica un payload de comando en el topic EMQX devices/{device_id}/commands — p. ej. {"set_fan": "high"}. |
| Envía una notificación por SMTP. | |
| Evento Redpanda | Publica un evento en un subject de Redpanda para consumidores downstream. |
El evento rule.triggered y las métricas
Sección titulada «El evento rule.triggered y las métricas»Cada vez que una regla se dispara, además de sus acciones, el engine:
- Publica un evento en
rule.triggered.{tenant_id}.{rule_id}que transportarule_id,device_idy los valores del disparo. Esto es lo que impulsa los feeds de disparos de regla en vivo y el historial de disparos por regla en la UI. - Incrementa
rules_triggered_total{tenant="T1",rule="R1"}.
Si una regla no coincide, no se despacha ninguna acción y no se publica ningún evento.
La caché de reglas compiladas
Sección titulada «La caché de reglas compiladas»Las reglas compiladas se cachean en el namespace de Aerospike rules, set compiled, con
clave {tenant_id}:{rule_id}, con un TTL de 600 segundos (10 minutos). La evaluación
lee la forma compilada lista para ejecutar desde esta caché en lugar de recompilar por
cada punto. Una consecuencia práctica: después de editar o deshabilitar una regla, el
cambio surte pleno efecto dentro de la ventana del TTL de la caché. Deshabilitar una regla
establece su flag enabled en false para que las evaluaciones posteriores la omitan.
Detección de conflictos (informativa)
Sección titulada «Detección de conflictos (informativa)»Cuando creas o actualizas una regla, el engine comprueba si hay reglas potencialmente en
conflicto — por ejemplo, dos reglas en el mismo grupo de dispositivos con condiciones
solapadas. Los conflictos son informativos, no bloqueantes: la regla se guarda y la
respuesta incluye un array warnings que indica el solapamiento.
Cuándo recurrir a las rule chains en su lugar
Sección titulada «Cuándo recurrir a las rule chains en su lugar»Usa el rules engine para lógica simple y sin estado de condición → acción. Recurre a las rule chains cuando necesites enriquecer un mensaje con datos de activo o de entity relacionada, ramificar según múltiples condiciones, condicionar según la finalidad en blockchain, abanicar hacia connectors externos con reintentos y una dead-letter queue, o crear y gestionar alarms como entidades de primera clase.