Request-scoped DataLoaderRegistry blows up
kijanowski opened this issue · 6 comments
I have given a try the code introduced by #12
I used version 1.0 and created a CustomGraphQLInvocation
class based on https://github.com/graphql-java/graphql-java-spring/pull/12/files#diff-cb93f026b7b911f1cdfa731e56caa39f
The comment in this PR says:
I think it's overkill to create a ExecutionInputCustomizer for this, simply defining a (request-scoped) @bean should be sufficient.
I've create that bean like this:
@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public DataLoaderRegistry dataLoaderRegistry() {...}
And it fails with:
{
"timestamp": "2019-09-29T11:07:42.456+0000",
"status": 500,
"error": "Internal Server Error",
"message": "Error creating bean with name 'scopedTarget.dataLoaderRegistry': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.",
"path": "/graphql"
}
If I change the scope to WebApplicationContext.SCOPE_APPLICATION
then it doesn't throw an error. How should this be approached to have a request scoped DataLoaderRegistry
?
A working workaround is to change the DefaultGraphQLInvocation
and remove:
@Autowired(required = false)
DataLoaderRegistry dataLoaderRegistry;
and replace:
if (dataLoaderRegistry != null) {
executionInputBuilder.dataLoaderRegistry(dataLoaderRegistry);
}
with:
DataLoaderRegistry dataLoaderRegistry = new DataLoaderRegistry();
// Loader specific configuration
DataLoader<String, CountryTO> countryLoader = DataLoader.newDataLoader(dataFetchers.countryBatchLoader());
dataLoaderRegistry.register("countries", countryLoader);
// Loader specific configuration end
executionInputBuilder.dataLoaderRegistry(dataLoaderRegistry);
executionInputBuilder.context(dataLoaderRegistry); // add it to the context to use it later in fetchers
The whole thing can be checked out from https://github.com/kijanowski/graphql-springboot-scope-issue
I think we need to remove the DataLoaderRegistry
from the DefaultGraphQLInvocation
. Spring Request-scoped beans will not work when creating new threads.
I think it's better to create a custom ExecutionInputCustomizer
which creates the DataLoaderRegistry
and attaches it to the ExecutionInput
.
Can I ask the status of this? Is it possible to configure the DataLoaderRegistry to be request scoped as things stand?
Hello,
I had this working with request scoped dataloader, by removing the async. Not great, but at least I can have batch compute and avoid the 1+N request issue.
For this, I simply use return CompletableFuture.completedFuture(value);
in the DataLoader load method.
This way, my loader function can access all request scoped beans that are needed.
We can user Spring Async Executor Framework for DataLoaderRegistery
This project is now archived in favor of the official Spring GraphQL integration.