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

IllegalStateException: The current thread cannot be blocked using quarkus-cache with quarkus-resteasy-reactive #22251

Closed
jsmrcka opened this issue Dec 15, 2021 · 10 comments · Fixed by #22271

Comments

@jsmrcka
Copy link
Contributor

jsmrcka commented Dec 15, 2021

Describe the bug

Using quarkus-cache together with reactive execution model causes an IllegalStateException.
Calling e.g. a reactive endpoint:

@NonBlocking
@Path("/value")
public class ReactiveResource {

    public static final String CACHE_NAME = "mycache";

    private static int counter = 0;

    @CacheResult(cacheName = CACHE_NAME)
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public Uni<String> getValue() {
        return Uni.createFrom().item("Value: " + counter++);
    }
}

results in:

java.lang.IllegalStateException: The current thread cannot be blocked: vert.x-eventloop-thread-14

Expected behavior

OK response.

Actual behavior

2021-12-15 16:26:55,525 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (vert.x-eventloop-thread-14) HTTP Request to /value failed, error id: ec3dd35b-7d65-49bb-b718-8cddaeef8715-1: java.lang.IllegalStateException: The current thread cannot be blocked: vert.x-eventloop-thread-14
	at io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:30)
	at io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:62)
	at io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:43)
	at io.quarkus.cache.runtime.CacheResultInterceptor.intercept(CacheResultInterceptor.java:65)
	at io.quarkus.cache.runtime.CacheResultInterceptor_Bean.intercept(Unknown Source)
	at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
	at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
	at org.acme.ReactiveResource_Subclass.getValue(Unknown Source)
	at org.acme.ReactiveResource$quarkusrestinvoker$getValue_8f786ee95084b5bc35ad81779e5d693d969db3a2.invoke(Unknown Source)
	at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
	at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:7)
	at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:141)
	at org.jboss.resteasy.reactive.server.handlers.RestInitialHandler.beginProcessing(RestInitialHandler.java:49)
	at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:17)
	at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:7)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1193)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:362)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:340)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1193)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
	at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:67)
	at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:37)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$12.handle(VertxHttpRecorder.java:486)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$12.handle(VertxHttpRecorder.java:483)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$1.handle(VertxHttpRecorder.java:152)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$1.handle(VertxHttpRecorder.java:134)
	at io.vertx.core.http.impl.Http1xServerRequestHandler.handle(Http1xServerRequestHandler.java:67)
	at io.vertx.core.http.impl.Http1xServerRequestHandler.handle(Http1xServerRequestHandler.java:30)
	at io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:50)
	at io.vertx.core.impl.DuplicatedContext.emit(DuplicatedContext.java:168)
	at io.vertx.core.http.impl.Http1xServerConnection.handleMessage(Http1xServerConnection.java:145)
	at io.vertx.core.net.impl.ConnectionBase.read(ConnectionBase.java:156)
	at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:153)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:93)
	at io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtensionHandler.channelRead(WebSocketServerExtensionHandler.java:99)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.vertx.core.http.impl.Http1xUpgradeToH2CHandler.channelRead(Http1xUpgradeToH2CHandler.java:116)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	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:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:722)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:658)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:584)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:496)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
	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(Thread.java:829)

How to Reproduce?

reactive-cache-reproducer.zip

Use attached reproducer:

unzip reactive-cache-reproducer.zip
cd reactive-cache-reproducer
mvnw clean test

Output of uname -a or ver

Linux 5.15.6-200.fc35.x86_64

Output of java -version

openjdk version "11.0.11" 2021-04-20 OpenJDK Runtime Environment AdoptOpenJDK-11.0.11+9 (build 11.0.11+9) OpenJDK 64-Bit Server VM AdoptOpenJDK-11.0.11+9 (build 11.0.11+9, mixed mode)

GraalVM version (if different from Java)

No response

Quarkus version or git rev

999-SNAPSHOT (2.6 - main)

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

Apache Maven 3.8.1 (05c21c65bdfed0f71a2f2ada8b84da59348c4c5d)

Additional information

No response

@jsmrcka jsmrcka added the kind/bug Something isn't working label Dec 15, 2021
@quarkus-bot
Copy link

quarkus-bot bot commented Dec 15, 2021

/cc @FroMage, @geoand, @gwenneg, @stuartwdouglas

stuartwdouglas added a commit to stuartwdouglas/quarkus that referenced this issue Dec 16, 2021
jsmrcka added a commit to jsmrcka/quarkus-test-suite that referenced this issue Dec 16, 2021
stuartwdouglas added a commit to stuartwdouglas/quarkus that referenced this issue Dec 19, 2021
stuartwdouglas added a commit to stuartwdouglas/quarkus that referenced this issue Dec 19, 2021
@quarkus-bot quarkus-bot bot added this to the 2.7 - main milestone Dec 20, 2021
@gsmet gsmet modified the milestones: 2.7 - main, 2.6.1.Final Dec 22, 2021
gsmet pushed a commit to gsmet/quarkus that referenced this issue Dec 22, 2021
@Leonardofreua
Copy link

I'm having this same problem using Multi instead Uni.

@gwenneg
Copy link
Member

gwenneg commented Jan 1, 2022

Hi @Leonardofreua. Caching Multi is not currently supported by the quarkus-cache extension. There will be a warning soon at build time if you add any caching annotations to a method returning Multi and the caching logic will be skipped entirely when the method is invoked. In the meantime, I would advise to remove the caching annotations you added on your Multi method.

@mmizera
Copy link

mmizera commented Jan 24, 2022

Was this fixed (in which version) ? I still have (probably) same issue (quarkus 2.6.1)

2022-01-24 14:44:00,064 ERROR [io.sma.rea.mes.provider] (vert.x-eventloop-thread-14) SRMSG00201: Error caught while processing a message: java.lang.IllegalStateException: The current thread cannot be blocked: vert.x-eventloop-thread-14
at io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:30)
at io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:62)
at io.smallrye.mutiny.groups.UniAwait.indefinitely(UniAwait.java:43)
at io.quarkus.cache.runtime.CacheResultInterceptor.intercept(CacheResultInterceptor.java:96)
at io.quarkus.cache.runtime.CacheResultInterceptor_Bean.intercept(Unknown Source)
at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)

I consuming kafka messages using Incomming annotation. Consumer needs some configuration, which is retrieved from etcd (Uni converted from CompletableFuture). That Uni result is cached (using CachedResult) and everything works ok.

But when cache is invalidated calling empty method with CacheInvalidation annotation, then it just dies.
There is blocking code in cache interceptor [ CacheResultInterceptor.intercept(CacheResultInterceptor.java:96) ].

@gwenneg
Copy link
Member

gwenneg commented Jan 24, 2022

@mmizera Could you please show me the signature of the method annotated with @CacheInvalidate?

@gwenneg
Copy link
Member

gwenneg commented Jan 24, 2022

#22683 may fix your issue but it will only be available with Quarkus 2.7.0 which is currently a release candidate (CR1).

@mmizera
Copy link

mmizera commented Jan 24, 2022

Ok, It was probably "false-positive". I reviewed my code again and problem was in cache method signature. I used generic response type T, so it was blocking. I modified it to and it works. There is if in CacheResultInterceptor for this, so with generic result it ends in blocking block of source code.

@manuelwallrapp
Copy link

manuelwallrapp commented Feb 9, 2022

If I want to cache the outcome of a Multi as an Uni, it causes a blocking Thread when this method is used by an REST Endpoint as well. In my opinion that should work:

@CacheResult(cacheName = "uniListCache")
public Uni<List> getRepos(String projectKey) {
return Uni.createFrom().item(myGitReposMulti.subscribe().runSubscriptionOn(Infrastructure.getDefaultWorkerPool()).asStream().collect(Collectors.toList()));
}

Can you verify that?

I attached a test:
reactive-cache-reproducer-withmmulti.zip

@gwenneg
Copy link
Member

gwenneg commented Feb 9, 2022

Hi @manuelwallrapp.

The thread is blocked because of the cached method body and not because of the quarkus-cache extension.

If you remove the @CacheResult annotation, the test will still fail. On the other hand, if you change the method body like this, the test will pass:

    @CacheResult(cacheName = "listCacheFromMulti")
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public Uni<List<String>> getValueListFromMulti() {
        return Multi.createFrom().items("1", "2").collect().asList();
    }

@manuelwallrapp
Copy link

manuelwallrapp commented Feb 9, 2022

Hi @gwenneg

Oh my bad. The asStream call creates a blocking call. Thank you very much.

Would be great to put such an example in the Cache guide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants