Distributed Transactions — Notes#
The hard truth#
ACID across services + queues + caches is expensive. Most production systems prefer business-level consistency (sagas + idempotent retries) over technical 2PC.
Outbox pattern (most useful trick)#
- In one DB transaction, write the domain row + insert an
outbox(event_id, payload)row. - A relay (Debezium tailing the WAL, or polling) reads outbox and publishes to Kafka.
- Mark row as
sent. - Consumers deduplicate via
inbox(event_id).
This gives you exactly-once effect with at-least-once delivery.
Idempotency keys#
- Client generates a unique key for each user action.
- Server stores
(key → result)in idempotency table with TTL. - Re-submit returns the original result.
Two-phase commit caveats#
- "In-doubt" transactions if coordinator crashes between phases.
- Heuristic resolutions can leave data inconsistent.
- Avoid XA across MQ and DB in modern systems — use outbox instead.
CP global stores#
- Spanner: Paxos within range, 2PC across ranges, TrueTime for serializability with commit-wait.
- CockroachDB / TiDB / YugabyteDB: similar Paxos+2PC architecture.
Refs#
- "Sagas" — Garcia-Molina & Salem 1987.
- Pat Helland: "Life Beyond Distributed Transactions: An Apostate's Opinion."
- Spanner OSDI '12, Calvin paper.
- Microservices Patterns (Chris Richardson), DDIA ch.7 & 9.