Saga Pattern Support

Introduction

Since decades, Computer Science is dealing with transactions. ACID principles form the ground of our traditional database systems. The core of transactions is to process a set of operations together in an all-or-nothing scenario. Especially in monolithic applications, transactions are used to keep data consistent all the time - although multiple rows or tables are affected.

Transactional consistency is important and required by a lot of business scenarios. Transactions are widely used and accepted and have proven their practical relevance over time being. Traditional approaches to use transactions in a distributed system turn out to not work. Therefore other patterns have been established over time, like avoiding transactions by design or Saga.

The Saga architecture pattern provides transaction management by using a sequence of local transactions. Saga participants perform those local transactions as a unit of work. The participants are responsible for rolling back the operations by applying compensational actions.

The Saga pattern guarantees that either all operations complete successfully or the corresponding compensations are run to (semantically) undo the previous work.

Requests can abort and must be idempotent (retriable). Compensating Requests semantically undo the effect of a request. They cannot abort and must be idempotent and commutative.

Warning:

One major problem of Saga is the lack of isolation. This means, that it is guaranteed to obtain a consistent state only at the end of the Saga, but not in any intermediate step.

Saga Pattern Support in ISW

For Domain Services based on Java Spring Boot Stack 2.0, IBM Industry Solutions Workbench provides various capabilities, to support the development of transactional use cases.

The extension is available for the following stacks:

  • Java Spring Boot Stack 2.0.0 and later

How it works

If the Saga Pattern support extension is enabled, the Solution Designer UI offers additional modelling capabilities.

Domain and Integration Services can now be created with a Saga Pattern role. Services with the Orchestrator role are representing the starting point of a Saga. Saga Participant services contain the actual execution logic of the steps within a Saga and are associated to an Orchestrator. When a Saga is triggered, all of the associated participants are executed by the orchestrator in parallel. To learn more about modelling a Saga, please check Modelling of Saga Pattern role in Services.

Dependending on what has been modelled in the Solution Designer UI, you will get auto-generated stub files for your orchestrators and participants. There, you will only have to implement the execution, completion and compensation logic for your use case, while a secure connection to the lra-coordinator is established out of the box. This part will be automatically provided in the SDK, not requiring any custom implementation. To learn more about implementating a Saga, please check Implement Saga pattern.

A Saga can also spawn across several microservices. Therefore, API Operations can be marked as a Saga participant as well. It is recommended, that those API operations will again trigger the execution of an Orchestrator. When doing this, the Saga context will automatically be applied. To learn more about this, please check Modelling of API Operations and Implementing Saga across multiple services.

Tip:

For your compensation logic, you need to know what has been changed in the related context of the Saga. In most cases, the only input you will get there is the Long-Running-Action-Id. When designing the use case, it is often helpful to consider storing those Long-Running-Action-Ids to make use of them during compensation.

Deployment

It is recommended to deploy Saga use cases through an Application Composition project. When doing this, the Application needs to have exactly one lra-coordinator in it. To share the Saga context between all the services in the Application, the services must be bound to the same lra-coordinator. By default, the binding to the lra-coordinator is automatically set up correctly and does not require any custom configuration.


Tip:

According to best practices, the Saga should not leave the boundaries of an Application Composition project.

Custom configuration

When Saga services are used together with a lra-coordinator within an Application Composition project, all required configurations are set up correctly by default. However, in some cases it might be necessary to apply custom configurations. Changing the default settings is easily possible through the Solution Designer.

Disable security

For all Saga services, the communication with the lra-coordinator, is secured by default. To disable security in your project, which is implementing the Saga, you have to execute the following steps:

  • Open action Configure Component in your Application Composition project
  • Add a custom configuration to your component:
    # Feature flags
    feature:
      secureNarayana: false
  • Save your custom configuration and commit your changes
Note:

To get this setup running, please ensure, that the used lra-coordinator has disabled security as well.

Use a different lra-coordinator service

By default, a Saga service in an Application Composition project always uses the lra-coordinator, which is also deployed through the same application. The information, which lra-coordinator is used, is defined within a secret (naming convention k5-appAcronym-lra-coordinator-binding). This secret needs to be created and filled with proper values by the lra-coordinator component.

Configuring a different lra-coordinator for a component can be done by the following steps:

  • Create a new secret with a custom name
  • Set the correct values to the secret (key/value pair):
    lra.coordinatorUrl: <url-of-lra-coordinator>
    lra.coordinator-context-path: <context-path-of-lra-coordinator>
  • Open action Configure Component of your Saga service in your Application Composition project
  • Add a custom configuration to your component:
    # Saga
    camel:
      lra:
        coordinatorBindingSecretName: "<your-new-secret-name>"
  • Save your custom configuration and commit your changes