Saltar a contenido

Incident Management en Zeebe

Resumen: Los incidents en Zeebe son mecanismos de self-healing que capturan situaciones irrecuperables automáticamente (JOB_NO_RETRIES, UNHANDLED_ERROR_EVENT, EXTRACT_VALUE_ERROR entre otros). El IncidentResolveProcessor reconstruye el comando original fallido y lo re-procesa; si la resolución falla, el sistema crea un nuevo incident automáticamente, garantizando que ningún error se pierde silenciosamente.


Concepto de Incident

Un incident en Zeebe representa una situación anormal que requiere atención y que el engine no puede resolver por sí solo. A diferencia de una excepción que se propaga y termina un proceso, un incident congela la ejecución del elemento afectado, permitiendo que:

  1. El resto del proceso (otras ramas, otros elementos) continúe ejecutándose.
  2. Un operador humano o un sistema automatizado inspeccione y resuelva el problema.
  3. La ejecución se reanude exactamente desde donde se detuvo.

Los incidents son first-class citizens en la arquitectura de Zeebe: tienen su propio estado, se persisten en RocksDB, se exportan a Elasticsearch/OpenSearch, y son visibles en Operate.


Triggers de incidents

JOB_NO_RETRIES

Es el trigger más común. Se activa cuando un job falla y no tiene reintentos restantes:

Worker → FailJob(retries=0) → JobFailProcessor → crea Incident(JOB_NO_RETRIES)
  • El job queda en estado FAILED.
  • El incident referencia al job key y al process instance.
  • Para resolverlo, se debe actualizar el número de retries del job (via UpdateJobRetries) y luego resolver el incident.
  • Ver concepts/retry-backoff para el flujo completo de retries.

UNHANDLED_ERROR_EVENT

Se activa cuando un worker lanza un error (vía ThrowError) y no existe un error boundary event que lo capture:

Worker → ThrowError(errorCode="PAYMENT_FAILED")
    → Engine busca error catch event en scope actual y padres
    → No encuentra ninguno
    → Crea Incident(UNHANDLED_ERROR_EVENT)
  • El incident incluye el errorCode y errorMessage del error no capturado.
  • La resolución típica implica:
  • Desplegar una versión actualizada del proceso que incluya el error catch event.
  • Resolver el incident, lo cual reintenta el ThrowError y ahora encuentra el catch event.
  • O bien, resolver manualmente el estado y continuar.

EXTRACT_VALUE_ERROR

Se activa cuando el engine no puede evaluar una expresión FEEL o extraer un valor de una variable:

Proceso tiene condición: =order.total > 1000
    → Variable "order" no existe o no tiene campo "total"
    → Crea Incident(EXTRACT_VALUE_ERROR)

Escenarios comunes:

Contexto Ejemplo
Condition expression Gateway con expresión que referencia variable inexistente
Input/output mapping =customer.email cuando customer es null
Timer expression =retryDelay cuando la variable no está definida
Message correlation key =orderId cuando no existe en el scope
Multi-instance collection =items cuando la variable no es una lista
Script task Expresión FEEL con error de tipo

Otros triggers

  • CALLED_ELEMENT_ERROR: error al resolver el processId de un call activity.
  • MESSAGE_SIZE_EXCEEDED: el resultado de un job o un mensaje excede el tamaño máximo configurado.
  • FORM_NOT_FOUND: un user task referencia un form ID que no existe en el deployment.

IncidentResolveProcessor

El IncidentResolveProcessor es el componente que maneja la resolución de incidents, implementando un patrón de replay del comando fallido.

Flujo de resolución

1. Operador/API llama ResolveIncident(incidentKey)
2. IncidentResolveProcessor:
   a. Lee el incident de RocksDB
   b. Identifica el comando original que causó el incident
   c. Reconstruye el comando con el contexto actual
   d. Re-procesa el comando
3. Si el re-procesamiento tiene éxito:
   a. El incident se marca como RESOLVED
   b. El elemento BPMN continúa su ejecución normal
4. Si el re-procesamiento falla:
   a. Se crea un NUEVO incident (self-healing)
   b. El incident original se marca como RESOLVED
   c. El nuevo incident refleja el nuevo error

Reconstrucción del comando fallido

El processor no simplemente "reintenta" — reconstruye el comando original:

  1. Lee el estado actual del elemento: obtiene el element instance de RocksDB con su estado actualizado (incluyendo variables que podrían haber sido modificadas por el operador).
  2. Recrea el intent del comando: identifica qué operación estaba intentando ejecutar el engine cuando falló (activar un gateway, evaluar una expresión, completar un job, etc.).
  3. Ejecuta con contexto fresco: el comando reconstruido se procesa con las variables actuales, no con las del momento del fallo original.

Esto es crucial porque permite que la resolución funcione mediante la corrección del estado:

  • Si el incident fue EXTRACT_VALUE_ERROR porque faltaba la variable orderId, el operador puede agregar la variable vía la API y luego resolver el incident.
  • El processor reconstruirá la evaluación de la expresión, ahora encontrará orderId, y continuará.

Self-healing: cascada de incidents

Una propiedad clave del sistema es que si la resolución de un incident falla, se crea un nuevo incident automáticamente. Esto garantiza:

  1. No silent failures: nunca se pierde un error. Si el operador resuelve un incident pero la condición subyacente no se corrigió completamente, se generará un nuevo incident con información actualizada.
  2. Idempotencia segura: resolver un incident es una operación segura — en el peor caso, genera un nuevo incident que describe el problema actual.
  3. Trazabilidad: la cadena de incidents (original → resolved → nuevo) queda registrada en el log y exportada a Elasticsearch/OpenSearch.

Ejemplo de cascada

1. Proceso intenta evaluar: =customer.address.zipCode
   → "customer" existe pero "address" es null
   → Incident #1: EXTRACT_VALUE_ERROR

2. Operador agrega variable "address" = {"city": "Berlin"}
   → Resuelve Incident #1

3. IncidentResolveProcessor re-evalúa: =customer.address.zipCode
   → "address" existe pero "zipCode" no está
   → Incident #1 se marca RESOLVED
   → Incident #2: EXTRACT_VALUE_ERROR (nuevo mensaje de error)

4. Operador actualiza "address" = {"city": "Berlin", "zipCode": "10115"}
   → Resuelve Incident #2
   → Re-evaluación exitosa
   → Proceso continúa

Estado y persistencia

Estado del incident en RocksDB

Campo Descripción
incidentKey Identificador único del incident
type Tipo del incident (JOB_NO_RETRIES, EXTRACT_VALUE_ERROR, etc.)
processInstanceKey Process instance afectado
elementInstanceKey Element instance específico donde ocurrió
jobKey Key del job (si el incident es de tipo JOB_NO_RETRIES)
errorMessage Descripción del error
state CREATED, RESOLVED
creationTime Timestamp de creación

Exportación

Los incidents se exportan a Elasticsearch/OpenSearch como eventos, lo que permite:

  • Operate: visualización de incidents activos, resolución via UI.
  • Alertas: configurar alertas basadas en tipo de incident, proceso afectado, antigüedad.
  • Métricas: tracking de frecuencia de incidents por tipo, proceso, y tiempo de resolución.

Impacto en la ejecución del proceso

Cuando un incident se crea:

  • El element instance se congela: no avanza ni se cancela.
  • El process instance sigue activo: otros tokens en el proceso continúan normalmente.
  • Jobs asociados no se reactivan: si el incident es de tipo JOB_NO_RETRIES, el job no se ofrece a workers.
  • Timers siguen corriendo: timers en otras ramas del proceso no se afectan.
  • Sub-procesos: un incident en un sub-proceso no afecta al proceso padre (salvo que el sub-proceso tenga un timer boundary event que expire).

Consideraciones para un MVP

  • Esencial: el patrón de crear incidents en lugar de fallar silenciosamente es fundamental para un workflow engine de producción. Sin incidents, los procesos se "pierden" cuando algo falla.
  • Esencial: la reconstrucción del comando fallido (no simple retry) es lo que permite resolver incidents modificando el estado.
  • Esencial: el self-healing (nuevo incident si la resolución falla) previene silent failures.
  • Simplificable: para un MVP, soportar solo JOB_NO_RETRIES y EXTRACT_VALUE_ERROR cubre la mayoría de los casos. UNHANDLED_ERROR_EVENT puede agregarse cuando se implemente error handling completo en BPMN.
  • Simplificable: la exportación a Elasticsearch puede reemplazarse con una API REST simple sobre el state store para visualizar y resolver incidents.