88import java .time .Instant ;
99import java .util .ArrayList ;
1010import java .util .Collection ;
11- import java .util .LinkedHashMap ;
1211import java .util .LinkedHashSet ;
1312import java .util .List ;
1413import java .util .Map ;
@@ -335,45 +334,46 @@ CompletableFuture<List<V>> invokeLoader(List<K> keys, List<Object> keyContexts,
335334
336335 assertState (keys .size () == cachedValues .size (), () -> "The size of the cached values MUST be the same size as the key list" );
337336
338- LinkedHashMap <K , V > valuesInKeyOrder = new LinkedHashMap <>();
339- List <K > cacheMissedKeys = new ArrayList <>();
340- List <Object > cacheMissedContexts = new ArrayList <>();
337+ // the following is NOT a Map because keys in data loader can repeat (by design)
338+ // and hence "a","b","c","b" is a valid set of keys
339+ List <Try <V >> valuesInKeyOrder = new ArrayList <>();
340+ List <Integer > missedKeyIndexes = new ArrayList <>();
341+ List <K > missedKeys = new ArrayList <>();
342+ List <Object > missedKeyContexts = new ArrayList <>();
341343 for (int i = 0 ; i < keys .size (); i ++) {
342- K key = keys .get (i );
343- Object keyContext = keyContexts .get (i );
344344 Try <V > cacheGet = cachedValues .get (i );
345- if (cacheGet .isSuccess ()) {
346- valuesInKeyOrder .put (key , cacheGet .get ());
347- } else {
348- valuesInKeyOrder .put (key , null ); // an entry to be replaced later
349- cacheMissedKeys .add (key );
350- cacheMissedContexts .add (keyContext );
345+ valuesInKeyOrder .add (cacheGet );
346+ if (cacheGet .isFailure ()) {
347+ missedKeyIndexes .add (i );
348+ missedKeys .add (keys .get (i ));
349+ missedKeyContexts .add (keyContexts .get (i ));
351350 }
352351 }
353- if (cacheMissedKeys .isEmpty ()) {
352+ if (missedKeys .isEmpty ()) {
354353 //
355354 // everything was cached
356355 //
357- return completedFuture (new ArrayList <>(valuesInKeyOrder .values ()));
356+ List <V > assembledValues = valuesInKeyOrder .stream ().map (Try ::get ).collect (toList ());
357+ return completedFuture (assembledValues );
358358 } else {
359359 //
360360 // we missed some of the keys from cache, so send them to the batch loader
361361 // and then fill in their values
362362 //
363- CompletableFuture <List <V >> batchLoad = invokeLoader (cacheMissedKeys , cacheMissedContexts );
363+ CompletableFuture <List <V >> batchLoad = invokeLoader (missedKeys , missedKeyContexts );
364364 return batchLoad .thenCompose (missedValues -> {
365- assertResultSize (cacheMissedKeys , missedValues );
365+ assertResultSize (missedKeys , missedValues );
366366
367367 for (int i = 0 ; i < missedValues .size (); i ++) {
368- K missedKey = cacheMissedKeys .get (i );
369368 V v = missedValues .get (i );
370- valuesInKeyOrder .put (missedKey , v );
369+ Integer listIndex = missedKeyIndexes .get (i );
370+ valuesInKeyOrder .set (listIndex , Try .succeeded (v ));
371371 }
372- List <V > assembledValues = new ArrayList <>( valuesInKeyOrder .values ());
372+ List <V > assembledValues = valuesInKeyOrder .stream (). map ( Try :: get ). collect ( toList ());
373373 //
374374 // fire off a call to the ValueCache to allow it to set values into the
375375 // cache now that we have them
376- return setToValueCache (assembledValues , cacheMissedKeys , missedValues );
376+ return setToValueCache (assembledValues , missedKeys , missedValues );
377377 });
378378 }
379379 });
@@ -405,7 +405,7 @@ private CompletableFuture<List<V>> invokeListBatchLoader(List<K> keys, BatchLoad
405405 } else {
406406 loadResult = ((BatchLoader <K , V >) batchLoadFunction ).load (keys );
407407 }
408- return nonNull (loadResult , () -> "Your batch loader function MUST return a non null CompletionStage promise " ).toCompletableFuture ();
408+ return nonNull (loadResult , () -> "Your batch loader function MUST return a non null CompletionStage" ).toCompletableFuture ();
409409 }
410410
411411
@@ -422,7 +422,7 @@ private CompletableFuture<List<V>> invokeMapBatchLoader(List<K> keys, BatchLoade
422422 } else {
423423 loadResult = ((MappedBatchLoader <K , V >) batchLoadFunction ).load (setOfKeys );
424424 }
425- CompletableFuture <Map <K , V >> mapBatchLoad = nonNull (loadResult , () -> "Your batch loader function MUST return a non null CompletionStage promise " ).toCompletableFuture ();
425+ CompletableFuture <Map <K , V >> mapBatchLoad = nonNull (loadResult , () -> "Your batch loader function MUST return a non null CompletionStage" ).toCompletableFuture ();
426426 return mapBatchLoad .thenApply (map -> {
427427 List <V > values = new ArrayList <>();
428428 for (K key : keys ) {
@@ -445,7 +445,7 @@ int dispatchDepth() {
445445
446446 private CompletableFuture <List <Try <V >>> getFromValueCache (List <K > keys ) {
447447 try {
448- return nonNull (valueCache .getValues (keys ), () -> "Your ValueCache.getValues function MUST return a non null promise " );
448+ return nonNull (valueCache .getValues (keys ), () -> "Your ValueCache.getValues function MUST return a non null CompletableFuture " );
449449 } catch (RuntimeException e ) {
450450 return CompletableFutureKit .failedFuture (e );
451451 }
@@ -456,7 +456,7 @@ private CompletableFuture<List<V>> setToValueCache(List<V> assembledValues, List
456456 boolean completeValueAfterCacheSet = loaderOptions .getValueCacheOptions ().isCompleteValueAfterCacheSet ();
457457 if (completeValueAfterCacheSet ) {
458458 return nonNull (valueCache
459- .setValues (missedKeys , missedValues ), () -> "Your ValueCache.setValues function MUST return a non null promise " )
459+ .setValues (missedKeys , missedValues ), () -> "Your ValueCache.setValues function MUST return a non null CompletableFuture " )
460460 // we dont trust the set cache to give us the values back - we have them - lets use them
461461 // if the cache set fails - then they wont be in cache and maybe next time they will
462462 .handle ((ignored , setExIgnored ) -> assembledValues );
0 commit comments