Let's image that we have a simple endpoint called CreateEntity
in some .NET Core
controller and we have two services which saves the entity to the Mongo
and Neo4j
databases (in this particular order).
I want to implement some pattern to make sure that we have consistent data.
Why ?
Because we might have situations when entity is added to Mongo
, but the call to the Neo4j
crashes. And that means the data remains inserted in the Mongo
.
One solution is to use try-catch
statement for second service call and use compensating transactions for Mongo
insert. In this case, remove the entity from Mongo
. Another one could be using a state for entity and put Pending as initial state and mark it as Completed after Neo4j
call is made with success.
I also looked over SAGA and 2PC patterns but they are used in asynchronous contexts and across microservices (you know, by communicating between microservices with RabbitMQ, Kafka, Service Bus & other)
But in my case there is a single microservice. Any ideas on this ?
CodePudding user response:
You want to wrap your mongo operations in a transaction. If the Neo4j fails, roll back the mongo transaction, if it succeeds, commit it.
CodePudding user response:
Temporal open source project can be used to implement compensations and SAGAs in synchronous scenarios. The basic idea of Temporal is that you write your code as a failure of the code that performs remote calls cannot happen. In case of a process failure Temporal is going to migrate the execution to a different machine in exactly the same state as before the crash. The state includes all local variables and blocking calls. Here is a SAGA example using Temporal Java SDK:
public void bookTrip(String name) {
// Configure SAGA to run compensation activities in parallel
Saga.Options sagaOptions = new Saga.Options.Builder().setParallelCompensation(true).build();
Saga saga = new Saga(sagaOptions);
try {
String carReservationID = activities.reserveCar(name);
saga.addCompensation(activities::cancelCar, carReservationID, name);
String hotelReservationID = activities.bookHotel(name);
saga.addCompensation(activities::cancelHotel, hotelReservationID, name);
String flightReservationID = activities.bookFlight(name);
saga.addCompensation(activities::cancelFlight, flightReservationID, name);
} catch (ActivityFailure e) {
saga.compensate();
throw e;
}
}
Unfortunately, the .NET SDK for Temporal is still under development as of the summer of 2022. The currently supported languages are Typescript/Javascript, Go, Java, Python & PHP.
For .NET you can also use Durable Task Framework and/or Azure Durable Functions that are based on the same idea as Temporal.