Introduction
In this blog we are going to see the Performance of the SAGA design pattern with Apache Camel, we followed the saga pattern that Héctor Garcia-Molina and Kenneth Salem created in 1987. Our use case, allows us to enforce the dispersed business transaction, handle the error cases, and ensure that we can leave the system in an ultimately consistent state. It does this by dividing a database transaction into two types of sub-transactions: atomic transactions and compensating transactions. The database will run these transactions if there is a need to roll back the whole process.
OVERVIEW
Saga executions bring together disparate services that communicate through any medium to provide a globally consistent result. Saga EIPs differ from traditional ACID distributed transactions in that the status of the many participating services is guaranteed only at the end of the Saga, not in any intermediate steps. They’re also great for stateless cloud services because they don’t need a transaction log. Saga EIPs, unlike transactions, are not required to be completed in a short period of time. As a result, they can survive for longer periods of time, ranging from seconds to days. In Camel routes, compensating actions can be declared using Java.
SAGA EIP OPTIONS:
S.No | Name | Description | Default | Type |
1 | Propagation | Set the Saga propagation mode (REQUIRED, REQUIRES_NEW, MANDATORY, SUPPORTS, NOT_SUPPORTED, NEVER). | REQUIRED | SagaPropagation |
2 | CompletionMode | Determine how the Saga should be considered complete. When set to AUTO, the Saga is completed when the exchange that initiates the Saga is processed successfully or compensated when it completes exceptionally. When set to MANUAL, the user must complete or compensate the Saga using the saga: complete or saga: compensate endpoints. | AUTO | SagaCompletionM ode |
3 | TimeoutInMillisec onds | Set the maximum amount of time for the Saga. After the timeout is expired, the saga is compensated automatically (unless a different decision has been taken in the meantime). | Long | |
4 | Compensation | The compensation endpoint URI must be called to compensate for all changes done in the route. The route corresponding to the compensation URI must perform compensation and complete without error. If an error occurs during compensation, the Saga service calls the compensation URI again to retry. | SagaActionUri Definition | |
5 | completion | The completion endpoint URI is called when the Saga is completed successfully. The route corresponding to the completion URI must perform completion tasks and terminate without error. If an error occurs during completion, the Saga service calls the completion URI again to retry. | SagaActionUri Definition | |
6 | Option | Allows saving properties of the current exchange in order to reuse them in a compensation or completion callback route. Options are usually helpful, for example, to store and retrieve identifiers of objects that are deleted in compensating actions. Option values are transformed into input headers of the compensation/completion exchange. | List |
SAGA SERVICE CONFIGURATION
The Saga EIP specifies that a service must implement the org.apache.camel.saga interface. The Camel context now includes CamelSagaService. Camel currently supports the following Saga Service:
InMemorySagaService: This is a basic implementation of the Saga EIP that does not support advanced features (no remote context propagation, no consistency guarantee in case of application failure).
Example
You want to place a new order and you have two different services in your system: one managing the orders and one managing the stock. Logically you can place an order if you have adequate stock for it. With the Saga EIP you can model the direct:buy route as a Saga composed of two distinct actions, one to create the order and one to take the stock. from("direct:buy")
.saga()
.to("direct:newOrder")
.to("direct:reserveCredit");
The buy action does not change for the rest of the examples. The following are some of the different choices for modeling New Order and Reserve stock action:from("direct:newOrder")
.saga()
.propagation(SagaPropagation.MANDATORY)
.compensation("direct:cancelOrder")
.transform().header(Exchange.SAGA_LONG_RUNNING_ACTION)
.bean(orderManagerService, "newOrder")
.log("Order ${body} created");
The propagation mode is set to MANDATORY in this case, which means that every transaction flowing through this route must already be a Saga. The direct:newOrder route declares a compensating action that is called direct:cancelOrder, responsible for undoing the order in case the Saga is canceled. Each exchange always contains an Exchange.SAGA_LONG_RUNNING_ACTION header that is used
here is the id of the order. This identifies the order to delete in the interconnected compensating action, but it is not a requirement (options can be used as an alternative solution). The compensating action of
direct:newOrder is direct:cancelOrder and it is shown below:from("direct:cancelOrder")
.transform().header(Exchange.SAGA_LONG_RUNNING_ACTION)
.bean(orderManagerService, "cancelOrder")
.log("Order ${body} cancelled");
It is called automatically by the Saga EIP implementation when the order should be canceled. It does not end with an error. In case an error is thrown in the direct:cancelOrder route, the EIP implementation should periodically retry to execute the compensating action up to a certain limit. This means that any compensating action must be idempotent, so it should take into account that it may be triggered multiple times and should not fail in any case. If compensation cannot be done after all
retries, a manual intervention process should be triggered by the Saga implementation.
Conclusion
In conclusion, we covered the basic guide of SAGA EIP with Apache Camel. How we can use the Apache Camel to implement the SAGA. For more detail, you can refer to SAGA EIP.