diff --git a/frameworks/Java/inverno/README.md b/frameworks/Java/inverno/README.md
index b205ef706b6..492cd60e430 100755
--- a/frameworks/Java/inverno/README.md
+++ b/frameworks/Java/inverno/README.md
@@ -2,20 +2,23 @@
### Test Type Implementation Source Code
-* [JSON](src/main/java/com/techempower/inverno/benchmark/internal/Handler.java)
-* [PLAINTEXT](src/main/java/com/techempower/inverno/benchmark/internal/Handler.java)
-* [DB](src/main/java/com/techempower/inverno/benchmark/internal/Handler.java)
-* [QUERY](src/main/java/com/techempower/inverno/benchmark/internal/Handler.java)
-* [CACHED QUERY](src/main/java/com/techempower/inverno/benchmark/internal/Handler.java)
-* [UPDATE](src/main/java/com/techempower/inverno/benchmark/internal/Handler.java)
-* [FORTUNES](src/main/java/com/techempower/inverno/benchmark/internal/Handler.java)
+* [JSON](src/main/java/com/techempower/inverno/benchmark/internal/Controller.java)
+* [PLAINTEXT](src/main/java/com/techempower/inverno/benchmark/internal/Controller.java)
+* [DB](src/main/java/com/techempower/inverno/benchmark/internal/Controller.java)
+* [QUERY](src/main/java/com/techempower/inverno/benchmark/internal/Controller.java)
+* [CACHED QUERY](src/main/java/com/techempower/inverno/benchmark/internal/Controller.java)
+* [UPDATE](src/main/java/com/techempower/inverno/benchmark/internal/Controller.java)
+* [FORTUNES](src/main/java/com/techempower/inverno/benchmark/internal/Controller.java)
## Important Libraries
+
The tests were run with:
-* [Java OpenJDK 16](https://openjdk.java.net/)
-* [Inverno 1.4.1](https://inverno.io)
+* [Java OpenJDK 21](https://openjdk.java.net/)
+* [Inverno 1.12.0](https://inverno.io)
+* [DSL-JSON 2.0.2](https://github.com/ngs-doo/dsl-json)
## Test URLs
+
### JSON
http://localhost:8080/json
diff --git a/frameworks/Java/inverno/inverno-postgres.dockerfile b/frameworks/Java/inverno/inverno-postgres.dockerfile
index 737cb434013..d1d333ef5a5 100644
--- a/frameworks/Java/inverno/inverno-postgres.dockerfile
+++ b/frameworks/Java/inverno/inverno-postgres.dockerfile
@@ -1,8 +1,7 @@
-FROM maven:3.9.6-amazoncorretto-21 as maven
+FROM maven:3.9.9-eclipse-temurin-21 as maven
WORKDIR /inverno
COPY src src
COPY pom.xml pom.xml
-RUN yum -y install binutils
RUN mvn package -q -Pio.inverno.io_uring
EXPOSE 8080
diff --git a/frameworks/Java/inverno/inverno.dockerfile b/frameworks/Java/inverno/inverno.dockerfile
index 1dd39d53b27..5f666a70a95 100644
--- a/frameworks/Java/inverno/inverno.dockerfile
+++ b/frameworks/Java/inverno/inverno.dockerfile
@@ -1,8 +1,7 @@
-FROM maven:3.9.6-amazoncorretto-21 as maven
+FROM maven:3.9.9-eclipse-temurin-21 as maven
WORKDIR /inverno
COPY src src
COPY pom.xml pom.xml
-RUN yum -y install binutils
RUN mvn package -q -Pio.inverno.io_uring
EXPOSE 8080
diff --git a/frameworks/Java/inverno/pom.xml b/frameworks/Java/inverno/pom.xml
index c91b31c2d37..3b11a165f97 100644
--- a/frameworks/Java/inverno/pom.xml
+++ b/frameworks/Java/inverno/pom.xml
@@ -6,7 +6,7 @@
io.inverno.dist
inverno-parent
- 1.10.0
+ 1.12.0
com.techempower
inverno-benchmark
@@ -53,6 +53,11 @@
unbescape
1.1.6.RELEASE
+
+ com.dslplatform
+ dsl-json
+ 2.0.2
+
io.vertx
vertx-pg-client
@@ -82,7 +87,52 @@
log4j-core
-
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ default-compile
+
+
+
+ com.dslplatform
+ dsl-json
+ 2.0.2
+
+
+
+
+
+
+
+
+
+
+
+ io.inverno.tool
+ inverno-maven-plugin
+
+
+
+ com.dslplatform.dsl.json
+
+
+
+
+
+ -Dlog4j2.simplelogLevel=INFO -Dlog4j2.level=INFO --add-reads com.techempower.inverno.benchmark=com.dslplatform.dsl.json --add-opens com.techempower.inverno.benchmark/com.techempower.inverno.benchmark.model=com.dslplatform.dsl.json
+
+
+
+
+
+
io.inverno.epoll
@@ -110,7 +160,7 @@
inverno-benchmark
- -Xms2g -Xmx2g -server -XX:+UseNUMA -XX:+UseParallelGC -Dlog4j2.level=OFF -Dio.netty.leakDetection.level=disabled -Dio.netty.buffer.checkBounds=false -Dio.netty.buffer.checkAccessible=false -Dvertx.disableHttpHeadersValidation=true -Dvertx.disableMetrics=true -Dvertx.disableH2c=true -Dvertx.disableWebsockets=true -Dvertx.flashPolicyHandler=false -Dvertx.threadChecks=false -Dvertx.disableContextTimings=true -Dvertx.disableTCCL=true --add-modules io.netty.transport.unix.common,io.netty.transport.classes.epoll,io.netty.transport.epoll.linux.x86_64
+ -Xms2g -Xmx2g -server -XX:+UseNUMA -XX:+UseParallelGC -Dlog4j2.level=OFF -Dio.netty.leakDetection.level=disabled -Dio.netty.buffer.checkBounds=false -Dio.netty.buffer.checkAccessible=false -Dvertx.disableHttpHeadersValidation=true -Dvertx.disableMetrics=true -Dvertx.disableH2c=true -Dvertx.disableWebsockets=true -Dvertx.flashPolicyHandler=false -Dvertx.threadChecks=false -Dvertx.disableContextTimings=true -Dvertx.disableTCCL=true --add-modules io.netty.transport.unix.common,io.netty.transport.classes.epoll,io.netty.transport.epoll.linux.x86_64 --add-reads com.techempower.inverno.benchmark=com.dslplatform.dsl.json --add-opens com.techempower.inverno.benchmark/com.techempower.inverno.benchmark.model=com.dslplatform.dsl.json
@@ -119,9 +169,6 @@
-
- --add-modules io.netty.transport.unix.common,io.netty.transport.classes.epoll,io.netty.transport.epoll.linux.x86_64
-
@@ -157,7 +204,7 @@
inverno-benchmark
- -Xms2g -Xmx2g -server -XX:+UseNUMA -XX:+UseParallelGC -Dlog4j2.level=OFF -Dio.netty.leakDetection.level=disabled -Dio.netty.buffer.checkBounds=false -Dio.netty.buffer.checkAccessible=false -Dvertx.disableHttpHeadersValidation=true -Dvertx.disableMetrics=true -Dvertx.disableH2c=true -Dvertx.disableWebsockets=true -Dvertx.flashPolicyHandler=false -Dvertx.threadChecks=false -Dvertx.disableContextTimings=true -Dvertx.disableTCCL=true --add-modules io.netty.transport.unix.common,io.netty.incubator.transport.classes.io_uring,io.netty.incubator.transport.io_uring.linux.x86_64
+ -Xms2g -Xmx2g -server -XX:+UseNUMA -XX:+UseParallelGC -Dlog4j2.level=OFF -Dio.netty.leakDetection.level=disabled -Dio.netty.buffer.checkBounds=false -Dio.netty.buffer.checkAccessible=false -Dvertx.disableHttpHeadersValidation=true -Dvertx.disableMetrics=true -Dvertx.disableH2c=true -Dvertx.disableWebsockets=true -Dvertx.flashPolicyHandler=false -Dvertx.threadChecks=false -Dvertx.disableContextTimings=true -Dvertx.disableTCCL=true --add-modules io.netty.transport.unix.common,io.netty.incubator.transport.classes.io_uring,io.netty.incubator.transport.io_uring.linux.x86_64 --add-reads com.techempower.inverno.benchmark=com.dslplatform.dsl.json --add-opens com.techempower.inverno.benchmark/com.techempower.inverno.benchmark.model=com.dslplatform.dsl.json
@@ -166,9 +213,6 @@
-
- --add-modules io.netty.transport.unix.common,io.netty.incubator.transport.classes.io_uring,io.netty.incubator.transport.io_uring.linux.x86_64
-
diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/Main.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/Main.java
index 74aaf730a7e..42695fc65e7 100644
--- a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/Main.java
+++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/Main.java
@@ -1,17 +1,16 @@
package com.techempower.inverno.benchmark;
-import java.io.IOException;
-import java.util.function.Supplier;
-
import io.inverno.core.annotation.Bean;
import io.inverno.core.v1.Application;
import io.inverno.mod.configuration.ConfigurationSource;
import io.inverno.mod.configuration.source.BootstrapConfigurationSource;
+import java.io.IOException;
+import java.util.function.Supplier;
public class Main {
@Bean
- public interface AppConfigurationSource extends Supplier> {}
+ public interface AppConfigurationSource extends Supplier {}
public static void main(String[] args) throws IllegalStateException, IOException {
Application.with(new Benchmark.Builder()
diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Controller.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Controller.java
index 33c94a5c7b4..c16e6d4d767 100644
--- a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Controller.java
+++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Controller.java
@@ -1,7 +1,5 @@
package com.techempower.inverno.benchmark.internal;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
import com.techempower.inverno.benchmark.model.Fortune;
import com.techempower.inverno.benchmark.model.Message;
import com.techempower.inverno.benchmark.model.World;
@@ -14,15 +12,14 @@
import io.inverno.mod.base.concurrent.Reactor;
import io.inverno.mod.base.concurrent.ReactorScope;
import io.inverno.mod.base.converter.ConverterException;
+import io.inverno.mod.base.reflect.Types;
import io.inverno.mod.http.base.ExchangeContext;
import io.inverno.mod.http.base.HttpException;
-import io.inverno.mod.http.base.InternalServerErrorException;
import io.inverno.mod.http.base.Parameter;
import io.inverno.mod.http.base.Status;
-import io.inverno.mod.http.server.Exchange;
import io.inverno.mod.http.server.ErrorExchange;
+import io.inverno.mod.http.server.Exchange;
import io.inverno.mod.http.server.ServerController;
-import io.inverno.mod.sql.SqlClient;
import io.inverno.mod.sql.UnsafeSqlOperations;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
@@ -30,13 +27,14 @@
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.util.AsciiString;
+import java.lang.reflect.Type;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
+import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
-import java.util.function.Supplier;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -51,36 +49,32 @@ public class Controller implements ServerController> sqlClient;
+ private final ReactorScope jsonSerializer;
+ private final ReactorScope> dbRepository;
private EventLoopGroup dateEventLoopGroup;
private CharSequence date;
public Controller(Reactor reactor,
- ObjectMapper mapper,
- ReactorScope> sqlClient
+ ReactorScope jsonSerializer,
+ ReactorScope> dbRepository
) {
this.reactor = reactor;
- this.mapper = mapper;
- this.sqlClient = sqlClient;
+ this.jsonSerializer = jsonSerializer;
+ this.dbRepository = dbRepository;
}
@Init
public void init() {
this.dateEventLoopGroup = this.reactor.createIoEventLoopGroup(1);
- this.dateEventLoopGroup.scheduleAtFixedRate(() -> {
- this.date = new AsciiString(DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now()));
- }, 0, 1000, TimeUnit.MILLISECONDS);
-
-
+ this.dateEventLoopGroup.scheduleAtFixedRate(() -> this.date = new AsciiString(DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now())), 0, 1000, TimeUnit.MILLISECONDS);
}
@Destroy
@@ -139,15 +133,8 @@ public void handle(Exchange exchange) throws HttpException {
}
private static final CharSequence STATIC_PLAINTEXT_LEN_VALUE = AsciiString.cached(String.valueOf(STATIC_PLAINTEXT_LEN));
-
- private static class PlaintextSupplier implements Supplier {
- @Override
- public ByteBuf get() {
- return STATIC_PLAINTEXT_BYTEBUF.duplicate();
- }
- }
-
- private static final Mono PLAIN_TEXT_MONO = Mono.fromSupplier(new PlaintextSupplier());
+
+ private static final Mono PLAIN_TEXT_MONO = Mono.fromSupplier(STATIC_PLAINTEXT_BYTEBUF::duplicate);
public void handle_plaintext(Exchange exchange) throws HttpException {
exchange.response()
@@ -163,20 +150,15 @@ public void handle_plaintext(Exchange exchange) throws HttpExce
}
public void handle_json(Exchange exchange) throws HttpException {
- try {
- exchange.response()
- .headers(h -> h
- .add(HttpHeaderNames.SERVER, STATIC_SERVER)
- .add(HttpHeaderNames.DATE, this.date)
- .add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON)
- )
- .body()
- .raw()
- .value(Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(new Message("Hello, World!"))));
- }
- catch (JsonProcessingException | IllegalStateException e) {
- throw new InternalServerErrorException("Error serializing message as JSON", e);
- }
+ exchange.response()
+ .headers(h -> h
+ .add(HttpHeaderNames.SERVER, STATIC_SERVER)
+ .add(HttpHeaderNames.DATE, this.date)
+ .add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON)
+ )
+ .body()
+ .raw()
+ .value(this.jsonSerializer.get().serialize(new Message("Hello, World!"), Message.class));
}
private static int randomWorldId() {
@@ -191,20 +173,10 @@ public void handle_db(Exchange exchange) throws HttpException {
.add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON)
)
.body()
- .raw().stream(this.sqlClient.get().flatMap(client ->
- client.queryForObject(
- DB_SELECT_WORLD,
- row -> {
- try {
- return Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(new World(row.getInteger(0), row.getInteger(1))));
- }
- catch (JsonProcessingException e) {
- throw new InternalServerErrorException(e);
- }
- },
- randomWorldId()
- )
- ));
+ .raw().stream(this.dbRepository.get()
+ .flatMap(repository -> repository.getWorld(randomWorldId()))
+ .map(world -> this.jsonSerializer.get().serialize(world, World.class))
+ );
}
private static final String PARAMETER_QUERIES = "queries";
@@ -227,8 +199,8 @@ public void handle_queries(Exchange exchange) throws HttpExcept
.add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON)
)
.body()
- .raw().stream(this.sqlClient.get()
- .flatMapMany(client -> ((UnsafeSqlOperations)client)
+ .raw().stream(this.dbRepository.get()
+ .flatMapMany(repository -> ((UnsafeSqlOperations)repository.getSqlClient())
.batchQueries(ops ->
Flux.range(0, queries)
.map(ign -> ops.queryForObject(
@@ -239,58 +211,39 @@ public void handle_queries(Exchange exchange) throws HttpExcept
)
)
.collectList()
- .map(worlds -> {
- try {
- return Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(worlds));
- }
- catch (JsonProcessingException e) {
- throw new InternalServerErrorException(e);
- }
- })
+ .map(worlds -> this.jsonSerializer.get().serialize(worlds, LIST_WORLD_TYPE))
);
}
public void handle_updates(Exchange exchange) throws HttpException {
int queries = this.extractQueriesParameter(exchange);
-
exchange.response()
- .headers(h -> h
- .add(HttpHeaderNames.SERVER, STATIC_SERVER)
- .add(HttpHeaderNames.DATE, this.date)
- .add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON)
- )
- .body()
- .raw().stream(this.sqlClient.get()
- .flatMapMany(client -> Flux.from(((UnsafeSqlOperations)client)
- .batchQueries(ops ->
- Flux.range(0, queries)
- .map(ign -> ops.queryForObject(
- DB_SELECT_WORLD,
- row -> new World(row.getInteger(0), randomWorldId()),
- randomWorldId()
- ))
- ))
- .collectSortedList()
- .delayUntil(worlds -> client.batchUpdate(
- DB_UPDATE_WORLD,
- worlds.stream().map(world -> new Object[] { world.getRandomNumber(), world.getId() })
- )
+ .headers(h -> h
+ .add(HttpHeaderNames.SERVER, STATIC_SERVER)
+ .add(HttpHeaderNames.DATE, this.date)
+ .add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON)
+ )
+ .body()
+ .raw().stream(this.dbRepository.get()
+ .flatMapMany(repository -> Flux.from(((UnsafeSqlOperations)repository.getSqlClient())
+ .batchQueries(ops ->
+ Flux.range(0, queries)
+ .map(ign -> ops.queryForObject(
+ DB_SELECT_WORLD,
+ row -> new World(row.getInteger(0), randomWorldId()),
+ randomWorldId()
+ ))
+ ))
+ .collectSortedList()
+ .delayUntil(repository::updateWorlds)
+ .map(worlds -> this.jsonSerializer.get().serialize(worlds, LIST_WORLD_TYPE))
)
- .map(worlds -> {
- try {
- return Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(worlds));
- }
- catch (JsonProcessingException e) {
- throw new InternalServerErrorException(e);
- }
- })
- )
- );
+ );
}
private static final CharSequence MEDIA_TEXT_HTML_UTF8 = AsciiString.cached("text/html; charset=utf-8");
- private static final FortunesTemplate.Renderer> FORTUNES_RENDERER = FortunesTemplate.bytebuf(() -> Unpooled.buffer());
+ private static final FortunesTemplate.Renderer> FORTUNES_RENDERER = FortunesTemplate.bytebuf(Unpooled::buffer);
public void handle_fortunes(Exchange exchange) throws HttpException {
exchange.response()
@@ -300,12 +253,7 @@ public void handle_fortunes(Exchange exchange) throws HttpExcep
.add(HttpHeaderNames.CONTENT_TYPE, MEDIA_TEXT_HTML_UTF8)
)
.body()
- .raw().stream(this.sqlClient.get().flatMapMany(client ->
- client.query(
- DB_SELECT_FORTUNE,
- row -> new Fortune(row.getInteger(0), row.getString(1))
- )
- )
+ .raw().stream(this.dbRepository.get().flatMapMany(DbRepository::listFortunes)
.collectList()
.flatMap(fortunes -> {
fortunes.add(new Fortune(0, "Additional fortune added at request time."));
diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/DbRepository.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/DbRepository.java
new file mode 100644
index 00000000000..28699e3c1d0
--- /dev/null
+++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/DbRepository.java
@@ -0,0 +1,132 @@
+package com.techempower.inverno.benchmark.internal;
+
+import com.techempower.inverno.benchmark.AppConfiguration;
+import com.techempower.inverno.benchmark.model.Fortune;
+import com.techempower.inverno.benchmark.model.World;
+import io.inverno.core.annotation.Bean;
+import io.inverno.core.annotation.Destroy;
+import io.inverno.core.annotation.Init;
+import io.inverno.mod.base.concurrent.Reactor;
+import io.inverno.mod.base.concurrent.VertxReactor;
+import io.inverno.mod.sql.PreparedStatement;
+import io.inverno.mod.sql.SqlClient;
+import io.inverno.mod.sql.vertx.ConnectionSqlClient;
+import io.vertx.core.Vertx;
+import io.vertx.core.VertxOptions;
+import io.vertx.pgclient.PgConnectOptions;
+import io.vertx.pgclient.PgConnection;
+import java.util.ArrayList;
+import java.util.List;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class DbRepository {
+
+ public static final String DB_SELECT_WORLD = "SELECT id, randomnumber from WORLD where id = $1";
+ public static final String DB_SELECT_FORTUNE = "SELECT id, message from FORTUNE";
+
+ private final SqlClient sqlClient;
+
+ private final PreparedStatement selectWorldByIdQuery;
+ private final PreparedStatement selectFortuneQuery;
+ private final PreparedStatement[] updateWorldQueries;
+
+ public DbRepository(SqlClient sqlClient) {
+ this.sqlClient = sqlClient;
+
+ this.selectWorldByIdQuery = sqlClient.preparedStatement(DB_SELECT_WORLD);
+ this.selectFortuneQuery = sqlClient.preparedStatement(DB_SELECT_FORTUNE);
+ this.updateWorldQueries = new PreparedStatement[500];
+ for(int i=0;i getWorld(int id) {
+ return Mono.from(this.selectWorldByIdQuery.bind(id).execute(row -> new World(row.getInteger(0), row.getInteger(1))));
+ }
+
+ public Flux listFortunes() {
+ return Flux.from(this.selectFortuneQuery.execute(row -> new Fortune(row.getInteger(0), row.getString(1))));
+ }
+
+ public Mono updateWorlds(List worlds) {
+ int len = worlds.size();
+ List