1
1
package com .techempower ;
2
2
3
+ import java .util .ArrayList ;
4
+ import java .util .Arrays ;
3
5
import java .util .List ;
4
6
import java .util .concurrent .ExecutionException ;
5
7
import java .util .function .BiConsumer ;
@@ -32,10 +34,13 @@ public class PgClient {
32
34
private static final String SELECT_FORTUNE = "SELECT id, message from FORTUNE" ;
33
35
34
36
private static class DbConnection {
37
+ private SqlClientInternal queries ;
35
38
private PreparedQuery <RowSet <Row >> SELECT_WORLD_QUERY ;
36
39
private PreparedQuery <RowSet <Row >> SELECT_FORTUNE_QUERY ;
37
40
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 ];
39
44
}
40
45
41
46
private static class DbConnectionFactory extends ThreadLocal <DbConnection > {
@@ -64,20 +69,32 @@ private <T> Handler<AsyncResult<T>> onSuccess(Handler<T> handler) {
64
69
.setWorkerPoolSize (1 )
65
70
.setInternalBlockingPoolSize (1 )
66
71
);
67
- var future = PgConnection .connect (vertx , options )
72
+ var client1 = PgConnection .connect (vertx , options )
68
73
.flatMap (conn -> {
69
- result .connection = (SqlClientInternal ) conn ;
74
+ result .queries = (SqlClientInternal ) conn ;
70
75
Future <PreparedStatement > f1 = conn .prepare (SELECT_WORLD )
71
76
.andThen (onSuccess (ps -> result .SELECT_WORLD_QUERY = ps .query ()));
72
77
Future <PreparedStatement > f2 = conn .prepare (SELECT_FORTUNE )
73
78
.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 ();
81
98
82
99
Throwable cause = future .cause ();
83
100
if (cause != null ) {
@@ -91,6 +108,18 @@ private <T> Handler<AsyncResult<T>> onSuccess(Handler<T> handler) {
91
108
throw SneakyThrows .propagate (ex .getCause ());
92
109
}
93
110
}
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
+ }
94
123
}
95
124
96
125
private final ThreadLocal <DbConnection > sqlClient ;
@@ -104,7 +133,7 @@ public void selectWorld(Tuple row, Handler<AsyncResult<RowSet<Row>>> handler) {
104
133
}
105
134
106
135
public void selectWorlds (int queries , Handler <AsyncResult <RowSet <Row >>> handler ) {
107
- this .sqlClient .get ().connection .group (c -> {
136
+ this .sqlClient .get ().queries .group (c -> {
108
137
for (int i = 0 ; i < queries ; i ++) {
109
138
c .preparedQuery (SELECT_WORLD ).execute (Tuple .of (Util .randomWorld ()), handler );
110
139
}
@@ -117,16 +146,34 @@ public void fortunes(Handler<AsyncResult<RowSet<Row>>> handler) {
117
146
118
147
public void selectWorldForUpdate (int queries ,
119
148
BiConsumer <Integer , PreparedQuery <RowSet <Row >>> consumer ) {
120
- this .sqlClient .get ().connection .group (c -> {
149
+ this .sqlClient .get ().queries .group (c -> {
121
150
PreparedQuery <RowSet <Row >> statement = c .preparedQuery (SELECT_WORLD );
122
151
for (int i = 0 ; i < queries ; i ++) {
123
152
consumer .accept (i , statement );
124
153
}
125
154
});
126
155
}
127
156
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
+ }
130
177
}
131
178
132
179
private PgConnectOptions pgPoolOptions (Config config ) {
0 commit comments