AcctIdx: respect disk idx mem size param (#22050)

This commit is contained in:
Jeff Washington (jwash) 2021-12-22 09:54:05 -06:00 committed by GitHub
parent 4d62f03297
commit 61cc7b10a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 72 additions and 17 deletions

View File

@ -806,6 +806,13 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
thread_rng().gen_range(0, N) == 0 thread_rng().gen_range(0, N) == 0
} }
/// assumes 1 entry in the slot list. Ignores overhead of the HashMap and such
fn approx_size_of_one_entry() -> usize {
std::mem::size_of::<T>()
+ std::mem::size_of::<Pubkey>()
+ std::mem::size_of::<AccountMapEntry<T>>()
}
/// return true if 'entry' should be removed from the in-mem index /// return true if 'entry' should be removed from the in-mem index
fn should_remove_from_mem( fn should_remove_from_mem(
&self, &self,
@ -813,24 +820,30 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
entry: &AccountMapEntry<T>, entry: &AccountMapEntry<T>,
startup: bool, startup: bool,
update_stats: bool, update_stats: bool,
exceeds_budget: bool,
) -> bool { ) -> bool {
// 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()) {
// only read the slot list if we are planning to throw the item out if exceeds_budget {
let slot_list = entry.slot_list.read().unwrap(); // if we are already holding too many items in-mem, then we need to be more aggressive at kicking things out
if slot_list.len() != 1 { true
if update_stats {
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.
} else { } else {
// keep items with slot lists that contained cached items // only read the slot list if we are planning to throw the item out
let remove = !slot_list.iter().any(|(_, info)| info.is_cached()); let slot_list = entry.slot_list.read().unwrap();
if !remove && update_stats { if slot_list.len() != 1 {
Self::update_stat(&self.stats().held_in_mem_slot_list_cached, 1); if update_stats {
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.
} else {
// keep items with slot lists that contained cached items
let remove = !slot_list.iter().any(|(_, info)| info.is_cached());
if !remove && update_stats {
Self::update_stat(&self.stats().held_in_mem_slot_list_cached, 1);
}
remove
} }
remove
} }
} else { } else {
false false
@ -848,6 +861,12 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
return; return;
} }
let in_mem_count = self.stats().count_in_mem.load(Ordering::Relaxed);
let limit = self.storage.mem_budget_mb;
let exceeds_budget = limit
.map(|limit| in_mem_count * Self::approx_size_of_one_entry() >= limit * 1024 * 1024)
.unwrap_or_default();
// may have to loop if disk has to grow and we have to restart // may have to loop if disk has to grow and we have to restart
loop { loop {
let mut removes; let mut removes;
@ -863,7 +882,7 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
removes = Vec::with_capacity(map.len()); removes = 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_remove_from_mem(current_age, v, startup, true) { if self.should_remove_from_mem(current_age, v, startup, true, exceeds_budget) {
removes.push(*k); removes.push(*k);
} else if Self::random_chance_of_eviction() { } else if Self::random_chance_of_eviction() {
removes_random.push(*k); removes_random.push(*k);
@ -902,9 +921,19 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
let m = Measure::start("flush_remove_or_grow"); let m = Measure::start("flush_remove_or_grow");
match disk_resize { match disk_resize {
Ok(_) => { Ok(_) => {
if !self.flush_remove_from_cache(removes, current_age, startup, false) if !self.flush_remove_from_cache(
|| !self.flush_remove_from_cache(removes_random, current_age, startup, true) removes,
{ current_age,
startup,
false,
exceeds_budget,
) || !self.flush_remove_from_cache(
removes_random,
current_age,
startup,
true,
exceeds_budget,
) {
iterate_for_age = false; // did not make it all the way through this bucket, so didn't handle age completely iterate_for_age = false; // did not make it all the way through this bucket, so didn't handle age completely
} }
Self::update_time_stat(&self.stats().flush_remove_us, m); Self::update_time_stat(&self.stats().flush_remove_us, m);
@ -934,6 +963,7 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
current_age: Age, current_age: Age,
startup: bool, startup: bool,
randomly_evicted: bool, randomly_evicted: bool,
exceeds_budget: bool,
) -> bool { ) -> bool {
let mut completed_scan = true; let mut completed_scan = true;
if removes.is_empty() { if removes.is_empty() {
@ -956,7 +986,13 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
if v.dirty() if v.dirty()
|| (!randomly_evicted || (!randomly_evicted
&& !self.should_remove_from_mem(current_age, v, startup, false)) && !self.should_remove_from_mem(
current_age,
v,
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
@ -1053,6 +1089,18 @@ mod tests {
AccountMapEntryMeta::default(), AccountMapEntryMeta::default(),
)); ));
// exceeded budget
assert!(bucket.should_remove_from_mem(
current_age,
&Arc::new(AccountMapEntryInner::new(
vec![],
ref_count,
AccountMapEntryMeta::default()
)),
startup,
false,
true,
));
// empty slot list // empty slot list
assert!(!bucket.should_remove_from_mem( assert!(!bucket.should_remove_from_mem(
current_age, current_age,
@ -1063,6 +1111,7 @@ mod tests {
)), )),
startup, startup,
false, false,
false,
)); ));
// 1 element slot list // 1 element slot list
assert!(bucket.should_remove_from_mem( assert!(bucket.should_remove_from_mem(
@ -1070,6 +1119,7 @@ mod tests {
&one_element_slot_list_entry, &one_element_slot_list_entry,
startup, startup,
false, false,
false,
)); ));
// 2 element slot list // 2 element slot list
assert!(!bucket.should_remove_from_mem( assert!(!bucket.should_remove_from_mem(
@ -1081,6 +1131,7 @@ mod tests {
)), )),
startup, startup,
false, false,
false,
)); ));
{ {
@ -1095,6 +1146,7 @@ mod tests {
)), )),
startup, startup,
false, false,
false,
)); ));
} }
@ -1104,6 +1156,7 @@ mod tests {
&one_element_slot_list_entry, &one_element_slot_list_entry,
startup, startup,
false, false,
false,
)); ));
// 1 element slot list, but not current age // 1 element slot list, but not current age
@ -1113,6 +1166,7 @@ mod tests {
&one_element_slot_list_entry, &one_element_slot_list_entry,
startup, startup,
false, false,
false,
)); ));
// 1 element slot list, but at startup and age not current // 1 element slot list, but at startup and age not current
@ -1122,6 +1176,7 @@ mod tests {
&one_element_slot_list_entry, &one_element_slot_list_entry,
startup, startup,
false, false,
false,
)); ));
} }