Two-Phase Commit en C#: Guía Completa

Introducción: consistencia distribuida

2PC coordina múltiples recursos para confirmar o abortar en conjunto. Fase 1: Prepare. Coordinator pregunta a todos participantes: ¿puedes committear? Espera votes. Fase 2: Commit/Abort. Si todos sí, commit. Si alguno no, abort. Garantía: ACID distribuido. Costo: bloqueos durante ambas fases. Si network falla, deadlock potencial. El algoritmo es fundamental para transacciones críticas (banca, bolsa). Pero lento y frágil en sistemas altamente distribuidos. Hay alternativas como Sagas para consistencia eventual.

2PC
Prompt: two-phase commit workflow, minimal style.

1. Naturaleza: consenso grupal

Una junta directiva vota una decisión. Primero dialogan, todos dicen sí o no. Si todos sí, ejecutan. Si alguno no, rechazan. 2PC es esto. Prepare = diálogo. Vote = consenso. Commit = ejecución. Ventaja: decisión es vinculante, todos comprometidos. Desventaja: si alguien se opone, todos pierden tiempo. En vida real, juntas ejecutivas usan variantes: mayoría vs consenso. En sistemas, 2PC requiere consenso total. Por eso es pesado. Naturaleza muestra que consenso es humano, pero costoso.

Consenso
Prompt: group consensus, soft illustration.

2. Mundo Real: transferencia bancaria internacional

Banco A envía $1000 a Banco B. 2PC: Banco A prepara (reduce balance, lo reserva). Banco B prepara (está listo para recibir). Si ambos votan sí, A commits y B commits simultáneamente. Dinero transferido. Si B no puede (limite de cuenta), ambos abortan, A restablece balance. Sin 2PC, A reduce pero B nunca recibe (dinero perdido). O B recibe pero A no se reduce (dinero creado). Desastre. Bolsa de valores: vender acciones y recibir dinero. Simultáneamente en dos bolsas. 2PC garantiza: ambas transacciones o ninguna. Es por eso que bancos y bolsas requieren 2PC.

Transferencia
Prompt: bank transfer agreement, flat infographic.

3. Implementación en C#: Código Paso a Paso

var coordinator = new TransactionCoordinator(participants);
var votes = await coordinator.Prepare();

if (votes.All(v => v == VoteType.Yes))
{
    await coordinator.Commit(); // Todos ejecutan
}
else
{
    await coordinator.Abort(); // Todos revierten
}

El Coordinator mantiene log de decisión. Si crash, recupera from log. Participants deben ser idempotentes (guardar con mismo ID dos veces = una sola ejecución). Timeouts previenen deadlocks (si participant no responde, asumir no y abortar). En .NET, esto se implementa con frameworks como Dapr o NServiceBus. Directo es tedioso y error-prone.

4. 2PC vs Saga: consistencia vs disponibilidad

2PC busca consistencia fuerte (ACID). Bloquea hasta todo confirmado. Saga tolera consistencia eventual (se llama saga, cada paso es independiente, si uno falla, compensa). 2PC: A y B se actualizan simultáneamente o ninguno. Saga: A se actualiza, luego B. Si B falla, compensate A. Ambos correctos si diseño es correcto. 2PC para datos críticos (dinero). Saga para datos no-críticos o distribuidos (microservicios). El trending moderno es Saga porque 2PC no escala en sistemas distribuidos (network partitions => deadlocks).

5. Diagrama UML y Flujo Temporal

2PC tiene Coordinator y Participants. Flujo: Coordinator envía Prepare a todos. Cada Participant responde Yes/No. Coordinator recolecta votes. Si todos Yes, envía Commit. Si algún No, envía Abort. Diagram muestra timeline: T1 prepare, T2-T5 participants responden, T6 coordinator decide, T7-T10 ejecución. El bloqueo es desde T1 a T10. Durante este tiempo, recursos están locked. Eso es el costo. Con muchos participants o red lenta, este período es largo.

UML 2PC
Prompt: UML two-phase commit, clean vector.
Flow 2PC
Prompt: 2PC flow diagram, minimal infographic.

⚠️ Cuándo NO Usar 2PC

  • Latencia crítica: Si <100ms es requerido, 2PC locking es prohibitivo.
  • Sistema distribuido puro: Network partitions rompen 2PC. Usa Saga o Quorum.
  • Escalabilidad masiva: Con 100+ nodes, coordinación es cuello de botella.

💪 Ejercicio

Implementa 2PC para transferencia entre dos bases de datos.

DB A y DB B. Transferir $1000 de cuenta A en DB A a cuenta B en DB B. Usa 2PC: Prepare (resérvalo en ambas DBs), Commit si ambas lisas. Simula network failure (DB B no responde en Prepare) => Abort, ambas revierten.

Conclusión

2PC garantiza consistencia ACID distribuida. El costo es bloqueos y complejidad. Necesario para datos críticos (dinero). Pero ha sido reemplazado por Sagas en arquitecturas cloud-native. Es fundamental entender porque aún se usa y por qué tiene limitaciones.