From a7deef07058edd5a4064fcecc75ef5cc1bfb075a Mon Sep 17 00:00:00 2001 From: Santiago Pericas-Geertsen Date: Fri, 1 Nov 2024 16:16:32 -0400 Subject: [PATCH 01/12] Changes to fortunes. Signed-off-by: Santiago Pericas-Geertsen --- .../main/java/io/helidon/benchmark/nima/models/Fortune.java | 6 +++++- .../io/helidon/benchmark/nima/services/FortuneHandler.java | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/Fortune.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/Fortune.java index 96a5e2070be..190c792d345 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/Fortune.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/Fortune.java @@ -1,7 +1,7 @@ package io.helidon.benchmark.nima.models; -public final class Fortune { +public final class Fortune implements Comparable { public int id; public String message; @@ -17,4 +17,8 @@ public int getId() { public String getMessage() { return message; } + @Override + public int compareTo(Fortune other) { + return message.compareTo(other.message); + } } diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java index 8847b7d0bc1..062fd33934f 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java @@ -1,7 +1,7 @@ package io.helidon.benchmark.nima.services; -import java.util.Comparator; +import java.util.Collections; import java.util.List; import com.fizzed.rocker.runtime.ArrayOfByteArraysOutput; @@ -32,7 +32,7 @@ public void handle(ServerRequest req, ServerResponse res) { res.header(CONTENT_TYPE_HTML); List fortuneList = repository.getFortunes(); fortuneList.add(ADDITIONAL_FORTUNE); - fortuneList.sort(Comparator.comparing(Fortune::getMessage)); + Collections.sort(fortuneList); res.send(fortunes.template(fortuneList) .render(ArrayOfByteArraysOutput.FACTORY) .toByteArray()); From df21e04a1d438c9bbbd295df0342e86494b690e6 Mon Sep 17 00:00:00 2001 From: Santiago Pericas-Geertsen Date: Mon, 4 Nov 2024 10:08:12 -0500 Subject: [PATCH 02/12] Write fortunes output with an output stream. Signed-off-by: Santiago Pericas-Geertsen --- .../benchmark/nima/services/FortuneHandler.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java index 062fd33934f..61294716629 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java @@ -1,12 +1,14 @@ package io.helidon.benchmark.nima.services; +import java.io.IOException; import java.util.Collections; import java.util.List; import com.fizzed.rocker.runtime.ArrayOfByteArraysOutput; import io.helidon.benchmark.nima.models.DbRepository; import io.helidon.benchmark.nima.models.Fortune; +import io.helidon.common.buffers.BufferData; import io.helidon.webserver.http.Handler; import io.helidon.webserver.http.ServerRequest; import io.helidon.webserver.http.ServerResponse; @@ -14,6 +16,7 @@ import static io.helidon.benchmark.nima.Main.CONTENT_TYPE_HTML; import static io.helidon.benchmark.nima.Main.SERVER; +import static io.helidon.http.HeaderNames.CONTENT_LENGTH; public class FortuneHandler implements Handler { @@ -30,11 +33,19 @@ public FortuneHandler(DbRepository repository) { public void handle(ServerRequest req, ServerResponse res) { res.header(SERVER); res.header(CONTENT_TYPE_HTML); + List fortuneList = repository.getFortunes(); fortuneList.add(ADDITIONAL_FORTUNE); Collections.sort(fortuneList); - res.send(fortunes.template(fortuneList) - .render(ArrayOfByteArraysOutput.FACTORY) - .toByteArray()); + ArrayOfByteArraysOutput output = fortunes.template(fortuneList).render(ArrayOfByteArraysOutput.FACTORY); + List entity = output.getArrays(); + BufferData bufferData = BufferData.create(entity.stream().map(BufferData::create).toList()); + int length = bufferData.available(); + res.header(CONTENT_LENGTH, String.valueOf(length)); + try (var out = res.outputStream()) { + bufferData.writeTo(out); + } catch (IOException e) { + throw new RuntimeException(e); + } } } \ No newline at end of file From c4a735d84905dc33c2df19be9e1a316954eb742d Mon Sep 17 00:00:00 2001 From: Santiago Pericas-Geertsen Date: Mon, 4 Nov 2024 10:31:14 -0500 Subject: [PATCH 03/12] Write fortunes output with an output stream using byte[]. Signed-off-by: Santiago Pericas-Geertsen --- .../benchmark/nima/services/FortuneHandler.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java index 61294716629..3d6e9ba4cfb 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java @@ -8,7 +8,6 @@ import com.fizzed.rocker.runtime.ArrayOfByteArraysOutput; import io.helidon.benchmark.nima.models.DbRepository; import io.helidon.benchmark.nima.models.Fortune; -import io.helidon.common.buffers.BufferData; import io.helidon.webserver.http.Handler; import io.helidon.webserver.http.ServerRequest; import io.helidon.webserver.http.ServerResponse; @@ -39,11 +38,17 @@ public void handle(ServerRequest req, ServerResponse res) { Collections.sort(fortuneList); ArrayOfByteArraysOutput output = fortunes.template(fortuneList).render(ArrayOfByteArraysOutput.FACTORY); List entity = output.getArrays(); - BufferData bufferData = BufferData.create(entity.stream().map(BufferData::create).toList()); - int length = bufferData.available(); + + int length = 0; + for (byte[] bytes : entity) { + length += bytes.length; + } res.header(CONTENT_LENGTH, String.valueOf(length)); + try (var out = res.outputStream()) { - bufferData.writeTo(out); + for (byte[] bytes : entity) { + out.write(bytes); + } } catch (IOException e) { throw new RuntimeException(e); } From dab117a50b37f02e538628be0a43a55fb9d0c1bf Mon Sep 17 00:00:00 2001 From: Santiago Pericas-Geertsen Date: Mon, 4 Nov 2024 14:59:10 -0500 Subject: [PATCH 04/12] New connection pool. Signed-off-by: Santiago Pericas-Geertsen --- .../nima/models/PgClientConnectionPool.java | 125 ++++++++++++++++++ .../nima/models/PgClientRepository.java | 92 ++++--------- .../nima/services/FortuneHandler.java | 3 + .../nima/src/main/resources/application.yaml | 1 - 4 files changed, 154 insertions(+), 67 deletions(-) create mode 100644 frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java new file mode 100644 index 00000000000..1f0605b1303 --- /dev/null +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java @@ -0,0 +1,125 @@ + +package io.helidon.benchmark.nima.models; + +import io.vertx.core.Vertx; +import io.vertx.pgclient.PgConnectOptions; +import io.vertx.pgclient.PgConnection; +import io.vertx.sqlclient.PreparedQuery; +import io.vertx.sqlclient.Row; +import io.vertx.sqlclient.RowSet; + +class PgClientConnectionPool implements AutoCloseable { + + private final int size; + private final Vertx vertx; + private final PgConnectOptions options; + private final PgClientConnection[] connections; + + public PgClientConnectionPool(Vertx vertx, int size, PgConnectOptions options) { + this.size = size; + this.vertx = vertx; + this.options = options; + this.connections = new PgClientConnection[size]; + } + + public PgClientConnection clientConnection() { + int bucket = Thread.currentThread().hashCode() % size; + return connections[bucket]; + } + + public void connect() { + try { + for (int i = 0; i < size; i++) { + PgConnection conn = PgConnection.connect(vertx, options) + .toCompletionStage().toCompletableFuture().get(); + PgClientConnection clientConn = new PgClientConnection(conn); + clientConn.prepare(); + connections[i] = clientConn; + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public void close() { + try { + for (PgClientConnection connection : connections) { + connection.close(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static class PgClientConnection implements AutoCloseable { + private static final int UPDATE_QUERIES = 500; + private static String SELECT_WORLD = "SELECT id, randomnumber from WORLD where id=$1"; + private static String SELECT_FORTUNE = "SELECT id, message from FORTUNE"; + + private PreparedQuery> worldQuery; + private PreparedQuery> fortuneQuery; + private PreparedQuery>[] updateQuery; + + private final PgConnection conn; + + PgClientConnection(PgConnection conn) { + this.conn = conn; + } + + public PgConnection pgConnection() { + return conn; + } + + @Override + public void close() { + conn.close(); + } + + public PreparedQuery> worldQuery() { + return worldQuery; + } + + public PreparedQuery> fortuneQuery() { + return fortuneQuery; + } + + public PreparedQuery> updateQuery(int queryCount) { + return updateQuery[queryCount - 1]; + } + + @SuppressWarnings("unchecked") + void prepare() { + try { + worldQuery = conn.prepare(SELECT_WORLD) + .toCompletionStage().toCompletableFuture().get().query(); + fortuneQuery = conn.prepare(SELECT_FORTUNE) + .toCompletionStage().toCompletableFuture().get().query(); + updateQuery = (PreparedQuery>[]) new PreparedQuery[UPDATE_QUERIES]; + for (int i = 0; i < UPDATE_QUERIES; i++) { + updateQuery[i] = conn.prepare(singleUpdate(i + 1)) + .toCompletionStage().toCompletableFuture().get().query(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static String singleUpdate(int count) { + StringBuilder sql = new StringBuilder(); + sql.append("UPDATE WORLD SET RANDOMNUMBER = CASE ID"); + for (int i = 0; i < count; i++) { + int k = i * 2 + 1; + sql.append(" WHEN $").append(k).append(" THEN $").append(k + 1); + } + sql.append(" ELSE RANDOMNUMBER"); + sql.append(" END WHERE ID IN ($1"); + for (int i = 1; i < count; i++) { + int k = i * 2 + 1; + sql.append(",$").append(k); + } + sql.append(")"); + return sql.toString(); + } + } +} diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java index 291131eca17..6c6da76e97e 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java @@ -2,7 +2,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ExecutionException; import java.util.logging.Logger; import io.helidon.config.Config; @@ -10,25 +9,17 @@ 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.sqlclient.PreparedQuery; import io.vertx.sqlclient.Row; import io.vertx.sqlclient.RowSet; -import io.vertx.sqlclient.SqlClient; import io.vertx.sqlclient.Tuple; import static io.helidon.benchmark.nima.models.DbRepository.randomWorldNumber; public class PgClientRepository implements DbRepository { private static final Logger LOGGER = Logger.getLogger(PgClientRepository.class.getName()); - private static final int UPDATE_QUERIES = 500; - private final SqlClient updatePool; - - private final PreparedQuery> getFortuneQuery; - private final PreparedQuery> getWorldQuery; - private final PreparedQuery>[] updateWorldSingleQuery; + private final PgClientConnectionPool connectionPool; @SuppressWarnings("unchecked") public PgClientRepository(Config config) { @@ -41,27 +32,16 @@ public PgClientRepository(Config config) { .setUser(config.get("username").asString().orElse("benchmarkdbuser")) .setPassword(config.get("password").asString().orElse("benchmarkdbpass")) .setPipeliningLimit(100000); - - int sqlPoolSize = config.get("sql-pool-size").asInt().orElse(64); - PoolOptions clientOptions = new PoolOptions().setMaxSize(sqlPoolSize); - LOGGER.info("sql-pool-size is " + sqlPoolSize); - - SqlClient queryPool = PgPool.client(vertx, connectOptions, clientOptions); - updatePool = PgPool.client(vertx, connectOptions, clientOptions); - - getWorldQuery = queryPool.preparedQuery("SELECT id, randomnumber FROM world WHERE id = $1"); - getFortuneQuery = queryPool.preparedQuery("SELECT id, message FROM fortune"); - - updateWorldSingleQuery = new PreparedQuery[UPDATE_QUERIES]; - for (int i = 0; i < UPDATE_QUERIES; i++) { - updateWorldSingleQuery[i] = queryPool.preparedQuery(singleUpdate(i + 1)); - } + int sqlPoolSize = config.get("sql-pool-size").asInt().orElse(Runtime.getRuntime().availableProcessors()); + connectionPool = new PgClientConnectionPool(vertx, sqlPoolSize, connectOptions); + connectionPool.connect(); } @Override public World getWorld(int id) { try { - return getWorldQuery.execute(Tuple.of(id)) + PreparedQuery> worldQuery = connectionPool.clientConnection().worldQuery(); + return worldQuery.execute(Tuple.of(id)) .map(rows -> { Row r = rows.iterator().next(); return new World(r.getInteger(0), r.getInteger(1)); @@ -74,13 +54,14 @@ public World getWorld(int id) { @Override public List getWorlds(int count) { try { + PreparedQuery> worldQuery = connectionPool.clientConnection().worldQuery(); List> futures = new ArrayList<>(); for (int i = 0; i < count; i++) { - futures.add(getWorldQuery.execute(Tuple.of(randomWorldNumber())) - .map(rows -> { - Row r = rows.iterator().next(); - return new World(r.getInteger(0), r.getInteger(1)); - })); + futures.add(worldQuery.execute(Tuple.of(randomWorldNumber())) + .map(rows -> { + Row r = rows.iterator().next(); + return new World(r.getInteger(0), r.getInteger(1)); + })); } return Future.all(futures).toCompletionStage().toCompletableFuture().get().list(); } catch (Exception e) { @@ -92,7 +73,18 @@ public List getWorlds(int count) { public List updateWorlds(int count) { List worlds = getWorlds(count); try { - return updateWorlds(worlds, count, updatePool); + PreparedQuery> updateQuery = connectionPool.clientConnection().updateQuery(count); + List updateParams = new ArrayList<>(count * 2); + for (World world : worlds) { + updateParams.add(world.id); + world.randomNumber = randomWorldNumber(); + updateParams.add(world.randomNumber); + } + return updateQuery.execute(Tuple.wrap(updateParams)) + .toCompletionStage() + .thenApply(rows -> worlds) + .toCompletableFuture() + .get(); } catch (Exception e) { throw new RuntimeException(e); } @@ -101,7 +93,8 @@ public List updateWorlds(int count) { @Override public List getFortunes() { try { - return getFortuneQuery.execute() + PreparedQuery> fortuneQuery = connectionPool.clientConnection().fortuneQuery(); + return fortuneQuery.execute() .map(rows -> { List fortunes = new ArrayList<>(rows.size() + 1); for (Row r : rows) { @@ -113,37 +106,4 @@ public List getFortunes() { throw new RuntimeException(e); } } - - private List updateWorlds(List worlds, int count, SqlClient pool) - throws ExecutionException, InterruptedException { - int size = worlds.size(); - List updateParams = new ArrayList<>(size * 2); - for (World world : worlds) { - updateParams.add(world.id); - world.randomNumber = randomWorldNumber(); - updateParams.add(world.randomNumber); - } - return updateWorldSingleQuery[count - 1].execute(Tuple.wrap(updateParams)) - .toCompletionStage() - .thenApply(rows -> worlds) - .toCompletableFuture() - .get(); - } - - private static String singleUpdate(int count) { - StringBuilder sql = new StringBuilder(); - sql.append("UPDATE WORLD SET RANDOMNUMBER = CASE ID"); - for (int i = 0; i < count; i++) { - int k = i * 2 + 1; - sql.append(" WHEN $").append(k).append(" THEN $").append(k + 1); - } - sql.append(" ELSE RANDOMNUMBER"); - sql.append(" END WHERE ID IN ($1"); - for (int i = 1; i < count; i++) { - int k = i * 2 + 1; - sql.append(",$").append(k); - } - sql.append(")"); - return sql.toString(); - } } \ No newline at end of file diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java index 3d6e9ba4cfb..96871280513 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java @@ -33,18 +33,21 @@ public void handle(ServerRequest req, ServerResponse res) { res.header(SERVER); res.header(CONTENT_TYPE_HTML); + // render using template and get list of buffers List fortuneList = repository.getFortunes(); fortuneList.add(ADDITIONAL_FORTUNE); Collections.sort(fortuneList); ArrayOfByteArraysOutput output = fortunes.template(fortuneList).render(ArrayOfByteArraysOutput.FACTORY); List entity = output.getArrays(); + // compute entity length and set header int length = 0; for (byte[] bytes : entity) { length += bytes.length; } res.header(CONTENT_LENGTH, String.valueOf(length)); + // write entity to output try (var out = res.outputStream()) { for (byte[] bytes : entity) { out.write(bytes); diff --git a/frameworks/Java/helidon/nima/src/main/resources/application.yaml b/frameworks/Java/helidon/nima/src/main/resources/application.yaml index d2d8e8943b4..4a26ea0d0e0 100644 --- a/frameworks/Java/helidon/nima/src/main/resources/application.yaml +++ b/frameworks/Java/helidon/nima/src/main/resources/application.yaml @@ -36,6 +36,5 @@ host: "tfb-database" db: "hello_world" username: benchmarkdbuser password: benchmarkdbpass -sql-pool-size: 300 db-repository: "pgclient" # "pgclient" (default) or "hikari" From 249c88206c9f7f00da5e2379ce7af522f28c62a8 Mon Sep 17 00:00:00 2001 From: Santiago Pericas-Geertsen Date: Tue, 5 Nov 2024 09:21:01 -0500 Subject: [PATCH 05/12] Old fortune handler. Signed-off-by: Santiago Pericas-Geertsen --- .../nima/services/FortuneHandler.java | 29 ++++--------------- 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java index 96871280513..8847b7d0bc1 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java @@ -1,8 +1,7 @@ package io.helidon.benchmark.nima.services; -import java.io.IOException; -import java.util.Collections; +import java.util.Comparator; import java.util.List; import com.fizzed.rocker.runtime.ArrayOfByteArraysOutput; @@ -15,7 +14,6 @@ import static io.helidon.benchmark.nima.Main.CONTENT_TYPE_HTML; import static io.helidon.benchmark.nima.Main.SERVER; -import static io.helidon.http.HeaderNames.CONTENT_LENGTH; public class FortuneHandler implements Handler { @@ -32,28 +30,11 @@ public FortuneHandler(DbRepository repository) { public void handle(ServerRequest req, ServerResponse res) { res.header(SERVER); res.header(CONTENT_TYPE_HTML); - - // render using template and get list of buffers List fortuneList = repository.getFortunes(); fortuneList.add(ADDITIONAL_FORTUNE); - Collections.sort(fortuneList); - ArrayOfByteArraysOutput output = fortunes.template(fortuneList).render(ArrayOfByteArraysOutput.FACTORY); - List entity = output.getArrays(); - - // compute entity length and set header - int length = 0; - for (byte[] bytes : entity) { - length += bytes.length; - } - res.header(CONTENT_LENGTH, String.valueOf(length)); - - // write entity to output - try (var out = res.outputStream()) { - for (byte[] bytes : entity) { - out.write(bytes); - } - } catch (IOException e) { - throw new RuntimeException(e); - } + fortuneList.sort(Comparator.comparing(Fortune::getMessage)); + res.send(fortunes.template(fortuneList) + .render(ArrayOfByteArraysOutput.FACTORY) + .toByteArray()); } } \ No newline at end of file From cc9ef0e197fd34cd640e406776fd2d0c3b263a0b Mon Sep 17 00:00:00 2001 From: Santiago Pericas-Geertsen Date: Tue, 5 Nov 2024 13:37:26 -0500 Subject: [PATCH 06/12] Lazy connections. Signed-off-by: Santiago Pericas-Geertsen --- .../nima/models/PgClientConnectionPool.java | 35 ++++++++++++------- .../nima/models/PgClientRepository.java | 1 - 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java index 1f0605b1303..2ac2e13efb3 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java @@ -1,6 +1,8 @@ package io.helidon.benchmark.nima.models; +import java.util.concurrent.locks.ReentrantLock; + import io.vertx.core.Vertx; import io.vertx.pgclient.PgConnectOptions; import io.vertx.pgclient.PgConnection; @@ -14,6 +16,7 @@ class PgClientConnectionPool implements AutoCloseable { private final Vertx vertx; private final PgConnectOptions options; private final PgClientConnection[] connections; + private final ReentrantLock lock = new ReentrantLock(); public PgClientConnectionPool(Vertx vertx, int size, PgConnectOptions options) { this.size = size; @@ -24,29 +27,37 @@ public PgClientConnectionPool(Vertx vertx, int size, PgConnectOptions options) { public PgClientConnection clientConnection() { int bucket = Thread.currentThread().hashCode() % size; + if (connections[bucket] == null) { + try { + lock.lock(); + if (connections[bucket] == null) { + connect(bucket); + } + } finally { + lock.unlock(); + } + } return connections[bucket]; } - public void connect() { + @Override + public void close() { try { - for (int i = 0; i < size; i++) { - PgConnection conn = PgConnection.connect(vertx, options) - .toCompletionStage().toCompletableFuture().get(); - PgClientConnection clientConn = new PgClientConnection(conn); - clientConn.prepare(); - connections[i] = clientConn; + for (PgClientConnection connection : connections) { + connection.close(); } } catch (Exception e) { throw new RuntimeException(e); } } - @Override - public void close() { + private void connect(int bucket) { try { - for (PgClientConnection connection : connections) { - connection.close(); - } + PgConnection conn = PgConnection.connect(vertx, options) + .toCompletionStage().toCompletableFuture().get(); + PgClientConnection clientConn = new PgClientConnection(conn); + clientConn.prepare(); + connections[bucket] = clientConn; } catch (Exception e) { throw new RuntimeException(e); } diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java index 6c6da76e97e..a02c0786e0e 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java @@ -34,7 +34,6 @@ public PgClientRepository(Config config) { .setPipeliningLimit(100000); int sqlPoolSize = config.get("sql-pool-size").asInt().orElse(Runtime.getRuntime().availableProcessors()); connectionPool = new PgClientConnectionPool(vertx, sqlPoolSize, connectOptions); - connectionPool.connect(); } @Override From 6521f3608282e0a2107473ae0ab304f0460768ba Mon Sep 17 00:00:00 2001 From: Santiago Pericas-Geertsen Date: Tue, 26 Nov 2024 14:14:43 -0500 Subject: [PATCH 07/12] New connection pool using carrier threads. Signed-off-by: Santiago Pericas-Geertsen --- .../nima/models/PgClientConnectionPool.java | 35 +++++++++++-------- .../nima/models/PgClientRepository.java | 16 ++++++--- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java index 2ac2e13efb3..520c781af1b 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java @@ -1,6 +1,8 @@ package io.helidon.benchmark.nima.models; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.locks.ReentrantLock; import io.vertx.core.Vertx; @@ -12,38 +14,38 @@ class PgClientConnectionPool implements AutoCloseable { - private final int size; private final Vertx vertx; private final PgConnectOptions options; - private final PgClientConnection[] connections; private final ReentrantLock lock = new ReentrantLock(); + private final Map connectionMap = new HashMap<>(); - public PgClientConnectionPool(Vertx vertx, int size, PgConnectOptions options) { - this.size = size; + public PgClientConnectionPool(Vertx vertx, PgConnectOptions options) { this.vertx = vertx; this.options = options; - this.connections = new PgClientConnection[size]; } public PgClientConnection clientConnection() { - int bucket = Thread.currentThread().hashCode() % size; - if (connections[bucket] == null) { + String carrierThread = carrierThread(); + PgClientConnection connection = connectionMap.get(carrierThread); + if (connection == null) { try { lock.lock(); - if (connections[bucket] == null) { - connect(bucket); + connection = connectionMap.get(carrierThread); + if (connection == null) { + connection = newConnection(); + connectionMap.put(carrierThread, connection); } } finally { lock.unlock(); } } - return connections[bucket]; + return connection; } @Override public void close() { try { - for (PgClientConnection connection : connections) { + for (PgClientConnection connection : connectionMap.values()) { connection.close(); } } catch (Exception e) { @@ -51,20 +53,25 @@ public void close() { } } - private void connect(int bucket) { + private PgClientConnection newConnection() { try { PgConnection conn = PgConnection.connect(vertx, options) .toCompletionStage().toCompletableFuture().get(); PgClientConnection clientConn = new PgClientConnection(conn); clientConn.prepare(); - connections[bucket] = clientConn; + return clientConn; } catch (Exception e) { throw new RuntimeException(e); } } + static String carrierThread() { + String threadName = Thread.currentThread().toString(); + return threadName.substring(threadName.indexOf('@') + 1); + } + public static class PgClientConnection implements AutoCloseable { - private static final int UPDATE_QUERIES = 500; + static final int UPDATE_QUERIES = 500; private static String SELECT_WORLD = "SELECT id, randomnumber from WORLD where id=$1"; private static String SELECT_FORTUNE = "SELECT id, message from FORTUNE"; diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java index a02c0786e0e..3f3bd2624eb 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java @@ -15,6 +15,7 @@ import io.vertx.sqlclient.Tuple; import static io.helidon.benchmark.nima.models.DbRepository.randomWorldNumber; +import static io.helidon.benchmark.nima.models.PgClientConnectionPool.PgClientConnection.UPDATE_QUERIES; public class PgClientRepository implements DbRepository { private static final Logger LOGGER = Logger.getLogger(PgClientRepository.class.getName()); @@ -23,17 +24,24 @@ public class PgClientRepository implements DbRepository { @SuppressWarnings("unchecked") public PgClientRepository(Config config) { - Vertx vertx = Vertx.vertx(new VertxOptions().setPreferNativeTransport(true)); + VertxOptions vertxOptions = new VertxOptions() + .setPreferNativeTransport(true) + .setBlockedThreadCheckInterval(100000); + Vertx vertx = Vertx.vertx(vertxOptions); PgConnectOptions connectOptions = new PgConnectOptions() .setPort(config.get("port").asInt().orElse(5432)) - .setCachePreparedStatements(config.get("cache-prepared-statements").asBoolean().orElse(true)) .setHost(config.get("host").asString().orElse("tfb-database")) .setDatabase(config.get("db").asString().orElse("hello_world")) .setUser(config.get("username").asString().orElse("benchmarkdbuser")) .setPassword(config.get("password").asString().orElse("benchmarkdbpass")) + .setCachePreparedStatements(true) + .setPreparedStatementCacheMaxSize(UPDATE_QUERIES + 2) + .setPreparedStatementCacheSqlFilter(s -> true) // cache all + .setTcpNoDelay(true) + .setTcpQuickAck(true) + .setTcpKeepAlive(true) .setPipeliningLimit(100000); - int sqlPoolSize = config.get("sql-pool-size").asInt().orElse(Runtime.getRuntime().availableProcessors()); - connectionPool = new PgClientConnectionPool(vertx, sqlPoolSize, connectOptions); + connectionPool = new PgClientConnectionPool(vertx, connectOptions); } @Override From 96c3a1b9b003854a852837fcefcf5b1b46fe68e0 Mon Sep 17 00:00:00 2001 From: Santiago Pericas-Geertsen Date: Tue, 26 Nov 2024 17:46:16 -0500 Subject: [PATCH 08/12] Updated fortunes query. Signed-off-by: Santiago Pericas-Geertsen --- .../helidon/benchmark/nima/models/PgClientConnectionPool.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java index 520c781af1b..478847702cb 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java @@ -73,7 +73,7 @@ static String carrierThread() { public static class PgClientConnection implements AutoCloseable { static final int UPDATE_QUERIES = 500; private static String SELECT_WORLD = "SELECT id, randomnumber from WORLD where id=$1"; - private static String SELECT_FORTUNE = "SELECT id, message from FORTUNE"; + private static String SELECT_FORTUNE = "SELECT * from FORTUNE"; private PreparedQuery> worldQuery; private PreparedQuery> fortuneQuery; From 2fbd1d3e49efea28f6f2adf0f16c20c884a7e870 Mon Sep 17 00:00:00 2001 From: Santiago Pericas-Geertsen Date: Tue, 26 Nov 2024 18:13:42 -0500 Subject: [PATCH 09/12] Cleans fortunes template. Signed-off-by: Santiago Pericas-Geertsen --- .../main/resources/views/fortunes.rocker.html | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/frameworks/Java/helidon/nima/src/main/resources/views/fortunes.rocker.html b/frameworks/Java/helidon/nima/src/main/resources/views/fortunes.rocker.html index 3ebcae44729..86b8c004692 100644 --- a/frameworks/Java/helidon/nima/src/main/resources/views/fortunes.rocker.html +++ b/frameworks/Java/helidon/nima/src/main/resources/views/fortunes.rocker.html @@ -1,24 +1,4 @@ @import io.helidon.benchmark.nima.models.Fortune @import java.util.List @args (List fortunes) - - - - -Fortunes - - - - - - - - @for (f : fortunes) { - - - - - } -
idmessage
@f.getId()@f.getMessage()
- - \ No newline at end of file +Fortunes@for (f : fortunes) {}
idmessage
@f.getId()@f.getMessage()
From d669122a516d679d678e56333f0e27a4cbb021ef Mon Sep 17 00:00:00 2001 From: Santiago Pericas-Geertsen Date: Wed, 27 Nov 2024 10:22:56 -0500 Subject: [PATCH 10/12] Using jte. Signed-off-by: Santiago Pericas-Geertsen --- frameworks/Java/helidon/nima/pom.xml | 23 +++++----- .../nima/services/FortuneHandler.java | 45 ++++++++++++++++--- .../src/main/resources/views/fortunes.jte | 2 + .../main/resources/views/fortunes.rocker.html | 4 -- 4 files changed, 54 insertions(+), 20 deletions(-) create mode 100644 frameworks/Java/helidon/nima/src/main/resources/views/fortunes.jte delete mode 100644 frameworks/Java/helidon/nima/src/main/resources/views/fortunes.rocker.html diff --git a/frameworks/Java/helidon/nima/pom.xml b/frameworks/Java/helidon/nima/pom.xml index c859b758cfc..d240518fc59 100644 --- a/frameworks/Java/helidon/nima/pom.xml +++ b/frameworks/Java/helidon/nima/pom.xml @@ -38,6 +38,7 @@ 1.3.0 4.5.3 0.9.23 + 3.1.15 @@ -78,9 +79,9 @@ 42.6.1 - com.fizzed - rocker-runtime - ${rocker.version} + gg.jte + jte + ${jte.version} io.helidon.common.testing @@ -98,7 +99,6 @@ test - @@ -125,20 +125,21 @@ + - com.fizzed - rocker-maven-plugin - ${rocker.version} + gg.jte + jte-maven-plugin + ${jte.version} + + ${project.basedir}/src/main/resources/views + Html + - generate-rocker-templates generate-sources generate - - src/main/resources - diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java index 8847b7d0bc1..b821f8f9261 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java @@ -1,16 +1,21 @@ package io.helidon.benchmark.nima.services; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import java.util.Comparator; import java.util.List; -import com.fizzed.rocker.runtime.ArrayOfByteArraysOutput; +import gg.jte.TemplateOutput; import io.helidon.benchmark.nima.models.DbRepository; import io.helidon.benchmark.nima.models.Fortune; import io.helidon.webserver.http.Handler; import io.helidon.webserver.http.ServerRequest; import io.helidon.webserver.http.ServerResponse; -import views.fortunes; + +import gg.jte.html.OwaspHtmlTemplateOutput; +import gg.jte.generated.precompiled.JtefortunesGenerated; import static io.helidon.benchmark.nima.Main.CONTENT_TYPE_HTML; import static io.helidon.benchmark.nima.Main.SERVER; @@ -33,8 +38,38 @@ public void handle(ServerRequest req, ServerResponse res) { List fortuneList = repository.getFortunes(); fortuneList.add(ADDITIONAL_FORTUNE); fortuneList.sort(Comparator.comparing(Fortune::getMessage)); - res.send(fortunes.template(fortuneList) - .render(ArrayOfByteArraysOutput.FACTORY) - .toByteArray()); + try (OutputStream os = res.outputStream()) { + JtefortunesGenerated.render(new OwaspHtmlTemplateOutput(new HelidonTemplateOutput(os)), + null, fortuneList); + } catch (IOException e) { + throw new RuntimeException(e); + } } + + static class HelidonTemplateOutput implements TemplateOutput{ + private final OutputStream os; + + HelidonTemplateOutput(OutputStream os) { + this.os = os; + } + + @Override + public void writeContent(String value) { + writeBinaryContent(value.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public void writeContent(String value, int beginIndex, int endIndex) { + writeBinaryContent(value.substring(beginIndex, endIndex).getBytes(StandardCharsets.UTF_8)); + } + + @Override + public void writeBinaryContent(byte[] value) { + try { + os.write(value); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; } \ No newline at end of file diff --git a/frameworks/Java/helidon/nima/src/main/resources/views/fortunes.jte b/frameworks/Java/helidon/nima/src/main/resources/views/fortunes.jte new file mode 100644 index 00000000000..52dc350b25f --- /dev/null +++ b/frameworks/Java/helidon/nima/src/main/resources/views/fortunes.jte @@ -0,0 +1,2 @@ +@param java.util.List fortunes +Fortunes@for(io.helidon.benchmark.nima.models.Fortune fortune : fortunes)@endfor
idmessage
${fortune.getId()}${fortune.getMessage()}
\ No newline at end of file diff --git a/frameworks/Java/helidon/nima/src/main/resources/views/fortunes.rocker.html b/frameworks/Java/helidon/nima/src/main/resources/views/fortunes.rocker.html deleted file mode 100644 index 86b8c004692..00000000000 --- a/frameworks/Java/helidon/nima/src/main/resources/views/fortunes.rocker.html +++ /dev/null @@ -1,4 +0,0 @@ -@import io.helidon.benchmark.nima.models.Fortune -@import java.util.List -@args (List fortunes) -Fortunes@for (f : fortunes) {}
idmessage
@f.getId()@f.getMessage()
From 8c47f6c81a72fd99b2282ed29e0f1369d89407b4 Mon Sep 17 00:00:00 2001 From: Santiago Pericas-Geertsen Date: Thu, 16 Jan 2025 09:17:17 -0500 Subject: [PATCH 11/12] Updates Helidon version to 4.1.5. Signed-off-by: Santiago Pericas-Geertsen --- frameworks/Java/helidon/nima/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/Java/helidon/nima/pom.xml b/frameworks/Java/helidon/nima/pom.xml index d240518fc59..0d312c8eed8 100644 --- a/frameworks/Java/helidon/nima/pom.xml +++ b/frameworks/Java/helidon/nima/pom.xml @@ -21,7 +21,7 @@ io.helidon.applications helidon-se - 4.1.2 + 4.1.5 From 437a76eb1a0f5d31fe48a700fb7a9241590dde2a Mon Sep 17 00:00:00 2001 From: Santiago Pericas-Geertsen Date: Thu, 16 Jan 2025 10:23:18 -0500 Subject: [PATCH 12/12] Updates code to use new send method. Signed-off-by: Santiago Pericas-Geertsen --- .../benchmark/nima/JsonSerializer.java | 41 +++++++------- .../java/io/helidon/benchmark/nima/Main.java | 28 ++++++---- .../benchmark/nima/services/DbService.java | 53 ++++++++++++------- 3 files changed, 75 insertions(+), 47 deletions(-) diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/JsonSerializer.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/JsonSerializer.java index 322a7cf030c..0564fa8a911 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/JsonSerializer.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/JsonSerializer.java @@ -2,8 +2,8 @@ import java.io.IOException; import java.util.Arrays; -import java.util.Map; import java.util.List; +import java.util.Map; import com.jsoniter.output.JsonStream; import com.jsoniter.output.JsonStreamPool; @@ -15,7 +15,7 @@ private JsonSerializer() { } /** - * Serialize an instance into a JSON object and return it as a byte array. + * Serialize an instance into a byte array. * * @param obj the instance * @return the byte array @@ -28,19 +28,31 @@ public static byte[] serialize(Object obj) { return Arrays.copyOfRange(stream.buffer().data(), 0, stream.buffer().tail()); } catch (IOException e) { throw new JsonException(e); - } finally { - JsonStreamPool.returnJsonStream(stream); } } /** - * Serialize a map of strings into a JSON object and return it as a byte array. + * Serialize an instance into a JSON stream. + * + * @param obj the instance + * @param stream the JSON stream + */ + public static void serialize(Object obj, JsonStream stream) { + try { + stream.reset(null); + stream.writeVal(obj.getClass(), obj); + } catch (IOException e) { + throw new JsonException(e); + } + } + + /** + * Serialize a map of strings into a JSON stream. * * @param map the map - * @return the byte array + * @param stream the JSON stream */ - public static byte[] serialize(Map map) { - JsonStream stream = JsonStreamPool.borrowJsonStream(); + public static void serialize(Map map, JsonStream stream) { try { stream.reset(null); stream.writeObjectStart(); @@ -53,22 +65,18 @@ public static byte[] serialize(Map map) { } }); stream.writeObjectEnd(); - return Arrays.copyOfRange(stream.buffer().data(), 0, stream.buffer().tail()); } catch (IOException e) { throw new JsonException(e); - } finally { - JsonStreamPool.returnJsonStream(stream); } } /** - * Serialize a list of objects into a JSON array and return it as a byte array. + * Serialize a list of objects into a JSON stream. * * @param objs the list of objects - * @return the byte array + * @param stream the JSON stream */ - public static byte[] serialize(List objs) { - JsonStream stream = JsonStreamPool.borrowJsonStream(); + public static void serialize(List objs, JsonStream stream) { try { stream.reset(null); stream.writeArrayStart(); @@ -82,11 +90,8 @@ public static byte[] serialize(List objs) { } stream.writeArrayEnd(); - return Arrays.copyOfRange(stream.buffer().data(), 0, stream.buffer().tail()); } catch (IOException e) { throw new JsonException(e); - } finally { - JsonStreamPool.returnJsonStream(stream); } } } diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/Main.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/Main.java index df669d8a7a7..800ce927537 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/Main.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/Main.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Oracle and/or its affiliates. + * Copyright (c) 2022, 2025 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,16 +19,18 @@ import java.nio.charset.StandardCharsets; import java.util.logging.Logger; +import com.jsoniter.output.JsonStream; +import com.jsoniter.output.JsonStreamPool; import io.helidon.benchmark.nima.models.DbRepository; import io.helidon.benchmark.nima.models.HikariJdbcRepository; import io.helidon.benchmark.nima.models.PgClientRepository; import io.helidon.benchmark.nima.services.DbService; import io.helidon.benchmark.nima.services.FortuneHandler; +import io.helidon.config.Config; +import io.helidon.config.ConfigException; import io.helidon.http.Header; import io.helidon.http.HeaderNames; import io.helidon.http.HeaderValues; -import io.helidon.config.Config; -import io.helidon.config.ConfigException; import io.helidon.logging.common.LogConfig; import io.helidon.webserver.WebServer; import io.helidon.webserver.http.Handler; @@ -93,7 +95,7 @@ static void routing(HttpRules rules) { static class PlaintextHandler implements Handler { static final Header CONTENT_TYPE = HeaderValues.createCached(HeaderNames.CONTENT_TYPE, - "text/plain; charset=UTF-8"); + "text/plain; charset=UTF-8"); static final Header CONTENT_LENGTH = HeaderValues.createCached(HeaderNames.CONTENT_LENGTH, "13"); private static final byte[] RESPONSE_BYTES = "Hello, World!".getBytes(StandardCharsets.UTF_8); @@ -110,14 +112,20 @@ static class JsonHandler implements Handler { private static final String MESSAGE = "Hello, World!"; private static final int JSON_LENGTH = serialize(new Message(MESSAGE)).length; static final Header CONTENT_LENGTH = HeaderValues.createCached(HeaderNames.CONTENT_LENGTH, - String.valueOf(JSON_LENGTH)); + String.valueOf(JSON_LENGTH)); @Override public void handle(ServerRequest req, ServerResponse res) { - res.header(CONTENT_LENGTH); - res.header(HeaderValues.CONTENT_TYPE_JSON); - res.header(Main.SERVER); - res.send(serialize(new Message(MESSAGE))); + JsonStream stream = JsonStreamPool.borrowJsonStream(); + try { + res.header(CONTENT_LENGTH); + res.header(HeaderValues.CONTENT_TYPE_JSON); + res.header(Main.SERVER); + serialize(new Message(MESSAGE), stream); + res.send(stream.buffer().data(), 0, stream.buffer().tail()); + } finally { + JsonStreamPool.returnJsonStream(stream); + } } } @@ -147,4 +155,4 @@ public String getMessage() { return message; } } -} +} \ No newline at end of file diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/DbService.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/DbService.java index e3bd1fe39fc..a1e97de44b5 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/DbService.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/DbService.java @@ -1,21 +1,19 @@ - package io.helidon.benchmark.nima.services; -import java.util.List; - +import com.jsoniter.output.JsonStream; +import com.jsoniter.output.JsonStreamPool; import io.helidon.benchmark.nima.models.DbRepository; -import io.helidon.benchmark.nima.models.World; +import io.helidon.common.mapper.OptionalValue; import io.helidon.common.parameters.Parameters; import io.helidon.http.HeaderValues; import io.helidon.webserver.http.HttpRules; import io.helidon.webserver.http.HttpService; import io.helidon.webserver.http.ServerRequest; import io.helidon.webserver.http.ServerResponse; -import io.helidon.common.mapper.OptionalValue; +import static io.helidon.benchmark.nima.JsonSerializer.serialize; import static io.helidon.benchmark.nima.Main.SERVER; import static io.helidon.benchmark.nima.models.DbRepository.randomWorldNumber; -import static io.helidon.benchmark.nima.JsonSerializer.serialize; public class DbService implements HttpService { @@ -33,24 +31,41 @@ public void routing(HttpRules httpRules) { } private void db(ServerRequest req, ServerResponse res) { - res.header(SERVER); - res.header(HeaderValues.CONTENT_TYPE_JSON); - res.send(serialize(repository.getWorld(randomWorldNumber()))); + JsonStream stream = JsonStreamPool.borrowJsonStream(); + try { + res.header(SERVER); + res.header(HeaderValues.CONTENT_TYPE_JSON); + serialize(repository.getWorld(randomWorldNumber()), stream); + res.send(stream.buffer().data(), 0, stream.buffer().tail()); + } finally { + JsonStreamPool.returnJsonStream(stream); + } } private void queries(ServerRequest req, ServerResponse res) { - res.header(SERVER); - res.header(HeaderValues.CONTENT_TYPE_JSON); - int count = parseQueryCount(req.query()); - res.send(serialize(repository.getWorlds(count))); + JsonStream stream = JsonStreamPool.borrowJsonStream(); + try { + res.header(SERVER); + res.header(HeaderValues.CONTENT_TYPE_JSON); + int count = parseQueryCount(req.query()); + serialize(repository.getWorlds(count), stream); + res.send(stream.buffer().data(), 0, stream.buffer().tail()); + } finally { + JsonStreamPool.returnJsonStream(stream); + } } private void updates(ServerRequest req, ServerResponse res) { - res.header(SERVER); - res.header(HeaderValues.CONTENT_TYPE_JSON); - int count = parseQueryCount(req.query()); - List worlds = repository.updateWorlds(count); - res.send(serialize(worlds)); + JsonStream stream = JsonStreamPool.borrowJsonStream(); + try { + res.header(SERVER); + res.header(HeaderValues.CONTENT_TYPE_JSON); + int count = parseQueryCount(req.query()); + serialize(repository.updateWorlds(count), stream); + res.send(stream.buffer().data(), 0, stream.buffer().tail()); + } finally { + JsonStreamPool.returnJsonStream(stream); + } } private int parseQueryCount(Parameters parameters) { @@ -66,4 +81,4 @@ private int parseQueryCount(Parameters parameters) { } return Math.min(500, Math.max(1, parsedValue)); } -} \ No newline at end of file +}