From e39626ab141e4d77ae98be660fa365e77685a33a Mon Sep 17 00:00:00 2001 From: "Jeff Washington (jwash)" Date: Thu, 23 Feb 2023 15:05:06 -0600 Subject: [PATCH] add second type to accounts index for disk (#30446) --- runtime/benches/accounts_index.rs | 2 +- runtime/src/accounts_db.rs | 4 +- runtime/src/accounts_index.rs | 190 +++++++++++++------------ runtime/src/accounts_index_storage.rs | 16 +-- runtime/src/bucket_map_holder.rs | 27 ++-- runtime/src/bucket_map_holder_stats.rs | 11 +- runtime/src/in_mem_accounts_index.rs | 62 ++++---- 7 files changed, 174 insertions(+), 138 deletions(-) diff --git a/runtime/benches/accounts_index.rs b/runtime/benches/accounts_index.rs index 4539f72f5f..b3b228e5c3 100644 --- a/runtime/benches/accounts_index.rs +++ b/runtime/benches/accounts_index.rs @@ -24,7 +24,7 @@ fn bench_accounts_index(bencher: &mut Bencher) { const NUM_FORKS: u64 = 16; let mut reclaims = vec![]; - let index = AccountsIndex::::new( + let index = AccountsIndex::::new( Some(ACCOUNTS_INDEX_CONFIG_FOR_BENCHMARKS), &Arc::default(), ); diff --git a/runtime/src/accounts_db.rs b/runtime/src/accounts_db.rs index 69ec907c0e..681bc07a7f 100644 --- a/runtime/src/accounts_db.rs +++ b/runtime/src/accounts_db.rs @@ -1344,7 +1344,7 @@ struct RemoveUnrootedSlotsSynchronization { signal: Condvar, } -type AccountInfoAccountsIndex = AccountsIndex; +type AccountInfoAccountsIndex = AccountsIndex; // 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( - index: &AccountsIndex, + index: &AccountsIndex, locked_account_entry: &ReadAccountMapEntry, max_inclusive: Option, ) -> (SlotList, RefCount) { diff --git a/runtime/src/accounts_index.rs b/runtime/src/accounts_index.rs index 3e7f719e6e..edc8909aac 100644 --- a/runtime/src/accounts_index.rs +++ b/runtime/src/accounts_index.rs @@ -70,7 +70,7 @@ pub type ScanResult = Result; pub type SlotList = Vec<(Slot, T)>; pub type SlotSlice<'s, T> = &'s [(Slot, T)]; pub type RefCount = u64; -pub type AccountMap = Arc>; +pub type AccountMap = Arc>; #[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(storage: &Arc>, is_cached: bool) -> Self { + pub fn new_dirty + Into>( + storage: &Arc>, + is_cached: bool, + ) -> Self { AccountMapEntryMeta { dirty: AtomicBool::new(true), age: AtomicU8::new(storage.future_age_to_flush(is_cached)), } } - pub fn new_clean(storage: &Arc>) -> Self { + pub fn new_clean + Into>( + storage: &Arc>, + ) -> Self { AccountMapEntryMeta { dirty: AtomicBool::new(false), age: AtomicU8::new(storage.future_age_to_flush(false)), @@ -397,10 +402,10 @@ impl PreAllocatedAccountMapEntry { /// 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 + Into>( slot: Slot, account_info: T, - storage: &Arc>, + storage: &Arc>, store_raw: bool, ) -> PreAllocatedAccountMapEntry { if store_raw { @@ -410,10 +415,10 @@ impl PreAllocatedAccountMapEntry { } } - fn allocate( + fn allocate + Into>( slot: Slot, account_info: T, - storage: &Arc>, + storage: &Arc>, ) -> AccountMapEntry { let is_cached = account_info.is_cached(); let ref_count = u64::from(!is_cached); @@ -425,7 +430,10 @@ impl PreAllocatedAccountMapEntry { )) } - pub fn into_account_map_entry(self, storage: &Arc>) -> AccountMapEntry { + pub fn into_account_map_entry + Into>( + self, + storage: &Arc>, + ) -> AccountMapEntry { 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, +pub struct AccountsIndexIterator<'a, T: IndexValue, U: IndexValue + From + Into> { + account_maps: &'a LockMapTypeSlice, bin_calculator: &'a PubkeyBinCalculator24, start_bound: Bound, end_bound: Bound, @@ -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 + Into> AccountsIndexIterator<'a, T, U> { fn range( - map: &AccountMaps, + map: &AccountMaps, range: R, collect_all_unsorted: bool, ) -> Vec<(Pubkey, AccountMapEntry)> @@ -556,7 +564,7 @@ impl<'a, T: IndexValue> AccountsIndexIterator<'a, T> { } pub fn new( - index: &'a AccountsIndex, + index: &'a AccountsIndex, 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 + Into> Iterator + for AccountsIndexIterator<'a, T, U> +{ type Item = Vec<(Pubkey, AccountMapEntry)>; fn next(&mut self) -> Option { if self.is_finished { @@ -634,10 +644,10 @@ pub trait ZeroLamport { fn is_zero_lamport(&self) -> bool; } -type MapType = AccountMap; -type LockMapType = Vec>; -type LockMapTypeSlice = [MapType]; -type AccountMaps<'a, T> = &'a MapType; +type MapType = AccountMap; +type LockMapType = Vec>; +type LockMapTypeSlice = [MapType]; +type AccountMaps<'a, T, U> = &'a MapType; #[derive(Debug, Default)] pub struct ScanSlotTracker { @@ -665,8 +675,10 @@ pub enum AccountsIndexScanResult { } #[derive(Debug)] -pub struct AccountsIndex { - pub account_maps: LockMapType, +/// T: account info type to interact in in-memory items +/// U: account info type to be persisted to disk +pub struct AccountsIndex + Into> { + pub account_maps: LockMapType, pub bin_calculator: PubkeyBinCalculator24, program_id_index: SecondaryIndex, spl_token_mint_index: SecondaryIndex, @@ -685,7 +697,7 @@ pub struct AccountsIndex { // scanning the fork with that Bank at the tip is no longer possible. pub removed_bank_ids: Mutex>, - storage: AccountsIndexStorage, + storage: AccountsIndexStorage, /// when a scan's accumulated data exceeds this limit, abort the scan pub scan_results_limit_bytes: Option, @@ -703,7 +715,7 @@ pub struct AccountsIndex { pub rent_paying_accounts_by_partition: OnceCell, } -impl AccountsIndex { +impl + Into> AccountsIndex { pub fn default_for_tests() -> Self { Self::new(Some(ACCOUNTS_INDEX_CONFIG_FOR_TESTING), &Arc::default()) } @@ -742,9 +754,9 @@ impl AccountsIndex { config: Option, exit: &Arc, ) -> ( - LockMapType, + LockMapType, PubkeyBinCalculator24, - AccountsIndexStorage, + AccountsIndexStorage, ) { let bins = config .as_ref() @@ -759,7 +771,7 @@ impl AccountsIndex { (account_maps, bin_calculator, storage) } - fn iter(&self, range: Option<&R>, collect_all_unsorted: bool) -> AccountsIndexIterator + fn iter(&self, range: Option<&R>, collect_all_unsorted: bool) -> AccountsIndexIterator where R: RangeBounds, { @@ -1121,7 +1133,7 @@ impl AccountsIndex { pub fn get_account_read_entry_with_lock( &self, pubkey: &Pubkey, - lock: &AccountMaps<'_, T>, + lock: &AccountMaps<'_, T, U>, ) -> Option> { lock.get(pubkey) .map(ReadAccountMapEntry::from_account_map_entry) @@ -1572,7 +1584,7 @@ impl AccountsIndex { ); } - pub(crate) fn get_bin(&self, pubkey: &Pubkey) -> AccountMaps { + pub(crate) fn get_bin(&self, pubkey: &Pubkey) -> AccountMaps { &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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::default_for_tests(); let _type_check = SecondaryIndexTypes::RwLock(&index.spl_token_owner_index); } @@ -2132,7 +2144,7 @@ pub mod tests { } } - impl AccountsIndex { + impl AccountsIndex { /// 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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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( slot: Slot, account_info: T, - storage: &Arc>, + storage: &Arc>, store_raw: bool, to_raw_first: bool, ) -> PreAllocatedAccountMapEntry { @@ -2562,7 +2574,7 @@ pub mod tests { let key0 = solana_sdk::pubkey::new_rand(); let key1 = solana_sdk::pubkey::new_rand(); - let index = AccountsIndex::::default_for_tests(); + let index = AccountsIndex::::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::::new(Some(config), &Arc::default()); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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, Vec) { - let index = AccountsIndex::::default_for_tests(); + fn setup_accounts_index_keys(num_pubkeys: usize) -> (AccountsIndex, Vec) { + let index = AccountsIndex::::default_for_tests(); let root_slot = 0; let mut pubkeys: Vec = std::iter::repeat_with(|| { @@ -2962,7 +2974,7 @@ pub mod tests { } fn run_test_range( - index: &AccountsIndex, + index: &AccountsIndex, pubkeys: &[Pubkey], start_bound: Bound, end_bound: Bound, @@ -3004,7 +3016,7 @@ pub mod tests { } fn run_test_range_indexes( - index: &AccountsIndex, + index: &AccountsIndex, pubkeys: &[Pubkey], start: Option, end: Option, @@ -3102,7 +3114,7 @@ pub mod tests { #[test] fn test_is_alive_root() { - let index = AccountsIndex::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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(index: &AccountsIndex) -> usize { + fn account_maps_stats_len(index: &AccountsIndex) -> usize { index.storage.storage.stats.total_count() } #[test] fn test_purge() { let key = solana_sdk::pubkey::new_rand(); - let index = AccountsIndex::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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, + index: &AccountsIndex, secondary_index: &SecondaryIndex, 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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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, + index: &AccountsIndex, secondary_index: &SecondaryIndex, 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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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, + index: &AccountsIndex, secondary_index: &SecondaryIndex, 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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::default_for_tests(); let iter = AccountsIndexIterator::new( &index, None::<&RangeInclusive>, @@ -3984,7 +3996,7 @@ pub mod tests { #[test] fn test_get_newest_root_in_slot_list() { - let index = AccountsIndex::::default_for_tests(); + let index = AccountsIndex::::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::::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::::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::::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::::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::::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::::get_newest_root_in_slot_list( &roots_tracker.alive_roots, &slot_list, Some(slot99), @@ -4053,7 +4065,7 @@ pub mod tests { } } - impl AccountsIndex { + impl AccountsIndex { 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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::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::::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::::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::::default_for_tests(); + let index = AccountsIndex::::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::::default_for_tests(); + let index = AccountsIndex::::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::::new(Some(config), &Arc::default()); + AccountsIndex::::new(Some(config), &Arc::default()); } #[test] diff --git a/runtime/src/accounts_index_storage.rs b/runtime/src/accounts_index_storage.rs index 22c92ab2ae..c3db58a16b 100644 --- a/runtime/src/accounts_index_storage.rs +++ b/runtime/src/accounts_index_storage.rs @@ -16,18 +16,18 @@ use { }; /// Manages the lifetime of the background processing threads. -pub struct AccountsIndexStorage { +pub struct AccountsIndexStorage + Into> { _bg_threads: BgThreads, - pub storage: Arc>, - pub in_mem: Vec>>, + pub storage: Arc>, + pub in_mem: Vec>>, exit: Arc, /// set_startup(true) creates bg threads which are kept alive until set_startup(false) startup_worker_threads: Mutex>, } -impl Debug for AccountsIndexStorage { +impl + Into> Debug for AccountsIndexStorage { fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Ok(()) } @@ -53,9 +53,9 @@ impl Drop for BgThreads { } impl BgThreads { - fn new( - storage: &Arc>, - in_mem: &[Arc>], + fn new + Into>( + storage: &Arc>, + in_mem: &[Arc>], threads: usize, can_advance_age: bool, exit: &Arc, @@ -109,7 +109,7 @@ pub enum Startup { StartupWithExtraThreads, } -impl AccountsIndexStorage { +impl + Into> AccountsIndexStorage { /// 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 diff --git a/runtime/src/bucket_map_holder.rs b/runtime/src/bucket_map_holder.rs index 703646f801..976180fa5b 100644 --- a/runtime/src/bucket_map_holder.rs +++ b/runtime/src/bucket_map_holder.rs @@ -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 = Some(10_000); -pub struct BucketMapHolder { - pub disk: Option>, +pub struct BucketMapHolder + Into> { + pub disk: Option>, pub count_buckets_flushed: AtomicUsize, @@ -66,16 +67,17 @@ pub struct BucketMapHolder { /// and writing to disk in parallel are. /// Note startup is an optimization and is not required for correctness. startup: AtomicBool, + _phantom: PhantomData, } -impl Debug for BucketMapHolder { +impl + Into> Debug for BucketMapHolder { fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Ok(()) } } #[allow(clippy::mutex_atomic)] -impl BucketMapHolder { +impl + Into> BucketMapHolder { /// 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 BucketMapHolder { startup: AtomicBool::default(), mem_budget_mb, threads, + _phantom: PhantomData::default(), } } @@ -328,7 +331,7 @@ impl BucketMapHolder { pub fn background( &self, exit: Vec>, - in_mem: Vec>>, + in_mem: Vec>>, 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::::new(bins, &Some(AccountsIndexConfig::default()), 1); + let test = BucketMapHolder::::new(bins, &Some(AccountsIndexConfig::default()), 1); let visited = (0..bins) .map(|_| AtomicUsize::default()) .collect::>(); @@ -425,7 +428,7 @@ pub mod tests { fn test_ages() { solana_logger::setup(); let bins = 4; - let test = BucketMapHolder::::new(bins, &Some(AccountsIndexConfig::default()), 1); + let test = BucketMapHolder::::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::::new(bins, &Some(AccountsIndexConfig::default()), 1); + let test = BucketMapHolder::::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::::new(bins, &Some(AccountsIndexConfig::default()), 1); + let test = BucketMapHolder::::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::::new(bins, &Some(config), 1); + let test = BucketMapHolder::::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::::new(bins, &Some(AccountsIndexConfig::default()), 1); + let test = BucketMapHolder::::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::::new(bins, &Some(AccountsIndexConfig::default()), 1); + let test = BucketMapHolder::::new(bins, &Some(AccountsIndexConfig::default()), 1); assert_eq!(test.current_age(), 0); for _ in 0..bins { assert!(!test.all_buckets_flushed_at_current_age()); diff --git a/runtime/src/bucket_map_holder_stats.rs b/runtime/src/bucket_map_holder_stats.rs index 1d706e9128..2d574558b6 100644 --- a/runtime/src/bucket_map_holder_stats.rs +++ b/runtime/src/bucket_map_holder_stats.rs @@ -99,7 +99,11 @@ impl BucketMapHolderStats { per_bucket.map(|stat| stat.fetch_sub(count, Ordering::Relaxed)); } - fn ms_per_age(&self, storage: &BucketMapHolder, elapsed_ms: u64) -> u64 { + fn ms_per_age + Into>( + &self, + storage: &BucketMapHolder, + 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(&self, storage: &BucketMapHolder) { + pub fn report_stats + Into>( + &self, + storage: &BucketMapHolder, + ) { let elapsed_ms = self.last_time.elapsed_ms(); if elapsed_ms < STATS_INTERVAL_MS { return; diff --git a/runtime/src/in_mem_accounts_index.rs b/runtime/src/in_mem_accounts_index.rs index 4fe043b825..cc04c11ca5 100644 --- a/runtime/src/in_mem_accounts_index.rs +++ b/runtime/src/in_mem_accounts_index.rs @@ -83,15 +83,15 @@ impl PossibleEvictions { } // one instance of this represents one bin of the accounts index. -pub struct InMemAccountsIndex { +pub struct InMemAccountsIndex + Into> { last_age_flushed: AtomicU8, // backing store map_internal: RwLock>, - storage: Arc>, + storage: Arc>, bin: usize, - bucket: Option>>, + bucket: Option>>, // 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 { age_to_flush_bin_mod: Age, } -impl Debug for InMemAccountsIndex { +impl + Into> Debug for InMemAccountsIndex { fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Ok(()) } @@ -141,8 +141,8 @@ struct FlushScanResult { evictions_random: Vec<(Pubkey, AccountMapEntry)>, } -impl InMemAccountsIndex { - pub fn new(storage: &Arc>, bin: usize) -> Self { +impl + Into> InMemAccountsIndex { + pub fn new(storage: &Arc>, 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 InMemAccountsIndex { keys } - fn load_from_disk(&self, pubkey: &Pubkey) -> Option<(SlotList, RefCount)> { + fn load_from_disk(&self, pubkey: &Pubkey) -> Option<(SlotList, 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 InMemAccountsIndex { // convert from raw data on disk to AccountMapEntry, set to age in future fn disk_to_cache_entry( &self, - slot_list: SlotList, + slot_list: SlotList, ref_count: RefCount, ) -> AccountMapEntry { 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 InMemAccountsIndex { Entry::Occupied(occupied) => { // in cache, so merge into cache let (slot, account_info) = new_entry.into(); - InMemAccountsIndex::lock_and_update_slot_list( + InMemAccountsIndex::::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 InMemAccountsIndex { 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::::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 InMemAccountsIndex { // 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 InMemAccountsIndex { 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 InMemAccountsIndex { 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::>(), + 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(in_mem_accounts_index: &'a InMemAccountsIndex) -> Self { + fn lock + Into>( + in_mem_accounts_index: &'a InMemAccountsIndex, + ) -> 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() -> InMemAccountsIndex { + fn new_for_test() -> InMemAccountsIndex { 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() -> InMemAccountsIndex { + fn new_disk_buckets_for_test() -> InMemAccountsIndex { 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::::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::::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::::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::::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::::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::::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::::lock_and_update_slot_list( &test, (2, info), None,