@@ -134,6 +134,12 @@ impl<T: Debug> fmt::Display for Errors<T> {
134
134
}
135
135
}
136
136
137
+ impl < T > Errors < T > {
138
+ pub fn num_errors ( & self ) -> usize {
139
+ self . 0 . len ( )
140
+ }
141
+ }
142
+
137
143
/// Reasons why a candidate might not be ready.
138
144
#[ derive( Debug , Clone , Copy ) ]
139
145
pub enum CandidateError {
@@ -599,46 +605,41 @@ impl<T: SlotClock, E: EthSpec> BeaconNodeFallback<T, E> {
599
605
F : Fn ( & ' a BeaconNodeHttpClient ) -> R ,
600
606
R : Future < Output = Result < O , Err > > ,
601
607
{
602
- let mut results = vec ! [ ] ;
603
608
let mut to_retry = vec ! [ ] ;
604
609
let mut retry_unsynced = vec ! [ ] ;
605
610
606
611
// Run `func` using a `candidate`, returning the value or capturing errors.
607
- //
608
- // We use a macro instead of a closure here since it is not trivial to move `func` into a
609
- // closure.
610
- macro_rules! try_func {
611
- ( $candidate: ident) => { {
612
- inc_counter_vec( & ENDPOINT_REQUESTS , & [ $candidate. beacon_node. as_ref( ) ] ) ;
612
+ let run_on_candidate = |candidate : & ' a CandidateBeaconNode < E > | async {
613
+ inc_counter_vec ( & ENDPOINT_REQUESTS , & [ candidate. beacon_node . as_ref ( ) ] ) ;
613
614
614
- // There exists a race condition where `func` may be called when the candidate is
615
- // actually not ready. We deem this an acceptable inefficiency.
616
- match func( & $candidate. beacon_node) . await {
617
- Ok ( val) => results. push( Ok ( val) ) ,
618
- Err ( e) => {
619
- // If we have an error on this function, make the client as not-ready.
620
- //
621
- // There exists a race condition where the candidate may have been marked
622
- // as ready between the `func` call and now. We deem this an acceptable
623
- // inefficiency.
624
- if matches!( offline_on_failure, OfflineOnFailure :: Yes ) {
625
- $candidate. set_offline( ) . await ;
626
- }
627
- results. push( Err ( (
628
- $candidate. beacon_node. to_string( ) ,
629
- Error :: RequestFailed ( e) ,
630
- ) ) ) ;
631
- inc_counter_vec( & ENDPOINT_ERRORS , & [ $candidate. beacon_node. as_ref( ) ] ) ;
615
+ // There exists a race condition where `func` may be called when the candidate is
616
+ // actually not ready. We deem this an acceptable inefficiency.
617
+ match func ( & candidate. beacon_node ) . await {
618
+ Ok ( val) => Ok ( val) ,
619
+ Err ( e) => {
620
+ // If we have an error on this function, mark the client as not-ready.
621
+ //
622
+ // There exists a race condition where the candidate may have been marked
623
+ // as ready between the `func` call and now. We deem this an acceptable
624
+ // inefficiency.
625
+ if matches ! ( offline_on_failure, OfflineOnFailure :: Yes ) {
626
+ candidate. set_offline ( ) . await ;
632
627
}
628
+ inc_counter_vec ( & ENDPOINT_ERRORS , & [ candidate. beacon_node . as_ref ( ) ] ) ;
629
+ Err ( ( candidate. beacon_node . to_string ( ) , Error :: RequestFailed ( e) ) )
633
630
}
634
- } } ;
635
- }
631
+ }
632
+ } ;
636
633
637
634
// First pass: try `func` on all synced and ready candidates.
638
635
//
639
636
// This ensures that we always choose a synced node if it is available.
637
+ let mut first_batch_futures = vec ! [ ] ;
640
638
for candidate in & self . candidates {
641
639
match candidate. status ( RequireSynced :: Yes ) . await {
640
+ Ok ( _) => {
641
+ first_batch_futures. push ( run_on_candidate ( candidate) ) ;
642
+ }
642
643
Err ( CandidateError :: NotSynced ) if require_synced == false => {
643
644
// This client is unsynced we will try it after trying all synced clients
644
645
retry_unsynced. push ( candidate) ;
@@ -647,22 +648,24 @@ impl<T: SlotClock, E: EthSpec> BeaconNodeFallback<T, E> {
647
648
// This client was not ready on the first pass, we might try it again later.
648
649
to_retry. push ( candidate) ;
649
650
}
650
- Ok ( _) => try_func ! ( candidate) ,
651
651
}
652
652
}
653
+ let first_batch_results = futures:: future:: join_all ( first_batch_futures) . await ;
653
654
654
655
// Second pass: try `func` on ready unsynced candidates. This only runs if we permit
655
656
// unsynced candidates.
656
657
//
657
658
// Due to async race-conditions, it is possible that we will send a request to a candidate
658
659
// that has been set to an offline/unready status. This is acceptable.
659
- if require_synced == false {
660
- for candidate in retry_unsynced {
661
- try_func ! ( candidate ) ;
662
- }
663
- }
660
+ let second_batch_results = if require_synced == false {
661
+ futures :: future :: join_all ( retry_unsynced. into_iter ( ) . map ( run_on_candidate ) ) . await
662
+ } else {
663
+ vec ! [ ]
664
+ } ;
664
665
665
666
// Third pass: try again, attempting to make non-ready clients become ready.
667
+ let mut third_batch_futures = vec ! [ ] ;
668
+ let mut third_batch_results = vec ! [ ] ;
666
669
for candidate in to_retry {
667
670
// If the candidate hasn't luckily transferred into the correct state in the meantime,
668
671
// force an update of the state.
@@ -676,16 +679,21 @@ impl<T: SlotClock, E: EthSpec> BeaconNodeFallback<T, E> {
676
679
} ;
677
680
678
681
match new_status {
679
- Ok ( ( ) ) => try_func ! ( candidate) ,
680
- Err ( CandidateError :: NotSynced ) if require_synced == false => try_func ! ( candidate) ,
681
- Err ( e) => {
682
- results. push ( Err ( (
683
- candidate. beacon_node . to_string ( ) ,
684
- Error :: Unavailable ( e) ,
685
- ) ) ) ;
682
+ Ok ( ( ) ) => third_batch_futures. push ( run_on_candidate ( candidate) ) ,
683
+ Err ( CandidateError :: NotSynced ) if require_synced == false => {
684
+ third_batch_futures. push ( run_on_candidate ( candidate) )
686
685
}
686
+ Err ( e) => third_batch_results. push ( Err ( (
687
+ candidate. beacon_node . to_string ( ) ,
688
+ Error :: Unavailable ( e) ,
689
+ ) ) ) ,
687
690
}
688
691
}
692
+ third_batch_results. extend ( futures:: future:: join_all ( third_batch_futures) . await ) ;
693
+
694
+ let mut results = first_batch_results;
695
+ results. extend ( second_batch_results) ;
696
+ results. extend ( third_batch_results) ;
689
697
690
698
let errors: Vec < _ > = results. into_iter ( ) . filter_map ( |res| res. err ( ) ) . collect ( ) ;
691
699
0 commit comments