Hard20 minDistributed Systems
UpdatedAug 6, 2026
Edit

Kafka: Database Write and Event

Question Variations

  • "What failures can occur between a database write and Kafka publish?"
  • "How does an outbox relay recover after a crash?"
  • "Why isn't a Kafka transaction always enough for an API database write?"

Why This Is Asked

An API must create an order in its database and publish order.created to Kafka, even if the service crashes at an unlucky moment. This tests the dual-write problem, transactional outbox design, and safe consumer offset handling.

Key Concepts

  • Dual-write gap: A database commit and Kafka publish cannot be assumed to succeed atomically across separate systems.
  • Transactional outbox: Persist the domain change and outbound event in one database transaction, then publish reliably.
  • Idempotent publication: Use a stable event ID so publisher retries do not create semantically duplicated events.
  • Offset timing: Commit a consumer offset only after the durable local effect succeeds.

Question Variations

  • “What failures can occur between a database write and Kafka publish?”
  • “How does an outbox relay recover after a crash?”
  • “Why isn’t a Kafka transaction always enough for an API database write?”

Answers by Technology

+ Add Variant
Apache KafkaImprove this answer ✏️

Expected Answer

Do not write the order to a database and publish to Kafka as two unrelated operations. A crash after the database commit but before publication leaves downstream services unaware; publishing first can create an event for an order that never commits. Instead, write the order and an outbox row containing a stable event ID in one database transaction. A relay reads pending rows, publishes them to Kafka, and marks them sent. It can safely retry because consumers deduplicate the stable event ID. On the consume side, apply a local state change and processed-event record atomically, then commit the Kafka offset. A crash before the offset commit redelivers a harmless duplicate.

Why It Matters

The outbox closes a common lost-event gap without pretending a database and broker share one transaction manager.

Example Code

await db.transaction(async (tx) => {
  const order = await tx.order.create({ data: input });
  await tx.outbox.create({ data: { id: crypto.randomUUID(), type: "order.created", payload: JSON.stringify(order) } });
});

Common Mistakes

  • Publishing after commit in application code: A process crash leaves an unannounced order.
  • Committing the offset before the local write: A crash loses the consumed effect.

Follow-up Questions

  • Why is an event ID needed? (Answer: It gives relays and consumers a stable deduplication identity.)
  • Can Kafka transactions cover an arbitrary database? (Answer: No; use an outbox or another deliberate cross-system consistency design.)

Related Questions

References