Distributed Transactions: Two-Phase Commit, Transactional Outbox & CDC Event Publishing
Publishing an event to Kafka in the same handler that commits a database transaction is a dual-write hazard that silently loses events on partial failure. Learn how the Transactional Outbox pattern and CDC via Debezium WAL tailing provide exactly-once event publishing guarantees — and when Two-Phase Commit is still the right tool.
Distributed Architecture & System Design
Distributed Transactions: Two-Phase Commit, Transactional Outbox & CDC Event Publishing
Every boundary is a failure isolation decision — and the dual-write hazard is one of the most deceptively simple failure modes in distributed systems. The pattern appears in almost every codebase that integrates a relational database with Kafka or any external event stream:
Series positioning: This is Part 9 — the capstone of the Distributed Architecture & System Design series. Having explored workflow coordination in Part 8: The SAGA Pattern, this final article resolves the dual-write hazard using the Transactional Outbox, Debezium CDC via WAL tailing, and the Consumer Inbox pattern to guarantee end-to-end exactly-once business outcomes.
There is no atomic operation that spans a PostgreSQL commit and a Kafka publish. The application can crash between them. The network can fail. The Kafka broker can be temporarily unavailable. Every engineer who has written this pattern has introduced a silent data loss risk that only manifests under failure conditions — exactly when it matters most.
1. Two-Phase Commit: When It Works and When It Doesn't
Two-Phase Commit (2PC) is the classical distributed transaction protocol. It uses a coordinator to synchronize commits across multiple resource managers.
1.1 The 2PC Protocol
When the 2PC coordinator crashes after PREPARE but before COMMIT, both databases hold their locks in an in-doubt transaction state indefinitely. Rows in the PREPARE phase are locked and cannot be read or written by any other transaction until the coordinator recovers and issues COMMIT or ROLLBACK. This is a blocking failure mode — 2PC provides durability at the cost of availability.
1.3 When to Use 2PC
| ✅ Use 2PC When | ❌ Avoid 2PC When |
|---|---|
| Both databases are PostgreSQL (XA-compatible) within a single trust boundary | Coordinating with a message broker (Kafka has no XA support) |
| Coordinator failure is recoverable within seconds (high-availability coordinator) | Coordinating across microservice API boundaries |
| The transaction volume is low (2PC blocks during coordinator outage) | Third-party APIs are involved (cannot roll back an external charge) |
| Strong consistency is a hard requirement | High throughput is required (in-doubt lock blocking degrades concurrency) |
2. The Transactional Outbox Pattern
The Transactional Outbox eliminates the dual-write hazard by treating the event payload as a row in the same database transaction as the domain state change.

2.1 Outbox Table Schema
2.2 Atomic Write: Domain State + Outbox Row
2.3 Polling Relay (Simple)
3. Debezium CDC — WAL-Based Event Publishing
The polling relay adds 100ms–1s publishing latency and requires a polling process to run continuously. Change Data Capture (CDC) via Debezium tails the database Write-Ahead Log (WAL) directly, publishing outbox row inserts to Kafka in near real-time without application polling.
3.1 How WAL Tailing Works
3.2 Debezium Configuration
3.3 Polling Relay vs CDC Comparison
| Property | Polling Relay | Debezium CDC |
|---|---|---|
| Publishing latency | 100ms–1s (polling interval) | < 100ms (WAL tail) |
| Dependency | Application process only | Debezium connector + Kafka Connect cluster |
| Operational complexity | Low | Medium (WAL slot management, lag monitoring) |
| Database load | Polling query load on outbox_events |
WAL replication slot (low overhead) |
| Delivery guarantee | At-least-once | At-least-once |
| Recommended for | Low-throughput (< 100 events/s) | High-throughput (> 100 events/s) |
4. The Inbox: Idempotent Consumer
The Outbox guarantees at-least-once publishing — Kafka may deliver the message more than once (relay retry, rebalance, etc.). The Inbox pattern on the consumer side provides idempotent deduplication to achieve end-to-end exactly-once semantics:
5. End-to-End Exactly-Once Guarantee

The system achieves end-to-end exactly-once without requiring Kafka's transactional API:
- Producer: domain state + outbox row commit atomically (or both roll back)
- Transport: Kafka delivers at-least-once (duplicates possible)
- Consumer: inbox deduplication ensures each business effect runs exactly once
6. WAL Slot Management & Operational Concerns
Summary
| Architectural Concern | Production Rule |
|---|---|
| Dual-Write Hazard | Publishing to Kafka after (or before) a DB commit has an unavoidable race window; never rely on application-level coordination. |
| Transactional Outbox | Domain state + event payload commit in one ACID transaction; the event is guaranteed to persist alongside state. |
| Debezium CDC | WAL tailing publishes outbox rows to Kafka in sub-second latency without application polling; provides at-least-once transport. |
| Inbox Pattern | Consumers deduplicate using per-message idempotency keys in an ACID inbox table within the processing transaction. |
| End-to-End Exactly-Once | Outbox (producer) + at-least-once Kafka + Inbox (consumer) = exactly-once business outcome without Kafka transaction overhead. |
Series Conclusion
This concludes the Distributed Architecture & System Design series. You have now mastered the full progression of distributed systems engineering — from strategic bounded context decomposition and RPC communication protocols, to high-throughput messaging brokers (Kafka & RabbitMQ), CQRS command/query models, append-only Event Sourcing, zero-downtime Materialized View rebuilds, distributed SAGA workflows, and the Transactional Outbox with Debezium CDC.
The unifying principle across all nine parts remains: every boundary is a failure isolation decision; design every message, transaction, and state machine as if partial failure is the default, not the exception.
This article was developed with AI-assisted deep search, specification cross-referencing, and technical research synthesis.