question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Quarkus REST Reactive ServerRequestFilter Has To Be Blocking When Using With Hibernate Panache Reactive

See original GitHub issue

I need to write a ServerRequestFilter do some checking by sending request via RestClient. Here is the code:

FDC1123B-0FEE-4D6A-8452-4E4F62F9380E

The RestClient is reactive:

F39DB864-0AD5-4DF4-8CD8-FE1240CBDD14

So in CheckAuthFilter I use await and marked the check() method blocking:

4F51F258-986B-4AF6-935E-90D56FC92EB1

F1FDF934-4061-44AE-9F15-3A62DFC965FE

In the document of ServerRequestFilter, it describes several return types:

58BEA6F6-88E9-4CB4-9849-EACF4AE64CA3

I can’t use Uni<RestResponse<Void>> return type so I can avoid calling .await() method of Uni. Because in my web API, I have some hibernate-panache-reactive code like this:

BD509C5B-F0C4-4A6A-8B5F-8913B4822602

I have to mark the above method as @Blocking, and also my ServerRequestFilter as blocking. If I modify my ServerRequestFilter to return Uni, and make my above web method as @NonBlocking like this:

8EA5062A-5E07-4356-A7FE-5C161890C899

1A3AA89C-0B2E-4A65-B9B1-23C3028A6215

Then during the runtime, it will throw hibernate related runtime error like this:

67B253FE-CFC2-4D76-8715-2CFD5E9AD9AC

From above screenshot I can see the the error is:

java.lang.IllegalStateException: HR000069: Detected use of the reactive Session from a different Thread than the one which was used to open the reactive Session - this suggests an invalid integration; original thread: 'vert.x-eventloop-thread-4' current Thread: 'vert.x-eventloop-thread-8'

Here is the code I changed that could reproduce the above exception:

➤ git diff                                                                                                                                                                                                                                                                                                      10:27:53
diff --git a/src/main/java/cn/alchemystudio/taskserver/auth/CheckAuthFilter.java b/src/main/java/cn/alchemystudio/taskserver/auth/CheckAuthFilter.java
index 4d20b44..84d9466 100644
--- a/src/main/java/cn/alchemystudio/taskserver/auth/CheckAuthFilter.java
+++ b/src/main/java/cn/alchemystudio/taskserver/auth/CheckAuthFilter.java
@@ -4,6 +4,7 @@ import cn.alchemystudio.taskserver.client.HtyucProxy;
 import cn.alchemystudio.taskserver.commons.Bag;
 import cn.alchemystudio.taskserver.commons.Commons;
 import io.smallrye.common.annotation.Blocking;
+import io.smallrye.mutiny.Uni;
 import org.eclipse.microprofile.rest.client.inject.RestClient;
 import org.jboss.logging.Logger;
 import org.jboss.resteasy.reactive.RestResponse;
@@ -30,8 +31,8 @@ public class CheckAuthFilter {
     HtyucProxy uc;
 
     @ServerRequestFilter
-    @Blocking
-    public Optional<RestResponse<Void>> check(ContainerRequestContext ctx) throws Exception {
+//    @Blocking
+    public Uni<RestResponse<Void>> check(ContainerRequestContext ctx) throws Exception {
 
         logger.debug(":::CHECK_AUTH_FILTER:::");
         logger.debug("CHECK_AUTH_FILTER -> REQUEST_CTX / " + ctx.toString());
@@ -40,7 +41,7 @@ public class CheckAuthFilter {
 
         if (ctx.getHeaderString("HtySudoerToken") == null) {
             logger.debug("CHECK_AUTH_FILTER CHECK FAILED -> empty `HtySudoerToken` header");
-            return Optional.of(RestResponse.status(Response.Status.FORBIDDEN));
+            return Uni.createFrom().item(RestResponse.status(Response.Status.FORBIDDEN));
         }
 
         var tokenStr = ctx.getHeaderString("HtySudoerToken");
@@ -71,7 +72,7 @@ public class CheckAuthFilter {
 //                    }
 //            );
 
-            return (Optional<RestResponse<Void>>) uc.verifyJwtToken(tokenStr).onItem().transform(
+            return uc.verifyJwtToken(tokenStr).onItem().transform(
                     resp -> {
                         var respStr = resp.readEntity(String.class);
                         logger.debug("CHECK_AUTH_FILTER -> uc.verifyJwtToken RESPONSE / " + respStr);
@@ -80,19 +81,19 @@ public class CheckAuthFilter {
                             logger.debug("CHECK_AUTH_FILTER CHECK PASSED -> GOOD JWT TOKEN: " + token);
                             verifyResult.setData(Boolean.TRUE);
                             verifyOk.setData(true);
-                            return Optional.empty();
+                            return null;
                         } else {
                             logger.info("CHECK_AUTH_FILTER CHECK FAILED -> " + token + " RESP / " + respStr);
-                            return Optional.of(RestResponse.status(Response.Status.FORBIDDEN));
+                            return RestResponse.status(Response.Status.FORBIDDEN);
                         }
                     }
             ).onFailure().recoverWithItem(err -> {
                 logger.debug("CHECK_AUTH_FILTER CHECK FAILED -> ERR: " + err);
-                return Optional.of(RestResponse.status(Response.Status.FORBIDDEN));
-            }).await().atMost(c.TIMEOUT);
+                return RestResponse.status(Response.Status.FORBIDDEN);
+            });
         }
 
         logger.debug("CHECK_AUTH_FILTER -> TOKEN INVAILD / " + tokenStr);
-        return Optional.of(RestResponse.status(Response.Status.FORBIDDEN));
+        return Uni.createFrom().item(RestResponse.status(Response.Status.FORBIDDEN));
     }
 }
\ No newline at end of file
diff --git a/src/main/java/cn/alchemystudio/taskserver/service/TaskServer.java b/src/main/java/cn/alchemystudio/taskserver/service/TaskServer.java
index 1fbbd50..7838707 100644
--- a/src/main/java/cn/alchemystudio/taskserver/service/TaskServer.java
+++ b/src/main/java/cn/alchemystudio/taskserver/service/TaskServer.java
@@ -121,7 +121,7 @@ public class TaskServer {
 
     @GET
     @Path("/task_status/{task_id}")
-    @Blocking
+//    @Blocking
     public Uni<Response> task_status(@RestPath String task_id) {
         logger.debug("[QUERY_TASK_STATUS] -> " + task_id);
         return DbTask.findById(task_id).onItem().transform(task -> {

In conclusion, if I use resteasy-reactive and hibernate-panache-reactive together, and in addition I’m using ServerRequestFilter, then I have to mark both my filter and my web service as @Blocking, or I will get the reactive Session error thrown by hibernate-panache-reactive.

I’m not sure whether it is a bug or it is by design, or maybe it’s my usage problem. so I report it here asking for help/suggestions.

Here is the Quarkus version I’m using:

<quarkus.platform.artifact-id>quarkus-universe-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
<quarkus.platform.version>2.2.3.Final</quarkus.platform.version>

Here are the reactive components I’m using:

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-resteasy-reactive</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-reactive-pg-client</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-hibernate-reactive-panache</artifactId>
</dependency>

Thanks for reading 😄

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
stuartwdouglascommented, Nov 5, 2021

I think your issue is calling await, I think you need to just return a Uni<Response> to keep everything non blocking.

0reactions
Moicencommented, Nov 7, 2021

I switch from hibernate-panache-reactive to hibernate-panache, and all the transaction related problems are gone. I guess this is the correct way for my usage scenario. Thanks for help!

Read more comments on GitHub >

github_iconTop Results From Across the Web

RESTEasy Reactive - To block or not to block - Quarkus
The central question is about the usage of Hibernate ORM. As Hibernate ORM classic (we also have Hibernate reactive) is blocking, you can't...
Read more >
Simplified Hibernate Reactive with Panache - Quarkus
Solution 1: using the active record pattern. Defining your entity. To define a Panache entity, simply extend PanacheEntity , annotate it with @Entity...
Read more >
Announcing RESTEasy Reactive - Quarkus
It gives the Quarkus and RESTEasy teams great pleasure to announce that RESTEasy Reactive integration in Quarkus has landed in the main ...
Read more >
Writing REST Services with RESTEasy Reactive - Quarkus
This guide explains how to write REST Services with RESTEasy Reactive in Quarkus. This is the reference guide for RESTEasy Reactive. For a...
Read more >
Getting Started With Reactive - Quarkus
This guide will help you with: Bootstrapping a reactive CRUD application with Quarkus. Using Hibernate Reactive with Panache to interact with a database...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found