diff --git a/accounts-db/src/accounts.rs b/accounts-db/src/accounts.rs index c65e13a59cdc61..6afb3b7ce987bc 100644 --- a/accounts-db/src/accounts.rs +++ b/accounts-db/src/accounts.rs @@ -884,14 +884,6 @@ mod tests { assert_eq!(loaded, vec![]); } - #[test] - fn test_accounts_empty_bank_hash_stats() { - let accounts_db = AccountsDb::new_single_for_tests(); - let accounts = Accounts::new(Arc::new(accounts_db)); - assert!(accounts.accounts_db.get_bank_hash_stats(0).is_some()); - assert!(accounts.accounts_db.get_bank_hash_stats(1).is_none()); - } - #[test] fn test_lock_accounts_with_duplicates() { let accounts_db = AccountsDb::new_single_for_tests(); diff --git a/accounts-db/src/accounts_db.rs b/accounts-db/src/accounts_db.rs index a18e88c8b9ba7b..98d5f6f6c565dc 100644 --- a/accounts-db/src/accounts_db.rs +++ b/accounts-db/src/accounts_db.rs @@ -33,8 +33,8 @@ use { }, accounts_cache::{AccountsCache, CachedAccount, SlotCache}, accounts_db::stats::{ - AccountsStats, BankHashStats, CleanAccountsStats, FlushStats, PurgeStats, - ShrinkAncientStats, ShrinkStats, ShrinkStatsSub, StoreAccountsTiming, + AccountsStats, CleanAccountsStats, FlushStats, PurgeStats, ShrinkAncientStats, + ShrinkStats, ShrinkStatsSub, StoreAccountsTiming, }, accounts_file::{ AccountsFile, AccountsFileError, AccountsFileProvider, MatchAccountOwnerError, @@ -1533,7 +1533,6 @@ pub struct AccountsDb { pub thread_pool_hash: ThreadPool, - bank_hash_stats: Mutex>, accounts_delta_hashes: Mutex>, accounts_hashes: Mutex>, incremental_accounts_hashes: @@ -1979,8 +1978,6 @@ impl AccountsDb { Self::DEFAULT_MAX_READ_ONLY_CACHE_DATA_SIZE_HI, )); - let bank_hash_stats = Mutex::new(HashMap::from([(0, BankHashStats::default())])); - // Increase the stack for foreground threads // rayon needs a lot of stack const ACCOUNTS_STACK_SIZE: usize = 8 * 1024 * 1024; @@ -2049,7 +2046,6 @@ impl AccountsDb { .into(), verify_experimental_accumulator_hash: accounts_db_config .verify_experimental_accumulator_hash, - bank_hash_stats, thread_pool, thread_pool_clean, thread_pool_hash, @@ -4553,12 +4549,10 @@ impl AccountsDb { dropped_roots: impl Iterator, ) { let mut accounts_delta_hashes = self.accounts_delta_hashes.lock().unwrap(); - let mut bank_hash_stats = self.bank_hash_stats.lock().unwrap(); dropped_roots.for_each(|slot| { self.accounts_index.clean_dead_slot(slot); accounts_delta_hashes.remove(&slot); - bank_hash_stats.remove(&slot); // the storage has been removed from this slot and recycled or dropped assert!(self.storage.remove(&slot, false).is_none()); debug_assert!( @@ -4970,21 +4964,6 @@ impl AccountsDb { } } - /// Insert a default bank hash stats for `slot` - /// - /// This fn is called when creating a new bank from parent. - pub fn insert_default_bank_hash_stats(&self, slot: Slot, parent_slot: Slot) { - let mut bank_hash_stats = self.bank_hash_stats.lock().unwrap(); - if bank_hash_stats.get(&slot).is_some() { - error!( - "set_hash: already exists; multiple forks with shared slot {slot} as child \ - (parent: {parent_slot})!?" - ); - return; - } - bank_hash_stats.insert(slot, BankHashStats::default()); - } - pub fn load( &self, ancestors: &Ancestors, @@ -7651,30 +7630,6 @@ impl AccountsDb { .cloned() } - /// When reconstructing AccountsDb from a snapshot, insert the `bank_hash_stats` into the - /// internal bank hash stats map. - /// - /// This fn is only called when loading from a snapshot, which means AccountsDb is new and its - /// bank hash stats map is unpopulated. Except for slot 0. - /// - /// Slot 0 is a special case. When a new AccountsDb is created--like when loading from a - /// snapshot--the bank hash stats map is populated with a default entry at slot 0. Remove the - /// default entry at slot 0, and then insert the new value at `slot`. - pub fn update_bank_hash_stats_from_snapshot( - &mut self, - slot: Slot, - stats: BankHashStats, - ) -> Option { - let mut bank_hash_stats = self.bank_hash_stats.lock().unwrap(); - bank_hash_stats.remove(&0); - bank_hash_stats.insert(slot, stats) - } - - /// Get the bank hash stats for `slot` in the `bank_hash_stats` map - pub fn get_bank_hash_stats(&self, slot: Slot) -> Option { - self.bank_hash_stats.lock().unwrap().get(&slot).cloned() - } - fn update_index<'a>( &self, infos: Vec, @@ -7896,13 +7851,10 @@ impl AccountsDb { ); let mut accounts_delta_hashes = self.accounts_delta_hashes.lock().unwrap(); - let mut bank_hash_stats = self.bank_hash_stats.lock().unwrap(); for slot in dead_slots_iter { accounts_delta_hashes.remove(slot); - bank_hash_stats.remove(slot); } drop(accounts_delta_hashes); - drop(bank_hash_stats); measure.stop(); inc_new_counter_info!("remove_dead_slots_metadata-ms", measure.as_ms() as usize); @@ -8132,12 +8084,10 @@ impl AccountsDb { return; } - let mut stats = BankHashStats::default(); let mut total_data = 0; (0..accounts.len()).for_each(|index| { accounts.account(index, |account| { total_data += account.data().len(); - stats.update(&account); }) }); @@ -8145,16 +8095,6 @@ impl AccountsDb { .store_total_data .fetch_add(total_data as u64, Ordering::Relaxed); - { - // we need to drop the bank_hash_stats lock to prevent deadlocks - self.bank_hash_stats - .lock() - .unwrap() - .entry(accounts.target_slot()) - .or_default() - .accumulate(&stats); - } - // we use default hashes for now since the same account may be stored to the cache multiple times self.store_accounts_unfrozen( accounts, diff --git a/accounts-db/src/accounts_db/stats.rs b/accounts-db/src/accounts_db/stats.rs index f5d80b19bcc226..d33ae9c1f63658 100644 --- a/accounts-db/src/accounts_db/stats.rs +++ b/accounts-db/src/accounts_db/stats.rs @@ -1,56 +1,18 @@ use { crate::{ - accounts_index::{AccountsIndexRootsStats, ZeroLamport}, + accounts_index::AccountsIndexRootsStats, append_vec::{ APPEND_VEC_MMAPPED_FILES_DIRTY, APPEND_VEC_MMAPPED_FILES_OPEN, APPEND_VEC_OPEN_AS_FILE_IO, }, }, - serde::{Deserialize, Serialize}, - solana_sdk::{account::ReadableAccount, timing::AtomicInterval}, + solana_sdk::timing::AtomicInterval, std::{ num::Saturating, sync::atomic::{AtomicU64, AtomicUsize, Ordering}, }, }; -#[cfg_attr(feature = "frozen-abi", derive(AbiExample))] -#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct BankHashStats { - pub num_updated_accounts: u64, - pub num_removed_accounts: u64, - pub num_lamports_stored: u64, - pub total_data_len: u64, - pub num_executable_accounts: u64, -} - -impl BankHashStats { - pub fn update(&mut self, account: &T) { - if account.is_zero_lamport() { - self.num_removed_accounts += 1; - } else { - self.num_updated_accounts += 1; - } - self.total_data_len = self - .total_data_len - .wrapping_add(account.data().len() as u64); - if account.executable() { - self.num_executable_accounts += 1; - } - self.num_lamports_stored = self.num_lamports_stored.wrapping_add(account.lamports()); - } - - pub fn accumulate(&mut self, other: &BankHashStats) { - self.num_updated_accounts += other.num_updated_accounts; - self.num_removed_accounts += other.num_removed_accounts; - self.total_data_len = self.total_data_len.wrapping_add(other.total_data_len); - self.num_lamports_stored = self - .num_lamports_stored - .wrapping_add(other.num_lamports_stored); - self.num_executable_accounts += other.num_executable_accounts; - } -} - #[derive(Debug, Default)] pub struct AccountsStats { pub delta_hash_scan_time_total_us: AtomicU64, diff --git a/accounts-db/src/accounts_db/tests.rs b/accounts-db/src/accounts_db/tests.rs index 1cf6a5a3d02bb2..82e1cf640c7a55 100644 --- a/accounts-db/src/accounts_db/tests.rs +++ b/accounts-db/src/accounts_db/tests.rs @@ -1089,14 +1089,12 @@ fn run_test_remove_unrooted_slot(is_cached: bool, db: AccountsDb) { } else { db.store_for_tests(unrooted_slot, &[(&key, &account0)]); } - assert!(db.get_bank_hash_stats(unrooted_slot).is_some()); assert!(db.accounts_index.contains(&key)); db.assert_load_account(unrooted_slot, key, 1); // Purge the slot db.remove_unrooted_slots(&[(unrooted_slot, unrooted_bank_id)]); assert!(db.load_without_fixed_root(&ancestors, &key).is_none()); - assert!(db.get_bank_hash_stats(unrooted_slot).is_none()); assert!(db.accounts_cache.slot_cache(unrooted_slot).is_none()); assert!(db.storage.get_slot_storage_entry(unrooted_slot).is_none()); assert!(!db.accounts_index.contains(&key)); @@ -2394,32 +2392,6 @@ fn test_hash_stored_account() { ); } -#[test] -fn test_bank_hash_stats() { - solana_logger::setup(); - let db = AccountsDb::new_single_for_tests(); - - let key = Pubkey::default(); - let some_data_len = 5; - let some_slot: Slot = 0; - let account = AccountSharedData::new(1, some_data_len, &key); - let ancestors = vec![(some_slot, 0)].into_iter().collect(); - - db.store_for_tests(some_slot, &[(&key, &account)]); - let mut account = db.load_without_fixed_root(&ancestors, &key).unwrap().0; - account.checked_sub_lamports(1).unwrap(); - account.set_executable(true); - db.store_for_tests(some_slot, &[(&key, &account)]); - db.add_root(some_slot); - - let stats = db.get_bank_hash_stats(some_slot).unwrap(); - assert_eq!(stats.num_updated_accounts, 1); - assert_eq!(stats.num_removed_accounts, 1); - assert_eq!(stats.num_lamports_stored, 1); - assert_eq!(stats.total_data_len, 2 * some_data_len as u64); - assert_eq!(stats.num_executable_accounts, 1); -} - // something we can get a ref to lazy_static! { pub static ref EPOCH_SCHEDULE: EpochSchedule = EpochSchedule::default(); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index f03a983e903196..f67c737181442e 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -467,6 +467,7 @@ pub struct BankFieldsToDeserialize { pub(crate) epoch_accounts_hash: Option, // When removing the accounts lt hash featurization code, also remove this Option wrapper pub(crate) accounts_lt_hash: Option, + pub(crate) bank_hash_stats: BankHashStats, } /// Bank's common fields shared by all supported snapshot versions for serialization. @@ -591,6 +592,7 @@ impl PartialEq for Bank { cache_for_accounts_lt_hash: _, stats_for_accounts_lt_hash: _, block_id, + bank_hash_stats: _, // Ignore new fields explicitly if they do not impact PartialEq. // Adding ".." will remove compile-time checks that if a new field // is added to the struct, this PartialEq is accordingly updated. @@ -947,6 +949,9 @@ pub struct Bank { /// None for banks that have not yet completed replay or for leader banks as we cannot populate block_id /// until bankless leader. Can be computed directly from shreds without needing to execute transactions. block_id: RwLock>, + + /// Accounts stats for computing the bank hash + bank_hash_stats: AtomicBankHashStats, } struct VoteWithStakeDelegations { @@ -1011,6 +1016,87 @@ pub struct ProcessedTransactionCounts { pub signature_count: u64, } +/// Account stats for computing the bank hash +/// This struct is serialized and stored in the snapshot. +#[cfg_attr(feature = "frozen-abi", derive(AbiExample))] +#[derive(Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub struct BankHashStats { + pub num_updated_accounts: u64, + pub num_removed_accounts: u64, + pub num_lamports_stored: u64, + pub total_data_len: u64, + pub num_executable_accounts: u64, +} + +impl BankHashStats { + pub fn update(&mut self, account: &T) { + if account.lamports() == 0 { + self.num_removed_accounts += 1; + } else { + self.num_updated_accounts += 1; + } + self.total_data_len = self + .total_data_len + .wrapping_add(account.data().len() as u64); + if account.executable() { + self.num_executable_accounts += 1; + } + self.num_lamports_stored = self.num_lamports_stored.wrapping_add(account.lamports()); + } + pub fn accumulate(&mut self, other: &BankHashStats) { + self.num_updated_accounts += other.num_updated_accounts; + self.num_removed_accounts += other.num_removed_accounts; + self.total_data_len = self.total_data_len.wrapping_add(other.total_data_len); + self.num_lamports_stored = self + .num_lamports_stored + .wrapping_add(other.num_lamports_stored); + self.num_executable_accounts += other.num_executable_accounts; + } +} + +#[derive(Debug, Default)] +pub struct AtomicBankHashStats { + pub num_updated_accounts: AtomicU64, + pub num_removed_accounts: AtomicU64, + pub num_lamports_stored: AtomicU64, + pub total_data_len: AtomicU64, + pub num_executable_accounts: AtomicU64, +} + +impl AtomicBankHashStats { + pub fn new(stat: &BankHashStats) -> Self { + AtomicBankHashStats { + num_updated_accounts: AtomicU64::new(stat.num_updated_accounts), + num_removed_accounts: AtomicU64::new(stat.num_removed_accounts), + num_lamports_stored: AtomicU64::new(stat.num_lamports_stored), + total_data_len: AtomicU64::new(stat.total_data_len), + num_executable_accounts: AtomicU64::new(stat.num_executable_accounts), + } + } + + pub fn accumulate(&self, other: &BankHashStats) { + self.num_updated_accounts + .fetch_add(other.num_updated_accounts, Relaxed); + self.num_removed_accounts + .fetch_add(other.num_removed_accounts, Relaxed); + self.total_data_len.fetch_add(other.total_data_len, Relaxed); + self.num_lamports_stored + .fetch_add(other.num_lamports_stored, Relaxed); + self.num_executable_accounts + .fetch_add(other.num_executable_accounts, Relaxed); + } + + pub fn load(&self) -> BankHashStats { + BankHashStats { + num_updated_accounts: self.num_updated_accounts.load(Relaxed), + num_removed_accounts: self.num_removed_accounts.load(Relaxed), + num_lamports_stored: self.num_lamports_stored.load(Relaxed), + total_data_len: self.total_data_len.load(Relaxed), + num_executable_accounts: self.num_executable_accounts.load(Relaxed), + } + } +} + impl Bank { fn default_with_accounts(accounts: Accounts) -> Self { let mut bank = Self { @@ -1081,6 +1167,7 @@ impl Bank { cache_for_accounts_lt_hash: RwLock::new(AHashMap::new()), stats_for_accounts_lt_hash: AccountsLtHashStats::default(), block_id: RwLock::new(None), + bank_hash_stats: AtomicBankHashStats::default(), }; bank.transaction_processor = @@ -1211,7 +1298,6 @@ impl Bank { let (rc, bank_rc_creation_time_us) = measure_us!({ let accounts_db = Arc::clone(&parent.rc.accounts.accounts_db); - accounts_db.insert_default_bank_hash_stats(slot, parent.slot()); BankRc { accounts: Arc::new(Accounts::new(accounts_db)), parent: RwLock::new(Some(Arc::clone(&parent))), @@ -1337,6 +1423,7 @@ impl Bank { cache_for_accounts_lt_hash: RwLock::new(AHashMap::new()), stats_for_accounts_lt_hash: AccountsLtHashStats::default(), block_id: RwLock::new(None), + bank_hash_stats: AtomicBankHashStats::default(), }; let (_, ancestors_time_us) = measure_us!({ @@ -1749,6 +1836,7 @@ impl Bank { cache_for_accounts_lt_hash: RwLock::new(AHashMap::new()), stats_for_accounts_lt_hash: AccountsLtHashStats::default(), block_id: RwLock::new(None), + bank_hash_stats: AtomicBankHashStats::new(&fields.bank_hash_stats), }; bank.transaction_processor = @@ -2786,9 +2874,11 @@ impl Bank { ); assert!(!self.freeze_started()); thread_pool.install(|| { - stake_rewards - .par_chunks(512) - .for_each(|chunk| self.rc.accounts.store_accounts_cached((slot, chunk))) + stake_rewards.par_chunks(512).for_each(|chunk| { + let to_store = (slot, chunk); + self.update_bank_hash_stats(&to_store); + self.rc.accounts.store_accounts_cached(to_store); + }) }); metrics .store_stake_accounts_us @@ -4065,6 +4155,16 @@ impl Bank { .accumulate(&accumulated_fee_details); } + fn update_bank_hash_stats<'a>(&self, accounts: &impl StorableAccounts<'a>) { + let mut stats = BankHashStats::default(); + (0..accounts.len()).for_each(|i| { + accounts.account(i, |account| { + stats.update(&account); + }) + }); + self.bank_hash_stats.accumulate(&stats); + } + pub fn commit_transactions( &self, sanitized_txs: &[impl TransactionWithMeta], @@ -4122,10 +4222,12 @@ impl Bank { &maybe_transaction_refs, &processing_results, ); - self.rc.accounts.store_cached( - (self.slot(), accounts_to_store.as_slice()), - transactions.as_deref(), - ); + + let to_store = (self.slot(), accounts_to_store.as_slice()); + self.update_bank_hash_stats(&to_store); + self.rc + .accounts + .store_cached(to_store, transactions.as_deref()); }); self.collect_rent(&processing_results); @@ -5092,6 +5194,7 @@ impl Bank { assert!(!self.freeze_started()); let mut m = Measure::start("stakes_cache.check_and_store"); let new_warmup_cooldown_rate_epoch = self.new_warmup_cooldown_rate_epoch(); + (0..accounts.len()).for_each(|i| { accounts.account(i, |account| { self.stakes_cache.check_and_store( @@ -5101,6 +5204,7 @@ impl Bank { ) }) }); + self.update_bank_hash_stats(&accounts); self.rc.accounts.store_accounts_cached(accounts); m.stop(); self.rc @@ -5626,12 +5730,7 @@ impl Bank { #[cfg(feature = "dev-context-only-utils")] let hash = hash_override.unwrap_or(std::hint::black_box(hash)); - let bank_hash_stats = self - .rc - .accounts - .accounts_db - .get_bank_hash_stats(slot) - .expect("No bank hash stats were found for this bank, that should not be possible"); + let bank_hash_stats = self.bank_hash_stats.load(); let total_us = measure_total.end_as_us(); datapoint_info!( @@ -7112,6 +7211,10 @@ impl Bank { self.transaction_processor .add_builtin(self, program_id, name, builtin) } + + pub fn get_bank_hash_stats(&self) -> BankHashStats { + self.bank_hash_stats.load() + } } impl TransactionProcessingCallback for Bank { diff --git a/runtime/src/bank/serde_snapshot.rs b/runtime/src/bank/serde_snapshot.rs index 9e44ee2951caf6..01bef524483442 100644 --- a/runtime/src/bank/serde_snapshot.rs +++ b/runtime/src/bank/serde_snapshot.rs @@ -222,7 +222,7 @@ mod tests { serde_snapshot::serialize_bank_snapshot_into( &mut writer, bank_fields, - accounts_db.get_bank_hash_stats(bank2_slot).unwrap(), + bank2.get_bank_hash_stats(), accounts_db.get_accounts_delta_hash(bank2_slot).unwrap(), expected_accounts_hash, &get_storages_to_serialize(&bank2.get_snapshot_storages(None)), @@ -306,7 +306,7 @@ mod tests { .clone()), expected_accounts_lt_hash, ); - + assert_eq!(dbank.get_bank_hash_stats(), bank2.get_bank_hash_stats()); assert_eq!(dbank, bank2); } } @@ -539,9 +539,9 @@ mod tests { mod test_bank_serialize { use { super::*, + crate::bank::BankHashStats, solana_accounts_db::{ - account_storage::meta::StoredMetaWriteVersion, accounts_db::stats::BankHashStats, - accounts_hash::AccountsLtHash, + account_storage::meta::StoredMetaWriteVersion, accounts_hash::AccountsLtHash, }, solana_frozen_abi::abi_example::AbiExample, solana_lattice_hash::lt_hash::LtHash, @@ -555,7 +555,7 @@ mod tests { // snapshot storages as well. // // It was avoided to impl AbiExample for Bank by wrapping it around PhantomData inside the - // spcecial wrapper called BankAbiTestWrapper. And internally, it creates an actual bank + // special wrapper called BankAbiTestWrapper. And internally, it creates an actual bank // from Bank::default_for_tests(). // // In this way, frozen abi can increase the coverage of the serialization code path as much @@ -570,7 +570,7 @@ mod tests { #[cfg_attr( feature = "frozen-abi", derive(AbiExample), - frozen_abi(digest = "9THbM4acSoJYENSTHp1qBdXyvPr3qUQTSTkotUNkFc76") + frozen_abi(digest = "2bWtYJSWVVvCEnBw6W2PsYZaR7RVs2V7CthFcHArdbUR") )] #[derive(Serialize)] pub struct BankAbiTestWrapper { diff --git a/runtime/src/serde_snapshot.rs b/runtime/src/serde_snapshot.rs index 841408e62e73c5..7a31a73efd30f2 100644 --- a/runtime/src/serde_snapshot.rs +++ b/runtime/src/serde_snapshot.rs @@ -4,7 +4,7 @@ use { crate::{ bank::{ builtins::BuiltinPrototype, Bank, BankFieldsToDeserialize, BankFieldsToSerialize, - BankRc, + BankHashStats, BankRc, }, epoch_stakes::{EpochStakes, VersionedEpochStakes}, runtime_config::RuntimeConfig, @@ -19,8 +19,8 @@ use { account_storage::meta::StoredMetaWriteVersion, accounts::Accounts, accounts_db::{ - stats::BankHashStats, AccountStorageEntry, AccountsDb, AccountsDbConfig, - AccountsFileId, AtomicAccountsFileId, DuplicatesLtHash, IndexGenerationInfo, + AccountStorageEntry, AccountsDb, AccountsDbConfig, AccountsFileId, + AtomicAccountsFileId, DuplicatesLtHash, IndexGenerationInfo, }, accounts_file::{AccountsFile, StorageAccess}, accounts_hash::{AccountsDeltaHash, AccountsHash}, @@ -203,6 +203,7 @@ impl From for BankFieldsToDeserialize { incremental_snapshot_persistence: None, epoch_accounts_hash: None, accounts_lt_hash: None, // populated from ExtraFieldsToDeserialize + bank_hash_stats: BankHashStats::default(), // populated from AccountsDbFields } } } @@ -703,7 +704,7 @@ impl<'a> Serialize for SerializableBankAndStorage<'a> { let slot = self.bank.slot(); let mut bank_fields = self.bank.get_fields_to_serialize(); let accounts_db = &self.bank.rc.accounts.accounts_db; - let bank_hash_stats = accounts_db.get_bank_hash_stats(slot).unwrap(); + let bank_hash_stats = self.bank.get_bank_hash_stats(); let accounts_delta_hash = accounts_db.get_accounts_delta_hash(slot).unwrap(); let accounts_hash = accounts_db.get_accounts_hash(slot).unwrap().0; let write_version = accounts_db.write_version.load(Ordering::Acquire); @@ -747,7 +748,7 @@ impl<'a> Serialize for SerializableBankAndStorageNoExtra<'a> { let slot = self.bank.slot(); let bank_fields = self.bank.get_fields_to_serialize(); let accounts_db = &self.bank.rc.accounts.accounts_db; - let bank_hash_stats = accounts_db.get_bank_hash_stats(slot).unwrap(); + let bank_hash_stats = self.bank.get_bank_hash_stats(); let accounts_delta_hash = accounts_db.get_accounts_delta_hash(slot).unwrap(); let accounts_hash = accounts_db.get_accounts_hash(slot).unwrap().0; let write_version = accounts_db.write_version.load(Ordering::Acquire); @@ -870,7 +871,7 @@ where .as_ref() .map(|bank_fields| bank_fields.capitalization), ); - let bank_fields = bank_fields.collapse_into(); + let mut bank_fields = bank_fields.collapse_into(); let (accounts_db, reconstructed_accounts_db_info) = reconstruct_accountsdb_from_fields( snapshot_accounts_db_fields, account_paths, @@ -886,6 +887,7 @@ where bank_fields.incremental_snapshot_persistence.as_ref(), bank_fields.accounts_lt_hash.is_some(), )?; + bank_fields.bank_hash_stats = reconstructed_accounts_db_info.bank_hash_stats; let bank_rc = BankRc::new(Accounts::new(Arc::new(accounts_db))); let runtime_config = Arc::new(runtime_config.clone()); @@ -904,7 +906,6 @@ where ); info!("rent_collector: {:?}", bank.rent_collector()); - Ok(( bank, ReconstructedBankInfo { @@ -1034,6 +1035,7 @@ pub(crate) fn remap_and_reconstruct_single_storage( pub struct ReconstructedAccountsDbInfo { pub accounts_data_len: u64, pub duplicates_lt_hash: Option>, + pub bank_hash_stats: BankHashStats, } #[allow(clippy::too_many_arguments)] @@ -1209,12 +1211,6 @@ where old_accounts_delta_hash.is_none(), "There should not already be an AccountsDeltaHash at slot {snapshot_slot}: {old_accounts_delta_hash:?}", ); - let old_stats = accounts_db - .update_bank_hash_stats_from_snapshot(snapshot_slot, snapshot_bank_hash_info.stats); - assert!( - old_stats.is_none(), - "There should not already be a BankHashStats at slot {snapshot_slot}: {old_stats:?}", - ); accounts_db.storage.initialize(storage); accounts_db .next_id @@ -1268,6 +1264,7 @@ where ReconstructedAccountsDbInfo { accounts_data_len, duplicates_lt_hash, + bank_hash_stats: snapshot_bank_hash_info.stats, }, )) } diff --git a/runtime/src/serde_snapshot/tests.rs b/runtime/src/serde_snapshot/tests.rs index 9fcf3f81818103..a5cafd4a9dd0b9 100644 --- a/runtime/src/serde_snapshot/tests.rs +++ b/runtime/src/serde_snapshot/tests.rs @@ -2,6 +2,7 @@ mod serde_snapshot_tests { use { crate::{ + bank::BankHashStats, serde_snapshot::{ deserialize_accounts_db_fields, reconstruct_accountsdb_from_fields, remap_append_vec_file, SerializableAccountsDb, SnapshotAccountsDbFields, @@ -108,7 +109,7 @@ mod serde_snapshot_tests { where W: Write, { - let bank_hash_stats = accounts_db.get_bank_hash_stats(slot).unwrap(); + let bank_hash_stats = BankHashStats::default(); let accounts_delta_hash = accounts_db.get_accounts_delta_hash(slot).unwrap(); let accounts_hash = accounts_db.get_accounts_hash(slot).unwrap().0; let write_version = accounts_db.write_version.load(Ordering::Acquire); diff --git a/runtime/src/snapshot_package.rs b/runtime/src/snapshot_package.rs index ee8a3fb861e40f..943c5e9be8c69c 100644 --- a/runtime/src/snapshot_package.rs +++ b/runtime/src/snapshot_package.rs @@ -1,6 +1,6 @@ use { crate::{ - bank::{Bank, BankFieldsToSerialize, BankSlotDelta}, + bank::{Bank, BankFieldsToSerialize, BankHashStats, BankSlotDelta}, serde_snapshot::BankIncrementalSnapshotPersistence, snapshot_hash::SnapshotHash, }, @@ -8,7 +8,7 @@ use { solana_accounts_db::{ account_storage::meta::StoredMetaWriteVersion, accounts::Accounts, - accounts_db::{stats::BankHashStats, AccountStorageEntry}, + accounts_db::AccountStorageEntry, accounts_hash::{AccountsDeltaHash, AccountsHash, AccountsHashKind}, epoch_accounts_hash::EpochAccountsHash, }, @@ -78,8 +78,7 @@ impl AccountsPackage { // Since we only snapshot rooted slots, and we know rooted slots must be frozen, // that guarantees this slot will have an accounts delta hash. let accounts_delta_hash = accounts_db.get_accounts_delta_hash(slot).unwrap(); - // SAFETY: Every slot *must* have a BankHashStats entry in AccountsDb. - let bank_hash_stats = accounts_db.get_bank_hash_stats(slot).unwrap(); + let bank_hash_stats = bank.get_bank_hash_stats(); let bank_fields_to_serialize = bank.get_fields_to_serialize(); SupplementalSnapshotInfo { status_cache_slot_deltas, diff --git a/runtime/src/snapshot_utils.rs b/runtime/src/snapshot_utils.rs index 57c98e4f77307d..bc59d3fc5f5ce8 100644 --- a/runtime/src/snapshot_utils.rs +++ b/runtime/src/snapshot_utils.rs @@ -1,6 +1,6 @@ use { crate::{ - bank::{BankFieldsToSerialize, BankSlotDelta}, + bank::{BankFieldsToSerialize, BankHashStats, BankSlotDelta}, serde_snapshot::{ self, BankIncrementalSnapshotPersistence, ExtraFieldsToSerialize, SnapshotStreams, }, @@ -24,7 +24,7 @@ use { regex::Regex, solana_accounts_db::{ account_storage::{meta::StoredMetaWriteVersion, AccountStorageMap}, - accounts_db::{stats::BankHashStats, AccountStorageEntry, AtomicAccountsFileId}, + accounts_db::{AccountStorageEntry, AtomicAccountsFileId}, accounts_file::{AccountsFile, AccountsFileError, InternalsForArchive, StorageAccess}, accounts_hash::{AccountsDeltaHash, AccountsHash}, epoch_accounts_hash::EpochAccountsHash,