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?
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:
Start the application with a specific property to disable the query handlers of certain components.
Construct a custom endpoint to reset a Streaming Event Processor, that also toggles the query handlers of a message handling component.
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.
If you are curious, you can find said issue here.
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();
}
}
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.