#64 Two-Phase Commit/Saga
Shopify Inventory Reservations
Hold stock for a customer during checkout across separate inventory, order and payment services, and release it cleanly if anything fails.
What is being asked
Design inventory reservation for an e-commerce platform. Checkout touches several services with their own databases; either all steps happen or the stock goes back. This is the standard setting for comparing two-phase commit with sagas.
Requirements
Functional
- Reserve stock when checkout starts; confirm on payment; release on failure or timeout.
- Never sell more than is in stock.
Non-functional
- Services are independent, each with its own database.
- High traffic during sales; partial failures are normal.
- A stuck checkout must not hold stock for ever.
Design
- Inventory keeps two numbers per item: available and reserved. Reserve is an atomic conditional update: move the quantity from available to reserved only if enough is available.
- The order service runs a saga: reserve stock, charge payment, create the shipment. Each step is a local transaction in one service.
- If a step fails, the orchestrator runs the compensating actions for the steps already done, in reverse: refund the payment, release the reservation.
- The saga's progress is recorded in a log, so after a crash it resumes or compensates.
- Each reservation has an expiry; a sweeper releases those never confirmed.
Key decisions and trade-offs
- Two-phase commit gives atomicity across services, but participants hold locks while waiting for the coordinator, and a coordinator failure blocks them. It suits a few databases under one team, not many independent services.
- Sagas never hold cross-service locks and tolerate failures, but there is no isolation: other requests see intermediate states, such as stock that is reserved but not yet paid for. The design must make those states meaningful.
- Orchestration or choreography. A central orchestrator makes the flow explicit and easy to trace. Services reacting to each other's events are more loosely coupled but the overall flow becomes hard to see.
- Idempotent steps and compensations. Every command may be delivered twice, so each carries the saga id and is applied once.
Follow-up questions
- What if the compensating refund itself fails?
- How do you size the reservation timeout?
- How would you stop a flash sale from overselling under this design?
- What does the customer see while the saga is in progress?