Process Instance Migration
Migration permite mover una process instance en ejecución de una versión a otra. Implementa 4 behaviors especializados (CatchEvents, Jobs, UserTasks, SequenceFlows). Soporta 18 element types, no soporta compensation/throw/escalation/error events. Requiere mapping explícito source→target. BFS iterativo evita stackoverflow en procesos profundos. Para MVP: clasificada como eliminable — alternativa simple es dejar v1 terminar mientras v2 sirve nuevas instancias.
¿Qué es migration?¶
Cuando un proceso se modifica y se redeploya como v2, las instancias activas de v1 siguen ejecutándose con v1. Migration permite moverlas a v2 en runtime.
flowchart TD
V1["Process v1 — Instance #123<br/>(en paso 'task_review')"]
V2["Process v2 — Instance #123<br/>(continúa en 'task_review_v2')"]
V1 -->|"MIGRATE command con mapping:<br/>task_review → task_review_v2<br/>task_approve → task_approve_v2"| V2
La instance NO se termina y recrea — su state se reasigna a la nueva process definition.
Componentes especializados¶
ProcessInstanceMigrationMigrateProcessor coordina 4 behaviors:
| Behavior | Maneja |
|---|---|
ProcessInstanceMigrationCatchEventBehavior |
Message subscriptions, timers, signals, compensation subscriptions |
ProcessInstanceMigrationJobBehavior |
Jobs activos de service tasks |
ProcessInstanceMigrationUserTaskBehavior |
User tasks pendientes |
ProcessInstanceMigrationSequenceFlowBehavior |
Sequence flows en curso |
Cada tipo de elemento BPMN requiere lógica de migration diferente — por eso la separación.
Element types soportados¶
PROCESS SERVICE_TASK
USER_TASK SUB_PROCESS
CALL_ACTIVITY INTERMEDIATE_CATCH_EVENT
RECEIVE_TASK EVENT_SUB_PROCESS
EXCLUSIVE_GATEWAY EVENT_BASED_GATEWAY
BUSINESS_RULE_TASK SCRIPT_TASK
SEND_TASK MULTI_INSTANCE_BODY
PARALLEL_GATEWAY INCLUSIVE_GATEWAY
AD_HOC_SUB_PROCESS AD_HOC_SUB_PROCESS_INNER_INSTANCE
18 element types. El resto (boundary events, throw events, end events, etc.) son rechazados si están "activos".
Intermediate catch events soportados¶
Solo 4 tipos: - Message - Timer - Signal - Conditional
Otros (error, escalation, compensation) no son migrables.
Flujo del processor¶
1. Validate processInstance exists
2. Authorization check (UPDATE_PROCESS_INSTANCE on bpmnProcessId)
3. Validate mapping (no duplicate source IDs)
4. Validate target process definition exists
5. Validate no pending start event subscriptions for target
6. Validate all referred element IDs exist in both processes
7. Build mapping table: source_element_id → target_element_id
8. BFS through all element instances (process + children):
- For each: tryMigrateElementInstance(...)
├── Update process definition key
├── Update element ID si hay mapping
├── Delegate a behavior según element type
└── Update children references
9. Emit MIGRATED event
Mapping Instructions¶
El cliente provee mappings explícitos:
{
"processInstanceKey": 123,
"targetProcessDefinitionKey": 456,
"mappingInstructions": [
{"sourceElementId": "task_review", "targetElementId": "task_review_v2"},
{"sourceElementId": "gateway_decision", "targetElementId": "gateway_decision_v2"}
]
}
Regla: si un element ID es el mismo en source y target, NO necesita mapping. Solo se mappean elementos con IDs distintos.
BFS iterativo (no recursión)¶
// avoid stackoverflow using a queue to iterate over the descendants instead of recursion
final var elementInstances = new ArrayDeque<>(List.of(processInstance));
while (!elementInstances.isEmpty()) {
final var elementInstance = elementInstances.poll();
tryMigrateElementInstance(...);
elementInstances.addAll(elementInstanceState.getChildren(elementInstance.getKey()));
}
Decisión clave: procesos pueden tener nesting profundo (sub-process dentro de sub-process dentro de multi-instance). Recursión causaría stackoverflow. ArrayDeque + while loop es safe.
Precondiciones que pueden causar rejection¶
Del análisis del código:
- Process instance no existe: NOT_FOUND
- Process instance baneada: rejection
- Authorization falla: NOT_FOUND (no FORBIDDEN — opacidad)
- Target process definition no existe: NOT_FOUND
- Duplicate source element IDs en mapping: INVALID_ARGUMENT
- Source o target element ID no existe en sus procesos: INVALID_ARGUMENT
- Element type no soportado: INVALID_STATE
- Element types source/target incompatibles: INVALID_STATE
- Pending message subscriptions del target process: INVALID_STATE
- Element está en estado intermedio (ACTIVATING, COMPLETING): INVALID_STATE
Authorization¶
AuthorizationRequest.builder()
.resourceType(PROCESS_DEFINITION)
.permissionType(UPDATE_PROCESS_INSTANCE)
.tenantId(currentTenant)
.addResourceId(currentBpmnProcessId)
Permisos necesarios: UPDATE_PROCESS_INSTANCE sobre el bpmnProcessId actual (no el target). Esto refleja que migration es una modificación a una instance existente.
Lo que NO se puede migrar¶
| Cosa | Razón |
|---|---|
| Elementos en estado intermedio (ACTIVATING, COMPLETING) | State inconsistente, esperar a estado estable |
| Boundary events activos con timer corriendo | State complejo de migrar |
| Compensation handlers ejecutándose | State complejo |
| Error events activos | No en supported intermediate types |
| Escalation events | No en supported types |
| Throw events | Por definición no esperan |
| End events | Instance ya terminó |
| Process baneado | Process instance broken |
Implicaciones para el MVP¶
Decisión: SKIP migration en MVP¶
En analysis/mvp-feature-matrix está clasificado como eliminable. Razones:
- Implementación muy compleja: 4 behaviors, ~2000 LOC, casos edge difíciles
- Casos de uso limitados: solo workflows long-running se benefician
- Alternativa válida: dejar v1 terminar, v2 sirve nuevas
- Testing complejo: combinaciones de element types × estados explotan
Alternativa simple¶
flowchart LR
subgraph V1Lane["v1 (instances cerca de completar)"]
V1Active[v1 active] --> V1Done[completed]
end
subgraph V2Lane["v2 (instances nuevas)"]
V2Deployed[v2 deployed] --> V2Active[v2 active]
end
V1Active -.->|tiempo| V2Deployed
Cuando se deploya v2: - Nuevas instancias van a v2 (latest version) - v1 instances activas terminan en v1 - Eventualmente todas las v1 terminan, se puede limpiar la definition
Esto cubre 80% de casos reales sin código de migration.
Cuándo SÍ implementar migration¶
- Workflows long-running (días/semanas)
- Bugfix urgente que requiere actualizar instances activas
- Compliance changes con deadline
Estos son casos de empresa grande, no MVP típico.
Si se implementa: subset mínimo¶
Subset razonable para reducir ~80% complejidad:
SUPPORTED_ELEMENT_TYPES_MVP = {
SERVICE_TASK,
USER_TASK,
EXCLUSIVE_GATEWAY,
PARALLEL_GATEWAY,
INTERMEDIATE_CATCH_EVENT // solo message
}
NO SOPORTAR:
- Sub-processes (explosion de complejidad)
- Multi-instance (otra explosion)
- Boundary events
- Compensation
- Inclusive gateways
Esto cubre service tasks y user tasks en procesos lineales con gateways — el caso 80%.
Lecciones de diseño¶
- BFS iterativo: para cualquier tree traversal, evitar recursión
- Behaviors especializados: separar logic por element type
- Validación temprana: lista larga de preconditions, fail fast
- Mapping explícito: no asumir auto-match — el cliente sabe la intención
- Single MIGRATED event: simplifica audit log