@@ -502,21 +502,28 @@ pub struct PrunedBanksRequestHandler {
502
502
503
503
impl PrunedBanksRequestHandler {
504
504
pub fn handle_request ( & self , bank : & Bank , is_serialized_with_abs : bool ) -> usize {
505
- let slots = self . pruned_banks_receiver . try_iter ( ) . collect :: < Vec < _ > > ( ) ;
506
- let count = slots. len ( ) ;
507
- bank. rc . accounts . accounts_db . thread_pool_clean . install ( || {
508
- slots
509
- . into_par_iter ( )
510
- . for_each ( |( pruned_slot, pruned_bank_id) | {
511
- bank. rc . accounts . accounts_db . purge_slot (
512
- pruned_slot,
513
- pruned_bank_id,
514
- is_serialized_with_abs,
515
- ) ;
516
- } ) ;
505
+ let mut banks_to_purge: Vec < _ > = self . pruned_banks_receiver . try_iter ( ) . collect ( ) ;
506
+ // We need a stable sort to ensure we purge banks—with the same slot—in the same order
507
+ // they were sent into the channel.
508
+ banks_to_purge. sort_by_key ( |( slot, _id) | * slot) ;
509
+ let num_banks_to_purge = banks_to_purge. len ( ) ;
510
+
511
+ // Group the banks into slices with the same slot
512
+ let grouped_banks_to_purge: Vec < _ > =
513
+ GroupBy :: new ( banks_to_purge. as_slice ( ) , |a, b| a. 0 == b. 0 ) . collect ( ) ;
514
+
515
+ // Purge all the slots in parallel
516
+ // Banks for the same slot are purged sequentially
517
+ let accounts_db = bank. rc . accounts . accounts_db . as_ref ( ) ;
518
+ accounts_db. thread_pool_clean . install ( || {
519
+ grouped_banks_to_purge. into_par_iter ( ) . for_each ( |group| {
520
+ group. iter ( ) . for_each ( |( slot, bank_id) | {
521
+ accounts_db. purge_slot ( * slot, * bank_id, is_serialized_with_abs) ;
522
+ } )
523
+ } ) ;
517
524
} ) ;
518
525
519
- count
526
+ num_banks_to_purge
520
527
}
521
528
522
529
fn remove_dead_slots (
@@ -790,6 +797,56 @@ fn cmp_requests_by_priority(
790
797
. then ( slot_a. cmp ( & slot_b) )
791
798
}
792
799
800
+ /// An iterator over a slice producing non-overlapping runs
801
+ /// of elements using a predicate to separate them.
802
+ ///
803
+ /// This can be used to extract sorted subslices.
804
+ ///
805
+ /// (`Vec::group_by()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.group_by)
806
+ /// is currently a nightly-only experimental API. Once the API is stablized, use it instead.
807
+ ///
808
+ /// tracking issue: https://github.com/rust-lang/rust/issues/80552
809
+ /// rust-lang PR: https://github.com/rust-lang/rust/pull/79895/
810
+ /// implementation permalink: https://github.com/Kerollmops/rust/blob/8b53be660444d736bb6a6e1c6ba42c8180c968e7/library/core/src/slice/iter.rs#L2972-L3023
811
+ struct GroupBy < ' a , T : ' a , P > {
812
+ slice : & ' a [ T ] ,
813
+ predicate : P ,
814
+ }
815
+ impl < ' a , T : ' a , P > GroupBy < ' a , T , P >
816
+ where
817
+ P : FnMut ( & T , & T ) -> bool ,
818
+ {
819
+ fn new ( slice : & ' a [ T ] , predicate : P ) -> Self {
820
+ GroupBy { slice, predicate }
821
+ }
822
+ }
823
+ impl < ' a , T : ' a , P > Iterator for GroupBy < ' a , T , P >
824
+ where
825
+ P : FnMut ( & T , & T ) -> bool ,
826
+ {
827
+ type Item = & ' a [ T ] ;
828
+
829
+ #[ inline]
830
+ fn next ( & mut self ) -> Option < Self :: Item > {
831
+ if self . slice . is_empty ( ) {
832
+ None
833
+ } else {
834
+ let mut len = 1 ;
835
+ let mut iter = self . slice . windows ( 2 ) ;
836
+ while let Some ( [ l, r] ) = iter. next ( ) {
837
+ if ( self . predicate ) ( l, r) {
838
+ len += 1
839
+ } else {
840
+ break ;
841
+ }
842
+ }
843
+ let ( head, tail) = self . slice . split_at ( len) ;
844
+ self . slice = tail;
845
+ Some ( head)
846
+ }
847
+ }
848
+ }
849
+
793
850
#[ cfg( test) ]
794
851
mod test {
795
852
use {
@@ -1036,4 +1093,83 @@ mod test {
1036
1093
. get_next_snapshot_request( Some ( 480 ) )
1037
1094
. is_none( ) ) ;
1038
1095
}
1096
+
1097
+ /// Ensure that we can prune banks with the same slot (if they were on different forks)
1098
+ #[ test]
1099
+ fn test_pruned_banks_request_handler_handle_request ( ) {
1100
+ let ( pruned_banks_sender, pruned_banks_receiver) = crossbeam_channel:: unbounded ( ) ;
1101
+ let pruned_banks_request_handler = PrunedBanksRequestHandler {
1102
+ pruned_banks_receiver,
1103
+ } ;
1104
+ let genesis_config_info = create_genesis_config ( 10 ) ;
1105
+ let bank = Bank :: new_for_tests ( & genesis_config_info. genesis_config ) ;
1106
+ bank. set_startup_verification_complete ( ) ;
1107
+ bank. rc . accounts . accounts_db . enable_bank_drop_callback ( ) ;
1108
+ bank. set_callback ( Some ( Box :: new ( SendDroppedBankCallback :: new (
1109
+ pruned_banks_sender,
1110
+ ) ) ) ) ;
1111
+
1112
+ let fork0_bank0 = Arc :: new ( bank) ;
1113
+ let fork0_bank1 = Arc :: new ( Bank :: new_from_parent (
1114
+ fork0_bank0. clone ( ) ,
1115
+ & Pubkey :: new_unique ( ) ,
1116
+ fork0_bank0. slot ( ) + 1 ,
1117
+ ) ) ;
1118
+ let fork1_bank1 = Arc :: new ( Bank :: new_from_parent (
1119
+ fork0_bank0. clone ( ) ,
1120
+ & Pubkey :: new_unique ( ) ,
1121
+ fork0_bank0. slot ( ) + 1 ,
1122
+ ) ) ;
1123
+ let fork2_bank1 = Arc :: new ( Bank :: new_from_parent (
1124
+ fork0_bank0. clone ( ) ,
1125
+ & Pubkey :: new_unique ( ) ,
1126
+ fork0_bank0. slot ( ) + 1 ,
1127
+ ) ) ;
1128
+ let fork0_bank2 = Arc :: new ( Bank :: new_from_parent (
1129
+ fork0_bank1. clone ( ) ,
1130
+ & Pubkey :: new_unique ( ) ,
1131
+ fork0_bank1. slot ( ) + 1 ,
1132
+ ) ) ;
1133
+ let fork1_bank2 = Arc :: new ( Bank :: new_from_parent (
1134
+ fork1_bank1. clone ( ) ,
1135
+ & Pubkey :: new_unique ( ) ,
1136
+ fork1_bank1. slot ( ) + 1 ,
1137
+ ) ) ;
1138
+ let fork0_bank3 = Arc :: new ( Bank :: new_from_parent (
1139
+ fork0_bank2. clone ( ) ,
1140
+ & Pubkey :: new_unique ( ) ,
1141
+ fork0_bank2. slot ( ) + 1 ,
1142
+ ) ) ;
1143
+ let fork3_bank3 = Arc :: new ( Bank :: new_from_parent (
1144
+ fork0_bank2. clone ( ) ,
1145
+ & Pubkey :: new_unique ( ) ,
1146
+ fork0_bank2. slot ( ) + 1 ,
1147
+ ) ) ;
1148
+ fork0_bank3. squash ( ) ;
1149
+
1150
+ drop ( fork3_bank3) ;
1151
+ drop ( fork1_bank2) ;
1152
+ drop ( fork0_bank2) ;
1153
+ drop ( fork1_bank1) ;
1154
+ drop ( fork2_bank1) ;
1155
+ drop ( fork0_bank1) ;
1156
+ drop ( fork0_bank0) ;
1157
+ let num_banks_purged = pruned_banks_request_handler. handle_request ( & fork0_bank3, true ) ;
1158
+ assert_eq ! ( num_banks_purged, 7 ) ;
1159
+ }
1160
+
1161
+ // This test is for our copied impl of GroupBy, above.
1162
+ // When it is removed, this test can be removed.
1163
+ #[ test]
1164
+ fn test_group_by ( ) {
1165
+ let slice = & [ 1 , 1 , 1 , 3 , 3 , 2 , 2 , 2 , 1 , 0 ] ;
1166
+
1167
+ let mut iter = GroupBy :: new ( slice, |a, b| a == b) ;
1168
+ assert_eq ! ( iter. next( ) , Some ( & [ 1 , 1 , 1 ] [ ..] ) ) ;
1169
+ assert_eq ! ( iter. next( ) , Some ( & [ 3 , 3 ] [ ..] ) ) ;
1170
+ assert_eq ! ( iter. next( ) , Some ( & [ 2 , 2 , 2 ] [ ..] ) ) ;
1171
+ assert_eq ! ( iter. next( ) , Some ( & [ 1 ] [ ..] ) ) ;
1172
+ assert_eq ! ( iter. next( ) , Some ( & [ 0 ] [ ..] ) ) ;
1173
+ assert_eq ! ( iter. next( ) , None ) ;
1174
+ }
1039
1175
}
0 commit comments