quarkus: AsyncHealthCheck does not work with Hibernate Reactive

Describe the bug

The following health check based on the AsyncHealthCheck example from the smallrye-health doc works perfectly fine with Quarkus 1.13.7 when Hibernate Reactive is used:

@ApplicationScoped
@Liveness
public class MyHealthCheck implements AsyncHealthCheck {

    @Inject
    Mutiny.Session session;

    @Override
    public Uni<HealthCheckResponse> call() {
        return session.createNativeQuery("SELECT 1")
                .getSingleResult()
                .replaceWith(TRUE)
                .onFailure().recoverWithItem(FALSE)
                .onItem().transform(dbState ->
                        HealthCheckResponse.named("Health check").status(dbState).build()
                );
    }
}

With Quarkus 2.2.2.Final, the same code no longer works because the call method isn’t run on the Vert.x event loop thread.

Expected behavior

The check should be run on the Vert.x event loop thread.

Actual behavior

2021-09-15 11:09:45,142 ERROR [io.sma.health] (executor-thread-0) SRHCK01000: Error processing Health Checks: java.lang.IllegalStateException: HR000068: This method should exclusively be invoked from a Vert.x EventLoop thread; currently running on thread 'executor-thread-0'
        at org.hibernate.reactive.common.InternalStateAssertions.assertUseOnEventLoop(InternalStateAssertions.java:40)
        at org.hibernate.reactive.mutiny.impl.MutinySessionFactoryImpl.proxyConnection(MutinySessionFactoryImpl.java:150)
        at org.hibernate.reactive.mutiny.impl.MutinySessionFactoryImpl.openSession(MutinySessionFactoryImpl.java:69)
        at io.quarkus.hibernate.reactive.runtime.ReactiveSessionProducer.createMutinySession(ReactiveSessionProducer.java:23)
        at io.quarkus.hibernate.reactive.runtime.ReactiveSessionProducer_ProducerMethod_createMutinySession_1321d110ee9e92bda147899150401e0a136779c7_Bean.create(ReactiveSessionProducer_ProducerMethod_createMutinySession_1321d110ee9e92bda147899150401e0a136779c7_Bean.zig:247)
        at io.quarkus.hibernate.reactive.runtime.ReactiveSessionProducer_ProducerMethod_createMutinySession_1321d110ee9e92bda147899150401e0a136779c7_Bean.create(ReactiveSessionProducer_ProducerMethod_createMutinySession_1321d110ee9e92bda147899150401e0a136779c7_Bean.zig:285)
        at io.quarkus.arc.impl.RequestContext.getIfActive(RequestContext.java:68)
        at io.quarkus.arc.impl.ClientProxies.getDelegate(ClientProxies.java:33)
        at io.quarkus.hibernate.reactive.runtime.ReactiveSessionProducer_ProducerMethod_createMutinySession_1321d110ee9e92bda147899150401e0a136779c7_ClientProxy.arc$delegate(ReactiveSessionProducer_ProducerMethod_createMutinySession_1321d110ee9e92bda147899150401e0a136779c7_ClientProxy.zig:60)
        at io.quarkus.hibernate.reactive.runtime.ReactiveSessionProducer_ProducerMethod_createMutinySession_1321d110ee9e92bda147899150401e0a136779c7_ClientProxy.createNativeQuery(ReactiveSessionProducer_ProducerMethod_createMutinySession_1321d110ee9e92bda147899150401e0a136779c7_ClientProxy.zig:294)
        at org.acme.FailingHealthCheck.call(FailingHealthCheck.java:24)
        at org.acme.FailingHealthCheck_ClientProxy.call(FailingHealthCheck_ClientProxy.zig:203)
        at io.smallrye.context.impl.wrappers.SlowContextualSupplier.get(SlowContextualSupplier.java:21)
        at io.smallrye.mutiny.operators.uni.builders.UniCreateFromDeferredSupplier.subscribe(UniCreateFromDeferredSupplier.java:25)
        at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
        at io.smallrye.mutiny.operators.uni.UniOnFailureFlatMap.subscribe(UniOnFailureFlatMap.java:31)
        at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransform.subscribe(UniOnItemTransform.java:22)
        at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
        at io.smallrye.mutiny.operators.uni.UniAndCombination$UniHandler.subscribe(UniAndCombination.java:203)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
        at io.smallrye.mutiny.operators.uni.UniAndCombination$AndSupervisor.run(UniAndCombination.java:71)
        at io.smallrye.mutiny.operators.uni.UniAndCombination$AndSupervisor.access$000(UniAndCombination.java:53)
        at io.smallrye.mutiny.operators.uni.UniAndCombination.subscribe(UniAndCombination.java:50)
        at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
        at io.smallrye.mutiny.operators.uni.UniBlockingAwait.await(UniBlockingAwait.java:54)
        at io.smallrye.mutiny.groups.UniAwait.atMost(UniAwait.java:61)
        at io.smallrye.health.SmallRyeHealthReporter.getHealth(SmallRyeHealthReporter.java:192)
        at io.smallrye.health.SmallRyeHealthReporter_ClientProxy.getHealth(SmallRyeHealthReporter_ClientProxy.zig:802)
        at io.quarkus.smallrye.health.runtime.SmallRyeHealthHandler.getHealth(SmallRyeHealthHandler.java:11)
        at io.quarkus.smallrye.health.runtime.SmallRyeHealthHandlerBase.doHandle(SmallRyeHealthHandlerBase.java:44)
        at io.quarkus.smallrye.health.runtime.SmallRyeHealthHandlerBase.handle(SmallRyeHealthHandlerBase.java:31)
        at io.quarkus.smallrye.health.runtime.SmallRyeHealthHandler.handle(SmallRyeHealthHandler.java:7)
        at io.quarkus.smallrye.health.runtime.SmallRyeHealthHandlerBase.handle(SmallRyeHealthHandlerBase.java:19)
        at io.vertx.ext.web.impl.BlockingHandlerDecorator.lambda$handle$0(BlockingHandlerDecorator.java:48)
        at io.vertx.core.impl.ContextImpl.lambda$null$0(ContextImpl.java:159)
        at io.vertx.core.impl.AbstractContext.dispatch(AbstractContext.java:100)
        at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$1(ContextImpl.java:157)
        at io.quarkus.vertx.core.runtime.VertxCoreRecorder$13.runWith(VertxCoreRecorder.java:543)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:829)

How to Reproduce?

Steps to reproduce:

  1. Clone https://github.com/gwenneg/quarkus-async-health-check
  2. Run ./mvnw clean install

Output of uname -a or ver

No response

Output of java -version

No response

GraalVM version (if different from Java)

No response

Quarkus version or git rev

2.2.2.Final

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

No response

Additional information

No response

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 24 (23 by maintainers)

Most upvoted comments

I took a super quick look at this, and it seems like it would require more than just Quarkus work.

Essentially what is going on now is that Quarkus creates a Route (which is always blocking) and when that route is called the various health checks are called as part of that request - there is no separation between sync and async health checks. So this likely needs some smallrye work. cc @radcortez @kenfinnigan

cc @xstefank

If @stuartwdouglas also agrees with this approach, I can certainly take it on.