Saltar al contenido
Underc0de /foro

Al confirmar un pedido guardás en la base y publicás un evento en Kafka. Si falla uno de los dos, ¿qué pasa y cómo lo resolvés?

$ interview --rol dev

Evalúa si el candidato reconoce el problema del dual write y conoce el patrón transactional outbox, la entrega at-least-once y los consumidores idempotentes.

Desarrollonivel senior#bases-de-datos#devops-cloud

Respuesta

El problema (dual write): la base y el broker son dos sistemas sin una transacción común. Pueden pasar dos cosas:
  • El commit se hace pero la publicación falla: el pedido existe y nadie se entera, por ejemplo no se factura.
  • Se publica pero el commit falla: se emite un evento de algo que nunca existió.
Reintentar no lo resuelve, porque el proceso puede morir justo entre las dos operaciones. Un 2PC/XA sería costoso, frágil y Kafka no participa de él.

Solución: transactional outbox.

Código: sql
BEGIN;
INSERT INTO orders (...) VALUES (...);
INSERT INTO outbox (id, aggregate_id, type, payload, created_at)
VALUES (gen_random_uuid(), :order_id, 'OrderConfirmed', :json, now());
COMMIT;
El evento queda guardado en la misma transacción que el cambio de negocio: o se guardan los dos, o ninguno.

Relay hacia el broker:
  • Polling: un worker lee las filas pendientes (con
    Código: text
    FOR UPDATE SKIP LOCKED
    si hay varias instancias), las publica y las marca como enviadas. Es simple y agrega algo de latencia.
  • CDC: Debezium lee el WAL/binlog de la tabla outbox y publica. Tiene menos carga y latencia, pero suma infraestructura.

Consecuencias de diseño:
  • La entrega es at-least-once: si el relay se cae después de publicar y antes de marcar, el evento se duplica. Por eso los consumidores tienen que ser idempotentes, deduplicando por event_id en una tabla de eventos procesados u operando con upserts.
  • Orden: uso el aggregate_id como clave de partición para mantener el orden por pedido.
  • Operación: limpieza periódica de la tabla outbox, métricas de lag y de eventos pendientes, y alertas si el relay se detiene.

Del lado del consumidor existe el patrón simétrico, el inbox. Event sourcing también evita el dual write, pero cambia todo el modelo y solo lo usaría si el dominio lo justifica.

Errores comunes

Proponer publicar después del commit con reintentos, o dentro de la transacción antes del commit, como si eso fuera atómico. Prometer exactly-once sin mencionar que los consumidores deben ser idempotentes.

Redactado con el asistente Underc0de IA. Actualizado el 14/11/2026. ¿Ves un error? Contanos en el foro.

Grupo de WhatsApp