Ecommet
Topic 55 of 75

Compensating Transactions

When a multi-step operation fails halfway and the finished steps cannot be rolled back, you undo them with new actions: refunds, cancellations, corrections. Learn how to design compensations that are safe to retry and honest about what cannot be undone.

Advanced 3 min read 3 quiz questions

Jump to the quiz

In a nutshell

  • A compensating transaction undoes the effect of a completed step with a new action, not by erasing history.
  • Compensations run in reverse order and must be idempotent, since they may be retried.
  • Some effects cannot be undone (an email was sent, goods were shipped): compensate with a business action (apology, return label).
  • Record each step's progress durably, so compensation can resume after a crash.

Undo by doing something new

Inside one database, a rollback makes a failed transaction disappear. Across services, each step has already committed and others may have seen it. The only way back is forward: a refund for a charge, a cancellation for a booking, a credit note for an invoice. This is the undo side of a saga.

StepCompensationNotes
Reserve stockRelease the reservationEasy: an internal state change
Charge cardRefundThe customer may see both on their statement
Book hotelCancel bookingMay cost a cancellation fee: a business decision
Send confirmation emailSend a correction emailCannot be unsent
Ship goodsArrange a returnSlow and expensive: put irreversible steps last

Designing compensations

  • Durable progress log: record which steps completed (in the orchestrator's database or as events), so a crashed coordinator can pick up and compensate.
  • Idempotent: "refund payment 812" twice must refund once; use the original step's ID as the compensation's idempotency key.
  • Retry until done: a compensation that fails must be retried (with backoff), and alert a person if it keeps failing.
  • Semantic, not exact: the system returns to a consistent business state, not to the exact bytes it had before. Other changes may have happened in between.
  • Visible states: mark records as "pending" or "cancelling" so users and other services understand what they see.

The code sample in the distributed transactions topic shows an orchestrator that runs steps and, on failure, runs compensations newest-first.

Common mistakes

  • Compensations that are not idempotent, so a retry refunds twice.
  • Running irreversible steps early in the flow.
  • Keeping saga progress only in memory, losing it on a crash.
  • Assuming compensation restores the exact previous state.

Interview questions

Try answering out loud before you open each one.

Payment succeeded but the shipment service rejects the order. What happens?

The orchestrator records the failure and runs compensations in reverse: release the stock reservation, refund the payment (idempotent, keyed by the payment ID), mark the order cancelled and notify the customer. Each compensation is retried until it succeeds, with alerts if it cannot.

Check yourself

1. How is a charged card "undone" in a saga?
Show the answer

Answer: A refund, as a new action. The original charge happened; a refund compensates it.

2. Why put irreversible steps last?
Show the answer

Answer: So they only run once everything that could fail has succeeded. Then they rarely need compensating.

3. Why must compensations be idempotent?
Show the answer

Answer: They may be retried after failures and must not apply twice. A double refund is a real loss.

Further reading

Your notes

Saved in this browser as you type.

Finished this topic?

Mark it read to track your progress, then carry on with the next one.

Next topic