DiskIdx: rename 'remove' to 'evict' (#23188)
This commit is contained in:
parent
7ebf398ed7
commit
8b32e80ee2
|
@ -44,10 +44,10 @@ pub struct InMemAccountsIndex<T: IndexValue> {
|
||||||
|
|
||||||
// pubkey ranges that this bin must hold in the cache while the range is present in this vec
|
// pubkey ranges that this bin must hold in the cache while the range is present in this vec
|
||||||
pub(crate) cache_ranges_held: CacheRangesHeld,
|
pub(crate) cache_ranges_held: CacheRangesHeld,
|
||||||
// incremented each time stop_flush is changed
|
// incremented each time stop_evictions is changed
|
||||||
stop_flush_changes: AtomicU64,
|
stop_evictions_changes: AtomicU64,
|
||||||
// true while ranges are being manipulated. Used to keep an async flush from removing things while a range is being held.
|
// true while ranges are being manipulated. Used to keep an async flush from removing things while a range is being held.
|
||||||
stop_flush: AtomicU64,
|
stop_evictions: AtomicU64,
|
||||||
// set to true when any entry in this bin is marked dirty
|
// set to true when any entry in this bin is marked dirty
|
||||||
bin_dirty: AtomicBool,
|
bin_dirty: AtomicBool,
|
||||||
// set to true while this bin is being actively flushed
|
// set to true while this bin is being actively flushed
|
||||||
|
@ -79,8 +79,8 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
||||||
.map(|disk| disk.get_bucket_from_index(bin))
|
.map(|disk| disk.get_bucket_from_index(bin))
|
||||||
.map(Arc::clone),
|
.map(Arc::clone),
|
||||||
cache_ranges_held: CacheRangesHeld::default(),
|
cache_ranges_held: CacheRangesHeld::default(),
|
||||||
stop_flush_changes: AtomicU64::default(),
|
stop_evictions_changes: AtomicU64::default(),
|
||||||
stop_flush: AtomicU64::default(),
|
stop_evictions: AtomicU64::default(),
|
||||||
bin_dirty: AtomicBool::default(),
|
bin_dirty: AtomicBool::default(),
|
||||||
flushing_active: AtomicBool::default(),
|
flushing_active: AtomicBool::default(),
|
||||||
// initialize this to max, to make it clear we have not flushed at age 0, the starting age
|
// initialize this to max, to make it clear we have not flushed at age 0, the starting age
|
||||||
|
@ -142,10 +142,10 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
||||||
pub fn keys(&self) -> Vec<Pubkey> {
|
pub fn keys(&self) -> Vec<Pubkey> {
|
||||||
Self::update_stat(&self.stats().keys, 1);
|
Self::update_stat(&self.stats().keys, 1);
|
||||||
// easiest implementation is to load evrything from disk into cache and return the keys
|
// easiest implementation is to load evrything from disk into cache and return the keys
|
||||||
self.start_stop_flush(true);
|
self.start_stop_evictions(true);
|
||||||
self.put_range_in_cache(&None::<&RangeInclusive<Pubkey>>);
|
self.put_range_in_cache(&None::<&RangeInclusive<Pubkey>>);
|
||||||
let keys = self.map().read().unwrap().keys().cloned().collect();
|
let keys = self.map().read().unwrap().keys().cloned().collect();
|
||||||
self.start_stop_flush(false);
|
self.start_stop_evictions(false);
|
||||||
keys
|
keys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -720,17 +720,17 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
||||||
already_held
|
already_held
|
||||||
}
|
}
|
||||||
|
|
||||||
/// called with 'stop'=true to stop bg flusher from removing any entries from in-mem idx
|
/// called with 'stop'=true to stop bg flusher from evicting any entries from in-mem idx
|
||||||
/// called with 'stop'=false to allow bg flusher to remove eligible (not in held ranges) entries from in-mem idx
|
/// called with 'stop'=false to allow bg flusher to evict eligible (not in held ranges) entries from in-mem idx
|
||||||
fn start_stop_flush(&self, stop: bool) {
|
fn start_stop_evictions(&self, stop: bool) {
|
||||||
if stop {
|
if stop {
|
||||||
self.stop_flush.fetch_add(1, Ordering::Release);
|
self.stop_evictions.fetch_add(1, Ordering::Release);
|
||||||
} else if 1 == self.stop_flush.fetch_sub(1, Ordering::Release) {
|
} else if 1 == self.stop_evictions.fetch_sub(1, Ordering::Release) {
|
||||||
// stop_flush went to 0, so this bucket could now be ready to be aged
|
// stop_evictions went to 0, so this bucket could now be ready to be aged
|
||||||
self.storage.wait_dirty_or_aged.notify_one();
|
self.storage.wait_dirty_or_aged.notify_one();
|
||||||
}
|
}
|
||||||
// note that this value has changed
|
// note that this value has changed
|
||||||
self.stop_flush_changes.fetch_add(1, Ordering::Release);
|
self.stop_evictions_changes.fetch_add(1, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// if 'start_holding'=true, then:
|
/// if 'start_holding'=true, then:
|
||||||
|
@ -744,7 +744,7 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
||||||
where
|
where
|
||||||
R: RangeBounds<Pubkey> + Debug,
|
R: RangeBounds<Pubkey> + Debug,
|
||||||
{
|
{
|
||||||
self.start_stop_flush(true);
|
self.start_stop_evictions(true);
|
||||||
|
|
||||||
if !start_holding || !self.add_hold_range_in_memory_if_already_held(range) {
|
if !start_holding || !self.add_hold_range_in_memory_if_already_held(range) {
|
||||||
if start_holding {
|
if start_holding {
|
||||||
|
@ -755,14 +755,14 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
||||||
self.just_set_hold_range_in_memory(range, start_holding);
|
self.just_set_hold_range_in_memory(range, start_holding);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.start_stop_flush(false);
|
self.start_stop_evictions(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put_range_in_cache<R>(&self, range: &Option<&R>)
|
fn put_range_in_cache<R>(&self, range: &Option<&R>)
|
||||||
where
|
where
|
||||||
R: RangeBounds<Pubkey>,
|
R: RangeBounds<Pubkey>,
|
||||||
{
|
{
|
||||||
assert!(self.get_stop_flush()); // caller should be controlling the lifetime of how long this needs to be present
|
assert!(self.get_stop_evictions()); // caller should be controlling the lifetime of how long this needs to be present
|
||||||
let m = Measure::start("range");
|
let m = Measure::start("range");
|
||||||
|
|
||||||
let mut added_to_mem = 0;
|
let mut added_to_mem = 0;
|
||||||
|
@ -791,12 +791,14 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
||||||
Self::update_time_stat(&self.stats().get_range_us, m);
|
Self::update_time_stat(&self.stats().get_range_us, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_stop_flush(&self) -> bool {
|
/// returns true if there are active requests to stop evictions
|
||||||
self.stop_flush.load(Ordering::Acquire) > 0
|
fn get_stop_evictions(&self) -> bool {
|
||||||
|
self.stop_evictions.load(Ordering::Acquire) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_stop_flush_changes(&self) -> u64 {
|
/// return count of calls to 'start_stop_evictions', indicating changes could have been made to eviction strategy
|
||||||
self.stop_flush_changes.load(Ordering::Acquire)
|
fn get_stop_evictions_changes(&self) -> u64 {
|
||||||
|
self.stop_evictions_changes.load(Ordering::Acquire)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn flush(&self) {
|
pub(crate) fn flush(&self) {
|
||||||
|
@ -817,6 +819,8 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
||||||
self.storage.wait_dirty_or_aged.notify_one();
|
self.storage.wait_dirty_or_aged.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// returns true if a dice roll indicates this call should result in a random eviction.
|
||||||
|
/// This causes non-determinism in cache contents per validator.
|
||||||
fn random_chance_of_eviction() -> bool {
|
fn random_chance_of_eviction() -> bool {
|
||||||
// random eviction
|
// random eviction
|
||||||
const N: usize = 1000;
|
const N: usize = 1000;
|
||||||
|
@ -831,8 +835,8 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
||||||
+ std::mem::size_of::<AccountMapEntry<T>>()
|
+ std::mem::size_of::<AccountMapEntry<T>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// return true if 'entry' should be removed from the in-mem index
|
/// return true if 'entry' should be evicted from the in-mem index
|
||||||
fn should_remove_from_mem(
|
fn should_evict_from_mem(
|
||||||
&self,
|
&self,
|
||||||
current_age: Age,
|
current_age: Age,
|
||||||
entry: &AccountMapEntry<T>,
|
entry: &AccountMapEntry<T>,
|
||||||
|
@ -856,11 +860,11 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
||||||
false // keep 0 and > 1 slot lists in mem. They will be cleaned or shrunk soon.
|
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
|
// keep items with slot lists that contained cached items
|
||||||
let remove = !slot_list.iter().any(|(_, info)| info.is_cached());
|
let evict = !slot_list.iter().any(|(_, info)| info.is_cached());
|
||||||
if !remove && 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);
|
||||||
}
|
}
|
||||||
remove
|
evict
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -887,8 +891,8 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
||||||
|
|
||||||
// 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 evictions;
|
||||||
let mut removes_random = Vec::default();
|
let mut evictions_random = Vec::default();
|
||||||
let disk = self.bucket.as_ref().unwrap();
|
let disk = self.bucket.as_ref().unwrap();
|
||||||
|
|
||||||
let mut flush_entries_updated_on_disk = 0;
|
let mut flush_entries_updated_on_disk = 0;
|
||||||
|
@ -897,13 +901,13 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
||||||
// holds read lock
|
// holds read lock
|
||||||
{
|
{
|
||||||
let map = self.map().read().unwrap();
|
let map = self.map().read().unwrap();
|
||||||
removes = 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_remove_from_mem(current_age, v, startup, true, exceeds_budget) {
|
if self.should_evict_from_mem(current_age, v, startup, true, exceeds_budget) {
|
||||||
removes.push(*k);
|
evictions.push(*k);
|
||||||
} else if Self::random_chance_of_eviction() {
|
} else if Self::random_chance_of_eviction() {
|
||||||
removes_random.push(*k);
|
evictions_random.push(*k);
|
||||||
} else {
|
} 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;
|
||||||
|
@ -936,17 +940,17 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
||||||
flush_entries_updated_on_disk,
|
flush_entries_updated_on_disk,
|
||||||
);
|
);
|
||||||
|
|
||||||
let m = Measure::start("flush_remove_or_grow");
|
let m = Measure::start("flush_evict_or_grow");
|
||||||
match disk_resize {
|
match disk_resize {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
if !self.flush_remove_from_cache(
|
if !self.evict_from_cache(
|
||||||
removes,
|
evictions,
|
||||||
current_age,
|
current_age,
|
||||||
startup,
|
startup,
|
||||||
false,
|
false,
|
||||||
exceeds_budget,
|
exceeds_budget,
|
||||||
) || !self.flush_remove_from_cache(
|
) || !self.evict_from_cache(
|
||||||
removes_random,
|
evictions_random,
|
||||||
current_age,
|
current_age,
|
||||||
startup,
|
startup,
|
||||||
true,
|
true,
|
||||||
|
@ -973,23 +977,23 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove keys in 'removes' from in-mem cache due to age
|
// remove keys in 'evictions' from in-mem cache, likely due to age
|
||||||
// return true if the removal was completed
|
// return true if the removal was completed
|
||||||
fn flush_remove_from_cache(
|
fn evict_from_cache(
|
||||||
&self,
|
&self,
|
||||||
removes: Vec<Pubkey>,
|
evictions: Vec<Pubkey>,
|
||||||
current_age: Age,
|
current_age: Age,
|
||||||
startup: bool,
|
startup: bool,
|
||||||
randomly_evicted: bool,
|
randomly_evicted: bool,
|
||||||
exceeds_budget: bool,
|
exceeds_budget: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut completed_scan = true;
|
let mut completed_scan = true;
|
||||||
if removes.is_empty() {
|
if evictions.is_empty() {
|
||||||
return completed_scan; // completed, don't need to get lock or do other work
|
return completed_scan; // completed, don't need to get lock or do other work
|
||||||
}
|
}
|
||||||
|
|
||||||
let stop_flush_changes_at_start = self.get_stop_flush_changes();
|
let stop_evictions_changes_at_start = self.get_stop_evictions_changes();
|
||||||
if self.get_stop_flush() {
|
if self.get_stop_evictions() {
|
||||||
return false; // did NOT complete, ranges were changed, so have to restart
|
return false; // did NOT complete, ranges were changed, so have to restart
|
||||||
}
|
}
|
||||||
let ranges = self.cache_ranges_held.read().unwrap().clone();
|
let ranges = self.cache_ranges_held.read().unwrap().clone();
|
||||||
|
@ -997,7 +1001,7 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
||||||
let mut removed = 0;
|
let mut removed = 0;
|
||||||
// consider chunking these so we don't hold the write lock too long
|
// consider chunking these so we don't hold the write lock too long
|
||||||
let mut map = self.map().write().unwrap();
|
let mut map = self.map().write().unwrap();
|
||||||
for k in removes {
|
for k in evictions {
|
||||||
if let Entry::Occupied(occupied) = map.entry(k) {
|
if let Entry::Occupied(occupied) = map.entry(k) {
|
||||||
let v = occupied.get();
|
let v = occupied.get();
|
||||||
if Arc::strong_count(v) > 1 {
|
if Arc::strong_count(v) > 1 {
|
||||||
|
@ -1008,7 +1012,7 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
||||||
|
|
||||||
if v.dirty()
|
if v.dirty()
|
||||||
|| (!randomly_evicted
|
|| (!randomly_evicted
|
||||||
&& !self.should_remove_from_mem(
|
&& !self.should_evict_from_mem(
|
||||||
current_age,
|
current_age,
|
||||||
v,
|
v,
|
||||||
startup,
|
startup,
|
||||||
|
@ -1028,7 +1032,7 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if stop_flush_changes_at_start != self.get_stop_flush_changes() {
|
if stop_evictions_changes_at_start != self.get_stop_evictions_changes() {
|
||||||
return false; // did NOT complete, ranges were changed, so have to restart
|
return false; // did NOT complete, ranges were changed, so have to restart
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1098,7 +1102,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_should_remove_from_mem() {
|
fn test_should_evict_from_mem() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
let bucket = new_for_test::<u64>();
|
let bucket = new_for_test::<u64>();
|
||||||
let mut startup = false;
|
let mut startup = false;
|
||||||
|
@ -1112,7 +1116,7 @@ mod tests {
|
||||||
));
|
));
|
||||||
|
|
||||||
// exceeded budget
|
// exceeded budget
|
||||||
assert!(bucket.should_remove_from_mem(
|
assert!(bucket.should_evict_from_mem(
|
||||||
current_age,
|
current_age,
|
||||||
&Arc::new(AccountMapEntryInner::new(
|
&Arc::new(AccountMapEntryInner::new(
|
||||||
vec![],
|
vec![],
|
||||||
|
@ -1124,7 +1128,7 @@ mod tests {
|
||||||
true,
|
true,
|
||||||
));
|
));
|
||||||
// empty slot list
|
// empty slot list
|
||||||
assert!(!bucket.should_remove_from_mem(
|
assert!(!bucket.should_evict_from_mem(
|
||||||
current_age,
|
current_age,
|
||||||
&Arc::new(AccountMapEntryInner::new(
|
&Arc::new(AccountMapEntryInner::new(
|
||||||
vec![],
|
vec![],
|
||||||
|
@ -1136,7 +1140,7 @@ mod tests {
|
||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
// 1 element slot list
|
// 1 element slot list
|
||||||
assert!(bucket.should_remove_from_mem(
|
assert!(bucket.should_evict_from_mem(
|
||||||
current_age,
|
current_age,
|
||||||
&one_element_slot_list_entry,
|
&one_element_slot_list_entry,
|
||||||
startup,
|
startup,
|
||||||
|
@ -1144,7 +1148,7 @@ mod tests {
|
||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
// 2 element slot list
|
// 2 element slot list
|
||||||
assert!(!bucket.should_remove_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)],
|
||||||
|
@ -1159,7 +1163,7 @@ mod tests {
|
||||||
{
|
{
|
||||||
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_remove_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)],
|
||||||
|
@ -1173,7 +1177,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1 element slot list, age is now
|
// 1 element slot list, age is now
|
||||||
assert!(bucket.should_remove_from_mem(
|
assert!(bucket.should_evict_from_mem(
|
||||||
current_age,
|
current_age,
|
||||||
&one_element_slot_list_entry,
|
&one_element_slot_list_entry,
|
||||||
startup,
|
startup,
|
||||||
|
@ -1183,7 +1187,7 @@ mod tests {
|
||||||
|
|
||||||
// 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_remove_from_mem(
|
assert!(!bucket.should_evict_from_mem(
|
||||||
current_age,
|
current_age,
|
||||||
&one_element_slot_list_entry,
|
&one_element_slot_list_entry,
|
||||||
startup,
|
startup,
|
||||||
|
@ -1193,7 +1197,7 @@ mod tests {
|
||||||
|
|
||||||
// 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_remove_from_mem(
|
assert!(bucket.should_evict_from_mem(
|
||||||
current_age,
|
current_age,
|
||||||
&one_element_slot_list_entry,
|
&one_element_slot_list_entry,
|
||||||
startup,
|
startup,
|
||||||
|
|
Loading…
Reference in New Issue