0

I have a reactive quarkus app with hibernate-panache-reactive. The problem is it behaves differently when I run it as a Java app or a native app.

The app

  1. loads a lot of data from a MySQL DB via hibernate-panache-reactive
  2. builds a graph based on the data loaded
  3. runs some time consuming algorithm on the graph
  4. loads some more data from the DB based on the results returned from 3)

So initially the code looked something like this:

GraphProcessor graphProcessor = createInitialProcessor();
        return Uni.createFrom().item(graphProcessor)
// 1) loading of initial data
                .onItem().transformToUni(this::loadDataViaPanaceReactive1)
                .onItem().transformToUni(this::loadDataViaPanaceReactive2)
                .onItem().transformToUni(this::loadDataViaPanaceReactive3)
// 2) building of graph               
                .onItem().transform(graphProcessor::processLoadedData)
                .onItem().invoke(graphProcessor::loadingComplete) //sync
// 3) running time consuming algorithm on graph
                .onItem().transformToMulti(this::runTimeConsumingTask)
                .onItem().invoke(this::prepareDBQueries)
// 4) load more data from DB
                .onItem().transformToUniAndConcatenate(this::loadMoreData1)
                .onItem().transformToUniAndConcatenate(this::loadMoreData2)
                .onItem().transformToUniAndConcatenate(this::transformToPublicForm)
                .onFailure().invoke(log::error);

That worked fine when run as a Java app but when I tried to run it as a native app it first complained that the computation in 2 and 3 were taking too long and this was blocking the calling thread.

I fixed that by using

.emitOn(Infrastructure.getDefaultWorkerPool())

Between 1 and 2

This time I got another error

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-0' current Thread: 'vert.x-eventloop-thread-1'

I've fixed that by inserting

.emitOn(Infrastructure.getDefaultExecutor())

between 3 and 4.

GraphProcessor graphProcessor = createInitialProcessor();
        return Uni.createFrom().item(graphProcessor)
// 1) loading of initial data
                .onItem().transformToUni(this::loadDataViaPanaceReactive1)
                .onItem().transformToUni(this::loadDataViaPanaceReactive2)
                .onItem().transformToUni(this::loadDataViaPanaceReactive3)
// 2) building of graph               
                .emitOn(Infrastructure.getDefaultWorkerPool()) // Required for native mode
                .onItem().transform(graphProcessor::processLoadedData)
                .onItem().invoke(graphProcessor::loadingComplete)
// 3) running time consuming algorithm on graph
                .onItem().transformToMulti(this::runTimeConsumingTask)
                .onItem().invoke(this::prepareDBQueries)
                .emitOn(Infrastructure.getDefaultExecutor()) // Required for native mode
// 4) load more data from DB
                .onItem().transformToUniAndConcatenate(this::loadMoreData1)
                .onItem().transformToUniAndConcatenate(this::loadMoreData2)
                .onItem().transformToUniAndConcatenate(this::transformToPublicForm)
                .onFailure().invoke(log::error);

That worked when run in native mode but now when I run it in Java I get the same exception (Detected use of the reactive Session from a different Thread than the one which was used to open the reactive Session)

The emitOn(Infrastructure.getDefaultExcecutor()) should have switched back to the original thread.

The odd thing is also that this exception is not thrown every time I hit the app. So what am I doing wrong here? What is the best way to handle time consuming tasks and then having to do some more DB queries after?

You could use .runSubscriptionOn(Executor) but I would need to switch back to the original thread for part 4 again.

Thanks for you help.

Ben
  • 1,922
  • 3
  • 23
  • 37
  • First, it's not different behavior. It's expected, native being slower (no JIT). Hibernate Reactive requires all operations to be called on the event loop. So, you cannot offload the computation to another thread. I would recommend in your case to extend the vertex thread checker timeout using `quarkus.vertx.max-event-loop-execute-time=5S`. – Clement Jul 06 '22 at 06:02
  • Hi Clement, thanks for your reply but I am a bit confused. Compiled to native should be faster I thought. What would be the point of it if it is executing slower and from what I have seen it does run faster. 2nd, I am trying to run the Hibernate calls on the event loop by switching back to the default executor or are they not the same? Ideally I would like my computations in 2 and 3 to behave like any other reactive call in that it runs in its own thread and the calling thread gets and event when finished. – Ben Jul 06 '22 at 07:31
  • Faster is a relative term. What Clement says is that when the JVM JIT warms up, each request will be served in less time than what is achievable with the native binary – geoand Jul 06 '22 at 09:15
  • I've just read https://quarkus.io/guides/native-reference#why-is-runtime-performance-of-a-native-executable-inferior-compared-to-jvm-mode. I've been doing Java since 1996 and everyone was always complaining about the speed of Java. Then came the first JIT compilers and the gap got closer. I didn't realise that the scale has now tipped in Java's (JITs) favour. – Ben Jul 06 '22 at 15:00
  • 2
    That's right, JIT can take better advantage of detailed metrics which aren't available to any ahead of time compiler (AOT), resulting (typically) in better peak performance. The advantage of native (AOT compiled) is that it can aggressively prune dead code, boot faster and consume less memory as the JIT infrastructure (and metrics collection) isn't included. The advantage of GraalVM over say C is that your same Java code and all libraries can be "deployed" in either mode flexibly - without needing a substantial rewrite - but each of these have their own pros and cons. – Sanne Jul 06 '22 at 21:47

0 Answers0