Browse Source

Micronaut performance improvements (#9049)

* Performance improvements

* CR

* Correct benchmark_config.json
Denis Stepanov 1 year ago
parent
commit
78cd8a7505
18 changed files with 198 additions and 133 deletions
  1. 24 24
      frameworks/Java/micronaut/benchmark_config.json
  2. 6 12
      frameworks/Java/micronaut/common/src/main/java/benchmark/controller/AsyncBenchmarkController.java
  3. 0 3
      frameworks/Java/micronaut/common/src/main/java/benchmark/controller/BenchmarkController.java
  4. 4 1
      frameworks/Java/micronaut/common/src/main/java/benchmark/filter/ServerHeaderFilter.java
  5. 1 0
      frameworks/Java/micronaut/common/src/main/resources/application-benchmark.yml
  6. 3 2
      frameworks/Java/micronaut/common/src/main/resources/application-common.yml
  7. 2 2
      frameworks/Java/micronaut/micronaut-data-jdbc.dockerfile
  8. 2 2
      frameworks/Java/micronaut/micronaut-data-mongodb.dockerfile
  9. 2 2
      frameworks/Java/micronaut/micronaut-data-r2dbc.dockerfile
  10. 2 2
      frameworks/Java/micronaut/micronaut-jdbc.dockerfile
  11. 2 2
      frameworks/Java/micronaut/micronaut-r2dbc.dockerfile
  12. 7 9
      frameworks/Java/micronaut/micronaut-vertx-pg-client/src/main/java/benchmark/AbstractVertxSqlClientRepository.java
  13. 140 0
      frameworks/Java/micronaut/micronaut-vertx-pg-client/src/main/java/benchmark/ConnectionHolder.java
  14. 0 58
      frameworks/Java/micronaut/micronaut-vertx-pg-client/src/main/java/benchmark/PgClientFactory.java
  15. 0 5
      frameworks/Java/micronaut/micronaut-vertx-pg-client/src/main/java/benchmark/VertxPgFortuneRepository.java
  16. 1 6
      frameworks/Java/micronaut/micronaut-vertx-pg-client/src/main/java/benchmark/VertxPgWorldRepository.java
  17. 2 2
      frameworks/Java/micronaut/micronaut.dockerfile
  18. 0 1
      frameworks/Java/micronaut/run_benchmark.sh

+ 24 - 24
frameworks/Java/micronaut/benchmark_config.json

@@ -16,9 +16,9 @@
         "framework": "Micronaut",
         "framework": "Micronaut",
         "language": "Java",
         "language": "Java",
         "flavor": "None",
         "flavor": "None",
-        "orm": "raw",
+        "orm": "Raw",
         "platform": "Netty",
         "platform": "Netty",
-        "webserver": "None",
+        "webserver": "Netty",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
         "display_name": "Micronaut Vertx PG Client",
         "display_name": "Micronaut Vertx PG Client",
@@ -39,9 +39,9 @@
         "framework": "Micronaut",
         "framework": "Micronaut",
         "language": "Java",
         "language": "Java",
         "flavor": "None",
         "flavor": "None",
-        "orm": "raw",
+        "orm": "Raw",
         "platform": "Netty",
         "platform": "Netty",
-        "webserver": "None",
+        "webserver": "Netty",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
         "display_name": "Micronaut Vertx PG Client GraalVM",
         "display_name": "Micronaut Vertx PG Client GraalVM",
@@ -60,9 +60,9 @@
         "framework": "Micronaut",
         "framework": "Micronaut",
         "language": "Java",
         "language": "Java",
         "flavor": "None",
         "flavor": "None",
-        "orm": "raw",
+        "orm": "Raw",
         "platform": "Netty",
         "platform": "Netty",
-        "webserver": "None",
+        "webserver": "Netty",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
         "display_name": "Micronaut JDBC",
         "display_name": "Micronaut JDBC",
@@ -81,9 +81,9 @@
         "framework": "Micronaut",
         "framework": "Micronaut",
         "language": "Java",
         "language": "Java",
         "flavor": "None",
         "flavor": "None",
-        "orm": "raw",
+        "orm": "Raw",
         "platform": "Netty",
         "platform": "Netty",
-        "webserver": "None",
+        "webserver": "Netty",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
         "display_name": "Micronaut JDBC GraalVM",
         "display_name": "Micronaut JDBC GraalVM",
@@ -102,9 +102,9 @@
         "framework": "Micronaut",
         "framework": "Micronaut",
         "language": "Java",
         "language": "Java",
         "flavor": "None",
         "flavor": "None",
-        "orm": "raw",
+        "orm": "Raw",
         "platform": "Netty",
         "platform": "Netty",
-        "webserver": "None",
+        "webserver": "Netty",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
         "display_name": "Micronaut R2DBC",
         "display_name": "Micronaut R2DBC",
@@ -123,9 +123,9 @@
         "framework": "Micronaut",
         "framework": "Micronaut",
         "language": "Java",
         "language": "Java",
         "flavor": "None",
         "flavor": "None",
-        "orm": "raw",
+        "orm": "Raw",
         "platform": "Netty",
         "platform": "Netty",
-        "webserver": "None",
+        "webserver": "Netty",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
         "display_name": "Micronaut R2DBC GraalVM",
         "display_name": "Micronaut R2DBC GraalVM",
@@ -139,14 +139,14 @@
         "update_url": "/updates?queries=",
         "update_url": "/updates?queries=",
         "port": 8080,
         "port": 8080,
         "approach": "Realistic",
         "approach": "Realistic",
-        "classification": "Micro",
+        "classification": "fullstack",
         "database": "Postgres",
         "database": "Postgres",
         "framework": "Micronaut",
         "framework": "Micronaut",
         "language": "Java",
         "language": "Java",
         "flavor": "None",
         "flavor": "None",
-        "orm": "raw",
+        "orm": "Micro",
         "platform": "Netty",
         "platform": "Netty",
-        "webserver": "None",
+        "webserver": "Netty",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
         "display_name": "Micronaut Data JDBC",
         "display_name": "Micronaut Data JDBC",
@@ -160,14 +160,14 @@
         "update_url": "/updates?queries=",
         "update_url": "/updates?queries=",
         "port": 8080,
         "port": 8080,
         "approach": "Realistic",
         "approach": "Realistic",
-        "classification": "Micro",
+        "classification": "fullstack",
         "database": "Postgres",
         "database": "Postgres",
         "framework": "Micronaut",
         "framework": "Micronaut",
         "language": "Java",
         "language": "Java",
         "flavor": "None",
         "flavor": "None",
-        "orm": "raw",
+        "orm": "Micro",
         "platform": "Netty",
         "platform": "Netty",
-        "webserver": "None",
+        "webserver": "Netty",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
         "display_name": "Micronaut Data JDBC GraalVM",
         "display_name": "Micronaut Data JDBC GraalVM",
@@ -181,14 +181,14 @@
         "update_url": "/updates?queries=",
         "update_url": "/updates?queries=",
         "port": 8080,
         "port": 8080,
         "approach": "Realistic",
         "approach": "Realistic",
-        "classification": "Micro",
+        "classification": "fullstack",
         "database": "MongoDB",
         "database": "MongoDB",
         "framework": "Micronaut",
         "framework": "Micronaut",
         "language": "Java",
         "language": "Java",
         "flavor": "None",
         "flavor": "None",
-        "orm": "raw",
+        "orm": "Micro",
         "platform": "Netty",
         "platform": "Netty",
-        "webserver": "None",
+        "webserver": "Netty",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
         "display_name": "Micronaut Data MongoDB",
         "display_name": "Micronaut Data MongoDB",
@@ -202,14 +202,14 @@
         "update_url": "/updates?queries=",
         "update_url": "/updates?queries=",
         "port": 8080,
         "port": 8080,
         "approach": "Realistic",
         "approach": "Realistic",
-        "classification": "Micro",
+        "classification": "fullstack",
         "database": "MongoDB",
         "database": "MongoDB",
         "framework": "Micronaut",
         "framework": "Micronaut",
         "language": "Java",
         "language": "Java",
         "flavor": "None",
         "flavor": "None",
-        "orm": "raw",
+        "orm": "Micro",
         "platform": "Netty",
         "platform": "Netty",
-        "webserver": "None",
+        "webserver": "Netty",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
         "display_name": "Micronaut Data MongoDB GraalVM",
         "display_name": "Micronaut Data MongoDB GraalVM",

+ 6 - 12
frameworks/Java/micronaut/common/src/main/java/benchmark/controller/AsyncBenchmarkController.java

@@ -9,15 +9,12 @@ import io.micronaut.http.HttpResponse;
 import io.micronaut.http.annotation.Controller;
 import io.micronaut.http.annotation.Controller;
 import io.micronaut.http.annotation.Get;
 import io.micronaut.http.annotation.Get;
 import io.micronaut.http.annotation.QueryValue;
 import io.micronaut.http.annotation.QueryValue;
-import io.micronaut.scheduling.TaskExecutors;
-import jakarta.inject.Named;
 import views.fortunes;
 import views.fortunes;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.Comparator;
 import java.util.List;
 import java.util.List;
 import java.util.concurrent.CompletionStage;
 import java.util.concurrent.CompletionStage;
-import java.util.concurrent.Executor;
 
 
 import static java.util.Comparator.comparing;
 import static java.util.Comparator.comparing;
 
 
@@ -27,14 +24,11 @@ public class AsyncBenchmarkController extends AbstractBenchmarkController {
 
 
     private final AsyncWorldRepository worldRepository;
     private final AsyncWorldRepository worldRepository;
     private final AsyncFortuneRepository fortuneRepository;
     private final AsyncFortuneRepository fortuneRepository;
-    private final Executor executor;
 
 
     public AsyncBenchmarkController(AsyncWorldRepository worldRepository,
     public AsyncBenchmarkController(AsyncWorldRepository worldRepository,
-                                    AsyncFortuneRepository fortuneRepository,
-                                    @Named(TaskExecutors.BLOCKING) Executor executor) {
+                                    AsyncFortuneRepository fortuneRepository) {
         this.worldRepository = worldRepository;
         this.worldRepository = worldRepository;
         this.fortuneRepository = fortuneRepository;
         this.fortuneRepository = fortuneRepository;
-        this.executor = executor;
     }
     }
 
 
     @Get("/prepare-data-for-test")
     @Get("/prepare-data-for-test")
@@ -45,7 +39,7 @@ public class AsyncBenchmarkController extends AbstractBenchmarkController {
     // https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#single-database-query
     // https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#single-database-query
     @Get("/db")
     @Get("/db")
     public CompletionStage<World> db() {
     public CompletionStage<World> db() {
-        return worldRepository.findById(randomId()).thenApplyAsync(world -> world, executor);
+        return worldRepository.findById(randomId());
     }
     }
 
 
     // https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#multiple-database-queries
     // https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#multiple-database-queries
@@ -56,20 +50,20 @@ public class AsyncBenchmarkController extends AbstractBenchmarkController {
         for (int i = 0; i < count; i++) {
         for (int i = 0; i < count; i++) {
             ids.add(randomId());
             ids.add(randomId());
         }
         }
-        return worldRepository.findByIds(ids).thenApplyAsync(worlds -> worlds, executor);
+        return worldRepository.findByIds(ids);
     }
     }
 
 
     // https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#fortunes
     // https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#fortunes
     @Get(value = "/fortunes", produces = "text/html;charset=utf-8")
     @Get(value = "/fortunes", produces = "text/html;charset=utf-8")
     public CompletionStage<HttpResponse<String>> fortune() {
     public CompletionStage<HttpResponse<String>> fortune() {
-        return fortuneRepository.findAll().thenApplyAsync(fortuneList -> {
+        return fortuneRepository.findAll().thenApply(fortuneList -> {
             List<Fortune> all = new ArrayList<>(fortuneList.size() + 1);
             List<Fortune> all = new ArrayList<>(fortuneList.size() + 1);
             all.add(new Fortune(0, "Additional fortune added at request time."));
             all.add(new Fortune(0, "Additional fortune added at request time."));
             all.addAll(fortuneList);
             all.addAll(fortuneList);
             all.sort(comparing(Fortune::message));
             all.sort(comparing(Fortune::message));
             String body = fortunes.template(all).render().toString();
             String body = fortunes.template(all).render().toString();
             return HttpResponse.ok(body).contentType("text/html;charset=utf-8");
             return HttpResponse.ok(body).contentType("text/html;charset=utf-8");
-        }, executor);
+        });
     }
     }
 
 
     // https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#database-updates
     // https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#database-updates
@@ -80,7 +74,7 @@ public class AsyncBenchmarkController extends AbstractBenchmarkController {
                 world.setRandomNumber(randomWorldNumber());
                 world.setRandomNumber(randomWorldNumber());
             }
             }
             worlds.sort(Comparator.comparingInt(World::getId)); // Avoid deadlock
             worlds.sort(Comparator.comparingInt(World::getId)); // Avoid deadlock
-            return worldRepository.updateAll(worlds).thenApplyAsync(ignore -> worlds, executor);
+            return worldRepository.updateAll(worlds).thenApply(ignore -> worlds);
         });
         });
     }
     }
 
 

+ 0 - 3
frameworks/Java/micronaut/common/src/main/java/benchmark/controller/BenchmarkController.java

@@ -9,8 +9,6 @@ import io.micronaut.http.HttpResponse;
 import io.micronaut.http.annotation.Controller;
 import io.micronaut.http.annotation.Controller;
 import io.micronaut.http.annotation.Get;
 import io.micronaut.http.annotation.Get;
 import io.micronaut.http.annotation.QueryValue;
 import io.micronaut.http.annotation.QueryValue;
-import io.micronaut.scheduling.TaskExecutors;
-import io.micronaut.scheduling.annotation.ExecuteOn;
 import views.fortunes;
 import views.fortunes;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -20,7 +18,6 @@ import java.util.List;
 
 
 import static java.util.Comparator.comparing;
 import static java.util.Comparator.comparing;
 
 
-@ExecuteOn(TaskExecutors.IO)
 @Requires(beans = {WorldRepository.class, FortuneRepository.class})
 @Requires(beans = {WorldRepository.class, FortuneRepository.class})
 @Controller
 @Controller
 public class BenchmarkController extends AbstractBenchmarkController {
 public class BenchmarkController extends AbstractBenchmarkController {

+ 4 - 1
frameworks/Java/micronaut/common/src/main/java/benchmark/filter/ServerHeaderFilter.java

@@ -4,7 +4,9 @@ import io.micronaut.http.MutableHttpResponse;
 import io.micronaut.http.annotation.Filter;
 import io.micronaut.http.annotation.Filter;
 import io.micronaut.http.annotation.ResponseFilter;
 import io.micronaut.http.annotation.ResponseFilter;
 import io.micronaut.http.annotation.ServerFilter;
 import io.micronaut.http.annotation.ServerFilter;
+import io.micronaut.http.netty.NettyHttpHeaders;
 import io.micronaut.scheduling.annotation.Scheduled;
 import io.micronaut.scheduling.annotation.Scheduled;
+import io.netty.handler.codec.http.HttpHeaderNames;
 
 
 import java.time.ZonedDateTime;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatter;
@@ -25,7 +27,8 @@ public class ServerHeaderFilter {
 
 
     @ResponseFilter
     @ResponseFilter
     public void addDateHeader(MutableHttpResponse<?> mutableHttpResponse) {
     public void addDateHeader(MutableHttpResponse<?> mutableHttpResponse) {
-        mutableHttpResponse.header("Date", dateHeader);
+        NettyHttpHeaders nettyHttpHeaders = (NettyHttpHeaders) mutableHttpResponse.getHeaders();
+        nettyHttpHeaders.setUnsafe(HttpHeaderNames.DATE, dateHeader);
     }
     }
 
 
 }
 }

+ 1 - 0
frameworks/Java/micronaut/common/src/main/resources/application-benchmark.yml

@@ -5,6 +5,7 @@ micronaut:
     port: 8080
     port: 8080
     server-header: Micronaut
     server-header: Micronaut
     date-header: false
     date-header: false
+    validate-url: false
   http:
   http:
     client:
     client:
       read-timeout: 60s
       read-timeout: 60s

+ 3 - 2
frameworks/Java/micronaut/common/src/main/resources/application-common.yml

@@ -5,6 +5,7 @@ micronaut:
     port: 8080
     port: 8080
     server-header: Micronaut
     server-header: Micronaut
     date-header: false
     date-header: false
+    validate-url: false
 
 
 netty:
 netty:
   resource-leak-detector-level: DISABLED
   resource-leak-detector-level: DISABLED
@@ -17,7 +18,7 @@ datasources:
     driverClassName: org.postgresql.Driver
     driverClassName: org.postgresql.Driver
     db-type: postgresql
     db-type: postgresql
     dialect: POSTGRES
     dialect: POSTGRES
-    maximum-pool-size: 48
+    maximum-pool-size: 512
     transaction-per-operation: false
     transaction-per-operation: false
     allow-connection-per-operation: true
     allow-connection-per-operation: true
 
 
@@ -29,7 +30,7 @@ r2dbc:
       options:
       options:
         protocol: postgres
         protocol: postgres
         initialSize: 0 # https://github.com/micronaut-projects/micronaut-data/issues/2136
         initialSize: 0 # https://github.com/micronaut-projects/micronaut-data/issues/2136
-        maxSize: 48
+        maxSize: 512
 
 
 mongodb:
 mongodb:
   package-names:
   package-names:

+ 2 - 2
frameworks/Java/micronaut/micronaut-data-jdbc.dockerfile

@@ -1,9 +1,9 @@
-FROM gradle:8.7.0-jdk17 as build
+FROM gradle:8.7.0-jdk21 as build
 COPY --chown=gradle:gradle . /home/gradle/src
 COPY --chown=gradle:gradle . /home/gradle/src
 WORKDIR /home/gradle/src
 WORKDIR /home/gradle/src
 RUN gradle micronaut-data-jdbc:build -x test --no-daemon
 RUN gradle micronaut-data-jdbc:build -x test --no-daemon
 
 
-FROM openjdk:21
+FROM openjdk:22
 WORKDIR /micronaut
 WORKDIR /micronaut
 COPY --from=build /home/gradle/src/micronaut-data-jdbc/build/libs/micronaut-data-jdbc-all.jar micronaut.jar
 COPY --from=build /home/gradle/src/micronaut-data-jdbc/build/libs/micronaut-data-jdbc-all.jar micronaut.jar
 COPY run_benchmark.sh run_benchmark.sh
 COPY run_benchmark.sh run_benchmark.sh

+ 2 - 2
frameworks/Java/micronaut/micronaut-data-mongodb.dockerfile

@@ -1,9 +1,9 @@
-FROM gradle:8.7.0-jdk17 as build
+FROM gradle:8.7.0-jdk21 as build
 COPY --chown=gradle:gradle . /home/gradle/src
 COPY --chown=gradle:gradle . /home/gradle/src
 WORKDIR /home/gradle/src
 WORKDIR /home/gradle/src
 RUN gradle micronaut-data-mongodb:build -x test --no-daemon
 RUN gradle micronaut-data-mongodb:build -x test --no-daemon
 
 
-FROM openjdk:21
+FROM openjdk:22
 WORKDIR /micronaut
 WORKDIR /micronaut
 COPY --from=build /home/gradle/src/micronaut-data-mongodb/build/libs/micronaut-data-mongodb-all.jar micronaut.jar
 COPY --from=build /home/gradle/src/micronaut-data-mongodb/build/libs/micronaut-data-mongodb-all.jar micronaut.jar
 COPY run_benchmark.sh run_benchmark.sh
 COPY run_benchmark.sh run_benchmark.sh

+ 2 - 2
frameworks/Java/micronaut/micronaut-data-r2dbc.dockerfile

@@ -1,9 +1,9 @@
-FROM gradle:8.7.0-jdk17 as build
+FROM gradle:8.7.0-jdk21 as build
 COPY --chown=gradle:gradle . /home/gradle/src
 COPY --chown=gradle:gradle . /home/gradle/src
 WORKDIR /home/gradle/src
 WORKDIR /home/gradle/src
 RUN gradle micronaut-data-r2dbc:build -x test --no-daemon
 RUN gradle micronaut-data-r2dbc:build -x test --no-daemon
 
 
-FROM openjdk:21
+FROM openjdk:22
 WORKDIR /micronaut
 WORKDIR /micronaut
 COPY --from=build /home/gradle/src/micronaut-data-r2dbc/build/libs/micronaut-data-r2dbc-all.jar micronaut.jar
 COPY --from=build /home/gradle/src/micronaut-data-r2dbc/build/libs/micronaut-data-r2dbc-all.jar micronaut.jar
 COPY run_benchmark.sh run_benchmark.sh
 COPY run_benchmark.sh run_benchmark.sh

+ 2 - 2
frameworks/Java/micronaut/micronaut-jdbc.dockerfile

@@ -1,9 +1,9 @@
-FROM gradle:8.7.0-jdk17 as build
+FROM gradle:8.7.0-jdk21 as build
 COPY --chown=gradle:gradle . /home/gradle/src
 COPY --chown=gradle:gradle . /home/gradle/src
 WORKDIR /home/gradle/src
 WORKDIR /home/gradle/src
 RUN gradle micronaut-jdbc:build -x test --no-daemon
 RUN gradle micronaut-jdbc:build -x test --no-daemon
 
 
-FROM openjdk:21
+FROM openjdk:22
 WORKDIR /micronaut
 WORKDIR /micronaut
 COPY --from=build /home/gradle/src/micronaut-jdbc/build/libs/micronaut-jdbc-all.jar micronaut.jar
 COPY --from=build /home/gradle/src/micronaut-jdbc/build/libs/micronaut-jdbc-all.jar micronaut.jar
 COPY run_benchmark.sh run_benchmark.sh
 COPY run_benchmark.sh run_benchmark.sh

+ 2 - 2
frameworks/Java/micronaut/micronaut-r2dbc.dockerfile

@@ -1,9 +1,9 @@
-FROM gradle:8.7.0-jdk17 as build
+FROM gradle:8.7.0-jdk21 as build
 COPY --chown=gradle:gradle . /home/gradle/src
 COPY --chown=gradle:gradle . /home/gradle/src
 WORKDIR /home/gradle/src
 WORKDIR /home/gradle/src
 RUN gradle micronaut-r2dbc:build -x test --no-daemon
 RUN gradle micronaut-r2dbc:build -x test --no-daemon
 
 
-FROM openjdk:21
+FROM openjdk:22
 WORKDIR /micronaut
 WORKDIR /micronaut
 COPY --from=build /home/gradle/src/micronaut-r2dbc/build/libs/micronaut-r2dbc-all.jar micronaut.jar
 COPY --from=build /home/gradle/src/micronaut-r2dbc/build/libs/micronaut-r2dbc-all.jar micronaut.jar
 COPY run_benchmark.sh run_benchmark.sh
 COPY run_benchmark.sh run_benchmark.sh

+ 7 - 9
frameworks/Java/micronaut/micronaut-vertx-pg-client/src/main/java/benchmark/AbstractVertxSqlClientRepository.java

@@ -7,6 +7,7 @@ import io.vertx.sqlclient.Row;
 import io.vertx.sqlclient.RowSet;
 import io.vertx.sqlclient.RowSet;
 import io.vertx.sqlclient.SqlClient;
 import io.vertx.sqlclient.SqlClient;
 import io.vertx.sqlclient.Tuple;
 import io.vertx.sqlclient.Tuple;
+import jakarta.inject.Inject;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
@@ -15,23 +16,20 @@ import java.util.function.Function;
 
 
 public class AbstractVertxSqlClientRepository {
 public class AbstractVertxSqlClientRepository {
 
 
-    protected final Pool client;
-
-    public AbstractVertxSqlClientRepository(Pool client) {
-        this.client = client;
-    }
+    @Inject
+    protected ConnectionHolder holder;
 
 
     protected CompletionStage<?> execute(String sql) {
     protected CompletionStage<?> execute(String sql) {
-        return client.preparedQuery(sql).execute().toCompletionStage();
+        return holder.get().flatMap(conn->conn.preparedQuery(sql).execute()).toCompletionStage();
     }
     }
 
 
     protected <T> CompletionStage<T> executeAndCollectOne(String sql, Tuple tuple, Function<Row, T> mapper) {
     protected <T> CompletionStage<T> executeAndCollectOne(String sql, Tuple tuple, Function<Row, T> mapper) {
-        return client.preparedQuery(sql).execute(tuple).map(rows -> mapper.apply(rows.iterator().next()))
+        return holder.get().flatMap(conn->conn.preparedQuery(sql).execute(tuple).map(rows -> mapper.apply(rows.iterator().next())))
                 .toCompletionStage();
                 .toCompletionStage();
     }
     }
 
 
     protected <T> CompletionStage<List<T>> executeAndCollectList(String sql, Function<Row, T> mapper) {
     protected <T> CompletionStage<List<T>> executeAndCollectList(String sql, Function<Row, T> mapper) {
-        return client.preparedQuery(sql).execute().map(rows -> {
+        return holder.get().flatMap(conn->conn.preparedQuery(sql).execute()).map(rows -> {
             List<T> result = new ArrayList<>(rows.size());
             List<T> result = new ArrayList<>(rows.size());
             for (Row row : rows) {
             for (Row row : rows) {
                 result.add(mapper.apply(row));
                 result.add(mapper.apply(row));
@@ -52,7 +50,7 @@ public class AbstractVertxSqlClientRepository {
     }
     }
 
 
     protected CompletionStage<?> executeBatch(String sql, List<Tuple> data) {
     protected CompletionStage<?> executeBatch(String sql, List<Tuple> data) {
-        return client.preparedQuery(sql).executeBatch(data).toCompletionStage();
+        return holder.get().flatMap(conn->conn.preparedQuery(sql).executeBatch(data)).toCompletionStage();
     }
     }
 
 
 }
 }

+ 140 - 0
frameworks/Java/micronaut/micronaut-vertx-pg-client/src/main/java/benchmark/ConnectionHolder.java

@@ -0,0 +1,140 @@
+package benchmark;
+
+import io.micronaut.context.annotation.Property;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFactory;
+import io.netty.channel.EventLoop;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.ServerChannel;
+import io.netty.channel.socket.DatagramChannel;
+import io.netty.channel.socket.InternetProtocolFamily;
+import io.netty.util.concurrent.FastThreadLocal;
+import io.netty.util.internal.ThreadExecutorMap;
+import io.vertx.core.Future;
+import io.vertx.core.Vertx;
+import io.vertx.core.datagram.DatagramSocketOptions;
+import io.vertx.core.impl.VertxBuilder;
+import io.vertx.core.net.ClientOptionsBase;
+import io.vertx.core.net.NetServerOptions;
+import io.vertx.core.spi.transport.Transport;
+import io.vertx.pgclient.PgConnectOptions;
+import io.vertx.pgclient.PgConnection;
+import io.vertx.sqlclient.PoolOptions;
+import jakarta.inject.Singleton;
+
+import java.net.SocketAddress;
+import java.util.concurrent.ThreadFactory;
+
+@Singleton
+public class ConnectionHolder {
+    private final FastThreadLocal<Future<PgConnection>> conn = new FastThreadLocal<>();
+
+    @Property(name = "datasources.default.url") String url;
+    @Property(name = "datasources.default.username") String user;
+    @Property(name = "datasources.default.password") String password;
+    @Property(name = "datasources.default.maximum-pool-size") int maxPoolSize;
+
+    public Future<PgConnection> get() {
+        Future<PgConnection> c = conn.get();
+        if (c == null) {
+
+            PgConnectOptions connectOptions = PgConnectOptions.fromUri(url.substring(5))
+                    .setUser(user)
+                    .setPassword(password)
+                    .setCachePreparedStatements(true)
+                    .setTcpNoDelay(true)
+                    .setTcpQuickAck(true)
+                    .setPipeliningLimit(1024);
+            PoolOptions poolOptions = new PoolOptions();
+            poolOptions.setMaxSize(maxPoolSize);
+
+            VertxBuilder builder = new VertxBuilder()
+                    .init();
+
+            EventLoop loop = (EventLoop) ThreadExecutorMap.currentExecutor();
+
+            Vertx vertx = builder
+                    .findTransport(new ExistingTransport(builder.findTransport(), loop))
+                    .vertx();
+
+            c = PgConnection.connect(vertx, connectOptions);
+            conn.set(c);
+        }
+        return c;
+    }
+
+    private record ExistingTransport(Transport transport, EventLoop loop) implements Transport {
+
+        @Override
+            public boolean supportsDomainSockets() {
+                return transport.supportsDomainSockets();
+            }
+
+            @Override
+            public boolean supportFileRegion() {
+                return transport.supportFileRegion();
+            }
+
+            @Override
+            public boolean isAvailable() {
+                return transport.isAvailable();
+            }
+
+            @Override
+            public Throwable unavailabilityCause() {
+                return transport.unavailabilityCause();
+            }
+
+            @Override
+            public SocketAddress convert(io.vertx.core.net.SocketAddress address) {
+                return transport.convert(address);
+            }
+
+            @Override
+            public io.vertx.core.net.SocketAddress convert(SocketAddress address) {
+                return transport.convert(address);
+            }
+
+            @Override
+            public EventLoopGroup eventLoopGroup(int type, int nThreads, ThreadFactory threadFactory, int ignoredIoRatio) {
+                return loop;
+            }
+
+            @Override
+            public DatagramChannel datagramChannel() {
+                return transport.datagramChannel();
+            }
+
+            @Override
+            public DatagramChannel datagramChannel(InternetProtocolFamily family) {
+                return transport.datagramChannel(family);
+            }
+
+            @Override
+            public ChannelFactory<? extends Channel> channelFactory(boolean domainSocket) {
+                return transport.channelFactory(domainSocket);
+            }
+
+            @Override
+            public ChannelFactory<? extends ServerChannel> serverChannelFactory(boolean domainSocket) {
+                return transport.serverChannelFactory(domainSocket);
+            }
+
+            @Override
+            public void configure(DatagramChannel channel, DatagramSocketOptions options) {
+                transport.configure(channel, options);
+            }
+
+            @Override
+            public void configure(ClientOptionsBase options, boolean domainSocket, Bootstrap bootstrap) {
+                transport.configure(options, domainSocket, bootstrap);
+            }
+
+            @Override
+            public void configure(NetServerOptions options, boolean domainSocket, ServerBootstrap bootstrap) {
+                transport.configure(options, domainSocket, bootstrap);
+            }
+        }
+}

+ 0 - 58
frameworks/Java/micronaut/micronaut-vertx-pg-client/src/main/java/benchmark/PgClientFactory.java

@@ -1,58 +0,0 @@
-/*
- * Copyright 2017-2020 original authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package benchmark;
-
-import io.micronaut.context.annotation.Bean;
-import io.micronaut.context.annotation.Factory;
-import io.micronaut.context.annotation.Property;
-import io.vertx.core.Vertx;
-import io.vertx.core.VertxOptions;
-import io.vertx.core.impl.VertxBuilder;
-import io.vertx.pgclient.PgConnectOptions;
-import io.vertx.pgclient.PgPool;
-import io.vertx.sqlclient.PoolOptions;
-import jakarta.inject.Singleton;
-
-/**
- * The Factory for creating Vertx PG client.
- */
-@Factory
-public class PgClientFactory {
-
-    @Singleton
-    @Bean(preDestroy = "close")
-    public PgPool client(@Property(name = "datasources.default.url") String url,
-                         @Property(name = "datasources.default.username") String user,
-                         @Property(name = "datasources.default.password") String password,
-                         @Property(name = "datasources.default.maximum-pool-size") int maxPoolSize) {
-
-        VertxOptions vertxOptions = new VertxOptions()
-                .setPreferNativeTransport(true);
-
-        PgConnectOptions connectOptions = PgConnectOptions.fromUri(url.substring(5))
-                .setUser(user)
-                .setPassword(password)
-                .setCachePreparedStatements(true)
-                .setTcpNoDelay(true)
-                .setTcpQuickAck(true)
-                .setPipeliningLimit(1024);
-        PoolOptions poolOptions = new PoolOptions();
-        poolOptions.setMaxSize(maxPoolSize);
-
-        Vertx vertx = new VertxBuilder(vertxOptions).init().vertx();
-        return PgPool.pool(vertx, connectOptions, poolOptions);
-    }
-}

+ 0 - 5
frameworks/Java/micronaut/micronaut-vertx-pg-client/src/main/java/benchmark/VertxPgFortuneRepository.java

@@ -2,7 +2,6 @@ package benchmark;
 
 
 import benchmark.model.Fortune;
 import benchmark.model.Fortune;
 import benchmark.repository.AsyncFortuneRepository;
 import benchmark.repository.AsyncFortuneRepository;
-import io.vertx.sqlclient.Pool;
 import io.vertx.sqlclient.Tuple;
 import io.vertx.sqlclient.Tuple;
 import jakarta.inject.Singleton;
 import jakarta.inject.Singleton;
 
 
@@ -14,10 +13,6 @@ import java.util.stream.Collectors;
 @Singleton
 @Singleton
 public class VertxPgFortuneRepository extends AbstractVertxSqlClientRepository implements AsyncFortuneRepository {
 public class VertxPgFortuneRepository extends AbstractVertxSqlClientRepository implements AsyncFortuneRepository {
 
 
-    public VertxPgFortuneRepository(Pool client) {
-        super(client);
-    }
-
     private CompletionStage<?> createTable() {
     private CompletionStage<?> createTable() {
         return execute("DROP TABLE IF EXISTS Fortune;")
         return execute("DROP TABLE IF EXISTS Fortune;")
                 .thenCompose(ignore -> execute("CREATE TABLE Fortune (id INTEGER NOT NULL,message VARCHAR(255) NOT NULL);"));
                 .thenCompose(ignore -> execute("CREATE TABLE Fortune (id INTEGER NOT NULL,message VARCHAR(255) NOT NULL);"));

+ 1 - 6
frameworks/Java/micronaut/micronaut-vertx-pg-client/src/main/java/benchmark/VertxPgWorldRepository.java

@@ -2,7 +2,6 @@ package benchmark;
 
 
 import benchmark.model.World;
 import benchmark.model.World;
 import benchmark.repository.AsyncWorldRepository;
 import benchmark.repository.AsyncWorldRepository;
-import io.vertx.sqlclient.Pool;
 import io.vertx.sqlclient.Row;
 import io.vertx.sqlclient.Row;
 import io.vertx.sqlclient.Tuple;
 import io.vertx.sqlclient.Tuple;
 import jakarta.inject.Singleton;
 import jakarta.inject.Singleton;
@@ -19,10 +18,6 @@ public class VertxPgWorldRepository extends AbstractVertxSqlClientRepository imp
 
 
     private static final Function<Row, World> ROW_TO_WORLD_MAPPER = row -> new World(row.getInteger(0), row.getInteger(1));
     private static final Function<Row, World> ROW_TO_WORLD_MAPPER = row -> new World(row.getInteger(0), row.getInteger(1));
 
 
-    public VertxPgWorldRepository(Pool client) {
-        super(client);
-    }
-
     private CompletionStage<?> createTable() {
     private CompletionStage<?> createTable() {
         return execute("DROP TABLE IF EXISTS World;").thenCompose(ignore -> execute("CREATE TABLE World (id INTEGER NOT NULL,randomNumber INTEGER NOT NULL);"));
         return execute("DROP TABLE IF EXISTS World;").thenCompose(ignore -> execute("CREATE TABLE World (id INTEGER NOT NULL,randomNumber INTEGER NOT NULL);"));
     }
     }
@@ -44,7 +39,7 @@ public class VertxPgWorldRepository extends AbstractVertxSqlClientRepository imp
         for (Integer id : ids) {
         for (Integer id : ids) {
             data.add(Tuple.of(id));
             data.add(Tuple.of(id));
         }
         }
-        return client.withConnection(sqlConnection ->
+        return holder.get().flatMap(sqlConnection ->
                 executeMany(sqlConnection, "SELECT * FROM world WHERE id = $1", data, ROW_TO_WORLD_MAPPER))
                 executeMany(sqlConnection, "SELECT * FROM world WHERE id = $1", data, ROW_TO_WORLD_MAPPER))
                 .toCompletionStage();
                 .toCompletionStage();
     }
     }

+ 2 - 2
frameworks/Java/micronaut/micronaut.dockerfile

@@ -1,9 +1,9 @@
-FROM gradle:8.7.0-jdk17 as build
+FROM gradle:8.7.0-jdk21 as build
 COPY --chown=gradle:gradle . /home/gradle/src
 COPY --chown=gradle:gradle . /home/gradle/src
 WORKDIR /home/gradle/src
 WORKDIR /home/gradle/src
 RUN gradle micronaut-vertx-pg-client:build -x test --no-daemon
 RUN gradle micronaut-vertx-pg-client:build -x test --no-daemon
 
 
-FROM openjdk:21
+FROM openjdk:22
 WORKDIR /micronaut
 WORKDIR /micronaut
 COPY --from=build /home/gradle/src/micronaut-vertx-pg-client/build/libs/micronaut-vertx-pg-client-all.jar micronaut.jar
 COPY --from=build /home/gradle/src/micronaut-vertx-pg-client/build/libs/micronaut-vertx-pg-client-all.jar micronaut.jar
 COPY run_benchmark.sh run_benchmark.sh
 COPY run_benchmark.sh run_benchmark.sh

+ 0 - 1
frameworks/Java/micronaut/run_benchmark.sh

@@ -3,7 +3,6 @@
 JAVA_OPTIONS="-server \
 JAVA_OPTIONS="-server \
   -XX:+UseParallelGC \
   -XX:+UseParallelGC \
   -XX:+UseNUMA \
   -XX:+UseNUMA \
-  -XX:+UseStringDeduplication \
   -XX:-StackTraceInThrowable \
   -XX:-StackTraceInThrowable \
   -Dio.netty.buffer.checkBounds=false \
   -Dio.netty.buffer.checkBounds=false \
   -Dio.netty.buffer.checkAccessible=false \
   -Dio.netty.buffer.checkAccessible=false \