From afb6d8bbd25064f4a2773d31e232cd271c72eaef Mon Sep 17 00:00:00 2001 From: Jeremy Kuhn Date: Wed, 18 Aug 2021 22:53:20 +0200 Subject: [PATCH 01/14] Inverno framework test --- frameworks/Java/inverno/README.md | 45 +++ frameworks/Java/inverno/benchmark_config.json | 47 +++ .../Java/inverno/inverno-postgres.dockerfile | 11 + frameworks/Java/inverno/inverno.dockerfile | 9 + frameworks/Java/inverno/pom.xml | 125 +++++++ .../src/jmods/io.vertx.core/module-info.java | 99 ++++++ .../jmods/r2dbc.postgresql/module-info.java | 38 ++ .../inverno/benchmark/AppConfiguration.java | 36 ++ .../techempower/inverno/benchmark/Main.java | 21 ++ .../inverno/benchmark/internal/Handler.java | 325 ++++++++++++++++++ .../internal/PoolSqlClientReactorScope.java | 67 ++++ .../PooledClientSqlClientReactorScope.java | 67 ++++ .../inverno/benchmark/model/Fortune.java | 25 ++ .../inverno/benchmark/model/Message.java | 14 + .../inverno/benchmark/model/World.java | 33 ++ .../benchmark/templates/FortunesTemplate.irt | 30 ++ .../inverno/src/main/java/module-info.java | 28 ++ .../src/main/resources/configuration.cprops | 16 + .../Java/inverno/src/main/resources/db.sql | 25 ++ 19 files changed, 1061 insertions(+) create mode 100755 frameworks/Java/inverno/README.md create mode 100755 frameworks/Java/inverno/benchmark_config.json create mode 100644 frameworks/Java/inverno/inverno-postgres.dockerfile create mode 100644 frameworks/Java/inverno/inverno.dockerfile create mode 100644 frameworks/Java/inverno/pom.xml create mode 100644 frameworks/Java/inverno/src/jmods/io.vertx.core/module-info.java create mode 100644 frameworks/Java/inverno/src/jmods/r2dbc.postgresql/module-info.java create mode 100644 frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/AppConfiguration.java create mode 100644 frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/Main.java create mode 100644 frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java create mode 100644 frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/PoolSqlClientReactorScope.java create mode 100644 frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/PooledClientSqlClientReactorScope.java create mode 100644 frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/Fortune.java create mode 100644 frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/Message.java create mode 100644 frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/World.java create mode 100644 frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/templates/FortunesTemplate.irt create mode 100644 frameworks/Java/inverno/src/main/java/module-info.java create mode 100644 frameworks/Java/inverno/src/main/resources/configuration.cprops create mode 100644 frameworks/Java/inverno/src/main/resources/db.sql diff --git a/frameworks/Java/inverno/README.md b/frameworks/Java/inverno/README.md new file mode 100755 index 00000000000..18f28c8d4ff --- /dev/null +++ b/frameworks/Java/inverno/README.md @@ -0,0 +1,45 @@ +# inverno Benchmarking Test + +### 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) + +## Important Libraries +The tests were run with: +* [Java OpenJDK 16](https://openjdk.java.net/) +* [Inverno 1.2.1](https://inverno.io) + +## Test URLs +### JSON + +http://localhost:8080/json + +### PLAINTEXT + +http://localhost:8080/plaintext + +### DB + +http://localhost:8080/db + +### QUERY + +http://localhost:8080/query?queries= + +### CACHED QUERY + +http://localhost:8080/cached_query?queries= + +### UPDATE + +http://localhost:8080/update?queries= + +### FORTUNES + +http://localhost:8080/fortunes diff --git a/frameworks/Java/inverno/benchmark_config.json b/frameworks/Java/inverno/benchmark_config.json new file mode 100755 index 00000000000..f91b0aac623 --- /dev/null +++ b/frameworks/Java/inverno/benchmark_config.json @@ -0,0 +1,47 @@ +{ + "framework": "inverno", + "tests": [ + { + "default": { + "json_url": "/json", + "plaintext_url": "/plaintext", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "None", + "framework": "inverno", + "language": "Java", + "flavor": "None", + "orm": "Micro", + "platform": "Inverno", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "inverno", + "notes": "", + "versus": "None" + }, + "postgres": { + "db_url": "/db", + "query_url": "/queries?queries=", + "fortune_url": "/fortunes", + "update_url": "/updates?queries=", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "postgres", + "framework": "inverno", + "language": "Java", + "flavor": "None", + "orm": "Micro", + "platform": "Inverno", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "inverno", + "notes": "", + "versus": "None" + } + } + ] +} diff --git a/frameworks/Java/inverno/inverno-postgres.dockerfile b/frameworks/Java/inverno/inverno-postgres.dockerfile new file mode 100644 index 00000000000..5a2928679e3 --- /dev/null +++ b/frameworks/Java/inverno/inverno-postgres.dockerfile @@ -0,0 +1,11 @@ +FROM maven:3.8.2-openjdk-16 as maven +WORKDIR /inverno +COPY src src +COPY pom.xml pom.xml +RUN mvn package -q + +EXPOSE 8080 + +# CMD [ "target/maven-inverno/application_linux_amd64/inverno-benchmark-1.0.0-SNAPSHOT/bin/inverno-benchmark" ] +CMD export DBIP=`getent hosts tfb-database | awk '{ print $1 }'` && \ + target/maven-inverno/application_linux_amd64/inverno-benchmark-1.0.0-SNAPSHOT/bin/inverno-benchmark --com.techempower.inverno.benchmark.appConfiguration.db_host=\"$DBIP\" \ No newline at end of file diff --git a/frameworks/Java/inverno/inverno.dockerfile b/frameworks/Java/inverno/inverno.dockerfile new file mode 100644 index 00000000000..6af60a189dc --- /dev/null +++ b/frameworks/Java/inverno/inverno.dockerfile @@ -0,0 +1,9 @@ +FROM maven:3.8.2-openjdk-16 as maven +WORKDIR /inverno +COPY src src +COPY pom.xml pom.xml +RUN mvn package -q + +EXPOSE 8080 + +CMD [ "target/maven-inverno/application_linux_amd64/inverno-benchmark-1.0.0-SNAPSHOT/bin/inverno-benchmark", "--com.techempower.inverno.benchmark.appConfiguration.boot.reactor_prefer_vertx=false" ] diff --git a/frameworks/Java/inverno/pom.xml b/frameworks/Java/inverno/pom.xml new file mode 100644 index 00000000000..adf81017f13 --- /dev/null +++ b/frameworks/Java/inverno/pom.xml @@ -0,0 +1,125 @@ + + + 4.0.0 + + io.inverno.dist + inverno-parent + 1.2.1 + + com.techempower + inverno-benchmark + 1.0.0-SNAPSHOT + jar + + inverno-benchmark + Inverno framework benchmark test + + + 16 + 16 + 16 + + + + + io.inverno + inverno-core + + + io.inverno.mod + inverno-boot + + + io.inverno.mod + inverno-configuration + + + io.inverno.mod + inverno-http-server + + + io.inverno.mod + inverno-irt + + + io.inverno.mod + inverno-sql-vertx + + + + org.unbescape + unbescape + 1.1.6.RELEASE + + + io.vertx + vertx-pg-client + + + + io.netty + netty-codec-dns + ${version.netty} + + + io.netty + netty-codec-socks + ${version.netty} + + + io.netty + netty-handler-proxy + ${version.netty} + + + io.netty + netty-resolver-dns + ${version.netty} + + + + io.netty + netty-transport-native-epoll + linux-x86_64 + + + + org.apache.logging.log4j + log4j-core + + + + + + + io.inverno.tool + inverno-maven-plugin + + + inverno-package + package + + build-app + + + server + + + inverno-benchmark + -Xms2g -Xmx2g -server -XX:+UseNUMA -XX:+UseParallelGC -Dio.netty.leakDetection.level=disabled -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.epoll + + + + zip + + + + + + + + + + diff --git a/frameworks/Java/inverno/src/jmods/io.vertx.core/module-info.java b/frameworks/Java/inverno/src/jmods/io.vertx.core/module-info.java new file mode 100644 index 00000000000..56fbff0c18c --- /dev/null +++ b/frameworks/Java/inverno/src/jmods/io.vertx.core/module-info.java @@ -0,0 +1,99 @@ +module io.vertx.core { + requires io.netty.handler.proxy; + requires io.netty.resolver.dns; + requires io.netty.transport.epoll; + requires io.netty.transport.unix.common; + requires java.naming; + requires org.apache.logging.log4j; + + requires transitive com.fasterxml.jackson.core; + requires transitive com.fasterxml.jackson.databind; + requires transitive io.netty.buffer; + requires transitive io.netty.codec; + requires transitive io.netty.codec.dns; + requires transitive io.netty.codec.http; + requires transitive io.netty.codec.http2; + requires transitive io.netty.common; + requires transitive io.netty.handler; + requires transitive io.netty.resolver; + requires transitive io.netty.transport; + requires transitive java.compiler; + requires transitive java.logging; + + exports io.vertx.core; + exports io.vertx.core.buffer; + exports io.vertx.core.buffer.impl; + exports io.vertx.core.cli; + exports io.vertx.core.cli.annotations; + exports io.vertx.core.cli.converters; + exports io.vertx.core.cli.impl; + exports io.vertx.core.datagram; + exports io.vertx.core.datagram.impl; + exports io.vertx.core.dns; + exports io.vertx.core.dns.impl; + exports io.vertx.core.dns.impl.decoder; + exports io.vertx.core.eventbus; + exports io.vertx.core.eventbus.impl; + exports io.vertx.core.eventbus.impl.clustered; + exports io.vertx.core.eventbus.impl.codecs; + exports io.vertx.core.file; + exports io.vertx.core.file.impl; + exports io.vertx.core.http; + exports io.vertx.core.http.impl; + exports io.vertx.core.http.impl.cgbystrom; + exports io.vertx.core.http.impl.headers; + exports io.vertx.core.http.impl.ws; + exports io.vertx.core.impl; + exports io.vertx.core.impl.cpu; + exports io.vertx.core.impl.future; + exports io.vertx.core.impl.launcher; + exports io.vertx.core.impl.launcher.commands; + exports io.vertx.core.impl.logging; + exports io.vertx.core.impl.resolver; + exports io.vertx.core.impl.utils; + exports io.vertx.core.impl.verticle; + exports io.vertx.core.json; + exports io.vertx.core.json.impl; + exports io.vertx.core.json.jackson; + exports io.vertx.core.json.pointer; + exports io.vertx.core.json.pointer.impl; + exports io.vertx.core.logging; + exports io.vertx.core.metrics; + exports io.vertx.core.metrics.impl; + exports io.vertx.core.net; + exports io.vertx.core.net.impl; + exports io.vertx.core.net.impl.pkcs1; + exports io.vertx.core.net.impl.pool; + exports io.vertx.core.net.impl.transport; + exports io.vertx.core.parsetools; + exports io.vertx.core.parsetools.impl; + exports io.vertx.core.shareddata; + exports io.vertx.core.shareddata.impl; + exports io.vertx.core.spi; + exports io.vertx.core.spi.cluster; + exports io.vertx.core.spi.cluster.impl; + exports io.vertx.core.spi.cluster.impl.selector; + exports io.vertx.core.spi.json; + exports io.vertx.core.spi.launcher; + exports io.vertx.core.spi.logging; + exports io.vertx.core.spi.metrics; + exports io.vertx.core.spi.observability; + exports io.vertx.core.spi.resolver; + exports io.vertx.core.spi.tracing; + exports io.vertx.core.streams; + exports io.vertx.core.streams.impl; + exports io.vertx.core.tracing; + + provides io.vertx.core.spi.launcher.CommandFactory with + io.vertx.core.impl.launcher.commands.RunCommandFactory, + io.vertx.core.impl.launcher.commands.VersionCommandFactory, + io.vertx.core.impl.launcher.commands.BareCommandFactory, + io.vertx.core.impl.launcher.commands.ListCommandFactory, + io.vertx.core.impl.launcher.commands.StartCommandFactory, + io.vertx.core.impl.launcher.commands.StopCommandFactory; + + uses io.vertx.core.spi.VertxServiceProvider; + uses io.vertx.core.spi.VerticleFactory; + uses io.vertx.core.spi.JsonFactory; + +} diff --git a/frameworks/Java/inverno/src/jmods/r2dbc.postgresql/module-info.java b/frameworks/Java/inverno/src/jmods/r2dbc.postgresql/module-info.java new file mode 100644 index 00000000000..18208e43b04 --- /dev/null +++ b/frameworks/Java/inverno/src/jmods/r2dbc.postgresql/module-info.java @@ -0,0 +1,38 @@ +module r2dbc.postgresql { + requires com.ongres.scram.client; + requires com.ongres.scram.common; + requires io.netty.codec; + requires io.netty.resolver; + requires io.netty.transport; + requires io.netty.transport.epoll; + requires io.netty.transport.unix.common; + requires java.naming; + + requires transitive io.netty.buffer; + requires transitive io.netty.common; + requires transitive io.netty.handler; + requires transitive org.reactivestreams; + requires transitive r2dbc.spi; + requires transitive reactor.core; + requires transitive reactor.netty.core; + + exports io.r2dbc.postgresql; + exports io.r2dbc.postgresql.api; + exports io.r2dbc.postgresql.authentication; + exports io.r2dbc.postgresql.client; + exports io.r2dbc.postgresql.codec; + exports io.r2dbc.postgresql.extension; + exports io.r2dbc.postgresql.message; + exports io.r2dbc.postgresql.message.backend; + exports io.r2dbc.postgresql.message.frontend; + exports io.r2dbc.postgresql.replication; + exports io.r2dbc.postgresql.util; + + provides io.r2dbc.postgresql.extension.Extension with + io.r2dbc.postgresql.codec.BuiltinDynamicCodecs; + provides io.r2dbc.spi.ConnectionFactoryProvider with + io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider; + + uses io.r2dbc.postgresql.extension.Extension; + +} diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/AppConfiguration.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/AppConfiguration.java new file mode 100644 index 00000000000..542733af9be --- /dev/null +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/AppConfiguration.java @@ -0,0 +1,36 @@ +package com.techempower.inverno.benchmark; + +import io.inverno.core.annotation.NestedBean; +import io.inverno.mod.boot.BootConfiguration; +import io.inverno.mod.configuration.Configuration; +import io.inverno.mod.http.server.HttpServerConfiguration; + +@Configuration +public interface AppConfiguration { + + @NestedBean + BootConfiguration boot(); + + @NestedBean + HttpServerConfiguration http_server(); + + default String db_database() { + return "postgres"; + } + + default String db_host() { + return "localhost"; + } + + default int db_port() { + return 5432; + } + + default String db_username() { + return "postgres"; + } + + default String db_password() { + return "password"; + } +} 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 new file mode 100644 index 00000000000..74aaf730a7e --- /dev/null +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/Main.java @@ -0,0 +1,21 @@ +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; + +public class Main { + + @Bean + public interface AppConfigurationSource extends Supplier> {} + + public static void main(String[] args) throws IllegalStateException, IOException { + Application.with(new Benchmark.Builder() + .setAppConfigurationSource(new BootstrapConfigurationSource(Main.class.getModule(), args)) + ).run(); + } +} diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java new file mode 100644 index 00000000000..40f50082b0d --- /dev/null +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java @@ -0,0 +1,325 @@ +package com.techempower.inverno.benchmark.internal; + +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Collections; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +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; +import com.techempower.inverno.benchmark.templates.FortunesTemplate; + +import io.inverno.core.annotation.Bean; +import io.inverno.core.annotation.Bean.Visibility; +import io.inverno.core.annotation.Destroy; +import io.inverno.core.annotation.Init; +import io.inverno.mod.base.Charsets; +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.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.ExchangeHandler; +import io.inverno.mod.sql.SqlClient; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.EventLoopGroup; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaderValues; +import io.netty.util.AsciiString; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Bean( visibility = Visibility.PRIVATE ) +public class Handler implements ExchangeHandler { + + private static final String PATH_PLAINTEXT = "/plaintext"; + private static final String PATH_JSON = "/json"; + private static final String PATH_DB = "/db"; + private static final String PATH_QUERIES = "/queries"; + private static final String PATH_UPDATES = "/updates"; + private static final String PATH_FORTUNES = "/fortunes"; + + private static final CharSequence STATIC_SERVER = AsciiString.cached("inverno"); + + private final Reactor reactor; + private final ObjectMapper mapper; + private final ReactorScope pooledClientSqlClient; + private final ReactorScope poolSqlClient; + + private EventLoopGroup dateEventLoopGroup; + + private CharSequence date; + + public Handler(Reactor reactor, + ObjectMapper mapper, + ReactorScope pooledClientSqlClient, + ReactorScope poolSqlClient + ) { + this.reactor = reactor; + this.mapper = mapper; + this.pooledClientSqlClient = pooledClientSqlClient; + this.poolSqlClient = poolSqlClient; + } + + @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); + } + + @Destroy + public void destroy() { + this.dateEventLoopGroup.shutdownGracefully(); + } + + @Override + public void handle(Exchange exchange) throws HttpException { + switch(exchange.request().getPath()) { + case PATH_PLAINTEXT: { + this.handle_plaintext(exchange); + break; + } + case PATH_JSON: { + this.handle_json(exchange); + break; + } + case PATH_DB: { + this.handle_db(exchange); + break; + } + case PATH_FORTUNES: { + this.handle_fortunes(exchange); + break; + } + default: { + switch(exchange.request().getPathAbsolute()) { + case PATH_QUERIES: { + this.handle_queries(exchange); + break; + } + case PATH_UPDATES: { + this.handle_updates(exchange); + break; + } + default: { + exchange.response() + .headers(h -> h.status(Status.NOT_FOUND)) + .body() + .empty(); + } + } + } + } + } + + private static final byte[] STATIC_PLAINTEXT = "Hello, World!".getBytes(Charsets.UTF_8); + private static final int STATIC_PLAINTEXT_LEN = STATIC_PLAINTEXT.length; + + private static final ByteBuf STATIC_PLAINTEXT_BYTEBUF; + static { + ByteBuf tmpBuf = Unpooled.directBuffer(STATIC_PLAINTEXT_LEN); + tmpBuf.writeBytes(STATIC_PLAINTEXT); + STATIC_PLAINTEXT_BYTEBUF = Unpooled.unreleasableBuffer(tmpBuf); + } + + 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()); + + public void handle_plaintext(Exchange exchange) throws HttpException { + exchange.response() + .headers(h -> h + .add(HttpHeaderNames.SERVER, STATIC_SERVER) + .add(HttpHeaderNames.DATE, date) + .add(HttpHeaderNames.CONTENT_LENGTH, STATIC_PLAINTEXT_LEN_VALUE) + .add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN) + ) + .body() + .raw() + .stream(PLAIN_TEXT_MONO); + } + + 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.unreleasableBuffer(Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(new Message("Hello, World!"))))); + } + catch (JsonProcessingException | IllegalStateException e) { + throw new InternalServerErrorException("Error serializing message as JSON", e); + } + } + + private static final String DB_SELECT_WORLD = "SELECT id, randomnumber from WORLD where id = $1"; + + private static int randomWorldId() { + return 1 + ThreadLocalRandom.current().nextInt(10000); + } + + public void handle_db(Exchange exchange) throws HttpException { + 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.pooledClientSqlClient.get().queryForObject( + DB_SELECT_WORLD, + row -> { + try { + return Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(new World(row.getInteger(0), row.getInteger(1))))); + } + catch (JsonProcessingException e) { + throw new InternalServerErrorException(e); + } + }, + randomWorldId() + ) + ); + } + + private static final String PARAMETER_QUERIES = "queries"; + + private int extractQueriesParameter(Exchange exchange) { + try { + return Math.min(500, Math.max(1, exchange.request().queryParameters().get(PARAMETER_QUERIES).map(Parameter::asInteger).orElse(1))); + } + catch (ConverterException e) { // TODO + return 1; + } + } + + public void handle_queries(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(Flux.range(0, queries) + .flatMap(ign -> this.pooledClientSqlClient.get().queryForObject( + DB_SELECT_WORLD, + row -> new World(row.getInteger(0), row.getInteger(1)), + randomWorldId() + )) + .collectList() + .map(worlds -> { + try { + return Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(worlds))); + } + catch (JsonProcessingException e) { + throw new InternalServerErrorException(e); + } + }) + ); + } + + private static final String DB_UPDATE_WORLD = "UPDATE world SET randomnumber=$1 WHERE id=$2"; + + public void handle_updates(Exchange exchange) throws HttpException { + int queries = this.extractQueriesParameter(exchange); + + this.poolSqlClient.get().connection(ops -> Flux.range(0, queries) + .flatMap(ign -> ops.queryForObject( + DB_SELECT_WORLD, + row -> new World(row.getInteger(0), randomWorldId()), + randomWorldId() + ) + ) + .collectSortedList() + .delayUntil(worlds -> ops.batchUpdate( + DB_UPDATE_WORLD, + worlds.stream().map(world -> new Object[] { world.getRandomNumber(), world.getId() }) + ) + ) + .map(worlds -> { + try { + return Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(worlds))); + } + catch (JsonProcessingException e) { + throw new InternalServerErrorException(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().stream(this.poolSqlClient.get().connection(ops -> Flux.range(0, queries) + .flatMap(ign -> ops.queryForObject( + DB_SELECT_WORLD, + row -> new World(row.getInteger(0), randomWorldId()), + randomWorldId() + ) + ) + .collectSortedList() + .delayUntil(worlds -> ops.batchUpdate( + DB_UPDATE_WORLD, + worlds.stream().map(world -> new Object[] { world.getRandomNumber(), world.getId() }) + ) + ) + .map(worlds -> { + try { + return Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(worlds))); + } + catch (JsonProcessingException e) { + throw new InternalServerErrorException(e); + } + })) + ); + } + + private static final String DB_SELECT_FORTUNE = "SELECT id, message from FORTUNE"; + private static final CharSequence MEDIA_TEXT_HTML_UTF8 = AsciiString.cached("text/html; charset=utf-8"); + + public void handle_fortunes(Exchange exchange) throws HttpException { + exchange.response() + .headers(h -> h + .add(HttpHeaderNames.SERVER, STATIC_SERVER) + .add(HttpHeaderNames.DATE, this.date) + .add(HttpHeaderNames.CONTENT_TYPE, MEDIA_TEXT_HTML_UTF8) + ) + .body() + .raw().stream(Flux.from(this.pooledClientSqlClient.get().query( + DB_SELECT_FORTUNE, + row -> new Fortune(row.getInteger(0), row.getString(1)) + )) + .collectList() + .flatMap(fortunes -> { + fortunes.add(new Fortune(0, "Additional fortune added at request time.")); + Collections.sort(fortunes); + return Mono.fromFuture(() -> FortunesTemplate.bytebuf(() -> Unpooled.unreleasableBuffer(Unpooled.buffer())).render(fortunes)); + }) + ); + } +} diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/PoolSqlClientReactorScope.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/PoolSqlClientReactorScope.java new file mode 100644 index 00000000000..76eed32483b --- /dev/null +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/PoolSqlClientReactorScope.java @@ -0,0 +1,67 @@ +package com.techempower.inverno.benchmark.internal; + +import com.techempower.inverno.benchmark.AppConfiguration; + +import io.inverno.core.annotation.Bean; +import io.inverno.core.annotation.Destroy; +import io.inverno.core.annotation.Init; +import io.inverno.core.annotation.Bean.Visibility; +import io.inverno.mod.base.concurrent.Reactor; +import io.inverno.mod.base.concurrent.ReactorScope; +import io.inverno.mod.base.concurrent.VertxReactor; +import io.inverno.mod.sql.SqlClient; +import io.inverno.mod.sql.vertx.PoolSqlClient; +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; +import io.vertx.pgclient.PgConnectOptions; +import io.vertx.pgclient.PgPool; +import io.vertx.sqlclient.PoolOptions; + +@Bean( name = "poolSqlClient", visibility = Visibility.PRIVATE ) +public class PoolSqlClientReactorScope extends ReactorScope { + + private final AppConfiguration configuration; + private final Reactor reactor; + + private Vertx vertx; + private PgConnectOptions connectOptions; + private PoolOptions poolOptions; + + public PoolSqlClientReactorScope(AppConfiguration configuration, Reactor reactor) { + this.configuration = configuration; + this.reactor = reactor; + } + + @Init + public void init() { + if(this.reactor instanceof VertxReactor) { + this.vertx = ((VertxReactor)this.reactor).getVertx(); + } + else { + this.vertx = Vertx.vertx(new VertxOptions().setPreferNativeTransport(this.configuration.boot().prefer_native_transport())); + } + + this.connectOptions = new PgConnectOptions() + .setHost(this.configuration.db_host()) + .setPort(this.configuration.db_port()) + .setDatabase(this.configuration.db_database()) + .setUser(this.configuration.db_username()) + .setPassword(this.configuration.db_password()) + .setCachePreparedStatements(true); + + this.poolOptions = new PoolOptions().setMaxSize(4); + } + + @Destroy + public void destroy() { + if(!(this.reactor instanceof VertxReactor)) { + this.vertx.close(); + } + } + + @Override + protected SqlClient create() { + return new PoolSqlClient(PgPool.pool(this.vertx, this.connectOptions, this.poolOptions)); + } + +} diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/PooledClientSqlClientReactorScope.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/PooledClientSqlClientReactorScope.java new file mode 100644 index 00000000000..06516866c47 --- /dev/null +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/PooledClientSqlClientReactorScope.java @@ -0,0 +1,67 @@ +package com.techempower.inverno.benchmark.internal; + +import com.techempower.inverno.benchmark.AppConfiguration; + +import io.inverno.core.annotation.Bean; +import io.inverno.core.annotation.Bean.Visibility; +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.ReactorScope; +import io.inverno.mod.base.concurrent.VertxReactor; +import io.inverno.mod.sql.SqlClient; +import io.inverno.mod.sql.vertx.PooledClientSqlClient; +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; +import io.vertx.pgclient.PgConnectOptions; +import io.vertx.pgclient.PgPool; +import io.vertx.sqlclient.PoolOptions; + +@Bean( name = "pooledClientSqlClient", visibility = Visibility.PRIVATE ) +public class PooledClientSqlClientReactorScope extends ReactorScope { + + private final AppConfiguration configuration; + private final Reactor reactor; + + private Vertx vertx; + private PgConnectOptions connectOptions; + private PoolOptions poolOptions; + + public PooledClientSqlClientReactorScope(AppConfiguration configuration, Reactor reactor) { + this.configuration = configuration; + this.reactor = reactor; + } + + @Init + public void init() { + if(this.reactor instanceof VertxReactor) { + this.vertx = ((VertxReactor)this.reactor).getVertx(); + } + else { + this.vertx = Vertx.vertx(new VertxOptions().setPreferNativeTransport(this.configuration.boot().prefer_native_transport())); + } + + this.connectOptions = new PgConnectOptions() + .setHost(this.configuration.db_host()) + .setPort(this.configuration.db_port()) + .setDatabase(this.configuration.db_database()) + .setUser(this.configuration.db_username()) + .setPassword(this.configuration.db_password()) + .setCachePreparedStatements(true); + + this.poolOptions = new PoolOptions().setMaxSize(1); + } + + @Destroy + public void destroy() { + if(!(this.reactor instanceof VertxReactor)) { + this.vertx.close(); + } + } + + @Override + protected SqlClient create() { + return new PooledClientSqlClient(PgPool.client(this.vertx, this.connectOptions, this.poolOptions)); + } + +} diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/Fortune.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/Fortune.java new file mode 100644 index 00000000000..de4017c3c0d --- /dev/null +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/Fortune.java @@ -0,0 +1,25 @@ +package com.techempower.inverno.benchmark.model; + +public final class Fortune implements Comparable { + + private final int id; + private final String message; + + public Fortune(int id, String message) { + this.id = id; + this.message = message; + } + + public int getId() { + return this.id; + } + + public String getMessage() { + return this.message; + } + + @Override + public int compareTo(Fortune other) { + return getMessage().compareTo(other.getMessage()); + } +} diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/Message.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/Message.java new file mode 100644 index 00000000000..c9d554903d7 --- /dev/null +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/Message.java @@ -0,0 +1,14 @@ +package com.techempower.inverno.benchmark.model; + +public final class Message { + + private final String message; + + public Message(String message) { + this.message = message; + } + + public String getMessage() { + return this.message; + } +} \ No newline at end of file diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/World.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/World.java new file mode 100644 index 00000000000..f0f1825f3b9 --- /dev/null +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/World.java @@ -0,0 +1,33 @@ +package com.techempower.inverno.benchmark.model; + +public final class World implements Comparable { + + private final int id; + private int randomNumber; + + public World(int id) { + this.id = id; + } + + public World(int id, int randomNumber) { + this.id = id; + this.randomNumber = randomNumber; + } + + public int getId() { + return this.id; + } + + public int getRandomNumber() { + return this.randomNumber; + } + + public void setRandomNumber(int randomNumber) { + this.randomNumber = randomNumber; + } + + @Override + public int compareTo(World o) { + return Integer.compare(id, o.id); + } +} \ No newline at end of file diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/templates/FortunesTemplate.irt b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/templates/FortunesTemplate.irt new file mode 100644 index 00000000000..e0330edbb1c --- /dev/null +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/templates/FortunesTemplate.irt @@ -0,0 +1,30 @@ +package com.techempower.inverno.benchmark.templates; + +import com.techempower.inverno.benchmark.model.Fortune; + +import java.util.List; +import org.unbescape.html.HtmlEscape; + +option modes = {"STRING", "BYTEBUF", "STREAM", "PUBLISHER_STRING", "PUBLISHER_BYTEBUF"}; +option charset = "utf-8"; + +(List fortunes) -> { + + +Fortunes + + + + + + {fortunes} +
idmessage
+ + +} + +(Fortune fortune) -> { + + {@fortune.id} + {@fortune.message|((String s) -> HtmlEscape.escapeHtml5Xml(s))} + } diff --git a/frameworks/Java/inverno/src/main/java/module-info.java b/frameworks/Java/inverno/src/main/java/module-info.java new file mode 100644 index 00000000000..8370bb11766 --- /dev/null +++ b/frameworks/Java/inverno/src/main/java/module-info.java @@ -0,0 +1,28 @@ +@io.inverno.core.annotation.Module( excludes = { "io.inverno.mod.sql.vertx" } ) + +@io.inverno.core.annotation.Wire(beans="pooledClientSqlClient", into="handler:pooledClientSqlClient") +@io.inverno.core.annotation.Wire(beans="poolSqlClient", into="handler:poolSqlClient") +module com.techempower.inverno.benchmark { + requires io.inverno.mod.boot; + requires io.inverno.mod.http.server; + requires io.inverno.mod.irt; + + requires io.inverno.mod.sql; + requires io.inverno.mod.sql.vertx; + + requires io.netty.common; + requires io.netty.codec.http; + requires unbescape; + + requires io.vertx.client.sql.pg; + requires io.vertx.client.sql; + requires io.vertx.core; + requires java.sql; + + requires transitive io.netty.transport; + requires static io.netty.transport.unix.common; + requires static io.netty.transport.epoll; + + exports com.techempower.inverno.benchmark; + exports com.techempower.inverno.benchmark.model; +} diff --git a/frameworks/Java/inverno/src/main/resources/configuration.cprops b/frameworks/Java/inverno/src/main/resources/configuration.cprops new file mode 100644 index 00000000000..df4680e124b --- /dev/null +++ b/frameworks/Java/inverno/src/main/resources/configuration.cprops @@ -0,0 +1,16 @@ +com.techempower.inverno.benchmark.appConfiguration { + boot { + reactor_prefer_vertx = true + accept_backlog = null + } + http_server { + tls_enabled = false + server_port = 8080 + h2c_enabled = false + } + db_database = "hello_world" + db_host = "tfb-database" + db_port = 5432 + db_username = "benchmarkdbuser" + db_password = "benchmarkdbpass" +} diff --git a/frameworks/Java/inverno/src/main/resources/db.sql b/frameworks/Java/inverno/src/main/resources/db.sql new file mode 100644 index 00000000000..2fd0de35be5 --- /dev/null +++ b/frameworks/Java/inverno/src/main/resources/db.sql @@ -0,0 +1,25 @@ +DROP TABLE IF EXISTS World; + +CREATE TABLE World (id integer NOT NULL, randomNumber integer NOT NULL default 0, PRIMARY KEY (id)); +GRANT SELECT, UPDATE ON World to benchmarkdbuser; + +INSERT INTO World (id, randomnumber) SELECT x.id, random() * 10000 + 1 FROM generate_series(1,10000) as x(id); + + + +DROP TABLE IF EXISTS Fortune; +CREATE TABLE Fortune (id integer NOT NULL, message varchar(2048) NOT NULL, PRIMARY KEY (id)); +GRANT SELECT ON Fortune to benchmarkdbuser; + +INSERT INTO Fortune (id, message) VALUES (1, 'fortune: No such file or directory'); +INSERT INTO Fortune (id, message) VALUES (2, 'A computer scientist is someone who fixes things that aren''t broken.'); +INSERT INTO Fortune (id, message) VALUES (3, 'After enough decimal places, nobody gives a damn.'); +INSERT INTO Fortune (id, message) VALUES (4, 'A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1'); +INSERT INTO Fortune (id, message) VALUES (5, 'A computer program does what you tell it to do, not what you want it to do.'); +INSERT INTO Fortune (id, message) VALUES (6, 'Emacs is a nice operating system, but I prefer UNIX. — Tom Christaensen'); +INSERT INTO Fortune (id, message) VALUES (7, 'Any program that runs right is obsolete.'); +INSERT INTO Fortune (id, message) VALUES (8, 'A list is only as strong as its weakest link. — Donald Knuth'); +INSERT INTO Fortune (id, message) VALUES (9, 'Feature: A bug with seniority.'); +INSERT INTO Fortune (id, message) VALUES (10, 'Computers make very fast, very accurate mistakes.'); +INSERT INTO Fortune (id, message) VALUES (11, ''); +INSERT INTO Fortune (id, message) VALUES (12, 'フレームワークのベンチマーク'); \ No newline at end of file From 04043d89d1c159fbda72716b0475abd67537b25f Mon Sep 17 00:00:00 2001 From: Jeremy Kuhn Date: Fri, 20 Aug 2021 18:35:23 +0200 Subject: [PATCH 02/14] Remove unused code --- .../inverno/benchmark/internal/Handler.java | 24 +------------------ 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java index 40f50082b0d..a2eb6548f9a 100644 --- a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java @@ -121,7 +121,7 @@ public void handle(Exchange exchange) throws HttpException { } } } - } + } private static final byte[] STATIC_PLAINTEXT = "Hello, World!".getBytes(Charsets.UTF_8); private static final int STATIC_PLAINTEXT_LEN = STATIC_PLAINTEXT.length; @@ -246,28 +246,6 @@ public void handle_queries(Exchange exchange) throws HttpException { public void handle_updates(Exchange exchange) throws HttpException { int queries = this.extractQueriesParameter(exchange); - this.poolSqlClient.get().connection(ops -> Flux.range(0, queries) - .flatMap(ign -> ops.queryForObject( - DB_SELECT_WORLD, - row -> new World(row.getInteger(0), randomWorldId()), - randomWorldId() - ) - ) - .collectSortedList() - .delayUntil(worlds -> ops.batchUpdate( - DB_UPDATE_WORLD, - worlds.stream().map(world -> new Object[] { world.getRandomNumber(), world.getId() }) - ) - ) - .map(worlds -> { - try { - return Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(worlds))); - } - catch (JsonProcessingException e) { - throw new InternalServerErrorException(e); - } - })); - exchange.response() .headers(h -> h .add(HttpHeaderNames.SERVER, STATIC_SERVER) From 102d1cbf164f6eccc01d3a943eb46b3aaeb93aec Mon Sep 17 00:00:00 2001 From: Jeremy Kuhn Date: Mon, 23 Aug 2021 08:52:38 +0200 Subject: [PATCH 03/14] Optimization --- .../com/techempower/inverno/benchmark/internal/Handler.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java index a2eb6548f9a..d4a98ecd184 100644 --- a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java @@ -3,6 +3,7 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Collections; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; @@ -280,6 +281,8 @@ public void handle_updates(Exchange exchange) throws HttpException { private static final String DB_SELECT_FORTUNE = "SELECT id, message from FORTUNE"; 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.unreleasableBuffer(Unpooled.buffer())); + public void handle_fortunes(Exchange exchange) throws HttpException { exchange.response() .headers(h -> h @@ -296,7 +299,7 @@ public void handle_fortunes(Exchange exchange) throws HttpException { .flatMap(fortunes -> { fortunes.add(new Fortune(0, "Additional fortune added at request time.")); Collections.sort(fortunes); - return Mono.fromFuture(() -> FortunesTemplate.bytebuf(() -> Unpooled.unreleasableBuffer(Unpooled.buffer())).render(fortunes)); + return Mono.fromFuture(() -> FORTUNES_RENDERER.render(fortunes)); }) ); } From eec498ee22934edaa6df7d892518a79fb36ee38f Mon Sep 17 00:00:00 2001 From: Jeremy Kuhn Date: Mon, 6 Sep 2021 22:59:27 +0200 Subject: [PATCH 04/14] remove db.sql --- .../Java/inverno/src/main/resources/db.sql | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 frameworks/Java/inverno/src/main/resources/db.sql diff --git a/frameworks/Java/inverno/src/main/resources/db.sql b/frameworks/Java/inverno/src/main/resources/db.sql deleted file mode 100644 index 2fd0de35be5..00000000000 --- a/frameworks/Java/inverno/src/main/resources/db.sql +++ /dev/null @@ -1,25 +0,0 @@ -DROP TABLE IF EXISTS World; - -CREATE TABLE World (id integer NOT NULL, randomNumber integer NOT NULL default 0, PRIMARY KEY (id)); -GRANT SELECT, UPDATE ON World to benchmarkdbuser; - -INSERT INTO World (id, randomnumber) SELECT x.id, random() * 10000 + 1 FROM generate_series(1,10000) as x(id); - - - -DROP TABLE IF EXISTS Fortune; -CREATE TABLE Fortune (id integer NOT NULL, message varchar(2048) NOT NULL, PRIMARY KEY (id)); -GRANT SELECT ON Fortune to benchmarkdbuser; - -INSERT INTO Fortune (id, message) VALUES (1, 'fortune: No such file or directory'); -INSERT INTO Fortune (id, message) VALUES (2, 'A computer scientist is someone who fixes things that aren''t broken.'); -INSERT INTO Fortune (id, message) VALUES (3, 'After enough decimal places, nobody gives a damn.'); -INSERT INTO Fortune (id, message) VALUES (4, 'A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1'); -INSERT INTO Fortune (id, message) VALUES (5, 'A computer program does what you tell it to do, not what you want it to do.'); -INSERT INTO Fortune (id, message) VALUES (6, 'Emacs is a nice operating system, but I prefer UNIX. — Tom Christaensen'); -INSERT INTO Fortune (id, message) VALUES (7, 'Any program that runs right is obsolete.'); -INSERT INTO Fortune (id, message) VALUES (8, 'A list is only as strong as its weakest link. — Donald Knuth'); -INSERT INTO Fortune (id, message) VALUES (9, 'Feature: A bug with seniority.'); -INSERT INTO Fortune (id, message) VALUES (10, 'Computers make very fast, very accurate mistakes.'); -INSERT INTO Fortune (id, message) VALUES (11, ''); -INSERT INTO Fortune (id, message) VALUES (12, 'フレームワークのベンチマーク'); \ No newline at end of file From 6872c7839fb7a9c3aaa6ebb64c1057157ba4274e Mon Sep 17 00:00:00 2001 From: Jeremy Kuhn Date: Wed, 15 Sep 2021 21:56:03 +0200 Subject: [PATCH 05/14] Upgrade to Inverno 1.2.2 --- frameworks/Java/inverno/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/Java/inverno/pom.xml b/frameworks/Java/inverno/pom.xml index adf81017f13..27b2a3a3f5d 100644 --- a/frameworks/Java/inverno/pom.xml +++ b/frameworks/Java/inverno/pom.xml @@ -6,7 +6,7 @@ io.inverno.dist inverno-parent - 1.2.1 + 1.2.2 com.techempower inverno-benchmark From 3a86f43648848ed6c5e8b54afcde37be8d846689 Mon Sep 17 00:00:00 2001 From: Jeremy Kuhn Date: Wed, 9 Feb 2022 15:47:22 +0100 Subject: [PATCH 06/14] Upgrade to Inverno 1.4.1 --- frameworks/Java/inverno/pom.xml | 2 +- .../inverno/benchmark/internal/Handler.java | 110 ++++++++++-------- .../PooledClientSqlClientReactorScope.java | 67 ----------- ...rScope.java => SqlClientReactorScope.java} | 25 ++-- .../inverno/src/main/java/module-info.java | 3 - 5 files changed, 72 insertions(+), 135 deletions(-) delete mode 100644 frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/PooledClientSqlClientReactorScope.java rename frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/{PoolSqlClientReactorScope.java => SqlClientReactorScope.java} (70%) diff --git a/frameworks/Java/inverno/pom.xml b/frameworks/Java/inverno/pom.xml index 27b2a3a3f5d..296f7d99246 100644 --- a/frameworks/Java/inverno/pom.xml +++ b/frameworks/Java/inverno/pom.xml @@ -6,7 +6,7 @@ io.inverno.dist inverno-parent - 1.2.2 + 1.4.1 com.techempower inverno-benchmark diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java index d4a98ecd184..1e7052c275e 100644 --- a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java @@ -28,8 +28,10 @@ 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.ExchangeHandler; +import io.inverno.mod.http.server.ExchangeContext; +import io.inverno.mod.http.server.RootExchangeHandler; import io.inverno.mod.sql.SqlClient; +import io.inverno.mod.sql.UnsafeSqlOperations; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.EventLoopGroup; @@ -40,7 +42,7 @@ import reactor.core.publisher.Mono; @Bean( visibility = Visibility.PRIVATE ) -public class Handler implements ExchangeHandler { +public class Handler implements RootExchangeHandler> { private static final String PATH_PLAINTEXT = "/plaintext"; private static final String PATH_JSON = "/json"; @@ -49,12 +51,15 @@ public class Handler implements ExchangeHandler { private static final String PATH_UPDATES = "/updates"; private static final String PATH_FORTUNES = "/fortunes"; + public static final String DB_SELECT_WORLD = "SELECT id, randomnumber from WORLD where id = $1"; + public static final String DB_UPDATE_WORLD = "UPDATE world SET randomnumber=$1 WHERE id=$2"; + public static final String DB_SELECT_FORTUNE = "SELECT id, message from FORTUNE"; + private static final CharSequence STATIC_SERVER = AsciiString.cached("inverno"); private final Reactor reactor; private final ObjectMapper mapper; - private final ReactorScope pooledClientSqlClient; - private final ReactorScope poolSqlClient; + private final ReactorScope> sqlClient; private EventLoopGroup dateEventLoopGroup; @@ -62,13 +67,11 @@ public class Handler implements ExchangeHandler { public Handler(Reactor reactor, ObjectMapper mapper, - ReactorScope pooledClientSqlClient, - ReactorScope poolSqlClient + ReactorScope> sqlClient ) { this.reactor = reactor; this.mapper = mapper; - this.pooledClientSqlClient = pooledClientSqlClient; - this.poolSqlClient = poolSqlClient; + this.sqlClient = sqlClient; } @Init @@ -85,7 +88,7 @@ public void destroy() { } @Override - public void handle(Exchange exchange) throws HttpException { + public void handle(Exchange exchange) throws HttpException { switch(exchange.request().getPath()) { case PATH_PLAINTEXT: { this.handle_plaintext(exchange); @@ -145,7 +148,7 @@ public ByteBuf get() { private static final Mono PLAIN_TEXT_MONO = Mono.fromSupplier(new PlaintextSupplier()); - public void handle_plaintext(Exchange exchange) throws HttpException { + public void handle_plaintext(Exchange exchange) throws HttpException { exchange.response() .headers(h -> h .add(HttpHeaderNames.SERVER, STATIC_SERVER) @@ -158,7 +161,7 @@ public void handle_plaintext(Exchange exchange) throws HttpException { .stream(PLAIN_TEXT_MONO); } - public void handle_json(Exchange exchange) throws HttpException { + public void handle_json(Exchange exchange) throws HttpException { try { exchange.response() .headers(h -> h @@ -175,13 +178,11 @@ public void handle_json(Exchange exchange) throws HttpException { } } - private static final String DB_SELECT_WORLD = "SELECT id, randomnumber from WORLD where id = $1"; - private static int randomWorldId() { return 1 + ThreadLocalRandom.current().nextInt(10000); } - public void handle_db(Exchange exchange) throws HttpException { + public void handle_db(Exchange exchange) throws HttpException { exchange.response() .headers(h -> h .add(HttpHeaderNames.SERVER, STATIC_SERVER) @@ -189,7 +190,8 @@ public void handle_db(Exchange exchange) throws HttpException { .add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON) ) .body() - .raw().stream(this.pooledClientSqlClient.get().queryForObject( + .raw().stream(this.sqlClient.get().flatMap(client -> + client.queryForObject( DB_SELECT_WORLD, row -> { try { @@ -201,21 +203,21 @@ public void handle_db(Exchange exchange) throws HttpException { }, randomWorldId() ) - ); + )); } private static final String PARAMETER_QUERIES = "queries"; - private int extractQueriesParameter(Exchange exchange) { + private int extractQueriesParameter(Exchange exchange) { try { return Math.min(500, Math.max(1, exchange.request().queryParameters().get(PARAMETER_QUERIES).map(Parameter::asInteger).orElse(1))); } - catch (ConverterException e) { // TODO + catch (ConverterException e) { return 1; } } - public void handle_queries(Exchange exchange) throws HttpException { + public void handle_queries(Exchange exchange) throws HttpException { int queries = this.extractQueriesParameter(exchange); exchange.response() .headers(h -> h @@ -224,12 +226,17 @@ public void handle_queries(Exchange exchange) throws HttpException { .add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON) ) .body() - .raw().stream(Flux.range(0, queries) - .flatMap(ign -> this.pooledClientSqlClient.get().queryForObject( - DB_SELECT_WORLD, - row -> new World(row.getInteger(0), row.getInteger(1)), - randomWorldId() - )) + .raw().stream(this.sqlClient.get() + .flatMapMany(client -> ((UnsafeSqlOperations)client) + .batchQueries(ops -> + Flux.range(0, queries) + .map(ign -> ops.queryForObject( + DB_SELECT_WORLD, + row -> new World(row.getInteger(0), row.getInteger(1)), + randomWorldId() + )) + ) + ) .collectList() .map(worlds -> { try { @@ -242,27 +249,28 @@ public void handle_queries(Exchange exchange) throws HttpException { ); } - private static final String DB_UPDATE_WORLD = "UPDATE world SET randomnumber=$1 WHERE id=$2"; - - public void handle_updates(Exchange exchange) throws HttpException { + 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.poolSqlClient.get().connection(ops -> Flux.range(0, queries) - .flatMap(ign -> ops.queryForObject( - DB_SELECT_WORLD, - row -> new World(row.getInteger(0), randomWorldId()), - randomWorldId() - ) - ) + .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 -> ops.batchUpdate( + .delayUntil(worlds -> client.batchUpdate( DB_UPDATE_WORLD, worlds.stream().map(world -> new Object[] { world.getRandomNumber(), world.getId() }) ) @@ -274,16 +282,16 @@ public void handle_updates(Exchange exchange) throws HttpException { catch (JsonProcessingException e) { throw new InternalServerErrorException(e); } - })) - ); + }) + ) + ); } - private static final String DB_SELECT_FORTUNE = "SELECT id, message from FORTUNE"; 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.unreleasableBuffer(Unpooled.buffer())); - public void handle_fortunes(Exchange exchange) throws HttpException { + public void handle_fortunes(Exchange exchange) throws HttpException { exchange.response() .headers(h -> h .add(HttpHeaderNames.SERVER, STATIC_SERVER) @@ -291,10 +299,12 @@ public void handle_fortunes(Exchange exchange) throws HttpException { .add(HttpHeaderNames.CONTENT_TYPE, MEDIA_TEXT_HTML_UTF8) ) .body() - .raw().stream(Flux.from(this.pooledClientSqlClient.get().query( - DB_SELECT_FORTUNE, - row -> new Fortune(row.getInteger(0), row.getString(1)) - )) + .raw().stream(this.sqlClient.get().flatMapMany(client -> + client.query( + DB_SELECT_FORTUNE, + row -> new Fortune(row.getInteger(0), row.getString(1)) + ) + ) .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/PooledClientSqlClientReactorScope.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/PooledClientSqlClientReactorScope.java deleted file mode 100644 index 06516866c47..00000000000 --- a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/PooledClientSqlClientReactorScope.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.techempower.inverno.benchmark.internal; - -import com.techempower.inverno.benchmark.AppConfiguration; - -import io.inverno.core.annotation.Bean; -import io.inverno.core.annotation.Bean.Visibility; -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.ReactorScope; -import io.inverno.mod.base.concurrent.VertxReactor; -import io.inverno.mod.sql.SqlClient; -import io.inverno.mod.sql.vertx.PooledClientSqlClient; -import io.vertx.core.Vertx; -import io.vertx.core.VertxOptions; -import io.vertx.pgclient.PgConnectOptions; -import io.vertx.pgclient.PgPool; -import io.vertx.sqlclient.PoolOptions; - -@Bean( name = "pooledClientSqlClient", visibility = Visibility.PRIVATE ) -public class PooledClientSqlClientReactorScope extends ReactorScope { - - private final AppConfiguration configuration; - private final Reactor reactor; - - private Vertx vertx; - private PgConnectOptions connectOptions; - private PoolOptions poolOptions; - - public PooledClientSqlClientReactorScope(AppConfiguration configuration, Reactor reactor) { - this.configuration = configuration; - this.reactor = reactor; - } - - @Init - public void init() { - if(this.reactor instanceof VertxReactor) { - this.vertx = ((VertxReactor)this.reactor).getVertx(); - } - else { - this.vertx = Vertx.vertx(new VertxOptions().setPreferNativeTransport(this.configuration.boot().prefer_native_transport())); - } - - this.connectOptions = new PgConnectOptions() - .setHost(this.configuration.db_host()) - .setPort(this.configuration.db_port()) - .setDatabase(this.configuration.db_database()) - .setUser(this.configuration.db_username()) - .setPassword(this.configuration.db_password()) - .setCachePreparedStatements(true); - - this.poolOptions = new PoolOptions().setMaxSize(1); - } - - @Destroy - public void destroy() { - if(!(this.reactor instanceof VertxReactor)) { - this.vertx.close(); - } - } - - @Override - protected SqlClient create() { - return new PooledClientSqlClient(PgPool.client(this.vertx, this.connectOptions, this.poolOptions)); - } - -} diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/PoolSqlClientReactorScope.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/SqlClientReactorScope.java similarity index 70% rename from frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/PoolSqlClientReactorScope.java rename to frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/SqlClientReactorScope.java index 76eed32483b..81b30069ec2 100644 --- a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/PoolSqlClientReactorScope.java +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/SqlClientReactorScope.java @@ -3,31 +3,30 @@ import com.techempower.inverno.benchmark.AppConfiguration; import io.inverno.core.annotation.Bean; +import io.inverno.core.annotation.Bean.Visibility; import io.inverno.core.annotation.Destroy; import io.inverno.core.annotation.Init; -import io.inverno.core.annotation.Bean.Visibility; import io.inverno.mod.base.concurrent.Reactor; import io.inverno.mod.base.concurrent.ReactorScope; import io.inverno.mod.base.concurrent.VertxReactor; import io.inverno.mod.sql.SqlClient; -import io.inverno.mod.sql.vertx.PoolSqlClient; +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.PgPool; -import io.vertx.sqlclient.PoolOptions; +import io.vertx.pgclient.PgConnection; +import reactor.core.publisher.Mono; -@Bean( name = "poolSqlClient", visibility = Visibility.PRIVATE ) -public class PoolSqlClientReactorScope extends ReactorScope { +@Bean( name = "SqlClient", visibility = Visibility.PRIVATE ) +public class SqlClientReactorScope extends ReactorScope> { private final AppConfiguration configuration; private final Reactor reactor; private Vertx vertx; private PgConnectOptions connectOptions; - private PoolOptions poolOptions; - public PoolSqlClientReactorScope(AppConfiguration configuration, Reactor reactor) { + public SqlClientReactorScope(AppConfiguration configuration, Reactor reactor) { this.configuration = configuration; this.reactor = reactor; } @@ -47,9 +46,8 @@ public void init() { .setDatabase(this.configuration.db_database()) .setUser(this.configuration.db_username()) .setPassword(this.configuration.db_password()) - .setCachePreparedStatements(true); - - this.poolOptions = new PoolOptions().setMaxSize(4); + .setCachePreparedStatements(true) + .setPipeliningLimit(100_100); } @Destroy @@ -60,8 +58,7 @@ public void destroy() { } @Override - protected SqlClient create() { - return new PoolSqlClient(PgPool.pool(this.vertx, this.connectOptions, this.poolOptions)); + protected Mono create() { + return Mono.fromCompletionStage(PgConnection.connect(this.vertx, this.connectOptions).toCompletionStage()).map(pgConn -> (SqlClient)new ConnectionSqlClient(pgConn)).cache(); } - } diff --git a/frameworks/Java/inverno/src/main/java/module-info.java b/frameworks/Java/inverno/src/main/java/module-info.java index 8370bb11766..7f0f44d7b65 100644 --- a/frameworks/Java/inverno/src/main/java/module-info.java +++ b/frameworks/Java/inverno/src/main/java/module-info.java @@ -1,7 +1,4 @@ @io.inverno.core.annotation.Module( excludes = { "io.inverno.mod.sql.vertx" } ) - -@io.inverno.core.annotation.Wire(beans="pooledClientSqlClient", into="handler:pooledClientSqlClient") -@io.inverno.core.annotation.Wire(beans="poolSqlClient", into="handler:poolSqlClient") module com.techempower.inverno.benchmark { requires io.inverno.mod.boot; requires io.inverno.mod.http.server; From a516c0a23bc4ba1c33e8093c6427f5052afa5dd8 Mon Sep 17 00:00:00 2001 From: Jeremy Kuhn Date: Wed, 9 Feb 2022 15:50:26 +0100 Subject: [PATCH 07/14] Update README.md --- frameworks/Java/inverno/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/Java/inverno/README.md b/frameworks/Java/inverno/README.md index 18f28c8d4ff..b205ef706b6 100755 --- a/frameworks/Java/inverno/README.md +++ b/frameworks/Java/inverno/README.md @@ -13,7 +13,7 @@ ## Important Libraries The tests were run with: * [Java OpenJDK 16](https://openjdk.java.net/) -* [Inverno 1.2.1](https://inverno.io) +* [Inverno 1.4.1](https://inverno.io) ## Test URLs ### JSON From 4a500ea81d16f8714ef2cb85f34ec91c51f7314a Mon Sep 17 00:00:00 2001 From: Jeremy Kuhn Date: Tue, 6 Feb 2024 11:05:28 +0100 Subject: [PATCH 08/14] Upgrade to Inverno 1.6.2 --- .../Java/inverno/inverno-postgres.dockerfile | 6 +- frameworks/Java/inverno/inverno.dockerfile | 6 +- frameworks/Java/inverno/pom.xml | 132 +++++++++++++----- .../src/jmods/io.vertx.core/module-info.java | 99 ------------- .../jmods/r2dbc.postgresql/module-info.java | 38 ----- .../{Handler.java => Controller.java} | 9 +- .../inverno/src/main/java/module-info.java | 6 +- 7 files changed, 111 insertions(+), 185 deletions(-) delete mode 100644 frameworks/Java/inverno/src/jmods/io.vertx.core/module-info.java delete mode 100644 frameworks/Java/inverno/src/jmods/r2dbc.postgresql/module-info.java rename frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/{Handler.java => Controller.java} (96%) diff --git a/frameworks/Java/inverno/inverno-postgres.dockerfile b/frameworks/Java/inverno/inverno-postgres.dockerfile index 5a2928679e3..367a708fb45 100644 --- a/frameworks/Java/inverno/inverno-postgres.dockerfile +++ b/frameworks/Java/inverno/inverno-postgres.dockerfile @@ -1,11 +1,11 @@ -FROM maven:3.8.2-openjdk-16 as maven +FROM maven:3.9.6-amazoncorretto-21 as maven WORKDIR /inverno COPY src src COPY pom.xml pom.xml -RUN mvn package -q +RUN mvn package -q -Pio.inverno.io_uring EXPOSE 8080 # CMD [ "target/maven-inverno/application_linux_amd64/inverno-benchmark-1.0.0-SNAPSHOT/bin/inverno-benchmark" ] CMD export DBIP=`getent hosts tfb-database | awk '{ print $1 }'` && \ - target/maven-inverno/application_linux_amd64/inverno-benchmark-1.0.0-SNAPSHOT/bin/inverno-benchmark --com.techempower.inverno.benchmark.appConfiguration.db_host=\"$DBIP\" \ No newline at end of file + target/maven-inverno/application_linux_amd64/inverno-benchmark-1.0.0-SNAPSHOT/bin/inverno-benchmark --com.techempower.inverno.benchmark.appConfiguration.db_host=\"$DBIP\" diff --git a/frameworks/Java/inverno/inverno.dockerfile b/frameworks/Java/inverno/inverno.dockerfile index 6af60a189dc..2850508516e 100644 --- a/frameworks/Java/inverno/inverno.dockerfile +++ b/frameworks/Java/inverno/inverno.dockerfile @@ -1,9 +1,9 @@ -FROM maven:3.8.2-openjdk-16 as maven +FROM maven:3.9.6-amazoncorretto-21 as maven WORKDIR /inverno COPY src src COPY pom.xml pom.xml -RUN mvn package -q +RUN mvn package -q -Pio.inverno.io_uring EXPOSE 8080 -CMD [ "target/maven-inverno/application_linux_amd64/inverno-benchmark-1.0.0-SNAPSHOT/bin/inverno-benchmark", "--com.techempower.inverno.benchmark.appConfiguration.boot.reactor_prefer_vertx=false" ] +CMD [ "target/inverno-benchmark-1.0.0-SNAPSHOT-application_linux_amd64/bin/inverno-benchmark", "--com.techempower.inverno.benchmark.appConfiguration.boot.reactor_prefer_vertx=false" ] diff --git a/frameworks/Java/inverno/pom.xml b/frameworks/Java/inverno/pom.xml index 296f7d99246..e394d645fad 100644 --- a/frameworks/Java/inverno/pom.xml +++ b/frameworks/Java/inverno/pom.xml @@ -6,7 +6,7 @@ io.inverno.dist inverno-parent - 1.4.1 + 1.6.2 com.techempower inverno-benchmark @@ -17,9 +17,9 @@ Inverno framework benchmark test - 16 - 16 - 16 + 21 + 21 + 21 @@ -79,10 +79,21 @@ ${version.netty} - + + + + io.netty.incubator + netty-incubator-transport-native-io_uring + linux-x86_64 + + + + io.vertx + vertx-io_uring-incubator @@ -90,36 +101,87 @@ log4j-core - - - - - io.inverno.tool - inverno-maven-plugin - - - inverno-package - package - - build-app - - - server - - - inverno-benchmark - -Xms2g -Xmx2g -server -XX:+UseNUMA -XX:+UseParallelGC -Dio.netty.leakDetection.level=disabled -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.epoll - - - - zip - - - - - - - + + + + io.inverno.epoll + + + io.netty + netty-transport-native-epoll + linux-x86_64 + + + + + + io.inverno.tool + inverno-maven-plugin + + + inverno-package-app + package + + package-app + + + server + + + inverno-benchmark + -Xms2g -Xmx2g -server -XX:+UseNUMA -XX:+UseParallelGC -Dio.netty.leakDetection.level=disabled -Dio.netty.buffer.checkBounds=false -Dio.netty.buffer.checkBounds=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 + + + + zip + + + + + + + + + + io.inverno.io_uring + + + io.netty + netty-transport-native-epoll + linux-x86_64 + + + + + + io.inverno.tool + inverno-maven-plugin + + + inverno-package-app + package + + package-app + + + server + + + inverno-benchmark + -Xms2g -Xmx2g -server -XX:+UseNUMA -XX:+UseParallelGC -Dio.netty.leakDetection.level=disabled -Dio.netty.buffer.checkBounds=false -Dio.netty.buffer.checkBounds=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 + + + + zip + + + + + + + + + diff --git a/frameworks/Java/inverno/src/jmods/io.vertx.core/module-info.java b/frameworks/Java/inverno/src/jmods/io.vertx.core/module-info.java deleted file mode 100644 index 56fbff0c18c..00000000000 --- a/frameworks/Java/inverno/src/jmods/io.vertx.core/module-info.java +++ /dev/null @@ -1,99 +0,0 @@ -module io.vertx.core { - requires io.netty.handler.proxy; - requires io.netty.resolver.dns; - requires io.netty.transport.epoll; - requires io.netty.transport.unix.common; - requires java.naming; - requires org.apache.logging.log4j; - - requires transitive com.fasterxml.jackson.core; - requires transitive com.fasterxml.jackson.databind; - requires transitive io.netty.buffer; - requires transitive io.netty.codec; - requires transitive io.netty.codec.dns; - requires transitive io.netty.codec.http; - requires transitive io.netty.codec.http2; - requires transitive io.netty.common; - requires transitive io.netty.handler; - requires transitive io.netty.resolver; - requires transitive io.netty.transport; - requires transitive java.compiler; - requires transitive java.logging; - - exports io.vertx.core; - exports io.vertx.core.buffer; - exports io.vertx.core.buffer.impl; - exports io.vertx.core.cli; - exports io.vertx.core.cli.annotations; - exports io.vertx.core.cli.converters; - exports io.vertx.core.cli.impl; - exports io.vertx.core.datagram; - exports io.vertx.core.datagram.impl; - exports io.vertx.core.dns; - exports io.vertx.core.dns.impl; - exports io.vertx.core.dns.impl.decoder; - exports io.vertx.core.eventbus; - exports io.vertx.core.eventbus.impl; - exports io.vertx.core.eventbus.impl.clustered; - exports io.vertx.core.eventbus.impl.codecs; - exports io.vertx.core.file; - exports io.vertx.core.file.impl; - exports io.vertx.core.http; - exports io.vertx.core.http.impl; - exports io.vertx.core.http.impl.cgbystrom; - exports io.vertx.core.http.impl.headers; - exports io.vertx.core.http.impl.ws; - exports io.vertx.core.impl; - exports io.vertx.core.impl.cpu; - exports io.vertx.core.impl.future; - exports io.vertx.core.impl.launcher; - exports io.vertx.core.impl.launcher.commands; - exports io.vertx.core.impl.logging; - exports io.vertx.core.impl.resolver; - exports io.vertx.core.impl.utils; - exports io.vertx.core.impl.verticle; - exports io.vertx.core.json; - exports io.vertx.core.json.impl; - exports io.vertx.core.json.jackson; - exports io.vertx.core.json.pointer; - exports io.vertx.core.json.pointer.impl; - exports io.vertx.core.logging; - exports io.vertx.core.metrics; - exports io.vertx.core.metrics.impl; - exports io.vertx.core.net; - exports io.vertx.core.net.impl; - exports io.vertx.core.net.impl.pkcs1; - exports io.vertx.core.net.impl.pool; - exports io.vertx.core.net.impl.transport; - exports io.vertx.core.parsetools; - exports io.vertx.core.parsetools.impl; - exports io.vertx.core.shareddata; - exports io.vertx.core.shareddata.impl; - exports io.vertx.core.spi; - exports io.vertx.core.spi.cluster; - exports io.vertx.core.spi.cluster.impl; - exports io.vertx.core.spi.cluster.impl.selector; - exports io.vertx.core.spi.json; - exports io.vertx.core.spi.launcher; - exports io.vertx.core.spi.logging; - exports io.vertx.core.spi.metrics; - exports io.vertx.core.spi.observability; - exports io.vertx.core.spi.resolver; - exports io.vertx.core.spi.tracing; - exports io.vertx.core.streams; - exports io.vertx.core.streams.impl; - exports io.vertx.core.tracing; - - provides io.vertx.core.spi.launcher.CommandFactory with - io.vertx.core.impl.launcher.commands.RunCommandFactory, - io.vertx.core.impl.launcher.commands.VersionCommandFactory, - io.vertx.core.impl.launcher.commands.BareCommandFactory, - io.vertx.core.impl.launcher.commands.ListCommandFactory, - io.vertx.core.impl.launcher.commands.StartCommandFactory, - io.vertx.core.impl.launcher.commands.StopCommandFactory; - - uses io.vertx.core.spi.VertxServiceProvider; - uses io.vertx.core.spi.VerticleFactory; - uses io.vertx.core.spi.JsonFactory; - -} diff --git a/frameworks/Java/inverno/src/jmods/r2dbc.postgresql/module-info.java b/frameworks/Java/inverno/src/jmods/r2dbc.postgresql/module-info.java deleted file mode 100644 index 18208e43b04..00000000000 --- a/frameworks/Java/inverno/src/jmods/r2dbc.postgresql/module-info.java +++ /dev/null @@ -1,38 +0,0 @@ -module r2dbc.postgresql { - requires com.ongres.scram.client; - requires com.ongres.scram.common; - requires io.netty.codec; - requires io.netty.resolver; - requires io.netty.transport; - requires io.netty.transport.epoll; - requires io.netty.transport.unix.common; - requires java.naming; - - requires transitive io.netty.buffer; - requires transitive io.netty.common; - requires transitive io.netty.handler; - requires transitive org.reactivestreams; - requires transitive r2dbc.spi; - requires transitive reactor.core; - requires transitive reactor.netty.core; - - exports io.r2dbc.postgresql; - exports io.r2dbc.postgresql.api; - exports io.r2dbc.postgresql.authentication; - exports io.r2dbc.postgresql.client; - exports io.r2dbc.postgresql.codec; - exports io.r2dbc.postgresql.extension; - exports io.r2dbc.postgresql.message; - exports io.r2dbc.postgresql.message.backend; - exports io.r2dbc.postgresql.message.frontend; - exports io.r2dbc.postgresql.replication; - exports io.r2dbc.postgresql.util; - - provides io.r2dbc.postgresql.extension.Extension with - io.r2dbc.postgresql.codec.BuiltinDynamicCodecs; - provides io.r2dbc.spi.ConnectionFactoryProvider with - io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider; - - uses io.r2dbc.postgresql.extension.Extension; - -} diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Controller.java similarity index 96% rename from frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java rename to frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Controller.java index 1e7052c275e..639ec561159 100644 --- a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Handler.java +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/Controller.java @@ -23,13 +23,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.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.ExchangeContext; -import io.inverno.mod.http.server.RootExchangeHandler; +import io.inverno.mod.http.server.ErrorExchange; +import io.inverno.mod.http.server.ServerController; import io.inverno.mod.sql.SqlClient; import io.inverno.mod.sql.UnsafeSqlOperations; import io.netty.buffer.ByteBuf; @@ -42,7 +43,7 @@ import reactor.core.publisher.Mono; @Bean( visibility = Visibility.PRIVATE ) -public class Handler implements RootExchangeHandler> { +public class Controller implements ServerController, ErrorExchange> { private static final String PATH_PLAINTEXT = "/plaintext"; private static final String PATH_JSON = "/json"; @@ -65,7 +66,7 @@ public class Handler implements RootExchangeHandler> sqlClient ) { diff --git a/frameworks/Java/inverno/src/main/java/module-info.java b/frameworks/Java/inverno/src/main/java/module-info.java index 7f0f44d7b65..fefbf5c52a6 100644 --- a/frameworks/Java/inverno/src/main/java/module-info.java +++ b/frameworks/Java/inverno/src/main/java/module-info.java @@ -16,9 +16,9 @@ requires io.vertx.core; requires java.sql; - requires transitive io.netty.transport; - requires static io.netty.transport.unix.common; - requires static io.netty.transport.epoll; + //requires transitive io.netty.transport; + //requires static io.netty.transport.unix.common; + //requires static io.netty.transport.epoll; exports com.techempower.inverno.benchmark; exports com.techempower.inverno.benchmark.model; From 93791a57d2cf74cf9f055f1abc7f9eb5c790cdae Mon Sep 17 00:00:00 2001 From: Jeremy Kuhn Date: Tue, 6 Feb 2024 11:41:52 +0100 Subject: [PATCH 09/14] Add missing binutils when building docker images --- frameworks/Java/inverno/inverno-postgres.dockerfile | 1 + frameworks/Java/inverno/inverno.dockerfile | 1 + 2 files changed, 2 insertions(+) diff --git a/frameworks/Java/inverno/inverno-postgres.dockerfile b/frameworks/Java/inverno/inverno-postgres.dockerfile index 367a708fb45..bcac4561d5f 100644 --- a/frameworks/Java/inverno/inverno-postgres.dockerfile +++ b/frameworks/Java/inverno/inverno-postgres.dockerfile @@ -2,6 +2,7 @@ FROM maven:3.9.6-amazoncorretto-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 2850508516e..1dd39d53b27 100644 --- a/frameworks/Java/inverno/inverno.dockerfile +++ b/frameworks/Java/inverno/inverno.dockerfile @@ -2,6 +2,7 @@ FROM maven:3.9.6-amazoncorretto-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 From 71af367a7b876d05f7339dbe2b0385c5ec87e871 Mon Sep 17 00:00:00 2001 From: Jeremy Kuhn Date: Tue, 6 Feb 2024 11:57:23 +0100 Subject: [PATCH 10/14] Fix inverno postgres CMD --- frameworks/Java/inverno/inverno-postgres.dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frameworks/Java/inverno/inverno-postgres.dockerfile b/frameworks/Java/inverno/inverno-postgres.dockerfile index bcac4561d5f..6a3fcd9070f 100644 --- a/frameworks/Java/inverno/inverno-postgres.dockerfile +++ b/frameworks/Java/inverno/inverno-postgres.dockerfile @@ -7,6 +7,6 @@ RUN mvn package -q -Pio.inverno.io_uring EXPOSE 8080 -# CMD [ "target/maven-inverno/application_linux_amd64/inverno-benchmark-1.0.0-SNAPSHOT/bin/inverno-benchmark" ] +# CMD [ "target/inverno-benchmark-1.0.0-SNAPSHOT-application_linux_amd64/bin/inverno-benchmark" ] CMD export DBIP=`getent hosts tfb-database | awk '{ print $1 }'` && \ - target/maven-inverno/application_linux_amd64/inverno-benchmark-1.0.0-SNAPSHOT/bin/inverno-benchmark --com.techempower.inverno.benchmark.appConfiguration.db_host=\"$DBIP\" + target/inverno-benchmark-1.0.0-SNAPSHOT-application_linux_amd64/bin/inverno-benchmark --com.techempower.inverno.benchmark.appConfiguration.db_host=\"$DBIP\" From 0fc994537312ce7804497d354395016df165760c Mon Sep 17 00:00:00 2001 From: Jeremy Kuhn Date: Thu, 22 Feb 2024 22:52:01 +0100 Subject: [PATCH 11/14] Upgrade to Inverno 1.7.0 --- frameworks/Java/inverno/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/Java/inverno/pom.xml b/frameworks/Java/inverno/pom.xml index e394d645fad..40158dba480 100644 --- a/frameworks/Java/inverno/pom.xml +++ b/frameworks/Java/inverno/pom.xml @@ -6,7 +6,7 @@ io.inverno.dist inverno-parent - 1.6.2 + 1.7.0 com.techempower inverno-benchmark From 80a936ec7e1fe3be96b38bf4c082b3b40a9561ff Mon Sep 17 00:00:00 2001 From: Jeremy Kuhn Date: Fri, 12 Apr 2024 14:13:12 +0200 Subject: [PATCH 12/14] Upgrade to Inverno 1.9.0 + make app resilient to DB connection loss --- frameworks/Java/inverno/pom.xml | 36 ++++++++----------- .../internal/SqlClientReactorScope.java | 4 ++- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/frameworks/Java/inverno/pom.xml b/frameworks/Java/inverno/pom.xml index 40158dba480..c056d047bbc 100644 --- a/frameworks/Java/inverno/pom.xml +++ b/frameworks/Java/inverno/pom.xml @@ -6,7 +6,7 @@ io.inverno.dist inverno-parent - 1.7.0 + 1.9.0 com.techempower inverno-benchmark @@ -71,31 +71,12 @@ io.netty netty-handler-proxy - ${version.netty} io.netty netty-resolver-dns - ${version.netty} - - - - io.netty.incubator - netty-incubator-transport-native-io_uring - linux-x86_64 - - - - io.vertx - vertx-io_uring-incubator - - org.apache.logging.log4j log4j-core @@ -138,6 +119,9 @@ + + --add-modules io.netty.transport.unix.common,io.netty.transport.classes.epoll,io.netty.transport.epoll.linux.x86_64 + @@ -146,10 +130,15 @@ io.inverno.io_uring - io.netty - netty-transport-native-epoll + io.netty.incubator + netty-incubator-transport-native-io_uring linux-x86_64 + + + io.vertx + vertx-io_uring-incubator + @@ -177,6 +166,9 @@ + + --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/internal/SqlClientReactorScope.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/SqlClientReactorScope.java index 81b30069ec2..d3b8a463865 100644 --- a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/SqlClientReactorScope.java +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/SqlClientReactorScope.java @@ -59,6 +59,8 @@ public void destroy() { @Override protected Mono create() { - return Mono.fromCompletionStage(PgConnection.connect(this.vertx, this.connectOptions).toCompletionStage()).map(pgConn -> (SqlClient)new ConnectionSqlClient(pgConn)).cache(); + return Mono.fromCompletionStage(() -> PgConnection.connect(this.vertx, this.connectOptions).toCompletionStage()) + .map(pgConn -> (SqlClient)new ConnectionSqlClient(pgConn)) + .cacheInvalidateWhen(client -> ((ConnectionSqlClient)client).onClose()); } } From d1c9ebae42fc945deee9956014a6af911eec2a9a Mon Sep 17 00:00:00 2001 From: Jeremy Kuhn Date: Fri, 26 Apr 2024 14:20:04 +0200 Subject: [PATCH 13/14] Upgrade to Inverno 1.10.0 + minor changes --- .../Java/inverno/inverno-postgres.dockerfile | 5 ++-- frameworks/Java/inverno/pom.xml | 8 ++--- .../benchmark/internal/Controller.java | 30 +++++++++---------- .../src/main/resources/configuration.cprops | 4 ++- 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/frameworks/Java/inverno/inverno-postgres.dockerfile b/frameworks/Java/inverno/inverno-postgres.dockerfile index 6a3fcd9070f..737cb434013 100644 --- a/frameworks/Java/inverno/inverno-postgres.dockerfile +++ b/frameworks/Java/inverno/inverno-postgres.dockerfile @@ -7,6 +7,7 @@ RUN mvn package -q -Pio.inverno.io_uring EXPOSE 8080 -# CMD [ "target/inverno-benchmark-1.0.0-SNAPSHOT-application_linux_amd64/bin/inverno-benchmark" ] CMD export DBIP=`getent hosts tfb-database | awk '{ print $1 }'` && \ - target/inverno-benchmark-1.0.0-SNAPSHOT-application_linux_amd64/bin/inverno-benchmark --com.techempower.inverno.benchmark.appConfiguration.db_host=\"$DBIP\" + target/inverno-benchmark-1.0.0-SNAPSHOT-application_linux_amd64/bin/inverno-benchmark \ + --com.techempower.inverno.benchmark.appConfiguration.db_host=\"$DBIP\" \ + --com.techempower.inverno.benchmark.appConfiguration.boot.reactor_event_loop_group_size=$((`grep --count ^processor /proc/cpuinfo`)) diff --git a/frameworks/Java/inverno/pom.xml b/frameworks/Java/inverno/pom.xml index c056d047bbc..c91b31c2d37 100644 --- a/frameworks/Java/inverno/pom.xml +++ b/frameworks/Java/inverno/pom.xml @@ -6,7 +6,7 @@ io.inverno.dist inverno-parent - 1.9.0 + 1.10.0 com.techempower inverno-benchmark @@ -47,7 +47,7 @@ io.inverno.mod inverno-sql-vertx - + org.unbescape unbescape @@ -110,7 +110,7 @@ inverno-benchmark - -Xms2g -Xmx2g -server -XX:+UseNUMA -XX:+UseParallelGC -Dio.netty.leakDetection.level=disabled -Dio.netty.buffer.checkBounds=false -Dio.netty.buffer.checkBounds=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 @@ -157,7 +157,7 @@ inverno-benchmark - -Xms2g -Xmx2g -server -XX:+UseNUMA -XX:+UseParallelGC -Dio.netty.leakDetection.level=disabled -Dio.netty.buffer.checkBounds=false -Dio.netty.buffer.checkBounds=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 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 639ec561159..33c94a5c7b4 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,20 +1,11 @@ package com.techempower.inverno.benchmark.internal; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Collections; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; - 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; import com.techempower.inverno.benchmark.templates.FortunesTemplate; - import io.inverno.core.annotation.Bean; import io.inverno.core.annotation.Bean.Visibility; import io.inverno.core.annotation.Destroy; @@ -39,6 +30,13 @@ import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.util.AsciiString; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Collections; +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; @@ -81,6 +79,8 @@ public void init() { this.dateEventLoopGroup.scheduleAtFixedRate(() -> { this.date = new AsciiString(DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now())); }, 0, 1000, TimeUnit.MILLISECONDS); + + } @Destroy @@ -133,7 +133,7 @@ public void handle(Exchange exchange) throws HttpException { private static final ByteBuf STATIC_PLAINTEXT_BYTEBUF; static { - ByteBuf tmpBuf = Unpooled.directBuffer(STATIC_PLAINTEXT_LEN); + ByteBuf tmpBuf = Unpooled.buffer(STATIC_PLAINTEXT_LEN); tmpBuf.writeBytes(STATIC_PLAINTEXT); STATIC_PLAINTEXT_BYTEBUF = Unpooled.unreleasableBuffer(tmpBuf); } @@ -172,7 +172,7 @@ public void handle_json(Exchange exchange) throws HttpException ) .body() .raw() - .value(Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(new Message("Hello, World!"))))); + .value(Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(new Message("Hello, World!")))); } catch (JsonProcessingException | IllegalStateException e) { throw new InternalServerErrorException("Error serializing message as JSON", e); @@ -196,7 +196,7 @@ public void handle_db(Exchange exchange) throws HttpException { DB_SELECT_WORLD, row -> { try { - return Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(new World(row.getInteger(0), row.getInteger(1))))); + return Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(new World(row.getInteger(0), row.getInteger(1)))); } catch (JsonProcessingException e) { throw new InternalServerErrorException(e); @@ -241,7 +241,7 @@ public void handle_queries(Exchange exchange) throws HttpExcept .collectList() .map(worlds -> { try { - return Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(worlds))); + return Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(worlds)); } catch (JsonProcessingException e) { throw new InternalServerErrorException(e); @@ -278,7 +278,7 @@ public void handle_updates(Exchange exchange) throws HttpExcept ) .map(worlds -> { try { - return Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(worlds))); + return Unpooled.wrappedBuffer(this.mapper.writeValueAsBytes(worlds)); } catch (JsonProcessingException e) { throw new InternalServerErrorException(e); @@ -290,7 +290,7 @@ public void handle_updates(Exchange exchange) throws HttpExcept 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.unreleasableBuffer(Unpooled.buffer())); + private static final FortunesTemplate.Renderer> FORTUNES_RENDERER = FortunesTemplate.bytebuf(() -> Unpooled.buffer()); public void handle_fortunes(Exchange exchange) throws HttpException { exchange.response() diff --git a/frameworks/Java/inverno/src/main/resources/configuration.cprops b/frameworks/Java/inverno/src/main/resources/configuration.cprops index df4680e124b..9fc9d6fddd0 100644 --- a/frameworks/Java/inverno/src/main/resources/configuration.cprops +++ b/frameworks/Java/inverno/src/main/resources/configuration.cprops @@ -6,7 +6,9 @@ com.techempower.inverno.benchmark.appConfiguration { http_server { tls_enabled = false server_port = 8080 - h2c_enabled = false + h2_enabled = false + http1x_validate_headers = false + ws_enabled = false } db_database = "hello_world" db_host = "tfb-database" From b1928b26a69ee052ca7d348d11058d6e30d6aff1 Mon Sep 17 00:00:00 2001 From: Jeremy Kuhn Date: Mon, 20 Jan 2025 15:00:44 +0100 Subject: [PATCH 14/14] Inverno upgrade to 1.12.0 + misc performance improvements --- frameworks/Java/inverno/README.md | 21 ++- .../Java/inverno/inverno-postgres.dockerfile | 3 +- frameworks/Java/inverno/inverno.dockerfile | 3 +- frameworks/Java/inverno/pom.xml | 64 +++++-- .../techempower/inverno/benchmark/Main.java | 7 +- .../benchmark/internal/Controller.java | 160 ++++++------------ .../benchmark/internal/DbRepository.java | 132 +++++++++++++++ .../benchmark/internal/JsonSerializer.java | 38 +++++ .../internal/SqlClientReactorScope.java | 66 -------- .../inverno/benchmark/model/Fortune.java | 3 + .../inverno/benchmark/model/Message.java | 3 + .../inverno/benchmark/model/World.java | 11 +- .../inverno/src/main/java/module-info.java | 9 +- 13 files changed, 310 insertions(+), 210 deletions(-) create mode 100644 frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/DbRepository.java create mode 100644 frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/JsonSerializer.java delete mode 100644 frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/SqlClientReactorScope.java 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 + + + com.dslplatform.json.Configuration + + + + + -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 parameters = new ArrayList<>(len * 2); + for(World world : worlds) { + parameters.add(world.getId()); + parameters.add(world.getRandomNumber()); + } + return Mono.when(this.updateWorldQueries[len - 1].bind(parameters).execute()); + } + + @Bean( name = "dbRespository", visibility = Bean.Visibility.PRIVATE ) + public static class ReactorScope extends io.inverno.mod.base.concurrent.ReactorScope> { + + private final AppConfiguration configuration; + private final Reactor reactor; + + private Vertx vertx; + private PgConnectOptions connectOptions; + + public ReactorScope(AppConfiguration configuration, Reactor reactor) { + this.configuration = configuration; + this.reactor = reactor; + } + + @Init + public void init() { + if(this.reactor instanceof VertxReactor) { + this.vertx = ((VertxReactor)this.reactor).getVertx(); + } + else { + this.vertx = Vertx.vertx(new VertxOptions().setPreferNativeTransport(this.configuration.boot().prefer_native_transport())); + } + + this.connectOptions = new PgConnectOptions() + .setHost(this.configuration.db_host()) + .setPort(this.configuration.db_port()) + .setDatabase(this.configuration.db_database()) + .setUser(this.configuration.db_username()) + .setPassword(this.configuration.db_password()) + .setCachePreparedStatements(true) + .setPreparedStatementCacheMaxSize(1024) + .setPipeliningLimit(100_100); + } + + @Destroy + public void destroy() { + if(!(this.reactor instanceof VertxReactor)) { + this.vertx.close(); + } + } + + @Override + protected Mono create() { + return Mono.fromCompletionStage(() -> PgConnection.connect(this.vertx, this.connectOptions).toCompletionStage()) + .map(pgConn -> new DbRepository(new ConnectionSqlClient(pgConn))) + .cacheInvalidateWhen(repository -> ((ConnectionSqlClient)repository.getSqlClient()).onClose()); + } + } +} diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/JsonSerializer.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/JsonSerializer.java new file mode 100644 index 00000000000..24ce79eedd3 --- /dev/null +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/JsonSerializer.java @@ -0,0 +1,38 @@ +package com.techempower.inverno.benchmark.internal; + +import com.dslplatform.json.DslJson; +import com.dslplatform.json.JsonWriter; +import io.inverno.core.annotation.Bean; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import java.lang.reflect.Type; + +public class JsonSerializer { + + private static final DslJson DSL_JSON = new DslJson<>(); + + private final JsonWriter jsonWriter; + + public JsonSerializer() { + this.jsonWriter = DSL_JSON.newWriter(); + } + + public ByteBuf serialize(T value, Type type) { + try { + DSL_JSON.serialize(this.jsonWriter, type, value); + return Unpooled.wrappedBuffer(this.jsonWriter.toByteArray()); + } + finally { + this.jsonWriter.reset(); + } + } + + @Bean( name = "jsonSerializer", visibility = Bean.Visibility.PRIVATE ) + public static class ReactorScope extends io.inverno.mod.base.concurrent.ReactorScope { + + @Override + protected JsonSerializer create() { + return new JsonSerializer(); + } + } +} diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/SqlClientReactorScope.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/SqlClientReactorScope.java deleted file mode 100644 index d3b8a463865..00000000000 --- a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/internal/SqlClientReactorScope.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.techempower.inverno.benchmark.internal; - -import com.techempower.inverno.benchmark.AppConfiguration; - -import io.inverno.core.annotation.Bean; -import io.inverno.core.annotation.Bean.Visibility; -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.ReactorScope; -import io.inverno.mod.base.concurrent.VertxReactor; -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 reactor.core.publisher.Mono; - -@Bean( name = "SqlClient", visibility = Visibility.PRIVATE ) -public class SqlClientReactorScope extends ReactorScope> { - - private final AppConfiguration configuration; - private final Reactor reactor; - - private Vertx vertx; - private PgConnectOptions connectOptions; - - public SqlClientReactorScope(AppConfiguration configuration, Reactor reactor) { - this.configuration = configuration; - this.reactor = reactor; - } - - @Init - public void init() { - if(this.reactor instanceof VertxReactor) { - this.vertx = ((VertxReactor)this.reactor).getVertx(); - } - else { - this.vertx = Vertx.vertx(new VertxOptions().setPreferNativeTransport(this.configuration.boot().prefer_native_transport())); - } - - this.connectOptions = new PgConnectOptions() - .setHost(this.configuration.db_host()) - .setPort(this.configuration.db_port()) - .setDatabase(this.configuration.db_database()) - .setUser(this.configuration.db_username()) - .setPassword(this.configuration.db_password()) - .setCachePreparedStatements(true) - .setPipeliningLimit(100_100); - } - - @Destroy - public void destroy() { - if(!(this.reactor instanceof VertxReactor)) { - this.vertx.close(); - } - } - - @Override - protected Mono create() { - return Mono.fromCompletionStage(() -> PgConnection.connect(this.vertx, this.connectOptions).toCompletionStage()) - .map(pgConn -> (SqlClient)new ConnectionSqlClient(pgConn)) - .cacheInvalidateWhen(client -> ((ConnectionSqlClient)client).onClose()); - } -} diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/Fortune.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/Fortune.java index de4017c3c0d..b00a8a9df9f 100644 --- a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/Fortune.java +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/Fortune.java @@ -1,5 +1,8 @@ package com.techempower.inverno.benchmark.model; +import com.dslplatform.json.CompiledJson; + +@CompiledJson public final class Fortune implements Comparable { private final int id; diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/Message.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/Message.java index c9d554903d7..306d5c348d6 100644 --- a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/Message.java +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/Message.java @@ -1,5 +1,8 @@ package com.techempower.inverno.benchmark.model; +import com.dslplatform.json.CompiledJson; + +@CompiledJson public final class Message { private final String message; diff --git a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/World.java b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/World.java index f0f1825f3b9..6defeb042a3 100644 --- a/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/World.java +++ b/frameworks/Java/inverno/src/main/java/com/techempower/inverno/benchmark/model/World.java @@ -1,14 +1,15 @@ package com.techempower.inverno.benchmark.model; +import com.dslplatform.json.CompiledJson; + +@CompiledJson public final class World implements Comparable { - private final int id; + private int id; private int randomNumber; - public World(int id) { - this.id = id; - } - + public World() {} + public World(int id, int randomNumber) { this.id = id; this.randomNumber = randomNumber; diff --git a/frameworks/Java/inverno/src/main/java/module-info.java b/frameworks/Java/inverno/src/main/java/module-info.java index fefbf5c52a6..39d0224949b 100644 --- a/frameworks/Java/inverno/src/main/java/module-info.java +++ b/frameworks/Java/inverno/src/main/java/module-info.java @@ -10,16 +10,13 @@ requires io.netty.common; requires io.netty.codec.http; requires unbescape; - + requires static dsl.json; + requires io.vertx.client.sql.pg; requires io.vertx.client.sql; requires io.vertx.core; requires java.sql; - - //requires transitive io.netty.transport; - //requires static io.netty.transport.unix.common; - //requires static io.netty.transport.epoll; - + exports com.techempower.inverno.benchmark; exports com.techempower.inverno.benchmark.model; }