Try-Confirm-Cancel

Qué es

Try-Confirm-Cancel (TCC) es un protocolo de negocio que separa obtener recursos provisionalmente de decidir su utilización definitiva. Sus tres operaciones son Try, para reservar; Confirm, para finalizar la reserva; y Cancel, para liberarla.

Son tres operaciones con dos etapas de coordinación: primero se reúnen las reservas, después se confirma o cancela el conjunto. La aplicación debe definir qué representa cada reserva y cómo puede terminar. No basta con renombrar tres endpoints de una operación que ya produce un efecto irreversible.

La documentación de Oracle presenta TCC como coordinación de recursos reservados y deja su significado a cada aplicación. El contrato general no depende de los verbos HTTP ni de ese producto concreto. Try-Confirm/Cancel Transaction Protocol.

Qué problema resuelve

Una operación puede necesitar recursos de varios servicios sin mantener abiertas todas sus transacciones de base de datos. Verificar «hay dos asientos libres» y comprarlos más tarde es insuficiente: otra compra podría consumirlos entre ambas acciones.

TCC transforma la disponibilidad comprobada en un compromiso provisional identificable. Try limita qué pueden consumir otros actores; Confirm utiliza ese compromiso, y Cancel lo libera. El dominio tiene que ofrecer esta semántica: una API que solo cobra definitivamente o envía un paquete no participa automáticamente.

Cómo funciona

Try: reservar

Cada participante verifica las condiciones que necesita garantizar y conserva una reserva durable. En un inventario, puede mover unidades de «disponibles» a «reservadas» mediante una transacción local. En ese commit se registran también identidad, propietario y estado de la reserva.

Try debe proteger el recurso frente a competencia posterior. Comprobar saldo sin retener fondos no prepara un Confirm fiable. Tampoco debería repetir la validación de disponibilidad en Confirm como si la reserva no existiera: este paso debe operar sobre lo que ya se obtuvo.

Confirm: finalizar

Cuando todas las reservas necesarias están aceptadas, la coordinación decide confirmarlas y conserva esa decisión y su progreso. Cada participante convierte su reserva en el efecto final mediante una transición local controlada.

Confirm puede fallar por comunicación o crash y necesita reintentos. Un fallo técnico no equivale a un rechazo de negocio nuevo. La decisión global tomada no puede cambiarse alegremente a Cancel mientras otras ramas ya confirmaron.

Cancel: liberar

Si no se obtienen todas las reservas, o el negocio abandona antes de confirmar, Cancel libera lo adquirido. Debe liberar únicamente los recursos de esa intención; no escribir un valor anterior que borre reservas ajenas.

Cancel también puede llegar a un participante cuyo Try no se ejecutó o cuya respuesta se perdió. El estado durable debe permitir distinguir esos casos y rechazar un Try tardío después de cerrar la intención.

Ejemplo

Una compra necesita dos asientos y una reserva de fondos. El identificador purchase-42 correlaciona las ramas; cada una conserva su propio estado y resultado.

Try     -> reserve seats + reserve funds
Confirm -> assign reserved seats + finalize reserved funds
Cancel  -> release seats + release funds

Las flechas resumen operaciones en participantes distintos, no una única transacción compartida. Si falla la reserva de fondos, se cancela la de asientos. Si todas aceptan y se decide Confirm, el coordinador continúa hasta resolver ambas confirmaciones.

En el servicio de asientos, repetir Confirm de purchase-42 devuelve el resultado ya registrado sin asignar otros asientos. Confirmar una reserva cancelada es un conflicto de estado, no una compra nueva. El servicio de fondos necesita las mismas propiedades; una pasarela externa sin reservas compatibles requiere otro contrato.

Idempotencia, orden y recuperación

Confirm y Cancel necesitan idempotency porque una respuesta perdida puede causar reenvíos. Try también necesita protección contra reservas duplicadas para la misma intención. La identidad estable no basta: el cambio de estado y el efecto correspondiente deben confirmarse juntos localmente.

deduplication puede reconocer comandos repetidos, pero un seen separado del cambio de reserva deja una nueva ventana de fallo. at-least-once entrega reintentos; no decide qué transición sigue siendo válida.

Dos carreras importantes son Cancel sin Try y Try después de Cancel. Un registro de intención cerrada puede impedir que ese Try tardío vuelva a inmovilizar recursos. La retención de ese registro debe cubrir los reintentos admitidos. Apache Seata documenta estos problemas como empty rollback, hanging e idempotencia; su solución concreta no forma parte del concepto. TCC: idempotence, dangling and empty rollback.

La recuperación necesita conocer participantes, reservas, decisión y resultados por rama. Registrar la intención y la identidad de una rama antes de invocar Try permite consultarla aunque se pierda su respuesta. Si el coordinador cae, otro proceso autorizado debe retomar la misma decisión. Consultar el estado de cada reserva ayuda a resolver respuestas inciertas sin crear nuevas intenciones.

Expiración y resultados mixtos

Una reserva retiene capacidad de negocio aunque la transacción local de Try ya haya terminado. Expirarla evita recursos abandonados, pero introduce una carrera con Confirm: otro participante puede haber confirmado mientras esta reserva vence y se cancela.

El contrato debe definir plazos, arbitraje local entre confirmar y expirar, y qué ocurre cuando la decisión global llega tarde. Un margen de tiempo reduce carreras; no constituye una prueba de atomicidad ante retrasos ilimitados. Oracle identifica la expiración durante Confirm como una causa de resultados heurísticos mixtos, que pueden necesitar intervención. Transaction Manager for Microservices: Heuristic Outcomes.

Una vez mezcladas confirmaciones y cancelaciones, repetir mensajes puede no alcanzar para cumplir la intención original. Hay que detectar el conflicto y aplicar una reparación de negocio explícita. No declarar «todo o nada garantizado» si se permite liberar reservas unilateralmente durante una confirmación pendiente.

Diferencias y trade-offs

two-phase-commit prepara recursos transaccionales y mantiene su promesa hasta conocer la decisión. TCC normalmente confirma transacciones locales para guardar reservas visibles en el dominio; no conserva necesariamente locks de base de datos entre fases. Sí retiene asientos, fondos o capacidad, y su disponibilidad depende de esa retención.

saga-pattern coordina efectos locales confirmados y compensaciones. TCC concentra la incertidumbre en reservas que todavía pueden confirmarse o liberarse. Una implementación puede combinar ambos modelos, pero Cancel de una reserva y compensar un efecto final no son la misma operación.

Usar TCC cuando el dominio puede reservar lo necesario, las confirmaciones son recuperables y se aceptan sus costos de capacidad, latencia y operación. No usarlo para simular reservas inexistentes ni cuando una política de expiración permite un resultado parcial inaceptable. atomic-commit delimita el contrato más fuerte que habría que demostrar; el nombre TCC por sí solo no lo establece. El mapa distributed-transactions permite comparar estas opciones según la regla de negocio.