The refund failed after the booking failed
A hypothetical travel service reserves a seat, charges a card, then learns the hotel is full. It refunds the card. The refund times out. The customer may now have a charge, a refund, both requests pending, or a stale view of them.
āRoll back the bookingā is not an operation the system actually possesses.
Undo has business meaning
A database rollback erases an uncommitted local transaction. A compensation is a new committed action in the real world. A refund may have fees, delay, and an audit trail. An email cannot be unsent. A compensation can itself fail or be retried.
The card charge timed out before the hotel failed. Should you skip refund because the charge was not recorded as successful?
Make the workflow a recoverable state machine
Persist step intent and outcome, not just a process's stack. Give forward and compensation operations their own stable keys. A replacement orchestrator resumes the first unfinished decision after checking the partner's durable status; it does not start the trip again.
pseudocodetrip enters RESERVING reserve seat using key S and record hold reserve rooms using key H and record hold authorize payment using key P and record authorization validate/fence holds before the irreversible decision confirm reservations capture payment record notification intent on pre-decision failure: reconcile unknown steps compensate completed reversible steps in valid dependency order if undo is unfinished: retain ABORTING alert and reconcile
This is an illustrative workflow, not a universal travel policy. The order depends on partner contracts and the business's point of no return. Place cheap, likely-to-fail, reversible work early where possible. Authorization can be easier to reverse than capture, but its expiry and provider semantics matter.
Holds create a new race
A try-confirm-cancel participant reserves a resource temporarily. A late confirm may arrive after the hold expired and the resource was sold. A cancel can arrive before the original try, then the delayed try creates an orphan reservation.
Participants need durable state transitions, operation identity, expiry checks, and tombstones for already-cancelled intents. The clock owning the hold decides its expiry. Before crossing an irreversible pivot, prove required holds are still usable and prevent them from expiring out from under that decision under the chosen protocol. That can require a fenced prepared/confirmable state with its own recovery alarm; it is no longer an ordinary expiring cache entry.
Compensation must preserve concurrent work
If inventory was 10, your saga decremented to 9, and another legitimate order decremented to 8, ārestore inventory to 10ā loses the second order. A compensating increment linked to the original reservation, or releasing that specific hold, preserves the other transaction.
Sagas expose intermediate states. Other readers may see reserved resources or pending payments. Use explicit states, semantic locks, revalidation of quotes, and idempotent transitions. Sagas do not automatically provide database isolation across steps.
Orchestration makes progress and compensation visible in one durable workflow history. Choreography distributes reactions among services and can suit simpler event flows, but ownership of stuck progress and undo becomes harder to locate. Neither transport ordering nor a workflow engine removes the need for idempotent partner calls.
Fence orchestrator ownership so an old instance cannot overwrite newer workflow state. Version workflow definitions and make replay compatible with recorded history. Bound retries, retain unresolved operations in a durable recovery queue, and provide an auditable manual resolution path.
Mental model: a saga is a plan for living with independent commits, including the cost and failure of undo.
Transfer: the duplicate refund
A compensation worker sends a refund, crashes before recording success, and is restarted. What identity should the next refund attempt carry? What if the provider cannot deduplicate it?
Source trail: 10-two-phase-commit-and-saga-orchestration, 13-distributed-transactions-sagas-vs-two-phase-commit. See the source map for consolidation and reference-retained material.