Pārlūkot izejas kodu

Update vertxweb (#2970)

* Update vertx dependencies

* increase number of max connections in the pool

* use plain pojo and rely on jackson for serialization

* the updated db driver does not support mysql

* update startup and readme to reflect the current code

* Fix susom benchmark due to app update
Paulo Lopes 8 gadi atpakaļ
vecāks
revīzija
de161e8bf6

+ 4 - 4
frameworks/Java/vertx-web/Readme.md

@@ -17,20 +17,20 @@ http://localhost:8080/json
 http://localhost:8080/mongo/db
 http://localhost:8080/mongo/queries?queries=5
 
-http://localhost:8080/jdbc/db
-http://localhost:8080/jdbc/queries?queries=5
+http://localhost:8080/psql/db
+http://localhost:8080/psql/queries?queries=5
 
 ### Data-Store/Database Update Test
 
 http://localhost:8080/mongo/update?queries=5
 
-http://localhost:8080/jdbc/update?queries=5
+http://localhost:8080/psql/update?queries=5
 
 ### Template rendering Test
 
 http://localhost:8080/mongo/fortunes
 
-http://localhost:8080/jdbc/fortunes
+http://localhost:8080/psql/fortunes
 
 ## Generating Load
 It's best to generate load from a completely separate machine from the server if you can, to avoid resource contention

+ 0 - 22
frameworks/Java/vertx-web/benchmark_config.json

@@ -43,28 +43,6 @@
       "notes": "",
       "versus": ""
     },
-    "mysql": {
-      "setup_file": "setup_mysql",
-      "db_url": "/mysql/db",
-      "query_url": "/mysql/queries?queries=",
-      "fortune_url": "/mysql/fortunes",
-      "update_url": "/mysql/update?queries=",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Micro",
-      "database": "MySQL",
-      "framework": "vertx-web",
-      "language": "Java",
-      "flavor": "None",
-      "orm": "Raw",
-      "platform": "vertx",
-      "webserver": "None",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "vertx-web-mysql",
-      "notes": "",
-      "versus": ""
-    },
     "postgres": {
       "setup_file": "setup_postgresql",
       "db_url": "/psql/db",

+ 15 - 10
frameworks/Java/vertx-web/pom.xml

@@ -6,44 +6,43 @@
   <modelVersion>4.0.0</modelVersion>
 
   <groupId>io.vertx</groupId>
-  <artifactId>vertx-benchmark</artifactId>
-  <version>1.0.0-SNAPSHOT</version>
+  <artifactId>vertx-web-benchmark</artifactId>
+  <version>3.5.0.Beta1</version>
 
   <properties>
     <!-- the main class -->
     <main.verticle>io.vertx.benchmark.App</main.verticle>
-    <vertx.version>3.3.2</vertx.version>
   </properties>
 
   <dependencies>
     <dependency>
       <groupId>io.vertx</groupId>
       <artifactId>vertx-core</artifactId>
-      <version>${vertx.version}</version>
+      <version>${project.version}</version>
     </dependency>
 
     <dependency>
       <groupId>io.vertx</groupId>
       <artifactId>vertx-web</artifactId>
-      <version>${vertx.version}</version>
+      <version>${project.version}</version>
     </dependency>
 
     <dependency>
       <groupId>io.vertx</groupId>
       <artifactId>vertx-mongo-client</artifactId>
-      <version>${vertx.version}</version>
+      <version>${project.version}</version>
     </dependency>
 
     <dependency>
-      <groupId>io.vertx</groupId>
-      <artifactId>vertx-mysql-postgresql-client</artifactId>
-      <version>${vertx.version}</version>
+      <groupId>com.julienviet</groupId>
+      <artifactId>vertx-pg-client</artifactId>
+      <version>0.3.0</version>
     </dependency>
 
     <dependency>
       <groupId>io.vertx</groupId>
       <artifactId>vertx-web-templ-handlebars</artifactId>
-      <version>${vertx.version}</version>
+      <version>${project.version}</version>
     </dependency>
 
     <dependency>
@@ -58,6 +57,12 @@
       <version>42.1.1</version>
     </dependency>
 
+    <dependency>
+      <groupId>com.fasterxml.jackson.module</groupId>
+      <artifactId>jackson-module-afterburner</artifactId>
+      <version>2.7.4</version>
+    </dependency>
+
   </dependencies>
 
   <build>

+ 18 - 1
frameworks/Java/vertx-web/setup.sh

@@ -6,4 +6,21 @@ fw_depends java maven
 
 mvn clean package
 
-java -server -XX:+UseNUMA -XX:+UseParallelGC -XX:+AggressiveOpts -Dvertx.disableWebsockets=true -Dvertx.flashPolicyHandler=false -Dvertx.threadChecks=false -Dvertx.disableContextTimings=true -Dvertx.disableTCCL=true -jar target/vertx-benchmark-1.0.0-SNAPSHOT-fat.jar --instances `grep --count ^processor /proc/cpuinfo` --conf src/main/conf/config.json &
+java \
+  -server                                           \
+  -XX:+UseNUMA                                      \
+  -XX:+UseParallelGC                                \
+  -XX:+AggressiveOpts                               \
+  -Dvertx.disableMetrics=true                       \
+  -Dvertx.disableH2c=true                           \
+  -Dvertx.disableWebsockets=true                    \
+  -Dvertx.flashPolicyHandler=false                  \
+  -Dvertx.threadChecks=false                        \
+  -Dvertx.disableContextTimings=true                \
+  -Dvertx.disableTCCL=true                          \
+  -jar                                              \
+  target/vertx-web-benchmark-3.5.0.Beta1-fat.jar    \
+  --instances                                       \
+  `grep --count ^processor /proc/cpuinfo`           \
+  --conf                                            \
+  src/main/conf/config.json &

+ 0 - 5
frameworks/Java/vertx-web/setup_mysql.sh

@@ -1,5 +0,0 @@
-#!/bin/bash
-
-fw_depends mysql
-
-source ./setup.sh

+ 1 - 1
frameworks/Java/vertx-web/src/main/conf/config.json

@@ -6,5 +6,5 @@
   "username": "benchmarkdbuser",
   "password": "benchmarkdbpass",
   "database": "hello_world",
-  "maxPoolSize": 32
+  "maxPoolSize": 64
 }

+ 191 - 257
frameworks/Java/vertx-web/src/main/java/io/vertx/benchmark/App.java

@@ -1,23 +1,21 @@
 package io.vertx.benchmark;
 
+import com.fasterxml.jackson.module.afterburner.AfterburnerModule;
 import com.github.susom.database.Config;
 import com.github.susom.database.ConfigFrom;
 import com.github.susom.database.DatabaseProviderVertx;
 import com.github.susom.database.DatabaseProviderVertx.Builder;
-import com.github.susom.database.Sql;
 import com.github.susom.database.SqlInsert;
+import com.julienviet.pgclient.*;
 import io.vertx.benchmark.model.Fortune;
 import io.vertx.benchmark.model.Message;
 import io.vertx.benchmark.model.World;
 import io.vertx.core.*;
 import io.vertx.core.http.HttpHeaders;
+import io.vertx.core.json.Json;
 import io.vertx.core.json.JsonArray;
 import io.vertx.core.json.JsonObject;
-import io.vertx.ext.asyncsql.AsyncSQLClient;
-import io.vertx.ext.asyncsql.MySQLClient;
-import io.vertx.ext.asyncsql.PostgreSQLClient;
 import io.vertx.ext.mongo.MongoClient;
-import io.vertx.ext.sql.SQLConnection;
 import io.vertx.ext.web.Router;
 import io.vertx.ext.web.RoutingContext;
 import io.vertx.ext.web.templ.HandlebarsTemplateEngine;
@@ -26,19 +24,26 @@ import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 
+import static io.vertx.benchmark.Helper.randomWorld;
+
 public class App extends AbstractVerticle {
 
+  static {
+    Json.mapper.registerModule(new AfterburnerModule());
+    Json.prettyMapper.registerModule(new AfterburnerModule());
+  }
+
   /**
    * MongoDB implementation
    */
-  private final class MongoDB {
+  private final class MongoDBBenchmark {
     private final JsonObject FIELDS = new JsonObject().put("_id", 0);
 
     private final MongoClient database;
     // In order to use a template we first need to create an engine
     private final HandlebarsTemplateEngine engine;
 
-    public MongoDB(Vertx vertx, JsonObject config) {
+    public MongoDBBenchmark(Vertx vertx, JsonObject config) {
       final JsonObject mongoConfig = config.copy();
 
       // mongo is configured without credentials
@@ -50,7 +55,7 @@ public class App extends AbstractVerticle {
     }
 
     public final void dbHandler(final RoutingContext ctx) {
-      database.findOne("world", new JsonObject().put("_id", Helper.randomWorld()), FIELDS, findOne -> {
+      database.findOne("world", new JsonObject().put("_id", randomWorld()), FIELDS, findOne -> {
         if (findOne.failed()) {
           ctx.fail(findOne.cause());
           return;
@@ -60,7 +65,7 @@ public class App extends AbstractVerticle {
             .putHeader(HttpHeaders.SERVER, SERVER)
             .putHeader(HttpHeaders.DATE, date)
             .putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
-            .end(new World(findOne.result()).encode());
+            .end(Json.encodeToBuffer(new World(findOne.result())));
       });
     }
 
@@ -78,13 +83,13 @@ public class App extends AbstractVerticle {
                 .putHeader(HttpHeaders.SERVER, SERVER)
                 .putHeader(HttpHeaders.DATE, date)
                 .putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
-                .end(new JsonArray(Arrays.asList(worlds)).encode());
+                .end(Json.encodeToBuffer(worlds));
 
           } else {
 
             final Handler<Integer> self = this;
 
-            database.findOne("world", new JsonObject().put("_id", Helper.randomWorld()), FIELDS, findOne -> {
+            database.findOne("world", new JsonObject().put("_id", randomWorld()), FIELDS, findOne -> {
               if (findOne.failed()) {
                 ctx.fail(findOne.cause());
                 return;
@@ -144,13 +149,13 @@ public class App extends AbstractVerticle {
                 .putHeader(HttpHeaders.SERVER, SERVER)
                 .putHeader(HttpHeaders.DATE, date)
                 .putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
-                .end(new JsonArray(Arrays.asList(worlds)).encode());
+                .end(Json.encodeToBuffer(worlds));
 
           } else {
 
             final Handler<Integer> self = this;
 
-            final int id = Helper.randomWorld();
+            final int id = randomWorld();
 
             final JsonObject query = new JsonObject().put("_id", id);
 
@@ -160,7 +165,7 @@ public class App extends AbstractVerticle {
                 return;
               }
 
-              final int newRandomNumber = Helper.randomWorld();
+              final int newRandomNumber = randomWorld();
 
               database.update("world", query, new JsonObject().put("$set", new JsonObject().put("randomNumber", newRandomNumber)), update -> {
                 if (update.failed()) {
@@ -180,280 +185,214 @@ public class App extends AbstractVerticle {
   }
 
   /**
-   * JDBC implementation
+   * PgClient implementation
    */
-  private final class AsyncSQL {
+  private final class PgClientBenchmark {
 
-    public static final int MYSQL = 0;
-    public static final int POSTGRES = 1;
+    private static final String UPDATE_WORLD = "UPDATE world SET randomnumber=$1 WHERE id=$2";
+    private static final String SELECT_WORLD = "SELECT id, randomnumber from WORLD where id=$1";
+    private static final String SELECT_FORTUNE = "SELECT id, message from FORTUNE";
 
-    private final AsyncSQLClient database;
-    private final int dbms;
+    private final PgClient client;
+    private final PgConnectionPool pool;
 
     // In order to use a template we first need to create an engine
     private final HandlebarsTemplateEngine engine;
 
-    public AsyncSQL(Vertx vertx, int driver, JsonObject config) {
-      switch (driver) {
-        case MYSQL:
-          this.database = MySQLClient.createNonShared(vertx, config);
-          this.dbms = MYSQL;
-          break;
-        case POSTGRES:
-          this.database = PostgreSQLClient.createNonShared(vertx, config);
-          this.dbms = POSTGRES;
-          break;
-        default:
-          throw new RuntimeException("Unsupported DB");
-      }
+    public PgClientBenchmark(Vertx vertx, JsonObject config) {
+      PgClientOptions options = new PgClientOptions();
+      options.setDatabase(config.getString("database"));
+      options.setHost(config.getString("host"));
+      options.setPort(config.getInteger("port", 5432));
+      options.setUsername(config.getString("username"));
+      options.setPassword(config.getString("password"));
+      options.setCachePreparedStatements(true);
+      client = PgClient.create(vertx, options);
+      pool = client.createPool(new PgPoolOptions().setMode(PoolingMode.STATEMENT));
       this.engine = HandlebarsTemplateEngine.create();
     }
 
     public final void dbHandler(final RoutingContext ctx) {
-      database.getConnection(getConnection -> {
+      pool.getConnection(getConnection -> {
         if (getConnection.failed()) {
           ctx.fail(getConnection.cause());
           return;
         }
 
-        final SQLConnection conn = getConnection.result();
+        final PgConnection conn = getConnection.result();
+        final PgPreparedStatement worldSelect = conn.prepare(SELECT_WORLD);
 
-        conn.query("SELECT id, randomnumber from WORLD where id = " + Helper.randomWorld(), query -> {
-          // free the connection
+        worldSelect.query(randomWorld()).execute(res -> {
           conn.close();
-
-          if (query.failed()) {
-            ctx.fail(query.cause());
-            return;
-          }
-
-          final List<JsonArray> resultSet = query.result().getResults();
-
-          if (resultSet == null || resultSet.size() == 0) {
-            ctx.fail(404);
-            return;
-          }
-
-          final JsonArray row = resultSet.get(0);
-
-          ctx.response()
+          if (res.succeeded()) {
+            final List<JsonArray> resultSet = res.result().getResults();
+            if (resultSet.isEmpty()) {
+              ctx.response()
+                .setStatusCode(404)
+                .end();
+              return;
+            }
+            final JsonArray row = resultSet.get(0);
+            ctx.response()
               .putHeader(HttpHeaders.SERVER, SERVER)
               .putHeader(HttpHeaders.DATE, date)
               .putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
-              .end(new World(row.getInteger(0), row.getInteger(1)).encode());
+              .end(Json.encodeToBuffer(new World(row.getInteger(0), row.getInteger(1))));
+          } else {
+            ctx.fail(res.cause());
+          }
         });
       });
     }
 
     public final void queriesHandler(final RoutingContext ctx) {
-      final int queries = Helper.getQueries(ctx.request());
-      final JsonArray worlds = new JsonArray();
 
-      database.getConnection(getConnection -> {
+      pool.getConnection(getConnection -> {
         if (getConnection.failed()) {
           ctx.fail(getConnection.cause());
           return;
         }
 
-        final SQLConnection conn = getConnection.result();
+        final PgConnection conn = getConnection.result();
+        final PgPreparedStatement worldSelect = conn.prepare(SELECT_WORLD);
+        final int queries = Helper.getQueries(ctx.request());
+        final World[] worlds = new World[queries];
+        final boolean[] failed = { false };
+        final int[] cnt = { 0 };
+
+        for (int i = 0; i < queries; i++) {
+          worldSelect.query(randomWorld()).execute(ar -> {
+            if (!failed[0]) {
+              if (ar.failed()) {
+                conn.close();
+                failed[0] = true;
+                ctx.fail(ar.cause());
+                return;
+              }
+
+              // we need a final reference
+              final JsonArray row = ar.result().getResults().get(0);
+              worlds[cnt[0]++] = new World(row.getInteger(0), row.getInteger(1));
 
-        new Handler<Integer>() {
-          @Override
-          public void handle(Integer idx) {
-            if (idx == queries) {
               // stop condition
-              ctx.response()
+              if (cnt[0] == queries) {
+                conn.close();
+                ctx.response()
                   .putHeader(HttpHeaders.SERVER, SERVER)
                   .putHeader(HttpHeaders.DATE, date)
                   .putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
-                  .end(worlds.encode());
-
-              conn.close();
-            } else {
-
-              final Handler<Integer> self = this;
-
-              conn.query("SELECT id, randomnumber from WORLD where id = " + Helper.randomWorld(), query -> {
-                if (query.failed()) {
-                  ctx.fail(query.cause());
-                  conn.close();
-                  return;
-                }
-
-                final List<JsonArray> resultSet = query.result().getResults();
-
-                if (resultSet == null || resultSet.size() == 0) {
-                  ctx.fail(404);
-                  conn.close();
-                  return;
-                }
-
-                final JsonArray row = resultSet.get(0);
-
-                worlds.add(new World(row.getInteger(0), row.getInteger(1)));
-                self.handle(idx + 1);
-              });
+                  .end(Json.encodeToBuffer(worlds));
+              }
             }
-          }
-        }.handle(0);
+          });
+        }
       });
     }
 
     public final void fortunesHandler(final RoutingContext ctx) {
-      final List<Fortune> fortunes = new LinkedList<>();
 
-      database.getConnection(getConnection -> {
+      pool.getConnection(getConnection -> {
         if (getConnection.failed()) {
           ctx.fail(getConnection.cause());
           return;
         }
 
-        final SQLConnection conn = getConnection.result();
-
-        conn.query("SELECT id, message from FORTUNE", query -> {
-          // free the connection
+        final PgConnection conn = getConnection.result();
+        final PgPreparedStatement fortuneSelect = conn.prepare(SELECT_FORTUNE);
+        fortuneSelect.query().execute(ar -> {
           conn.close();
+          if (ar.succeeded()) {
+            final List<JsonArray> resultSet = ar.result().getResults();
+            if (resultSet == null || resultSet.size() == 0) {
+              ctx.fail(404);
+              return;
+            }
 
-          if (query.failed()) {
-            ctx.fail(query.cause());
-            return;
-          }
-
-          final List<JsonArray> resultSet = query.result().getResults();
-
-          if (resultSet == null || resultSet.size() == 0) {
-            ctx.fail(404);
-            return;
-          }
+            final List<Fortune> fortunes = new ArrayList<>(resultSet.size() + 1);
 
-          for (JsonArray row : resultSet) {
-            fortunes.add(new Fortune(row.getInteger(0), row.getString(1)));
-          }
+            for (JsonArray row : resultSet) {
+              fortunes.add(new Fortune(row.getInteger(0), row.getString(1)));
+            }
 
-          fortunes.add(new Fortune(0, "Additional fortune added at request time."));
-          Collections.sort(fortunes);
+            fortunes.add(new Fortune(0, "Additional fortune added at request time."));
+            Collections.sort(fortunes);
 
-          ctx.put("fortunes", fortunes);
+            ctx.put("fortunes", fortunes);
 
-          // and now delegate to the engine to render it.
-          engine.render(ctx, "templates/fortunes.hbs", res -> {
-            if (res.succeeded()) {
-              ctx.response()
-                  .putHeader(HttpHeaders.SERVER, SERVER)
-                  .putHeader(HttpHeaders.DATE, date)
-                  .putHeader(HttpHeaders.CONTENT_TYPE, "text/html; charset=UTF-8")
-                  .end(res.result());
-            } else {
-              ctx.fail(res.cause());
-            }
-          });
+            // and now delegate to the engine to render it.
+            engine.render(ctx, "templates", "/fortunes.hbs", res -> {
+              if (res.succeeded()) {
+                ctx.response()
+                        .putHeader(HttpHeaders.SERVER, SERVER)
+                        .putHeader(HttpHeaders.DATE, date)
+                        .putHeader(HttpHeaders.CONTENT_TYPE, "text/html; charset=UTF-8")
+                        .end(res.result());
+              } else {
+                ctx.fail(res.cause());
+              }
+            });
+          } else {
+            ctx.fail(ar.cause());
+          }
         });
       });
     }
 
     public final void updateHandler(final RoutingContext ctx) {
-      final int queries = Helper.getQueries(ctx.request());
-      final JsonArray worlds = new JsonArray();
 
-      final StringBuffer batch;
-
-      if (dbms == POSTGRES) {
-        // Postgres can batch queries
-        batch = new StringBuffer();
-      } else {
-        batch = null;
-      }
-
-      database.getConnection(getConnection -> {
+      pool.getConnection(getConnection -> {
         if (getConnection.failed()) {
           ctx.fail(getConnection.cause());
           return;
         }
 
-        final SQLConnection conn = getConnection.result();
-
-        new Handler<Integer>() {
-          @Override
-          public void handle(Integer idx) {
-            if (idx == queries) {
-              switch (dbms) {
-                case MYSQL:
-                  ctx.response()
-                      .putHeader(HttpHeaders.SERVER, SERVER)
-                      .putHeader(HttpHeaders.DATE, date)
-                      .putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
-                      .end(worlds.encode());
-
-                  conn.close();
-                  break;
-                case POSTGRES:
-                  // stop condition, first run the batch update
-                  conn.update(batch.toString(), update -> {
-                    if (update.failed()) {
-                      ctx.fail(update.cause());
-                      conn.close();
-                      return;
-                    }
-                    ctx.response()
-                        .putHeader(HttpHeaders.SERVER, SERVER)
-                        .putHeader(HttpHeaders.DATE, date)
-                        .putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
-                        .end(worlds.encode());
-
-                    conn.close();
-                  });
-                  break;
+        final PgConnection conn = getConnection.result();
+
+        final PgPreparedStatement worldSelect = conn.prepare(SELECT_WORLD);
+        final int queries = Helper.getQueries(ctx.request());
+        final World[] worlds = new World[queries];
+        final boolean[] failed = { false };
+        final int[] queryCount = { 0 };
+
+        for (int i = 0; i < worlds.length; i++) {
+          int id = randomWorld();
+          worldSelect.query(id).execute(ar2 -> {
+            if (!failed[0]) {
+              if (ar2.failed()) {
+                failed[0] = true;
+                conn.close();
+                ctx.fail(ar2.cause());
+                return;
               }
-            } else {
-
-              final Handler<Integer> self = this;
-              final int id = Helper.randomWorld();
 
-              conn.query("SELECT id, randomnumber from WORLD where id = " + id, query -> {
-                if (query.failed()) {
-                  ctx.fail(query.cause());
-                  conn.close();
-                  return;
-                }
+              final JsonArray row = ar2.result().getResults().get(0);
+              worlds[queryCount[0]++] = new World(row.getInteger(0), randomWorld());
 
-                final List<JsonArray> resultSet = query.result().getResults();
+              if (queryCount[0] == worlds.length) {
+                Arrays.sort(worlds);
+                PgPreparedStatement worldUpdate = conn.prepare(UPDATE_WORLD);
+                PgBatch batch = worldUpdate.batch();
 
-                if (resultSet == null || resultSet.size() == 0) {
-                  ctx.fail(404);
-                  conn.close();
-                  return;
+                for (World world : worlds) {
+                  batch.add(world.getRandomNumber(), world.getId());
                 }
+                batch.execute(ar3 -> {
+                  conn.close();
+                  if (ar3.failed()) {
+                    ctx.fail(ar3.cause());
+                    return;
+                  }
 
-                final int newRandomNumber = Helper.randomWorld();
-                worlds.add(new World(id, newRandomNumber));
-
-                switch (dbms) {
-                  case MYSQL:
-                    conn.update("UPDATE WORLD SET randomnumber = " + newRandomNumber + " WHERE id = " + id, update -> {
-                      if (update.failed()) {
-                        ctx.fail(update.cause());
-                        conn.close();
-                        return;
-                      }
-
-                      self.handle(idx + 1);
-                    });
-                    break;
-                  case POSTGRES:
-                    batch
-                        .append("UPDATE WORLD SET randomnumber = ")
-                        .append(newRandomNumber)
-                        .append(" WHERE id = ")
-                        .append(id)
-                        .append("; ");
-
-                    self.handle(idx + 1);
-                    break;
-                }
-              });
+                  ctx.response()
+                    .putHeader(HttpHeaders.SERVER, SERVER)
+                    .putHeader(HttpHeaders.DATE, date)
+                    .putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
+                    .end(Json.encodeToBuffer(worlds));
+                });
+              }
             }
-          }
-        }.handle(0);
+          });
+        }
       });
     }
   }
@@ -461,13 +400,13 @@ public class App extends AbstractVerticle {
   /**
    * Implementation using com.github.susom:database library and standard JDBC driver.
    */
-  private final class DatabaseSql {
+  private final class DatabaseSqlBenchmark {
     private final Builder dbBuilder;
 
     // In order to use a template we first need to create an engine
     private final HandlebarsTemplateEngine engine;
 
-    DatabaseSql(Vertx vertx, JsonObject jsonConfig) {
+    DatabaseSqlBenchmark(Vertx vertx, JsonObject jsonConfig) {
       Config config = ConfigFrom.firstOf().custom(jsonConfig::getString)
           .rename("username", "database.user")
           .rename("password", "database.password")
@@ -481,8 +420,8 @@ public class App extends AbstractVerticle {
     void dbHandler(final RoutingContext ctx) {
       dbBuilder.transactAsync(dbs ->
         dbs.get().toSelect("SELECT id, randomnumber from WORLD where id = ?")
-            .argInteger(Helper.randomWorld())
-            .queryFirstOrNull(row -> new World(row.getIntegerOrZero(), row.getIntegerOrZero()).encode())
+            .argInteger(randomWorld())
+            .queryFirstOrNull(row -> new World(row.getIntegerOrZero(), row.getIntegerOrZero()))
       , call -> {
         if (call.succeeded()) {
           if (call.result() == null) {
@@ -492,7 +431,7 @@ public class App extends AbstractVerticle {
                 .putHeader(HttpHeaders.SERVER, SERVER)
                 .putHeader(HttpHeaders.DATE, date)
                 .putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
-                .end(call.result());
+                .end(Json.encode(call.result()));
           }
         } else {
           ctx.fail(call.cause());
@@ -504,14 +443,14 @@ public class App extends AbstractVerticle {
       int queries = Helper.getQueries(ctx.request());
 
       dbBuilder.transactAsync(dbs -> {
-        JsonArray worlds = new JsonArray();
+        List<World> worlds = new ArrayList<>();
         for (int i = 1; i <= queries; i++) {
           dbs.get()
               .toSelect("SELECT id, randomnumber from WORLD where id = ?")
-              .argInteger(Helper.randomWorld())
+              .argInteger(randomWorld())
               .queryFirstOrNull(row -> worlds.add(new World(row.getIntegerOrZero(), row.getIntegerOrZero())));
         }
-        return worlds.encode();
+        return worlds;
       }, call -> {
         if (call.succeeded()) {
           if (call.result() == null) {
@@ -521,7 +460,7 @@ public class App extends AbstractVerticle {
                 .putHeader(HttpHeaders.SERVER, SERVER)
                 .putHeader(HttpHeaders.DATE, date)
                 .putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
-                .end(call.result());
+                .end(Json.encodeToBuffer(call.result()));
           }
         } else {
           ctx.fail(call.cause());
@@ -565,23 +504,23 @@ public class App extends AbstractVerticle {
       int queries = Helper.getQueries(ctx.request());
 
       dbBuilder.transactAsync(dbs -> {
-        JsonArray worlds = new JsonArray();
+        List<World> worlds = new ArrayList<>();
         // Haven't implemented batching yet on toUpdate(), so hijack toInsert() as work-around
         SqlInsert batchUpdate = dbs.get().toInsert("UPDATE WORLD SET randomnumber = ? WHERE id = ?");
         for (int i = 1; i <= queries; i++) {
           World oldWorld = dbs.get()
               .toSelect("SELECT id, randomnumber from WORLD where id = ?")
-              .argInteger(Helper.randomWorld())
+              .argInteger(randomWorld())
               .queryFirstOrNull(row -> new World(row.getIntegerOrZero(), row.getIntegerOrZero()));
           if (oldWorld == null) {
             return null;
           }
-          World newWorld = new World(oldWorld.getId(), Helper.randomWorld());
+          World newWorld = new World(oldWorld.getId(), randomWorld());
           worlds.add(newWorld);
           batchUpdate.argInteger(newWorld.getRandomNumber()).argInteger(newWorld.getId()).batch();
         }
         batchUpdate.insertBatchUnchecked();
-        return worlds.encode();
+        return worlds;
       }, call -> {
         if (call.succeeded()) {
           if (call.result() == null) {
@@ -591,7 +530,7 @@ public class App extends AbstractVerticle {
                 .putHeader(HttpHeaders.SERVER, SERVER)
                 .putHeader(HttpHeaders.DATE, date)
                 .putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
-                .end(call.result());
+                .end(Json.encodeToBuffer(call.result()));
           }
         } else {
           ctx.fail(call.cause());
@@ -609,12 +548,11 @@ public class App extends AbstractVerticle {
 
     vertx.setPeriodic(1000, handler -> date = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now()));
 
-    final MongoDB mongoDB = new MongoDB(vertx, config());
-    final AsyncSQL psql = new AsyncSQL(vertx, AsyncSQL.POSTGRES, config());
-    final AsyncSQL mysql = new AsyncSQL(vertx, AsyncSQL.MYSQL, config());
-    final DatabaseSql dbpsql = new DatabaseSql(vertx, config());
+    final MongoDBBenchmark mongoDBBenchmark = new MongoDBBenchmark(vertx, config());
+    final PgClientBenchmark pgClientBenchmark= new PgClientBenchmark(vertx, config());
+    final DatabaseSqlBenchmark databaseSqlBenchmark = new DatabaseSqlBenchmark(vertx, config());
 
-    /**
+    /*
      * This test exercises the framework fundamentals including keep-alive support, request routing, request header
      * parsing, object instantiation, JSON serialization, response header generation, and request count throughput.
      */
@@ -623,48 +561,44 @@ public class App extends AbstractVerticle {
           .putHeader(HttpHeaders.SERVER, SERVER)
           .putHeader(HttpHeaders.DATE, date)
           .putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
-          .end(new Message("Hello, World!").encode());
+          .end(Json.encodeToBuffer(new Message("Hello, World!")));
     });
 
-    /**
+    /*
      * This test exercises the framework's object-relational mapper (ORM), random number generator, database driver,
      * and database connection pool.
      */
-    app.get("/mongo/db").handler(mongoDB::dbHandler);
-    app.get("/psql/db").handler(psql::dbHandler);
-    app.get("/mysql/db").handler(mysql::dbHandler);
-    app.get("/dbpsql/db").handler(dbpsql::dbHandler);
+    app.get("/mongo/db").handler(mongoDBBenchmark::dbHandler);
+    app.get("/psql/db").handler(pgClientBenchmark::dbHandler);
+    app.get("/dbpsql/db").handler(databaseSqlBenchmark::dbHandler);
 
-    /**
+    /*
      * This test is a variation of Test #2 and also uses the World table. Multiple rows are fetched to more dramatically
      * punish the database driver and connection pool. At the highest queries-per-request tested (20), this test
      * demonstrates all frameworks' convergence toward zero requests-per-second as database activity increases.
      */
-    app.get("/mongo/queries").handler(mongoDB::queriesHandler);
-    app.get("/psql/queries").handler(psql::queriesHandler);
-    app.get("/mysql/queries").handler(mysql::queriesHandler);
-    app.get("/dbpsql/queries").handler(dbpsql::queriesHandler);
+    app.get("/mongo/queries").handler(mongoDBBenchmark::queriesHandler);
+    app.get("/psql/queries").handler(pgClientBenchmark::queriesHandler);
+    app.get("/dbpsql/queries").handler(databaseSqlBenchmark::queriesHandler);
 
-    /**
+    /*
      * This test exercises the ORM, database connectivity, dynamic-size collections, sorting, server-side templates,
      * XSS countermeasures, and character encoding.
      */
-    app.get("/mongo/fortunes").handler(mongoDB::fortunesHandler);
-    app.get("/psql/fortunes").handler(psql::fortunesHandler);
-    app.get("/mysql/fortunes").handler(mysql::fortunesHandler);
-    app.get("/dbpsql/fortunes").handler(dbpsql::fortunesHandler);
+    app.get("/mongo/fortunes").handler(mongoDBBenchmark::fortunesHandler);
+    app.get("/psql/fortunes").handler(pgClientBenchmark::fortunesHandler);
+    app.get("/dbpsql/fortunes").handler(databaseSqlBenchmark::fortunesHandler);
 
-    /**
+    /*
      * This test is a variation of Test #3 that exercises the ORM's persistence of objects and the database driver's
      * performance at running UPDATE statements or similar. The spirit of this test is to exercise a variable number of
      * read-then-write style database operations.
      */
-    app.route("/mongo/update").handler(mongoDB::updateHandler);
-    app.route("/psql/update").handler(psql::updateHandler);
-    app.route("/mysql/update").handler(mysql::updateHandler);
-    app.route("/dbpsql/update").handler(dbpsql::updateHandler);
+    app.route("/mongo/update").handler(mongoDBBenchmark::updateHandler);
+    app.route("/psql/update").handler(pgClientBenchmark::updateHandler);
+    app.route("/dbpsql/update").handler(databaseSqlBenchmark::updateHandler);
 
-    /**
+    /*
      * This test is an exercise of the request-routing fundamentals only, designed to demonstrate the capacity of
      * high-performance platforms in particular. Requests will be sent using HTTP pipelining. The response payload is
      * still small, meaning good performance is still necessary in order to saturate the gigabit Ethernet of the test

+ 10 - 11
frameworks/Java/vertx-web/src/main/java/io/vertx/benchmark/model/Fortune.java

@@ -2,15 +2,13 @@ package io.vertx.benchmark.model;
 
 import io.vertx.core.json.JsonObject;
 
-import java.util.Collections;
-
 /**
  * The model for the "fortune" database table.
  */
-public final class Fortune extends JsonObject implements Comparable<Fortune> {
+public final class Fortune implements Comparable<Fortune> {
 
-  private static final String ID = "id";
-  private static final String MESSAGE = "message";
+  private final int id;
+  private final String message;
 
   /**
    * Constructs a new fortune object with the given parameters.
@@ -19,24 +17,25 @@ public final class Fortune extends JsonObject implements Comparable<Fortune> {
    * @param message the message of the fortune
    */
   public Fortune(int id, String message) {
-    put(ID, id);
-    put(MESSAGE, message);
+    this.id = id;
+    this.message = message;
   }
 
   public Fortune(JsonObject doc) {
-    super(doc == null ? Collections.emptyMap() : doc.getMap());
+    this.id = doc.getInteger("id");
+    this.message = doc.getString("message");
   }
 
   public int getId() {
-    return getInteger(ID);
+    return id;
   }
 
   public String getMessage() {
-    return getString(MESSAGE);
+    return message;
   }
 
   @Override
   public int compareTo(Fortune other) {
-    return getMessage().compareTo(other.getMessage());
+    return message.compareTo(other.message);
   }
 }

+ 4 - 6
frameworks/Java/vertx-web/src/main/java/io/vertx/benchmark/model/Message.java

@@ -1,16 +1,14 @@
 package io.vertx.benchmark.model;
 
-import io.vertx.core.json.JsonObject;
+public class Message {
 
-public class Message extends JsonObject {
-
-  private static final String MESSAGE = "message";
+  private final String message;
 
   public Message(String message) {
-    put(MESSAGE, message);
+    this.message = message;
   }
 
   public String getMessage() {
-    return getString(MESSAGE);
+    return message;
   }
 }

+ 14 - 10
frameworks/Java/vertx-web/src/main/java/io/vertx/benchmark/model/World.java

@@ -2,15 +2,13 @@ package io.vertx.benchmark.model;
 
 import io.vertx.core.json.JsonObject;
 
-import java.util.Collections;
-
 /**
  * The model for the "world" database table.
  */
-public final class World extends JsonObject {
+public final class World implements Comparable<World> {
 
-  public static final String ID = "id";
-  public static final String RANDOM_NUMBER = "randomNumber";
+  private final int id;
+  private final int randomNumber;
 
   /**
    * Constructs a new world object with the given parameters.
@@ -19,19 +17,25 @@ public final class World extends JsonObject {
    * @param randomNumber the random number of the world
    */
   public World(int id, int randomNumber) {
-    put(ID, id);
-    put(RANDOM_NUMBER, randomNumber);
+    this.id = id;
+    this.randomNumber = randomNumber;
   }
 
   public World(JsonObject doc) {
-    super(doc == null ? Collections.emptyMap() : doc.getMap());
+    this.id = doc.getInteger("id");
+    this.randomNumber = doc.getInteger("randomNumber");
   }
 
   public int getId() {
-    return getInteger(ID);
+    return id;
   }
 
   public int getRandomNumber() {
-    return getInteger(RANDOM_NUMBER);
+    return randomNumber;
+  }
+
+  @Override
+  public int compareTo(World o) {
+    return Integer.compare(getId(), o.getId());
   }
 }