Saltar a contenido

Deployment Pipeline

El deployment en Zeebe es un proceso multi-fase: el cliente envía resources a la partición de deployment (partition 1), donde se transforman (BPMN/DMN/Forms parsing), se validan, se persisten, y luego se distribuyen a todas las demás particiones con dedup y retry automático.

Flujo general

flowchart TD
    Client[Cliente] --> GW[Gateway]
    GW --> P1[Partition 1<br/>DEPLOYMENT_PARTITION]
    P1 --> Proc[DeploymentCreateProcessor.processNewCommand]
    Proc --> Auth[Authorization check<br/>CREATE on RESOURCE]
    Auth --> Trans[DeploymentTransformer.transform]
    subgraph Transform
        BPMN[Parse BPMN XML --> ExecutableProcess]
        DMN[Parse DMN XML --> Decisions + Requirements]
        Forms[Parse Forms JSON --> Form metadata]
        Val[Validate all resources]
    end
    Trans --> Transform
    Transform --> Keys[Generate keys<br/>processDefinitionKey, etc.]
    Keys --> Events[Write CREATED events per resource]
    Events --> Dist[Distribute to all other partitions]
    Dist --> Subs[Manage start event subscriptions]

DeploymentCreateProcessor

Implementa DistributedTypedRecordProcessor<DeploymentRecord>, una interfaz especializada que distingue entre:

  • processNewCommand(): procesa commands que llegan por primera vez (desde el cliente via Gateway)
  • processDistributedCommand(): procesa commands que llegan distribuidos desde otra partición

Fase 1: Authorization

AuthorizationRequest.builder()
    .command(command)
    .resourceType(AuthorizationResourceType.RESOURCE)
    .permissionType(PermissionType.CREATE)
    .tenantId(command.getValue().getTenantId())
    .newResource()
    .build();

Si el check falla, el command se rechaza con el tipo de rejection correspondiente.

Fase 2: Transformation

DeploymentTransformer procesa cada resource del deployment:

  • BPMN: pasa por el pipeline de 5 pasos (ver concepts/bpmn-execution-model) — XML → BpmnModelInstance → validation → ExecutableProcess → cache
  • DMN: parseo via dmn-scala, genera DecisionRequirementsRecord + DecisionRecord por cada decisión
  • Forms: parsea JSON, extrae metadata (formId, formVersion)
  • Resources: recursos genéricos sin transformación especial

Para cada resource procesado, se emiten events de creación: - PROCESS:CREATED - DECISION:CREATED / DECISION_REQUIREMENTS:CREATED - FORM:CREATED - RESOURCE:CREATED

Fase 3: Distribution

Después de la transformación exitosa, el deployment se distribuye a todas las demás particiones:

flowchart LR
    P1[Partition 1 leader] --> CDB[CommandDistributionBehavior]
    CDB -->|DEPLOYMENT:DISTRIBUTE| P2[Partition 2]
    CDB -->|DEPLOYMENT:DISTRIBUTE| P3[Partition 3]
    CDB -->|DEPLOYMENT:DISTRIBUTE| PN[Partition N]

Cada partición receptora: 1. Verifica dedup: deploymentState.hasStoredDeploymentRecord(key) — si ya existe, ALREADY_EXISTS rejection 2. Procesa y almacena el deployment localmente 3. Gestiona start event subscriptions 4. Envía DEPLOYMENT_DISTRIBUTION:COMPLETE acknowledgment

Fase 4: Start Event Subscription Management

StartEventSubscriptionManager.tryReOpenStartEventSubscription() maneja las subscriptions de start events:

  • Message start events: crea subscriptions para recibir mensajes que inicien nuevas instancias
  • Signal start events: registra para recibir broadcasts de señales
  • Timer start events: crea timer instances para triggers periódicos

Cuando se deploya una nueva versión de un proceso, las subscriptions de la versión anterior se cancelan y se crean nuevas.

Retry y redistribution

DeploymentRedistributionScheduler es un listener que periódicamente reintenta la distribución de deployments que no fueron acknowledged:

  • Revisa el DeploymentState para encontrar distribuciones pendientes
  • Reenvía DEPLOYMENT:DISTRIBUTE commands a las particiones que no respondieron
  • Esto garantiza eventual consistency incluso si una partición estaba temporalmente no disponible

Error handling

El processor maneja dos tipos de errores de forma diferenciada:

Error Tipo Acción
ResourceTransformationFailedException BPMN/DMN inválido INVALID_ARGUMENT rejection
TimerCreationFailedException No se puede crear timer de start event PROCESSING_ERROR rejection

En ambos casos, se invalidan los caches para evitar estado inconsistente (si ya se había generado metadata pero no los events).

Implicaciones para simplificación

Para un MVP single-partition:

  1. Eliminar distribution: no hay otras particiones a las que enviar
  2. Simplificar transformer: solo BPMN (sin DMN, forms, generic resources inicialmente)
  3. Eliminar dedup check: no hay distributed commands
  4. Mantener start event subscriptions: esencial para message/timer start events
  5. Simplificar authorization: RBAC simple en vez del AuthorizationRequest builder

El deployment se reduce a: parse BPMN → validate → store en PostgreSQL → crear subscriptions.