quarkus: @Blocking doesn't work

Hi guys.

Describe the bug

@Blocking does not seem to be taken into account when using a reactive call stack.

“”" Annotation used to indicate that the annotated method is inherently blocking and so should not be executed on a non-blockable thread (I/O thread, event loops…) “”"

Expected behavior

Using another thread pool than vertx when @Blocking is detected

Actual behavior

The example of the code logs a WARN because we see a vert.x-eventloop-thread used.

2023-03-08 09:32:42,616 WARN  [io.ver.cor.imp.BlockedThreadChecker] (vertx-blocked-thread-checker) Thread Thread[vert.x-eventloop-thread-0,5,main] has been blocked for 3688 ms, time limit is 2000 ms: io.vertx.core.VertxException: Thread blocked
        at java.base@17.0.6/java.lang.Thread.sleep(Native Method)
        at org.test.GreetingResource.schedule(GreetingResource.java:39)
        at org.test.GreetingResource.hello(GreetingResource.java:26)
        at org.test.GreetingResource$quarkusrestinvoker$hello_d1fd18c65b32335d71a4b7282527a960c5fc08f2.invoke(Unknown Source)
        at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
        at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:114)
        at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:145)
        at org.jboss.resteasy.reactive.server.handlers.RestInitialHandler.beginProcessing(RestInitialHandler.java:48)
        at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:23)
        at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:10)
        at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
        at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
        at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:101)
        at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:87)
        at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:140)
        at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
        at io.vertx.ext.web.handler.impl.StaticHandlerImpl.lambda$sendStatic$1(StaticHandlerImpl.java:290)
        at io.vertx.ext.web.handler.impl.StaticHandlerImpl$$Lambda$959/0x000000080113e208.handle(Unknown Source)
        at io.vertx.core.impl.future.FutureImpl$3.onSuccess(FutureImpl.java:141)
        at io.vertx.core.impl.future.FutureBase.lambda$emitSuccess$0(FutureBase.java:54)
        at io.vertx.core.impl.future.FutureBase$$Lambda$936/0x0000000801135c68.run(Unknown Source)
        at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174)
        at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167)
        at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
        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@17.0.6/java.lang.Thread.run(Thread.java:833)

How to Reproduce?

@ApplicationScoped
public class GreetingResource {
    private static final Logger log = LoggerFactory.getLogger(GreetingResource.class);
    private final Semaphore semaphore = new Semaphore(1);

    @Scheduled(every = "1s")
    Uni<Void> everySecond() throws InterruptedException {
        schedule();
        return Uni.createFrom().voidItem();
    }
    
    @Blocking
    public void schedule() throws InterruptedException {
        log.info("scheduler run new thread");
        if (semaphore.tryAcquire(10, SECONDS)) {

            log.info("thread in activity");
            try {
                Thread.sleep(50000);
            } finally {
                semaphore.release();
            }
        }
    }
}

Output of uname -a or ver

No response

Output of java -version

openjdk version “17.0.6” 2023-01-17 LTS
OpenJDK Runtime Environment (build 17.0.6+10-LTS)
OpenJDK 64-Bit Server VM (build 17.0.6+10-LTS, mixed mode, sharing)

GraalVM version (if different from Java)

No response

Quarkus version or git rev

2.16.4.Final

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

Apache Maven 3.9.0 (9b58d2bad23a66be161c4664ef21ce219c2c8584)
Maven home: E:\git\scoop\apps\maven\current
Java version: 17.0.6, vendor: BellSoft, runtime: E:\git\scoop\apps\liberica17-full-jdk\current Default locale: en_US, platform encoding: Cp1252
OS name: “windows 10”, version: “10.0”, arch: “amd64”, family: “windows”

Additional information

Thanks !

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 28 (26 by maintainers)

Most upvoted comments

It’s expected, this gives some explanation

Ok, so it seems that in the dev mode there are two separate thread pools: core blocking pool (represented by ExecutorBuildItem and potentially backed by ThreadFactoryBuildItem) and the worker pool used for Vertx.executeBlocking(). However, in the test and production mode there is a single thread pool used for both the core pool and worker pool.

The relevant code can be found in the io.quarkus.vertx.core.runtime.QuarkusExecutorFactory.createExecutor() method.

    @Scheduled(every = "1s", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
    // @Blocking
    void everySecond() throws InterruptedException {
        Thread.sleep(65000L);
        log.info("thread = " + Thread.currentThread().getName());
    }

@geoand I believe it is the same behavior, even if @Blocking is put on the `everySecond()``method. @mpusg can you check?