Implementation Strategies for Distributed Saga pattern
In a event-driven architecture, each service listens to events emitted by other services and perform the required action. Let’s take the example of the trip service:
When a user requests for a trip creation, Trip service triggers Trip_Created_Event. The Flight service listens to Trip service for the Trip_Created_Event and books a flight and emits Flight_Booked_Event. Car rental service listens to Flight service for the Flight_Booked_Event and books a cab and emits Car_Rented_Event. Hotel reservation service listens to Car rental service for Car_Rented_Event and makes a hotel reservation and emits Room_Reserved_Event. The Trip service listens for the Room_Reserved_Event and updates the Trip state.
In case we failed to book a hotel room we have to execute compensating requests for canceling car booking and canceling the flight ticket.
The Car rental service listens for Reservation_Failed event and executes the compensating cancel car booking request and emits Car_Booking_Cancelled event. The flight service listens for Car_Booking_Cancelled event and executes compensating request to cancel flight ticket and emits Flight_Booking_Cancelled event. The trip service listens for this event and updates the state of the trip.
- It is easy to implement
- Participants are loosely coupled
- Can easily become confusing as the application becomes complex.
- Difficult to trace the flow without observing the live system.
- Testing will become tricky as all the services should be running.
In this approach, there is a single service which acts as a coordinator for the whole saga. It knows all the steps involved in executing a saga
It sends commands to the required services to book a flight, rent a cab and reserve a hotel room.
In case of failure in either of them, it calls the compensating requests for the successful requests. For example, if we fail to reserve a hotel room:
It calls the compensating request for the car rental service and flight service.
- The orchestration of the distributed transactions is centralized
- Reduce participants complexity as they only need to execute/reply to commands
- Easier to implement
- Easier to manage a rollback
- Easy to add new stages to the Saga
- We have to manage an extra service
- We are making smart architecture and dumb services
Points to keep in mind during implementation
We can design our system in such a way that, we send the response to the trip service once the saga execution is complete
- Advantage: Response specifies the outcome
- Disadvantage: Reduce availability as the whole saga has to execute before returning a response
We can design our system to return response as soon as the saga execution starts
- Advantage: Improved availability
- Disadvantage: Client must poll or be notified to check the status of the Saga execution.
We have to keep in mind on how to cancel a pending operation
- Whether to wait for it to succeed and then cancel it.
- Cancel it and make sure if you get the request late, it is not executed.
We can also use asynchronous messaging to ensure saga completes when participants are temporarily unavailable.