To communicate or not communicate the eventual consistency

Please let me know if there is a better place to ask this question.

What are patterns that engineers have used to successfully “manage” the eventual consistency concept between the UI and backend (between Command and Query)? I think this is what makes most people nervous about embarking on an ES voyage.

For example, over the last couple of days, I’ve been running stress tests against a simple sandbox. The test is a combination of requests that create and update, and requests that read. In general, performance is great. But, there is always the possibility that a Projection may not have been updated yet. Under reasonable duress, I’m seeing over a second elapsed time between event storage (Command side) and when the projection EventHandler completes and the data would be available for query (Projection side). Using the gateway sendAndWait has helped minimize the gap, but it still exists.

If the UI sends a query request and the data isn’t there, should the UI just retry in a couple of seconds? Should the UI know if it sent a query request with a bad ID, or if the request is OK but the respective event handling is lagging behind slightly? Or is the UI workflow suppose to be designed to avoid immediately querying data that should have been just updated? <= My assumption so far

I keep feeling like the Command results in a ‘promise’ to the user that the projection side will be updated soon, and there should be a way to let the UI know when the projection has been updated. I’m trying hard to evolve my thinking from traditional immediate update workflows, and it’s hard.

Thank you!

Hi Jerry,

I’ll try to answer your question.

In some (rare, by my opinion) situations, the eventual consistency is not a good fit. Business is mostly fine with eventual consistency.

By using CQRS, you are embracing asynchronous communication:

The command side is reacting to Command messages (Commands) and publishes Event messages (Events) as a result. The Query side is eventually handling these Events and creates data projections in some database that should be easy to query. Query side can expose Query messages (Queries) for the UI to consume them.
So far you have two components in your application: command side and query side. Command side has Commands and Events as API, and Query side has Queries as API.

Let’s consider two scenarios:

Synchronous

You are building REST/WEB endpoint/component on top of command and query components, and you have to synchronize on this level (before you reach your UI). In this case, your endpoint Controller will receive user requests and map it to Command that will be handled by the command component. In return, the Query component will eventually materialize the date in some projection. The thing here is to block by issuing a Subscription Query to query component and exit with HTTP 200 once the projection is updated. This way your UI logic stays synchronous, but you block too early in my opinion. I prefer to be asynchronous all the way if possible.

Asynchronous

You are building Websockets or RSocket endpoint/component on top of command and query components, and do not have to synchronize on this level anymore (these protocols are bidirectional and async by its nature). In this case, your endpoint Controller will receive user requests and map it to Command that will be handled by the command component. In return, the Query component will eventually materialize the date in some projection.

In this case, UI workflow supposes to be designed to subscribe to Queries (through the websocket/rsocket component) and update the state of your UI component. You do not block your flow in this case, and this option should scale better. It will decouple your components more as well.

Subscription queries are introduced with version 3.3 of Axon Framework: https://axoniq.io/blog-overview/introducing-subscription-queries

Best,
Ivan

Synchronous
You are building REST/WEB endpoint/component on top of command and query components, and you have to synchronize on this level (before you reach your UI). In this case, your endpoint Controller will receive user requests and map it to Command that will be handled by the command component. In return, the Query component will eventually materialize the date in some projection. The thing here is to block by issuing a Subscription Query to query component and exit with HTTP 200 once the projection is updated. This way your UI logic stays synchronous, but you block too early in my opinion. I prefer to be asynchronous all the way if possible.

We’ve implemented this approach. Most UI developers expect to read their own writes wrt to server interaction.

Our commands have the notion of a “correlation” with an event and a notification via subscription query.
In the external request handler, before the command is sent, a subscriptionQuery is opened. The view is then responsible for emitting the notification when the event that correlates to the command eventually updates the view.

There are a number of concerns with this approach:

  • there is a tight coupling between correlated commands and events
  • view updates are more complicated - although if you are implementing queries for other reasons this is likely less of a concern
  • our naive/simple implementation between our external endpoints (REST/graphql) and handlers that perform synchronous commands and query subscriptions is blocking with timeouts
    – this may be a serious issue for apps that must handle a high volume mutations

[Related, from the UI perspective is “optimistic UI”.
This is where the UI updates locally with the expectation that the command succeeds and the expected data will eventually materialise.

Optimistic UI can be useful for both Synchronous and Asynchronous server interaction as it makes the UI more responsive by hiding mutation latency - at the expense of implementation complexity.]

I think it’s fair to say that finding a good fit between UI framework, browser<->server integration, and Axon requires some experimentation and prototyping.
The right answer will depend on specific requirements - I don’t believe there are any widely implemented solutions yet?

Yes, the right answer will depend on the specific requirements you have.

I have radically described two different options (1. synchronize on the backend and 2. don’t synchronize on the backend)
My option is don't synchronize on the backend :slight_smile: and design your UI components in a message-driven fashion so they can be updated (via subscription queries) asynchronously. If this is not the option for your case then you can choose “optimistic UI” or you can block on the backend side (REST option I have mentioned before). Blocking threads is something we try to avoid these days, so we can scale vertically better.

I had some experiments with Websockets, RSocket, and Server-Sent Events to extend the message stream to the UI (Angular, React). With RSocket you will have backpressure out of the box.

I don’t have anything publically available yet, and I will work on that in the future.

There should be a demo application written in Vaadin under this link https://axoniq.io/blog-overview/introducing-subscription-queries

Thank you both for the insight. I saw subscription queries mentioned in the Axon documentation before, but it didn’t click. With your suggestion, I found an example (I liked the chat-scaling-out one) and implemented a POC using Subscription Queries the other day. For now, it perfectly meets what I was trying to prove.

Thanks again!!