add second type to accounts index for disk (#30446)

This commit is contained in:
Jeff Washington (jwash) 2023-02-23 15:05:06 -06:00 committed by GitHub
parent 6df160bedd
commit e39626ab14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 174 additions and 138 deletions

View File

@ -24,7 +24,7 @@ fn bench_accounts_index(bencher: &mut Bencher) {
const NUM_FORKS: u64 = 16;
let mut reclaims = vec![];
let index = AccountsIndex::<AccountInfo>::new(
let index = AccountsIndex::<AccountInfo, AccountInfo>::new(
Some(ACCOUNTS_INDEX_CONFIG_FOR_BENCHMARKS),
&Arc::default(),
);

View File

@ -1344,7 +1344,7 @@ struct RemoveUnrootedSlotsSynchronization {
signal: Condvar,
}
type AccountInfoAccountsIndex = AccountsIndex<AccountInfo>;
type AccountInfoAccountsIndex = AccountsIndex<AccountInfo, AccountInfo>;
// This structure handles the load/store of the accounts
#[derive(Debug)]
@ -13593,7 +13593,7 @@ pub mod tests {
// returns the rooted entries and the storage ref count
fn roots_and_ref_count<T: IndexValue>(
index: &AccountsIndex<T>,
index: &AccountsIndex<T, T>,
locked_account_entry: &ReadAccountMapEntry<T>,
max_inclusive: Option<Slot>,
) -> (SlotList<T>, RefCount) {

View File

@ -70,7 +70,7 @@ pub type ScanResult<T> = Result<T, ScanError>;
pub type SlotList<T> = Vec<(Slot, T)>;
pub type SlotSlice<'s, T> = &'s [(Slot, T)];
pub type RefCount = u64;
pub type AccountMap<V> = Arc<InMemAccountsIndex<V>>;
pub type AccountMap<T, U> = Arc<InMemAccountsIndex<T, U>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// how accounts index 'upsert' should handle reclaims
@ -232,13 +232,18 @@ pub struct AccountMapEntryMeta {
}
impl AccountMapEntryMeta {
pub fn new_dirty<T: IndexValue>(storage: &Arc<BucketMapHolder<T>>, is_cached: bool) -> Self {
pub fn new_dirty<T: IndexValue, U: IndexValue + From<T> + Into<T>>(
storage: &Arc<BucketMapHolder<T, U>>,
is_cached: bool,
) -> Self {
AccountMapEntryMeta {
dirty: AtomicBool::new(true),
age: AtomicU8::new(storage.future_age_to_flush(is_cached)),
}
}
pub fn new_clean<T: IndexValue>(storage: &Arc<BucketMapHolder<T>>) -> Self {
pub fn new_clean<T: IndexValue, U: IndexValue + From<T> + Into<T>>(
storage: &Arc<BucketMapHolder<T, U>>,
) -> Self {
AccountMapEntryMeta {
dirty: AtomicBool::new(false),
age: AtomicU8::new(storage.future_age_to_flush(false)),
@ -397,10 +402,10 @@ impl<T: IndexValue> PreAllocatedAccountMapEntry<T> {
/// 1. new empty (refcount=0, slot_list={})
/// 2. update(slot, account_info)
/// This code is called when the first entry [ie. (slot,account_info)] for a pubkey is inserted into the index.
pub fn new(
pub fn new<U: IndexValue + From<T> + Into<T>>(
slot: Slot,
account_info: T,
storage: &Arc<BucketMapHolder<T>>,
storage: &Arc<BucketMapHolder<T, U>>,
store_raw: bool,
) -> PreAllocatedAccountMapEntry<T> {
if store_raw {
@ -410,10 +415,10 @@ impl<T: IndexValue> PreAllocatedAccountMapEntry<T> {
}
}
fn allocate(
fn allocate<U: IndexValue + From<T> + Into<T>>(
slot: Slot,
account_info: T,
storage: &Arc<BucketMapHolder<T>>,
storage: &Arc<BucketMapHolder<T, U>>,
) -> AccountMapEntry<T> {
let is_cached = account_info.is_cached();
let ref_count = u64::from(!is_cached);
@ -425,7 +430,10 @@ impl<T: IndexValue> PreAllocatedAccountMapEntry<T> {
))
}
pub fn into_account_map_entry(self, storage: &Arc<BucketMapHolder<T>>) -> AccountMapEntry<T> {
pub fn into_account_map_entry<U: IndexValue + From<T> + Into<T>>(
self,
storage: &Arc<BucketMapHolder<T, U>>,
) -> AccountMapEntry<T> {
match self {
Self::Entry(entry) => entry,
Self::Raw((slot, account_info)) => Self::allocate(slot, account_info, storage),
@ -487,8 +495,8 @@ pub struct AccountsIndexRootsStats {
pub clean_dead_slot_us: u64,
}
pub struct AccountsIndexIterator<'a, T: IndexValue> {
account_maps: &'a LockMapTypeSlice<T>,
pub struct AccountsIndexIterator<'a, T: IndexValue, U: IndexValue + From<T> + Into<T>> {
account_maps: &'a LockMapTypeSlice<T, U>,
bin_calculator: &'a PubkeyBinCalculator24,
start_bound: Bound<Pubkey>,
end_bound: Bound<Pubkey>,
@ -496,9 +504,9 @@ pub struct AccountsIndexIterator<'a, T: IndexValue> {
collect_all_unsorted: bool,
}
impl<'a, T: IndexValue> AccountsIndexIterator<'a, T> {
impl<'a, T: IndexValue, U: IndexValue + From<T> + Into<T>> AccountsIndexIterator<'a, T, U> {
fn range<R>(
map: &AccountMaps<T>,
map: &AccountMaps<T, U>,
range: R,
collect_all_unsorted: bool,
) -> Vec<(Pubkey, AccountMapEntry<T>)>
@ -556,7 +564,7 @@ impl<'a, T: IndexValue> AccountsIndexIterator<'a, T> {
}
pub fn new<R>(
index: &'a AccountsIndex<T>,
index: &'a AccountsIndex<T, U>,
range: Option<&R>,
collect_all_unsorted: bool,
) -> Self
@ -596,7 +604,9 @@ impl<'a, T: IndexValue> AccountsIndexIterator<'a, T> {
}
}
impl<'a, T: IndexValue> Iterator for AccountsIndexIterator<'a, T> {
impl<'a, T: IndexValue, U: IndexValue + From<T> + Into<T>> Iterator
for AccountsIndexIterator<'a, T, U>
{
type Item = Vec<(Pubkey, AccountMapEntry<T>)>;
fn next(&mut self) -> Option<Self::Item> {
if self.is_finished {
@ -634,10 +644,10 @@ pub trait ZeroLamport {
fn is_zero_lamport(&self) -> bool;
}
type MapType<T> = AccountMap<T>;
type LockMapType<T> = Vec<MapType<T>>;
type LockMapTypeSlice<T> = [MapType<T>];
type AccountMaps<'a, T> = &'a MapType<T>;
type MapType<T, U> = AccountMap<T, U>;
type LockMapType<T, U> = Vec<MapType<T, U>>;
type LockMapTypeSlice<T, U> = [MapType<T, U>];
type AccountMaps<'a, T, U> = &'a MapType<T, U>;
#[derive(Debug, Default)]
pub struct ScanSlotTracker {
@ -665,8 +675,10 @@ pub enum AccountsIndexScanResult {
}
#[derive(Debug)]
pub struct AccountsIndex<T: IndexValue> {
pub account_maps: LockMapType<T>,
/// T: account info type to interact in in-memory items
/// U: account info type to be persisted to disk
pub struct AccountsIndex<T: IndexValue, U: IndexValue + From<T> + Into<T>> {
pub account_maps: LockMapType<T, U>,
pub bin_calculator: PubkeyBinCalculator24,
program_id_index: SecondaryIndex<DashMapSecondaryIndexEntry>,
spl_token_mint_index: SecondaryIndex<DashMapSecondaryIndexEntry>,
@ -685,7 +697,7 @@ pub struct AccountsIndex<T: IndexValue> {
// scanning the fork with that Bank at the tip is no longer possible.
pub removed_bank_ids: Mutex<HashSet<BankId>>,
storage: AccountsIndexStorage<T>,
storage: AccountsIndexStorage<T, U>,
/// when a scan's accumulated data exceeds this limit, abort the scan
pub scan_results_limit_bytes: Option<usize>,
@ -703,7 +715,7 @@ pub struct AccountsIndex<T: IndexValue> {
pub rent_paying_accounts_by_partition: OnceCell<RentPayingAccountsByPartition>,
}
impl<T: IndexValue> AccountsIndex<T> {
impl<T: IndexValue, U: IndexValue + From<T> + Into<T>> AccountsIndex<T, U> {
pub fn default_for_tests() -> Self {
Self::new(Some(ACCOUNTS_INDEX_CONFIG_FOR_TESTING), &Arc::default())
}
@ -742,9 +754,9 @@ impl<T: IndexValue> AccountsIndex<T> {
config: Option<AccountsIndexConfig>,
exit: &Arc<AtomicBool>,
) -> (
LockMapType<T>,
LockMapType<T, U>,
PubkeyBinCalculator24,
AccountsIndexStorage<T>,
AccountsIndexStorage<T, U>,
) {
let bins = config
.as_ref()
@ -759,7 +771,7 @@ impl<T: IndexValue> AccountsIndex<T> {
(account_maps, bin_calculator, storage)
}
fn iter<R>(&self, range: Option<&R>, collect_all_unsorted: bool) -> AccountsIndexIterator<T>
fn iter<R>(&self, range: Option<&R>, collect_all_unsorted: bool) -> AccountsIndexIterator<T, U>
where
R: RangeBounds<Pubkey>,
{
@ -1121,7 +1133,7 @@ impl<T: IndexValue> AccountsIndex<T> {
pub fn get_account_read_entry_with_lock(
&self,
pubkey: &Pubkey,
lock: &AccountMaps<'_, T>,
lock: &AccountMaps<'_, T, U>,
) -> Option<ReadAccountMapEntry<T>> {
lock.get(pubkey)
.map(ReadAccountMapEntry::from_account_map_entry)
@ -1572,7 +1584,7 @@ impl<T: IndexValue> AccountsIndex<T> {
);
}
pub(crate) fn get_bin(&self, pubkey: &Pubkey) -> AccountMaps<T> {
pub(crate) fn get_bin(&self, pubkey: &Pubkey) -> AccountMaps<T, U> {
&self.account_maps[self.bin_calculator.bin_from_pubkey(pubkey)]
}
@ -2090,7 +2102,7 @@ pub mod tests {
fn create_dashmap_secondary_index_state() -> (usize, usize, AccountSecondaryIndexes) {
{
// Check that we're actually testing the correct variant
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let _type_check = SecondaryIndexTypes::DashMap(&index.spl_token_mint_index);
}
@ -2100,7 +2112,7 @@ pub mod tests {
fn create_rwlock_secondary_index_state() -> (usize, usize, AccountSecondaryIndexes) {
{
// Check that we're actually testing the correct variant
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let _type_check = SecondaryIndexTypes::RwLock(&index.spl_token_owner_index);
}
@ -2132,7 +2144,7 @@ pub mod tests {
}
}
impl<T: IndexValue> AccountsIndex<T> {
impl<T: IndexValue> AccountsIndex<T, T> {
/// provides the ability to refactor this function on the api without bloody changes
pub fn get_for_tests(
&self,
@ -2147,7 +2159,7 @@ pub mod tests {
#[test]
fn test_get_next_original_root() {
let ancestors = None;
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
for slot in 0..2 {
assert_eq!(index.get_next_original_root(slot, ancestors), None);
}
@ -2173,7 +2185,7 @@ pub mod tests {
fn test_get_next_original_root_ancestors() {
let orig_ancestors = Ancestors::default();
let ancestors = Some(&orig_ancestors);
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
for slot in 0..2 {
assert_eq!(index.get_next_original_root(slot, ancestors), None);
}
@ -2201,7 +2213,7 @@ pub mod tests {
fn test_get_next_original_root_roots_and_ancestors() {
let orig_ancestors = Ancestors::default();
let ancestors = Some(&orig_ancestors);
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
for slot in 0..2 {
assert_eq!(index.get_next_original_root(slot, ancestors), None);
}
@ -2226,7 +2238,7 @@ pub mod tests {
#[test]
fn test_remove_old_historical_roots() {
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
index.add_root(1);
index.add_root(2);
assert_eq!(
@ -2267,7 +2279,7 @@ pub mod tests {
);
// now use 'keep'
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
index.add_root(1);
index.add_root(2);
let hash_set_1 = vec![1].into_iter().collect();
@ -2307,7 +2319,7 @@ pub mod tests {
#[test]
fn test_get_empty() {
let key = solana_sdk::pubkey::new_rand();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let ancestors = Ancestors::default();
let key = &key;
assert!(index.get_for_tests(key, Some(&ancestors), None).is_none());
@ -2373,7 +2385,7 @@ pub mod tests {
#[test]
fn test_insert_no_ancestors() {
let key = solana_sdk::pubkey::new_rand();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let mut gc = Vec::new();
index.upsert(
0,
@ -2421,7 +2433,7 @@ pub mod tests {
let pubkey = &key;
let slot = 0;
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let account_info = true;
let items = vec![(*pubkey, account_info)];
index.set_startup(Startup::Startup);
@ -2456,7 +2468,7 @@ pub mod tests {
assert_eq!(num, 1);
// not zero lamports
let index = AccountsIndex::<AccountInfoTest>::default_for_tests();
let index = AccountsIndex::<AccountInfoTest, AccountInfoTest>::default_for_tests();
let account_info: AccountInfoTest = 0 as AccountInfoTest;
let items = vec![(*pubkey, account_info)];
index.set_startup(Startup::Startup);
@ -2494,7 +2506,7 @@ pub mod tests {
fn get_pre_allocated<T: IndexValue>(
slot: Slot,
account_info: T,
storage: &Arc<BucketMapHolder<T>>,
storage: &Arc<BucketMapHolder<T, T>>,
store_raw: bool,
to_raw_first: bool,
) -> PreAllocatedAccountMapEntry<T> {
@ -2562,7 +2574,7 @@ pub mod tests {
let key0 = solana_sdk::pubkey::new_rand();
let key1 = solana_sdk::pubkey::new_rand();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let account_infos = [true, false];
index.set_startup(Startup::Startup);
@ -2600,7 +2612,7 @@ pub mod tests {
} else {
IndexLimitMb::InMemOnly // in-mem only
};
let index = AccountsIndex::<T>::new(Some(config), &Arc::default());
let index = AccountsIndex::<T, T>::new(Some(config), &Arc::default());
let mut gc = Vec::new();
if upsert {
@ -2715,7 +2727,7 @@ pub mod tests {
#[test]
fn test_insert_with_lock_no_ancestors() {
let key = solana_sdk::pubkey::new_rand();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let slot = 0;
let account_info = true;
@ -2761,7 +2773,7 @@ pub mod tests {
#[test]
fn test_insert_wrong_ancestors() {
let key = solana_sdk::pubkey::new_rand();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let mut gc = Vec::new();
index.upsert(
0,
@ -2792,7 +2804,7 @@ pub mod tests {
{
// non-cached
let key = solana_sdk::pubkey::new_rand();
let index = AccountsIndex::<u64>::default_for_tests();
let index = AccountsIndex::<u64, u64>::default_for_tests();
let mut reclaims = Vec::new();
let slot = 0;
let value = 1;
@ -2838,7 +2850,7 @@ pub mod tests {
{
// cached
let key = solana_sdk::pubkey::new_rand();
let index = AccountsIndex::<AccountInfoTest>::default_for_tests();
let index = AccountsIndex::<AccountInfoTest, AccountInfoTest>::default_for_tests();
let mut reclaims = Vec::new();
let slot = 0;
let value = 1.0;
@ -2886,7 +2898,7 @@ pub mod tests {
#[test]
fn test_insert_with_ancestors() {
let key = solana_sdk::pubkey::new_rand();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let mut gc = Vec::new();
index.upsert(
0,
@ -2921,8 +2933,8 @@ pub mod tests {
assert!(found_key);
}
fn setup_accounts_index_keys(num_pubkeys: usize) -> (AccountsIndex<bool>, Vec<Pubkey>) {
let index = AccountsIndex::<bool>::default_for_tests();
fn setup_accounts_index_keys(num_pubkeys: usize) -> (AccountsIndex<bool, bool>, Vec<Pubkey>) {
let index = AccountsIndex::<bool, bool>::default_for_tests();
let root_slot = 0;
let mut pubkeys: Vec<Pubkey> = std::iter::repeat_with(|| {
@ -2962,7 +2974,7 @@ pub mod tests {
}
fn run_test_range(
index: &AccountsIndex<bool>,
index: &AccountsIndex<bool, bool>,
pubkeys: &[Pubkey],
start_bound: Bound<usize>,
end_bound: Bound<usize>,
@ -3004,7 +3016,7 @@ pub mod tests {
}
fn run_test_range_indexes(
index: &AccountsIndex<bool>,
index: &AccountsIndex<bool, bool>,
pubkeys: &[Pubkey],
start: Option<usize>,
end: Option<usize>,
@ -3102,7 +3114,7 @@ pub mod tests {
#[test]
fn test_is_alive_root() {
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
assert!(!index.is_alive_root(0));
index.add_root(0);
assert!(index.is_alive_root(0));
@ -3111,7 +3123,7 @@ pub mod tests {
#[test]
fn test_insert_with_root() {
let key = solana_sdk::pubkey::new_rand();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let mut gc = Vec::new();
index.upsert(
0,
@ -3132,7 +3144,7 @@ pub mod tests {
#[test]
fn test_clean_first() {
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
index.add_root(0);
index.add_root(1);
index.clean_dead_slot(0, &mut AccountsIndexRootsStats::default());
@ -3143,7 +3155,7 @@ pub mod tests {
#[test]
fn test_clean_last() {
//this behavior might be undefined, clean up should only occur on older slots
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
index.add_root(0);
index.add_root(1);
index.clean_dead_slot(1, &mut AccountsIndexRootsStats::default());
@ -3153,7 +3165,7 @@ pub mod tests {
#[test]
fn test_clean_and_unclean_slot() {
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
assert_eq!(0, index.roots_tracker.read().unwrap().uncleaned_roots.len());
index.add_root(0);
index.add_root(1);
@ -3227,7 +3239,7 @@ pub mod tests {
#[test]
fn test_update_last_wins() {
let key = solana_sdk::pubkey::new_rand();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let ancestors = vec![(0, 0)].into_iter().collect();
let mut gc = Vec::new();
index.upsert(
@ -3265,7 +3277,7 @@ pub mod tests {
fn test_update_new_slot() {
solana_logger::setup();
let key = solana_sdk::pubkey::new_rand();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let ancestors = vec![(0, 0)].into_iter().collect();
let mut gc = Vec::new();
index.upsert(
@ -3300,7 +3312,7 @@ pub mod tests {
#[test]
fn test_update_gc_purged_slot() {
let key = solana_sdk::pubkey::new_rand();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let mut gc = Vec::new();
index.upsert(
0,
@ -3381,14 +3393,14 @@ pub mod tests {
assert!(found_key);
}
fn account_maps_stats_len<T: IndexValue>(index: &AccountsIndex<T>) -> usize {
fn account_maps_stats_len<T: IndexValue>(index: &AccountsIndex<T, T>) -> usize {
index.storage.storage.stats.total_count()
}
#[test]
fn test_purge() {
let key = solana_sdk::pubkey::new_rand();
let index = AccountsIndex::<u64>::default_for_tests();
let index = AccountsIndex::<u64, u64>::default_for_tests();
let mut gc = Vec::new();
assert_eq!(0, account_maps_stats_len(&index));
index.upsert(
@ -3439,7 +3451,7 @@ pub mod tests {
#[test]
fn test_latest_slot() {
let slot_slice = vec![(0, true), (5, true), (3, true), (7, true)];
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
// No ancestors, no root, should return None
assert!(index.latest_slot(None, &slot_slice, None).is_none());
@ -3482,7 +3494,7 @@ pub mod tests {
fn run_test_purge_exact_secondary_index<
SecondaryIndexEntryType: SecondaryIndexEntry + Default + Sync + Send,
>(
index: &AccountsIndex<bool>,
index: &AccountsIndex<bool, bool>,
secondary_index: &SecondaryIndex<SecondaryIndexEntryType>,
key_start: usize,
key_end: usize,
@ -3548,7 +3560,7 @@ pub mod tests {
#[test]
fn test_purge_exact_dashmap_secondary_index() {
let (key_start, key_end, secondary_indexes) = create_dashmap_secondary_index_state();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
run_test_purge_exact_secondary_index(
&index,
&index.spl_token_mint_index,
@ -3561,7 +3573,7 @@ pub mod tests {
#[test]
fn test_purge_exact_rwlock_secondary_index() {
let (key_start, key_end, secondary_indexes) = create_rwlock_secondary_index_state();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
run_test_purge_exact_secondary_index(
&index,
&index.spl_token_owner_index,
@ -3574,7 +3586,7 @@ pub mod tests {
#[test]
fn test_purge_older_root_entries() {
// No roots, should be no reclaims
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let mut slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
let mut reclaims = vec![];
index.purge_older_root_entries(&mut slot_list, &mut reclaims, None);
@ -3666,7 +3678,7 @@ pub mod tests {
SecondaryIndexEntryType: SecondaryIndexEntry + Default + Sync + Send,
>(
token_id: &Pubkey,
index: &AccountsIndex<bool>,
index: &AccountsIndex<bool, bool>,
secondary_index: &SecondaryIndex<SecondaryIndexEntryType>,
key_start: usize,
key_end: usize,
@ -3766,7 +3778,7 @@ pub mod tests {
#[test]
fn test_dashmap_secondary_index() {
let (key_start, key_end, secondary_indexes) = create_dashmap_secondary_index_state();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
for token_id in [inline_spl_token::id(), inline_spl_token_2022::id()] {
run_test_spl_token_secondary_indexes(
&token_id,
@ -3782,7 +3794,7 @@ pub mod tests {
#[test]
fn test_rwlock_secondary_index() {
let (key_start, key_end, secondary_indexes) = create_rwlock_secondary_index_state();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
for token_id in [inline_spl_token::id(), inline_spl_token_2022::id()] {
run_test_spl_token_secondary_indexes(
&token_id,
@ -3799,7 +3811,7 @@ pub mod tests {
SecondaryIndexEntryType: SecondaryIndexEntry + Default + Sync + Send,
>(
token_id: &Pubkey,
index: &AccountsIndex<bool>,
index: &AccountsIndex<bool, bool>,
secondary_index: &SecondaryIndex<SecondaryIndexEntryType>,
index_key_start: usize,
index_key_end: usize,
@ -3887,7 +3899,7 @@ pub mod tests {
#[test]
fn test_dashmap_secondary_index_same_slot_and_forks() {
let (key_start, key_end, account_index) = create_dashmap_secondary_index_state();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
for token_id in [inline_spl_token::id(), inline_spl_token_2022::id()] {
run_test_secondary_indexes_same_slot_and_forks(
&token_id,
@ -3903,7 +3915,7 @@ pub mod tests {
#[test]
fn test_rwlock_secondary_index_same_slot_and_forks() {
let (key_start, key_end, account_index) = create_rwlock_secondary_index_state();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
for token_id in [inline_spl_token::id(), inline_spl_token_2022::id()] {
run_test_secondary_indexes_same_slot_and_forks(
&token_id,
@ -3942,7 +3954,7 @@ pub mod tests {
#[test]
fn test_bin_start_and_range() {
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let iter = AccountsIndexIterator::new(
&index,
None::<&RangeInclusive<Pubkey>>,
@ -3984,7 +3996,7 @@ pub mod tests {
#[test]
fn test_get_newest_root_in_slot_list() {
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let return_0 = 0;
let slot1 = 1;
let slot2 = 2;
@ -3996,7 +4008,7 @@ pub mod tests {
let slot_list = Vec::<(Slot, bool)>::default();
assert_eq!(
return_0,
AccountsIndex::get_newest_root_in_slot_list(
AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
&roots_tracker.alive_roots,
&slot_list,
Some(slot1),
@ -4004,7 +4016,7 @@ pub mod tests {
);
assert_eq!(
return_0,
AccountsIndex::get_newest_root_in_slot_list(
AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
&roots_tracker.alive_roots,
&slot_list,
Some(slot2),
@ -4012,7 +4024,7 @@ pub mod tests {
);
assert_eq!(
return_0,
AccountsIndex::get_newest_root_in_slot_list(
AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
&roots_tracker.alive_roots,
&slot_list,
Some(slot99),
@ -4027,7 +4039,7 @@ pub mod tests {
let slot_list = vec![(slot2, true)];
assert_eq!(
slot2,
AccountsIndex::get_newest_root_in_slot_list(
AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
&roots_tracker.alive_roots,
&slot_list,
Some(slot2),
@ -4036,7 +4048,7 @@ pub mod tests {
// no newest root
assert_eq!(
return_0,
AccountsIndex::get_newest_root_in_slot_list(
AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
&roots_tracker.alive_roots,
&slot_list,
Some(slot1),
@ -4044,7 +4056,7 @@ pub mod tests {
);
assert_eq!(
slot2,
AccountsIndex::get_newest_root_in_slot_list(
AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
&roots_tracker.alive_roots,
&slot_list,
Some(slot99),
@ -4053,7 +4065,7 @@ pub mod tests {
}
}
impl<T: IndexValue> AccountsIndex<T> {
impl<T: IndexValue> AccountsIndex<T, T> {
fn upsert_simple_test(&self, key: &Pubkey, slot: Slot, value: T) {
let mut gc = Vec::new();
self.upsert(
@ -4074,7 +4086,7 @@ pub mod tests {
fn test_unref() {
let value = true;
let key = solana_sdk::pubkey::new_rand();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let slot1 = 1;
index.upsert_simple_test(&key, slot1, value);
@ -4107,7 +4119,7 @@ pub mod tests {
let value = true;
let key = solana_sdk::pubkey::new_rand();
let key_unknown = solana_sdk::pubkey::new_rand();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
let slot1 = 1;
let mut gc = Vec::new();
@ -4165,7 +4177,7 @@ pub mod tests {
let slot_list = vec![(slot2, value)];
assert_eq!(
0,
AccountsIndex::get_newest_root_in_slot_list(
AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
&roots_tracker.alive_roots,
&slot_list,
None,
@ -4178,7 +4190,7 @@ pub mod tests {
let slot_list = vec![(slot2, value)];
assert_eq!(
slot2,
AccountsIndex::get_newest_root_in_slot_list(
AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
&roots_tracker.alive_roots,
&slot_list,
None,
@ -4186,7 +4198,7 @@ pub mod tests {
);
assert_eq!(
0,
AccountsIndex::get_newest_root_in_slot_list(
AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
&roots_tracker.alive_roots,
&slot_list,
Some(0),
@ -4208,7 +4220,7 @@ pub mod tests {
#[test]
fn test_handle_dead_keys_return() {
let key = solana_sdk::pubkey::new_rand();
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
assert_eq!(
index.handle_dead_keys(&[&key], &AccountSecondaryIndexes::default()),
@ -4218,7 +4230,7 @@ pub mod tests {
#[test]
fn test_start_end_bin() {
let index = AccountsIndex::<bool>::default_for_tests();
let index = AccountsIndex::<bool, bool>::default_for_tests();
assert_eq!(index.bins(), BINS_FOR_TESTING);
let iter = AccountsIndexIterator::new(
&index,
@ -4282,7 +4294,7 @@ pub mod tests {
fn test_illegal_bins() {
let mut config = AccountsIndexConfig::default();
config.bins = Some(3);
AccountsIndex::<bool>::new(Some(config), &Arc::default());
AccountsIndex::<bool, bool>::new(Some(config), &Arc::default());
}
#[test]

View File

@ -16,18 +16,18 @@ use {
};
/// Manages the lifetime of the background processing threads.
pub struct AccountsIndexStorage<T: IndexValue> {
pub struct AccountsIndexStorage<T: IndexValue, U: IndexValue + From<T> + Into<T>> {
_bg_threads: BgThreads,
pub storage: Arc<BucketMapHolder<T>>,
pub in_mem: Vec<Arc<InMemAccountsIndex<T>>>,
pub storage: Arc<BucketMapHolder<T, U>>,
pub in_mem: Vec<Arc<InMemAccountsIndex<T, U>>>,
exit: Arc<AtomicBool>,
/// set_startup(true) creates bg threads which are kept alive until set_startup(false)
startup_worker_threads: Mutex<Option<BgThreads>>,
}
impl<T: IndexValue> Debug for AccountsIndexStorage<T> {
impl<T: IndexValue, U: IndexValue + From<T> + Into<T>> Debug for AccountsIndexStorage<T, U> {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Ok(())
}
@ -53,9 +53,9 @@ impl Drop for BgThreads {
}
impl BgThreads {
fn new<T: IndexValue>(
storage: &Arc<BucketMapHolder<T>>,
in_mem: &[Arc<InMemAccountsIndex<T>>],
fn new<T: IndexValue, U: IndexValue + From<T> + Into<T>>(
storage: &Arc<BucketMapHolder<T, U>>,
in_mem: &[Arc<InMemAccountsIndex<T, U>>],
threads: usize,
can_advance_age: bool,
exit: &Arc<AtomicBool>,
@ -109,7 +109,7 @@ pub enum Startup {
StartupWithExtraThreads,
}
impl<T: IndexValue> AccountsIndexStorage<T> {
impl<T: IndexValue, U: IndexValue + From<T> + Into<T>> AccountsIndexStorage<T, U> {
/// startup=true causes:
/// in mem to act in a way that flushes to disk asap
/// also creates some additional bg threads to facilitate flushing to disk asap

View File

@ -13,6 +13,7 @@ use {
},
std::{
fmt::Debug,
marker::PhantomData,
sync::{
atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering},
Arc,
@ -27,8 +28,8 @@ const AGE_MS: u64 = DEFAULT_MS_PER_SLOT; // match one age per slot time
// 10 GB limit for in-mem idx. In practice, we don't get this high. This tunes how aggressively to save items we expect to use soon.
pub const DEFAULT_DISK_INDEX: Option<usize> = Some(10_000);
pub struct BucketMapHolder<T: IndexValue> {
pub disk: Option<BucketMap<(Slot, T)>>,
pub struct BucketMapHolder<T: IndexValue, U: IndexValue + From<T> + Into<T>> {
pub disk: Option<BucketMap<(Slot, U)>>,
pub count_buckets_flushed: AtomicUsize,
@ -66,16 +67,17 @@ pub struct BucketMapHolder<T: IndexValue> {
/// and writing to disk in parallel are.
/// Note startup is an optimization and is not required for correctness.
startup: AtomicBool,
_phantom: PhantomData<T>,
}
impl<T: IndexValue> Debug for BucketMapHolder<T> {
impl<T: IndexValue, U: IndexValue + From<T> + Into<T>> Debug for BucketMapHolder<T, U> {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Ok(())
}
}
#[allow(clippy::mutex_atomic)]
impl<T: IndexValue> BucketMapHolder<T> {
impl<T: IndexValue, U: IndexValue + From<T> + Into<T>> BucketMapHolder<T, U> {
/// is the accounts index using disk as a backing store
pub fn is_disk_index_enabled(&self) -> bool {
self.disk.is_some()
@ -256,6 +258,7 @@ impl<T: IndexValue> BucketMapHolder<T> {
startup: AtomicBool::default(),
mem_budget_mb,
threads,
_phantom: PhantomData::default(),
}
}
@ -328,7 +331,7 @@ impl<T: IndexValue> BucketMapHolder<T> {
pub fn background(
&self,
exit: Vec<Arc<AtomicBool>>,
in_mem: Vec<Arc<InMemAccountsIndex<T>>>,
in_mem: Vec<Arc<InMemAccountsIndex<T, U>>>,
can_advance_age: bool,
) {
let bins = in_mem.len();
@ -402,7 +405,7 @@ pub mod tests {
fn test_next_bucket_to_flush() {
solana_logger::setup();
let bins = 4;
let test = BucketMapHolder::<u64>::new(bins, &Some(AccountsIndexConfig::default()), 1);
let test = BucketMapHolder::<u64, u64>::new(bins, &Some(AccountsIndexConfig::default()), 1);
let visited = (0..bins)
.map(|_| AtomicUsize::default())
.collect::<Vec<_>>();
@ -425,7 +428,7 @@ pub mod tests {
fn test_ages() {
solana_logger::setup();
let bins = 4;
let test = BucketMapHolder::<u64>::new(bins, &Some(AccountsIndexConfig::default()), 1);
let test = BucketMapHolder::<u64, u64>::new(bins, &Some(AccountsIndexConfig::default()), 1);
assert_eq!(0, test.current_age());
assert_eq!(test.ages_to_stay_in_cache, test.future_age_to_flush(false));
assert_eq!(u8::MAX, test.future_age_to_flush(true));
@ -445,7 +448,7 @@ pub mod tests {
fn test_age_increment() {
solana_logger::setup();
let bins = 4;
let test = BucketMapHolder::<u64>::new(bins, &Some(AccountsIndexConfig::default()), 1);
let test = BucketMapHolder::<u64, u64>::new(bins, &Some(AccountsIndexConfig::default()), 1);
for age in 0..513 {
assert_eq!(test.current_age(), (age % 256) as Age);
@ -466,7 +469,7 @@ pub mod tests {
fn test_throttle() {
solana_logger::setup();
let bins = 128;
let test = BucketMapHolder::<u64>::new(bins, &Some(AccountsIndexConfig::default()), 1);
let test = BucketMapHolder::<u64, u64>::new(bins, &Some(AccountsIndexConfig::default()), 1);
let bins = test.bins as u64;
let interval_ms = test.age_interval_ms();
// 90% of time elapsed, all but 1 bins flushed, should not wait since we'll end up right on time
@ -498,7 +501,7 @@ pub mod tests {
index_limit_mb: IndexLimitMb::Limit(0),
..AccountsIndexConfig::default()
};
let test = BucketMapHolder::<u64>::new(bins, &Some(config), 1);
let test = BucketMapHolder::<u64, u64>::new(bins, &Some(config), 1);
assert!(test.is_disk_index_enabled());
}
@ -506,7 +509,7 @@ pub mod tests {
fn test_age_time() {
solana_logger::setup();
let bins = 1;
let test = BucketMapHolder::<u64>::new(bins, &Some(AccountsIndexConfig::default()), 1);
let test = BucketMapHolder::<u64, u64>::new(bins, &Some(AccountsIndexConfig::default()), 1);
let threads = 2;
let time = AGE_MS * 8 / 3;
let expected = (time / AGE_MS) as Age;
@ -538,7 +541,7 @@ pub mod tests {
fn test_age_broad() {
solana_logger::setup();
let bins = 4;
let test = BucketMapHolder::<u64>::new(bins, &Some(AccountsIndexConfig::default()), 1);
let test = BucketMapHolder::<u64, u64>::new(bins, &Some(AccountsIndexConfig::default()), 1);
assert_eq!(test.current_age(), 0);
for _ in 0..bins {
assert!(!test.all_buckets_flushed_at_current_age());

View File

@ -99,7 +99,11 @@ impl BucketMapHolderStats {
per_bucket.map(|stat| stat.fetch_sub(count, Ordering::Relaxed));
}
fn ms_per_age<T: IndexValue>(&self, storage: &BucketMapHolder<T>, elapsed_ms: u64) -> u64 {
fn ms_per_age<T: IndexValue, U: IndexValue + From<T> + Into<T>>(
&self,
storage: &BucketMapHolder<T, U>,
elapsed_ms: u64,
) -> u64 {
let age_now = storage.current_age();
let ages_flushed = storage.count_buckets_flushed() as u64;
let last_age = self.last_age.swap(age_now, Ordering::Relaxed) as u64;
@ -172,7 +176,10 @@ impl BucketMapHolderStats {
in_mem.saturating_sub(held_in_mem) as usize
}
pub fn report_stats<T: IndexValue>(&self, storage: &BucketMapHolder<T>) {
pub fn report_stats<T: IndexValue, U: IndexValue + From<T> + Into<T>>(
&self,
storage: &BucketMapHolder<T, U>,
) {
let elapsed_ms = self.last_time.elapsed_ms();
if elapsed_ms < STATS_INTERVAL_MS {
return;

View File

@ -83,15 +83,15 @@ impl<T: IndexValue> PossibleEvictions<T> {
}
// one instance of this represents one bin of the accounts index.
pub struct InMemAccountsIndex<T: IndexValue> {
pub struct InMemAccountsIndex<T: IndexValue, U: IndexValue + From<T> + Into<T>> {
last_age_flushed: AtomicU8,
// backing store
map_internal: RwLock<InMemMap<T>>,
storage: Arc<BucketMapHolder<T>>,
storage: Arc<BucketMapHolder<T, U>>,
bin: usize,
bucket: Option<Arc<BucketApi<(Slot, T)>>>,
bucket: Option<Arc<BucketApi<(Slot, U)>>>,
// pubkey ranges that this bin must hold in the cache while the range is present in this vec
pub(crate) cache_ranges_held: CacheRangesHeld,
@ -112,7 +112,7 @@ pub struct InMemAccountsIndex<T: IndexValue> {
age_to_flush_bin_mod: Age,
}
impl<T: IndexValue> Debug for InMemAccountsIndex<T> {
impl<T: IndexValue, U: IndexValue + From<T> + Into<T>> Debug for InMemAccountsIndex<T, U> {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Ok(())
}
@ -141,8 +141,8 @@ struct FlushScanResult<T> {
evictions_random: Vec<(Pubkey, AccountMapEntry<T>)>,
}
impl<T: IndexValue> InMemAccountsIndex<T> {
pub fn new(storage: &Arc<BucketMapHolder<T>>, bin: usize) -> Self {
impl<T: IndexValue, U: IndexValue + From<T> + Into<T>> InMemAccountsIndex<T, U> {
pub fn new(storage: &Arc<BucketMapHolder<T, U>>, bin: usize) -> Self {
let ages_to_stay_in_cache = storage.ages_to_stay_in_cache;
Self {
map_internal: RwLock::default(),
@ -237,7 +237,7 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
keys
}
fn load_from_disk(&self, pubkey: &Pubkey) -> Option<(SlotList<T>, RefCount)> {
fn load_from_disk(&self, pubkey: &Pubkey) -> Option<(SlotList<U>, RefCount)> {
self.bucket.as_ref().and_then(|disk| {
let m = Measure::start("load_disk_found_count");
let entry_disk = disk.read_value(pubkey);
@ -641,11 +641,14 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
// convert from raw data on disk to AccountMapEntry, set to age in future
fn disk_to_cache_entry(
&self,
slot_list: SlotList<T>,
slot_list: SlotList<U>,
ref_count: RefCount,
) -> AccountMapEntry<T> {
Arc::new(AccountMapEntryInner::new(
slot_list,
slot_list
.into_iter()
.map(|(slot, info)| (slot, info.into()))
.collect(),
ref_count,
AccountMapEntryMeta::new_clean(&self.storage),
))
@ -681,7 +684,7 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
Entry::Occupied(occupied) => {
// in cache, so merge into cache
let (slot, account_info) = new_entry.into();
InMemAccountsIndex::lock_and_update_slot_list(
InMemAccountsIndex::<T, U>::lock_and_update_slot_list(
occupied.get(),
(slot, account_info),
None, // should be None because we don't expect a different slot # during index generation
@ -699,7 +702,7 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
self.stats().inc_mem_count(self.bin);
if let Some(disk_entry) = disk_entry {
let (slot, account_info) = new_entry.into();
InMemAccountsIndex::lock_and_update_slot_list(
InMemAccountsIndex::<T, U>::lock_and_update_slot_list(
&disk_entry,
(slot, account_info),
// None because we are inserting the first element in the slot list for this pubkey.
@ -1051,7 +1054,7 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
// merge this in, mark as conflict
let mut slot_list = Vec::with_capacity(current_slot_list.len() + 1);
slot_list.extend_from_slice(current_slot_list);
slot_list.push(entry); // will never be from the same slot that already exists in the list
slot_list.push((entry.0, entry.1.into())); // will never be from the same slot that already exists in the list
ref_count += new_ref_count;
duplicates.push((slot, k));
Some((slot_list, ref_count))
@ -1059,7 +1062,7 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
None => {
count += 1;
// not on disk, insert it
Some((vec![entry], new_ref_count))
Some((vec![(entry.0, entry.1.into())], new_ref_count))
}
}
});
@ -1159,7 +1162,16 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
let slot_list = slot_list
.take()
.unwrap_or_else(|| v.slot_list.read().unwrap());
disk.try_write(&k, (&slot_list, v.ref_count()))
disk.try_write(
&k,
(
&slot_list
.iter()
.map(|(slot, info)| (*slot, (*info).into()))
.collect::<Vec<_>>(),
v.ref_count(),
),
)
};
match disk_resize {
Ok(_) => {
@ -1385,7 +1397,9 @@ struct EvictionsGuard<'a> {
impl<'a> EvictionsGuard<'a> {
#[must_use = "if unused, this evictions lock will be immediately unlocked"]
fn lock<T: IndexValue>(in_mem_accounts_index: &'a InMemAccountsIndex<T>) -> Self {
fn lock<T: IndexValue, U: IndexValue + From<T> + Into<T>>(
in_mem_accounts_index: &'a InMemAccountsIndex<T, U>,
) -> Self {
Self::lock_with(
&in_mem_accounts_index.stop_evictions,
&in_mem_accounts_index.stop_evictions_changes,
@ -1433,7 +1447,7 @@ mod tests {
itertools::Itertools,
};
fn new_for_test<T: IndexValue>() -> InMemAccountsIndex<T> {
fn new_for_test<T: IndexValue>() -> InMemAccountsIndex<T, T> {
let holder = Arc::new(BucketMapHolder::new(
BINS_FOR_TESTING,
&Some(AccountsIndexConfig::default()),
@ -1443,7 +1457,7 @@ mod tests {
InMemAccountsIndex::new(&holder, bin)
}
fn new_disk_buckets_for_test<T: IndexValue>() -> InMemAccountsIndex<T> {
fn new_disk_buckets_for_test<T: IndexValue>() -> InMemAccountsIndex<T, T> {
let holder = Arc::new(BucketMapHolder::new(
BINS_FOR_TESTING,
&Some(AccountsIndexConfig {
@ -1723,7 +1737,7 @@ mod tests {
let mut slot_list = Vec::default();
// upserting into empty slot_list, so always addref
assert!(
InMemAccountsIndex::update_slot_list(
InMemAccountsIndex::<u64, u64>::update_slot_list(
&mut slot_list,
new_slot,
info,
@ -1746,7 +1760,7 @@ mod tests {
// upserting into slot_list that does NOT contain an entry at 'new_slot'
// but, it DOES contain an entry at other_slot, so we do NOT add-ref. The assumption is that 'other_slot' is going away
// and that the previously held add-ref is now used by 'new_slot'
!InMemAccountsIndex::update_slot_list(
!InMemAccountsIndex::<u64, u64>::update_slot_list(
&mut slot_list,
new_slot,
info,
@ -1766,7 +1780,7 @@ mod tests {
// upserting into slot_list that already contain an entry at 'new-slot', so do NOT addref
let mut reclaims = Vec::default();
assert!(
!InMemAccountsIndex::update_slot_list(
!InMemAccountsIndex::<u64, u64>::update_slot_list(
&mut slot_list,
new_slot,
info,
@ -1837,7 +1851,7 @@ mod tests {
let original = slot_list.clone();
let mut reclaims = Vec::default();
let result = InMemAccountsIndex::update_slot_list(
let result = InMemAccountsIndex::<u64, u64>::update_slot_list(
&mut slot_list,
new_slot,
info,
@ -1965,7 +1979,7 @@ mod tests {
let info = 65;
let mut reclaims = Vec::default();
// first upsert, should increase
let len = InMemAccountsIndex::lock_and_update_slot_list(
let len = InMemAccountsIndex::<u64, u64>::lock_and_update_slot_list(
&test,
(1, info),
None,
@ -1975,7 +1989,7 @@ mod tests {
assert_eq!(test.slot_list.read().unwrap().len(), len);
assert_eq!(len, 1);
// update to different slot, should increase
let len = InMemAccountsIndex::lock_and_update_slot_list(
let len = InMemAccountsIndex::<u64, u64>::lock_and_update_slot_list(
&test,
(2, info),
None,
@ -1985,7 +1999,7 @@ mod tests {
assert_eq!(test.slot_list.read().unwrap().len(), len);
assert_eq!(len, 2);
// update to same slot, should not increase
let len = InMemAccountsIndex::lock_and_update_slot_list(
let len = InMemAccountsIndex::<u64, u64>::lock_and_update_slot_list(
&test,
(2, info),
None,