Monthly Archives: May 2022

Saga Orchestration – A Copy-and-Paste Implementation

Requirements:

  • BPMN activities:
    • service task activity
    • fork and split gateway for a parallel execution of activities
    • compensation activity
  • compensation logics
    • automatic trigger of compensation on exception
    • reverse order on compensation
  • store, read, and pass variables
  • save point after each activity for fault tolerance (e.g., DB or file system)
  • visualization of transactions for debugging and manual compensation purposes

Scenario:

  1. create pending order
  2. reduce inventory
  3. fulfil payment
  4. publish order

Start a saga instance:

// start saga instance from saga model "order creation"
orderCreationSaga = sagaFactory.startNewInstanceFromSagaModel("order creation")
orderId = orderCreationSaga.getVariables().getOrWait("orderId", 500, Millis)
orderCreationSaga.waitForCompletion(3_000, Millis)

The task to execute a single saga step:

	public SagaStepRunnable(SagaInstance sagaInstance, SagaActivity sagaActivity) {
		this.sagaInstance = sagaInstance;
		this.sagaActivity = sagaActivity;
	}

	@Override
	public void run() {
		SagaLog sagaLog = sagaInstance.getSagaLog();

		try {
			sagaActivity.execute(sagaInstance.getVariables());
			// if the thread crashes at this point, the saga restarts after the last successful activity.
			// hence, all activities (local and remote) must be idempotent.
			sagaLog.storeSuccessEventFor(sagaActivity, sagaInstance.getVariables());
		} catch (Exception e) {
			sagaLog.storeFailureEventFor(sagaActivity, sagaInstance.getVariables(), e);
			sagaInstance.switchToCompensationMode();
		}

		sagaInstance.scheduleNextActivities();
	}

Questions:

  • necessary to sync “entity creation for order” with “current variables” and “success event”?
  • remote AND local services must be idempotent?