Playbook
Why Eventual Consistency Beats Distributed Transactions (Why Eventual Consistency Beats Distributed Transactions)
Building a 2PC across the PSP, the order, and finance is a trap. Saga plus reconciliation is the real answer this eight-part arc has been building toward.
Distributed Payment Engine
Part 16 of 22
A series on distributed payment architecture — the gap between capture and complete.
Across these eight parts we moved from provider abstraction to semantic events, failure taxonomy, retry algorithms, leases, reconciliation, and healing orphan charges. The question underneath all of it can finally be asked directly: why endure all this complexity? Why not just wrap the PSP, the order, and the finance record in one transaction and solve every one of these problems at once?
The answer is simple and final: the PSP can never participate in your transaction.
What 2PC requires
Coordinator ←→ Participant 1 (Order DB)
Coordinator ←→ Participant 2 (Finance DB)
Coordinator ←→ Participant 3 (PSP??)
The PSP's actual world
Doesn't run your transaction protocol
Lives on its own network boundary, with its own consistency model
Has no concept of your 'prepare' or 'commit' signal
Where the concepts first show up
📦 Two-Phase Commit (2PC)
A protocol that gets multiple participants to either fully commit or fully roll back a transaction together.
📦 Saga
A pattern that splits a workflow that doesn't fit in a single ACID transaction into a sequence of local operations, each with its own compensation.
📦 Eventual consistency
A model that accepts the system may not be consistent at every instant, but converges to a consistent state within a bounded time.
📦 Reconciliation as backstop
The final safety net that restores the true state when a saga's compensation step fails or gets skipped.
2PC requires every participant to share the same coordinator, the same protocol, and the same assumptions about network reliability. The PSP accepts none of that — it's a system outside your control, running on its own SLA, its own API, and its own failure model.
Why the PSP can't join a 2PC
Even if you wanted to send a PSP a 'prepare' request followed by a 'commit' or 'rollback', it wouldn't support that two-phase protocol — because the card itself, the bank network, and fraud checks already made their decision, usually in a single phase. A PSP's API doesn't say 'I'll decide later, wait'; it says 'yes' or 'no'. If the second phase (commit) fails on your side, there's no concept of the PSP rolling back its operation — there's only a separate refund request, which is itself an asynchronous, unguaranteed operation.
The world 2PC assumes
Prepare → every participant says 'ready' → Commit → all accept at once
The PSP's real world
Charge request → the PSP decides instantly → the outcome is final
Want to undo it → a separate Refund request, a separate async process
Saga: a chain of local decisions
The approach that replaces 2PC has each system run its own local transaction, moving to the next step only via an event. If a step fails, prior steps aren't rolled back — each is corrected by its own compensating action.
Charge succeeds at the PSP
→ create the order (local transaction)
→ create the finance record (local transaction)
If the finance record fails
→ compensation for the order: cancel it
→ compensation for the PSP: send a refund request
This is the direct consequence of the 'capture is easy, finalization is hard' fact from earlier in this series: the difficulty of finalization is exactly the difficulty of designing a saga's compensating steps.
Reconciliation: the saga's safety net
A saga doesn't guarantee its compensating steps always run — the compensation request can fail too, the network can drop, a worker can crash. That's exactly why everything built in the previous parts (leases, the reconciliation worker, orphan-charge healing) is the second layer that cleans up the moments a saga alone isn't enough.
Saga (the primary path)
→ moves step by step, each step owning its own compensation
Reconciliation (the secondary safety net)
→ periodically scans for and corrects what the saga skipped or failed to compensate
The real cost of eventual consistency: a window, not incorrectness
Eventual consistency doesn't mean 'the data can be wrong for a while'; it means 'the data can be incomplete or stale for a while, but that while is measured and bounded'. That window needs to be visible on the product side: how many seconds or minutes can pass before an order's status is visible after payment? That's not a technical detail, it's a product decision — and it's the core of what this series has argued from the start: in a distributed payment system, perfect instantaneous consistency is an illusion; the real goal is a short, measured, observable window of inconsistency.
| Approach | Guarantee | Actually achievable |
|---|---|---|
| 2PC (including the PSP) | Instant, full consistency | No — the PSP can't participate |
| Saga + compensation | Step-by-step progress, retroactive correction | Yes |
| Saga + reconciliation | A bounded, measured inconsistency window | Yes — the model this series has argued for |
Distinctions that get blurred
❌ Eventual consistency = an inconsistent system
✓ Eventual consistency = converging to consistency within a bounded, measured window
❌ A saga is just a simpler version of 2PC
✓ A saga is a different model entirely: there's no rollback, only compensation
❌ Needing reconciliation means the saga was designed badly
✓ Reconciliation is a permanent safety net inherent to distributed systems, not a saga's shortcoming
2PC vs. Saga + Reconciliation
| Criterion | 2PC | Saga + Reconciliation |
|---|---|---|
| PSP participation | Required but impossible | Not required |
| Lock duration | Held across all participants | None |
| Resilience to partial failure | Low | High |
| Operational complexity | Low in theory, impossible in practice | High but real |
Checklist for evaluating this model
- Can every external dependency in the system (including the PSP) join the same transaction protocol? If not, 2PC isn't an option.
- Does every saga step have a clearly defined compensating action?
- What happens when a compensating action fails — does it silently disappear, or does reconciliation catch it?
- Is the eventual-consistency window measured and shared with the product team?
- Does the system target 'consistent within a short bounded time' rather than the illusion of 'consistent at every instant'?
What to take away from these eight parts
- The PSP is an external system and can never join your transaction protocol — that's exactly why 2PC is a trap.
- A saga is the realistic alternative, where every step runs its own local transaction and owns its own compensation.
- Reconciliation isn't a saga's shortcoming; it's a permanent safety net inherent to distributed systems.
- Eventual consistency isn't incorrectness — it's a measured, observable window of convergence.
What a distributed payment system needs isn't perfect instantaneous consistency — it's knowing precisely how long the inconsistency will last.
This closes the eight-part arc that started with provider abstraction: keeping SDKs from leaking, producing semantic events, classifying failure correctly, retrying with discipline, safely owning work with leases, catching drift through reconciliation, and healing orphan charges with evidence — all of it serves one single fact: a distributed payment system doesn't aim for perfection, it aims for controlled, observable inconsistency.
FAQ
Frequently asked questions
What is Two-Phase Commit (2PC)?
A protocol that gets multiple participants to either fully commit or fully roll back a transaction together.
What is Saga?
A pattern that splits a workflow that doesn't fit in a single ACID transaction into a sequence of local operations, each with its own compensation.
Is it true that "Eventual consistency = an inconsistent system"?
Eventual consistency = converging to consistency within a bounded, measured window
What does this part lock in?
The PSP's actual world Doesn't run your transaction protocol Lives on its own network boundary, with its own consistency model Has no concept of your 'prepare' or 'commit' signal ``` The PSP is an external system and can never join your transaction protocol — that's exactly why 2PC is a trap. Across these eight parts we moved from provider abstraction to semantic events, failure taxonomy, retry algorithms, leases, reconciliation, and healing orphan charges. The question underneath all of it can finally be asked directly: why endure all this complexity? Why not just wrap the PSP, the order, and the finance record in one transaction and solve every one of these problems at once?
Engineering Principles Learned
- The PSP can never join your transaction protocol — that's why 2PC is a trap.
- A saga doesn't roll back, it compensates — a genuinely different model.
- Eventual consistency isn't incorrectness, it's a measured, observable window.
Continue reading
Continue reading
Next in series
Optimistic Concurrency Under Webhooks
When a webhook and a synchronous response touch the same payment at once, how do a version token and a lease resolve the race — and why can a stale read…
Next in series
Healing Paid-But-Unordered Payments
An incident playbook: the customer was charged but no order exists; the multi-intent cart problem; and why dedup must be cleaned up carefully.
Same series
Building a Payment Reconciliation Worker
How sweepers heal drift: the PSP says succeeded while the local record says expired, and how aged FinalizePending records get resolved.