orquestador/specs/RECONCILIATION.md

78 lines
4.3 KiB
Markdown

# Reintentos y conciliación — T12
## Reintentos durables
Los fallos TRANSIENT de READ_ONLY e IDEMPOTENT_WRITE se reencolan en RETRY_WAIT.
La programación, el fallo del intento y RETRY_SCHEDULED se confirman juntos, con
fencing y tiempo de PostgreSQL. No hay un sleep del worker ni una cola en memoria.
La solicitud permanece ocupada y conserva estado/datos de negocio.
La política publicada limita a tres intentos totales por acción (incluido el
primero). Defaults: base exponencial 2 s/4 s, jitter entre 80 % y 120 %, limitado
por maxBackoff. Se respeta Retry-After numérico o fecha HTTP, acotado a 120 s;
valores inválidos se ignoran. El mayor de backoff con jitter y Retry-After define
el retraso. Los checkpoints SUCCEEDED no se repiten y la clave externa
executionId:actionId permanece estable.
Al agotar intentos READ_ONLY sin escrituras pendientes se termina FAILED.
Escrituras inciertas, incluyendo una IDEMPOTENT_WRITE agotada, mantienen
RECONCILIATION_REQUIRED. Una operación NON_IDEMPOTENT_WRITE RUNNING al perder
lease jamás se repite automáticamente; incluso una caída antes del envío exige
conciliación si no existe prueba durable de ausencia de efecto. El límite de
intentos no puede liberar esa incertidumbre.
## Resolución por operador
POST /api/v1/admin/executions/{id}/resolve requiere:
- FLOW_ADMIN_ENABLED=true y runtime DB restringido.
- JWT válido, rol EXECUTION_OPERATOR, ámbito en ADMIN_BUSINESS_SCOPES y coincidente
con la solicitud. El operador puede diferir de su propietario.
- Idempotency-Key obligatorio y comando ReconciliationCommand del contrato.
- Ejecución RECONCILIATION_REQUIRED para una resolución nueva.
RESUME registra resultados verificados de acciones existentes y reencola PENDING.
La salida debe cumplir el contrato/perfil fijado y el efecto del checkpoint debe
coincidir con el catálogo confiable. No se permiten acciones inventadas, IDs
duplicados, incertidumbres pendientes ni sobrescribir un checkpoint exitoso con
otro resultado. Una acción ya exitosa puede incluirse únicamente con el mismo
resultado; no vuelve a ejecutarse. No se inyectan resultados manuales de
REGISTER_DOCUMENT: sus referencias deben provenir de la verificación documental.
La solicitud debe conservar estado y revisión aceptados.
CLOSE_WITHOUT_TRANSITION exige ABSENT_VERIFIED o COMPENSATED_VERIFIED y cierra
FAILED sin transición, cambios de datos ni incremento de revisión. ABSENT_VERIFIED
se rechaza cuando existe una escritura SUCCEEDED durable; en ese caso corresponde
documentar compensación. La solicitud vuelve a admitir eventos.
La evidencia exige reference y reason no vacíos. **La verificación la realiza el
operador fuera del servicio**: estos campos son una declaración autorizada y
auditada, no una consulta automática al proveedor. Usar una referencia a un caso
verificable; no adjuntar documentos personales, JWT o secretos.
Resolución, checkpoints, cola y EXECUTION_RESOLVED comparten transacción. El ledger
es append-only. Idempotencia por executionId/clave; replay exige mismo comando
canónico y mismo sujeto operador, y conserva respuesta original incluso tras
completar la ejecución. Otro comando u operador con esa clave recibe 409.
Dos operadores con claves distintas no pueden aplicar simultáneamente dos
resoluciones nuevas. La resolución incrementa fencing_token.
No existe campo para elegir un estado destino. El worker continúa aplicando la
definición publicada y confirma la transición de forma normal.
## Pruebas
RetryPolicyTest cubre límites y parsing. RetryReconciliationIT usa HTTP/JWT,
PostgreSQL real y triggers de fallos de auditoría. CrashWorkerProcess es una JVM
exclusiva de test que termina con Runtime.halt antes/después de llamar a un mock
HTTP: el proceso padre reconstruye el worker y recupera desde DB. El mock registra
un efecto lógico por clave; no acredita idempotencia de un proveedor real.
WorkerIT conserva las pruebas de lease vencido, worker obsoleto, fallo después del
checkpoint y rollback de confirmación final. HttpIntegrationIT ahora espera
RETRY_WAIT en timeout/503 y FAILED en salida inválida.
Ejecutar mvnw.cmd clean verify -Pmigration-tests-external con MIGRATION_TEST_DB_*
en el proceso y base orquestador_test. Sin ese perfil se requiere Docker para
Testcontainers. No se despliega ni se utiliza un proveedor real.