We are using Axon Framework 4.11
with Spring Boot
and no Axon Server (yet), relying on the SimpleQueryBus
implementation and sending queries via the QueryGateway
. Here we sent two queries and expected them to be processed in parallel, when in fact they were processed sequentually.
A look at its implementation shows it invokes handlers directly in the calling thread (no executor). So even though the QueryGateway
calls return CompletableFuture
s, the query handlers themselves are executed sequentially (blocking) by default if the underlying query handlers block. That could us off-guard at first.
Example
// Two queries in parallel
var f1 = queryGateway.query(new FindExampleQuery(id1), ResponseTypes.optionalInstanceOf(ExampleDto.class));
var f2 = queryGateway.query(new FindOtherQuery(id2), ResponseTypes.optionalInstanceOf(OtherDto.class));
CompletableFuture.allOf(f1, f2).join();
// Blocking query handler
@QueryHandler
public Optional<ExampleDto> on(FindExampleQuery query) {
return exampleQueryService.fetchExample(query.getId());
}
// Actual query service
@Service
public class ExampleQueryService {
private final ExampleRepository exampleRepository;
public TransactionalQueryService(ExampleRepository exampleRepository) {
this.exampleRepository = exampleRepository;
}
@Transactional(readOnly = true)
public Optional<ExampleDto> fetchExample(String id) {
return exampleRepository.findById(id)
.map(ExampleEntity::toDto);
}
}
The examples I have seen in the documentation all implement blocking query handlers.
Question
What is the recommended/idiomatic approach when using Spring Boot
, SimpleQueryBus
, and no reactive programming to enable non-blocking query handlers?
Would it be to use @Async
and returned a completed CompletableFuture
in the query handlers? Or is there any alternative QueryBus implementation which calls query handlers off-thread? Are there any examples and recommendations which we may have missed?
@Async("queryExecutor")
@QueryHandler
public CompletableFuture<Optional<ExampleDto>> on(FindExampleQuery query) {
var result = exampleQueryService.fetchExample(query.getId());
return CompletableFuture.completedFuture(result);
}