Ordering: a payment is declined in the middle of a saga Intermediate
Estimated reading time: 1 min read
Seat and hotel are already committed in their own services when the payment fails. There is no ROLLBACK across services: the orchestrator has to undo them with compensations, and the order matters.
Order the recovery steps below, from the moment PaymentDeclined arrives to the message the customer receives. Press Check to see which steps are out of place and what breaks when they are.
Payment declined mid-saga: recover the tour booking in the right order
Saga saga-123 books a tour package through an orchestrator. T1 ReserveSeat held seat 12A, T2 ReserveHotelRoom held room 204, both committed in their own services. T3 ChargePayment just came back PaymentDeclined. T4 IssueTicket has not run.
The orchestrator keeps its state in saga_instance and sends commands through an outbox; the broker delivers at least once. Order the recovery steps.
Use ▲▼ to move each step, or tap a step’s ⠿ and then tap its new position. On a computer you can drag and drop.
- Stop forward steps: the guard only sends
IssueTicketwhen state isRUNNING, so no forward command for saga-123 goes out - Commit
state = COMPENSATEDtogether with aBookingFailedevent in the outbox - Send
CancelHotelRoomwith keysaga-123:hotel:cancel, written to the outbox with the step change - Send
ReleaseSeatwith keysaga-123:seat:release, retried the same way untilSeatReleased - Notify the customer from
BookingFailed: payment declined, nothing charged, seat and room released - Retry
CancelHotelRoomwith the same key untilHotelCancelledarrives ("no such hold" also counts as done) - Commit
state = COMPENSATING,failed_step = charge_paymenttosaga_instance
Step kinds, saga state and isolation countermeasures: Saga pattern.