Quarkus: GraphQL Batch processing throws a ContextNotActiveException

Created on 19 Nov 2020  路  13Comments  路  Source: quarkusio/quarkus

Describe the bug
GraphQL Batch processing throws a ContextNotActiveException

Expected behavior
As described in https://quarkus.io/guides/microprofile-graphql#batching Batch processing should work =)

Actual behavior
GraphQL Batch processing throws a ContextNotActiveException

To Reproduce
See attachment for simple setup:
TestGraphQLResource.zip

Simple Source relations like the following work as expected:

    public Yyy getYyy(@Source Xxx xxx) {
        return mapYyy(xxx);
    }

    public List<Zzz> getZzz(@Source Xxx xxx) {
        return mapZzz(xxx);
    }

When changing to Batch processing a ContextNotActiveException is thrown.

    public List<Yyy> getYyy(@Source List<Xxx> xxxList) {
        return xxxList.stream().map(this::mapYyy).collect(Collectors.toList());
    }

    public List<List<Zzz>> getZzz(@Source List<Xxx> xxxList) {
        return xxxList.stream().map(this::mapZzz).collect(Collectors.toList());
    }

Configuration
No properties set for GraphQL

Screenshots

2020-11-19 11:29:59,847 ERROR [io.sma.graphql] (ForkJoinPool.commonPool-worker-13) SRGQL012000: Data Fetching Error: java.util.concurrent.CompletionException: java.lang.RuntimeException: javax.enterprise.context.ContextNotActiveException: interface javax.enterprise.context.RequestScoped
        at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:314)
        at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:319)
        at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1702)
        at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1692)
        at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
        at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
        at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
        at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
        at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
Caused by: java.lang.RuntimeException: javax.enterprise.context.ContextNotActiveException: interface javax.enterprise.context.RequestScoped
        at io.smallrye.graphql.execution.batchloader.SourceBatchLoader.doSourceCall(SourceBatchLoader.java:83)
        at io.smallrye.graphql.execution.batchloader.SourceBatchLoader.access$000(SourceBatchLoader.java:27)
        at io.smallrye.graphql.execution.batchloader.SourceBatchLoader$1.run(SourceBatchLoader.java:63)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at io.smallrye.graphql.execution.batchloader.SourceBatchLoader.invokePrivileged(SourceBatchLoader.java:56)
        at io.smallrye.graphql.execution.batchloader.SourceBatchLoader.lambda$load$0(SourceBatchLoader.java:49)
        at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
        ... 6 more
Caused by: javax.enterprise.context.ContextNotActiveException: interface javax.enterprise.context.RequestScoped
        at whatever.wherever.TestGraphQLResource_ClientProxy.arc$delegate(TestGraphQLResource_ClientProxy.zig:68)
        at whatever.wherever.TestGraphQLResource_ClientProxy.getZzz(TestGraphQLResource_ClientProxy.zig:249)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at io.smallrye.graphql.execution.datafetcher.helper.ReflectionHelper.invoke(ReflectionHelper.java:45)
        at io.smallrye.graphql.execution.batchloader.SourceBatchLoader.doSourceCall(SourceBatchLoader.java:80)
        ... 12 more

Environment (please complete the following information):

  • Output of uname -a or ver:
  • Output of java -version:
    openjdk version "11.0.7" 2020-04-14
    OpenJDK Runtime Environment GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02)
    OpenJDK 64-Bit Server VM GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02, mixed mode, sharing)
  • GraalVM version (if different from Java):
  • Quarkus version or git rev: 1.9.2
  • Build tool (ie. output of mvnw --version or gradlew --version): 3.6.3

Additional context
(Add any other context about the problem here.)

aregraphql aresecurity aresmallrye kinbug

All 13 comments

/cc @jmartisk, @phillip-kruger, @sberyozkin

Hi @funcbro , thanks for this. We know about this one. Can you test against 1.10.0 and let me know, or alternatively if you have a project example I can look at ?

Hi @phillip-kruger, just tested the problem in 1.10.0.Final. The Exception is still thrown.

Here is the Test-Project based on the 1.10.0.Final archetype:
microprofile-graphql-quickstart.zip

And here is the test-query:

{
  xxx {
    id
    a
    yyyId
    yyy {
      id
      a
    }
    zzzIdList
    zzz {
      id
      a
    }
  }
}

Thanks ! I am look at this.

@funcbro For now, remove @RequestScoped and see if that work please. Let me know

@phillip-kruger just a little hint should you be unsure where to look - https://github.com/quarkusio/quarkus/blob/1.10.0.Final/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLExecutionHandler.java#L57 this is where we activate the request context. I think the problem with batching is that the handling is performed asynchronously in different threads which don't have the request context active (it didn't go through this piece of code)?
It has nothing to do with the application containing @RequestScoped or not. We have to activate the request scope always.

Yes, I just wanted to give @funcbro a "workaround". Do you want to do a fix ?

Haven't tried but I think that if the processing is asynchronous then the annotation won't have any effect.
Sure, I can have a look into it.

Removing RequestScoped (even it if worked) is no option in my case. Might work for the test-case though.
Also tried to use ActivateRequestContext annotation which also has no impact

Not sure yet how to fix it.
We are processing a request, the request context is active, but during the processing, we spawn a new thread in the ForkJoin pool (see https://github.com/smallrye/smallrye-graphql/blob/1.0.15/server/implementation/src/main/java/io/smallrye/graphql/spi/datafetcher/DefaultDataFetcherService.java#L54) - and that thread accesses CDI beans, so it needs to have the request context propagated into it - how should we do that?
There is the problem that this is happenning in SmallRye code, so we don't have access to Arc.container().requestContext() - do we need to move this code to Quarkus codebase to be able to use Arc and pass the active context to the new thread? Or switch from ForkJoin pool to an executor managed by SR Context Propagation?
Perhaps @mkouba has some clues...

Isn't it what SmallRye Context Propagation is supposed to solve?

Well, it's not a Quarkus-only problem. If you spawn a new thread you need to make sure the request context is (a) active or (b) propagated. For (a) you can use e.g. the RequestContextController (portable CDI API). For (b) @gsmet is right and the SmallRye Context Propagation is the way to go.

I've started tinkering with the integration of SR Context Propagation into SR GraphQL. Hope to know more next week, it's kind of an uncharted territory for me :)

Was this page helpful?
0 / 5 - 0 ratings