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. 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 expresión CEL de cada regla se compila por cada evaluación — el engine no mantiene un conjunto de programas precompilados y listos para ejecutar.
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.events"]
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.
Esta validación ocurre en el momento de crear/actualizar, así que una expresión rota se detecta antes de que llegue siquiera a la ruta de evaluación. En tiempo de ejecución la expresión se compila de nuevo en cada evaluación, en lugar de leerse como un artefacto listo para ejecutar.
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 Redpanda mqtt.commands; un bridge de EMQX lo reenvía al dispositivo — 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 el topic estático
rule.eventsque 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 origen de reglas
Sección titulada «La caché de origen de reglas»Las reglas se cachean en el namespace de Aerospike rules, set compiled, con
clave {tenant_id}:{rule_id}, con un TTL de 600 segundos (10 minutos). A pesar del
nombre del set, lo que se almacena es el origen de la regla (su condición CEL y los
metadatos), no un programa compilado y listo para ejecutar: el engine recompila la
expresión CEL en cada evaluación. La caché simplemente evita releer la definición de la
regla desde el almacenamiento primario en 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.