3030import com .google .protobuf .ByteString ;
3131import java .time .Duration ;
3232import java .util .ArrayList ;
33+ import java .util .Collections ;
3334import java .util .List ;
3435import java .util .TreeMap ;
3536import java .util .concurrent .CompletableFuture ;
@@ -77,9 +78,11 @@ public void teardown() {
7778
7879 @ Test
7980 public void addToSameBatch () {
80- when (storeClient .latestEffectiveRouter ()).thenReturn (new TreeMap <>(BoundaryUtil ::compare ) {{
81- put (FULL_BOUNDARY , setting (id , "V1" , 0 ));
82- }});
81+ when (storeClient .latestEffectiveRouter ()).thenReturn (new TreeMap <>(BoundaryUtil ::compare ) {
82+ {
83+ put (FULL_BOUNDARY , setting (id , "V1" , 0 ));
84+ }
85+ });
8386
8487 when (storeClient .createMutationPipeline ("V1" )).thenReturn (mutationPipeline1 );
8588 when (mutationPipeline1 .execute (any ()))
@@ -103,7 +106,8 @@ public void addToSameBatch() {
103106 String [] keys = request .getRwCoProc ().getRaw ().toStringUtf8 ().split ("_" );
104107 assertEquals (keys .length , Sets .newSet (keys ).size ());
105108 }
106- // the resp order preserved
109+ Collections .sort (reqList );
110+ Collections .sort (respList );
107111 assertEquals (reqList , respList );
108112 }
109113
@@ -124,19 +128,24 @@ public void addToDifferentBatch() {
124128 int req = ThreadLocalRandom .current ().nextInt (1 , 1001 );
125129 reqList .add (req );
126130 if (req < 500 ) {
127- when (storeClient .latestEffectiveRouter ()).thenReturn (new TreeMap <>(BoundaryUtil ::compare ) {{
128- put (FULL_BOUNDARY , setting (id , "V1" , 0 ));
129- }});
131+ when (storeClient .latestEffectiveRouter ()).thenReturn (new TreeMap <>(BoundaryUtil ::compare ) {
132+ {
133+ put (FULL_BOUNDARY , setting (id , "V1" , 0 ));
134+ }
135+ });
130136 } else {
131- when (storeClient .latestEffectiveRouter ()).thenReturn (new TreeMap <>(BoundaryUtil ::compare ) {{
132- put (FULL_BOUNDARY , setting (id , "V2" , 0 ));
133- }});
137+ when (storeClient .latestEffectiveRouter ()).thenReturn (new TreeMap <>(BoundaryUtil ::compare ) {
138+ {
139+ put (FULL_BOUNDARY , setting (id , "V2" , 0 ));
140+ }
141+ });
134142 }
135143 futures .add (scheduler .schedule (ByteString .copyFromUtf8 (Integer .toString (req )))
136144 .thenAccept ((v ) -> respList .add (Integer .parseInt (v .toStringUtf8 ()))));
137145 }
138146 CompletableFuture .allOf (futures .toArray (CompletableFuture []::new )).join ();
139- // the resp order preserved
147+ Collections .sort (reqList );
148+ Collections .sort (respList );
140149 assertEquals (reqList , respList );
141150 }
142151
@@ -166,4 +175,82 @@ public void executeManySmallBatchesNoRecursion() {
166175 CompletableFuture .allOf (futures .toArray (CompletableFuture []::new )).join ();
167176 assertEquals (execCount .get (), n );
168177 }
178+
179+ @ Test
180+ public void reScanWhenHitNonBatchable () {
181+ when (storeClient .latestEffectiveRouter ()).thenReturn (new TreeMap <>(BoundaryUtil ::compare ) {
182+ {
183+ put (FULL_BOUNDARY , setting (id , "V1" , 0 ));
184+ }
185+ });
186+ when (storeClient .createMutationPipeline ("V1" )).thenReturn (mutationPipeline1 );
187+ when (mutationPipeline1 .execute (any ()))
188+ .thenReturn (CompletableFuture .supplyAsync (() -> KVRangeRWReply .newBuilder ().build ()));
189+
190+ MutationCallScheduler <ByteString , ByteString , TestBatchMutationCall > scheduler =
191+ new MutationCallScheduler <>(NonBatchableBatchCall ::new , Duration .ofMillis (1000 ).toNanos (), storeClient ) {
192+ @ Override
193+ protected ByteString rangeKey (ByteString call ) {
194+ return call ;
195+ }
196+ };
197+ List <CompletableFuture <Void >> futures = new ArrayList <>();
198+ List <ByteString > reqs = List .of (
199+ ByteString .copyFromUtf8 ("k1" ),
200+ ByteString .copyFromUtf8 ("k_dup" ), // will mark non-batchable in first batch
201+ ByteString .copyFromUtf8 ("k2" ));
202+ List <ByteString > resps = new CopyOnWriteArrayList <>();
203+ reqs .forEach (req -> futures .add (scheduler .schedule (req ).thenAccept (resps ::add )));
204+ CompletableFuture .allOf (futures .toArray (CompletableFuture []::new )).join ();
205+ List <String > reqSorted = reqs .stream ().map (ByteString ::toStringUtf8 ).sorted ().toList ();
206+ List <String > respSorted = resps .stream ().map (ByteString ::toStringUtf8 ).sorted ().toList ();
207+ assertEquals (reqSorted , respSorted );
208+ }
209+
210+ @ Test
211+ public void mixDifferentVersions () {
212+ when (storeClient .createMutationPipeline ("V1" )).thenReturn (mutationPipeline1 );
213+ when (storeClient .createMutationPipeline ("V2" )).thenReturn (mutationPipeline2 );
214+ when (mutationPipeline1 .execute (any ()))
215+ .thenReturn (CompletableFuture .supplyAsync (() -> KVRangeRWReply .newBuilder ().build ()));
216+ when (mutationPipeline2 .execute (any ()))
217+ .thenReturn (CompletableFuture .supplyAsync (() -> KVRangeRWReply .newBuilder ().build ()));
218+ TestMutationCallScheduler scheduler = new TestMutationCallScheduler (storeClient , Duration .ofMillis (1000 ));
219+ List <CompletableFuture <Void >> futures = new ArrayList <>();
220+ List <ByteString > reqs = new ArrayList <>();
221+ List <ByteString > resps = new CopyOnWriteArrayList <>();
222+ for (int i = 0 ; i < 20 ; i ++) {
223+ ByteString req = ByteString .copyFromUtf8 ("k" + i );
224+ reqs .add (req );
225+ if (i % 2 == 0 ) {
226+ when (storeClient .latestEffectiveRouter ()).thenReturn (new TreeMap <>(BoundaryUtil ::compare ) {
227+ {
228+ put (FULL_BOUNDARY , setting (id , "V1" , 0 ));
229+ }
230+ });
231+ } else {
232+ when (storeClient .latestEffectiveRouter ()).thenReturn (new TreeMap <>(BoundaryUtil ::compare ) {
233+ {
234+ put (FULL_BOUNDARY , setting (id , "V2" , 1 ));
235+ }
236+ });
237+ }
238+ futures .add (scheduler .schedule (req ).thenAccept (resps ::add ));
239+ }
240+ CompletableFuture .allOf (futures .toArray (CompletableFuture []::new )).join ();
241+ List <String > reqSorted = reqs .stream ().map (ByteString ::toStringUtf8 ).sorted ().toList ();
242+ List <String > respSorted = resps .stream ().map (ByteString ::toStringUtf8 ).sorted ().toList ();
243+ assertEquals (reqSorted , respSorted );
244+ }
245+
246+ private static class NonBatchableBatchCall extends TestBatchMutationCall {
247+ protected NonBatchableBatchCall (IMutationPipeline pipeline , MutationCallBatcherKey batcherKey ) {
248+ super (pipeline , batcherKey );
249+ }
250+
251+ @ Override
252+ protected NonBatchableFirstBatch newBatch (long ver ) {
253+ return new NonBatchableFirstBatch (ver );
254+ }
255+ }
169256}
0 commit comments