@@ -18,15 +18,15 @@ use crate::proto::errorpb;
1818use crate :: proto:: errorpb:: EpochNotMatch ;
1919use crate :: proto:: kvrpcpb;
2020use crate :: request:: shard:: HasNextBatch ;
21- use crate :: request:: KvRequest ;
2221use crate :: request:: NextBatch ;
2322use crate :: request:: Shardable ;
23+ use crate :: request:: { KvRequest , StoreRequest } ;
2424use crate :: stats:: tikv_stats;
25- use crate :: store:: HasKeyErrors ;
2625use crate :: store:: HasRegionError ;
2726use crate :: store:: HasRegionErrors ;
2827use crate :: store:: KvClient ;
2928use crate :: store:: RegionStore ;
29+ use crate :: store:: { HasKeyErrors , Store } ;
3030use crate :: transaction:: resolve_locks;
3131use crate :: transaction:: HasLocks ;
3232use crate :: transaction:: ResolveLocksContext ;
@@ -73,7 +73,19 @@ impl<Req: KvRequest> Plan for Dispatch<Req> {
7373 }
7474}
7575
76+ impl < Req : KvRequest + StoreRequest > StoreRequest for Dispatch < Req > {
77+ fn apply_store ( & mut self , store : & Store ) {
78+ self . kv_client = Some ( store. client . clone ( ) ) ;
79+ self . request . apply_store ( store) ;
80+ }
81+ }
82+
7683const MULTI_REGION_CONCURRENCY : usize = 16 ;
84+ const MULTI_STORES_CONCURRENCY : usize = 16 ;
85+
86+ fn is_grpc_error ( e : & Error ) -> bool {
87+ matches ! ( e, Error :: GrpcAPI ( _) | Error :: Grpc ( _) )
88+ }
7789
7890pub struct RetryableMultiRegion < P : Plan , PdC : PdClient > {
7991 pub ( super ) inner : P ,
@@ -150,7 +162,6 @@ where
150162 let res = plan. execute ( ) . await ;
151163 drop ( permit) ;
152164
153- let is_grpc_error = |e : & Error | matches ! ( e, Error :: GrpcAPI ( _) | Error :: Grpc ( _) ) ;
154165 let mut resp = match res {
155166 Ok ( resp) => resp,
156167 Err ( e) if is_grpc_error ( & e) => {
@@ -354,6 +365,90 @@ where
354365 }
355366}
356367
368+ pub struct RetryableAllStores < P : Plan , PdC : PdClient > {
369+ pub ( super ) inner : P ,
370+ pub pd_client : Arc < PdC > ,
371+ pub backoff : Backoff ,
372+ }
373+
374+ impl < P : Plan , PdC : PdClient > Clone for RetryableAllStores < P , PdC > {
375+ fn clone ( & self ) -> Self {
376+ RetryableAllStores {
377+ inner : self . inner . clone ( ) ,
378+ pd_client : self . pd_client . clone ( ) ,
379+ backoff : self . backoff . clone ( ) ,
380+ }
381+ }
382+ }
383+
384+ // About `HasRegionError`:
385+ // Store requests should be return region errors.
386+ // But as the response of only store request by now (UnsafeDestroyRangeResponse) has the `region_error` field,
387+ // we require `HasRegionError` to check whether there is region error returned from TiKV.
388+ #[ async_trait]
389+ impl < P : Plan + StoreRequest , PdC : PdClient > Plan for RetryableAllStores < P , PdC >
390+ where
391+ P :: Result : HasKeyErrors + HasRegionError ,
392+ {
393+ type Result = Vec < Result < P :: Result > > ;
394+
395+ async fn execute ( & self ) -> Result < Self :: Result > {
396+ let concurrency_permits = Arc :: new ( Semaphore :: new ( MULTI_STORES_CONCURRENCY ) ) ;
397+ let stores = self . pd_client . clone ( ) . all_stores ( ) . await ?;
398+ let mut handles = Vec :: with_capacity ( stores. len ( ) ) ;
399+ for store in stores {
400+ let mut clone = self . inner . clone ( ) ;
401+ clone. apply_store ( & store) ;
402+ let handle = tokio:: spawn ( Self :: single_store_handler (
403+ clone,
404+ self . backoff . clone ( ) ,
405+ concurrency_permits. clone ( ) ,
406+ ) ) ;
407+ handles. push ( handle) ;
408+ }
409+ let results = try_join_all ( handles) . await ?;
410+ Ok ( results. into_iter ( ) . collect :: < Vec < _ > > ( ) )
411+ }
412+ }
413+
414+ impl < P : Plan , PdC : PdClient > RetryableAllStores < P , PdC >
415+ where
416+ P :: Result : HasKeyErrors + HasRegionError ,
417+ {
418+ async fn single_store_handler (
419+ plan : P ,
420+ mut backoff : Backoff ,
421+ permits : Arc < Semaphore > ,
422+ ) -> Result < P :: Result > {
423+ loop {
424+ let permit = permits. acquire ( ) . await . unwrap ( ) ;
425+ let res = plan. execute ( ) . await ;
426+ drop ( permit) ;
427+
428+ match res {
429+ Ok ( mut resp) => {
430+ if let Some ( e) = resp. key_errors ( ) {
431+ return Err ( Error :: MultipleKeyErrors ( e) ) ;
432+ } else if let Some ( e) = resp. region_error ( ) {
433+ // Store request should not return region error.
434+ return Err ( Error :: RegionError ( Box :: new ( e) ) ) ;
435+ } else {
436+ return Ok ( resp) ;
437+ }
438+ }
439+ Err ( e) if is_grpc_error ( & e) => match backoff. next_delay_duration ( ) {
440+ Some ( duration) => {
441+ sleep ( duration) . await ;
442+ continue ;
443+ }
444+ None => return Err ( e) ,
445+ } ,
446+ Err ( e) => return Err ( e) ,
447+ }
448+ }
449+ }
450+ }
451+
357452/// A technique for merging responses into a single result (with type `Out`).
358453pub trait Merge < In > : Sized + Clone + Send + Sync + ' static {
359454 type Out : Send ;
0 commit comments