advanced 2 min answer Multiple choice

An order at extreme e-commerce scale must reserve inventory, charge payment, create the order and notify fulfilment - across four independently deployed services. Which transaction strategy fits?

distributed-transactionssagatwo-phase-commitalibabaarchitecture-selection
Pick one
Show the full answer Hide the answer

Why two-phase commit is wrong here

Not because it is theoretically unsound — it is sound — but because of what it costs at this scale and in this topology:

  • It holds locks for the duration of the whole transaction, including the payment call to an external provider. At flash-sale volume, holding an inventory lock across a network call to a payment gateway serialises the hottest resource in the system behind the slowest participant.
  • It is a blocking protocol. If the coordinator fails after prepare, participants hold locks indefinitely until it recovers. During a peak event this is an outage.
  • Payment providers do not participate. External services do not offer prepare/commit, so the transaction is not actually atomic regardless of what the internal services do.

Why the saga fits

The order is naturally a sequence of steps, each locally transactional, each with a defined compensation:

Step Compensation
Reserve inventory Release reservation
Charge payment Refund
Create order Cancel order
Notify fulfilment Recall / cancel dispatch

The system is never globally locked. Each step commits locally and publishes an event; the next step consumes it. Failure at any point triggers compensation backwards.

The two details that make it correct

1. The transactional outbox. The classic bug: a service commits its local state and then crashes before publishing its event. The order is charged and never created. The fix is to write the event to an outbox table in the same local transaction as the state change, and have a separate process publish from the outbox. Then "state changed" and "event will be published" are atomic, and delivery is at-least-once — which forces the next requirement.

2. Idempotent consumers. At-least-once delivery means every step will occasionally run twice. Each must be idempotent on a business key: reserving the same order's inventory twice reserves once, charging the same payment intent twice charges once.

Why inventory is the exception

Inventory reservation is the one step that must be strongly consistent, because overselling is unrecoverable in a way the other steps are not. A double charge can be refunded; a double sale of a limited item cannot be un-sold, and at flash-sale scale it happens thousands of times before anyone notices.

So inventory uses a conditional atomic decrement at a single authoritative owner — and everything around it is eventual. That asymmetry is the real design insight: identify the one invariant that cannot be compensated, protect it properly, and refuse to pay for strong consistency anywhere else.

Compensation is not rollback

Worth stating explicitly, because it is where teams get surprised: compensation is a new business action, visible to the customer and often to accounting. A refund is not the absence of a charge. The saga's states must therefore be modelled as real business states — payment_refunded is not payment_never_happened — and the customer communication has to match.