11package com .techempower ;
22
3+ import java .util .ArrayList ;
4+ import java .util .Arrays ;
35import java .util .List ;
46import java .util .concurrent .ExecutionException ;
57import java .util .function .BiConsumer ;
@@ -32,10 +34,13 @@ public class PgClient {
3234 private static final String SELECT_FORTUNE = "SELECT id, message from FORTUNE" ;
3335
3436 private static class DbConnection {
37+ private SqlClientInternal queries ;
3538 private PreparedQuery <RowSet <Row >> SELECT_WORLD_QUERY ;
3639 private PreparedQuery <RowSet <Row >> SELECT_FORTUNE_QUERY ;
3740 private PreparedQuery <RowSet <Row >> UPDATE_WORLD_QUERY ;
38- private SqlClientInternal connection ;
41+ private SqlClientInternal updates ;
42+ @ SuppressWarnings ("unchecked" )
43+ private PreparedQuery <RowSet <Row >>[] AGGREGATED_UPDATE_WORLD_QUERY = new PreparedQuery [128 ];
3944 }
4045
4146 private static class DbConnectionFactory extends ThreadLocal <DbConnection > {
@@ -64,20 +69,32 @@ private <T> Handler<AsyncResult<T>> onSuccess(Handler<T> handler) {
6469 .setWorkerPoolSize (1 )
6570 .setInternalBlockingPoolSize (1 )
6671 );
67- var future = PgConnection .connect (vertx , options )
72+ var client1 = PgConnection .connect (vertx , options )
6873 .flatMap (conn -> {
69- result .connection = (SqlClientInternal ) conn ;
74+ result .queries = (SqlClientInternal ) conn ;
7075 Future <PreparedStatement > f1 = conn .prepare (SELECT_WORLD )
7176 .andThen (onSuccess (ps -> result .SELECT_WORLD_QUERY = ps .query ()));
7277 Future <PreparedStatement > f2 = conn .prepare (SELECT_FORTUNE )
7378 .andThen (onSuccess (ps -> result .SELECT_FORTUNE_QUERY = ps .query ()));
74- Future <PreparedStatement > f3 = conn .prepare (UPDATE_WORLD )
75- .andThen (onSuccess (ps -> result .UPDATE_WORLD_QUERY = ps .query ()));
76- return Future .join (f1 , f2 , f3 );
77- })
78- .toCompletionStage ()
79- .toCompletableFuture ()
80- .get ();
79+ return Future .join (f1 , f2 );
80+ });
81+
82+ var client2 = PgConnection .connect (vertx , options )
83+ .flatMap (conn -> {
84+ result .updates = (SqlClientInternal ) conn ;
85+ List <Future <?>> list = new ArrayList <>();
86+ Future <PreparedStatement > f1 = conn .prepare (UPDATE_WORLD )
87+ .andThen (onSuccess (ps -> result .UPDATE_WORLD_QUERY = ps .query ()));
88+ list .add (f1 );
89+ for (int i = 0 ; i < result .AGGREGATED_UPDATE_WORLD_QUERY .length ; i ++) {
90+ int idx = i ;
91+ list .add (conn
92+ .prepare (buildAggregatedUpdateQuery (1 + idx ))
93+ .andThen (onSuccess (ps -> result .AGGREGATED_UPDATE_WORLD_QUERY [idx ] = ps .query ())));
94+ }
95+ return Future .join (list );
96+ });
97+ var future = Future .join (client1 , client2 ).toCompletionStage ().toCompletableFuture ().get ();
8198
8299 Throwable cause = future .cause ();
83100 if (cause != null ) {
@@ -91,6 +108,18 @@ private <T> Handler<AsyncResult<T>> onSuccess(Handler<T> handler) {
91108 throw SneakyThrows .propagate (ex .getCause ());
92109 }
93110 }
111+
112+ private static String buildAggregatedUpdateQuery (int len ) {
113+ StringBuilder sb = new StringBuilder ();
114+ sb .append ("UPDATE world SET randomNumber = update_data.randomNumber FROM (VALUES" );
115+ char sep = ' ' ;
116+ for (int i = 1 ;i <= len ;i ++) {
117+ sb .append (sep ).append ("($" ).append (2 * i - 1 ).append ("::int,$" ).append (2 * i ).append ("::int)" );
118+ sep = ',' ;
119+ }
120+ sb .append (") AS update_data (id, randomNumber) WHERE world.id = update_data.id" );
121+ return sb .toString ();
122+ }
94123 }
95124
96125 private final ThreadLocal <DbConnection > sqlClient ;
@@ -104,7 +133,7 @@ public void selectWorld(Tuple row, Handler<AsyncResult<RowSet<Row>>> handler) {
104133 }
105134
106135 public void selectWorlds (int queries , Handler <AsyncResult <RowSet <Row >>> handler ) {
107- this .sqlClient .get ().connection .group (c -> {
136+ this .sqlClient .get ().queries .group (c -> {
108137 for (int i = 0 ; i < queries ; i ++) {
109138 c .preparedQuery (SELECT_WORLD ).execute (Tuple .of (Util .randomWorld ()), handler );
110139 }
@@ -117,16 +146,34 @@ public void fortunes(Handler<AsyncResult<RowSet<Row>>> handler) {
117146
118147 public void selectWorldForUpdate (int queries ,
119148 BiConsumer <Integer , PreparedQuery <RowSet <Row >>> consumer ) {
120- this .sqlClient .get ().connection .group (c -> {
149+ this .sqlClient .get ().queries .group (c -> {
121150 PreparedQuery <RowSet <Row >> statement = c .preparedQuery (SELECT_WORLD );
122151 for (int i = 0 ; i < queries ; i ++) {
123152 consumer .accept (i , statement );
124153 }
125154 });
126155 }
127156
128- public void updateWorld (List <Tuple > batch , Handler <AsyncResult <RowSet <Row >>> handler ) {
129- this .sqlClient .get ().UPDATE_WORLD_QUERY .executeBatch (batch , handler );
157+ public void updateWorld (World [] worlds , Handler <AsyncResult <RowSet <Row >>> handler ) {
158+ Arrays .sort (worlds );
159+ int len = worlds .length ;
160+ var connection = this .sqlClient .get ();
161+ if (0 < len && len <= connection .AGGREGATED_UPDATE_WORLD_QUERY .length ) {
162+ List <Object > arguments = new ArrayList <>();
163+ for (World world : worlds ) {
164+ arguments .add (world .getId ());
165+ arguments .add (world .getRandomNumber ());
166+ }
167+ Tuple tuple = Tuple .tuple (arguments );
168+ PreparedQuery <RowSet <Row >> query = connection .AGGREGATED_UPDATE_WORLD_QUERY [len - 1 ];
169+ query .execute (tuple , handler );
170+ } else {
171+ List <Tuple > batch = new ArrayList <>();
172+ for (World world : worlds ) {
173+ batch .add (Tuple .of (world .getRandomNumber (), world .getId ()));
174+ }
175+ connection .UPDATE_WORLD_QUERY .executeBatch (batch , handler );
176+ }
130177 }
131178
132179 private PgConnectOptions pgPoolOptions (Config config ) {
0 commit comments