replace AccountsIndex btree with hashmap of 8k bins (#19212)

This commit is contained in:
Jeff Washington (jwash)
2021-08-26 18:12:43 -05:00
committed by GitHub
parent 4aaca73e42
commit 02b050e0f5
3 changed files with 250 additions and 61 deletions

View File

@ -802,6 +802,7 @@ impl Accounts {
"load_to_collect_rent_eagerly_scan_elapsed", "load_to_collect_rent_eagerly_scan_elapsed",
ancestors, ancestors,
range, range,
true,
|collector: &mut Vec<(Pubkey, AccountSharedData)>, option| { |collector: &mut Vec<(Pubkey, AccountSharedData)>, option| {
Self::load_while_filtering(collector, option, |_| true) Self::load_while_filtering(collector, option, |_| true)
}, },

View File

@ -2643,6 +2643,7 @@ impl AccountsDb {
metric_name: &'static str, metric_name: &'static str,
ancestors: &Ancestors, ancestors: &Ancestors,
scan_func: F, scan_func: F,
collect_all_unsorted: bool,
) -> A ) -> A
where where
F: Fn(&mut A, (&Pubkey, LoadedAccount, Slot)), F: Fn(&mut A, (&Pubkey, LoadedAccount, Slot)),
@ -2660,6 +2661,7 @@ impl AccountsDb {
scan_func(&mut collector, (pubkey, loaded_account, slot)); scan_func(&mut collector, (pubkey, loaded_account, slot));
} }
}, },
collect_all_unsorted,
); );
collector collector
} }
@ -2669,6 +2671,7 @@ impl AccountsDb {
metric_name: &'static str, metric_name: &'static str,
ancestors: &Ancestors, ancestors: &Ancestors,
range: R, range: R,
collect_all_unsorted: bool,
scan_func: F, scan_func: F,
) -> A ) -> A
where where
@ -2681,6 +2684,7 @@ impl AccountsDb {
metric_name, metric_name,
ancestors, ancestors,
range, range,
collect_all_unsorted,
|pubkey, (account_info, slot)| { |pubkey, (account_info, slot)| {
// unlike other scan fns, this is called from Bank::collect_rent_eagerly(), // unlike other scan fns, this is called from Bank::collect_rent_eagerly(),
// which is on-consensus processing in the banking/replaying stage. // which is on-consensus processing in the banking/replaying stage.
@ -4589,7 +4593,11 @@ impl AccountsDb {
.accounts_index .accounts_index
.account_maps .account_maps
.iter() .iter()
.map(|btree| btree.read().unwrap().keys().cloned().collect::<Vec<_>>()) .map(|map| {
let mut keys = map.read().unwrap().keys().cloned().collect::<Vec<_>>();
keys.sort_unstable(); // hashmap is not ordered, but bins are relative to each other
keys
})
.flatten() .flatten()
.collect(); .collect();
collect.stop(); collect.stop();
@ -7152,6 +7160,8 @@ pub mod tests {
); );
} }
const COLLECT_ALL_UNSORTED_FALSE: bool = false;
#[test] #[test]
fn test_accountsdb_latest_ancestor() { fn test_accountsdb_latest_ancestor() {
solana_logger::setup(); solana_logger::setup();
@ -7182,6 +7192,7 @@ pub mod tests {
|accounts: &mut Vec<AccountSharedData>, option| { |accounts: &mut Vec<AccountSharedData>, option| {
accounts.push(option.1.take_account()); accounts.push(option.1.take_account());
}, },
COLLECT_ALL_UNSORTED_FALSE,
); );
assert_eq!(accounts, vec![account1]); assert_eq!(accounts, vec![account1]);
} }
@ -8717,6 +8728,7 @@ pub mod tests {
|accounts: &mut Vec<AccountSharedData>, option| { |accounts: &mut Vec<AccountSharedData>, option| {
accounts.push(option.1.take_account()); accounts.push(option.1.take_account());
}, },
COLLECT_ALL_UNSORTED_FALSE,
); );
assert_eq!(accounts, vec![account0]); assert_eq!(accounts, vec![account0]);
@ -8727,6 +8739,7 @@ pub mod tests {
|accounts: &mut Vec<AccountSharedData>, option| { |accounts: &mut Vec<AccountSharedData>, option| {
accounts.push(option.1.take_account()); accounts.push(option.1.take_account());
}, },
COLLECT_ALL_UNSORTED_FALSE,
); );
assert_eq!(accounts.len(), 2); assert_eq!(accounts.len(), 2);
} }

View File

@ -14,10 +14,7 @@ use solana_sdk::{
pubkey::{Pubkey, PUBKEY_BYTES}, pubkey::{Pubkey, PUBKEY_BYTES},
}; };
use std::{ use std::{
collections::{ collections::{btree_map::BTreeMap, hash_map::Entry, HashMap, HashSet},
btree_map::{self, BTreeMap, Entry},
HashSet,
},
fmt::Debug, fmt::Debug,
ops::{ ops::{
Bound, Bound,
@ -32,7 +29,7 @@ use std::{
use thiserror::Error; use thiserror::Error;
pub const ITER_BATCH_SIZE: usize = 1000; pub const ITER_BATCH_SIZE: usize = 1000;
pub const BINS_DEFAULT: usize = 16; pub const BINS_DEFAULT: usize = 8192;
pub const ACCOUNTS_INDEX_CONFIG_FOR_TESTING: AccountsIndexConfig = AccountsIndexConfig { pub const ACCOUNTS_INDEX_CONFIG_FOR_TESTING: AccountsIndexConfig = AccountsIndexConfig {
bins: Some(BINS_DEFAULT), bins: Some(BINS_DEFAULT),
}; };
@ -43,7 +40,7 @@ pub type ScanResult<T> = Result<T, ScanError>;
pub type SlotList<T> = Vec<(Slot, T)>; pub type SlotList<T> = Vec<(Slot, T)>;
pub type SlotSlice<'s, T> = &'s [(Slot, T)]; pub type SlotSlice<'s, T> = &'s [(Slot, T)];
pub type RefCount = u64; pub type RefCount = u64;
pub type AccountMap<K, V> = BTreeMap<K, V>; pub type AccountMap<K, V> = HashMap<K, V>;
type AccountMapEntry<T> = Arc<AccountMapEntryInner<T>>; type AccountMapEntry<T> = Arc<AccountMapEntryInner<T>>;
@ -624,9 +621,30 @@ pub struct AccountsIndexIterator<'a, T> {
start_bound: Bound<Pubkey>, start_bound: Bound<Pubkey>,
end_bound: Bound<Pubkey>, end_bound: Bound<Pubkey>,
is_finished: bool, is_finished: bool,
collect_all_unsorted: bool,
} }
impl<'a, T> AccountsIndexIterator<'a, T> { impl<'a, T> AccountsIndexIterator<'a, T> {
fn range<'b, R>(
map: &'b AccountMapsReadLock<'b, T>,
range: R,
collect_all_unsorted: bool,
) -> Vec<(&'b Pubkey, &'b AccountMapEntry<T>)>
where
R: RangeBounds<Pubkey>,
{
let mut result = Vec::with_capacity(map.len());
for (k, v) in map.iter() {
if range.contains(k) {
result.push((k, v));
}
}
if !collect_all_unsorted {
result.sort_unstable_by(|a, b| a.0.cmp(b.0));
}
result
}
fn clone_bound(bound: Bound<&Pubkey>) -> Bound<Pubkey> { fn clone_bound(bound: Bound<&Pubkey>) -> Bound<Pubkey> {
match bound { match bound {
Unbounded => Unbounded, Unbounded => Unbounded,
@ -670,7 +688,7 @@ impl<'a, T> AccountsIndexIterator<'a, T> {
(start_bin, bin_range) (start_bin, bin_range)
} }
pub fn new<R>(index: &'a AccountsIndex<T>, range: Option<R>) -> Self pub fn new<R>(index: &'a AccountsIndex<T>, range: Option<R>, collect_all_unsorted: bool) -> Self
where where
R: RangeBounds<Pubkey>, R: RangeBounds<Pubkey>,
{ {
@ -686,6 +704,7 @@ impl<'a, T> AccountsIndexIterator<'a, T> {
account_maps: &index.account_maps, account_maps: &index.account_maps,
is_finished: false, is_finished: false,
bin_calculator: &index.bin_calculator, bin_calculator: &index.bin_calculator,
collect_all_unsorted,
} }
} }
} }
@ -699,10 +718,12 @@ impl<'a, T: 'static + Clone> Iterator for AccountsIndexIterator<'a, T> {
let (start_bin, bin_range) = self.bin_start_and_range(); let (start_bin, bin_range) = self.bin_start_and_range();
let mut chunk: Vec<(Pubkey, AccountMapEntry<T>)> = Vec::with_capacity(ITER_BATCH_SIZE); let mut chunk: Vec<(Pubkey, AccountMapEntry<T>)> = Vec::with_capacity(ITER_BATCH_SIZE);
'outer: for i in self.account_maps.iter().skip(start_bin).take(bin_range) { 'outer: for i in self.account_maps.iter().skip(start_bin).take(bin_range) {
for (pubkey, account_map_entry) in for (pubkey, account_map_entry) in Self::range(
i.read().unwrap().range((self.start_bound, self.end_bound)) &i.read().unwrap(),
{ (self.start_bound, self.end_bound),
if chunk.len() >= ITER_BATCH_SIZE { self.collect_all_unsorted,
) {
if chunk.len() >= ITER_BATCH_SIZE && !self.collect_all_unsorted {
break 'outer; break 'outer;
} }
let item = (*pubkey, account_map_entry.clone()); let item = (*pubkey, account_map_entry.clone());
@ -713,6 +734,8 @@ impl<'a, T: 'static + Clone> Iterator for AccountsIndexIterator<'a, T> {
if chunk.is_empty() { if chunk.is_empty() {
self.is_finished = true; self.is_finished = true;
return None; return None;
} else if self.collect_all_unsorted {
self.is_finished = true;
} }
self.start_bound = Excluded(chunk.last().unwrap().0); self.start_bound = Excluded(chunk.last().unwrap().0);
@ -807,11 +830,11 @@ impl<T: IsCached> AccountsIndex<T> {
(account_maps, bin_calculator) (account_maps, bin_calculator)
} }
fn iter<R>(&self, range: Option<R>) -> AccountsIndexIterator<T> fn iter<R>(&self, range: Option<R>, collect_all_unsorted: bool) -> AccountsIndexIterator<T>
where where
R: RangeBounds<Pubkey>, R: RangeBounds<Pubkey>,
{ {
AccountsIndexIterator::new(self, range) AccountsIndexIterator::new(self, range, collect_all_unsorted)
} }
fn do_checked_scan_accounts<F, R>( fn do_checked_scan_accounts<F, R>(
@ -821,6 +844,7 @@ impl<T: IsCached> AccountsIndex<T> {
scan_bank_id: BankId, scan_bank_id: BankId,
func: F, func: F,
scan_type: ScanTypes<R>, scan_type: ScanTypes<R>,
collect_all_unsorted: bool,
) -> Result<(), ScanError> ) -> Result<(), ScanError>
where where
F: FnMut(&Pubkey, (&T, Slot)), F: FnMut(&Pubkey, (&T, Slot)),
@ -973,7 +997,14 @@ impl<T: IsCached> AccountsIndex<T> {
match scan_type { match scan_type {
ScanTypes::Unindexed(range) => { ScanTypes::Unindexed(range) => {
// Pass "" not to log metrics, so RPC doesn't get spammy // Pass "" not to log metrics, so RPC doesn't get spammy
self.do_scan_accounts(metric_name, ancestors, func, range, Some(max_root)); self.do_scan_accounts(
metric_name,
ancestors,
func,
range,
Some(max_root),
collect_all_unsorted,
);
} }
ScanTypes::Indexed(IndexKey::ProgramId(program_id)) => { ScanTypes::Indexed(IndexKey::ProgramId(program_id)) => {
self.do_scan_secondary_index( self.do_scan_secondary_index(
@ -1037,11 +1068,19 @@ impl<T: IsCached> AccountsIndex<T> {
ancestors: &Ancestors, ancestors: &Ancestors,
func: F, func: F,
range: Option<R>, range: Option<R>,
collect_all_unsorted: bool,
) where ) where
F: FnMut(&Pubkey, (&T, Slot)), F: FnMut(&Pubkey, (&T, Slot)),
R: RangeBounds<Pubkey>, R: RangeBounds<Pubkey>,
{ {
self.do_scan_accounts(metric_name, ancestors, func, range, None); self.do_scan_accounts(
metric_name,
ancestors,
func,
range,
None,
collect_all_unsorted,
);
} }
// Scan accounts and return latest version of each account that is either: // Scan accounts and return latest version of each account that is either:
@ -1054,6 +1093,7 @@ impl<T: IsCached> AccountsIndex<T> {
mut func: F, mut func: F,
range: Option<R>, range: Option<R>,
max_root: Option<Slot>, max_root: Option<Slot>,
collect_all_unsorted: bool,
) where ) where
F: FnMut(&Pubkey, (&T, Slot)), F: FnMut(&Pubkey, (&T, Slot)),
R: RangeBounds<Pubkey>, R: RangeBounds<Pubkey>,
@ -1067,7 +1107,7 @@ impl<T: IsCached> AccountsIndex<T> {
let mut read_lock_elapsed = 0; let mut read_lock_elapsed = 0;
let mut iterator_elapsed = 0; let mut iterator_elapsed = 0;
let mut iterator_timer = Measure::start("iterator_elapsed"); let mut iterator_timer = Measure::start("iterator_elapsed");
for pubkey_list in self.iter(range) { for pubkey_list in self.iter(range, collect_all_unsorted) {
iterator_timer.stop(); iterator_timer.stop();
iterator_elapsed += iterator_timer.as_us(); iterator_elapsed += iterator_timer.as_us();
for (pubkey, list) in pubkey_list { for (pubkey, list) in pubkey_list {
@ -1185,7 +1225,7 @@ impl<T: IsCached> AccountsIndex<T> {
if !dead_keys.is_empty() { if !dead_keys.is_empty() {
for key in dead_keys.iter() { for key in dead_keys.iter() {
let mut w_index = self.get_account_maps_write_lock(key); let mut w_index = self.get_account_maps_write_lock(key);
if let btree_map::Entry::Occupied(index_entry) = w_index.entry(**key) { if let Entry::Occupied(index_entry) = w_index.entry(**key) {
if index_entry.get().slot_list.read().unwrap().is_empty() { if index_entry.get().slot_list.read().unwrap().is_empty() {
index_entry.remove(); index_entry.remove();
@ -1209,6 +1249,7 @@ impl<T: IsCached> AccountsIndex<T> {
where where
F: FnMut(&Pubkey, (&T, Slot)), F: FnMut(&Pubkey, (&T, Slot)),
{ {
let collect_all_unsorted = false;
// Pass "" not to log metrics, so RPC doesn't get spammy // Pass "" not to log metrics, so RPC doesn't get spammy
self.do_checked_scan_accounts( self.do_checked_scan_accounts(
"", "",
@ -1216,6 +1257,7 @@ impl<T: IsCached> AccountsIndex<T> {
scan_bank_id, scan_bank_id,
func, func,
ScanTypes::Unindexed(None::<Range<Pubkey>>), ScanTypes::Unindexed(None::<Range<Pubkey>>),
collect_all_unsorted,
) )
} }
@ -1224,10 +1266,17 @@ impl<T: IsCached> AccountsIndex<T> {
metric_name: &'static str, metric_name: &'static str,
ancestors: &Ancestors, ancestors: &Ancestors,
func: F, func: F,
collect_all_unsorted: bool,
) where ) where
F: FnMut(&Pubkey, (&T, Slot)), F: FnMut(&Pubkey, (&T, Slot)),
{ {
self.do_unchecked_scan_accounts(metric_name, ancestors, func, None::<Range<Pubkey>>); self.do_unchecked_scan_accounts(
metric_name,
ancestors,
func,
None::<Range<Pubkey>>,
collect_all_unsorted,
);
} }
/// call func with every pubkey and index visible from a given set of ancestors with range /// call func with every pubkey and index visible from a given set of ancestors with range
@ -1236,13 +1285,20 @@ impl<T: IsCached> AccountsIndex<T> {
metric_name: &'static str, metric_name: &'static str,
ancestors: &Ancestors, ancestors: &Ancestors,
range: R, range: R,
collect_all_unsorted: bool,
func: F, func: F,
) where ) where
F: FnMut(&Pubkey, (&T, Slot)), F: FnMut(&Pubkey, (&T, Slot)),
R: RangeBounds<Pubkey>, R: RangeBounds<Pubkey>,
{ {
// Only the rent logic should be calling this, which doesn't need the safety checks // Only the rent logic should be calling this, which doesn't need the safety checks
self.do_unchecked_scan_accounts(metric_name, ancestors, func, Some(range)); self.do_unchecked_scan_accounts(
metric_name,
ancestors,
func,
Some(range),
collect_all_unsorted,
);
} }
/// call func with every pubkey and index visible from a given set of ancestors /// call func with every pubkey and index visible from a given set of ancestors
@ -1256,6 +1312,8 @@ impl<T: IsCached> AccountsIndex<T> {
where where
F: FnMut(&Pubkey, (&T, Slot)), F: FnMut(&Pubkey, (&T, Slot)),
{ {
let collect_all_unsorted = false;
// Pass "" not to log metrics, so RPC doesn't get spammy // Pass "" not to log metrics, so RPC doesn't get spammy
self.do_checked_scan_accounts( self.do_checked_scan_accounts(
"", "",
@ -1263,6 +1321,7 @@ impl<T: IsCached> AccountsIndex<T> {
scan_bank_id, scan_bank_id,
func, func,
ScanTypes::<Range<Pubkey>>::Indexed(index_key), ScanTypes::<Range<Pubkey>>::Indexed(index_key),
collect_all_unsorted,
) )
} }
@ -2584,6 +2643,8 @@ pub mod tests {
} }
} }
const COLLECT_ALL_UNSORTED_FALSE: bool = false;
#[test] #[test]
fn test_get_empty() { fn test_get_empty() {
let key = Keypair::new(); let key = Keypair::new();
@ -2593,7 +2654,12 @@ pub mod tests {
assert!(index.get(&key.pubkey(), None, None).is_none()); assert!(index.get(&key.pubkey(), None, None).is_none());
let mut num = 0; let mut num = 0;
index.unchecked_scan_accounts("", &ancestors, |_pubkey, _index| num += 1); index.unchecked_scan_accounts(
"",
&ancestors,
|_pubkey, _index| num += 1,
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(num, 0); assert_eq!(num, 0);
} }
@ -2666,7 +2732,12 @@ pub mod tests {
assert!(index.get(&key.pubkey(), None, None).is_none()); assert!(index.get(&key.pubkey(), None, None).is_none());
let mut num = 0; let mut num = 0;
index.unchecked_scan_accounts("", &ancestors, |_pubkey, _index| num += 1); index.unchecked_scan_accounts(
"",
&ancestors,
|_pubkey, _index| num += 1,
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(num, 0); assert_eq!(num, 0);
} }
@ -2699,12 +2770,22 @@ pub mod tests {
assert!(index.get(pubkey, None, None).is_none()); assert!(index.get(pubkey, None, None).is_none());
let mut num = 0; let mut num = 0;
index.unchecked_scan_accounts("", &ancestors, |_pubkey, _index| num += 1); index.unchecked_scan_accounts(
"",
&ancestors,
|_pubkey, _index| num += 1,
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(num, 0); assert_eq!(num, 0);
ancestors.insert(slot, 0); ancestors.insert(slot, 0);
assert!(index.get(pubkey, Some(&ancestors), None).is_some()); assert!(index.get(pubkey, Some(&ancestors), None).is_some());
assert_eq!(index.ref_count_from_storage(pubkey), 1); assert_eq!(index.ref_count_from_storage(pubkey), 1);
index.unchecked_scan_accounts("", &ancestors, |_pubkey, _index| num += 1); index.unchecked_scan_accounts(
"",
&ancestors,
|_pubkey, _index| num += 1,
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(num, 1); assert_eq!(num, 1);
// not zero lamports // not zero lamports
@ -2718,12 +2799,22 @@ pub mod tests {
assert!(index.get(pubkey, None, None).is_none()); assert!(index.get(pubkey, None, None).is_none());
let mut num = 0; let mut num = 0;
index.unchecked_scan_accounts("", &ancestors, |_pubkey, _index| num += 1); index.unchecked_scan_accounts(
"",
&ancestors,
|_pubkey, _index| num += 1,
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(num, 0); assert_eq!(num, 0);
ancestors.insert(slot, 0); ancestors.insert(slot, 0);
assert!(index.get(pubkey, Some(&ancestors), None).is_some()); assert!(index.get(pubkey, Some(&ancestors), None).is_some());
assert_eq!(index.ref_count_from_storage(pubkey), 0); // cached, so 0 assert_eq!(index.ref_count_from_storage(pubkey), 0); // cached, so 0
index.unchecked_scan_accounts("", &ancestors, |_pubkey, _index| num += 1); index.unchecked_scan_accounts(
"",
&ancestors,
|_pubkey, _index| num += 1,
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(num, 1); assert_eq!(num, 1);
} }
@ -2915,11 +3006,21 @@ pub mod tests {
assert!(index.get(&key.pubkey(), None, None).is_none()); assert!(index.get(&key.pubkey(), None, None).is_none());
let mut num = 0; let mut num = 0;
index.unchecked_scan_accounts("", &ancestors, |_pubkey, _index| num += 1); index.unchecked_scan_accounts(
"",
&ancestors,
|_pubkey, _index| num += 1,
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(num, 0); assert_eq!(num, 0);
ancestors.insert(slot, 0); ancestors.insert(slot, 0);
assert!(index.get(&key.pubkey(), Some(&ancestors), None).is_some()); assert!(index.get(&key.pubkey(), Some(&ancestors), None).is_some());
index.unchecked_scan_accounts("", &ancestors, |_pubkey, _index| num += 1); index.unchecked_scan_accounts(
"",
&ancestors,
|_pubkey, _index| num += 1,
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(num, 1); assert_eq!(num, 1);
} }
@ -2944,7 +3045,12 @@ pub mod tests {
assert!(index.get(&key.pubkey(), Some(&ancestors), None).is_none()); assert!(index.get(&key.pubkey(), Some(&ancestors), None).is_none());
let mut num = 0; let mut num = 0;
index.unchecked_scan_accounts("", &ancestors, |_pubkey, _index| num += 1); index.unchecked_scan_accounts(
"",
&ancestors,
|_pubkey, _index| num += 1,
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(num, 0); assert_eq!(num, 0);
} }
@ -2971,12 +3077,17 @@ pub mod tests {
let mut num = 0; let mut num = 0;
let mut found_key = false; let mut found_key = false;
index.unchecked_scan_accounts("", &ancestors, |pubkey, _index| { index.unchecked_scan_accounts(
if pubkey == &key.pubkey() { "",
found_key = true &ancestors,
}; |pubkey, _index| {
num += 1 if pubkey == &key.pubkey() {
}); found_key = true
};
num += 1
},
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(num, 1); assert_eq!(num, 1);
assert!(found_key); assert!(found_key);
} }
@ -3044,9 +3155,15 @@ pub mod tests {
let ancestors = Ancestors::default(); let ancestors = Ancestors::default();
let mut scanned_keys = HashSet::new(); let mut scanned_keys = HashSet::new();
index.range_scan_accounts("", &ancestors, pubkey_range, |pubkey, _index| { index.range_scan_accounts(
scanned_keys.insert(*pubkey); "",
}); &ancestors,
pubkey_range,
COLLECT_ALL_UNSORTED_FALSE,
|pubkey, _index| {
scanned_keys.insert(*pubkey);
},
);
let mut expected_len = 0; let mut expected_len = 0;
for key in &pubkeys[index_start..index_end] { for key in &pubkeys[index_start..index_end] {
@ -3115,9 +3232,14 @@ pub mod tests {
let ancestors = Ancestors::default(); let ancestors = Ancestors::default();
let mut scanned_keys = HashSet::new(); let mut scanned_keys = HashSet::new();
index.unchecked_scan_accounts("", &ancestors, |pubkey, _index| { index.unchecked_scan_accounts(
scanned_keys.insert(*pubkey); "",
}); &ancestors,
|pubkey, _index| {
scanned_keys.insert(*pubkey);
},
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(scanned_keys.len(), num_pubkeys); assert_eq!(scanned_keys.len(), num_pubkeys);
} }
@ -3133,7 +3255,7 @@ pub mod tests {
#[test] #[test]
fn test_accounts_iter_finished() { fn test_accounts_iter_finished() {
let (index, _) = setup_accounts_index_keys(0); let (index, _) = setup_accounts_index_keys(0);
let mut iter = index.iter(None::<Range<Pubkey>>); let mut iter = index.iter(None::<Range<Pubkey>>, COLLECT_ALL_UNSORTED_FALSE);
assert!(iter.next().is_none()); assert!(iter.next().is_none());
let mut gc = vec![]; let mut gc = vec![];
index.upsert( index.upsert(
@ -3412,13 +3534,18 @@ pub mod tests {
let mut num = 0; let mut num = 0;
let mut found_key = false; let mut found_key = false;
index.unchecked_scan_accounts("", &Ancestors::default(), |pubkey, _index| { index.unchecked_scan_accounts(
if pubkey == &key.pubkey() { "",
found_key = true; &Ancestors::default(),
assert_eq!(_index, (&true, 3)); |pubkey, _index| {
}; if pubkey == &key.pubkey() {
num += 1 found_key = true;
}); assert_eq!(_index, (&true, 3));
};
num += 1
},
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(num, 1); assert_eq!(num, 1);
assert!(found_key); assert!(found_key);
} }
@ -3976,20 +4103,40 @@ pub mod tests {
#[test] #[test]
fn test_bin_start_and_range() { fn test_bin_start_and_range() {
let index = AccountsIndex::<bool>::default_for_tests(); let index = AccountsIndex::<bool>::default_for_tests();
let iter = AccountsIndexIterator::new(&index, None::<RangeInclusive<Pubkey>>); let iter = AccountsIndexIterator::new(
&index,
None::<RangeInclusive<Pubkey>>,
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!((0, usize::MAX), iter.bin_start_and_range()); assert_eq!((0, usize::MAX), iter.bin_start_and_range());
let key_0 = Pubkey::new(&[0; 32]); let key_0 = Pubkey::new(&[0; 32]);
let key_ff = Pubkey::new(&[0xff; 32]); let key_ff = Pubkey::new(&[0xff; 32]);
let iter = AccountsIndexIterator::new(&index, Some(RangeInclusive::new(key_0, key_ff))); let iter = AccountsIndexIterator::new(
&index,
Some(RangeInclusive::new(key_0, key_ff)),
COLLECT_ALL_UNSORTED_FALSE,
);
let bins = index.bins(); let bins = index.bins();
assert_eq!((0, bins), iter.bin_start_and_range()); assert_eq!((0, bins), iter.bin_start_and_range());
let iter = AccountsIndexIterator::new(&index, Some(RangeInclusive::new(key_ff, key_0))); let iter = AccountsIndexIterator::new(
&index,
Some(RangeInclusive::new(key_ff, key_0)),
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!((bins - 1, 0), iter.bin_start_and_range()); assert_eq!((bins - 1, 0), iter.bin_start_and_range());
let iter = AccountsIndexIterator::new(&index, Some((Included(key_0), Unbounded))); let iter = AccountsIndexIterator::new(
&index,
Some((Included(key_0), Unbounded)),
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!((0, usize::MAX), iter.bin_start_and_range()); assert_eq!((0, usize::MAX), iter.bin_start_and_range());
let iter = AccountsIndexIterator::new(&index, Some((Included(key_ff), Unbounded))); let iter = AccountsIndexIterator::new(
&index,
Some((Included(key_ff), Unbounded)),
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!((bins - 1, usize::MAX), iter.bin_start_and_range()); assert_eq!((bins - 1, usize::MAX), iter.bin_start_and_range());
assert_eq!( assert_eq!(
@ -4006,30 +4153,58 @@ pub mod tests {
fn test_start_end_bin() { fn test_start_end_bin() {
let index = AccountsIndex::<bool>::default_for_tests(); let index = AccountsIndex::<bool>::default_for_tests();
assert_eq!(index.bins(), BINS_DEFAULT); assert_eq!(index.bins(), BINS_DEFAULT);
let iter = AccountsIndexIterator::new(&index, None::<RangeInclusive<Pubkey>>); let iter = AccountsIndexIterator::new(
&index,
None::<RangeInclusive<Pubkey>>,
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(iter.start_bin(), 0); // no range, so 0 assert_eq!(iter.start_bin(), 0); // no range, so 0
assert_eq!(iter.end_bin_inclusive(), usize::MAX); // no range, so max assert_eq!(iter.end_bin_inclusive(), usize::MAX); // no range, so max
let key = Pubkey::new(&[0; 32]); let key = Pubkey::new(&[0; 32]);
let iter = AccountsIndexIterator::new(&index, Some(RangeInclusive::new(key, key))); let iter = AccountsIndexIterator::new(
&index,
Some(RangeInclusive::new(key, key)),
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(iter.start_bin(), 0); // start at pubkey 0, so 0 assert_eq!(iter.start_bin(), 0); // start at pubkey 0, so 0
assert_eq!(iter.end_bin_inclusive(), 0); // end at pubkey 0, so 0 assert_eq!(iter.end_bin_inclusive(), 0); // end at pubkey 0, so 0
let iter = AccountsIndexIterator::new(&index, Some((Included(key), Excluded(key)))); let iter = AccountsIndexIterator::new(
&index,
Some((Included(key), Excluded(key))),
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(iter.start_bin(), 0); // start at pubkey 0, so 0 assert_eq!(iter.start_bin(), 0); // start at pubkey 0, so 0
assert_eq!(iter.end_bin_inclusive(), 0); // end at pubkey 0, so 0 assert_eq!(iter.end_bin_inclusive(), 0); // end at pubkey 0, so 0
let iter = AccountsIndexIterator::new(&index, Some((Excluded(key), Excluded(key)))); let iter = AccountsIndexIterator::new(
&index,
Some((Excluded(key), Excluded(key))),
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(iter.start_bin(), 0); // start at pubkey 0, so 0 assert_eq!(iter.start_bin(), 0); // start at pubkey 0, so 0
assert_eq!(iter.end_bin_inclusive(), 0); // end at pubkey 0, so 0 assert_eq!(iter.end_bin_inclusive(), 0); // end at pubkey 0, so 0
let key = Pubkey::new(&[0xff; 32]); let key = Pubkey::new(&[0xff; 32]);
let iter = AccountsIndexIterator::new(&index, Some(RangeInclusive::new(key, key))); let iter = AccountsIndexIterator::new(
&index,
Some(RangeInclusive::new(key, key)),
COLLECT_ALL_UNSORTED_FALSE,
);
let bins = index.bins(); let bins = index.bins();
assert_eq!(iter.start_bin(), bins - 1); // start at highest possible pubkey, so bins - 1 assert_eq!(iter.start_bin(), bins - 1); // start at highest possible pubkey, so bins - 1
assert_eq!(iter.end_bin_inclusive(), bins - 1); assert_eq!(iter.end_bin_inclusive(), bins - 1);
let iter = AccountsIndexIterator::new(&index, Some((Included(key), Excluded(key)))); let iter = AccountsIndexIterator::new(
&index,
Some((Included(key), Excluded(key))),
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(iter.start_bin(), bins - 1); // start at highest possible pubkey, so bins - 1 assert_eq!(iter.start_bin(), bins - 1); // start at highest possible pubkey, so bins - 1
assert_eq!(iter.end_bin_inclusive(), bins - 1); assert_eq!(iter.end_bin_inclusive(), bins - 1);
let iter = AccountsIndexIterator::new(&index, Some((Excluded(key), Excluded(key)))); let iter = AccountsIndexIterator::new(
&index,
Some((Excluded(key), Excluded(key))),
COLLECT_ALL_UNSORTED_FALSE,
);
assert_eq!(iter.start_bin(), bins - 1); // start at highest possible pubkey, so bins - 1 assert_eq!(iter.start_bin(), bins - 1); // start at highest possible pubkey, so bins - 1
assert_eq!(iter.end_bin_inclusive(), bins - 1); assert_eq!(iter.end_bin_inclusive(), bins - 1);
} }