Transactional Outbox & Sagas

Solve the dual-write problem, then coordinate distributed transactions without 2PC.

0/3 done

Outbox + saga = reliable distributed workflows

The dual-write problem

Your service must do two things atomically: update its database and publish an event. There is no distributed transaction across Postgres and Kafka, so a naive db.commit(); kafka.send() can crash between the two — DB updated, event lost (or vice-versa). Silent, corrupting drift.

The Transactional Outbox fixes it: within the same DB transaction that changes business state, insert a row into an outbox table. A separate relay (Debezium CDC on the outbox table, or a poller) publishes those rows to Kafka and marks them sent. Because the state change and the outbox row commit together, you can never have one without the other — the event is guaranteed to follow the state.

Sagas — distributed transactions without 2PC

A saga is a sequence of local transactions, each emitting an event that triggers the next. There's no global rollback; instead each step has a compensating action. If PaymentCaptured succeeds but ShipmentBooked fails, the saga emits RefundPayment to compensate. Sagas trade ACID atomicity for eventual consistency plus explicit compensation — the only realistic model across service boundaries.

Outbox write inside one DB transaction (Python)

Show the atomic part: the business row and the outbox row commit together. A Debezium connector on the outbox table does the publishing — your code never calls Kafka directly, which is the whole point.

Reflect

Find a place in your code that does db.commit() then kafka.send().

  • What is the blast radius if the process dies between those two lines?
  • Would an outbox table + CDC relay remove that failure mode entirely?
  • For your longest multi-service flow, what's the compensating action for each step?

Reading in progress · 0 of 3 activities done