Trade-offs · Production decisions
Saga vs two-phase commit
How to choose between a saga and two-phase commit for a job that spans several databases or services, the numbers that decide it, and what breaks.
Asked at: SDE-2Senior
The decision
Some jobs change data in several places, and each place has its own database. A normal transaction covers one database only. Two-phase commit (2PC) makes several databases act as one. A coordinator asks every participant to prepare: do the work, lock the rows and promise it can commit. If all say yes, the coordinator tells all to commit. If any says no, all abort. A saga (described in a 1987 paper by Garcia-Molina and Salem) splits the job into steps. Each step is a local transaction that commits at once. If a later step fails, the saga runs a compensation for each finished step: a new action that cancels its effect for the business. 2PC gives real all-or-nothing. A saga gives eventual all-or-nothing, and others can see the middle.
Choose 2PC when
- All participants are databases you run, and they support prepare. In PostgreSQL this is
PREPARE TRANSACTION. - There are two or three participants, close together, in one data centre.
- The transaction is short and the rows are not hot, because locks are held while you wait.
- Nobody may ever see the half-done state, for example when moving a row between two of your own shards.
Choose a saga when
- Steps are in different services or at outside companies. A payment gateway or an SMS provider has no prepare step.
- The job is long: seconds, minutes or days, such as a booking with a hold and a payment.
- You can write a compensation for each step, and the business accepts short in-between states like "order pending".
The numbers that change the answer
Time that locks are held (calculation). In 2PC, rows stay locked from the prepare until the final commit. Assumptions: a round trip inside one data centre is 0.5 ms, between cities 30 ms, and a disk flush 1 ms. The lock lasts about two round trips and two flushes.
- Same data centre: 2 x 0.5 + 2 x 1 = 3 ms. A hot row can then take at most 1,000 / 3 = 333 updates a second.
- Across two cities: 2 x 30 + 2 x 1 = 62 ms. The limit drops to 1,000 / 62 = 16 updates a second.
Availability (calculation). Assumption: each participant is up 99.9% of the time, independently. For three participants, 2PC needs all three at once: 0.999 to the power of 3 = 0.997003. In a 30-day month of 43,200 minutes, downtime is (1 - 0.997003) x 43,200 = 129.5 minutes. A saga retries a down step later, so the customer sees a delay, not an error.
A coordinator crash (assumption). If the coordinator dies after the prepare, participants keep their locks until it returns. Recovery takes 60 seconds. At 300 orders a second on those rows, that is 300 x 60 = 18,000 blocked requests.
Step order in a saga, worked example. Assumptions: 100,000 orders a day. The steps are stock reservation, payment and delivery booking. Payment fails for 3% of the orders that reach it, and delivery for 1%. A refund costs Rs 10 of handling. Releasing stock and a slot is free.
- Design A: stock, then payment, then delivery. Payment fails for 3,000 orders (3% of 100,000), which only releases stock. Then 97,000 are charged, and delivery fails for 1% = 970. Each of those needs a refund: 970 x Rs 10 = Rs 9,700 a day, and the customer waits for the money.
- Design B: stock, then delivery, then payment. Delivery fails for 1,000 orders (free undo). Payment fails for 3% of 99,000 = 2,970 (free undo: release stock and the slot). After payment, nothing can fail. Refunds: 0.
Put the step that is hardest to undo last. Design B saves Rs 9,700 a day and all refund waits.
Cited facts. The PostgreSQL manual says PREPARE TRANSACTION is meant for an outside transaction manager, not for applications. A prepared transaction keeps its locks, and it is unwise to leave one prepared for long. Debezium notes that Kafka cannot be enlisted in XA transactions (see Sources).
What breaks with each choice
2PC.
- A dead coordinator or a slow participant holds locks and blocks other work. Fallback: short transactions, time limits, an alert on old prepared transactions, and a practised recovery procedure.
- One participant that is down stops the whole job. Fallback: two or three participants at most, or a saga.
- Some systems cannot join at all. The only fallback is a saga.
Saga.
- No isolation. Others can see stock reserved but unpaid. Fallback: show honest states such as PENDING, and let the reservation expire.
- A compensation can fail. Fallback: retry until it works, keep it idempotent, and send a stuck case to a person.
- A timeout is not a failure. The payment may have worked. Fallback: retry with the same idempotency key, or ask for the result, before you undo anything.
- Lost progress after a crash. Fallback: save the current step in a database (a state machine), and use an outbox for messages.
- Bugs hide when the plan is spread over services. Fallback: an orchestrator that holds the plan in one place, with logs for each saga ID.
What it costs
- People. 2PC needs little business code, but it needs a transaction manager, XA drivers and someone to repair in-doubt cases. A saga needs a compensation for each step. Assumption: 2 to 3 engineer-days per step with tests, and 3 to 4 engineer-weeks for the first orchestrator. It also needs a small state table. Assumption: one 200-byte row per order, so 100,000 x 200 = 20 MB a day.
- Changing your mind. Moving from 2PC to a saga means redesigning the steps and what the user sees in the middle. It is a product change as well as a code change. Moving from a saga to 2PC is rare, because outside services cannot join.
How each level answers
SDE-1: 2PC asks all databases to prepare and then commit together. A saga does local steps and undoes them if one fails.
SDE-2: I use a saga for an order that touches stock, payment and delivery in separate services. A payment gateway cannot take part in 2PC, and 2PC would hold locks while it waits.
Senior: 2PC across two cities holds locks for about 62 ms, so a hot row gets 16 updates a second. With three participants at 99.9%, the job is down about 129 minutes a month. In a saga I put payment last, so refunds go from 970 a day to zero. Steps are idempotent, and a stuck compensation goes to a person's queue.
Staff: I look at the business cost of a half-done order, and who sees it. If the in-between state is short and acceptable, I choose a saga with one owning team and a daily ledger check as the safety net. I move flows over one at a time, starting with the one where 2PC locks hurt most. I use 2PC only inside our own database fleet for a short job. I measure the share of sagas that finish, the time to finish, sagas stuck for over 5 minutes, and daily money mismatches.
Say it in the interview
The order touches stock, payment and delivery in three services, and the payment gateway cannot join two-phase commit. So I would use a saga with an orchestrator and an undo for each step. Payment goes last, so a failed delivery never needs a refund. Every step is idempotent, a stuck undo goes to a person, and a daily reconciliation catches the rest.
Common mistakes
- Saying "roll back everything". A finished step in another service cannot be erased. You run a new action that cancels it.
- Undoing at the first timeout. The step may have worked. Check the result first.
- Putting the costly step first. A refund costs money and trust. Free undos go first.
Sources
- PostgreSQL manual,
PREPARE TRANSACTION: not intended for applications but for an external transaction manager; a prepared transaction keeps its locks; unwise to leave in the prepared state for a long time. https://www.postgresql.org/docs/current/sql-prepare-transaction.html (opens in a new tab) (checked 26 Oct 2026) - Debezium outbox article: Kafka cannot be enlisted in distributed (XA) transactions. https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/ (opens in a new tab) (checked 26 Oct 2026)
- H. Garcia-Molina and K. Salem, "Sagas", ACM SIGMOD Record 16(3), December 1987, pages 249 to 259. DOI link: https://dl.acm.org/doi/10.1145/38714.38742 (opens in a new tab) (the ACM site blocks automated checks; the title, authors and year were confirmed by a web search on 26 Oct 2026).
- Round trips, failure rates, counts and costs above are assumptions.
Understand the trade-off. Practise explaining your choice.
In an AI mock interview, the interviewer can push on a choice like this one: it changes a number and asks if your answer still holds.