Do periodic inbound cleaning for rooted slots (#8436)
* Do periodic inbound compaction for rooted slots * Add comment * nits * Consider not_compacted_roots in cleanup_dead_slot * Renames in AccountsIndex * Rename to reflect expansion of removed accounts * Fix a comment * rename * Parallelize clean over AccountsIndex * Some niceties * Reduce locks and real chunked parallelism * Measure each step for sampling opportunities * Just noticed par iter is maybe lazy * Replace storage scan with optimized index scan * Various clean-ups * Clear uncleared_roots even if no updates
This commit is contained in:
@@ -12,6 +12,7 @@ pub struct AccountsIndex<T> {
|
||||
pub account_maps: HashMap<Pubkey, RwLock<SlotList<T>>>,
|
||||
|
||||
pub roots: HashSet<Slot>,
|
||||
pub uncleaned_roots: HashSet<Slot>,
|
||||
}
|
||||
|
||||
impl<T: Clone> AccountsIndex<T> {
|
||||
@@ -55,7 +56,7 @@ impl<T: Clone> AccountsIndex<T> {
|
||||
let mut max = 0;
|
||||
let mut rv = None;
|
||||
for (i, (slot, _t)) in list.iter().rev().enumerate() {
|
||||
if *slot >= max && (ancestors.get(slot).is_some() || self.is_root(*slot)) {
|
||||
if *slot >= max && (ancestors.contains_key(slot) || self.is_root(*slot)) {
|
||||
rv = Some((list.len() - 1) - i);
|
||||
max = *slot;
|
||||
}
|
||||
@@ -112,31 +113,47 @@ impl<T: Clone> AccountsIndex<T> {
|
||||
account_info: T,
|
||||
reclaims: &mut Vec<(Slot, T)>,
|
||||
) -> Option<T> {
|
||||
let roots = &self.roots;
|
||||
if let Some(lock) = self.account_maps.get(pubkey) {
|
||||
let mut slot_vec = lock.write().unwrap();
|
||||
// filter out old entries
|
||||
// filter out other dirty entries
|
||||
reclaims.extend(slot_vec.iter().filter(|(f, _)| *f == slot).cloned());
|
||||
slot_vec.retain(|(f, _)| *f != slot);
|
||||
|
||||
// add the new entry
|
||||
slot_vec.push((slot, account_info));
|
||||
// now, do lazy clean
|
||||
self.purge_older_root_entries(&mut slot_vec, reclaims);
|
||||
|
||||
let max_root = Self::get_max_root(roots, &slot_vec);
|
||||
|
||||
reclaims.extend(
|
||||
slot_vec
|
||||
.iter()
|
||||
.filter(|(slot, _)| Self::can_purge(max_root, *slot))
|
||||
.cloned(),
|
||||
);
|
||||
slot_vec.retain(|(slot, _)| !Self::can_purge(max_root, *slot));
|
||||
None
|
||||
} else {
|
||||
Some(account_info)
|
||||
}
|
||||
}
|
||||
|
||||
fn purge_older_root_entries(
|
||||
&self,
|
||||
slot_vec: &mut Vec<(Slot, T)>,
|
||||
reclaims: &mut Vec<(Slot, T)>,
|
||||
) {
|
||||
let roots = &self.roots;
|
||||
|
||||
let max_root = Self::get_max_root(roots, &slot_vec);
|
||||
|
||||
reclaims.extend(
|
||||
slot_vec
|
||||
.iter()
|
||||
.filter(|(slot, _)| Self::can_purge(max_root, *slot))
|
||||
.cloned(),
|
||||
);
|
||||
slot_vec.retain(|(slot, _)| !Self::can_purge(max_root, *slot));
|
||||
}
|
||||
|
||||
pub fn clean_rooted_entries(&self, pubkey: &Pubkey, reclaims: &mut Vec<(Slot, T)>) {
|
||||
if let Some(lock) = self.account_maps.get(pubkey) {
|
||||
let mut slot_vec = lock.write().unwrap();
|
||||
self.purge_older_root_entries(&mut slot_vec, reclaims);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_index(&mut self, slot: Slot, pubkey: &Pubkey, account_info: T) {
|
||||
let entry = self
|
||||
.account_maps
|
||||
@@ -155,11 +172,13 @@ impl<T: Clone> AccountsIndex<T> {
|
||||
|
||||
pub fn add_root(&mut self, slot: Slot) {
|
||||
self.roots.insert(slot);
|
||||
self.uncleaned_roots.insert(slot);
|
||||
}
|
||||
/// Remove the slot when the storage for the slot is freed
|
||||
/// Accounts no longer reference this slot.
|
||||
pub fn cleanup_dead_slot(&mut self, slot: Slot) {
|
||||
pub fn clean_dead_slot(&mut self, slot: Slot) {
|
||||
self.roots.remove(&slot);
|
||||
self.uncleaned_roots.remove(&slot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,26 +278,36 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cleanup_first() {
|
||||
fn test_clean_first() {
|
||||
let mut index = AccountsIndex::<bool>::default();
|
||||
index.add_root(0);
|
||||
index.add_root(1);
|
||||
index.cleanup_dead_slot(0);
|
||||
index.clean_dead_slot(0);
|
||||
assert!(index.is_root(1));
|
||||
assert!(!index.is_root(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cleanup_last() {
|
||||
fn test_clean_last() {
|
||||
//this behavior might be undefined, clean up should only occur on older slots
|
||||
let mut index = AccountsIndex::<bool>::default();
|
||||
index.add_root(0);
|
||||
index.add_root(1);
|
||||
index.cleanup_dead_slot(1);
|
||||
index.clean_dead_slot(1);
|
||||
assert!(!index.is_root(1));
|
||||
assert!(index.is_root(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clean_and_unclean_slot() {
|
||||
let mut index = AccountsIndex::<bool>::default();
|
||||
assert_eq!(0, index.uncleaned_roots.len());
|
||||
index.add_root(1);
|
||||
assert_eq!(1, index.uncleaned_roots.len());
|
||||
index.clean_dead_slot(1);
|
||||
assert_eq!(0, index.uncleaned_roots.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_last_wins() {
|
||||
let key = Keypair::new();
|
||||
|
||||
Reference in New Issue
Block a user