|
@@ -1,5 +1,7 @@
|
|
|
package com.techempower;
|
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Arrays;
|
|
|
import java.util.List;
|
|
|
import java.util.concurrent.ExecutionException;
|
|
|
import java.util.function.BiConsumer;
|
|
@@ -32,10 +34,13 @@ public class PgClient {
|
|
|
private static final String SELECT_FORTUNE = "SELECT id, message from FORTUNE";
|
|
|
|
|
|
private static class DbConnection {
|
|
|
+ private SqlClientInternal queries;
|
|
|
private PreparedQuery<RowSet<Row>> SELECT_WORLD_QUERY;
|
|
|
private PreparedQuery<RowSet<Row>> SELECT_FORTUNE_QUERY;
|
|
|
private PreparedQuery<RowSet<Row>> UPDATE_WORLD_QUERY;
|
|
|
- private SqlClientInternal connection;
|
|
|
+ private SqlClientInternal updates;
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private PreparedQuery<RowSet<Row>>[] AGGREGATED_UPDATE_WORLD_QUERY = new PreparedQuery[128];
|
|
|
}
|
|
|
|
|
|
private static class DbConnectionFactory extends ThreadLocal<DbConnection> {
|
|
@@ -64,20 +69,32 @@ public class PgClient {
|
|
|
.setWorkerPoolSize(1)
|
|
|
.setInternalBlockingPoolSize(1)
|
|
|
);
|
|
|
- var future = PgConnection.connect(vertx, options)
|
|
|
+ var client1 = PgConnection.connect(vertx, options)
|
|
|
.flatMap(conn -> {
|
|
|
- result.connection = (SqlClientInternal) conn;
|
|
|
+ result.queries = (SqlClientInternal) conn;
|
|
|
Future<PreparedStatement> f1 = conn.prepare(SELECT_WORLD)
|
|
|
.andThen(onSuccess(ps -> result.SELECT_WORLD_QUERY = ps.query()));
|
|
|
Future<PreparedStatement> f2 = conn.prepare(SELECT_FORTUNE)
|
|
|
.andThen(onSuccess(ps -> result.SELECT_FORTUNE_QUERY = ps.query()));
|
|
|
- Future<PreparedStatement> f3 = conn.prepare(UPDATE_WORLD)
|
|
|
- .andThen(onSuccess(ps -> result.UPDATE_WORLD_QUERY = ps.query()));
|
|
|
- return Future.join(f1, f2, f3);
|
|
|
- })
|
|
|
- .toCompletionStage()
|
|
|
- .toCompletableFuture()
|
|
|
- .get();
|
|
|
+ return Future.join(f1, f2);
|
|
|
+ });
|
|
|
+
|
|
|
+ var client2 = PgConnection.connect(vertx, options)
|
|
|
+ .flatMap(conn -> {
|
|
|
+ result.updates = (SqlClientInternal) conn;
|
|
|
+ List<Future<?>> list = new ArrayList<>();
|
|
|
+ Future<PreparedStatement> f1 = conn.prepare(UPDATE_WORLD)
|
|
|
+ .andThen(onSuccess(ps -> result.UPDATE_WORLD_QUERY = ps.query()));
|
|
|
+ list.add(f1);
|
|
|
+ for (int i = 0; i < result.AGGREGATED_UPDATE_WORLD_QUERY.length; i++) {
|
|
|
+ int idx = i;
|
|
|
+ list.add(conn
|
|
|
+ .prepare(buildAggregatedUpdateQuery(1 + idx))
|
|
|
+ .andThen(onSuccess(ps -> result.AGGREGATED_UPDATE_WORLD_QUERY[idx] = ps.query())));
|
|
|
+ }
|
|
|
+ return Future.join(list);
|
|
|
+ });
|
|
|
+ var future = Future.join(client1, client2).toCompletionStage().toCompletableFuture().get();
|
|
|
|
|
|
Throwable cause = future.cause();
|
|
|
if (cause != null) {
|
|
@@ -91,6 +108,18 @@ public class PgClient {
|
|
|
throw SneakyThrows.propagate(ex.getCause());
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ private static String buildAggregatedUpdateQuery(int len) {
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ sb.append("UPDATE world SET randomNumber = update_data.randomNumber FROM (VALUES");
|
|
|
+ char sep = ' ';
|
|
|
+ for (int i = 1;i <= len;i++) {
|
|
|
+ sb.append(sep).append("($").append(2 * i - 1).append("::int,$").append(2 * i).append("::int)");
|
|
|
+ sep = ',';
|
|
|
+ }
|
|
|
+ sb.append(") AS update_data (id, randomNumber) WHERE world.id = update_data.id");
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private final ThreadLocal<DbConnection> sqlClient;
|
|
@@ -104,7 +133,7 @@ public class PgClient {
|
|
|
}
|
|
|
|
|
|
public void selectWorlds(int queries, Handler<AsyncResult<RowSet<Row>>> handler) {
|
|
|
- this.sqlClient.get().connection.group(c -> {
|
|
|
+ this.sqlClient.get().queries.group(c -> {
|
|
|
for (int i = 0; i < queries; i++) {
|
|
|
c.preparedQuery(SELECT_WORLD).execute(Tuple.of(Util.randomWorld()), handler);
|
|
|
}
|
|
@@ -117,7 +146,7 @@ public class PgClient {
|
|
|
|
|
|
public void selectWorldForUpdate(int queries,
|
|
|
BiConsumer<Integer, PreparedQuery<RowSet<Row>>> consumer) {
|
|
|
- this.sqlClient.get().connection.group(c -> {
|
|
|
+ this.sqlClient.get().queries.group(c -> {
|
|
|
PreparedQuery<RowSet<Row>> statement = c.preparedQuery(SELECT_WORLD);
|
|
|
for (int i = 0; i < queries; i++) {
|
|
|
consumer.accept(i, statement);
|
|
@@ -125,8 +154,26 @@ public class PgClient {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- public void updateWorld(List<Tuple> batch, Handler<AsyncResult<RowSet<Row>>> handler) {
|
|
|
- this.sqlClient.get().UPDATE_WORLD_QUERY.executeBatch(batch, handler);
|
|
|
+ public void updateWorld(World[] worlds, Handler<AsyncResult<RowSet<Row>>> handler) {
|
|
|
+ Arrays.sort(worlds);
|
|
|
+ int len = worlds.length;
|
|
|
+ var connection = this.sqlClient.get();
|
|
|
+ if (0 < len && len <= connection.AGGREGATED_UPDATE_WORLD_QUERY.length) {
|
|
|
+ List<Object> arguments = new ArrayList<>();
|
|
|
+ for (World world : worlds) {
|
|
|
+ arguments.add(world.getId());
|
|
|
+ arguments.add(world.getRandomNumber());
|
|
|
+ }
|
|
|
+ Tuple tuple = Tuple.tuple(arguments);
|
|
|
+ PreparedQuery<RowSet<Row>> query = connection.AGGREGATED_UPDATE_WORLD_QUERY[len - 1];
|
|
|
+ query.execute(tuple, handler);
|
|
|
+ } else {
|
|
|
+ List<Tuple> batch = new ArrayList<>();
|
|
|
+ for (World world : worlds) {
|
|
|
+ batch.add(Tuple.of(world.getRandomNumber(), world.getId()));
|
|
|
+ }
|
|
|
+ connection.UPDATE_WORLD_QUERY.executeBatch(batch, handler);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private PgConnectOptions pgPoolOptions(Config config) {
|