acct idx flush keeps slot_list read lock open (#23704)

This commit is contained in:
Jeff Washington (jwash)
2022-03-16 17:29:04 -05:00
committed by GitHub
parent 18bddbc730
commit caddb851be

View File

@ -892,20 +892,20 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
} }
/// return true if 'entry' should be evicted from the in-mem index /// return true if 'entry' should be evicted from the in-mem index
fn should_evict_from_mem( fn should_evict_from_mem<'a>(
&self, &self,
current_age: Age, current_age: Age,
entry: &AccountMapEntry<T>, entry: &'a AccountMapEntry<T>,
startup: bool, startup: bool,
update_stats: bool, update_stats: bool,
exceeds_budget: bool, exceeds_budget: bool,
) -> bool { ) -> (bool, Option<std::sync::RwLockReadGuard<'a, SlotList<T>>>) {
// this could be tunable dynamically based on memory pressure // this could be tunable dynamically based on memory pressure
// we could look at more ages or we could throw out more items we are choosing to keep in the cache // we could look at more ages or we could throw out more items we are choosing to keep in the cache
if startup || (current_age == entry.age()) { if startup || (current_age == entry.age()) {
if exceeds_budget { if exceeds_budget {
// if we are already holding too many items in-mem, then we need to be more aggressive at kicking things out // if we are already holding too many items in-mem, then we need to be more aggressive at kicking things out
true (true, None)
} else { } else {
// only read the slot list if we are planning to throw the item out // only read the slot list if we are planning to throw the item out
let slot_list = entry.slot_list.read().unwrap(); let slot_list = entry.slot_list.read().unwrap();
@ -913,18 +913,18 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
if update_stats { if update_stats {
Self::update_stat(&self.stats().held_in_mem_slot_list_len, 1); Self::update_stat(&self.stats().held_in_mem_slot_list_len, 1);
} }
false // keep 0 and > 1 slot lists in mem. They will be cleaned or shrunk soon. (false, None) // keep 0 and > 1 slot lists in mem. They will be cleaned or shrunk soon.
} else { } else {
// keep items with slot lists that contained cached items // keep items with slot lists that contained cached items
let evict = !slot_list.iter().any(|(_, info)| info.is_cached()); let evict = !slot_list.iter().any(|(_, info)| info.is_cached());
if !evict && update_stats { if !evict && update_stats {
Self::update_stat(&self.stats().held_in_mem_slot_list_cached, 1); Self::update_stat(&self.stats().held_in_mem_slot_list_cached, 1);
} }
evict (evict, if evict { Some(slot_list) } else { None })
} }
} }
} else { } else {
false (false, None)
} }
} }
@ -960,11 +960,9 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
evictions = Vec::with_capacity(map.len()); evictions = Vec::with_capacity(map.len());
let m = Measure::start("flush_scan_and_update"); // we don't care about lock time in this metric - bg threads can wait let m = Measure::start("flush_scan_and_update"); // we don't care about lock time in this metric - bg threads can wait
for (k, v) in map.iter() { for (k, v) in map.iter() {
if self.should_evict_from_mem(current_age, v, startup, true, exceeds_budget) { let (evict_for_age, slot_list) =
evictions.push(*k); self.should_evict_from_mem(current_age, v, startup, true, exceeds_budget);
} else if Self::random_chance_of_eviction() { if !evict_for_age && !Self::random_chance_of_eviction() {
evictions_random.push(*k);
} else {
// not planning to remove this item from memory now, so don't write it to disk yet // not planning to remove this item from memory now, so don't write it to disk yet
continue; continue;
} }
@ -978,8 +976,11 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
// That prevents dropping an item from cache before disk is updated to latest in mem. // That prevents dropping an item from cache before disk is updated to latest in mem.
// happens inside of lock on in-mem cache. This is because of deleting items // happens inside of lock on in-mem cache. This is because of deleting items
// it is possible that the item in the cache is marked as dirty while these updates are happening. That is ok. // it is possible that the item in the cache is marked as dirty while these updates are happening. That is ok.
disk_resize = {
disk.try_write(k, (&v.slot_list.read().unwrap(), v.ref_count())); let slot_list =
slot_list.unwrap_or_else(|| v.slot_list.read().unwrap());
disk_resize = disk.try_write(k, (&slot_list, v.ref_count()));
}
if disk_resize.is_ok() { if disk_resize.is_ok() {
flush_entries_updated_on_disk += 1; flush_entries_updated_on_disk += 1;
} else { } else {
@ -987,6 +988,13 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
v.set_dirty(true); v.set_dirty(true);
break; break;
} }
} else {
drop(slot_list);
}
if evict_for_age {
evictions.push(*k);
} else {
evictions_random.push(*k);
} }
} }
Self::update_time_stat(&self.stats().flush_scan_update_us, m); Self::update_time_stat(&self.stats().flush_scan_update_us, m);
@ -1068,13 +1076,9 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
if v.dirty() if v.dirty()
|| (!randomly_evicted || (!randomly_evicted
&& !self.should_evict_from_mem( && !self
current_age, .should_evict_from_mem(current_age, v, startup, false, exceeds_budget)
v, .0)
startup,
false,
exceeds_budget,
))
{ {
// marked dirty or bumped in age after we looked above // marked dirty or bumped in age after we looked above
// these will be handled in later passes // these will be handled in later passes
@ -1200,7 +1204,9 @@ mod tests {
)); ));
// exceeded budget // exceeded budget
assert!(bucket.should_evict_from_mem( assert!(
bucket
.should_evict_from_mem(
current_age, current_age,
&Arc::new(AccountMapEntryInner::new( &Arc::new(AccountMapEntryInner::new(
vec![], vec![],
@ -1210,9 +1216,13 @@ mod tests {
startup, startup,
false, false,
true, true,
)); )
.0
);
// empty slot list // empty slot list
assert!(!bucket.should_evict_from_mem( assert!(
!bucket
.should_evict_from_mem(
current_age, current_age,
&Arc::new(AccountMapEntryInner::new( &Arc::new(AccountMapEntryInner::new(
vec![], vec![],
@ -1222,17 +1232,25 @@ mod tests {
startup, startup,
false, false,
false, false,
)); )
.0
);
// 1 element slot list // 1 element slot list
assert!(bucket.should_evict_from_mem( assert!(
bucket
.should_evict_from_mem(
current_age, current_age,
&one_element_slot_list_entry, &one_element_slot_list_entry,
startup, startup,
false, false,
false, false,
)); )
.0
);
// 2 element slot list // 2 element slot list
assert!(!bucket.should_evict_from_mem( assert!(
!bucket
.should_evict_from_mem(
current_age, current_age,
&Arc::new(AccountMapEntryInner::new( &Arc::new(AccountMapEntryInner::new(
vec![(0, 0), (1, 1)], vec![(0, 0), (1, 1)],
@ -1242,12 +1260,16 @@ mod tests {
startup, startup,
false, false,
false, false,
)); )
.0
);
{ {
let bucket = new_for_test::<f64>(); let bucket = new_for_test::<f64>();
// 1 element slot list with a CACHED item - f64 acts like cached // 1 element slot list with a CACHED item - f64 acts like cached
assert!(!bucket.should_evict_from_mem( assert!(
!bucket
.should_evict_from_mem(
current_age, current_age,
&Arc::new(AccountMapEntryInner::new( &Arc::new(AccountMapEntryInner::new(
vec![(0, 0.0)], vec![(0, 0.0)],
@ -1257,37 +1279,51 @@ mod tests {
startup, startup,
false, false,
false, false,
)); )
.0
);
} }
// 1 element slot list, age is now // 1 element slot list, age is now
assert!(bucket.should_evict_from_mem( assert!(
bucket
.should_evict_from_mem(
current_age, current_age,
&one_element_slot_list_entry, &one_element_slot_list_entry,
startup, startup,
false, false,
false, false,
)); )
.0
);
// 1 element slot list, but not current age // 1 element slot list, but not current age
current_age = 1; current_age = 1;
assert!(!bucket.should_evict_from_mem( assert!(
!bucket
.should_evict_from_mem(
current_age, current_age,
&one_element_slot_list_entry, &one_element_slot_list_entry,
startup, startup,
false, false,
false, false,
)); )
.0
);
// 1 element slot list, but at startup and age not current // 1 element slot list, but at startup and age not current
startup = true; startup = true;
assert!(bucket.should_evict_from_mem( assert!(
bucket
.should_evict_from_mem(
current_age, current_age,
&one_element_slot_list_entry, &one_element_slot_list_entry,
startup, startup,
false, false,
false, false,
)); )
.0
);
} }
#[test] #[test]