Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit 2cf53b6

Browse files
authored
Purge banks, with the same slot, sequentially (#33149)
1 parent 3b10856 commit 2cf53b6

File tree

1 file changed

+149
-13
lines changed

1 file changed

+149
-13
lines changed

runtime/src/accounts_background_service.rs

Lines changed: 149 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -502,21 +502,28 @@ pub struct PrunedBanksRequestHandler {
502502

503503
impl PrunedBanksRequestHandler {
504504
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+
});
517524
});
518525

519-
count
526+
num_banks_to_purge
520527
}
521528

522529
fn remove_dead_slots(
@@ -790,6 +797,56 @@ fn cmp_requests_by_priority(
790797
.then(slot_a.cmp(&slot_b))
791798
}
792799

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+
793850
#[cfg(test)]
794851
mod test {
795852
use {
@@ -1036,4 +1093,83 @@ mod test {
10361093
.get_next_snapshot_request(Some(480))
10371094
.is_none());
10381095
}
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+
}
10391175
}

0 commit comments

Comments
 (0)