Saga Compensate vs Workflow CancellationScope

I have confusion regarding use case of Saga compensate vs workflow CancellationScope. Looks like both can perform cleanup for workflow and sub actvities. Any insight or suggestion?

Saga is a way to dynamically accumulate compensations based on the code path of the workflow. Cancellation is a way to interrupt workflow execution at any point.

    Saga saga = new Saga(sagaOptions);
    try {
      String carReservationID = activities.reserveCar(name);
      saga.addCompensation(activities::cancelCar, carReservationID, name);

      if (bookHotel) {
        String hotelReservationID = activities.bookHotel(name);
        saga.addCompensation(activities::cancelHotel, hotelReservationID, name);
      String flightReservationID = activities.bookFlight(name);
      saga.addCompensation(activities::cancelFlight, flightReservationID, name);
    } catch (TemporalFailure e) {
      Workflow.newDetachedCancellationScope(() -> saga.compensate()).run();
      throw e;

In the sample above if bookHotel is false it is not going to include cancelHotel activity invocation when saga.compensate is called. Note that the TemporalFailure can be either an ActivityFailure or CanceledFailure if the workflow is canceled while executing any of the reservation activities.

Thanks for the quick clarification. I have two follow questions.
1.If I only call saga.compensate() in the catch block( without calling Workflow.newDetachedCancellationScope(()), What will be the difference?

2.You stated that " TemporalFailure can be either an ActivityFailure or CanceledFailure". Suppose there was failure during bookFlight activity and compensate cancelCar and cancelHotel. Suppose cancelHotel failed as well may be network issue. As per this code, it will invoke compensate again for cancelCar and cancelHotel .Create a cyclic loop, is my understanding correct?

  1. If the root cancellation scope is cancelled because the whole workflow is cancelled, any attempt to execute activity or child workflow is going to fail with CanceledFailure. So if a compensation requires such calls it is going to fail. I filed an issue to execute compensations in a disconnected scope by default.

  2. I don’t see where in the above code a loop can be found. The behavior in case of a compensation failure is configured through Saga.Options.ContinueWithError. If it is set to true a compensation failure would be ignored and all other compensations executed. If it is set to false then an exception will be thrown from the catch block without executing other compensations and workflow is going to fail (if it fails to handle the exception). Note that your example should never execute this functionality as any compensation activity is expected to have an appropriate retry policy to not fail on intermittent errors like network outages.

just a quick question, when the compensate happens will those also be retired based on the actitvity/retry options?

Compensations are just callbacks. The code in these callbacks is a normal workflow code. So if it invokes activities they obey the configured retry options.

1 Like