Long Running Actions for MicroProfile on Helidon… Data Integrity for Microservices.
By: Paul Parkinson
Tags: Data, Microprofile, Helidon, Microservices, Oracle

Microservices create challenges for data consistency and integrity that necessitate changes in the transaction processing and data patterns used by them.

Traditional systems rely on two-phase commit and XA protocols that use synchronous communication, locking of resources, and recovery via rollback (or commit as the case may be). While this provides strong consistency and isolation it does not scale well in a microservices environment due to the latency of held locks and, therefore, is suitable for only a small subset of such use cases (generally those with lower throughput requirements)

The saga pattern uses asynchronous communication and local resources only (no distributed locks) and recovery via compensating actions. This scales well and so is well suited for long running transactions in a microservices environment. Additional application design considerations are necessary, however, for read isolation and compensation logic and debugging can be difficult.

The MicroProfile LRA, as stated at https://github.com/eclipse/microprofile-lra , “[…]introduces APIs for services to coordinate activities.

The main thrust of the proposal introduces an API for loosely coupled services to coordinate long running activities in such a way as to guarantee a globally consistent outcome without the need to take locks on data.”

In many ways, the Long Running Activities (LRA) APIs are to Sagas between microservices what the Java Transaction API (JTA) is to two-phase commit and XA for traditional applications (or for contained use within a single microservice).

Much is written on this topic already and so the focus here is on running LRA in Helidon. Helidon uses the Narayana implementation of the LRA specification.

Using LRA in Helidon MP

There are two main components involved:

Participants: these are the JAX-RS microservices that will be involved in the LRA and are annotated with the standard org.eclipse.microprofile.lra.annotation.ws.rs.LRA values listed in the following table from the specification…

The LRA coordinator: this is a devoted service that the participants implicitly register with (as a result of their LRA annotations) and is responsible for driving a LRA to complete, compensate, etc..

Update your LRA participant pom.xml (s)

Add dependencies for LRA annotations and runtime.

While not necessary for runtime, a build-time LRA annotation checker can be used.

Add LRA feature in your application

Your MP javax.ws.rs.core.Application will likely already have code similar to this. Just add the line that includes the DynamicFeature io.narayana.lra.filter.FilterRegistration :

Annotate the endpoints

Add LRA annotations to your microservice endpoints.

In this example we have two services involved in a LRA which are an order service and and inventory service.

Order Service:

call to placeOrder by client begins an LRA due to @LRA(value = LRA.Type.RequiresNew) annotation and registers service with the LRA coordinator

by client begins an LRA due to annotation and registers service with the LRA coordinator calls inventory service to check inventory with/propagating LRA_HTTP_CONTEXT_HEADER with the LRA id (achieved by registering client LRA filters) .

with the LRA id (achieved by registering client LRA filters) . if inventory exists (the default) success is returned to order service which implicitly completes the LRA and the coordinator calls the @Complete annotated method completeOrder

annotated method if inventory does not exist (achieved by calling the removeInventory endpoint on inventory service) failure is returned to order service which cancels the LRA and the coordinator calls the @Compensate annotated method cancelOrder

Inventory Service:

call to reserveInventoryForOrder by order service is executed within LRA started and propagated by order service due to @LRA(value = LRA.Type.Mandatary) annotation and registers service with the LRA coordinator

by order service is executed within LRA started and propagated by order service due to annotation and registers service with the LRA coordinator checks inventory and returns the result (success or fail) to the order service

Build and deploy the LRA coordinator

Add dependencies to build and run the LRA coordinator service jar.

Build and start the LRA app

Run the example ./build.sh script to build and package the order and inventory participant services and the coordinator.

Start the services.

Run the LRA app for the success case

Notice the output from the order service:

OrderResource.placeOrder in LRA due to LRA.Type.REQUIRES_NEW lraId:[...]

OrderResource.placeOrder response from inventory:inventorysuccess

OrderResource.completeOrder

Notice the output from the inventory service:

InventoryResource.addInventory

InventoryResource.placeOrder in LRA due to LRA.Type.MANDATORY lraID:[...]

InventoryResource.completeOrder prepare item for shipping lraId:[...]

Run the LRA app for the compensating case

Notice the output from the order service:

OrderResource.placeOrder in LRA due to LRA.Type.REQUIRES_NEW lraId:[...]

OrderResource.placeOrder response from inventory:inventoryfailure

OrderResource.cancelOrder

Notice the output from the inventory service:

InventoryResource.removeInventory

InventoryResource.placeOrder in LRA due to LRA.Type.MANDATORY lraID:[...]

InventoryResource.cancelOrder put inventory back lraId:[...]

What next?

The LRA specification is currently at 88% completion and expected to be included in the next MicroProfile release. Progress can be followed here: https://github.com/eclipse/microprofile-lra/milestone/1

Helidon will continue to facilitate this and other areas of microservice data patterns upon this by providing.

Equivalent Helidon SE support

Messaging (and event sourcing) features

HA configurations

Database integration features

The example can be found here: https://github.com/paulparkinson/helidon-examples-lra