Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot inform ArC to instantiate bean in worker thread #40418

Closed
chonton opened this issue May 2, 2024 · 8 comments
Closed

Cannot inform ArC to instantiate bean in worker thread #40418

chonton opened this issue May 2, 2024 · 8 comments
Labels
area/arc Issue related to ARC (dependency injection) area/reactive kind/question Further information is requested

Comments

@chonton
Copy link
Contributor

chonton commented May 2, 2024

Describe the bug

HttpAuthenticator is instantiated in io thread. I cannot figure out where to use a @Blocking annotation to inform ArC to run on worker thread.

Expected behavior

ArC creates beans in worker thread or ability to annotate injection points so that ArC will inject in worker thread.

Actual behavior

Bean is instantiated in io thread

org.jboss.resteasy.reactive.common.core.BlockingNotAllowedException
	at org.jboss.resteasy.reactive.client.impl.InvocationBuilderImpl.unwrap(InvocationBuilderImpl.java:199)
	at org.jboss.resteasy.reactive.client.impl.InvocationBuilderImpl.method(InvocationBuilderImpl.java:313)
	at com.example.q4p.auth.client.oidc.OidcWellKnown$$QuarkusRestClientInterface.getConfiguration(Unknown Source)
	at com.example.q4p.auth.oidc.OidcVerifier$PrincipalMapping.createConsumer(OidcVerifier.java:149)
	at com.example.q4p.auth.oidc.OidcVerifier$PrincipalMapping.<init>(OidcVerifier.java:130)
	at com.example.q4p.auth.oidc.OidcVerifier$ServiceMapping.<init>(OidcVerifier.java:227)
	at com.example.q4p.auth.oidc.OidcVerifier.createPrincipalMapping(OidcVerifier.java:66)
	at com.example.q4p.auth.oidc.OidcVerifier.lambda$addIssuers$1(OidcVerifier.java:75)
	at java.base/java.util.ArrayList.forEach(Unknown Source)
	at com.example.q4p.auth.oidc.OidcVerifier.addIssuers(OidcVerifier.java:72)
	at com.example.q4p.auth.oidc.OidcVerifier.<init>(OidcVerifier.java:59)
	at com.example.q4p.auth.oidc.OidcVerifier_Bean.doCreate(Unknown Source)
	at com.example.q4p.auth.oidc.OidcVerifier_Bean.create(Unknown Source)
	at com.example.q4p.auth.oidc.OidcVerifier_Bean.create(Unknown Source)
	at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:119)
	at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:38)
	at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:35)
	at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:32)
	at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69)
	at io.quarkus.arc.impl.ComputingCacheContextInstances.computeIfAbsent(ComputingCacheContextInstances.java:19)
	at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:35)
	at com.example.q4p.auth.oidc.OidcVerifier_Bean.get(Unknown Source)
	at com.example.q4p.auth.oidc.OidcVerifier_Bean.get(Unknown Source)
	at com.example.q4p.auth.oidc.OidcPrincipalFactory_Bean.doCreate(Unknown Source)
	at com.example.q4p.auth.oidc.OidcPrincipalFactory_Bean.create(Unknown Source)
	at com.example.q4p.auth.oidc.OidcPrincipalFactory_Bean.create(Unknown Source)
	at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:119)
	at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:38)
	at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:35)
	at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:32)
	at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69)
	at io.quarkus.arc.impl.ComputingCacheContextInstances.computeIfAbsent(ComputingCacheContextInstances.java:19)
	at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:35)
	at com.example.q4p.auth.oidc.OidcPrincipalFactory_Bean.get(Unknown Source)
	at com.example.q4p.auth.oidc.OidcPrincipalFactory_Bean.get(Unknown Source)
	at com.example.q4p.auth.oidc.OidcIdentityProvider_Bean.doCreate(Unknown Source)
	at com.example.q4p.auth.oidc.OidcIdentityProvider_Bean.create(Unknown Source)
	at com.example.q4p.auth.oidc.OidcIdentityProvider_Bean.create(Unknown Source)
	at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:119)
	at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:38)
	at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:35)
	at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:32)
	at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69)
	at io.quarkus.arc.impl.ComputingCacheContextInstances.computeIfAbsent(ComputingCacheContextInstances.java:19)
	at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:35)
	at com.example.q4p.auth.oidc.OidcIdentityProvider_Bean.get(Unknown Source)
	at com.example.q4p.auth.oidc.OidcIdentityProvider_Bean.get(Unknown Source)
	at io.quarkus.arc.impl.InstanceImpl.getBeanInstance(InstanceImpl.java:325)
	at io.quarkus.arc.impl.InstanceImpl$InstanceIterator.next(InstanceImpl.java:363)
	at io.quarkus.vertx.http.runtime.security.HttpAuthenticator.<init>(HttpAuthenticator.java:103)
	at io.quarkus.vertx.http.runtime.security.HttpAuthenticator_Bean.doCreate(Unknown Source)
	at io.quarkus.vertx.http.runtime.security.HttpAuthenticator_Bean.create(Unknown Source)
	at io.quarkus.vertx.http.runtime.security.HttpAuthenticator_Bean.create(Unknown Source)
	at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:119)
	at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:38)
	at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:35)
	at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:32)
	at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69)
	at io.quarkus.arc.impl.ComputingCacheContextInstances.computeIfAbsent(ComputingCacheContextInstances.java:19)
	at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:35)
	at io.quarkus.vertx.http.runtime.security.HttpAuthenticator_Bean.get(Unknown Source)
	at io.quarkus.vertx.http.runtime.security.HttpAuthenticator_Bean.get(Unknown Source)
	at io.quarkus.arc.impl.InstanceImpl.getBeanInstance(InstanceImpl.java:325)
	at io.quarkus.arc.impl.InstanceImpl.getInternal(InstanceImpl.java:309)
	at io.quarkus.arc.impl.InstanceImpl.get(InstanceImpl.java:190)
	at io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder$AbstractAuthenticationHandler.handle(HttpSecurityRecorder.java:236)
	at io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder$AbstractAuthenticationHandler.handle(HttpSecurityRecorder.java:224)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1285)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:137)
	at io.quarkus.vertx.http.runtime.cors.CORSFilter.handle(CORSFilter.java:138)
	at io.quarkus.vertx.http.runtime.cors.CORSFilter.handle(CORSFilter.java:21)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1285)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:137)
	at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$6.handle(HttpServerCommonHandlers.java:184)
	at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$6.handle(HttpServerCommonHandlers.java:180)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1285)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:137)
	at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$6.handle(HttpServerCommonHandlers.java:184)
	at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$6.handle(HttpServerCommonHandlers.java:180)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1285)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:137)
	at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:68)
	at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:37)
	at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$2.handle(HttpServerCommonHandlers.java:86)
	at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$2.handle(HttpServerCommonHandlers.java:69)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$1.handle(VertxHttpRecorder.java:167)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$1.handle(VertxHttpRecorder.java:143)
	at io.vertx.core.impl.ContextImpl.emit(ContextImpl.java:328)
	at io.vertx.core.impl.DuplicatedContext.emit(DuplicatedContext.java:166)
	at io.vertx.core.http.impl.Http2ServerRequest.dispatch(Http2ServerRequest.java:90)
	at io.vertx.core.http.impl.Http2ServerStream.onHeaders(Http2ServerStream.java:125)
	at io.vertx.core.http.impl.Http2ServerConnection.onHeadersRead(Http2ServerConnection.java:186)
	at io.vertx.core.http.impl.Http2ConnectionBase.onHeadersRead(Http2ConnectionBase.java:214)
	at io.vertx.core.http.impl.Http2ServerConnection.onHeadersRead(Http2ServerConnection.java:38)
	at io.vertx.core.http.impl.VertxHttp2ConnectionHandler.channelRead(VertxHttp2ConnectionHandler.java:416)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.vertx.core.http.impl.Http1xUpgradeToH2CHandler.channelRead(Http1xUpgradeToH2CHandler.java:110)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:289)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)
	at io.netty.handler.codec.MessageToMessageCodec.channelRead(MessageToMessageCodec.java:111)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.vertx.core.http.impl.Http1xOrH2CHandler.end(Http1xOrH2CHandler.java:61)
	at io.vertx.core.http.impl.Http1xOrH2CHandler.channelRead(Http1xOrH2CHandler.java:38)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:289)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
	at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:801)
	at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:509)
	at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:407)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Unknown Source)

How to Reproduce?

No response

Output of uname -a or ver

Darwin YGDMXD4XYJ 23.4.0 Darwin Kernel Version 23.4.0: Fri Mar 15 00:10:42 PDT 2024; root:xnu-10063.101.17~1/RELEASE_ARM64_T6000 arm64

Output of java -version

openjdk version "17.0.6" 2023-01-17 LTS OpenJDK Runtime Environment Zulu17.40+19-CA (build 17.0.6+10-LTS) OpenJDK 64-Bit Server VM Zulu17.40+19-CA (build 17.0.6+10-LTS, mixed mode, sharing)

Quarkus version or git rev

3.10.0

Build tool (ie. output of mvnw --version or gradlew --version)

Apache Maven 3.9.5

Additional information

No response

@chonton chonton added the kind/bug Something isn't working label May 2, 2024
@quarkus-bot quarkus-bot bot added the area/arc Issue related to ARC (dependency injection) label May 2, 2024
@quarkus-bot
Copy link

quarkus-bot bot commented May 2, 2024

/cc @Ladicek (arc), @manovotn (arc), @mkouba (arc)

@mkouba
Copy link
Contributor

mkouba commented May 3, 2024

CDI/ArC creates beans, by design, on the current thread. The CDI API (jakarta.enterprise.context.spi.Contextual etc.) is not asynchronous so there's no proper way to offload the construction logic to a worker thread and continue when the bean instance is ready. This feature would require a non-trivial API redesign.

A good mitigation strategy is to initialize the beans eagerly (if possible), for example an @ApplicationScoped/@Singleton bean during application start.

If you really need to execute some blocking code in your bean, in @PostConstruct, etc. and it's possible that this bean could be created on an event loop (io thread), then you'll need to offload the logic manually, something like:

import io.quarkus.runtime.BlockingOperationControl;

@RequestScoped
public class MyBean {
   
   @Inject
   Vertx vertx;
   
   @PostConstruct
   void init() {
      if (BlockingOperationControl.isBlockingAllowed()) {
         somethingThatBlocks();
      } else {
          vertx.executeBlocking(() -> {
            // note that request context is not activated here
            somethingThatBlocks();
            return null;
           });
      }
   }

   void somethingThatBlocks() {}

}

And of course, it gets more complicated if you need to synchronize the state, i.e. if the blocking operation initializes some state of the bean.

@gsmet gsmet added kind/question Further information is requested and removed kind/bug Something isn't working labels May 13, 2024
@gsmet
Copy link
Member

gsmet commented May 13, 2024

@chonton did @mkouba answer your question?

@chonton
Copy link
Contributor Author

chonton commented May 13, 2024

Eager initialization works for one of my uses. For my other uses, I have used the BlockingOperationControl check.

@chonton chonton closed this as completed May 13, 2024
@chonton
Copy link
Contributor Author

chonton commented May 13, 2024

Consider adding this to documentation somewhere?

@mkouba
Copy link
Contributor

mkouba commented May 14, 2024

Consider adding this to documentation somewhere?

That's a good point. BlockingOperationControl is not mentioned in the docs at all. And we should probably add something like a "Reactive traps" section in the CDI reference guide.

@manovotn @Ladicek @gsmet WDYT?

@Ladicek
Copy link
Contributor

Ladicek commented May 14, 2024

we should probably add something like a "Reactive traps" section in the CDI reference guide

Totally makes sense!

@manovotn
Copy link
Contributor

we should probably add something like a "Reactive traps" section in the CDI reference guide

Totally makes sense!

+1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/arc Issue related to ARC (dependency injection) area/reactive kind/question Further information is requested
Projects
None yet
Development

No branches or pull requests

5 participants