Prevent Queries execution during catch up

Hello AxonIQ Community,

I’m reaching out for advice on a challenge we’re facing with the Axon Framework in our microservice setup, which uses an H2 In-Memory Database. Our main issue arises during service startup: we need to rebuild projections based on events, and we want to prevent queries to the service until this catch-up process is complete.

Is there a “best Practice” way with the Axon Framework?

Edit: I previously found this post Disabling query handlers until event replays are done its old but already mention that is would be a nice feature. =)

Any insights or experiences with similar challenges in the Axon Framework would be greatly appreciated.

Thanks in advance!
Timo

Hey @beefman,

I wouldn’t say there’s a best practice for Axon Framework, to be frank.
I’ve seen several approaches up to now, all depending on the team’s preference:

  1. Start the application with a specific property to disable the query handlers of certain components.
  2. Construct a custom endpoint to reset a Streaming Event Processor, that also toggles the query handlers of a message handling component.
  3. A Spring Profile that switches the behavior of Streaming Event Processors.

All these allow for a way to define the start of the replay to not have query handlers be invoked. Flipping them back, automatically would require coding effort to validate the progress on the tokens.

One way to do this is to fetch a token and pull it through the ReplayToken#isReplay(TrackingToken) operation.
This operation will return true if the token in question is still replaying and return false otherwise. This operation should be performed periodically, and for all segments of a Streaming Event Processor, to be able to deduce whether the replay is done.

And by doing so, enables the query handlers to be toggled on again.

Or, you could take the route as described in the referred to thread, which takes an operator-approach to things by being required to shutdown and startup the application in question.

By the way, I have added an issue to Axon Framework’s board to provide built-in functionality for this. As I agree with what Allard states in the referred-to thread. :slight_smile:
If you are curious, you can find said issue here.

Hi,

thank you @Steven_van_Beelen for your information. Due to the fact that we store the projection data in memory the different profiles did not work. But i found this way to solve the problem and wanted to share my solution with the community.

I created a component that fires spring application events that describe the state of the application. I used the EventProcessingConfiguration to get the current state of all EventProcessor. At the start i publish an event CatchUpInitializedEvent and on finish i publish CatchUpFinishedEvent.

With this component i disconnect or and reconnect the query channel. For me it is working. I do not know if there are any side effects with this solution. All my tests seems fine. The axon server shows the correct connected queries and i also tried it with multiple instances.

@Component
public class CatchUpCommunicationConnector {

    @Autowired
    private AxonServerConnectionManager axonServerConnectionManager;

    private void connectQueryChannel() {
        axonServerConnectionManager.connections().forEach((context, isConnected) -> {
            if (isConnected) {
                QueryChannel queryChannel = axonServerConnectionManager.getConnection(context).queryChannel();
                if (queryChannel instanceof QueryChannelImpl) {
                    ((QueryChannelImpl) queryChannel).reconnect();
                }
            }
        });
    }

    private void disconnectQueryChannel() {
        axonServerConnectionManager.connections().forEach((context, isConnected) -> {
            if (isConnected) {
                QueryChannel queryChannel = axonServerConnectionManager.getConnection(context).queryChannel();
                if (queryChannel instanceof QueryChannelImpl) {
                    ((QueryChannelImpl) queryChannel).disconnect();
                }
            }
        });
    }

    @EventListener
    public void handleCatchUpInitialized(CatchUpInitializedEvent event) {
        disconnectQueryChannel();
    }

    @EventListener
    public void handleCatchUpFinished(CatchUpFinishedEvent event) {
        connectQueryChannel();
    }
}
1 Like

Glad to help out, @beefman!

The suggested solution to disconnect and connect the Axon Server Query Channel will indeed do the trick.
I wouldn’t expect any concerns from the code snippet you’ve shared.

So, thanks for sharing this with the community!
I’m pretty sure others can benefit from this as well.