From 518916553efa66d4e1d70560e65c305bcbf6d06d Mon Sep 17 00:00:00 2001 From: Thomas Segismont Date: Wed, 28 Jan 2026 17:02:33 +0100 Subject: [PATCH 1/2] Upgrade to Quarkus 3.32.2 It was required to add the os-maven-plugin. Otherwise, the container image build fails with: Could not find artifact io.netty:netty-transport-native-unix-common:jar:${os.detected.name}-${os.detected.arch}:4.1.130.Final in central Also, Hibernate no longer has the update method on the regular session. And, for convenience, added quarkus.hibernate-orm.log.sql in application.properties --- frameworks/Java/quarkus/README.md | 4 ++++ frameworks/Java/quarkus/pom.xml | 22 +++++++++++++------ .../quarkus-hibernate-reactive.dockerfile | 4 ++-- ...uarkus-reactive-routes-pgclient.dockerfile | 4 ++-- .../Java/quarkus/quarkus-vertx.dockerfile | 4 ++-- frameworks/Java/quarkus/quarkus.dockerfile | 4 ++-- .../quarkus/reactive-routes-pgclient/pom.xml | 4 ++-- .../pom.xml | 2 +- .../src/main/resources/application.properties | 2 ++ .../resteasy-reactive-hibernate/pom.xml | 4 ++-- .../benchmark/repository/WorldRepository.java | 14 ++++-------- frameworks/Java/quarkus/vertx/pom.xml | 6 ++--- 12 files changed, 41 insertions(+), 33 deletions(-) diff --git a/frameworks/Java/quarkus/README.md b/frameworks/Java/quarkus/README.md index 0bced001077..2925bb9e35c 100644 --- a/frameworks/Java/quarkus/README.md +++ b/frameworks/Java/quarkus/README.md @@ -13,6 +13,10 @@ There are currently 2 implementations: ./tfb --mode verify --test quarkus quarkus-hibernate-reactive +After the database container image is created, it can be started independently. + + docker run -d --name tfb-database -e POSTGRES_DB=hello_world -e POSTGRES_PASSWORD=benchmarkdbpass -e POSTGRES_USER=benchmarkdbuser -p 5432:5432 techempower/postgres:latest + ## Versions * [Java OpenJDK 17](http://openjdk.java.net/) diff --git a/frameworks/Java/quarkus/pom.xml b/frameworks/Java/quarkus/pom.xml index 26dcb870c26..9e2820c65dd 100644 --- a/frameworks/Java/quarkus/pom.xml +++ b/frameworks/Java/quarkus/pom.xml @@ -8,15 +8,15 @@ pom - 3.14.0 - 21 + 3.15.0 + 25 UTF-8 UTF-8 quarkus-bom io.quarkus - 3.21.2 + 3.32.2 true - 3.5.2 + 3.5.4 0.0.26.Final 1.3.0 @@ -61,6 +61,16 @@ + + + + + + kr.motd.maven + os-maven-plugin + 1.7.1 + + ${quarkus.platform.group-id} @@ -81,9 +91,7 @@ maven-compiler-plugin ${compiler-plugin.version} - - -parameters - + true diff --git a/frameworks/Java/quarkus/quarkus-hibernate-reactive.dockerfile b/frameworks/Java/quarkus/quarkus-hibernate-reactive.dockerfile index 861dd3b042a..6fad2093a60 100644 --- a/frameworks/Java/quarkus/quarkus-hibernate-reactive.dockerfile +++ b/frameworks/Java/quarkus/quarkus-hibernate-reactive.dockerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi9/openjdk-21:1.22 as maven +FROM registry.access.redhat.com/ubi9/openjdk-25:1.24 as maven ENV LANGUAGE='en_US:en' WORKDIR /quarkus @@ -29,7 +29,7 @@ WORKDIR /quarkus/$MODULE RUN mvn package -B -q WORKDIR /quarkus -FROM registry.access.redhat.com/ubi9/openjdk-21-runtime:1.22 +FROM registry.access.redhat.com/ubi9/openjdk-25-runtime:1.24 ENV LANGUAGE='en_US:en' WORKDIR /quarkus ENV MODULE=resteasy-reactive-hibernate-reactive diff --git a/frameworks/Java/quarkus/quarkus-reactive-routes-pgclient.dockerfile b/frameworks/Java/quarkus/quarkus-reactive-routes-pgclient.dockerfile index 21e918be45c..fc9d914e7d0 100644 --- a/frameworks/Java/quarkus/quarkus-reactive-routes-pgclient.dockerfile +++ b/frameworks/Java/quarkus/quarkus-reactive-routes-pgclient.dockerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi9/openjdk-21:1.22 as maven +FROM registry.access.redhat.com/ubi9/openjdk-25:1.24 as maven ENV LANGUAGE='en_US:en' WORKDIR /quarkus @@ -29,7 +29,7 @@ WORKDIR /quarkus/$MODULE RUN mvn package -B -q WORKDIR /quarkus -FROM registry.access.redhat.com/ubi9/openjdk-21-runtime:1.22 +FROM registry.access.redhat.com/ubi9/openjdk-25-runtime:1.24 ENV LANGUAGE='en_US:en' WORKDIR /quarkus ENV MODULE=reactive-routes-pgclient diff --git a/frameworks/Java/quarkus/quarkus-vertx.dockerfile b/frameworks/Java/quarkus/quarkus-vertx.dockerfile index 6969bc6d7ba..09865d3d086 100644 --- a/frameworks/Java/quarkus/quarkus-vertx.dockerfile +++ b/frameworks/Java/quarkus/quarkus-vertx.dockerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi9/openjdk-21:1.22 as maven +FROM registry.access.redhat.com/ubi9/openjdk-25:1.24 as maven ENV LANGUAGE='en_US:en' WORKDIR /quarkus @@ -29,7 +29,7 @@ WORKDIR /quarkus/$MODULE RUN mvn package -B -q WORKDIR /quarkus -FROM registry.access.redhat.com/ubi9/openjdk-21-runtime:1.22 +FROM registry.access.redhat.com/ubi9/openjdk-25-runtime:1.24 ENV LANGUAGE='en_US:en' WORKDIR /quarkus ENV MODULE=vertx diff --git a/frameworks/Java/quarkus/quarkus.dockerfile b/frameworks/Java/quarkus/quarkus.dockerfile index 40ec7a4ded7..b3eb400251e 100644 --- a/frameworks/Java/quarkus/quarkus.dockerfile +++ b/frameworks/Java/quarkus/quarkus.dockerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi9/openjdk-21:1.22 as maven +FROM registry.access.redhat.com/ubi9/openjdk-25:1.24 as maven ENV LANGUAGE='en_US:en' WORKDIR /quarkus @@ -29,7 +29,7 @@ WORKDIR /quarkus/$MODULE RUN mvn package -B -q WORKDIR /quarkus -FROM registry.access.redhat.com/ubi9/openjdk-21-runtime:1.22 +FROM registry.access.redhat.com/ubi9/openjdk-25-runtime:1.24 ENV LANGUAGE='en_US:en' WORKDIR /quarkus ENV MODULE=resteasy-reactive-hibernate diff --git a/frameworks/Java/quarkus/reactive-routes-pgclient/pom.xml b/frameworks/Java/quarkus/reactive-routes-pgclient/pom.xml index 599183b4656..a976941d583 100644 --- a/frameworks/Java/quarkus/reactive-routes-pgclient/pom.xml +++ b/frameworks/Java/quarkus/reactive-routes-pgclient/pom.xml @@ -40,7 +40,7 @@ com.google.guava guava - 32.0.0-jre + 32.0.1-jre io.netty @@ -75,7 +75,7 @@ com.google.guava guava - 32.0.0-jre + 32.0.1-jre diff --git a/frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/pom.xml b/frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/pom.xml index 8948c5ffb06..165e4e59d36 100644 --- a/frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/pom.xml +++ b/frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/pom.xml @@ -44,7 +44,7 @@ com.google.guava guava - 32.0.0-jre + 32.0.1-jre io.netty diff --git a/frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/resources/application.properties b/frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/resources/application.properties index df4d6512d35..29980229505 100644 --- a/frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/resources/application.properties +++ b/frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/resources/application.properties @@ -17,6 +17,8 @@ quarkus.hibernate-orm.second-level-caching-enabled=false #quarkus.vertx.storage=false +quarkus.hibernate-orm.log.sql=false + quarkus.log.console.enable=true quarkus.log.console.level=INFO quarkus.log.file.enable=false diff --git a/frameworks/Java/quarkus/resteasy-reactive-hibernate/pom.xml b/frameworks/Java/quarkus/resteasy-reactive-hibernate/pom.xml index 5cd634dbea5..ea98cd58163 100644 --- a/frameworks/Java/quarkus/resteasy-reactive-hibernate/pom.xml +++ b/frameworks/Java/quarkus/resteasy-reactive-hibernate/pom.xml @@ -44,7 +44,7 @@ com.google.guava guava - 32.0.0-jre + 32.0.1-jre io.netty @@ -79,7 +79,7 @@ com.google.guava guava - 32.0.0-jre + 32.0.1-jre diff --git a/frameworks/Java/quarkus/resteasy-reactive-hibernate/src/main/java/io/quarkus/benchmark/repository/WorldRepository.java b/frameworks/Java/quarkus/resteasy-reactive-hibernate/src/main/java/io/quarkus/benchmark/repository/WorldRepository.java index a9c82fb1eec..1194967a308 100644 --- a/frameworks/Java/quarkus/resteasy-reactive-hibernate/src/main/java/io/quarkus/benchmark/repository/WorldRepository.java +++ b/frameworks/Java/quarkus/resteasy-reactive-hibernate/src/main/java/io/quarkus/benchmark/repository/WorldRepository.java @@ -1,18 +1,14 @@ package io.quarkus.benchmark.repository; +import io.quarkus.benchmark.model.World; +import io.quarkus.benchmark.utils.LocalRandom; +import io.quarkus.benchmark.utils.Randomizer; import jakarta.inject.Inject; import jakarta.inject.Singleton; import jakarta.transaction.Transactional; - -import org.hibernate.FlushMode; -import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.StatelessSession; -import io.quarkus.benchmark.model.World; -import io.quarkus.benchmark.utils.LocalRandom; -import io.quarkus.benchmark.utils.Randomizer; - @Singleton public class WorldRepository { @@ -59,9 +55,8 @@ public World[] updateNWorlds(final int count) { //We're again forced to use the "individual load" pattern by the rules: final World[] list = loadNWorlds(count); final LocalRandom random = Randomizer.current(); - try (Session s = sf.openSession()) { + try (StatelessSession s = sf.openStatelessSession()) { s.setJdbcBatchSize(count); - s.setHibernateFlushMode(FlushMode.MANUAL); for (World w : list) { //Read the one field, as required by the following rule: // # vi. At least the randomNumber field must be read from the database result set. @@ -71,7 +66,6 @@ public World[] updateNWorlds(final int count) { w.setRandomNumber(random.getNextRandomExcluding(previousRead)); s.update(w); } - s.flush(); } return list; } diff --git a/frameworks/Java/quarkus/vertx/pom.xml b/frameworks/Java/quarkus/vertx/pom.xml index a5c6cdbcdde..07418ebf8a9 100644 --- a/frameworks/Java/quarkus/vertx/pom.xml +++ b/frameworks/Java/quarkus/vertx/pom.xml @@ -26,7 +26,7 @@ com.ongres.scram - client + scram-client io.quarkus @@ -40,7 +40,7 @@ com.google.guava guava - 32.0.0-jre + 32.0.1-jre io.netty @@ -75,7 +75,7 @@ com.google.guava guava - 32.0.0-jre + 32.0.1-jre From 40c9ee277d008b89ceca6b8b4d4d6373dd90b3d3 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 17 Mar 2026 09:27:03 +0100 Subject: [PATCH 2/2] Enable batching with StatelessSession (#4) * Stateless session should use batching In Hibernate ORM and Reactive, the stateless session ignore the batch size property. The reason is that the stateless session execute queries immediately and does not store the entities to execute a batch update later. To enable batching, we need to use the `updateMultiple` method instead. It will use the size of the list as batch value. * Use loop for creating data with Hibernate Reactive Currently, is not safe to use the Hibernate Reactive session with `Uni.combine().all()`. Even if it still work for a simple scenario like the one in the benchmark. This commit change it to for loop so that the uni are executed sequentially. Using `.usingConcurrencyOf(1)` would have also worked. * Small clean up * Remove unused findManaged --- .../benchmark/repository/WorldRepository.java | 35 +++++-------------- .../benchmark/resource/DbResource.java | 10 ++---- .../benchmark/repository/WorldRepository.java | 14 +++++--- 3 files changed, 20 insertions(+), 39 deletions(-) diff --git a/frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/java/io/quarkus/benchmark/repository/WorldRepository.java b/frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/java/io/quarkus/benchmark/repository/WorldRepository.java index 90130a91022..468c35593ea 100644 --- a/frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/java/io/quarkus/benchmark/repository/WorldRepository.java +++ b/frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/java/io/quarkus/benchmark/repository/WorldRepository.java @@ -24,31 +24,26 @@ public Uni createData() { return inSession(s -> { final LocalRandom random = Randomizer.current(); int MAX = 10000; - Uni[] unis = new Uni[MAX]; + Uni loop = Uni.createFrom().voidItem(); for (int i = 0; i < MAX; i++) { final World world = new World(); world.setId(i + 1); world.setRandomNumber(random.getNextRandom()); - unis[i] = s.persist(world).map(v -> null); + loop = loop.call(() -> s.persist(world)); } - return Uni.combine().all().unis(unis).with(l -> null) - .flatMap(v -> s.flush()) - .map(v -> null); + return loop.call(s::flush); }); } - public Uni> update(Mutiny.Session session, List worlds) { - return session - .setBatchSize(worlds.size()) - .flush() - .map(v -> worlds); + public Uni> update(Mutiny.StatelessSession session, List worlds) { + return session.updateMultiple(worlds).replaceWith(worlds); } public Uni> findStateless(int count) { return inStatelessSession(session -> findStateless(session, count)); } - private Uni> findStateless(Mutiny.StatelessSession s, int count) { + public Uni> findStateless(Mutiny.StatelessSession s, int count) { //The rules require individual load: we can't use the Hibernate feature which allows load by multiple IDs // as one single operation as Hibernate is too smart and will switch to use batched loads automatically. // Hence, use this awkward alternative: @@ -56,23 +51,9 @@ private Uni> findStateless(Mutiny.StatelessSession s, int count) { final List worlds = new ArrayList<>(count); Uni loopRoot = Uni.createFrom().voidItem(); for (int i = 0; i < count; i++) { - loopRoot = loopRoot.chain(() -> s.get(World.class, localRandom.getNextRandom()).invoke(worlds::add).replaceWithVoid()); + loopRoot = loopRoot.call(() -> s.get(World.class, localRandom.getNextRandom()).invoke(worlds::add)); } - return loopRoot.map(v -> worlds); - } - - public Uni> findManaged(Mutiny.Session s, int count) { - final List worlds = new ArrayList<>(count); - //The rules require individual load: we can't use the Hibernate feature which allows load by multiple IDs - // as one single operation as Hibernate is too smart and will switch to use batched loads. - // But also, we can't use "Uni#join" as we did in the above method as managed entities shouldn't use pipelining - - // so we also have to avoid Mutiny optimising things by establishing an explicit chain: - final LocalRandom localRandom = Randomizer.current(); - Uni loopRoot = Uni.createFrom().voidItem(); - for (int i = 0; i < count; i++) { - loopRoot = loopRoot.chain(() -> s.find(World.class, localRandom.getNextRandom()).invoke(worlds::add).replaceWithVoid()); - } - return loopRoot.map(v -> worlds); + return loopRoot.replaceWith(worlds); } public Uni findStateless() { diff --git a/frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/java/io/quarkus/benchmark/resource/DbResource.java b/frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/java/io/quarkus/benchmark/resource/DbResource.java index c536e7033f2..c440f4c0114 100644 --- a/frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/java/io/quarkus/benchmark/resource/DbResource.java +++ b/frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/java/io/quarkus/benchmark/resource/DbResource.java @@ -44,17 +44,14 @@ public Uni createData() { return worldRepository.createData(); } - private Uni> randomWorldsForWrite(Mutiny.Session session, int count) { - return worldRepository.findManaged(session, count); + private Uni> randomWorldsForWrite(Mutiny.StatelessSession session, int count) { + return worldRepository.findStateless(session, count); } @GET @Path("updates") public Uni> updates(@QueryParam("queries") String queries) { - return worldRepository.inSession(session -> { - - session.setFlushMode(FlushMode.MANUAL); - + return worldRepository.inStatelessSession(session -> { Uni> worlds = randomWorldsForWrite(session, parseQueryCount(queries)); return worlds.flatMap(worldsCollection -> { final LocalRandom localRandom = Randomizer.current(); @@ -66,7 +63,6 @@ public Uni> updates(@QueryParam("queries") String queries) { //the verification: w.setRandomNumber(localRandom.getNextRandomExcluding(previousRead)); } ); - return worldRepository.update(session, worldsCollection); }); }); diff --git a/frameworks/Java/quarkus/resteasy-reactive-hibernate/src/main/java/io/quarkus/benchmark/repository/WorldRepository.java b/frameworks/Java/quarkus/resteasy-reactive-hibernate/src/main/java/io/quarkus/benchmark/repository/WorldRepository.java index 1194967a308..a1e93d5ba58 100644 --- a/frameworks/Java/quarkus/resteasy-reactive-hibernate/src/main/java/io/quarkus/benchmark/repository/WorldRepository.java +++ b/frameworks/Java/quarkus/resteasy-reactive-hibernate/src/main/java/io/quarkus/benchmark/repository/WorldRepository.java @@ -1,5 +1,8 @@ package io.quarkus.benchmark.repository; +import java.util.ArrayList; +import java.util.List; + import io.quarkus.benchmark.model.World; import io.quarkus.benchmark.utils.LocalRandom; import io.quarkus.benchmark.utils.Randomizer; @@ -53,21 +56,22 @@ public World[] loadNWorlds(final int count) { public World[] updateNWorlds(final int count) { //We're again forced to use the "individual load" pattern by the rules: - final World[] list = loadNWorlds(count); + final World[] worlds = loadNWorlds(count); + final List worldList = new ArrayList<>(worlds.length); final LocalRandom random = Randomizer.current(); try (StatelessSession s = sf.openStatelessSession()) { - s.setJdbcBatchSize(count); - for (World w : list) { + for (World w : worlds) { //Read the one field, as required by the following rule: // # vi. At least the randomNumber field must be read from the database result set. final int previousRead = w.getRandomNumber(); //Update it, but make sure to exclude the current number as Hibernate optimisations would otherwise // skip the write operation: w.setRandomNumber(random.getNextRandomExcluding(previousRead)); - s.update(w); + worldList.add(w); } + s.updateMultiple(worldList); } - return list; + return worlds; } }