1616import io .vertx .redis .client .RedisReplicas ;
1717import io .vertx .redis .client .Request ;
1818import io .vertx .redis .client .Response ;
19+ import io .vertx .redis .client .impl .Primitives .Int ;
20+ import io .vertx .redis .client .impl .Primitives .IntList ;
1921import io .vertx .redis .client .impl .types .ErrorType ;
2022import io .vertx .redis .client .impl .types .SimpleStringType ;
2123
2224import java .util .ArrayList ;
2325import java .util .Collection ;
2426import java .util .Collections ;
2527import java .util .HashMap ;
26- import java .util .IdentityHashMap ;
2728import java .util .List ;
2829import java .util .Map ;
2930import java .util .Random ;
@@ -41,18 +42,48 @@ public class RedisClusterConnection implements RedisConnection {
4142 static final int RETRIES = 16 ;
4243
4344 // reduce from list of responses to a single response
44- private static final Map <Command , Function <List <Response >, Response >> REDUCERS = new HashMap <>();
45+ private static final Map <Command , Function <List <ResponseWithPositions >, Response >> REDUCERS = new HashMap <>();
4546 // List of commands that should always run only against master nodes
4647 private static final List <Command > MASTER_ONLY_COMMANDS = new ArrayList <>();
4748
49+ @ Deprecated (forRemoval = true )
4850 public static void addReducer (Command command , Function <List <Response >, Response > fn ) {
51+ REDUCERS .put (command , list -> {
52+ List <Response > responses = new ArrayList <>(list .size ());
53+ for (ResponseWithPositions r : list ) {
54+ responses .add (r .response ());
55+ }
56+ return fn .apply (responses );
57+ });
58+ }
59+
60+ static void addNewReducer (Command command , Function <List <ResponseWithPositions >, Response > fn ) {
4961 REDUCERS .put (command , fn );
5062 }
5163
64+ @ Deprecated (forRemoval = true )
5265 public static void addMasterOnlyCommand (Command command ) {
5366 MASTER_ONLY_COMMANDS .add (command );
5467 }
5568
69+ static class ResponseWithPositions {
70+ private final Response response ;
71+ private final IntList positions ;
72+
73+ private ResponseWithPositions (Response response , IntList positions ) {
74+ this .response = response ;
75+ this .positions = positions ;
76+ }
77+
78+ public Response response () {
79+ return response ;
80+ }
81+
82+ public IntList positions () {
83+ return positions ;
84+ }
85+ }
86+
5687 final VertxInternal vertx ;
5788 private final RedisConnectionManager connectionManager ;
5889 private final RedisClusterConnectOptions connectOptions ;
@@ -207,7 +238,12 @@ private Future<Response> send(Request request, Slots slots) {
207238 // means if one of the operations failed, then we can fail the handler
208239 promise .fail (composite .cause ());
209240 } else {
210- promise .succeed (REDUCERS .get (cmd ).apply (composite .result ().list ()));
241+ List <Response > list = composite .result ().list ();
242+ List <ResponseWithPositions > listWithPositions = new ArrayList <>(list .size ());
243+ for (Response resp : list ) {
244+ listWithPositions .add (new ResponseWithPositions (resp , new IntList ()));
245+ }
246+ promise .succeed (REDUCERS .get (cmd ).apply (listWithPositions ));
211247 }
212248 });
213249 } else {
@@ -232,28 +268,38 @@ private Future<Response> send(Request request, Slots slots) {
232268 return promise .future ();
233269 }
234270
235- final Map < Integer , Request > requests = splitRequest (cmd , args );
271+ final Collection < RequestWithSlotNumber > groupedRequests = splitRequest (cmd , args );
236272
237- if (requests .isEmpty ()) {
273+ if (groupedRequests .isEmpty ()) {
238274 // we can't continue as we don't know how to split this command
239275 promise .fail (buildCrossslotFailureMsg (req ));
240276 return promise .future ();
241277 }
242278
243- final List <Future <Response >> responses = new ArrayList <>(requests .size ());
279+ final List <Future <Response >> responses = new ArrayList <>(groupedRequests .size ());
280+ final Map <Integer , IntList > responsePositions = new HashMap <>();
244281
245- for (Map .Entry <Integer , Request > kv : requests .entrySet ()) {
282+ int i = 0 ;
283+ for (RequestWithSlotNumber rwsn : groupedRequests ) {
246284 final Promise <Response > p = vertx .promise ();
247- send (selectEndpoint (slots , kv . getKey () , cmd .isReadOnly (args ), forceMasterEndpoint ), RETRIES , kv . getValue () , p );
285+ send (selectEndpoint (slots , rwsn . slot , cmd .isReadOnly (args ), forceMasterEndpoint ), RETRIES , rwsn . request , p );
248286 responses .add (p .future ());
287+
288+ responsePositions .put (i , rwsn .includedArguments );
289+ i ++;
249290 }
250291
251292 Future .all (responses ).onComplete (composite -> {
252293 if (composite .failed ()) {
253294 // means if one of the operations failed, then we can fail the handler
254295 promise .fail (composite .cause ());
255296 } else {
256- promise .succeed (REDUCERS .get (cmd ).apply (composite .result ().list ()));
297+ List <Response > list = composite .result ().list ();
298+ List <ResponseWithPositions > listWithPositions = new ArrayList <>(list .size ());
299+ for (int j = 0 ; j < list .size (); j ++) {
300+ listWithPositions .add (new ResponseWithPositions (list .get (j ), responsePositions .get (j )));
301+ }
302+ promise .succeed (REDUCERS .get (cmd ).apply (listWithPositions ));
257303 }
258304 });
259305
@@ -267,41 +313,58 @@ private Future<Response> send(Request request, Slots slots) {
267313 }
268314 }
269315
270- private Map <Integer , Request > splitRequest (CommandImpl cmd , List <byte []> args ) {
316+ private static class RequestWithSlotNumber {
317+ final int slot ;
318+ final Request request ;
319+ final IntList includedArguments ;
320+
321+ RequestWithSlotNumber (int slot , Request request ) {
322+ this .slot = slot ;
323+ this .request = request ;
324+ this .includedArguments = new IntList ();
325+ }
326+ }
327+
328+ private Collection <RequestWithSlotNumber > splitRequest (CommandImpl cmd , List <byte []> args ) {
271329 // we will split the request across the slots
272- final Map <Integer , Request > map = new IdentityHashMap <>();
330+ final Map <Integer , RequestWithSlotNumber > map = new HashMap <>();
331+ final Int argCounter = new Int (0 );
273332
274333 int lastKey = cmd .iterateKeys (args , (begin , keyIdx , keyStep ) -> {
275334 int slot = ZModem .generate (args .get (keyIdx ));
276335 // get the client for the slot
277- Request request = map .get (slot );
278- if (request == null ) {
336+ Request request ;
337+ RequestWithSlotNumber rwsn = map .get (slot );
338+ if (rwsn == null ) {
279339 // we need to create a new one
280340 request = Request .cmd (cmd );
341+ rwsn = new RequestWithSlotNumber (slot , request );
281342 // all params before the key get added
282343 for (int j = 0 ; j < begin ; j ++) {
283344 request .arg (args .get (j ));
284345 }
285346 // add to the map
286- map .put (slot , request );
347+ map .put (slot , rwsn );
348+ } else {
349+ request = rwsn .request ;
287350 }
288- // request isn't null anymore
289- request .arg (args .get (keyIdx ));
290351 // all params before the next key get added
291- for (int j = keyIdx + 1 ; j < keyIdx + keyStep ; j ++) {
352+ for (int j = keyIdx ; j < keyIdx + keyStep ; j ++) {
292353 request .arg (args .get (j ));
293354 }
355+ rwsn .includedArguments .add (argCounter .value ++);
294356 });
295357
296358 // if there are args after the end they must be added to all requests
297- final Collection <Request > col = map .values ();
298- col .forEach (req -> {
359+ final Collection <RequestWithSlotNumber > requests = map .values ();
360+ for (RequestWithSlotNumber rwsn : requests ) {
361+ Request req = rwsn .request ;
299362 for (int j = lastKey ; j < args .size (); j ++) {
300363 req .arg (args .get (j ));
301364 }
302- });
365+ }
303366
304- return map ;
367+ return requests ;
305368 }
306369
307370 void send (String selectedEndpoint , int retries , Request command , Completable <Response > handler ) {
0 commit comments