3
3
import org .dataloader .annotations .GuardedBy ;
4
4
import org .dataloader .annotations .Internal ;
5
5
import org .dataloader .impl .CompletableFutureKit ;
6
+ import org .dataloader .impl .DataLoaderAssertionException ;
6
7
import org .dataloader .scheduler .BatchLoaderScheduler ;
7
8
import org .dataloader .stats .StatisticsCollector ;
8
9
import org .dataloader .stats .context .IncrementBatchLoadCountByStatisticsContext ;
@@ -624,6 +625,15 @@ private static <T> DispatchResult<T> emptyDispatchResult() {
624
625
return (DispatchResult <T >) EMPTY_DISPATCH_RESULT ;
625
626
}
626
627
628
+ /**********************************************************************************************
629
+ * ********************************************************************************************
630
+ * <p>
631
+ * The reactive support classes start here
632
+ *
633
+ * @param <T> for two
634
+ **********************************************************************************************
635
+ **********************************************************************************************
636
+ */
627
637
private abstract class DataLoaderSubscriberBase <T > implements Subscriber <T > {
628
638
629
639
final CompletableFuture <List <V >> valuesFuture ;
@@ -721,6 +731,11 @@ private DataLoaderSubscriber(
721
731
public synchronized void onNext (V value ) {
722
732
super .onNext (value );
723
733
734
+ if (idx >= keys .size ()) {
735
+ // hang on they have given us more values than we asked for in keys
736
+ // we cant handle this
737
+ return ;
738
+ }
724
739
K key = keys .get (idx );
725
740
Object callContext = callContexts .get (idx );
726
741
CompletableFuture <V > future = queuedFutures .get (idx );
@@ -734,8 +749,16 @@ public synchronized void onNext(V value) {
734
749
@ Override
735
750
public synchronized void onComplete () {
736
751
super .onComplete ();
737
- assertResultSize (keys , completedValues );
738
-
752
+ if (keys .size () != completedValues .size ()) {
753
+ // we have more or less values than promised
754
+ // we will go through all the outstanding promises and mark those that
755
+ // have not finished as failed
756
+ for (CompletableFuture <V > queuedFuture : queuedFutures ) {
757
+ if (!queuedFuture .isDone ()) {
758
+ queuedFuture .completeExceptionally (new DataLoaderAssertionException ("The size of the promised values MUST be the same size as the key list" ));
759
+ }
760
+ }
761
+ }
739
762
possiblyClearCacheEntriesOnExceptions (clearCacheKeys );
740
763
valuesFuture .complete (completedValues );
741
764
}
@@ -748,9 +771,11 @@ public synchronized void onError(Throwable ex) {
748
771
for (int i = idx ; i < queuedFutures .size (); i ++) {
749
772
K key = keys .get (i );
750
773
CompletableFuture <V > future = queuedFutures .get (i );
751
- future .completeExceptionally (ex );
752
- // clear any cached view of this key because they all failed
753
- dataLoader .clear (key );
774
+ if (! future .isDone ()) {
775
+ future .completeExceptionally (ex );
776
+ // clear any cached view of this key because it failed
777
+ dataLoader .clear (key );
778
+ }
754
779
}
755
780
valuesFuture .completeExceptionally (ex );
756
781
}
@@ -790,11 +815,14 @@ public synchronized void onNext(Map.Entry<K, V> entry) {
790
815
V value = entry .getValue ();
791
816
792
817
Object callContext = callContextByKey .get (key );
793
- List <CompletableFuture <V >> futures = queuedFuturesByKey .get (key );
818
+ List <CompletableFuture <V >> futures = queuedFuturesByKey .getOrDefault (key , List . of () );
794
819
795
820
onNextValue (key , value , callContext , futures );
796
821
797
- completedValuesByKey .put (key , value );
822
+ // did we have an actual key for this value - ignore it if they send us one outside the key set
823
+ if (!futures .isEmpty ()) {
824
+ completedValuesByKey .put (key , value );
825
+ }
798
826
}
799
827
800
828
@ Override
@@ -806,6 +834,16 @@ public synchronized void onComplete() {
806
834
for (K key : keys ) {
807
835
V value = completedValuesByKey .get (key );
808
836
values .add (value );
837
+
838
+ List <CompletableFuture <V >> futures = queuedFuturesByKey .getOrDefault (key , List .of ());
839
+ for (CompletableFuture <V > future : futures ) {
840
+ if (! future .isDone ()) {
841
+ // we have a future that never came back for that key
842
+ // but the publisher is done sending in data - it must be null
843
+ // e.g. for key X when found no value
844
+ future .complete (null );
845
+ }
846
+ }
809
847
}
810
848
valuesFuture .complete (values );
811
849
}
0 commit comments