Browse Source

Add undertow variant that uses reactive-pg-client (#3582)

Michael Hixson 7 years ago
parent
commit
060c7bc227

+ 21 - 0
frameworks/Java/undertow/benchmark_config.json

@@ -62,6 +62,27 @@
       "notes": "",
       "notes": "",
       "versus": ""
       "versus": ""
     },
     },
+    "postgresql-async" : {
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "Postgres",
+      "framework": "None",
+      "language": "Java",
+      "flavor": "None",
+      "orm": "Raw",
+      "platform": "Undertow",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "undertow-postgresql-reactive",
+      "notes": "",
+      "versus": ""
+    },
     "mongodb" : {
     "mongodb" : {
       "db_url": "/db",
       "db_url": "/db",
       "query_url": "/queries?queries=",
       "query_url": "/queries?queries=",

+ 13 - 1
frameworks/Java/undertow/pom.xml

@@ -14,13 +14,15 @@
     <maven.compiler.source>10</maven.compiler.source>
     <maven.compiler.source>10</maven.compiler.source>
     <maven.compiler.target>10</maven.compiler.target>
     <maven.compiler.target>10</maven.compiler.target>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    <hikaricp.version>3.0.0</hikaricp.version>
+    <hikaricp.version>3.1.0</hikaricp.version>
     <jackson.version>2.9.5</jackson.version>
     <jackson.version>2.9.5</jackson.version>
+    <jaxb.version>2.3.0</jaxb.version>
     <maven-shade-plugin.version>3.1.1</maven-shade-plugin.version>
     <maven-shade-plugin.version>3.1.1</maven-shade-plugin.version>
     <mongodb.version>3.6.3</mongodb.version>
     <mongodb.version>3.6.3</mongodb.version>
     <mustache.version>0.9.5</mustache.version>
     <mustache.version>0.9.5</mustache.version>
     <mysql.version>5.1.46</mysql.version>
     <mysql.version>5.1.46</mysql.version>
     <postgresql.version>42.2.2</postgresql.version>
     <postgresql.version>42.2.2</postgresql.version>
+    <reactive-pg-client.version>0.7.0</reactive-pg-client.version>
     <undertow.version>2.0.4.Final</undertow.version>
     <undertow.version>2.0.4.Final</undertow.version>
     <versions-maven-plugin.version>2.5</versions-maven-plugin.version>
     <versions-maven-plugin.version>2.5</versions-maven-plugin.version>
   </properties>
   </properties>
@@ -80,6 +82,16 @@
       <artifactId>compiler</artifactId>
       <artifactId>compiler</artifactId>
       <version>${mustache.version}</version>
       <version>${mustache.version}</version>
     </dependency>
     </dependency>
+    <dependency>
+      <groupId>io.reactiverse</groupId>
+      <artifactId>reactive-pg-client</artifactId>
+      <version>${reactive-pg-client.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>javax.xml.bind</groupId>
+      <artifactId>jaxb-api</artifactId>
+      <version>${jaxb.version}</version>
+    </dependency>
   </dependencies>
   </dependencies>
 
 
   <build>
   <build>

+ 44 - 0
frameworks/Java/undertow/src/main/java/hello/DbPgAsyncHandler.java

@@ -0,0 +1,44 @@
+package hello;
+
+import static hello.Helper.randomWorldNumber;
+import static hello.Helper.sendException;
+import static hello.Helper.sendJson;
+
+import io.reactiverse.pgclient.PgClient;
+import io.reactiverse.pgclient.PgResult;
+import io.reactiverse.pgclient.Row;
+import io.reactiverse.pgclient.Tuple;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.vertx.core.AsyncResult;
+import java.util.Objects;
+
+/**
+ * Handles the single-query database test using PostgreSQL with an asynchronous
+ * API.
+ */
+final class DbPgAsyncHandler implements HttpHandler {
+  private final PgClient client;
+
+  DbPgAsyncHandler(PgClient client) {
+    this.client = Objects.requireNonNull(client);
+  }
+
+  @Override
+  public void handleRequest(HttpServerExchange exchange) {
+    client.preparedQuery(
+        "SELECT id, randomnumber FROM World WHERE id = $1",
+        Tuple.of(randomWorldNumber()),
+        (AsyncResult<PgResult<Row>> result) -> {
+          if (result.failed()) {
+            sendException(exchange, result.cause());
+          } else {
+            Row row = result.result().iterator().next();
+            int id = row.getInteger(0);
+            int randomNumber = row.getInteger(1);
+            World world = new World(id, randomNumber);
+            sendJson(exchange, world);
+          }
+        });
+  }
+}

+ 46 - 0
frameworks/Java/undertow/src/main/java/hello/FortunesPgAsyncHandler.java

@@ -0,0 +1,46 @@
+package hello;
+
+import static hello.Helper.sendException;
+import static hello.Helper.sendHtml;
+
+import io.reactiverse.pgclient.PgClient;
+import io.reactiverse.pgclient.PgResult;
+import io.reactiverse.pgclient.Row;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.vertx.core.AsyncResult;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Handles the fortunes test using PostgreSQL with an asynchronous API.
+ */
+final class FortunesPgAsyncHandler implements HttpHandler {
+  private final PgClient client;
+
+  FortunesPgAsyncHandler(PgClient client) {
+    this.client = Objects.requireNonNull(client);
+  }
+
+  @Override
+  public void handleRequest(HttpServerExchange exchange) {
+    client.preparedQuery(
+        "SELECT id, message FROM Fortune",
+        (AsyncResult<PgResult<Row>> result) -> {
+          if (result.failed()) {
+            sendException(exchange, result.cause());
+          } else {
+            List<Fortune> fortunes = new ArrayList<>();
+            for (Row row : result.result()) {
+              int id = row.getInteger(0);
+              String message = row.getString(1);
+              fortunes.add(new Fortune(id, message));
+            }
+            fortunes.add(new Fortune(0, "Additional fortune added at request time."));
+            fortunes.sort(null);
+            sendHtml(exchange, fortunes, "hello/fortunes.mustache");
+          }
+        });
+  }
+}

+ 26 - 0
frameworks/Java/undertow/src/main/java/hello/HelloWebServer.java

@@ -10,6 +10,8 @@ import com.mongodb.connection.ClusterSettings;
 import com.mongodb.connection.ConnectionPoolSettings;
 import com.mongodb.connection.ConnectionPoolSettings;
 import com.zaxxer.hikari.HikariConfig;
 import com.zaxxer.hikari.HikariConfig;
 import com.zaxxer.hikari.HikariDataSource;
 import com.zaxxer.hikari.HikariDataSource;
+import io.reactiverse.pgclient.PgClient;
+import io.reactiverse.pgclient.PgPoolOptions;
 import io.undertow.Undertow;
 import io.undertow.Undertow;
 import io.undertow.UndertowOptions;
 import io.undertow.UndertowOptions;
 import io.undertow.server.HttpHandler;
 import io.undertow.server.HttpHandler;
@@ -97,6 +99,30 @@ public final class HelloWebServer {
       return handler;
       return handler;
     }),
     }),
 
 
+    /**
+     * The server will use a PostgreSQL database with an asynchronous API and
+     * will only implement the test types that require a database.
+     */
+    POSTGRESQL_ASYNC(() -> {
+      var options = new PgPoolOptions();
+      options.setHost("tfb-database");
+      options.setPort(5432);
+      options.setDatabase("hello_world");
+      options.setUsername("benchmarkdbuser");
+      options.setPassword("benchmarkdbpass");
+      options.setCachePreparedStatements(true);
+      options.setMaxSize(1); // Without this, the updates test breaks.
+
+      var client = PgClient.pool(options);
+
+      var handler = new PathHandler();
+      handler.addExactPath("/db", new AsyncHandler(new DbPgAsyncHandler(client)));
+      handler.addExactPath("/queries", new AsyncHandler(new QueriesPgAsyncHandler(client)));
+      handler.addExactPath("/fortunes", new AsyncHandler(new FortunesPgAsyncHandler(client)));
+      handler.addExactPath("/updates", new AsyncHandler(new UpdatesPgAsyncHandler(client)));
+      return handler;
+    }),
+
     /**
     /**
      * The server will use a MongoDB database and will only implement the test
      * The server will use a MongoDB database and will only implement the test
      * types that require a database.
      * types that require a database.

+ 76 - 0
frameworks/Java/undertow/src/main/java/hello/QueriesPgAsyncHandler.java

@@ -0,0 +1,76 @@
+package hello;
+
+import static hello.Helper.getQueries;
+import static hello.Helper.randomWorldNumber;
+import static hello.Helper.sendException;
+import static hello.Helper.sendJson;
+
+import io.reactiverse.pgclient.PgClient;
+import io.reactiverse.pgclient.PgResult;
+import io.reactiverse.pgclient.Row;
+import io.reactiverse.pgclient.Tuple;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.vertx.core.AsyncResult;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Handles the multi-query database test using PostgreSQL with an asynchronous
+ * API.
+ */
+final class QueriesPgAsyncHandler implements HttpHandler {
+  private final PgClient client;
+
+  QueriesPgAsyncHandler(PgClient client) {
+    this.client = Objects.requireNonNull(client);
+  }
+
+  @Override
+  public void handleRequest(HttpServerExchange exchange) {
+    int queries = getQueries(exchange);
+    nWorlds(queries).whenComplete(
+        (worlds, exception) -> {
+          if (exception != null) {
+            sendException(exchange, exception);
+          } else {
+            sendJson(exchange, worlds);
+          }
+        });
+  }
+
+  private CompletableFuture<World[]> nWorlds(int n) {
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    CompletableFuture<World>[] futures = new CompletableFuture[n];
+    for (int i = 0; i < futures.length; i++) {
+      futures[i] = oneWorld();
+    }
+    return CompletableFuture.allOf(futures).thenApply(
+        nil -> {
+          World[] worlds = new World[futures.length];
+          for (int i = 0; i < futures.length; i++) {
+            worlds[i] = futures[i].join();
+          }
+          return worlds;
+        });
+  }
+
+  private CompletableFuture<World> oneWorld() {
+    CompletableFuture<World> future = new CompletableFuture<>();
+    client.preparedQuery(
+        "SELECT id, randomnumber FROM World WHERE id = $1",
+        Tuple.of(randomWorldNumber()),
+        (AsyncResult<PgResult<Row>> result) -> {
+          if (result.failed()) {
+            future.completeExceptionally(result.cause());
+          } else {
+            Row row = result.result().iterator().next();
+            int id = row.getInteger(0);
+            int randomNumber = row.getInteger(1);
+            World world = new World(id, randomNumber);
+            future.complete(world);
+          }
+        });
+    return future;
+  }
+}

+ 100 - 0
frameworks/Java/undertow/src/main/java/hello/UpdatesPgAsyncHandler.java

@@ -0,0 +1,100 @@
+package hello;
+
+import static hello.Helper.getQueries;
+import static hello.Helper.randomWorldNumber;
+import static hello.Helper.sendException;
+import static hello.Helper.sendJson;
+
+import io.reactiverse.pgclient.PgClient;
+import io.reactiverse.pgclient.PgResult;
+import io.reactiverse.pgclient.Row;
+import io.reactiverse.pgclient.Tuple;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.vertx.core.AsyncResult;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Handles the updates test using PostgreSQL with an asynchronous API.
+ */
+final class UpdatesPgAsyncHandler implements HttpHandler {
+  private final PgClient client;
+
+  UpdatesPgAsyncHandler(PgClient client) {
+    this.client = Objects.requireNonNull(client);
+  }
+
+  @Override
+  public void handleRequest(HttpServerExchange exchange) {
+    int queries = getQueries(exchange);
+    nUpdatedWorlds(queries).whenComplete(
+        (worlds, exception) -> {
+          if (exception != null) {
+            sendException(exchange, exception);
+          } else {
+            sendJson(exchange, worlds);
+          }
+        });
+  }
+
+  private CompletableFuture<World[]> nUpdatedWorlds(int n) {
+    return nWorlds(n).thenCompose(
+        worlds -> {
+          List<Tuple> writes = new ArrayList<>(worlds.length);
+          for (World world : worlds) {
+            world.randomNumber = randomWorldNumber();
+            writes.add(Tuple.of(world.randomNumber, world.id));
+          }
+          CompletableFuture<World[]> next = new CompletableFuture<>();
+          client.preparedBatch(
+              "UPDATE world SET randomnumber = $1 WHERE id = $2",
+              writes,
+              (AsyncResult<PgResult<Row>> result) -> {
+                if (result.failed()) {
+                  next.completeExceptionally(result.cause());
+                } else {
+                  next.complete(worlds);
+                }
+              });
+          return next;
+        });
+  }
+
+  private CompletableFuture<World[]> nWorlds(int n) {
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    CompletableFuture<World>[] futures = new CompletableFuture[n];
+    for (int i = 0; i < futures.length; i++) {
+      futures[i] = oneWorld();
+    }
+    return CompletableFuture.allOf(futures).thenApply(
+        nil -> {
+          World[] worlds = new World[futures.length];
+          for (int i = 0; i < futures.length; i++) {
+            worlds[i] = futures[i].join();
+          }
+          return worlds;
+        });
+  }
+
+  private CompletableFuture<World> oneWorld() {
+    CompletableFuture<World> future = new CompletableFuture<>();
+    client.preparedQuery(
+        "SELECT id, randomnumber FROM World WHERE id = $1",
+        Tuple.of(randomWorldNumber()),
+        (AsyncResult<PgResult<Row>> result) -> {
+          if (result.failed()) {
+            future.completeExceptionally(result.cause());
+          } else {
+            Row row = result.result().iterator().next();
+            int id = row.getInteger(0);
+            int randomNumber = row.getInteger(1);
+            World world = new World(id, randomNumber);
+            future.complete(world);
+          }
+        });
+    return future;
+  }
+}

+ 10 - 0
frameworks/Java/undertow/undertow-postgresql-async.dockerfile

@@ -0,0 +1,10 @@
+FROM maven:3.5.3-jdk-10-slim as maven
+WORKDIR /undertow
+COPY pom.xml pom.xml
+COPY src src
+RUN mvn clean package -q
+
+FROM openjdk:10-jre-slim
+WORKDIR /undertow
+COPY --from=maven /undertow/target/app.jar app.jar
+CMD ["java", "-jar", "app.jar", "POSTGRESQL_ASYNC"]