Browse Source

Improved the Quarkus benchmark and added a pgclient variant (#5728)

Co-authored-by: Sanne Grinovero <[email protected]>
Stéphane Épardaud 5 years ago
parent
commit
ef0905588a
37 changed files with 644 additions and 406 deletions
  1. 15 43
      frameworks/Java/quarkus/README.md
  2. 7 0
      frameworks/Java/quarkus/base/pom.xml
  3. 0 45
      frameworks/Java/quarkus/base/src/main/java/io/quarkus/benchmark/filter/ServerHeaderFilter.java
  4. 14 28
      frameworks/Java/quarkus/benchmark_config.json
  5. 69 0
      frameworks/Java/quarkus/hibernate/README.md
  6. 21 0
      frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/cdi/HibernateOrmNativeComponents.java
  7. 32 0
      frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/filter/ServerHeaderFilter.java
  8. 3 0
      frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/model/hibernate/Fortune.java
  9. 0 24
      frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/model/hibernate/World.java
  10. 16 11
      frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/repository/hibernate/FortuneRepository.java
  11. 56 13
      frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/repository/hibernate/WorldRepository.java
  12. 5 6
      frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/resource/JsonResource.java
  13. 2 5
      frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/resource/PlaintextResource.java
  14. 74 28
      frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/resource/hibernate/DbResource.java
  15. 10 13
      frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/resource/hibernate/FortuneResource.java
  16. 17 4
      frameworks/Java/quarkus/hibernate/src/main/resources/application.properties
  17. 1 0
      frameworks/Java/quarkus/hibernate/src/main/resources/import.sql
  18. 1 0
      frameworks/Java/quarkus/hibernate/start-app.sh
  19. 4 0
      frameworks/Java/quarkus/pgclient/pom.xml
  20. 0 0
      frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/model/Fortune.java
  21. 3 16
      frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/model/World.java
  22. 11 22
      frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/repository/pgclient/FortuneRepository.java
  23. 56 0
      frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/repository/pgclient/PgClientFactory.java
  24. 38 0
      frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/repository/pgclient/PgClients.java
  25. 22 30
      frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/repository/pgclient/WorldRepository.java
  26. 29 0
      frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/BaseResource.java
  27. 42 58
      frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/DbResource.java
  28. 15 28
      frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/FortuneResource.java
  29. 21 0
      frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/JsonResource.java
  30. 17 0
      frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/PlaintextResource.java
  31. 28 0
      frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/ServerHeaderFilter.java
  32. 1 2
      frameworks/Java/quarkus/pgclient/src/main/resources/application.properties
  33. 2 0
      frameworks/Java/quarkus/pgclient/start-app.sh
  34. 2 2
      frameworks/Java/quarkus/pom.xml
  35. 0 18
      frameworks/Java/quarkus/quarkus-hibernate.dockerfile
  36. 4 4
      frameworks/Java/quarkus/quarkus-pgclient.dockerfile
  37. 6 6
      frameworks/Java/quarkus/quarkus.dockerfile

+ 15 - 43
frameworks/Java/quarkus/README.md

@@ -3,44 +3,20 @@
 This is the Quarkus portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
 This is the Quarkus portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
 
 
 ## Implementations
 ## Implementations
-There is currently two repository implementations.
 
 
-### Hibernate via JPA
-* [WorldRepository](hibernate/src/main/java/io/quarkus/benchmark/repository/hibernate/WorldRepository.java)
-* [FortuneRepository](hibernate/src/main/java/io/quarkus/benchmark/repository/hibernate/FortuneRepository.java)
+There are currently two repository implementations.
 
 
-### Asynchronous DB accesses via pgclient
-* [WorldRepository](pgclient/src/main/java/io/quarkus/benchmark/repository/pgclient/WorldRepository.java)
-* [FortuneRepository](pgclient/src/main/java/io/quarkus/benchmark/repository/pgclient/FortuneRepository.java)
+### JAX-RS and Hibernate via JPA
+* [Plaintext test source](hibernate/src/main/java/io/quarkus/benchmark/resource/PlaintextResource.java)
+* [JSON test source](hibernate/src/main/java/io/quarkus/benchmark/resource/JsonResource.java)
+* [Query, Queries, Update test source](hibernate/src/main/java/io/quarkus/benchmark/repository/hibernate/WorldRepository.java)
+* [Fortunes test source](hibernate/src/main/java/io/quarkus/benchmark/repository/hibernate/FortuneRepository.java)
 
 
-
-### Plaintext Test
-
-* [Plaintext test source](base/src/main/java/io/quarkus/benchmark/resource/PlaintextResource.java)
-
-### JSON Serialization Test
-
-* [JSON test source](base/src/main/java/io/quarkus/benchmark/resource/JsonResource.java)
-
-### Database Query Test
-
-* [Hibernate Database Query test source](hibernate/src/main/java/io/quarkus/benchmark/resource/hibernate/DbResource.java)
-* [PGClient Database Query test source](pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/DbResource.java)
-
-### Database Queries Test
-
-* [Hibernate Database Query test source](hibernate/src/main/java/io/quarkus/benchmark/resource/hibernate/DbResource.java)
-* [PGClient Database Query test source](pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/DbResource.java)
-
-### Database Update Test
-
-* [Hibernate Database Query test source](hibernate/src/main/java/io/quarkus/benchmark/resource/hibernate/DbResource.java)
-* [PGClient Database Query test source](pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/DbResource.java)
-
-### Template rendering Test
-
-* [Hibernate Template rendering test source](hibernate/src/main/java/io/quarkus/benchmark/resource/hibernate/FortuneResource.java)
-* [PGClient Template rendering test source](pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/FortuneResource.java)
+### Reactive Routes and Asynchronous DB accesses via pgclient
+* [Plaintext test source](pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/PlaintextResource.java)
+* [JSON test source](pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/JsonResource.java)
+* [Query, Queries, Update test source](pgclient/src/main/java/io/quarkus/benchmark/repository/pgclient/WorldRepository.java)
+* [Fortunes test source](pgclient/src/main/java/io/quarkus/benchmark/repository/pgclient/FortuneRepository.java)
 
 
 ## Versions
 ## Versions
 
 
@@ -59,20 +35,16 @@ There is currently two repository implementations.
 
 
 ### Database Query Test
 ### Database Query Test
 
 
-    http://localhost:8080/hibernate/db
-    http://localhost:8080/pgclient/db
+    http://localhost:8080/db
 
 
 ### Database Queries Test
 ### Database Queries Test
 
 
-    http://localhost:8080/hibernate/queries?queries=5
-    http://localhost:8080/pgclient/queries?queries=5
+    http://localhost:8080/queries?queries=5
 
 
 ### Database Update Test
 ### Database Update Test
 
 
-    http://localhost:8080/hibernate/updates?queries=5
-    http://localhost:8080/pgclient/updates?queries=5
+    http://localhost:8080/updates?queries=5
 
 
 ### Template rendering Test
 ### Template rendering Test
 
 
-    http://localhost:8080/hibernate/fortunes
-    http://localhost:8080/pgclient/fortunes
+    http://localhost:8080/fortunes

+ 7 - 0
frameworks/Java/quarkus/base/pom.xml

@@ -12,6 +12,13 @@
     <groupId>io.quarkus.benchmark</groupId>
     <groupId>io.quarkus.benchmark</groupId>
     <artifactId>base</artifactId>
     <artifactId>base</artifactId>
 
 
+    <dependencies>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-scheduler</artifactId>
+        </dependency>
+    </dependencies>
+
     <build>
     <build>
         <plugins>
         <plugins>
             <plugin>
             <plugin>

+ 0 - 45
frameworks/Java/quarkus/base/src/main/java/io/quarkus/benchmark/filter/ServerHeaderFilter.java

@@ -1,45 +0,0 @@
-package io.quarkus.benchmark.filter;
-
-import java.util.Timer;
-import java.util.TimerTask;
-
-import javax.ws.rs.container.ContainerRequestContext;
-import javax.ws.rs.container.ContainerResponseContext;
-import javax.ws.rs.container.ContainerResponseFilter;
-import javax.ws.rs.ext.Provider;
-
-import org.jboss.resteasy.util.HttpHeaderNames;
-
-@Provider
-public class ServerHeaderFilter implements ContainerResponseFilter {
-
-    private final DateHelper dateHelper = new DateHelper();
-
-    @Override
-    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
-        responseContext.getHeaders().add("Server", "Quarkus");
-        responseContext.getHeaders().add(HttpHeaderNames.DATE, dateHelper.getDate());
-    }
-
-    static class DateHelper extends TimerTask {
-        private Timer timer = new Timer();
-        private String date = generateDate();
-
-        public DateHelper() {
-            timer.schedule(this, 0, 1000);
-        }
-
-        @Override
-        public void run() {
-            date = generateDate();
-        }
-
-        private String generateDate() {
-            return java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME.format(java.time.ZonedDateTime.now());
-        }
-
-        public String getDate() {
-            return date;
-        }
-    }
-}

+ 14 - 28
frameworks/Java/quarkus/benchmark_config.json

@@ -4,6 +4,10 @@
     {
     {
       "default": {
       "default": {
         "json_url": "/json",
         "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "update_url": "/updates?queries=",
+        "fortune_url": "/fortunes",
         "plaintext_url": "/plaintext",
         "plaintext_url": "/plaintext",
         "port": 8080,
         "port": 8080,
         "approach": "Realistic",
         "approach": "Realistic",
@@ -12,40 +16,22 @@
         "framework": "Quarkus",
         "framework": "Quarkus",
         "language": "Java",
         "language": "Java",
         "flavor": "None",
         "flavor": "None",
-        "orm": "full",
+        "orm": "Full",
         "platform": "JAX-RS",
         "platform": "JAX-RS",
-        "webserver": "None",
+        "webserver": "Undertow",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
-        "display_name": "quarkus",
-        "notes": "",
-        "versus": "Netty"
-      },
-      "hibernate": {
-        "db_url": "/hibernate/db",
-        "query_url": "/hibernate/queries?queries=",
-        "fortune_url": "/hibernate/fortunes",
-        "port": 8080,
-        "approach": "Realistic",
-        "classification": "fullstack",
-        "database": "Postgres",
-        "framework": "Quarkus",
-        "language": "Java",
-        "flavor": "None",
-        "orm": "full",
-        "platform": "JAX-RS",
-        "webserver": "None",
-        "os": "Linux",
-        "database_os": "Linux",
-        "display_name": "quarkus-hibernate",
+        "display_name": "Quarkus + Hibernate ORM",
         "notes": "",
         "notes": "",
         "versus": "Netty"
         "versus": "Netty"
       },
       },
       "pgclient": {
       "pgclient": {
-        "db_url": "/pgclient/db",
-        "query_url": "/pgclient/queries?queries=",
-        "update_url": "/pgclient/updates?queries=",
-        "fortune_url": "/pgclient/fortunes",
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "update_url": "/updates?queries=",
+        "fortune_url": "/fortunes",
+        "plaintext_url": "/plaintext",
         "port": 8080,
         "port": 8080,
         "approach": "Realistic",
         "approach": "Realistic",
         "classification": "fullstack",
         "classification": "fullstack",
@@ -55,7 +41,7 @@
         "flavor": "None",
         "flavor": "None",
         "orm": "Micro",
         "orm": "Micro",
         "platform": "JAX-RS",
         "platform": "JAX-RS",
-        "webserver": "None",
+        "webserver": "Undertow",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
         "display_name": "quarkus-pgclient",
         "display_name": "quarkus-pgclient",

+ 69 - 0
frameworks/Java/quarkus/hibernate/README.md

@@ -0,0 +1,69 @@
+# Local development
+
+During development it might be easier to start a PostgreSQL instance directly:
+
+    sudo podman run --ulimit memlock=-1:-1 -it --rm=true --network host --memory-swappiness=0 --name HibernateTestingPGSQL -e POSTGRES_USER=benchmarkdbuser -e POSTGRES_PASSWORD=benchmarkdbpass -e POSTGRES_DB=hello_world -p 5432:5432 postgres:12
+
+Then edit the `application.properties` resource, so to point to the database on localhost.
+
+On first run make sure you set Hibernate to create the schema:
+
+    quarkus.hibernate-orm.database.generation=drop-and-create
+
+Build the application (might need to have installed the parent and dependencies first):
+
+    mvn clean package
+
+Run the application
+
+    ./start-app.sh
+
+If you just created the DB schema, you will need to create the test data. Hit this endpoint once:
+
+    http://127.0.0.1:8080/createdata
+
+Generate load on the application to test / profile it. I suggest to use `https://github.com/giltene/wrk2`.
+
+Example run, assuming you have built wrk2 in `~/sources/wrk2` :
+
+     ~/sources/wrk2/wrk -c 100 -d 60 -R 400 http://localhost:8080/db
+
+The URL `http://localhost:8080/db` represents one specific benchmark; there are several more to try
+but you will likely want to focus on them one at a time.
+
+
+## Test URLs
+
+### Plaintext Test
+
+    http://localhost:8080/plaintext
+
+### JSON Encoding Test
+
+    http://localhost:8080/json
+
+### Database Query Test
+
+    http://localhost:8080/db
+
+### Database Queries Test
+
+    http://localhost:8080/queries?queries=5
+
+### Database Update Test
+
+    http://localhost:8080/updates?queries=5
+
+### Template rendering Test
+
+    http://localhost:8080/fortunes
+
+## Full verification
+
+Use the main Techempower script in the root to run all official verifications:
+
+./tfb --type all --mode verify --test quarkus-hibernate
+
+## Run the benchmark
+
+./tfb --type all --mode benchmark --test quarkus-hibernate

+ 21 - 0
frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/cdi/HibernateOrmNativeComponents.java

@@ -0,0 +1,21 @@
+package io.quarkus.benchmark.cdi;
+
+import javax.enterprise.inject.Produces;
+import javax.inject.Singleton;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.PersistenceUnit;
+
+import org.hibernate.SessionFactory;
+
+@Singleton
+public class HibernateOrmNativeComponents {
+
+	@PersistenceUnit
+	EntityManagerFactory entityManagerFactory;
+
+	@Produces
+	SessionFactory extractSessionFactory() {
+		return entityManagerFactory.unwrap( SessionFactory.class );
+	}
+
+}

+ 32 - 0
frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/filter/ServerHeaderFilter.java

@@ -0,0 +1,32 @@
+package io.quarkus.benchmark.filter;
+
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
+import javax.inject.Singleton;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.Provider;
+
+import io.quarkus.scheduler.Scheduled;
+
+@Singleton
+@Provider
+public class ServerHeaderFilter implements ContainerResponseFilter {
+
+    private String date;
+
+    @Scheduled(every="1s")
+    void increment() {
+        date = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now());
+    }
+
+    @Override
+    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
+        final MultivaluedMap<String, Object> headers = responseContext.getHeaders();
+        headers.add( "Server", "Quarkus");
+        headers.add( "Date", date);
+    }
+}

+ 3 - 0
frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/model/hibernate/Fortune.java

@@ -1,11 +1,14 @@
 package io.quarkus.benchmark.model.hibernate;
 package io.quarkus.benchmark.model.hibernate;
 
 
+import org.hibernate.annotations.Immutable;
+
 import java.util.Objects;
 import java.util.Objects;
 
 
 import javax.persistence.Entity;
 import javax.persistence.Entity;
 import javax.persistence.Id;
 import javax.persistence.Id;
 
 
 @Entity
 @Entity
+@Immutable
 public class Fortune {
 public class Fortune {
 
 
     @Id
     @Id

+ 0 - 24
frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/model/hibernate/World.java

@@ -1,7 +1,5 @@
 package io.quarkus.benchmark.model.hibernate;
 package io.quarkus.benchmark.model.hibernate;
 
 
-import java.util.Objects;
-
 import javax.persistence.Entity;
 import javax.persistence.Entity;
 import javax.persistence.Id;
 import javax.persistence.Id;
 
 
@@ -12,13 +10,6 @@ public class World {
     private int id;
     private int id;
     private int randomNumber;
     private int randomNumber;
 
 
-    public World() {}
-
-    public World(int id, int randomNumber) {
-        this.id = id;
-        this.randomNumber = randomNumber;
-    }
-
     public int getId() {
     public int getId() {
         return id;
         return id;
     }
     }
@@ -35,19 +26,4 @@ public class World {
         this.randomNumber = randomNumber;
         this.randomNumber = randomNumber;
     }
     }
 
 
-    @Override
-    public boolean equals(Object o) {
-        if (this == o)
-            return true;
-        if (o == null || getClass() != o.getClass())
-            return false;
-        World world = (World) o;
-        return id == world.id &&
-                randomNumber == world.randomNumber;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(id, randomNumber);
-    }
 }
 }

+ 16 - 11
frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/repository/hibernate/FortuneRepository.java

@@ -1,24 +1,29 @@
 package io.quarkus.benchmark.repository.hibernate;
 package io.quarkus.benchmark.repository.hibernate;
 
 
-import java.util.List;
+import io.quarkus.benchmark.model.hibernate.Fortune;
+import org.hibernate.SessionFactory;
+import org.hibernate.StatelessSession;
 
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 import javax.inject.Inject;
-import javax.persistence.EntityManager;
-import javax.persistence.Query;
-import javax.transaction.Transactional;
-
-import io.quarkus.benchmark.model.hibernate.Fortune;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Root;
+import java.util.List;
 
 
 @ApplicationScoped
 @ApplicationScoped
 public class FortuneRepository {
 public class FortuneRepository {
 
 
     @Inject
     @Inject
-    EntityManager em;
+    SessionFactory sf;
 
 
-    @Transactional
-    public List<Fortune> findAll() {
-        Query query = em.createQuery("SELECT f FROM Fortune f");
-        return query.getResultList();
+    public List<Fortune> findAllStateless() {
+        try (StatelessSession s = sf.openStatelessSession()) {
+            CriteriaBuilder criteriaBuilder = sf.getCriteriaBuilder();
+            CriteriaQuery<Fortune> fortuneQuery = criteriaBuilder.createQuery(Fortune.class);
+            Root<Fortune> from = fortuneQuery.from(Fortune.class);
+            fortuneQuery.select(from);
+            return s.createQuery(fortuneQuery).getResultList();
+        }
     }
     }
 }
 }

+ 56 - 13
frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/repository/hibernate/WorldRepository.java

@@ -1,32 +1,75 @@
 package io.quarkus.benchmark.repository.hibernate;
 package io.quarkus.benchmark.repository.hibernate;
 
 
-import javax.enterprise.context.ApplicationScoped;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Set;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.stream.Collectors;
 import javax.inject.Inject;
 import javax.inject.Inject;
-import javax.persistence.EntityManager;
+import javax.inject.Singleton;
 import javax.transaction.Transactional;
 import javax.transaction.Transactional;
 
 
 import io.quarkus.benchmark.model.hibernate.World;
 import io.quarkus.benchmark.model.hibernate.World;
+import org.hibernate.FlushMode;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.StatelessSession;
 
 
-@ApplicationScoped
+
+@Singleton
 public class WorldRepository {
 public class WorldRepository {
 
 
     @Inject
     @Inject
-    EntityManager em;
+    SessionFactory sf;
 
 
+    /**
+     * This method is not required (nor specified) by the benchmark rules,
+     * but is quite handy to seed a local database and be able to experiment
+     * with the app locally.
+     */
     @Transactional
     @Transactional
-    public World find(int id) {
-        return em.find(World.class, id);
+    public void createData() {
+        try (StatelessSession statelessSession = sf.openStatelessSession()) {
+            final ThreadLocalRandom random = ThreadLocalRandom.current();
+            for (int i=1; i<=10000; i++) {
+                final World world = new World();
+                world.setId(i);
+                world.setRandomNumber(1 + random.nextInt(10000));
+                statelessSession.insert(world);
+            }
+        }
     }
     }
 
 
-    @Transactional
-    public void update(World world) {
-        em.merge(world);
+    public World findSingleAndStateless(int id) {
+        try (StatelessSession ss = sf.openStatelessSession()) {
+            return singleStatelessWorldLoad(ss,id);
+        }
     }
     }
 
 
-    @Transactional
-    public void update(World[] worlds) {
-        for (World world : worlds) {
-            update(world);
+    public void updateAll(Collection<World> worlds) {
+        try (Session s = sf.openSession()) {
+            s.setJdbcBatchSize(worlds.size());
+            s.setHibernateFlushMode(FlushMode.MANUAL);
+            for (World w : worlds) {
+                s.update(w);
+            }
+            s.flush();
         }
         }
     }
     }
+
+    public Collection<World> findReadonly(Set<Integer> ids) {
+        try (StatelessSession s = sf.openStatelessSession()) {
+            //The rules require individual load: we can't use the Hibernate feature which allows load by multiple IDs as one single operation
+            ArrayList l = new ArrayList<>(ids.size());
+            for (Integer id : ids) {
+                l.add(singleStatelessWorldLoad(s,id));
+            }
+            return l;
+        }
+    }
+
+    private static World singleStatelessWorldLoad(final StatelessSession ss, final Integer id) {
+        return (World) ss.get(World.class, id);
+    }
+
 }
 }

+ 5 - 6
frameworks/Java/quarkus/base/src/main/java/io/quarkus/benchmark/resource/JsonResource.java → frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/resource/JsonResource.java

@@ -1,13 +1,11 @@
 package io.quarkus.benchmark.resource;
 package io.quarkus.benchmark.resource;
 
 
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
-
 import javax.ws.rs.GET;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MediaType;
+import java.util.Collections;
+import java.util.Map;
 
 
 @Path("/json")
 @Path("/json")
 public class JsonResource {
 public class JsonResource {
@@ -16,7 +14,8 @@ public class JsonResource {
 
 
     @GET
     @GET
     @Produces(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public CompletionStage<Map<String, String>> json() {
-        return CompletableFuture.supplyAsync(() -> Map.of(MESSAGE, HELLO));
+    public Map<String, String> json() {
+        return Collections.singletonMap( MESSAGE, HELLO );
     }
     }
 }
 }
+

+ 2 - 5
frameworks/Java/quarkus/base/src/main/java/io/quarkus/benchmark/resource/PlaintextResource.java → frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/resource/PlaintextResource.java

@@ -1,8 +1,5 @@
 package io.quarkus.benchmark.resource;
 package io.quarkus.benchmark.resource;
 
 
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
-
 import javax.ws.rs.GET;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.Produces;
@@ -14,7 +11,7 @@ public class PlaintextResource {
 
 
     @GET
     @GET
     @Produces(MediaType.TEXT_PLAIN)
     @Produces(MediaType.TEXT_PLAIN)
-    public CompletionStage<String> plaintext() {
-        return CompletableFuture.supplyAsync(() -> HELLO);
+    public String plaintext() {
+        return HELLO;
     }
     }
 }
 }

+ 74 - 28
frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/resource/hibernate/DbResource.java

@@ -1,11 +1,14 @@
 package io.quarkus.benchmark.resource.hibernate;
 package io.quarkus.benchmark.resource.hibernate;
 
 
-import java.util.Arrays;
-import java.util.concurrent.ThreadLocalRandom;
+import io.quarkus.benchmark.model.hibernate.World;
+import io.quarkus.benchmark.repository.hibernate.WorldRepository;
 
 
-import javax.enterprise.context.ApplicationScoped;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ThreadLocalRandom;
 import javax.inject.Inject;
 import javax.inject.Inject;
-import javax.transaction.Transactional;
+import javax.inject.Singleton;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Path;
@@ -13,63 +16,106 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MediaType;
 
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import io.quarkus.benchmark.model.hibernate.World;
-import io.quarkus.benchmark.repository.hibernate.WorldRepository;
 
 
-@ApplicationScoped
-@Path("/hibernate")
+@Singleton
+@Path("/")
 @Produces(MediaType.APPLICATION_JSON)
 @Produces(MediaType.APPLICATION_JSON)
 @Consumes(MediaType.APPLICATION_JSON)
 @Consumes(MediaType.APPLICATION_JSON)
 public class DbResource {
 public class DbResource {
 
 
-    private static Logger LOG = LoggerFactory.getLogger(DbResource.class);
-
     @Inject
     @Inject
     WorldRepository worldRepository;
     WorldRepository worldRepository;
 
 
     @GET
     @GET
     @Path("/db")
     @Path("/db")
     public World db() {
     public World db() {
-        return randomWorld();
+        World world = randomWorldForRead();
+        if (world==null) throw new IllegalStateException( "No data found in DB. Did you seed the database? Make sure to invoke /createdata once." );
+        return world;
     }
     }
 
 
     @GET
     @GET
     @Path("/queries")
     @Path("/queries")
     public World[] queries(@QueryParam("queries") String queries) {
     public World[] queries(@QueryParam("queries") String queries) {
-        var worlds = new World[parseQueryCount(queries)];
-        Arrays.setAll(worlds, i -> randomWorld());
+        final int count = parseQueryCount(queries);
+        World[] worlds = randomWorldForRead(count).toArray(new World[0]);
         return worlds;
         return worlds;
     }
     }
 
 
     @GET
     @GET
     @Path("/updates")
     @Path("/updates")
+    //Rules: https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#database-updates
+    //N.B. the benchmark seems to be designed to get in deadlocks when using a "safe pattern" of updating
+    // the entity within the same transaction as the one which read it.
+    // We therefore need to do a "read then write" while relinquishing the transaction between the two operations, as
+    // all other tested frameworks seem to do.
     public World[] updates(@QueryParam("queries") String queries) {
     public World[] updates(@QueryParam("queries") String queries) {
-        int count = parseQueryCount(queries);
-        var worlds = new World[count];
-        Arrays.setAll(worlds, i -> updateOne());
+        final int count = parseQueryCount(queries);
+        final Collection<World> worlds = randomWorldForRead(count);
+        worlds.forEach( w -> {
+            //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 have us "fail"
+            //the verification:
+            w.setRandomNumber(randomWorldNumber(previousRead));
+        } );
+        worldRepository.updateAll(worlds);
+        return worlds.toArray(new World[0]);
+    }
 
 
-        return worlds;
+    @GET
+    @Path( "/createdata" )
+    public String createData() {
+        worldRepository.createData();
+        return "OK";
     }
     }
 
 
-    @Transactional
-    public World updateOne() {
-        World world = randomWorld();
-        world.setRandomNumber(randomWorldNumber());
-        worldRepository.update(world);
-        return world;
+    private World randomWorldForRead() {
+        return worldRepository.findSingleAndStateless(randomWorldNumber());
     }
     }
 
 
-    private World randomWorld() {
-        return worldRepository.find(randomWorldNumber());
+    private Collection<World> randomWorldForRead(int count) {
+        Set<Integer> ids = new HashSet<>(count);
+        int counter = 0;
+        while (counter < count) {
+            counter += ids.add(Integer.valueOf(randomWorldNumber())) ? 1 : 0;
+        }
+        return worldRepository.findReadonly(ids);
     }
     }
 
 
+    /**
+     * According to benchmark requirements
+     * @return returns a number from 1 to 10000
+     */
     private int randomWorldNumber() {
     private int randomWorldNumber() {
         return 1 + ThreadLocalRandom.current().nextInt(10000);
         return 1 + ThreadLocalRandom.current().nextInt(10000);
     }
     }
 
 
+
+    /**
+     * Also according to benchmark requirements, except that in this special case
+     * of the update test we need to ensure we'll actually generate an update operation:
+     * for this we need to generate a random number between 1 to 10000, but different
+     * from the current field value.
+     * @param previousRead
+     * @return
+     */
+    private int randomWorldNumber(final int previousRead) {
+        //conceptually split the random space in those before previousRead,
+        //and those after: this approach makes sure to not affect the random characteristics.
+        final int trueRandom = ThreadLocalRandom.current().nextInt(9999) + 2;
+        if (trueRandom<=previousRead) {
+            //all figures equal or before the current field read need to be shifted back by one
+            //so to avoid hitting the same number while not affecting the distribution.
+            return trueRandom - 1;
+        }
+        else {
+            //Those after are generated by taking the generated value 2...10000 as is.
+            return trueRandom;
+        }
+    }
+
     private int parseQueryCount(String textValue) {
     private int parseQueryCount(String textValue) {
         if (textValue == null) {
         if (textValue == null) {
             return 1;
             return 1;

+ 10 - 13
frameworks/Java/quarkus/hibernate/src/main/java/io/quarkus/benchmark/resource/hibernate/FortuneResource.java

@@ -5,8 +5,6 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Comparator;
 import java.util.List;
 import java.util.List;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
 
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 import javax.inject.Inject;
@@ -19,12 +17,11 @@ import javax.ws.rs.core.MediaType;
 import com.github.mustachejava.DefaultMustacheFactory;
 import com.github.mustachejava.DefaultMustacheFactory;
 import com.github.mustachejava.Mustache;
 import com.github.mustachejava.Mustache;
 import com.github.mustachejava.MustacheFactory;
 import com.github.mustachejava.MustacheFactory;
-
 import io.quarkus.benchmark.model.hibernate.Fortune;
 import io.quarkus.benchmark.model.hibernate.Fortune;
 import io.quarkus.benchmark.repository.hibernate.FortuneRepository;
 import io.quarkus.benchmark.repository.hibernate.FortuneRepository;
 
 
 @ApplicationScoped
 @ApplicationScoped
-@Path("/hibernate")
+@Path("/")
 @Produces(MediaType.TEXT_HTML)
 @Produces(MediaType.TEXT_HTML)
 @Consumes(MediaType.APPLICATION_JSON)
 @Consumes(MediaType.APPLICATION_JSON)
 public class FortuneResource {
 public class FortuneResource {
@@ -33,24 +30,24 @@ public class FortuneResource {
     FortuneRepository repository;
     FortuneRepository repository;
 
 
     private final Mustache template;
     private final Mustache template;
+    private final Comparator<Fortune> fortuneComparator;
 
 
     public FortuneResource() {
     public FortuneResource() {
         MustacheFactory mf = new DefaultMustacheFactory();
         MustacheFactory mf = new DefaultMustacheFactory();
         template = mf.compile("fortunes.mustache");
         template = mf.compile("fortunes.mustache");
+        fortuneComparator = Comparator.comparing(fortune -> fortune.getMessage());
     }
     }
 
 
     @GET
     @GET
     @Path("/fortunes")
     @Path("/fortunes")
-    public CompletionStage<String> fortunes() {
-        return CompletableFuture.supplyAsync(() -> {
-            List<Fortune> fortunes = new ArrayList<>(repository.findAll());
-            fortunes.add(new Fortune(0, "Additional fortune added at request time."));
-            fortunes.sort(Comparator.comparing(fortune -> fortune.getMessage()));
+    public String fortunes() {
+        List<Fortune> fortunes = new ArrayList<>(repository.findAllStateless());
+        fortunes.add(new Fortune(0, "Additional fortune added at request time."));
+        fortunes.sort(fortuneComparator);
 
 
-            StringWriter writer = new StringWriter();
-            template.execute(writer, Collections.singletonMap("fortunes", fortunes));
+        StringWriter writer = new StringWriter();
+        template.execute(writer, Collections.singletonMap("fortunes", fortunes));
 
 
-            return writer.toString();
-        });
+        return writer.toString();
     }
     }
 }
 }

+ 17 - 4
frameworks/Java/quarkus/hibernate/src/main/resources/application.properties

@@ -1,16 +1,29 @@
-quarkus.datasource.url=jdbc:postgresql://tfb-database:5432/hello_world
+#Test preferQueryMode ? sendBufferSize ? receiveBufferSize ?
+#disableColumnSanitiser requires lower case column names
+quarkus.datasource.url=jdbc:postgresql://tfb-database:5432/hello_world?loggerLevel=OFF&disableColumnSanitiser=true&assumeMinServerVersion=12&sslmode=disable
+%dev.quarkus.datasource.url=jdbc:postgresql://localhost:5432/hello_world?loggerLevel=OFF&disableColumnSanitiser=true&assumeMinServerVersion=12&sslmode=disable
 quarkus.datasource.driver=org.postgresql.Driver
 quarkus.datasource.driver=org.postgresql.Driver
 quarkus.datasource.username=benchmarkdbuser
 quarkus.datasource.username=benchmarkdbuser
 quarkus.datasource.password=benchmarkdbpass
 quarkus.datasource.password=benchmarkdbpass
+quarkus.datasource.transactions=disabled
+quarkus.datasource.detect-statement-leaks=false
 quarkus.datasource.max-size=64
 quarkus.datasource.max-size=64
 quarkus.datasource.min-size=16
 quarkus.datasource.min-size=16
+quarkus.datasource.initial-size=64
 
 
 quarkus.log.console.enable=true
 quarkus.log.console.enable=true
-quarkus.log.console.level=DEBUG
+quarkus.log.console.level=INFO
 quarkus.log.file.enable=false
 quarkus.log.file.enable=false
 quarkus.log.level=INFO
 quarkus.log.level=INFO
-quarkus.log.category."org.hibernate".level=INFO
 
 
+# Fully disable Hibernate ORM statistics gathering::
+quarkus.log.category."org.hibernate.engine.internal.StatisticalLoggingSessionEventListener".level=WARN
+
+# To create the schema:
+%dev.quarkus.hibernate-orm.database.generation=drop-and-create
+%dev.quarkus.hibernate-orm.sql-load-script=import.sql
+quarkus.hibernate-orm.database.generation=validate
 quarkus.hibernate-orm.log.sql=false
 quarkus.hibernate-orm.log.sql=false
 
 
-quarkus.thread-pool.queue-size=2147483647
+
+

+ 1 - 0
frameworks/Java/quarkus/hibernate/src/main/resources/import.sql

@@ -0,0 +1 @@
+INSERT INTO Fortune(id, message) VALUES (1, 'Test value One');

+ 1 - 0
frameworks/Java/quarkus/hibernate/start-app.sh

@@ -0,0 +1 @@
+java -XX:+FlightRecorder -XX:+UseParallelGC -Dquarkus.datasource.url=jdbc:postgresql://localhost:5432/hello_world?loggerLevel=OFF\&disableColumnSanitiser=true\&assumeMinServerVersion=12\&sslmode=disable -Dquarkus.http.host=127.0.0.1 -Djava.lang.Integer.IntegerCache.high=10000 -Dvertx.disableHttpHeadersValidation=true -Dvertx.disableMetrics=true -Dvertx.disableH2c=true -Dvertx.disableWebsockets=true -Dvertx.flashPolicyHandler=false -Dvertx.threadChecks=false -Dvertx.disableContextTimings=true -Dvertx.disableTCCL=true -Dhibernate.allow_update_outside_transaction=true -Djboss.threads.eqe.statistics=false -jar target/hibernate-1.0-SNAPSHOT-runner.jar

+ 4 - 0
frameworks/Java/quarkus/pgclient/pom.xml

@@ -21,5 +21,9 @@
             <groupId>io.quarkus</groupId>
             <groupId>io.quarkus</groupId>
             <artifactId>quarkus-reactive-pg-client</artifactId>
             <artifactId>quarkus-reactive-pg-client</artifactId>
         </dependency>
         </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-vertx-web</artifactId>
+        </dependency>
     </dependencies>
     </dependencies>
 </project>
 </project>

+ 0 - 0
frameworks/Java/quarkus/base/src/main/java/io/quarkus/benchmark/model/Fortune.java → frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/model/Fortune.java


+ 3 - 16
frameworks/Java/quarkus/base/src/main/java/io/quarkus/benchmark/model/World.java → frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/model/World.java

@@ -1,8 +1,6 @@
 package io.quarkus.benchmark.model;
 package io.quarkus.benchmark.model;
 
 
-import java.util.Objects;
-
-public class World {
+public class World implements Comparable<World>{
 
 
     private int id;
     private int id;
     private int randomNumber;
     private int randomNumber;
@@ -31,18 +29,7 @@ public class World {
     }
     }
 
 
     @Override
     @Override
-    public boolean equals(Object o) {
-        if (this == o)
-            return true;
-        if (o == null || getClass() != o.getClass())
-            return false;
-        World world = (World) o;
-        return id == world.id &&
-                randomNumber == world.randomNumber;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(id, randomNumber);
+    public int compareTo(World o) {
+        return Integer.compare(id, o.id);
     }
     }
 }
 }

+ 11 - 22
frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/repository/pgclient/FortuneRepository.java

@@ -2,39 +2,28 @@ package io.quarkus.benchmark.repository.pgclient;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
-import java.util.concurrent.Executors;
 
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 import javax.inject.Inject;
 
 
 import io.quarkus.benchmark.model.Fortune;
 import io.quarkus.benchmark.model.Fortune;
-import io.reactivex.Scheduler;
-import io.reactivex.Single;
-import io.reactivex.schedulers.Schedulers;
-import io.vertx.reactivex.pgclient.PgPool;
-import io.vertx.reactivex.sqlclient.Row;
-import io.vertx.reactivex.sqlclient.RowSet;
+import io.smallrye.mutiny.Uni;
+import io.vertx.mutiny.sqlclient.Row;
 
 
 @ApplicationScoped
 @ApplicationScoped
 public class FortuneRepository {
 public class FortuneRepository {
 
 
     @Inject
     @Inject
-    PgPool client;
+    PgClients clients;
 
 
-    private final Scheduler scheduler = Schedulers.from(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2));
-
-    public Single<List<Fortune>> findAll() {
-        return client.rxQuery("SELECT id, message FROM fortune")
-                .map(RowSet::iterator)
-                .map(rowIterator -> {
-                    List<Fortune> fortunes = new ArrayList<>();
-                    while (rowIterator.hasNext()) {
-                        Row row = rowIterator.next();
-                        Fortune fortune = new Fortune(row.getInteger(0), row.getString(1));
-                        fortunes.add(fortune);
+    public Uni<List<Fortune>> findAll() {
+        return clients.getClient().preparedQuery("SELECT * FROM Fortune" )
+                .map(rowset -> {
+                    List<Fortune> ret = new ArrayList<>(rowset.size()+1);
+                    for(Row r : rowset) {
+                        ret.add(new Fortune(r.getInteger("id"), r.getString("message")));
                     }
                     }
-                    return fortunes;
-                })
-                .subscribeOn(scheduler);
+                    return ret;
+                });
     }
     }
 }
 }

+ 56 - 0
frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/repository/pgclient/PgClientFactory.java

@@ -0,0 +1,56 @@
+package io.quarkus.benchmark.repository.pgclient;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.Produces;
+import javax.inject.Inject;
+
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+import io.vertx.mutiny.core.Vertx;
+import io.vertx.mutiny.pgclient.PgPool;
+import io.vertx.pgclient.PgConnectOptions;
+import io.vertx.sqlclient.PoolOptions;
+
+@ApplicationScoped
+public class PgClientFactory {
+
+    // vertx-reactive:postgresql://tfb-database:5432/hello_world
+    private static final String PG_URI_MATCHER = "vertx-reactive:postgresql://([-a-zA-Z]+):([0-9]+)/(.*)";
+
+    @ConfigProperty(name = "quarkus.datasource.url")
+	String url;
+
+	@ConfigProperty(name = "quarkus.datasource.username")
+	String user;
+
+	@ConfigProperty(name = "quarkus.datasource.password")
+	String pass;
+
+	@Inject
+	Vertx vertx;
+
+	@Produces
+	@ApplicationScoped
+	public PgClients pgClients() {
+	    return new PgClients(this);
+	}
+
+
+	PgPool sqlClient(int size) {
+		PoolOptions options = new PoolOptions();
+		PgConnectOptions connectOptions = new PgConnectOptions();
+		Matcher matcher = Pattern.compile(PG_URI_MATCHER).matcher(url);
+		matcher.matches();
+		connectOptions.setDatabase(matcher.group(3));
+		connectOptions.setHost(matcher.group(1));
+		connectOptions.setPort(Integer.parseInt(matcher.group(2)));
+		connectOptions.setUser(user);
+		connectOptions.setPassword(pass);
+		connectOptions.setCachePreparedStatements(true);
+		options.setMaxSize(size);
+		return PgPool.pool(vertx, connectOptions, options);
+	}
+}

+ 38 - 0
frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/repository/pgclient/PgClients.java

@@ -0,0 +1,38 @@
+package io.quarkus.benchmark.repository.pgclient;
+
+import io.vertx.mutiny.pgclient.PgPool;
+import io.vertx.mutiny.sqlclient.SqlClient;
+
+class PgClients {
+    private static final int POOL_SIZE = 4;
+
+    private ThreadLocal<SqlClient> sqlClient = new ThreadLocal<>();
+    private ThreadLocal<PgPool> pool = new ThreadLocal<>();
+    private PgClientFactory pgClientFactory;
+
+	// for ArC
+	public PgClients() {
+	}
+
+	public PgClients(PgClientFactory pgClientFactory) {
+	    this.pgClientFactory = pgClientFactory;
+    }
+
+    SqlClient getClient() {
+        SqlClient ret = sqlClient.get();
+        if(ret == null) {
+            ret = pgClientFactory.sqlClient(1);
+            sqlClient.set(ret);
+        }
+		return ret;
+	}
+
+	synchronized PgPool getPool() {
+        PgPool ret = pool.get();
+        if(ret == null) {
+            ret = pgClientFactory.sqlClient(POOL_SIZE);
+            pool.set(ret);
+        }
+        return ret;
+	}
+}

+ 22 - 30
frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/repository/pgclient/WorldRepository.java

@@ -1,46 +1,38 @@
 package io.quarkus.benchmark.repository.pgclient;
 package io.quarkus.benchmark.repository.pgclient;
 
 
-import java.util.concurrent.Executors;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 import javax.inject.Inject;
 
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import io.quarkus.benchmark.model.World;
 import io.quarkus.benchmark.model.World;
-import io.reactivex.Maybe;
-import io.reactivex.Scheduler;
-import io.reactivex.Single;
-import io.reactivex.schedulers.Schedulers;
-import io.vertx.reactivex.pgclient.PgPool;
-import io.vertx.reactivex.sqlclient.RowIterator;
-import io.vertx.reactivex.sqlclient.RowSet;
-import io.vertx.reactivex.sqlclient.Tuple;
+import io.smallrye.mutiny.Uni;
+import io.vertx.mutiny.sqlclient.Row;
+import io.vertx.mutiny.sqlclient.Tuple;
 
 
 @ApplicationScoped
 @ApplicationScoped
 public class WorldRepository {
 public class WorldRepository {
 
 
-    private static Logger LOG = LoggerFactory.getLogger(WorldRepository.class);
-
     @Inject
     @Inject
-    PgPool client;
-
-    private final Scheduler scheduler = Schedulers.from(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2));
-
-    public Maybe<World> find(int id) {
-        return client.rxPreparedQuery("SELECT id, randomnumber FROM world WHERE id = $1", Tuple.of(id))
-                .map(RowSet::iterator)
-                .filter(RowIterator::hasNext)
-                .map(RowIterator::next)
-                .map(row -> new World(row.getInteger(0), row.getInteger(1)))
-                .subscribeOn(scheduler);
+    PgClients clients;
+
+    public Uni<World> find(int id) {
+        return clients.getClient().preparedQuery("SELECT id, randomNumber FROM World WHERE id = $1", Tuple.of(id))
+                .map(rowset -> {
+                    Row row = rowset.iterator().next();
+                    return new World(row.getInteger(0), row.getInteger(1));
+                });
     }
     }
 
 
-    public Single<World> update(World world) {
-        return client.rxPreparedQuery("UPDATE world SET randomnumber = $1 WHERE id = $2",
-                Tuple.of(world.getRandomNumber(), world.getId()))
-                .map(rows -> world)
-                .subscribeOn(scheduler);
+    public Uni<Void> update(World[] worlds) {
+        Arrays.sort(worlds);
+        List<Tuple> args = new ArrayList<>(worlds.length);
+        for(World world : worlds) {
+            args.add(Tuple.of(world.getId(), world.getRandomNumber()));
+        }
+        return clients.getPool().preparedBatch("UPDATE World SET randomNumber = $2 WHERE id = $1", args)
+                .map(v -> null);
     }
     }
 }
 }

+ 29 - 0
frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/BaseResource.java

@@ -0,0 +1,29 @@
+package io.quarkus.benchmark.resource.pgclient;
+
+import javax.inject.Inject;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import io.vertx.ext.web.RoutingContext;
+
+public abstract class BaseResource {
+    
+    @Inject
+    ObjectMapper mapper;
+
+    void sendJson(RoutingContext rc, Object value) {
+        try {
+            rc.response().putHeader("Content-Type", "application/json");
+            rc.response().end(mapper.writeValueAsString(value));
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException(e);
+        }   
+    }
+
+    Void handleFail(RoutingContext rc, Throwable t) {
+        rc.response().setStatusCode(500).end(t.toString());
+        return null;
+    }
+
+}

+ 42 - 58
frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/DbResource.java

@@ -1,84 +1,68 @@
 package io.quarkus.benchmark.resource.pgclient;
 package io.quarkus.benchmark.resource.pgclient;
 
 
 import java.util.Arrays;
 import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.ThreadLocalRandom;
 
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 import javax.inject.Inject;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 
 import io.quarkus.benchmark.model.World;
 import io.quarkus.benchmark.model.World;
 import io.quarkus.benchmark.repository.pgclient.WorldRepository;
 import io.quarkus.benchmark.repository.pgclient.WorldRepository;
-import io.reactivex.Maybe;
-import io.reactivex.Single;
+import io.quarkus.vertx.web.Route;
+import io.smallrye.mutiny.Uni;
+import io.smallrye.mutiny.groups.UniAndGroupIterable;
+import io.vertx.ext.web.RoutingContext;
 
 
-@ApplicationScoped
-@Path("/pgclient")
-@Produces(MediaType.APPLICATION_JSON)
-@Consumes(MediaType.APPLICATION_JSON)
-public class DbResource {
 
 
-    private static Logger LOG = LoggerFactory.getLogger(WorldRepository.class);
+@ApplicationScoped
+public class DbResource extends BaseResource {
 
 
     @Inject
     @Inject
     WorldRepository worldRepository;
     WorldRepository worldRepository;
 
 
-    @GET
-    @Path("/db")
-    public CompletionStage<World> db() {
-        return randomWorld()
-                .to(m -> {
-                    CompletableFuture<World> cf = new CompletableFuture<>();
-                    m.subscribe(cf::complete, cf::completeExceptionally, () -> cf.complete(null));
-                    return cf;
-                });
+    @Route(path = "db")
+    public void db(RoutingContext rc) {
+        randomWorld().subscribe().with(world -> sendJson(rc, world),
+                                       t -> handleFail(rc, t));
     }
     }
 
 
-    @GET
-    @Path("/queries")
-    public CompletionStage<List<World>> queries(@QueryParam("queries") String queries) {
-        Maybe<World>[] worlds = new Maybe[parseQueryCount(queries)];
-        Arrays.setAll(worlds, i -> randomWorld());
+    @Route(path = "queries")
+    public void queries(RoutingContext rc) {
+        var queries = rc.request().getParam("queries");
+        var worlds = new Uni[parseQueryCount(queries)];
+        var ret = new World[worlds.length];
+        Arrays.setAll(worlds, i -> {
+            return randomWorld().map(w -> ret[i] = w);
+        });
 
 
-        return Maybe.concatArray(worlds)
-                .toList()
-                .to(m -> {
-                    CompletableFuture<List<World>> cf = new CompletableFuture<>();
-                    m.subscribe(cf::complete, cf::completeExceptionally);
-                    return cf;
-                });
+        ((UniAndGroupIterable<Void>)Uni.combine().all().unis(worlds))
+        .combinedWith(v -> Arrays.asList(ret))
+        .subscribe().with(list -> sendJson(rc, list),
+                          t -> handleFail(rc, t));
     }
     }
 
 
-    @GET
-    @Path("/updates")
-    public CompletionStage<List<World>> updates(@QueryParam("queries") String queries) {
-        Single<World>[] worlds = new Single[parseQueryCount(queries)];
-        Arrays.setAll(worlds, i -> randomWorld().flatMapSingle(world -> {
-            world.setId(randomWorldNumber());
-            return worldRepository.update(world);
-        }));
+    @Route(path = "updates")
+    public void updates(RoutingContext rc) {
+        var queries = rc.request().getParam("queries");
+        var worlds = new Uni[parseQueryCount(queries)];
+        var ret = new World[worlds.length];
+        Arrays.setAll(worlds, i -> {
+            return randomWorld().map(w -> {
+                w.setRandomNumber(randomWorldNumber());
+                ret[i] = w;
+                return w;
+            });
+        });
 
 
-        return Single.concatArray(worlds)
-                .toList()
-                .to(m -> {
-                    CompletableFuture<List<World>> cf = new CompletableFuture<>();
-                    m.subscribe(cf::complete, cf::completeExceptionally);
-                    return cf;
-                });
+        ((UniAndGroupIterable<Void>)Uni.combine().all().unis(worlds))
+        .combinedWith(v -> null)
+        .flatMap(v -> worldRepository.update(ret))
+        .map(v -> Arrays.asList(ret))
+        .subscribe().with(list -> sendJson(rc, list),
+                          t -> handleFail(rc, t));
     }
     }
 
 
-    private Maybe<World> randomWorld() {
+    private Uni<World> randomWorld() {
         return worldRepository.find(randomWorldNumber());
         return worldRepository.find(randomWorldNumber());
     }
     }
 
 
@@ -98,4 +82,4 @@ public class DbResource {
         }
         }
         return Math.min(500, Math.max(1, parsedValue));
         return Math.min(500, Math.max(1, parsedValue));
     }
     }
-}
+}

+ 15 - 28
frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/FortuneResource.java

@@ -3,16 +3,9 @@ package io.quarkus.benchmark.resource.pgclient;
 import java.io.StringWriter;
 import java.io.StringWriter;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Comparator;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
 
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 import javax.inject.Inject;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
 
 
 import com.github.mustachejava.DefaultMustacheFactory;
 import com.github.mustachejava.DefaultMustacheFactory;
 import com.github.mustachejava.Mustache;
 import com.github.mustachejava.Mustache;
@@ -20,12 +13,11 @@ import com.github.mustachejava.MustacheFactory;
 
 
 import io.quarkus.benchmark.model.Fortune;
 import io.quarkus.benchmark.model.Fortune;
 import io.quarkus.benchmark.repository.pgclient.FortuneRepository;
 import io.quarkus.benchmark.repository.pgclient.FortuneRepository;
+import io.quarkus.vertx.web.Route;
+import io.vertx.ext.web.RoutingContext;
 
 
 @ApplicationScoped
 @ApplicationScoped
-@Path("/pgclient")
-@Produces(MediaType.TEXT_HTML)
-@Consumes(MediaType.APPLICATION_JSON)
-public class FortuneResource {
+public class FortuneResource extends BaseResource {
 
 
     @Inject
     @Inject
     FortuneRepository repository;
     FortuneRepository repository;
@@ -37,22 +29,17 @@ public class FortuneResource {
         template = mf.compile("fortunes.mustache");
         template = mf.compile("fortunes.mustache");
     }
     }
 
 
-    @GET
-    @Path("/fortunes")
-    public CompletionStage<String> fortunes() {
-        return repository.findAll()
-                .map(fortunes -> {
-                    fortunes.add(new Fortune(0, "Additional fortune added at request time."));
-                    fortunes.sort(Comparator.comparing(fortune -> fortune.getMessage()));
-
-                    StringWriter writer = new StringWriter();
-                    template.execute(writer, Collections.singletonMap("fortunes", fortunes));
-
-                    return writer.toString();
-                }).to(m -> {
-                    CompletableFuture<String> cf = new CompletableFuture<>();
-                    m.subscribe(cf::complete, cf::completeExceptionally);
-                    return cf;
-                });
+    @Route(path = "fortunes")
+    public void fortunes(RoutingContext rc) {
+        repository.findAll()
+        .subscribe().with( fortunes -> {
+            fortunes.add(new Fortune(0, "Additional fortune added at request time."));
+            fortunes.sort(Comparator.comparing(fortune -> fortune.getMessage()));
+            StringWriter writer = new StringWriter();
+            template.execute(writer, Collections.singletonMap("fortunes", fortunes));
+            rc.response().putHeader("Content-Type", "text/html;charset=UTF-8");
+            rc.response().end(writer.toString());
+        },
+                           t -> handleFail(rc, t));
     }
     }
 }
 }

+ 21 - 0
frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/JsonResource.java

@@ -0,0 +1,21 @@
+package io.quarkus.benchmark.resource.pgclient;
+
+import java.util.Collections;
+
+import javax.enterprise.context.ApplicationScoped;
+
+import io.quarkus.vertx.web.Route;
+import io.vertx.ext.web.RoutingContext;
+
+@ApplicationScoped
+public class JsonResource extends BaseResource {
+
+    private static final String MESSAGE = "message";
+    private static final String HELLO = "Hello, World!";
+
+    @Route(path = "json")
+    public void json(RoutingContext rc) {
+        sendJson(rc, Collections.singletonMap( MESSAGE, HELLO ));
+    }
+}
+

+ 17 - 0
frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/PlaintextResource.java

@@ -0,0 +1,17 @@
+package io.quarkus.benchmark.resource.pgclient;
+
+import javax.enterprise.context.ApplicationScoped;
+
+import io.quarkus.vertx.web.Route;
+import io.vertx.ext.web.RoutingContext;
+
+@ApplicationScoped
+public class PlaintextResource {
+    private static final String HELLO = "Hello, World!";
+
+    @Route(path = "plaintext")
+    public void plaintext(RoutingContext rc) {
+        rc.response().putHeader("Content-Type", "text/plain");
+        rc.response().end(HELLO);
+    }
+}

+ 28 - 0
frameworks/Java/quarkus/pgclient/src/main/java/io/quarkus/benchmark/resource/pgclient/ServerHeaderFilter.java

@@ -0,0 +1,28 @@
+package io.quarkus.benchmark.resource.pgclient;
+
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
+import javax.inject.Singleton;
+
+import io.quarkus.scheduler.Scheduled;
+import io.quarkus.vertx.web.RouteFilter;
+import io.vertx.ext.web.RoutingContext;
+
+@Singleton
+public class ServerHeaderFilter {
+
+    private String date;
+
+    @Scheduled(every="1s")
+    void increment() {
+        date = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now());
+    }
+
+    @RouteFilter(100) 
+    void myFilter(RoutingContext rc) {
+       rc.response().putHeader( "Server", "Quarkus");
+       rc.response().putHeader( "Date", date);
+       rc.next(); 
+    }
+}

+ 1 - 2
frameworks/Java/quarkus/pgclient/src/main/resources/application.properties

@@ -5,5 +5,4 @@ quarkus.datasource.max-size=64
 quarkus.log.console.enable=true
 quarkus.log.console.enable=true
 quarkus.log.console.level=INFO
 quarkus.log.console.level=INFO
 quarkus.log.file.enable=false
 quarkus.log.file.enable=false
-quarkus.log.level=INFO
-quarkus.thread-pool.queue-size=2147483647
+quarkus.log.level=INFO

+ 2 - 0
frameworks/Java/quarkus/pgclient/start-app.sh

@@ -0,0 +1,2 @@
+java -XX:+FlightRecorder -XX:+UseParallelGC -Dquarkus.datasource.url=vertx-reactive:postgresql://localhost:5432/hello_world -Dquarkus.http.host=127.0.0.1 -Djava.lang.Integer.IntegerCache.high=10000 -Dvertx.disableHttpHeadersValidation=true -Dvertx.disableMetrics=true -Dvertx.disableH2c=true -Dvertx.disableWebsockets=true -Dvertx.flashPolicyHandler=false -Dvertx.threadChecks=false -Dvertx.disableContextTimings=true -Dvertx.disableTCCL=true -Dhibernate.allow_update_outside_transaction=true -Djboss.threads.eqe.statistics=false -jar target/pgclient-1.0-SNAPSHOT-runner.jar
+

+ 2 - 2
frameworks/Java/quarkus/pom.xml

@@ -8,7 +8,7 @@
     <packaging>pom</packaging>
     <packaging>pom</packaging>
 
 
     <properties>
     <properties>
-        <quarkus.version>1.1.1.Final</quarkus.version>
+        <quarkus.version>1.4.2.Final</quarkus.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <maven.compiler.source>11</maven.compiler.source>
         <maven.compiler.source>11</maven.compiler.source>
         <maven.compiler.target>11</maven.compiler.target>
         <maven.compiler.target>11</maven.compiler.target>
@@ -44,7 +44,7 @@
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>io.quarkus</groupId>
             <groupId>io.quarkus</groupId>
-            <artifactId>quarkus-resteasy-jsonb</artifactId>
+            <artifactId>quarkus-resteasy-jackson</artifactId>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>io.quarkus</groupId>
             <groupId>io.quarkus</groupId>

+ 0 - 18
frameworks/Java/quarkus/quarkus-hibernate.dockerfile

@@ -1,18 +0,0 @@
-FROM maven:3.6.1-jdk-11-slim as maven
-WORKDIR /quarkus
-COPY pom.xml pom.xml
-COPY base/pom.xml base/pom.xml
-COPY hibernate/pom.xml hibernate/pom.xml
-COPY pgclient/pom.xml pgclient/pom.xml
-RUN mvn dependency:go-offline -q -pl base
-COPY base/src base/src
-COPY hibernate/src hibernate/src
-COPY pgclient/src pgclient/src
-
-RUN mvn package -q -pl hibernate -am
-
-FROM openjdk:11.0.3-jdk-slim
-WORKDIR /quarkus
-COPY --from=maven /quarkus/hibernate/target/lib lib
-COPY --from=maven /quarkus/hibernate/target/hibernate-1.0-SNAPSHOT-runner.jar app.jar
-CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-jar", "app.jar"]

+ 4 - 4
frameworks/Java/quarkus/quarkus-pgclient.dockerfile

@@ -1,18 +1,18 @@
-FROM maven:3.6.1-jdk-11-slim as maven
+FROM maven:3.6.3-jdk-11-slim as maven
 WORKDIR /quarkus
 WORKDIR /quarkus
 COPY pom.xml pom.xml
 COPY pom.xml pom.xml
 COPY base/pom.xml base/pom.xml
 COPY base/pom.xml base/pom.xml
 COPY hibernate/pom.xml hibernate/pom.xml
 COPY hibernate/pom.xml hibernate/pom.xml
 COPY pgclient/pom.xml pgclient/pom.xml
 COPY pgclient/pom.xml pgclient/pom.xml
 RUN mvn dependency:go-offline -q -pl base
 RUN mvn dependency:go-offline -q -pl base
-COPY base/src base/src
+COPY base/src/main/resources base/src/main/resources
 COPY hibernate/src hibernate/src
 COPY hibernate/src hibernate/src
 COPY pgclient/src pgclient/src
 COPY pgclient/src pgclient/src
 
 
 RUN mvn package -q -pl pgclient -am
 RUN mvn package -q -pl pgclient -am
 
 
-FROM openjdk:11.0.3-jdk-slim
+FROM openjdk:11.0.6-jdk-slim
 WORKDIR /quarkus
 WORKDIR /quarkus
 COPY --from=maven /quarkus/pgclient/target/lib lib
 COPY --from=maven /quarkus/pgclient/target/lib lib
 COPY --from=maven /quarkus/pgclient/target/pgclient-1.0-SNAPSHOT-runner.jar app.jar
 COPY --from=maven /quarkus/pgclient/target/pgclient-1.0-SNAPSHOT-runner.jar app.jar
-CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dquarkus.profile=pgclient", "-jar", "app.jar"]
+CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Djava.lang.Integer.IntegerCache.high=10000", "-Dvertx.disableHttpHeadersValidation=true", "-Dvertx.disableMetrics=true", "-Dvertx.disableH2c=true", "-Dvertx.disableWebsockets=true", "-Dvertx.flashPolicyHandler=false", "-Dvertx.threadChecks=false", "-Dvertx.disableContextTimings=true", "-Dvertx.disableTCCL=true", "-Dhibernate.allow_update_outside_transaction=true", "-Djboss.threads.eqe.statistics=false", "-jar", "app.jar"]

+ 6 - 6
frameworks/Java/quarkus/quarkus.dockerfile

@@ -1,4 +1,4 @@
-FROM maven:3.6.1-jdk-11-slim as maven
+FROM maven:3.6.3-jdk-11-slim as maven
 WORKDIR /quarkus
 WORKDIR /quarkus
 COPY pom.xml pom.xml
 COPY pom.xml pom.xml
 COPY base/pom.xml base/pom.xml
 COPY base/pom.xml base/pom.xml
@@ -9,10 +9,10 @@ COPY base/src base/src
 COPY hibernate/src hibernate/src
 COPY hibernate/src hibernate/src
 COPY pgclient/src pgclient/src
 COPY pgclient/src pgclient/src
 
 
-RUN mvn package -q -pl base
+RUN mvn package -q -pl hibernate -am
 
 
-FROM openjdk:11.0.3-jdk-slim
+FROM openjdk:11.0.6-jdk-slim
 WORKDIR /quarkus
 WORKDIR /quarkus
-COPY --from=maven /quarkus/base/target/lib lib
-COPY --from=maven /quarkus/base/target/base-1.0-SNAPSHOT-runner.jar app.jar
-CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-jar", "app.jar"]
+COPY --from=maven /quarkus/hibernate/target/lib lib
+COPY --from=maven /quarkus/hibernate/target/hibernate-1.0-SNAPSHOT-runner.jar app.jar
+CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Djava.lang.Integer.IntegerCache.high=10000", "-Dvertx.disableHttpHeadersValidation=true", "-Dvertx.disableMetrics=true", "-Dvertx.disableH2c=true", "-Dvertx.disableWebsockets=true", "-Dvertx.flashPolicyHandler=false", "-Dvertx.threadChecks=false", "-Dvertx.disableContextTimings=true", "-Dvertx.disableTCCL=true", "-Dhibernate.allow_update_outside_transaction=true", "-Djboss.threads.eqe.statistics=false", "-jar", "app.jar"]