La elección entre Two-Phase Commit (2PC) y Saga empieza por una regla de negocio: qué debe cumplirse siempre y qué puede resolverse en varias etapas. Evitar vender dos veces un asiento exige una decisión atómica sobre el inventario. Coordinar esa reserva con un proveedor de pagos exige además resolver efectos que cruzan un límite de sistema.
La escala influye en el coste de coordinar esas operaciones, pero el número de servicios no decide por sí solo el protocolo. Primero hay que establecer el límite de la transacción y los estados que el producto puede admitir.
Empezar por una transacción local
Cuando los datos que deben cambiar juntos caben en una misma base de datos, una transacción local suele ser la opción más sencilla. Las restricciones y las transiciones condicionales pueden proteger la regla de negocio sin coordinar un commit entre varios servicios.
La atomicidad garantiza que los cambios de esa transacción se confirman juntos o se revierten. El aislamiento determina cómo interactúan transacciones concurrentes; debe elegirse junto con los bloqueos o condiciones necesarios para proteger la regla. Usar una transacción no convierte automáticamente cualquier secuencia de lecturas y escrituras en una operación serializable.
Esa transacción tampoco incluye un cobro externo, un correo enviado o una llamada a otro servicio. Aunque el inventario y la orden compartan una base de datos, coordinar efectos externos sigue necesitando un flujo de recuperación.
Qué garantiza 2PC
2PC coordina una decisión de commit o abort entre participantes que pueden preparar su trabajo y conservarlo de forma durable. Tiene dos fases:
- Preparar. El coordinador pregunta si cada participante puede confirmar. Un participante que vota a favor persiste el estado necesario para completar la transacción después y puede retener bloqueos u otros recursos.
- Decidir. Si todos votan a favor, el coordinador decide commit; si no puede completar la preparación, puede decidir abort. Persiste la decisión antes de comunicarla y los participantes la aplican para completar la transacción.
La propiedad central es que los participantes recuperables sigan la misma decisión. 2PC aporta coordinación de atomicidad; el aislamiento y las garantías de lectura dependen del sistema que lo implementa. El protocolo, por sí solo, no promete lecturas linearizables ni que todos los observadores vean el resultado al mismo instante.
Su dificultad es el bloqueo. Si un participante ya preparó y pierde contacto con el coordinador, puede quedar sin saber si debe confirmar o abortar. Un timeout local no siempre permite abortar con seguridad: la decisión de commit podría existir y haber llegado a otros participantes.
El trabajo que necesita los recursos retenidos puede esperar hasta que se recupere la decisión. Un participante lento también prolonga la coordinación. El impacto depende de los datos afectados, la contención y la recuperación; un fallo no necesariamente detiene todo el sistema.
2PC puede encajar cuando todos los participantes soportan el protocolo y el coste de coordinación es aceptable. Un conjunto de APIs HTTP arbitrarias, especialmente un proveedor de pagos externo, normalmente no ofrece esa capacidad de preparación. Si una base de datos distribuida gestiona el commit internamente, sigue siendo necesario revisar su aislamiento, latencia y comportamiento durante fallos; delegar la implementación no elimina esos compromisos.
Qué garantiza una Saga
Una Saga divide el flujo en transacciones locales. Cada paso confirma su propio cambio y, cuando hace falta, dispone de una acción compensatoria. Así puede coordinar servicios que no comparten una transacción de base de datos.
En una compra, el flujo puede reservar inventario, autorizar un pago y confirmar la orden. Si la confirmación resulta imposible, puede anular la autorización y liberar la reserva. Si el dinero ya se cobró, la acción correspondiente puede ser un reembolso. Cada transición valida qué ocurrió antes y si todavía tiene derecho a modificar ese estado.
Una compensación es una nueva operación de negocio. Un reembolso no borra el cobro original del historial y un correo enviado no puede recuperarse. Algunas acciones son irreversibles; otras solo permiten una reparación parcial. Conviene ordenar los pasos para posponer efectos difíciles de compensar cuando el producto lo permita.
La Saga tampoco proporciona aislamiento global. Otros usuarios pueden observar una reserva pendiente mientras el pago se resuelve, y dos flujos pueden competir por el mismo inventario. Las transacciones locales, los identificadores de reserva, las versiones y las condiciones de actualización deben proteger esas invariantes.
La consistencia eventual describe un modelo en el que réplicas o proyecciones pueden retrasarse y converger si las actualizaciones pueden propagarse y dejan de producirse nuevos cambios. En una Saga, resolver el proceso de negocio requiere además reintentos, reconciliación y compensaciones que puedan progresar. El tiempo, por sí solo, no repara una operación incompleta.
El flujo necesita estados explícitos, como pendiente, confirmado, compensando, cancelado o pendiente de intervención. Una compensación también puede fallar; debe quedar registrada para reintentarse o para que alguien pueda resolverla.
Coreografía y orquestación
En la coreografía, cada participante reacciona a eventos y publica el resultado de su transacción. Esto funciona bien cuando las responsabilidades son claras y las dependencias entre eventos son fáciles de seguir. La trazabilidad debe permitir reconstruir el flujo entre servicios y saber quién inicia una compensación.
En la orquestación, un componente conserva el estado del flujo, envía comandos y decide qué paso sigue o qué debe compensarse. Es útil cuando hay ramificaciones, plazos, varios caminos de recuperación o una necesidad clara de consultar el estado completo de la operación.
No existe un máximo técnico de tres o cuatro pasos para usar coreografía. El criterio es cuánto cuesta comprender, cambiar y recuperar el flujo. La orquestación suele facilitar ese trabajo a medida que crecen sus dependencias.
El orquestador necesita estado durable y capacidad de recuperación. Si se reinicia, debe saber qué solicitó, qué resultados recibió y qué sigue pendiente. Un motor de workflows puede ayudar a persistir y reanudar ese trabajo, pero los comandos y las actividades todavía pueden repetirse; sus efectos requieren idempotencia.
Outbox transaccional
La escritura dual aparece cuando un servicio guarda un cambio en su base de datos y publica un evento en un broker mediante dos operaciones independientes. Si confirma el dato y cae antes de publicar, otros servicios no se enteran. Si publica primero y luego falla la transacción, pueden reaccionar a un cambio que nunca ocurrió.
La outbox resuelve ese hueco dentro del servicio: el cambio de estado y el evento pendiente se escriben en la misma transacción local. Si la transacción confirma, ambos quedan persistidos; si aborta, ninguno queda guardado.
Un proceso independiente, el relay, lee la outbox mediante polling o captura de cambios (CDC) y publica los eventos. En una Saga orquestada, uno de esos eventos puede comunicar el resultado de un paso:
--- config: look: handDrawn --- flowchart TD orchestrator["Orquestador<br/>Estado durable de la Saga"] -->|"Comando con identificador"| participant["Servicio participante"] participant --> database[("Transacción local<br/>Estado + evento en outbox")] database --> relay["Relay<br/>Polling o CDC"] relay -->|"Publicar evento"| broker["Broker de mensajes"] broker -->|"Resultado; puede repetirse"| orchestrator orchestrator -->|"Fallo definitivo"| compensation["Compensar pasos completados<br/>Registrar y reintentar"]
El relay puede caer después de publicar y antes de registrar que terminó. Al recuperarse, puede enviar el mismo evento otra vez. La outbox hace atómica la persistencia del dato y del evento pendiente; la entrega al broker y el procesamiento posterior todavía pueden repetirse.
Cada evento necesita una identidad estable. El consumidor puede registrar el evento procesado junto con su cambio local, en una misma transacción, para deduplicar entregas. Cuando el orden importa, hacen falta versiones o secuencias por entidad. Los efectos externos requieren además coordinación e idempotencia según las garantías de su proveedor.
El retraso de la outbox, los intentos fallidos y los mensajes pendientes deben ser observables. Una publicación que falla indefinidamente necesita reparación o intervención; guardar el evento no basta para que llegue a su destino.
Cómo elegir
La decisión debe asociarse a una operación concreta. Una actualización contable puede necesitar una transacción local estricta, mientras que una transferencia hacia otro sistema requiere estados de seguimiento y reconciliación. Un asiento puede asignarse con una transición atómica en la base de inventario y comprarse mediante una Saga que coordina reserva, pago y confirmación.
| Aspecto | 2PC | Saga |
|---|---|---|
| Coordinación | Decisión común de commit o abort. | Secuencia de transacciones locales. |
| Participantes | Deben soportar preparación y recuperación. | Deben exponer pasos y recuperación de negocio. |
| Estados intermedios | El aislamiento determina qué puede observarse. | Los pasos confirmados pueden ser visibles antes de terminar. |
| Recuperación | Recuperar y aplicar la decisión durable. | Reintentar, reconciliar y compensar. |
| Coste principal | Acoplamiento y recursos retenidos. | Complejidad del flujo y sus compensaciones. |
Empieza por una transacción local si puede contener la invariante. Evalúa 2PC cuando necesitas una decisión atómica entre participantes compatibles y aceptas su coste operativo. Usa una Saga cuando el producto admite etapas y has definido qué hacer con cada efecto confirmado o incierto.
Para flujos complejos, una Saga orquestada con operaciones idempotentes y outbox suele ser una base útil. Si una regla no puede tolerar un estado intermedio, hay que resolverla dentro de un límite transaccional adecuado o rediseñar ese límite; añadir compensaciones no sustituye esa garantía.
Qué verificar
En 2PC, las pruebas deben detener al coordinador después de preparar y antes o después de persistir la decisión. Hay que comprobar la recuperación de los participantes y cuánto tiempo retienen recursos.
En una Saga, conviene duplicar comandos y eventos, invertir el orden de notificaciones y reiniciar el orquestador entre pasos. También hay que probar resultados inciertos y compensaciones que fallan. Un timeout después de pedir un cobro exige reconciliar su resultado antes de asumir que no ocurrió.
Para la outbox, la prueba central detiene el relay después de publicar y antes de marcar el evento como enviado. El consumidor debe tolerar la repetición. Las señales útiles son el tiempo de las operaciones pendientes, el retraso de publicación, las compensaciones sin resolver y el trabajo que requiere intervención.
El diseño queda conectado con un caso concreto en SD: Sistema de boletaje, donde el inventario conserva su regla local mientras la compra coordina pagos y recuperación.