Skip to content

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)#

  1. In one DB transaction, write the domain row + insert an outbox(event_id, payload) row.
  2. A relay (Debezium tailing the WAL, or polling) reads outbox and publishes to Kafka.
  3. Mark row as sent.
  4. 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.