Skip to content
← All field guides
Reliability7 min read

By · Editorial policy

Workflow Compensation vs Rollback After a Partial Failure

Rollback reverses an uncommitted local transaction. Compensation is a new business action after an external effect has committed, such as refunding a charge or releasing a reservation. Build a compensation plan from confirmed effect receipts, run it in reverse dependency order where required, give each action an idempotency key, and verify the provider result. If the original outcome is unknown, reconcile it before deciding whether compensation is needed.

What is the practical answer?

Rollback applies to an uncommitted local transaction; compensation is a new action after an external effect has committed. If a travel workflow reserves a hotel and charges a card before ticketing fails, the recovery plan may refund the charge and release the reservation. Build that plan from confirmed provider receipts rather than merely visited workflow steps. An unknown payment outcome must be reconciled before deciding whether to refund. Order compensation by business dependency, give each compensation its own stable idempotency key, and verify the provider result before marking the original effect compensated. Some actions, such as a sent notification, cannot be undone. Retain these as visible residuals with an owner and next action instead of labeling the run fully rolled back. Orch8's receipt-backed continuity tools help inspect and plan recovery, while each external provider still defines its own idempotency and lookup contract.

Evidence: Microsoft: saga distributed transactions · Microsoft: compensating transaction pattern · Orch8 Engine: continuity operations

Committed charge and reservation require new refund and release effects after ticketing fails
Compensation adds recorded actions; it does not erase committed provider effects. Diagram by Orch8 Engineering.

A refund is a new transaction, not a rollback

An uncommitted database transaction can roll back and leave no visible order row. Once a payment provider has charged a card, the workflow cannot erase that event by rewinding its internal state. A refund is a second provider operation with its own request, receipt, fees, timing, and possible failure. The same distinction applies to cancelling a shipment, deleting a published post, or releasing reserved inventory.

Microsoft's saga pattern coordinates local transactions and compensating actions across services. The compensation may restore an acceptable business state, but it does not make history disappear. A customer can still see both a charge and a refund. Name the final state honestly, especially when only some actions were reversed.

SituationOperationEvidence needed
Local transaction did not commitRollbackTransaction outcome
External effect committedCompensateProvider receipt and compensation rule
External outcome unknownReconcile firstProvider lookup or manual evidence
Action cannot be reversedContain and reviewResidual impact and owner

Plan from effects known to have happened

Consider a travel workflow: reserve a hotel, charge a card, then issue a ticket. If ticketing fails, a compensation plan may refund the charge and cancel the reservation. But a step merely marked 'started' is insufficient evidence that the hotel accepted the booking. Conversely, a timeout after dispatch does not prove it failed. Receipt state must distinguish planned, dispatched, committed, verified, and unknown outcomes.

Construct the plan from committed or verified effects, not from every step the workflow visited. For an unknown result, first query the provider by the original request identity. If no lookup exists, route it to a person with the relevant request IDs and timestamps. Automatically compensating an effect that never happened may cause a second error or an unauthorized business action.

Reverse dependencies, not merely timestamps

A simple saga often compensates in reverse order, but business dependencies decide the sequence. If inventory must remain reserved until a payment refund is confirmed, refund first and release inventory second. If an irreversible notification has already been sent, no later state rewrite can unsend it; the plan may require a correction message or a manual customer response.

Write each forward step's compensation rule when designing the workflow, including the data needed to call the provider later. Keep the rule version with the execution. A deployment that changes refund parameters must not silently reinterpret an older run's receipts. Preview the plan so an operator can see missing rules, uncertain effects, and irreversible actions before authorizing it.

  • Identify the committed receipt for each candidate.
  • Resolve parameters from retained, authorized evidence.
  • Order actions by business dependency.
  • List missing rules and unknown outcomes before execution.

Make compensation itself retryable and auditable

A compensation can time out after the provider accepts it. Give it a stable key derived from the original effect and compensation purpose, not from the retry attempt. Persist the request and result as a new effect. If a worker lease expires, a new worker must reconcile the old attempt rather than creating a second refund. Only mark the original effect compensated after the provider result is verified under the policy you selected.

Orch8's 0.7 line documents receipt-backed compensation planning in its portable continuity surface. That engine evidence helps the operator decide what to reverse; it does not provide universal exactly-once behavior at a payment or shipping API. The integration still needs provider-specific idempotency, lookup, and authorization controls.

compensation-state.txt
charge: committed -> refund prepared -> refund unknown -> refund verified
reservation: committed -> release prepared -> release verified
workflow: completed_with_residuals until every required effect is resolved

Test partial compensation, not only a happy rollback

Force ticketing to fail after the hotel and payment effects commit. Confirm the preview includes only those two effects and states the intended refund-before-release order. Then let the refund provider accept the request while the worker loses the response. The next attempt must reconcile using the same key, and the operator view must still show an unknown refund until evidence arrives.

Finally, make the hotel cancellation fail permanently. The run should retain a residual with a named owner, provider reference, and next action. A result labeled simply 'rolled back' would hide a still-active reservation. Rehearsing this path reveals whether the product can communicate partial recovery as clearly as full success.

  • Fail after two confirmed external effects.
  • Lose a compensation response after provider acceptance.
  • Retry under a new worker lease.
  • Leave one compensation unresolved and verify the residual is visible.

Sources and further reading

Official references support technical claims; community discussions are used only as problem signals.