diff --git a/frameworks/vertx/README.md b/frameworks/vertx/README.md index 3c31f2536..c5a11dc89 100644 --- a/frameworks/vertx/README.md +++ b/frameworks/vertx/README.md @@ -17,6 +17,11 @@ Eclipse Vert.x with vertx-web on Netty, one server verticle per core. | `/baseline11` | POST | Sums query parameters + request body | | `/json/{count}?m=N` | GET | First `count` dataset items with `total = price * quantity * m` | | `/upload` | POST | Streams the body and returns the byte count | +| `/baseline2` | GET | Sums query parameter values | +| `/async-db` | GET | Reads from PostgreSQL through the reactive pg client | +| `/static/:filename` | GET | Serves a file from disk with `sendFile`, the brotli or gzip variant when the client accepts one | +| `/crud/items` | GET/POST | Lists items by category with paging, or inserts one | +| `/crud/items/:id` | GET/PUT | Reads one item through a Redis cache-aside, or updates it and drops the cached copy | ## Notes @@ -25,3 +30,13 @@ Eclipse Vert.x with vertx-web on Netty, one server verticle per core. - Compression through `HttpServerOptions.setCompressionSupported(true)` - The verticle is deployed once per core, all instances sharing port 8080 - `/upload` is read from the request stream, so the 20 MB body is never buffered + +`json-tls` and `static-tls` listen on `8081` when `/certs/server.crt` and `/certs/server.key` are +mounted. The verticle is deployed once per core and each instance binds both ports, so the TLS +listener is spread across every event loop rather than parked on one. ALPN is off: those two +profiles want HTTP/1.1 negotiated and no h2 offered. + +Static file bodies are read from disk on every request - `sendFile` hands the descriptor to the +kernel rather than holding the bytes - and the pre-compressed sibling is picked per request. + +`tags` is a JSONB column, so the pg client hands back a `JsonArray` rather than text. diff --git a/frameworks/vertx/meta.json b/frameworks/vertx/meta.json index 61316b984..47c516fa1 100644 --- a/frameworks/vertx/meta.json +++ b/frameworks/vertx/meta.json @@ -19,7 +19,14 @@ "limited-conn", "json", "json-comp", - "upload" + "json-tls", + "upload", + "static", + "static-tls", + "async-db", + "crud", + "api-4", + "api-16" ], "maintainers": [] } diff --git a/frameworks/vertx/pom.xml b/frameworks/vertx/pom.xml index 045952db7..85b842d17 100644 --- a/frameworks/vertx/pom.xml +++ b/frameworks/vertx/pom.xml @@ -26,6 +26,22 @@ vertx-web ${vertx.version} + + io.vertx + vertx-pg-client + ${vertx.version} + + + + com.ongres.scram + scram-client + 3.1 + + + io.vertx + vertx-redis-client + ${vertx.version} + diff --git a/frameworks/vertx/src/main/java/httparena/ServerVerticle.java b/frameworks/vertx/src/main/java/httparena/ServerVerticle.java index d1f2988de..9ec195586 100644 --- a/frameworks/vertx/src/main/java/httparena/ServerVerticle.java +++ b/frameworks/vertx/src/main/java/httparena/ServerVerticle.java @@ -1,5 +1,7 @@ package httparena; +import java.io.File; +import java.util.List; import java.util.Map; import io.vertx.core.Future; @@ -8,30 +10,77 @@ import io.vertx.core.http.HttpServerRequest; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; +import io.vertx.core.net.PemKeyCertOptions; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.handler.BodyHandler; +import io.vertx.pgclient.PgBuilder; +import io.vertx.pgclient.PgConnectOptions; +import io.vertx.redis.client.Redis; +import io.vertx.redis.client.RedisAPI; +import io.vertx.sqlclient.Pool; +import io.vertx.sqlclient.PoolOptions; +import io.vertx.sqlclient.Row; +import io.vertx.sqlclient.Tuple; public class ServerVerticle extends VerticleBase { private static final long MAX_BODY = 25L * 1024 * 1024; + private static final String STATIC_ROOT = "/data/static"; + private static final String CERT = "/certs/server.crt"; + private static final String KEY = "/certs/server.key"; + + private static final String ITEM_COLUMNS = + "id, name, category, price, quantity, active, tags, rating_score, rating_count"; + + // The profile reads and writes the same ids, so a long TTL would answer from a copy + // the writes have already moved past. + private static final String CRUD_TTL_MS = "200"; private final JsonArray dataset; + private Pool pgPool; + private RedisAPI redis; + public ServerVerticle(JsonArray dataset) { this.dataset = dataset; } @Override public Future start() { + String dbUrl = System.getenv("DATABASE_URL"); + if (dbUrl != null && !dbUrl.isEmpty()) { + // The pool is per verticle instance and one is deployed per core, so the + // harness's connection budget is split across them rather than opened by each. + pgPool = PgBuilder.pool() + .connectingTo(PgConnectOptions.fromUri(dbUrl)) + .with(new PoolOptions().setMaxSize(4)) + .using(vertx) + .build(); + } + String redisUrl = System.getenv("REDIS_URL"); + if (redisUrl != null && !redisUrl.isEmpty()) { + redis = RedisAPI.api(Redis.createClient(vertx, redisUrl)); + } Router router = Router.router(vertx); router.get("/pipeline").handler(ctx -> text(ctx, "ok")); + router.get("/baseline2").handler(this::baselineGet); router.get("/baseline11").handler(this::baselineGet); router.post("/baseline11") .handler(BodyHandler.create().setBodyLimit(MAX_BODY)) .handler(this::baselinePost); router.get("/json/:count").handler(this::jsonItems); + router.get("/async-db").handler(this::asyncDb); + router.get("/static/:filename").handler(this::staticFile); + router.get("/crud/items").handler(this::crudList); + router.post("/crud/items") + .handler(BodyHandler.create()) + .handler(this::crudCreate); + router.get("/crud/items/:id").handler(this::crudRead); + router.put("/crud/items/:id") + .handler(BodyHandler.create()) + .handler(this::crudUpdate); router.post("/upload").handler(this::upload); HttpServerOptions options = new HttpServerOptions() @@ -39,13 +88,40 @@ public Future start() { .setPort(8080) .setCompressionSupported(true); - return vertx.createHttpServer(options).requestHandler(router).listen(); + Future plain = vertx.createHttpServer(options).requestHandler(router).listen(); + + // json-tls and static-tls on 8081, the same router behind TLS. This verticle is + // deployed once per core, so every instance binds both ports and the listener is + // spread across all the event loops rather than parked on one. ALPN is off: the + // two profiles want HTTP/1.1 negotiated and no h2 offered. The harness only mounts + // /certs for the TLS profiles. + if (new File(CERT).isFile() && new File(KEY).isFile()) { + HttpServerOptions tls = new HttpServerOptions() + .setHost("0.0.0.0") + .setPort(8081) + .setCompressionSupported(true) + .setSsl(true) + .setUseAlpn(false) + .setKeyCertOptions(new PemKeyCertOptions().setCertPath(CERT).setKeyPath(KEY)); + return Future.all(plain, vertx.createHttpServer(tls).requestHandler(router).listen()); + } + return plain; } private void text(RoutingContext ctx, String body) { ctx.response().putHeader("content-type", "text/plain").end(body); } + private void json(RoutingContext ctx, String body) { + ctx.response().putHeader("content-type", "application/json").end(body); + } + + private void dbError(RoutingContext ctx, String message, int status) { + ctx.response().setStatusCode(status) + .putHeader("content-type", "application/json") + .end("{\"error\":\"" + message + "\"}"); + } + private long querySum(RoutingContext ctx) { long sum = 0; for (Map.Entry entry : ctx.queryParams()) { @@ -57,6 +133,16 @@ private long querySum(RoutingContext ctx) { return sum; } + private int intParam(RoutingContext ctx, String name, int fallback) { + String raw = ctx.queryParams().get(name); + if (raw == null) return fallback; + try { + return Integer.parseInt(raw.trim()); + } catch (NumberFormatException e) { + return fallback; + } + } + private void baselineGet(RoutingContext ctx) { text(ctx, Long.toString(querySum(ctx))); } @@ -100,6 +186,243 @@ private void jsonItems(RoutingContext ctx) { ctx.json(new JsonObject().put("items", items).put("count", count)); } + // tags is a JSONB column, so the client hands back a JsonArray rather than text. + private JsonArray tags(Row row) { + Object value = row.getValue("tags"); + if (value instanceof JsonArray array) return array; + if (value instanceof String text) return new JsonArray(text); + return new JsonArray(); + } + + private JsonObject itemShape(Row row) { + return new JsonObject() + .put("id", row.getLong("id")) + .put("name", row.getString("name")) + .put("category", row.getString("category")) + .put("price", row.getLong("price")) + .put("quantity", row.getLong("quantity")) + .put("active", row.getBoolean("active")) + .put("tags", tags(row)) + .put("rating", new JsonObject() + .put("score", row.getLong("rating_score")) + .put("count", row.getLong("rating_count"))); + } + + private void asyncDb(RoutingContext ctx) { + if (pgPool == null) { + json(ctx, "{\"items\":[],\"count\":0}"); + return; + } + int min = intParam(ctx, "min", 10); + int max = intParam(ctx, "max", 50); + int limit = Math.max(1, Math.min(50, intParam(ctx, "limit", 50))); + + pgPool.preparedQuery("SELECT " + ITEM_COLUMNS + " FROM items WHERE price BETWEEN $1 AND $2 LIMIT $3") + .execute(Tuple.of(min, max, limit)) + .onSuccess(rows -> { + JsonArray items = new JsonArray(); + for (Row row : rows) items.add(itemShape(row)); + json(ctx, new JsonObject().put("items", items).put("count", items.size()).encode()); + }) + .onFailure(e -> json(ctx, "{\"items\":[],\"count\":0}")); + } + + // Standard mode still reads static bodies from disk on every request: sendFile hands + // the descriptor to the kernel rather than holding the bytes. The pre-compressed + // sibling is picked per request and is also read from disk. + private void staticFile(RoutingContext ctx) { + String filename = ctx.pathParam("filename"); + if (filename == null || filename.contains("/") || filename.contains("..")) { + ctx.response().setStatusCode(404).end(); + return; + } + String path = STATIC_ROOT + "/" + filename; + if (!new File(path).isFile()) { + ctx.response().setStatusCode(404).end(); + return; + } + ctx.response().putHeader("content-type", contentType(filename)); + + String accept = ctx.request().getHeader("accept-encoding"); + if (accept != null) { + if (accept.contains("br") && new File(path + ".br").isFile()) { + ctx.response().putHeader("content-encoding", "br").sendFile(path + ".br"); + return; + } + if (accept.contains("gzip") && new File(path + ".gz").isFile()) { + ctx.response().putHeader("content-encoding", "gzip").sendFile(path + ".gz"); + return; + } + } + ctx.response().sendFile(path); + } + + private String contentType(String filename) { + int dot = filename.lastIndexOf('.'); + String ext = dot < 0 ? "" : filename.substring(dot); + return switch (ext) { + case ".css" -> "text/css"; + case ".js" -> "application/javascript"; + case ".html" -> "text/html"; + case ".woff2" -> "font/woff2"; + case ".svg" -> "image/svg+xml"; + case ".webp" -> "image/webp"; + case ".json" -> "application/json"; + default -> "application/octet-stream"; + }; + } + + private void crudList(RoutingContext ctx) { + if (pgPool == null) { + dbError(ctx, "DB not available", 500); + return; + } + String category = ctx.queryParams().get("category"); + if (category == null) category = "electronics"; + int page = Math.max(1, intParam(ctx, "page", 1)); + int limit = Math.max(1, Math.min(50, intParam(ctx, "limit", 10))); + + pgPool.preparedQuery("SELECT " + ITEM_COLUMNS + " FROM items WHERE category = $1 ORDER BY id LIMIT $2 OFFSET $3") + .execute(Tuple.of(category, limit, (page - 1) * limit)) + .onSuccess(rows -> { + JsonArray items = new JsonArray(); + for (Row row : rows) items.add(itemShape(row)); + json(ctx, new JsonObject() + .put("items", items) + .put("total", items.size()) + .put("page", page) + .put("limit", limit) + .encode()); + }) + .onFailure(e -> dbError(ctx, "query failed", 500)); + } + + private void crudCreate(RoutingContext ctx) { + if (pgPool == null) { + dbError(ctx, "DB not available", 500); + return; + } + JsonObject body = ctx.body().asJsonObject(); + if (body == null) { + dbError(ctx, "insert failed", 500); + return; + } + pgPool.preparedQuery("INSERT INTO items (id, name, category, price, quantity, active, tags, rating_score, rating_count) " + + "VALUES ($1, $2, $3, $4, $5, true, '[\"bench\"]', 0, 0) " + + "ON CONFLICT (id) DO UPDATE SET name = $2, price = $4, quantity = $5 RETURNING id") + .execute(Tuple.of( + body.getLong("id"), + body.getString("name", "New Product"), + body.getString("category", "test"), + body.getLong("price", 0L), + body.getLong("quantity", 0L))) + .onSuccess(rows -> ctx.response().setStatusCode(201) + .putHeader("content-type", "application/json") + .end(new JsonObject() + .put("id", rows.iterator().next().getLong("id")) + .put("name", body.getValue("name")) + .put("category", body.getValue("category")) + .put("price", body.getValue("price")) + .put("quantity", body.getValue("quantity")) + .encode())) + .onFailure(e -> dbError(ctx, "insert failed", 500)); + } + + // Cache-aside on Redis where the harness provides it - crud is the one profile that + // does, and the cache is shared across every verticle instance. + private void crudRead(RoutingContext ctx) { + if (pgPool == null) { + dbError(ctx, "DB not available", 500); + return; + } + long id; + try { + id = Long.parseLong(ctx.pathParam("id")); + } catch (NumberFormatException e) { + ctx.response().setStatusCode(404).end(); + return; + } + String key = "crud:" + id; + if (redis == null) { + crudReadFromDb(ctx, id, key); + return; + } + redis.get(key) + .onSuccess(hit -> { + if (hit != null) { + ctx.response() + .putHeader("content-type", "application/json") + .putHeader("x-cache", "HIT") + .end(hit.toString()); + } else { + crudReadFromDb(ctx, id, key); + } + }) + .onFailure(e -> crudReadFromDb(ctx, id, key)); + } + + private void crudReadFromDb(RoutingContext ctx, long id, String key) { + pgPool.preparedQuery("SELECT " + ITEM_COLUMNS + " FROM items WHERE id = $1 LIMIT 1") + .execute(Tuple.of(id)) + .onSuccess(rows -> { + if (rows.size() == 0) { + ctx.response().setStatusCode(404).end(); + return; + } + String body = itemShape(rows.iterator().next()).encode(); + if (redis != null) { + redis.set(List.of(key, body, "PX", CRUD_TTL_MS)); + } + ctx.response() + .putHeader("content-type", "application/json") + .putHeader("x-cache", "MISS") + .end(body); + }) + .onFailure(e -> dbError(ctx, "query failed", 500)); + } + + private void crudUpdate(RoutingContext ctx) { + if (pgPool == null) { + dbError(ctx, "DB not available", 500); + return; + } + long id; + try { + id = Long.parseLong(ctx.pathParam("id")); + } catch (NumberFormatException e) { + ctx.response().setStatusCode(404).end(); + return; + } + JsonObject body = ctx.body().asJsonObject(); + if (body == null) { + dbError(ctx, "update failed", 500); + return; + } + long finalId = id; + pgPool.preparedQuery("UPDATE items SET name = $1, price = $2, quantity = $3 WHERE id = $4") + .execute(Tuple.of( + body.getString("name", "Updated"), + body.getLong("price", 0L), + body.getLong("quantity", 0L), + id)) + .onSuccess(rows -> { + if (rows.rowCount() == 0) { + ctx.response().setStatusCode(404).end(); + return; + } + if (redis != null) { + redis.del(List.of("crud:" + finalId)); + } + json(ctx, new JsonObject() + .put("id", finalId) + .put("name", body.getValue("name")) + .put("price", body.getValue("price")) + .put("quantity", body.getValue("quantity")) + .encode()); + }) + .onFailure(e -> dbError(ctx, "update failed", 500)); + } + private void upload(RoutingContext ctx) { HttpServerRequest request = ctx.request(); long[] size = {0}; diff --git a/site/data/results/vertx.json b/site/data/results/vertx.json index a31cad606..70cc18f31 100644 --- a/site/data/results/vertx.json +++ b/site/data/results/vertx.json @@ -1,22 +1,94 @@ { "framework": "vertx", "results": { + "api-16-1024": { + "framework": "vertx", + "language": "Java", + "rps": 104880, + "avg_latency": "8.31ms", + "p99_latency": "84.30ms", + "cpu": "1580.4%", + "memory": "2.0GiB", + "connections": 1024, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "524.16MB/s", + "input_bw": "5.90MB/s", + "reconnects": 314499, + "status_2xx": 1573208, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0, + "tpl_baseline": 588640, + "tpl_json": 590836, + "tpl_db": 0, + "tpl_upload": 0, + "tpl_static": 0, + "tpl_async_db": 393731 + }, + "api-4-256": { + "framework": "vertx", + "language": "Java", + "rps": 29786, + "avg_latency": "7.76ms", + "p99_latency": "92.40ms", + "cpu": "396.3%", + "memory": "618MiB", + "connections": 256, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "148.70MB/s", + "input_bw": "1.68MB/s", + "reconnects": 89335, + "status_2xx": 446803, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0, + "tpl_baseline": 167473, + "tpl_json": 167233, + "tpl_db": 0, + "tpl_upload": 0, + "tpl_static": 0, + "tpl_async_db": 112097 + }, + "async-db-1024": { + "framework": "vertx", + "language": "Java", + "rps": 198525, + "avg_latency": "4.72ms", + "p99_latency": "47.00ms", + "cpu": "4772.4%", + "memory": "5.1GiB", + "connections": 1024, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "750.36MB/s", + "input_bw": "13.25MB/s", + "reconnects": 79391, + "status_2xx": 1985254, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, "baseline-4096": { "framework": "vertx", "language": "Java", - "rps": 2060804, - "avg_latency": "1.99ms", - "p99_latency": "5.03ms", - "cpu": "6246.7%", - "memory": "4.5GiB", + "rps": 1994369, + "avg_latency": "2.06ms", + "p99_latency": "5.07ms", + "cpu": "6290.9%", + "memory": "4.8GiB", "connections": 4096, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "129.68MB/s", - "input_bw": "159.19MB/s", + "bandwidth": "125.50MB/s", + "input_bw": "154.06MB/s", "reconnects": 0, - "status_2xx": 10304024, + "status_2xx": 9971849, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -24,19 +96,39 @@ "baseline-512": { "framework": "vertx", "language": "Java", - "rps": 2072254, - "avg_latency": "246us", - "p99_latency": "460us", - "cpu": "6418.1%", - "memory": "3.3GiB", + "rps": 2042479, + "avg_latency": "250us", + "p99_latency": "468us", + "cpu": "6487.6%", + "memory": "2.8GiB", "connections": 512, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "130.39MB/s", - "input_bw": "160.08MB/s", + "bandwidth": "128.53MB/s", + "input_bw": "157.78MB/s", "reconnects": 0, - "status_2xx": 10361272, + "status_2xx": 10212399, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "crud-4096": { + "framework": "vertx", + "language": "Java", + "rps": 226986, + "avg_latency": "18.03ms", + "p99_latency": "62.80ms", + "cpu": "4087.6%", + "memory": "2.9GiB", + "connections": 4096, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "64.18MB/s", + "input_bw": "19.48MB/s", + "reconnects": 15028, + "status_2xx": 3404796, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -44,19 +136,19 @@ "json-4096": { "framework": "vertx", "language": "Java", - "rps": 688783, - "avg_latency": "5.66ms", - "p99_latency": "22.90ms", - "cpu": "6242.5%", - "memory": "11.4GiB", + "rps": 671315, + "avg_latency": "5.80ms", + "p99_latency": "29.90ms", + "cpu": "6382.1%", + "memory": "6.6GiB", "connections": 4096, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "2.30GB/s", - "input_bw": "32.84MB/s", - "reconnects": 135929, - "status_2xx": 3443915, + "bandwidth": "2.25GB/s", + "input_bw": "32.01MB/s", + "reconnects": 132671, + "status_2xx": 3356579, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -64,19 +156,19 @@ "json-comp-16384": { "framework": "vertx", "language": "Java", - "rps": 256936, - "avg_latency": "63.14ms", - "p99_latency": "149.40ms", - "cpu": "6245.0%", - "memory": "12.8GiB", + "rps": 256570, + "avg_latency": "63.01ms", + "p99_latency": "146.90ms", + "cpu": "6181.9%", + "memory": "15.2GiB", "connections": 16384, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "330.58MB/s", - "input_bw": "19.11MB/s", - "reconnects": 44713, - "status_2xx": 1284683, + "bandwidth": "330.20MB/s", + "input_bw": "19.09MB/s", + "reconnects": 44005, + "status_2xx": 1282851, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -84,19 +176,19 @@ "json-comp-4096": { "framework": "vertx", "language": "Java", - "rps": 259189, - "avg_latency": "15.78ms", - "p99_latency": "43.40ms", - "cpu": "5997.8%", - "memory": "6.7GiB", + "rps": 256544, + "avg_latency": "15.94ms", + "p99_latency": "45.40ms", + "cpu": "6095.9%", + "memory": "6.4GiB", "connections": 4096, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "333.51MB/s", - "input_bw": "19.28MB/s", - "reconnects": 50044, - "status_2xx": 1295945, + "bandwidth": "330.08MB/s", + "input_bw": "19.08MB/s", + "reconnects": 49511, + "status_2xx": 1282722, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -104,19 +196,38 @@ "json-comp-512": { "framework": "vertx", "language": "Java", - "rps": 259837, - "avg_latency": "1.97ms", - "p99_latency": "8.95ms", - "cpu": "6166.2%", + "rps": 257543, + "avg_latency": "1.98ms", + "p99_latency": "8.92ms", + "cpu": "6182.5%", "memory": "4.1GiB", "connections": 512, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "334.34MB/s", - "input_bw": "19.33MB/s", - "reconnects": 51952, - "status_2xx": 1299185, + "bandwidth": "331.36MB/s", + "input_bw": "19.16MB/s", + "reconnects": 51502, + "status_2xx": 1287716, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "json-tls-4096": { + "framework": "vertx", + "language": "Java", + "rps": 601618, + "avg_latency": "7.37ms", + "p99_latency": "134.88ms", + "cpu": "6181.7%", + "memory": "8.0GiB", + "connections": 4096, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "2.01GB", + "reconnects": 0, + "status_2xx": 3067146, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -124,19 +235,19 @@ "limited-conn-4096": { "framework": "vertx", "language": "Java", - "rps": 605570, - "avg_latency": "6.71ms", - "p99_latency": "68.60ms", - "cpu": "3343.8%", - "memory": "2.3GiB", + "rps": 602586, + "avg_latency": "6.77ms", + "p99_latency": "68.70ms", + "cpu": "3254.6%", + "memory": "2.4GiB", "connections": 4096, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "38.10MB/s", - "input_bw": "46.78MB/s", - "reconnects": 302831, - "status_2xx": 3027853, + "bandwidth": "37.92MB/s", + "input_bw": "46.55MB/s", + "reconnects": 301293, + "status_2xx": 3012933, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -144,19 +255,19 @@ "limited-conn-512": { "framework": "vertx", "language": "Java", - "rps": 600428, - "avg_latency": "845us", - "p99_latency": "8.29ms", - "cpu": "3301.4%", + "rps": 583157, + "avg_latency": "870us", + "p99_latency": "8.54ms", + "cpu": "3191.5%", "memory": "2.4GiB", "connections": 512, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "37.78MB/s", - "input_bw": "46.38MB/s", - "reconnects": 300212, - "status_2xx": 3002141, + "bandwidth": "36.69MB/s", + "input_bw": "45.05MB/s", + "reconnects": 291584, + "status_2xx": 2915789, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -164,18 +275,18 @@ "pipelined-4096": { "framework": "vertx", "language": "Java", - "rps": 11211420, - "avg_latency": "5.86ms", - "p99_latency": "14.60ms", - "cpu": "6099.4%", - "memory": "6.2GiB", + "rps": 11782768, + "avg_latency": "5.58ms", + "p99_latency": "13.40ms", + "cpu": "6288.8%", + "memory": "7.3GiB", "connections": 4096, "threads": 64, "duration": "5s", "pipeline": 16, - "bandwidth": "705.50MB/s", + "bandwidth": "741.43MB/s", "reconnects": 0, - "status_2xx": 56057104, + "status_2xx": 58913840, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -183,18 +294,132 @@ "pipelined-512": { "framework": "vertx", "language": "Java", - "rps": 11418576, - "avg_latency": "716us", - "p99_latency": "3.60ms", - "cpu": "6154.3%", - "memory": "5.7GiB", + "rps": 12414412, + "avg_latency": "659us", + "p99_latency": "3.55ms", + "cpu": "6192.5%", + "memory": "5.2GiB", "connections": 512, "threads": 64, "duration": "5s", "pipeline": 16, - "bandwidth": "718.53MB/s", + "bandwidth": "781.18MB/s", + "reconnects": 0, + "status_2xx": 62072064, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "static-1024": { + "framework": "vertx", + "language": "Java", + "rps": 230227, + "avg_latency": "4.51ms", + "p99_latency": "43.67ms", + "cpu": "6447.1%", + "memory": "2.8GiB", + "connections": 1024, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "3.51GB", + "reconnects": 0, + "status_2xx": 1174059, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "static-4096": { + "framework": "vertx", + "language": "Java", + "rps": 227767, + "avg_latency": "17.96ms", + "p99_latency": "96.37ms", + "cpu": "6441.7%", + "memory": "3.1GiB", + "connections": 4096, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "3.48GB", + "reconnects": 0, + "status_2xx": 1161592, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "static-6800": { + "framework": "vertx", + "language": "Java", + "rps": 226617, + "avg_latency": "29.86ms", + "p99_latency": "148.10ms", + "cpu": "6321.8%", + "memory": "3.2GiB", + "connections": 6800, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "3.46GB", + "reconnects": 0, + "status_2xx": 1155791, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "static-tls-1024": { + "framework": "vertx", + "language": "Java", + "rps": 195103, + "avg_latency": "5.40ms", + "p99_latency": "49.58ms", + "cpu": "6204.9%", + "memory": "1.6GiB", + "connections": 1024, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "2.98GB", + "reconnects": 0, + "status_2xx": 994981, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "static-tls-4096": { + "framework": "vertx", + "language": "Java", + "rps": 191689, + "avg_latency": "21.44ms", + "p99_latency": "183.98ms", + "cpu": "6313.9%", + "memory": "3.4GiB", + "connections": 4096, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "2.93GB", + "reconnects": 0, + "status_2xx": 977503, + "status_3xx": 0, + "status_4xx": 0, + "status_5xx": 0 + }, + "static-tls-6800": { + "framework": "vertx", + "language": "Java", + "rps": 187031, + "avg_latency": "36.14ms", + "p99_latency": "235.22ms", + "cpu": "6282.1%", + "memory": "2.8GiB", + "connections": 6800, + "threads": 64, + "duration": "5s", + "pipeline": 1, + "bandwidth": "2.85GB", "reconnects": 0, - "status_2xx": 57092880, + "status_2xx": 953524, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -202,19 +427,19 @@ "upload-256": { "framework": "vertx", "language": "Java", - "rps": 2029, - "avg_latency": "122.87ms", - "p99_latency": "680.90ms", - "cpu": "6011.4%", - "memory": "8.9GiB", + "rps": 2148, + "avg_latency": "116.81ms", + "p99_latency": "683.80ms", + "cpu": "6072.8%", + "memory": "9.7GiB", "connections": 256, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "141.20KB/s", - "input_bw": "16.09GB/s", - "reconnects": 1982, - "status_2xx": 10145, + "bandwidth": "149.50KB/s", + "input_bw": "17.04GB/s", + "reconnects": 2110, + "status_2xx": 10744, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -222,19 +447,19 @@ "upload-32": { "framework": "vertx", "language": "Java", - "rps": 2105, - "avg_latency": "15.17ms", - "p99_latency": "62.10ms", - "cpu": "2628.5%", + "rps": 1846, + "avg_latency": "17.30ms", + "p99_latency": "73.30ms", + "cpu": "2664.8%", "memory": "7.8GiB", "connections": 32, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "146.41KB/s", - "input_bw": "16.70GB/s", - "reconnects": 2106, - "status_2xx": 10525, + "bandwidth": "128.43KB/s", + "input_bw": "14.64GB/s", + "reconnects": 1846, + "status_2xx": 9233, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 diff --git a/site/static/logs/api-16/1024/vertx.log b/site/static/logs/api-16/1024/vertx.log new file mode 100644 index 000000000..e69de29bb diff --git a/site/static/logs/api-4/256/vertx.log b/site/static/logs/api-4/256/vertx.log new file mode 100644 index 000000000..e69de29bb diff --git a/site/static/logs/async-db/1024/vertx.log b/site/static/logs/async-db/1024/vertx.log new file mode 100644 index 000000000..e69de29bb diff --git a/site/static/logs/crud/4096/vertx.log b/site/static/logs/crud/4096/vertx.log new file mode 100644 index 000000000..e69de29bb diff --git a/site/static/logs/json-tls/4096/vertx.log b/site/static/logs/json-tls/4096/vertx.log new file mode 100644 index 000000000..e69de29bb diff --git a/site/static/logs/static-tls/1024/vertx.log b/site/static/logs/static-tls/1024/vertx.log new file mode 100644 index 000000000..e69de29bb diff --git a/site/static/logs/static-tls/4096/vertx.log b/site/static/logs/static-tls/4096/vertx.log new file mode 100644 index 000000000..e69de29bb diff --git a/site/static/logs/static-tls/6800/vertx.log b/site/static/logs/static-tls/6800/vertx.log new file mode 100644 index 000000000..e69de29bb diff --git a/site/static/logs/static/1024/vertx.log b/site/static/logs/static/1024/vertx.log new file mode 100644 index 000000000..e69de29bb diff --git a/site/static/logs/static/4096/vertx.log b/site/static/logs/static/4096/vertx.log new file mode 100644 index 000000000..e69de29bb diff --git a/site/static/logs/static/6800/vertx.log b/site/static/logs/static/6800/vertx.log new file mode 100644 index 000000000..e69de29bb