Acabas de dividir tu monolito en microservicios y la latencia ha mejorado, pero te enfrentas a una pesadilla silenciosa: la inconsistencia de datos. En un sistema monolítico, @Transactional era tu red de seguridad; si fallaba el pago, el pedido no se creaba. En una arquitectura de microservicios (MSA), tienes una base de datos por servicio. Si el servicio de Pedidos confirma (commit) pero el servicio de Pagos falla por un timeout o saldo insuficiente, tienes un pedido "fantasma" sin pago asociado. Bienvenido al infierno de la consistencia eventual.
Por qué el Two-Phase Commit (2PC) no es la solución
En una migración reciente de una plataforma Fintech procesando más de 5,000 TPS, intentamos inicialmente usar XA Transactions (Two-Phase Commit). El resultado fue catastrófico. El protocolo de bloqueo distribuido aumentó la latencia del sistema de 50ms a 800ms, y durante picos de tráfico, los bloqueos en la base de datos provocaron deadlocks masivos.
La Solución: Patrón Saga y Transacciones Compensatorias
El patrón Saga divide una transacción distribuida en una secuencia de transacciones locales. Si una transacción local falla, la Saga ejecuta transacciones compensatorias para deshacer los cambios realizados por las transacciones anteriores. No es un rollback automático de base de datos; es una lógica de negocio inversa.
Coreografía vs. Orquestación
Existen dos formas de implementar Sagas:
- Coreografía: Los servicios se comunican a través de eventos. No hay un coordinador central. Es bueno para sistemas simples, pero se vuelve inmanejable ("Event Hell") cuando hay más de 4 servicios involucrados.
- Orquestación: Un servicio central (Orquestador) le dice a los participantes qué hacer. Esta es la estrategia preferida para flujos complejos y críticos.
Implementación: Saga Orquestada con Spring Boot
A continuación, presento una implementación simplificada de un Orquestador de Pedidos. Observa cómo manejamos explícitamente el flujo de compensación cuando falla el inventario o el pago.
// OrderSagaOrchestrator.java
@Service
@RequiredArgsConstructor
public class OrderSagaOrchestrator {
private final OrderService orderService;
private final PaymentService paymentService;
private final InventoryService inventoryService;
public void createOrder(OrderRequest request) {
// 1. Paso 1: Crear Pedido (Estado: PENDING)
Long orderId = orderService.createOrder(request);
try {
// 2. Paso 2: Reservar Inventario
// Si falla, lanza excepción y va al catch
inventoryService.reserve(orderId, request.getItems());
// 3. Paso 3: Procesar Pago
paymentService.processPayment(orderId, request.getTotalAmount());
// 4. Confirmar Pedido
orderService.confirmOrder(orderId);
} catch (Exception e) {
// ERROR DETECTADO: Iniciar Compensación
log.error("Fallo en la Saga del Pedido {}. Iniciando rollback...", orderId, e);
compensateOrder(orderId);
throw new SagaExecutionException("No se pudo procesar el pedido", e);
}
}
private void compensateOrder(Long orderId) {
// Lógica de compensación idempotente
try {
// Deshacer pago si se realizó (verificación interna)
paymentService.refundPayment(orderId);
// Liberar inventario
inventoryService.releaseInventory(orderId);
// Marcar pedido como FAILED
orderService.cancelOrder(orderId);
} catch (Exception e) {
// ALERTA CRÍTICA: La compensación falló.
// Esto requiere intervención manual o un job de reconciliación.
log.error("CRITICAL: Fallo en compensación para Pedido {}", orderId, e);
}
}
}
Verificación de Rendimiento: Monolito vs Saga
El cambio a Saga introduce complejidad, pero desbloquea la escalabilidad. Aquí están los datos comparativos de nuestra prueba de carga.
| Métrica | Monolito (ACID) | Microservicios (2PC/XA) | Microservicios (Saga) |
|---|---|---|---|
| Throughput (TPS) | 1,200 | 450 (Bloqueos) | 5,500+ |
| Latencia P99 | 120ms | 950ms | 210ms |
| Consistencia | Inmediata (Strong) | Inmediata (Strong) | Eventual |
| Complejidad de Código | Baja | Media | Alta (Compensaciones) |
Conclusión
Implementar el Patrón Saga no es "gratis". Requiere diseñar cada servicio pensando en que las operaciones pueden fallar y deben ser revertidas. Sin embargo, para sistemas de alta concurrencia donde la disponibilidad es prioritaria sobre la consistencia inmediata, es la única arquitectura viable. Empieza con Coreografía para flujos simples, pero migra rápidamente a Orquestación en cuanto la lógica de negocio involucre más de tres microservicios.
Post a Comment