When using spring-boot-starter-graphql with spring-boot-starter-web and @EnableAsync I would expect it to honor the spring.mvc.async.request-timeout property. Instead it is throwing a AsyncRequestTimeoutException after 30 seconds.
Steps to Reproduce
- Create a project with
spring-boot-starter-webandspring-boot-starter-graphql - Add
@EnableAsync - Add Property
spring.mvc.async.request-timeout=50s - Add
QueryMappingmethod that sleeps.
@QueryMapping
@Async
public CompletableFuture<Book> bookById(@Argument String id) {
return CompletableFuture.supplyAsync(() -> {
try {
LOGGER.info("Starting Sleep");
TimeUnit.SECONDS.sleep(45);
return Book.getById(id);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
}
- Send Request and observe error
.w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.context.request.async.AsyncRequestTimeoutException]
I observed this behavior using Spring Boot 3.3.5
Comment From: bclozel
Thanks for raising this and providing a sample.
This has been implemented in https://github.com/spring-projects/spring-boot/pull/42966 and I'm afraid we cannot backport it to a previous generation.
Comment From: matthew-js-porter
@bclozel the pull request you linked seems to be specific to an SSE request timeout so I would not expect that to address this issue, since the sample being used is not using GraphQL over SSE
Comment From: bclozel
Sorry about that. I think this is expected as query mappings don't align one to one with HTTP requests and in general GraphQL is transport agnostic.
If you wish to apply async behavior at the query level you can use one of the return types that involves async behavior. See https://docs.spring.io/spring-graphql/reference/controllers.html#controllers.schema-mapping.return.values
If you would like to discuss this further please create an issue in the spring-graphql project.
Comment From: matthew-js-porter
@bclozel no worries! I spent some time in the debugger to try and get a better understanding on this and realized it was a async timeout from tomcat. Adding this to the sample fixes the problem.
@Bean
public TomcatConnectorCustomizer tomcatConnectorCustomizer() {
return connector -> connector.setAsyncTimeout(Duration.ofMinutes(1).toMillis());
}
Thanks!