On this page
Distributed Transactions
A write operation across multiple services—no global ACID. 2PC aims for strong consistency but blocks all participants if the coordinator fails; Saga and TCC trade availability for compensating transactions, at the cost of requiring application-level rollback logic. The outbox pattern and idempotency ensure the feasibility of "at-least-once" delivery in engineering practice.
In a single-node database, transactions are ACID—just use BEGIN/COMMIT/ROLLBACK. Distributed systems involve write operations across multiple services, with no global ACID transaction.
2PC (Two-Phase Commit)
The most basic distributed transaction protocol—blocking, with no fault tolerance for coordinator failure.
Problem: If the coordinator crashes after PREPARE but before COMMIT → participants are in an uncertain state—they cannot unilaterally commit (they don't know if other participants replied YES) nor unilaterally abort (others might have already committed). Participants block until the coordinator recovers—this is the "blocking protocol".
In practice: XA transactions (an implementation of 2PC) are used for cross-shard database transactions, but handling coordinator crashes requires administrators to manually perform heuristic commit/abort.
Saga
Breaks a long transaction into multiple local transaction steps, each with its own compensating action. If a step fails, the compensating actions for the completed steps are executed in reverse order.
Saga lacks isolation (other transactions can see intermediate states), but it is non-blocking—the system is always progressing at any moment.
Orchestration Patterns
- Choreography: Each service consumes the event from the previous step and publishes its own completion/failure event—no central orchestrator.
- Orchestration: A central Saga coordinator tells each service to "execute step" or "execute compensation"—centralized logic, easier to manage.
TCC (Try-Confirm-Cancel)
A variant of Saga, suitable for more "rigid" resource reservation:
Try: Reserve resources (check inventory, freeze balance) — resources are **not consumed**, just marked as "pending use"
Confirm: Actually consume resources (deduct inventory, deduct funds)
Cancel: Release reservation (release inventory, unfreeze balance)
Difference from Saga: Saga's steps directly modify data (book_flight actually books the ticket), and compensation is an undo. TCC's Try only reserves, and Confirm is the actual modification—Cancel can be executed at any time without needing to perform an undo later.
Idempotency
The most important non-functional requirement in distributed systems: f(x) = f(f(x))—retrying an operation does not affect the result.
Implementation methods:
1. Unique idempotency key: Client generates a UUID, server stores (key) → (processed/unprocessed)
→ Duplicate key → Return cached result, do not re-execute
2. Database constraints: INSERT INTO orders VALUES (order_id=123, ...)
→ Retry → UNIQUE constraint violation → Ignore or read existing
3. Token-based: Server gives client a token, client uses token to mark request
→ Duplicate token → Reject
Non-idempotent example:
UPDATE users SET balance = balance + 100 -- Retry = added twice!
Fix: UPDATE users SET balance = ? WHERE version = ? (optimistic locking)
Outbox Pattern
Transactionally writing to the database and sending a message to the message queue—how to ensure atomicity between the two? You cannot write to DB first and then send the message (DB write succeeds but message sending fails → inconsistency).
[Local Transaction]:
INSERT INTO orders (...);
INSERT INTO outbox (event_type, payload, created_at) VALUES ('order_created', '...', NOW());
COMMIT; ← Both are in the same local transaction!
[Outbox Poller]:
SELECT * FROM outbox ORDER BY id → Send to Kafka/MQ → On success → Delete from outbox
Guarantee: order write + event generation are atomic (same transaction). The Outbox poller ensures at-least-once delivery.
References
- Paper: "Sagas" (Garcia-Molina & Salem, 1987)
- Microservices Transactions: microservices.io/patterns/data/saga.html
- Idempotency: stripe.com/docs/idempotency (production-grade implementation)
Keywords: 2PC, XA, Saga, TCC, compensating action, idempotency, outbox pattern, distributed transaction