Passing parameters dynamically to Saga Compensation activities

We have a scenario where we would like to call saga compensation activity in case of failure and termination(using cancellation scope) as well. Is there a way to pass this status(failure/terminated) dynamically to saga compensation activity. Reason being, we have different tasks to finish based on the status.

for eg:

try {
      activityCancellationScope = Workflow.newCancellationScope(
          () -> {
            String carReservationID = activities.reserveCar(name);
            saga.addCompensation(activities::handleCarBooking, statusCode, name);
            String flightReservationID = activities.bookFlight(name);
            saga.addCompensation(activities::handleFlightBooking, statusCode, name);
            String hotelReservationID = activities.bookHotel(name);
            saga.addCompensation(activities::handleHotelBooking, statusCode, name);
          });
      activityCancellationScope.run();
    } catch (ActivityFailure e) {
      if(e.getCause() instanceof CanceledFailure) {
        status = "Terminated";  //is there a way to get this dynamically passed
        Workflow.newDetachedCancellationScope(() -> saga.compensate()).run();
      }
      status = "Failed";
      Workflow.newDetachedCancellationScope(() -> saga.compensate()).run();
      throw e;
    }

For activity invocation you can make compensation decision based on cause of ActivityFailure as you have shown. Can check for cause ApplicationFailure (through which you can get the failure thrown in your activity), TimeoutFailure (in case of activity timeout), CanceledFailure in case of cancellation.

Workflow termination is done on the server side and you won’t be able to handle that within wf code. Don’t make termination part of your business logic (use cancellation).

I got the first part, not quite sure about the second part.

Let me rephrase my question.

In case of failure, I need to call certain set of clean up activities and for termination I need to call a different set. So to accomplish this, do I need to register two sagas as below or is it possible with one saga where we can pass Terminated/Failed status dynamically.

try {
      activityCancellationScope = Workflow.newCancellationScope(
          () -> {
            String carReservationID = activities.reserveCar(name);
            failureSaga.addCompensation(activities::handleCarBooking, "FAILED", name);
            terminationSaga.addCompensation(activities::handleCarBooking, "TERMINATED", name);
          });
      activityCancellationScope.run();
    } catch (ActivityFailure e) {
      if(e.getCause() instanceof CanceledFailure) {
        Workflow.newDetachedCancellationScope(() -> terminationSaga.compensate()).run();
      }
    
      Workflow.newDetachedCancellationScope(() -> failureSaga.compensate()).run();
      throw e;
    }

This is not possible with the Temporal definition of termination. Termination is an operation that is intentionally a hard stop that precludes running any cleanup logic. The solution is not to use termination for normal business operations. Use cancellation instead.

It is like asking Linux to run some code in your application as a reaction to kill -9.

Currently we are indeed using cancellation scope for termination. In the catch block, as @tihomir mentioned we are able to differentiate between Timeout and Canceled Failures as well. So, my only question was how can we pass the type of failure(Timeout/Canceled) to saga compensation activity. @maxim Do you mean to say it’s not possible?

Also, I have one more question for you. We are planning to use Saga pattern in Java SDK. Could you please confirm if this is going to be supported long time? Hope it’s not an experimental feature.

Could you please confirm if this is going to be supported long time?

Compensation (saga) impl is included in the SDK there shouldn’t be plans to remove it for some reason. You can extend this impl and add custom logic if needed as well.

how can we pass the type of failure(Timeout/Canceled) to saga compensation activity

Without extending the saga impl I think you could have two separate Saga objects in your workflow and then call compensate on one or the other depending on the type of failure you catch in workflow code.
If you need to pass the failure inputs to your compensation activities, you could addCompensation in your catch block and pass the failure message and type to the compensation activity.

Your options:

  • Inside the compensation closures you can access any workflow variables to change the compensation logic dynamically.
  • Create two Saga objects, one per failure type.
  • Clone Saga.java and modify it to add an argument to Saga.compensate. Then pass this argument to each compensation callback.

@tihomir , @maxim Thank you so much for the info, much appreciated.