OpenSleigh: tackling state persistence, part 1

OpenSleigh: tackling state persistence, part 1

2021, Jan 17    

Hi All! Here we go with another article about OpenSleigh. Today we’re going a bit deeper into the rabbit hole and see how it is dealing with state persistency.

The last time we introduced the library, discussed a bit about what the Saga pattern is and what’s the general idea behind Orchestration and Choreography.

Now. Sagas are all about messages. Commands get issued and events get published. At the same time though, we have to make sure that changes to the internal state of the Saga are being persisted safely. Also, we want outgoing messages to be dispatched only when the local state is saved.

In an ideal world, we wouldn’t have to worry about this, but unfortunately, we have to deal with a bunch of nasty stuff like concurrency and horizontal scalability.

For example, let’s suppose that our application is composed of 2 services: a Web API and a background worker service. Let’s call them “Orders API” and “Orders processor“, that should help you get the picture.

The Web API is responsible for receiving client requests, packing them into nice messages, and pushing them to a service bus. The worker subscribes to those messages and uses OpenSleigh to handle Saga execution.

This is a pretty nice and standard way of handling long-running operations asynchronously. Works pretty well and, most importantly, scales pretty well.

But what happens if we have more instances of the worker help handling the load? Suppose for a moment that the Orders API pushes a _ProcessOrder **_command for the same order “123” twice. It had a hiccup, a network glitch. Sh*t happens.

Now, this quite unfortunate event means that the same order will probably be processed twice. Moreover, if we have more worker instances, it might happen that both the messages will be dispatched and processed concurrently.

How does OpenSleigh handle these situations?

First of all, we can assume that the ProcessOrder command starts the ProcessOrderSaga. Each OpenSleigh Saga has a configurable unique ID, which is used also as correlation ID. In this case, I think it’s safe to use the Order ID to populate this value.

When the Orders processor service receives a message, OpenSleigh will extract the correlation id and use it to fetch the right Saga state from the persistence storage.

If no existing Saga State is available, we check if that message is capable of starting a new Saga. If so, we’ll get a shiny new instance and we can finally get down to business.

That’s all for today. The next time we’ll continue analyzing the flow and we’ll see some code. Ciao!