add filler accounts to bloat validator and predict failure (#20491)
* add filler accounts to bloat validator and predict failure * assert no accounts match filler * cleanup magic numbers * panic if can't load from snapshot with filler accounts specified * some renames * renames * into_par_iter * clean filler accts, too
This commit is contained in:
committed by
GitHub
parent
61ba8d1ecf
commit
a8e000a2a6
@ -134,6 +134,7 @@ impl AccountsHashVerifier {
|
|||||||
HashStats::default(),
|
HashStats::default(),
|
||||||
false,
|
false,
|
||||||
None,
|
None,
|
||||||
|
None, // this will fail with filler accounts
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -893,6 +893,12 @@ fn main() {
|
|||||||
.validator(is_parsable::<usize>)
|
.validator(is_parsable::<usize>)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.help("How much memory the accounts index can consume. If this is exceeded, some account index entries will be stored on disk. If missing, the entire index is stored in memory.");
|
.help("How much memory the accounts index can consume. If this is exceeded, some account index entries will be stored on disk. If missing, the entire index is stored in memory.");
|
||||||
|
let accounts_filler_count = Arg::with_name("accounts_filler_count")
|
||||||
|
.long("accounts-filler-count")
|
||||||
|
.value_name("COUNT")
|
||||||
|
.validator(is_parsable::<usize>)
|
||||||
|
.takes_value(true)
|
||||||
|
.help("How many accounts to add to stress the system. Accounts are ignored in operations related to correctness.");
|
||||||
let account_paths_arg = Arg::with_name("account_paths")
|
let account_paths_arg = Arg::with_name("account_paths")
|
||||||
.long("accounts")
|
.long("accounts")
|
||||||
.value_name("PATHS")
|
.value_name("PATHS")
|
||||||
@ -1217,6 +1223,7 @@ fn main() {
|
|||||||
.arg(&limit_load_slot_count_from_snapshot_arg)
|
.arg(&limit_load_slot_count_from_snapshot_arg)
|
||||||
.arg(&accounts_index_bins)
|
.arg(&accounts_index_bins)
|
||||||
.arg(&accounts_index_limit)
|
.arg(&accounts_index_limit)
|
||||||
|
.arg(&accounts_filler_count)
|
||||||
.arg(&verify_index_arg)
|
.arg(&verify_index_arg)
|
||||||
.arg(&hard_forks_arg)
|
.arg(&hard_forks_arg)
|
||||||
.arg(&no_accounts_db_caching_arg)
|
.arg(&no_accounts_db_caching_arg)
|
||||||
@ -1961,9 +1968,12 @@ fn main() {
|
|||||||
accounts_index_config.drives = Some(accounts_index_paths);
|
accounts_index_config.drives = Some(accounts_index_paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let filler_account_count = value_t!(arg_matches, "accounts_filler_count", usize).ok();
|
||||||
|
|
||||||
let accounts_db_config = Some(AccountsDbConfig {
|
let accounts_db_config = Some(AccountsDbConfig {
|
||||||
index: Some(accounts_index_config),
|
index: Some(accounts_index_config),
|
||||||
accounts_hash_cache_path: Some(ledger_path.clone()),
|
accounts_hash_cache_path: Some(ledger_path.clone()),
|
||||||
|
filler_account_count,
|
||||||
});
|
});
|
||||||
|
|
||||||
let process_options = ProcessOptions {
|
let process_options = ProcessOptions {
|
||||||
|
@ -97,6 +97,16 @@ pub fn load(
|
|||||||
info!("Snapshots disabled; will load from genesis");
|
info!("Snapshots disabled; will load from genesis");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if process_options
|
||||||
|
.accounts_db_config
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|config| config.filler_account_count)
|
||||||
|
.unwrap_or_default()
|
||||||
|
> 0
|
||||||
|
{
|
||||||
|
panic!("filler accounts specified, but not loading from snapshot");
|
||||||
|
}
|
||||||
|
|
||||||
load_from_genesis(
|
load_from_genesis(
|
||||||
genesis_config,
|
genesis_config,
|
||||||
blockstore,
|
blockstore,
|
||||||
|
@ -273,6 +273,7 @@ impl Accounts {
|
|||||||
key,
|
key,
|
||||||
&mut account,
|
&mut account,
|
||||||
rent_for_sysvars,
|
rent_for_sysvars,
|
||||||
|
self.accounts_db.filler_account_suffix.as_ref(),
|
||||||
);
|
);
|
||||||
(account, rent_due)
|
(account, rent_due)
|
||||||
} else {
|
} else {
|
||||||
|
@ -52,7 +52,7 @@ use solana_measure::measure::Measure;
|
|||||||
use solana_rayon_threadlimit::get_thread_count;
|
use solana_rayon_threadlimit::get_thread_count;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::{AccountSharedData, ReadableAccount},
|
account::{AccountSharedData, ReadableAccount},
|
||||||
clock::{BankId, Epoch, Slot},
|
clock::{BankId, Epoch, Slot, SlotCount},
|
||||||
genesis_config::ClusterType,
|
genesis_config::ClusterType,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
@ -68,6 +68,7 @@ use std::{
|
|||||||
io::{Error as IoError, Result as IoResult},
|
io::{Error as IoError, Result as IoResult},
|
||||||
ops::{Range, RangeBounds},
|
ops::{Range, RangeBounds},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
str::FromStr,
|
||||||
sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering},
|
sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering},
|
||||||
sync::{Arc, Condvar, Mutex, MutexGuard, RwLock},
|
sync::{Arc, Condvar, Mutex, MutexGuard, RwLock},
|
||||||
thread::Builder,
|
thread::Builder,
|
||||||
@ -129,10 +130,12 @@ const CACHE_VIRTUAL_STORED_SIZE: usize = 0;
|
|||||||
pub const ACCOUNTS_DB_CONFIG_FOR_TESTING: AccountsDbConfig = AccountsDbConfig {
|
pub const ACCOUNTS_DB_CONFIG_FOR_TESTING: AccountsDbConfig = AccountsDbConfig {
|
||||||
index: Some(ACCOUNTS_INDEX_CONFIG_FOR_TESTING),
|
index: Some(ACCOUNTS_INDEX_CONFIG_FOR_TESTING),
|
||||||
accounts_hash_cache_path: None,
|
accounts_hash_cache_path: None,
|
||||||
|
filler_account_count: None,
|
||||||
};
|
};
|
||||||
pub const ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS: AccountsDbConfig = AccountsDbConfig {
|
pub const ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS: AccountsDbConfig = AccountsDbConfig {
|
||||||
index: Some(ACCOUNTS_INDEX_CONFIG_FOR_BENCHMARKS),
|
index: Some(ACCOUNTS_INDEX_CONFIG_FOR_BENCHMARKS),
|
||||||
accounts_hash_cache_path: None,
|
accounts_hash_cache_path: None,
|
||||||
|
filler_account_count: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type BinnedHashData = Vec<Vec<CalculateHashIntermediate>>;
|
pub type BinnedHashData = Vec<Vec<CalculateHashIntermediate>>;
|
||||||
@ -141,6 +144,7 @@ pub type BinnedHashData = Vec<Vec<CalculateHashIntermediate>>;
|
|||||||
pub struct AccountsDbConfig {
|
pub struct AccountsDbConfig {
|
||||||
pub index: Option<AccountsIndexConfig>,
|
pub index: Option<AccountsIndexConfig>,
|
||||||
pub accounts_hash_cache_path: Option<PathBuf>,
|
pub accounts_hash_cache_path: Option<PathBuf>,
|
||||||
|
pub filler_account_count: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FoundStoredAccount<'a> {
|
struct FoundStoredAccount<'a> {
|
||||||
@ -1044,6 +1048,9 @@ pub struct AccountsDb {
|
|||||||
|
|
||||||
/// AccountsDbPlugin accounts update notifier
|
/// AccountsDbPlugin accounts update notifier
|
||||||
accounts_update_notifier: Option<AccountsUpdateNotifier>,
|
accounts_update_notifier: Option<AccountsUpdateNotifier>,
|
||||||
|
|
||||||
|
filler_account_count: usize,
|
||||||
|
pub filler_account_suffix: Option<Pubkey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@ -1536,6 +1543,8 @@ impl AccountsDb {
|
|||||||
dirty_stores: DashMap::default(),
|
dirty_stores: DashMap::default(),
|
||||||
zero_lamport_accounts_to_purge_after_full_snapshot: DashSet::default(),
|
zero_lamport_accounts_to_purge_after_full_snapshot: DashSet::default(),
|
||||||
accounts_update_notifier: None,
|
accounts_update_notifier: None,
|
||||||
|
filler_account_count: 0,
|
||||||
|
filler_account_suffix: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1565,6 +1574,15 @@ impl AccountsDb {
|
|||||||
let accounts_hash_cache_path = accounts_db_config
|
let accounts_hash_cache_path = accounts_db_config
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|x| x.accounts_hash_cache_path.clone());
|
.and_then(|x| x.accounts_hash_cache_path.clone());
|
||||||
|
let filler_account_count = accounts_db_config
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|cfg| cfg.filler_account_count)
|
||||||
|
.unwrap_or_default();
|
||||||
|
let filler_account_suffix = if filler_account_count > 0 {
|
||||||
|
Some(solana_sdk::pubkey::new_rand())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let paths_is_empty = paths.is_empty();
|
let paths_is_empty = paths.is_empty();
|
||||||
let mut new = Self {
|
let mut new = Self {
|
||||||
paths,
|
paths,
|
||||||
@ -1573,6 +1591,8 @@ impl AccountsDb {
|
|||||||
caching_enabled,
|
caching_enabled,
|
||||||
shrink_ratio,
|
shrink_ratio,
|
||||||
accounts_update_notifier,
|
accounts_update_notifier,
|
||||||
|
filler_account_count,
|
||||||
|
filler_account_suffix,
|
||||||
..Self::default_with_accounts_index(accounts_index, accounts_hash_cache_path)
|
..Self::default_with_accounts_index(accounts_index, accounts_hash_cache_path)
|
||||||
};
|
};
|
||||||
if paths_is_empty {
|
if paths_is_empty {
|
||||||
@ -4855,6 +4875,9 @@ impl AccountsDb {
|
|||||||
let result: Vec<Hash> = pubkeys
|
let result: Vec<Hash> = pubkeys
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|pubkey| {
|
.filter_map(|pubkey| {
|
||||||
|
if self.is_filler_account(pubkey) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
if let AccountIndexGetResult::Found(lock, index) =
|
if let AccountIndexGetResult::Found(lock, index) =
|
||||||
self.accounts_index.get(pubkey, Some(ancestors), Some(slot))
|
self.accounts_index.get(pubkey, Some(ancestors), Some(slot))
|
||||||
{
|
{
|
||||||
@ -4880,7 +4903,7 @@ impl AccountsDb {
|
|||||||
|loaded_account| {
|
|loaded_account| {
|
||||||
let loaded_hash = loaded_account.loaded_hash();
|
let loaded_hash = loaded_account.loaded_hash();
|
||||||
let balance = account_info.lamports;
|
let balance = account_info.lamports;
|
||||||
if check_hash {
|
if check_hash && !self.is_filler_account(pubkey) {
|
||||||
let computed_hash =
|
let computed_hash =
|
||||||
loaded_account.compute_hash(*slot, pubkey);
|
loaded_account.compute_hash(*slot, pubkey);
|
||||||
if computed_hash != loaded_hash {
|
if computed_hash != loaded_hash {
|
||||||
@ -5233,6 +5256,11 @@ impl AccountsDb {
|
|||||||
timings,
|
timings,
|
||||||
check_hash,
|
check_hash,
|
||||||
accounts_cache_and_ancestors,
|
accounts_cache_and_ancestors,
|
||||||
|
if self.filler_account_count > 0 {
|
||||||
|
self.filler_account_suffix.as_ref()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
self.calculate_accounts_hash(slot, ancestors, check_hash)
|
self.calculate_accounts_hash(slot, ancestors, check_hash)
|
||||||
@ -5318,6 +5346,7 @@ impl AccountsDb {
|
|||||||
&Ancestors,
|
&Ancestors,
|
||||||
&AccountInfoAccountsIndex,
|
&AccountInfoAccountsIndex,
|
||||||
)>,
|
)>,
|
||||||
|
filler_account_suffix: Option<&Pubkey>,
|
||||||
) -> Result<Vec<BinnedHashData>, BankHashVerificationError> {
|
) -> Result<Vec<BinnedHashData>, BankHashVerificationError> {
|
||||||
let bin_calculator = PubkeyBinCalculator16::new(bins);
|
let bin_calculator = PubkeyBinCalculator16::new(bins);
|
||||||
assert!(bin_range.start < bins && bin_range.end <= bins && bin_range.start < bin_range.end);
|
assert!(bin_range.start < bins && bin_range.end <= bins && bin_range.start < bin_range.end);
|
||||||
@ -5352,7 +5381,7 @@ impl AccountsDb {
|
|||||||
let source_item =
|
let source_item =
|
||||||
CalculateHashIntermediate::new(loaded_account.loaded_hash(), balance, *pubkey);
|
CalculateHashIntermediate::new(loaded_account.loaded_hash(), balance, *pubkey);
|
||||||
|
|
||||||
if check_hash {
|
if check_hash && !Self::is_filler_account_helper(pubkey, filler_account_suffix) {
|
||||||
let computed_hash = loaded_account.compute_hash(slot, pubkey);
|
let computed_hash = loaded_account.compute_hash(slot, pubkey);
|
||||||
if computed_hash != source_item.hash {
|
if computed_hash != source_item.hash {
|
||||||
info!(
|
info!(
|
||||||
@ -5425,6 +5454,7 @@ impl AccountsDb {
|
|||||||
&Ancestors,
|
&Ancestors,
|
||||||
&AccountInfoAccountsIndex,
|
&AccountInfoAccountsIndex,
|
||||||
)>,
|
)>,
|
||||||
|
filler_account_suffix: Option<&Pubkey>,
|
||||||
) -> Result<(Hash, u64), BankHashVerificationError> {
|
) -> Result<(Hash, u64), BankHashVerificationError> {
|
||||||
let mut scan_and_hash = move || {
|
let mut scan_and_hash = move || {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -5450,9 +5480,13 @@ impl AccountsDb {
|
|||||||
&bounds,
|
&bounds,
|
||||||
check_hash,
|
check_hash,
|
||||||
accounts_cache_and_ancestors,
|
accounts_cache_and_ancestors,
|
||||||
|
filler_account_suffix,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (hash, lamports, for_next_pass) = AccountsHash::rest_of_hash_calculation(
|
let hash = AccountsHash {
|
||||||
|
filler_account_suffix: filler_account_suffix.cloned(),
|
||||||
|
};
|
||||||
|
let (hash, lamports, for_next_pass) = hash.rest_of_hash_calculation(
|
||||||
result,
|
result,
|
||||||
&mut stats,
|
&mut stats,
|
||||||
pass == NUM_SCAN_PASSES - 1,
|
pass == NUM_SCAN_PASSES - 1,
|
||||||
@ -5570,12 +5604,19 @@ impl AccountsDb {
|
|||||||
.scan_account_storage(
|
.scan_account_storage(
|
||||||
slot,
|
slot,
|
||||||
|loaded_account: LoadedAccount| {
|
|loaded_account: LoadedAccount| {
|
||||||
|
if self.is_filler_account(loaded_account.pubkey()) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
// Cache only has one version per key, don't need to worry about versioning
|
// Cache only has one version per key, don't need to worry about versioning
|
||||||
Some((*loaded_account.pubkey(), loaded_account.loaded_hash()))
|
Some((*loaded_account.pubkey(), loaded_account.loaded_hash()))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|accum: &DashMap<Pubkey, (u64, Hash)>, loaded_account: LoadedAccount| {
|
|accum: &DashMap<Pubkey, (u64, Hash)>, loaded_account: LoadedAccount| {
|
||||||
let loaded_write_version = loaded_account.write_version();
|
let loaded_write_version = loaded_account.write_version();
|
||||||
let loaded_hash = loaded_account.loaded_hash();
|
let loaded_hash = loaded_account.loaded_hash();
|
||||||
|
if self.is_filler_account(loaded_account.pubkey()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let should_insert =
|
let should_insert =
|
||||||
if let Some(existing_entry) = accum.get(loaded_account.pubkey()) {
|
if let Some(existing_entry) = accum.get(loaded_account.pubkey()) {
|
||||||
loaded_write_version > existing_entry.value().version()
|
loaded_write_version > existing_entry.value().version()
|
||||||
@ -6369,9 +6410,10 @@ impl AccountsDb {
|
|||||||
(result, slots)
|
(result, slots)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_storage_slot(
|
fn process_storage_slot<'a>(
|
||||||
storage_maps: &[Arc<AccountStorageEntry>],
|
&self,
|
||||||
) -> GenerateIndexAccountsMap<'_> {
|
storage_maps: &'a [Arc<AccountStorageEntry>],
|
||||||
|
) -> GenerateIndexAccountsMap<'a> {
|
||||||
let num_accounts = storage_maps
|
let num_accounts = storage_maps
|
||||||
.iter()
|
.iter()
|
||||||
.map(|storage| storage.approx_stored_count())
|
.map(|storage| storage.approx_stored_count())
|
||||||
@ -6381,7 +6423,9 @@ impl AccountsDb {
|
|||||||
let accounts = storage.all_accounts();
|
let accounts = storage.all_accounts();
|
||||||
accounts.into_iter().for_each(|stored_account| {
|
accounts.into_iter().for_each(|stored_account| {
|
||||||
let this_version = stored_account.meta.write_version;
|
let this_version = stored_account.meta.write_version;
|
||||||
match accounts_map.entry(stored_account.meta.pubkey) {
|
let pubkey = stored_account.meta.pubkey;
|
||||||
|
assert!(!self.is_filler_account(&pubkey));
|
||||||
|
match accounts_map.entry(pubkey) {
|
||||||
std::collections::hash_map::Entry::Vacant(entry) => {
|
std::collections::hash_map::Entry::Vacant(entry) => {
|
||||||
entry.insert(IndexAccountMapEntry {
|
entry.insert(IndexAccountMapEntry {
|
||||||
write_version: this_version,
|
write_version: this_version,
|
||||||
@ -6398,7 +6442,7 @@ impl AccountsDb {
|
|||||||
stored_account,
|
stored_account,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
assert!(occupied_version != this_version);
|
assert_ne!(occupied_version, this_version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6462,6 +6506,123 @@ impl AccountsDb {
|
|||||||
insert_us
|
insert_us
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn filler_unique_id_bytes() -> usize {
|
||||||
|
std::mem::size_of::<u32>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filler_rent_partition_prefix_bytes() -> usize {
|
||||||
|
std::mem::size_of::<u64>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filler_prefix_bytes() -> usize {
|
||||||
|
Self::filler_unique_id_bytes() + Self::filler_rent_partition_prefix_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_filler_account_helper(
|
||||||
|
pubkey: &Pubkey,
|
||||||
|
filler_account_suffix: Option<&Pubkey>,
|
||||||
|
) -> bool {
|
||||||
|
let offset = Self::filler_prefix_bytes();
|
||||||
|
filler_account_suffix
|
||||||
|
.as_ref()
|
||||||
|
.map(|filler_account_suffix| {
|
||||||
|
pubkey.as_ref()[offset..] == filler_account_suffix.as_ref()[offset..]
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_filler_account(&self, pubkey: &Pubkey) -> bool {
|
||||||
|
Self::is_filler_account_helper(pubkey, self.filler_account_suffix.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// filler accounts are space-holding accounts which are ignored by hash calculations and rent.
|
||||||
|
/// They are designed to allow a validator to run against a network successfully while simulating having many more accounts present.
|
||||||
|
/// All filler accounts share a common pubkey suffix. The suffix is randomly generated per validator on startup.
|
||||||
|
/// The filler accounts are added to each slot in the snapshot after index generation.
|
||||||
|
/// The accounts added in a slot are setup to have pubkeys such that rent will be collected from them before (or when?) their slot becomes an epoch old.
|
||||||
|
/// Thus, the filler accounts are rewritten by rent and the old slot can be thrown away successfully.
|
||||||
|
pub fn maybe_add_filler_accounts(&self, ticks_per_slot: SlotCount) {
|
||||||
|
if self.filler_account_count == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("adding {} filler accounts", self.filler_account_count);
|
||||||
|
// break this up to force the accounts out of memory after each pass
|
||||||
|
let passes = 100;
|
||||||
|
let roots = self.storage.all_slots();
|
||||||
|
let root_count = roots.len();
|
||||||
|
let per_pass = std::cmp::max(1, root_count / passes);
|
||||||
|
let overall_index = AtomicUsize::new(0);
|
||||||
|
let string = "FiLLERACCoUNTooooooooooooooooooooooooooooooo";
|
||||||
|
let hash = Hash::from_str(string).unwrap();
|
||||||
|
let owner = Pubkey::from_str(string).unwrap();
|
||||||
|
let lamports = 100_000_000;
|
||||||
|
let space = 0;
|
||||||
|
let account = AccountSharedData::new(lamports, space, &owner);
|
||||||
|
let added = AtomicUsize::default();
|
||||||
|
for pass in 0..=passes {
|
||||||
|
self.accounts_index.set_startup(true);
|
||||||
|
let roots_in_this_pass = roots
|
||||||
|
.iter()
|
||||||
|
.skip(pass * per_pass)
|
||||||
|
.take(per_pass)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let slot_count_in_two_day =
|
||||||
|
crate::bank::Bank::slot_count_in_two_day_helper(ticks_per_slot);
|
||||||
|
self.thread_pool.install(|| {
|
||||||
|
roots_in_this_pass.into_par_iter().for_each(|slot| {
|
||||||
|
let storage_maps: Vec<Arc<AccountStorageEntry>> = self
|
||||||
|
.storage
|
||||||
|
.get_slot_storage_entries(*slot)
|
||||||
|
.unwrap_or_default();
|
||||||
|
if storage_maps.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let partition = *crate::bank::Bank::get_partitions(
|
||||||
|
*slot,
|
||||||
|
slot.saturating_sub(1),
|
||||||
|
slot_count_in_two_day,
|
||||||
|
)
|
||||||
|
.last()
|
||||||
|
.unwrap();
|
||||||
|
let subrange = crate::bank::Bank::pubkey_range_from_partition(partition);
|
||||||
|
|
||||||
|
let idx = overall_index.fetch_add(1, Ordering::Relaxed);
|
||||||
|
let filler_entries = (idx + 1) * self.filler_account_count / root_count
|
||||||
|
- idx * self.filler_account_count / root_count;
|
||||||
|
let accounts = (0..filler_entries)
|
||||||
|
.map(|_| {
|
||||||
|
let my_id = added.fetch_add(1, Ordering::Relaxed);
|
||||||
|
let my_id_bytes = u32::to_be_bytes(my_id as u32);
|
||||||
|
|
||||||
|
// pubkey begins life as entire filler 'suffix' pubkey
|
||||||
|
let mut key = self.filler_account_suffix.unwrap();
|
||||||
|
let rent_prefix_bytes = Self::filler_rent_partition_prefix_bytes();
|
||||||
|
// first bytes are replaced with rent partition range: filler_rent_partition_prefix_bytes
|
||||||
|
key.as_mut()[0..rent_prefix_bytes]
|
||||||
|
.copy_from_slice(&subrange.start().as_ref()[0..rent_prefix_bytes]);
|
||||||
|
// next bytes are replaced with my_id: filler_unique_id_bytes
|
||||||
|
key.as_mut()[rent_prefix_bytes
|
||||||
|
..(rent_prefix_bytes + Self::filler_unique_id_bytes())]
|
||||||
|
.copy_from_slice(&my_id_bytes);
|
||||||
|
assert!(subrange.contains(&key));
|
||||||
|
key
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let add = accounts
|
||||||
|
.iter()
|
||||||
|
.map(|key| (key, &account))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let hashes = (0..filler_entries).map(|_| hash).collect::<Vec<_>>();
|
||||||
|
self.store_accounts_frozen(*slot, &add[..], Some(&hashes[..]), None, None);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
self.accounts_index.set_startup(false);
|
||||||
|
}
|
||||||
|
info!("added {} filler accounts", added.load(Ordering::Relaxed));
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::needless_collect)]
|
#[allow(clippy::needless_collect)]
|
||||||
pub fn generate_index(&self, limit_load_slot_count_from_snapshot: Option<usize>, verify: bool) {
|
pub fn generate_index(&self, limit_load_slot_count_from_snapshot: Option<usize>, verify: bool) {
|
||||||
let mut slots = self.storage.all_slots();
|
let mut slots = self.storage.all_slots();
|
||||||
@ -6501,7 +6662,7 @@ impl AccountsDb {
|
|||||||
.storage
|
.storage
|
||||||
.get_slot_storage_entries(*slot)
|
.get_slot_storage_entries(*slot)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let accounts_map = Self::process_storage_slot(&storage_maps);
|
let accounts_map = self.process_storage_slot(&storage_maps);
|
||||||
scan_time.stop();
|
scan_time.stop();
|
||||||
scan_time_sum += scan_time.as_us();
|
scan_time_sum += scan_time.as_us();
|
||||||
Self::update_storage_info(
|
Self::update_storage_info(
|
||||||
@ -7005,6 +7166,7 @@ pub mod tests {
|
|||||||
bin_range,
|
bin_range,
|
||||||
check_hash,
|
check_hash,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7351,6 +7513,7 @@ pub mod tests {
|
|||||||
HashStats::default(),
|
HashStats::default(),
|
||||||
false,
|
false,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let expected_hash = Hash::from_str("GKot5hBsd81kMupNCXHaqbhv3huEbxAFMLnpcX2hniwn").unwrap();
|
let expected_hash = Hash::from_str("GKot5hBsd81kMupNCXHaqbhv3huEbxAFMLnpcX2hniwn").unwrap();
|
||||||
@ -7374,6 +7537,7 @@ pub mod tests {
|
|||||||
HashStats::default(),
|
HashStats::default(),
|
||||||
false,
|
false,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -12736,7 +12900,7 @@ pub mod tests {
|
|||||||
.get_slot_storage_entries(slot0)
|
.get_slot_storage_entries(slot0)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let storage_info = StorageSizeAndCountMap::default();
|
let storage_info = StorageSizeAndCountMap::default();
|
||||||
let accounts_map = AccountsDb::process_storage_slot(&storage_maps[..]);
|
let accounts_map = accounts.process_storage_slot(&storage_maps[..]);
|
||||||
AccountsDb::update_storage_info(&storage_info, &accounts_map, &Mutex::default());
|
AccountsDb::update_storage_info(&storage_info, &accounts_map, &Mutex::default());
|
||||||
assert_eq!(storage_info.len(), 1);
|
assert_eq!(storage_info.len(), 1);
|
||||||
for entry in storage_info.iter() {
|
for entry in storage_info.iter() {
|
||||||
@ -12749,9 +12913,10 @@ pub mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_calculate_storage_count_and_alive_bytes_0_accounts() {
|
fn test_calculate_storage_count_and_alive_bytes_0_accounts() {
|
||||||
|
let accounts = AccountsDb::new_single_for_tests();
|
||||||
let storage_maps = vec![];
|
let storage_maps = vec![];
|
||||||
let storage_info = StorageSizeAndCountMap::default();
|
let storage_info = StorageSizeAndCountMap::default();
|
||||||
let accounts_map = AccountsDb::process_storage_slot(&storage_maps[..]);
|
let accounts_map = accounts.process_storage_slot(&storage_maps[..]);
|
||||||
AccountsDb::update_storage_info(&storage_info, &accounts_map, &Mutex::default());
|
AccountsDb::update_storage_info(&storage_info, &accounts_map, &Mutex::default());
|
||||||
assert!(storage_info.is_empty());
|
assert!(storage_info.is_empty());
|
||||||
}
|
}
|
||||||
@ -12786,7 +12951,7 @@ pub mod tests {
|
|||||||
.get_slot_storage_entries(slot0)
|
.get_slot_storage_entries(slot0)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let storage_info = StorageSizeAndCountMap::default();
|
let storage_info = StorageSizeAndCountMap::default();
|
||||||
let accounts_map = AccountsDb::process_storage_slot(&storage_maps[..]);
|
let accounts_map = accounts.process_storage_slot(&storage_maps[..]);
|
||||||
AccountsDb::update_storage_info(&storage_info, &accounts_map, &Mutex::default());
|
AccountsDb::update_storage_info(&storage_info, &accounts_map, &Mutex::default());
|
||||||
assert_eq!(storage_info.len(), 1);
|
assert_eq!(storage_info.len(), 1);
|
||||||
for entry in storage_info.iter() {
|
for entry in storage_info.iter() {
|
||||||
|
@ -195,9 +195,9 @@ impl CumulativeOffsets {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct AccountsHash {
|
pub struct AccountsHash {
|
||||||
pub dummy: i32,
|
pub filler_account_suffix: Option<Pubkey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AccountsHash {
|
impl AccountsHash {
|
||||||
@ -504,6 +504,7 @@ impl AccountsHash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn de_dup_and_eliminate_zeros(
|
fn de_dup_and_eliminate_zeros(
|
||||||
|
&self,
|
||||||
sorted_data_by_pubkey: Vec<Vec<Vec<CalculateHashIntermediate>>>,
|
sorted_data_by_pubkey: Vec<Vec<Vec<CalculateHashIntermediate>>>,
|
||||||
stats: &mut HashStats,
|
stats: &mut HashStats,
|
||||||
max_bin: usize,
|
max_bin: usize,
|
||||||
@ -520,7 +521,7 @@ impl AccountsHash {
|
|||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.map(|bin| {
|
.map(|bin| {
|
||||||
let (hashes, lamports_bin, unreduced_entries_count) =
|
let (hashes, lamports_bin, unreduced_entries_count) =
|
||||||
Self::de_dup_accounts_in_parallel(&sorted_data_by_pubkey, bin);
|
self.de_dup_accounts_in_parallel(&sorted_data_by_pubkey, bin);
|
||||||
{
|
{
|
||||||
let mut lock = min_max_sum_entries_hashes.lock().unwrap();
|
let mut lock = min_max_sum_entries_hashes.lock().unwrap();
|
||||||
let (mut min, mut max, mut lamports_sum, mut entries, mut hash_total) = *lock;
|
let (mut min, mut max, mut lamports_sum, mut entries, mut hash_total) = *lock;
|
||||||
@ -594,6 +595,7 @@ impl AccountsHash {
|
|||||||
// b. lamport sum
|
// b. lamport sum
|
||||||
// c. unreduced count (ie. including duplicates and zero lamport)
|
// c. unreduced count (ie. including duplicates and zero lamport)
|
||||||
fn de_dup_accounts_in_parallel(
|
fn de_dup_accounts_in_parallel(
|
||||||
|
&self,
|
||||||
pubkey_division: &[Vec<Vec<CalculateHashIntermediate>>],
|
pubkey_division: &[Vec<Vec<CalculateHashIntermediate>>],
|
||||||
pubkey_bin: usize,
|
pubkey_bin: usize,
|
||||||
) -> (Vec<Hash>, u64, usize) {
|
) -> (Vec<Hash>, u64, usize) {
|
||||||
@ -658,7 +660,8 @@ impl AccountsHash {
|
|||||||
pubkey_division,
|
pubkey_division,
|
||||||
&mut indexes,
|
&mut indexes,
|
||||||
);
|
);
|
||||||
if item.lamports != ZERO_RAW_LAMPORTS_SENTINEL {
|
if !self.is_filler_account(&item.pubkey) && item.lamports != ZERO_RAW_LAMPORTS_SENTINEL
|
||||||
|
{
|
||||||
overall_sum = Self::checked_cast_for_capitalization(
|
overall_sum = Self::checked_cast_for_capitalization(
|
||||||
item.lamports as u128 + overall_sum as u128,
|
item.lamports as u128 + overall_sum as u128,
|
||||||
);
|
);
|
||||||
@ -668,11 +671,19 @@ impl AccountsHash {
|
|||||||
(hashes, overall_sum, item_len)
|
(hashes, overall_sum, item_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_filler_account(&self, pubkey: &Pubkey) -> bool {
|
||||||
|
crate::accounts_db::AccountsDb::is_filler_account_helper(
|
||||||
|
pubkey,
|
||||||
|
self.filler_account_suffix.as_ref(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// input:
|
// input:
|
||||||
// vec: group of slot data, ordered by Slot (low to high)
|
// vec: group of slot data, ordered by Slot (low to high)
|
||||||
// vec: [0..bins] - where bins are pubkey ranges (these are ordered by Pubkey range)
|
// vec: [0..bins] - where bins are pubkey ranges (these are ordered by Pubkey range)
|
||||||
// vec: [..] - items which fit in the containing bin. Sorted by: Pubkey, higher Slot, higher Write version (if pubkey =)
|
// vec: [..] - items which fit in the containing bin. Sorted by: Pubkey, higher Slot, higher Write version (if pubkey =)
|
||||||
pub fn rest_of_hash_calculation(
|
pub fn rest_of_hash_calculation(
|
||||||
|
&self,
|
||||||
data_sections_by_pubkey: Vec<Vec<Vec<CalculateHashIntermediate>>>,
|
data_sections_by_pubkey: Vec<Vec<Vec<CalculateHashIntermediate>>>,
|
||||||
mut stats: &mut HashStats,
|
mut stats: &mut HashStats,
|
||||||
is_last_pass: bool,
|
is_last_pass: bool,
|
||||||
@ -680,7 +691,7 @@ impl AccountsHash {
|
|||||||
max_bin: usize,
|
max_bin: usize,
|
||||||
) -> (Hash, u64, PreviousPass) {
|
) -> (Hash, u64, PreviousPass) {
|
||||||
let (mut hashes, mut total_lamports) =
|
let (mut hashes, mut total_lamports) =
|
||||||
Self::de_dup_and_eliminate_zeros(data_sections_by_pubkey, stats, max_bin);
|
self.de_dup_and_eliminate_zeros(data_sections_by_pubkey, stats, max_bin);
|
||||||
|
|
||||||
total_lamports += previous_state.lamports;
|
total_lamports += previous_state.lamports;
|
||||||
|
|
||||||
@ -823,7 +834,8 @@ pub mod tests {
|
|||||||
let val = CalculateHashIntermediate::new(hash, ZERO_RAW_LAMPORTS_SENTINEL, key);
|
let val = CalculateHashIntermediate::new(hash, ZERO_RAW_LAMPORTS_SENTINEL, key);
|
||||||
account_maps.push(val);
|
account_maps.push(val);
|
||||||
|
|
||||||
let result = AccountsHash::rest_of_hash_calculation(
|
let accounts_hash = AccountsHash::default();
|
||||||
|
let result = accounts_hash.rest_of_hash_calculation(
|
||||||
for_rest(account_maps.clone()),
|
for_rest(account_maps.clone()),
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
true,
|
true,
|
||||||
@ -839,7 +851,7 @@ pub mod tests {
|
|||||||
let val = CalculateHashIntermediate::new(hash, 20, key);
|
let val = CalculateHashIntermediate::new(hash, 20, key);
|
||||||
account_maps.insert(0, val);
|
account_maps.insert(0, val);
|
||||||
|
|
||||||
let result = AccountsHash::rest_of_hash_calculation(
|
let result = accounts_hash.rest_of_hash_calculation(
|
||||||
for_rest(account_maps.clone()),
|
for_rest(account_maps.clone()),
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
true,
|
true,
|
||||||
@ -855,7 +867,7 @@ pub mod tests {
|
|||||||
let val = CalculateHashIntermediate::new(hash, 30, key);
|
let val = CalculateHashIntermediate::new(hash, 30, key);
|
||||||
account_maps.insert(1, val);
|
account_maps.insert(1, val);
|
||||||
|
|
||||||
let result = AccountsHash::rest_of_hash_calculation(
|
let result = accounts_hash.rest_of_hash_calculation(
|
||||||
for_rest(account_maps),
|
for_rest(account_maps),
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
true,
|
true,
|
||||||
@ -898,9 +910,10 @@ pub mod tests {
|
|||||||
|
|
||||||
let mut previous_pass = PreviousPass::default();
|
let mut previous_pass = PreviousPass::default();
|
||||||
|
|
||||||
|
let accounts_index = AccountsHash::default();
|
||||||
if pass == 0 {
|
if pass == 0 {
|
||||||
// first pass that is not last and is empty
|
// first pass that is not last and is empty
|
||||||
let result = AccountsHash::rest_of_hash_calculation(
|
let result = accounts_index.rest_of_hash_calculation(
|
||||||
vec![vec![vec![]]],
|
vec![vec![vec![]]],
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
false, // not last pass
|
false, // not last pass
|
||||||
@ -915,7 +928,7 @@ pub mod tests {
|
|||||||
assert_eq!(previous_pass.lamports, 0);
|
assert_eq!(previous_pass.lamports, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = AccountsHash::rest_of_hash_calculation(
|
let result = accounts_index.rest_of_hash_calculation(
|
||||||
for_rest(account_maps.clone()),
|
for_rest(account_maps.clone()),
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
false, // not last pass
|
false, // not last pass
|
||||||
@ -932,8 +945,9 @@ pub mod tests {
|
|||||||
|
|
||||||
let expected_hash =
|
let expected_hash =
|
||||||
Hash::from_str("8j9ARGFv4W2GfML7d3sVJK2MePwrikqYnu6yqer28cCa").unwrap();
|
Hash::from_str("8j9ARGFv4W2GfML7d3sVJK2MePwrikqYnu6yqer28cCa").unwrap();
|
||||||
|
let accounts_index = AccountsHash::default();
|
||||||
if pass == 2 {
|
if pass == 2 {
|
||||||
let result = AccountsHash::rest_of_hash_calculation(
|
let result = accounts_index.rest_of_hash_calculation(
|
||||||
vec![vec![vec![]]],
|
vec![vec![vec![]]],
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
false,
|
false,
|
||||||
@ -947,7 +961,7 @@ pub mod tests {
|
|||||||
assert_eq!(previous_pass.lamports, account_maps[0].lamports);
|
assert_eq!(previous_pass.lamports, account_maps[0].lamports);
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = AccountsHash::rest_of_hash_calculation(
|
let result = accounts_index.rest_of_hash_calculation(
|
||||||
vec![vec![vec![]]],
|
vec![vec![vec![]]],
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
true, // finally, last pass
|
true, // finally, last pass
|
||||||
@ -979,8 +993,8 @@ pub mod tests {
|
|||||||
let hash = Hash::new(&[2u8; 32]);
|
let hash = Hash::new(&[2u8; 32]);
|
||||||
let val = CalculateHashIntermediate::new(hash, 20, key);
|
let val = CalculateHashIntermediate::new(hash, 20, key);
|
||||||
account_maps.push(val);
|
account_maps.push(val);
|
||||||
|
let accounts_hash = AccountsHash::default();
|
||||||
let result = AccountsHash::rest_of_hash_calculation(
|
let result = accounts_hash.rest_of_hash_calculation(
|
||||||
for_rest(vec![account_maps[0].clone()]),
|
for_rest(vec![account_maps[0].clone()]),
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
false, // not last pass
|
false, // not last pass
|
||||||
@ -995,7 +1009,7 @@ pub mod tests {
|
|||||||
assert_eq!(previous_pass.reduced_hashes.len(), 0);
|
assert_eq!(previous_pass.reduced_hashes.len(), 0);
|
||||||
assert_eq!(previous_pass.lamports, account_maps[0].lamports);
|
assert_eq!(previous_pass.lamports, account_maps[0].lamports);
|
||||||
|
|
||||||
let result = AccountsHash::rest_of_hash_calculation(
|
let result = accounts_hash.rest_of_hash_calculation(
|
||||||
for_rest(vec![account_maps[1].clone()]),
|
for_rest(vec![account_maps[1].clone()]),
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
false, // not last pass
|
false, // not last pass
|
||||||
@ -1014,7 +1028,7 @@ pub mod tests {
|
|||||||
let total_lamports_expected = account_maps[0].lamports + account_maps[1].lamports;
|
let total_lamports_expected = account_maps[0].lamports + account_maps[1].lamports;
|
||||||
assert_eq!(previous_pass.lamports, total_lamports_expected);
|
assert_eq!(previous_pass.lamports, total_lamports_expected);
|
||||||
|
|
||||||
let result = AccountsHash::rest_of_hash_calculation(
|
let result = accounts_hash.rest_of_hash_calculation(
|
||||||
vec![vec![vec![]]],
|
vec![vec![vec![]]],
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
true,
|
true,
|
||||||
@ -1046,6 +1060,7 @@ pub mod tests {
|
|||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
|
||||||
let mut account_maps = Vec::new();
|
let mut account_maps = Vec::new();
|
||||||
|
let accounts_hash = AccountsHash::default();
|
||||||
|
|
||||||
const TARGET_FANOUT_LEVEL: usize = 3;
|
const TARGET_FANOUT_LEVEL: usize = 3;
|
||||||
let target_fanout = MERKLE_FANOUT.pow(TARGET_FANOUT_LEVEL as u32);
|
let target_fanout = MERKLE_FANOUT.pow(TARGET_FANOUT_LEVEL as u32);
|
||||||
@ -1065,7 +1080,7 @@ pub mod tests {
|
|||||||
let sorted = chunk.clone();
|
let sorted = chunk.clone();
|
||||||
|
|
||||||
// first 4097 hashes (1 left over)
|
// first 4097 hashes (1 left over)
|
||||||
let result = AccountsHash::rest_of_hash_calculation(
|
let result = accounts_hash.rest_of_hash_calculation(
|
||||||
for_rest(chunk),
|
for_rest(chunk),
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
false, // not last pass
|
false, // not last pass
|
||||||
@ -1110,7 +1125,7 @@ pub mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// second 4097 hashes (2 left over)
|
// second 4097 hashes (2 left over)
|
||||||
let result = AccountsHash::rest_of_hash_calculation(
|
let result = accounts_hash.rest_of_hash_calculation(
|
||||||
for_rest(chunk),
|
for_rest(chunk),
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
false, // not last pass
|
false, // not last pass
|
||||||
@ -1138,7 +1153,7 @@ pub mod tests {
|
|||||||
.sum::<u64>()
|
.sum::<u64>()
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = AccountsHash::rest_of_hash_calculation(
|
let result = accounts_hash.rest_of_hash_calculation(
|
||||||
vec![vec![vec![]]],
|
vec![vec![vec![]]],
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
true,
|
true,
|
||||||
@ -1169,10 +1184,8 @@ pub mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_accountsdb_de_dup_accounts_zero_chunks() {
|
fn test_accountsdb_de_dup_accounts_zero_chunks() {
|
||||||
let (hashes, lamports, _) = AccountsHash::de_dup_accounts_in_parallel(
|
let (hashes, lamports, _) = AccountsHash::default()
|
||||||
&[vec![vec![CalculateHashIntermediate::default()]]],
|
.de_dup_accounts_in_parallel(&[vec![vec![CalculateHashIntermediate::default()]]], 0);
|
||||||
0,
|
|
||||||
);
|
|
||||||
assert_eq!(vec![Hash::default()], hashes);
|
assert_eq!(vec![Hash::default()], hashes);
|
||||||
assert_eq!(lamports, 0);
|
assert_eq!(lamports, 0);
|
||||||
}
|
}
|
||||||
@ -1180,8 +1193,9 @@ pub mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_accountsdb_de_dup_accounts_empty() {
|
fn test_accountsdb_de_dup_accounts_empty() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
let accounts_hash = AccountsHash::default();
|
||||||
|
|
||||||
let (hashes, lamports) = AccountsHash::de_dup_and_eliminate_zeros(
|
let (hashes, lamports) = accounts_hash.de_dup_and_eliminate_zeros(
|
||||||
vec![vec![], vec![]],
|
vec![vec![], vec![]],
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
one_range(),
|
one_range(),
|
||||||
@ -1192,7 +1206,7 @@ pub mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(lamports, 0);
|
assert_eq!(lamports, 0);
|
||||||
|
|
||||||
let (hashes, lamports) = AccountsHash::de_dup_and_eliminate_zeros(
|
let (hashes, lamports) = accounts_hash.de_dup_and_eliminate_zeros(
|
||||||
vec![],
|
vec![],
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
zero_range(),
|
zero_range(),
|
||||||
@ -1201,11 +1215,11 @@ pub mod tests {
|
|||||||
assert_eq!(empty, hashes);
|
assert_eq!(empty, hashes);
|
||||||
assert_eq!(lamports, 0);
|
assert_eq!(lamports, 0);
|
||||||
|
|
||||||
let (hashes, lamports, _) = AccountsHash::de_dup_accounts_in_parallel(&[], 1);
|
let (hashes, lamports, _) = accounts_hash.de_dup_accounts_in_parallel(&[], 1);
|
||||||
assert_eq!(vec![Hash::default(); 0], hashes);
|
assert_eq!(vec![Hash::default(); 0], hashes);
|
||||||
assert_eq!(lamports, 0);
|
assert_eq!(lamports, 0);
|
||||||
|
|
||||||
let (hashes, lamports, _) = AccountsHash::de_dup_accounts_in_parallel(&[], 2);
|
let (hashes, lamports, _) = accounts_hash.de_dup_accounts_in_parallel(&[], 2);
|
||||||
assert_eq!(vec![Hash::default(); 0], hashes);
|
assert_eq!(vec![Hash::default(); 0], hashes);
|
||||||
assert_eq!(lamports, 0);
|
assert_eq!(lamports, 0);
|
||||||
}
|
}
|
||||||
@ -1276,6 +1290,7 @@ pub mod tests {
|
|||||||
result
|
result
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
|
let hash = AccountsHash::default();
|
||||||
let mut expected_index = 0;
|
let mut expected_index = 0;
|
||||||
for last_slice in 0..2 {
|
for last_slice in 0..2 {
|
||||||
for start in 0..COUNT {
|
for start in 0..COUNT {
|
||||||
@ -1286,21 +1301,19 @@ pub mod tests {
|
|||||||
|
|
||||||
let slice2 = vec![vec![slice.to_vec()]];
|
let slice2 = vec![vec![slice.to_vec()]];
|
||||||
let slice = &slice2[..];
|
let slice = &slice2[..];
|
||||||
let (hashes2, lamports2, _) =
|
let (hashes2, lamports2, _) = hash.de_dup_accounts_in_parallel(slice, 0);
|
||||||
AccountsHash::de_dup_accounts_in_parallel(slice, 0);
|
let (hashes3, lamports3, _) = hash.de_dup_accounts_in_parallel(slice, 0);
|
||||||
let (hashes3, lamports3, _) =
|
let (hashes4, lamports4) = hash.de_dup_and_eliminate_zeros(
|
||||||
AccountsHash::de_dup_accounts_in_parallel(slice, 0);
|
|
||||||
let (hashes4, lamports4) = AccountsHash::de_dup_and_eliminate_zeros(
|
|
||||||
slice.to_vec(),
|
slice.to_vec(),
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
end - start,
|
end - start,
|
||||||
);
|
);
|
||||||
let (hashes5, lamports5) = AccountsHash::de_dup_and_eliminate_zeros(
|
let (hashes5, lamports5) = hash.de_dup_and_eliminate_zeros(
|
||||||
slice.to_vec(),
|
slice.to_vec(),
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
end - start,
|
end - start,
|
||||||
);
|
);
|
||||||
let (hashes6, lamports6) = AccountsHash::de_dup_and_eliminate_zeros(
|
let (hashes6, lamports6) = hash.de_dup_and_eliminate_zeros(
|
||||||
slice.to_vec(),
|
slice.to_vec(),
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
end - start,
|
end - start,
|
||||||
@ -1413,7 +1426,8 @@ pub mod tests {
|
|||||||
fn test_de_dup_accounts_in_parallel(
|
fn test_de_dup_accounts_in_parallel(
|
||||||
account_maps: &[CalculateHashIntermediate],
|
account_maps: &[CalculateHashIntermediate],
|
||||||
) -> (Vec<Hash>, u64, usize) {
|
) -> (Vec<Hash>, u64, usize) {
|
||||||
AccountsHash::de_dup_accounts_in_parallel(&vec![vec![account_maps.to_vec()]][..], 0)
|
AccountsHash::default()
|
||||||
|
.de_dup_accounts_in_parallel(&vec![vec![account_maps.to_vec()]][..], 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1803,7 +1817,7 @@ pub mod tests {
|
|||||||
),
|
),
|
||||||
CalculateHashIntermediate::new(Hash::new(&[2u8; 32]), offset + 1, Pubkey::new_unique()),
|
CalculateHashIntermediate::new(Hash::new(&[2u8; 32]), offset + 1, Pubkey::new_unique()),
|
||||||
];
|
];
|
||||||
AccountsHash::de_dup_accounts_in_parallel(&[vec![input]], 0);
|
AccountsHash::default().de_dup_accounts_in_parallel(&[vec![input]], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1824,7 +1838,7 @@ pub mod tests {
|
|||||||
Pubkey::new_unique(),
|
Pubkey::new_unique(),
|
||||||
)],
|
)],
|
||||||
];
|
];
|
||||||
AccountsHash::de_dup_and_eliminate_zeros(
|
AccountsHash::default().de_dup_and_eliminate_zeros(
|
||||||
vec![input],
|
vec![input],
|
||||||
&mut HashStats::default(),
|
&mut HashStats::default(),
|
||||||
2, // accounts above are in 2 groups
|
2, // accounts above are in 2 groups
|
||||||
|
@ -4410,6 +4410,7 @@ impl Bank {
|
|||||||
&pubkey,
|
&pubkey,
|
||||||
&mut account,
|
&mut account,
|
||||||
rent_for_sysvars,
|
rent_for_sysvars,
|
||||||
|
self.rc.accounts.accounts_db.filler_account_suffix.as_ref(),
|
||||||
);
|
);
|
||||||
total_rent += rent;
|
total_rent += rent;
|
||||||
// Store all of them unconditionally to purge old AppendVec,
|
// Store all of them unconditionally to purge old AppendVec,
|
||||||
@ -4431,7 +4432,7 @@ impl Bank {
|
|||||||
// start_index..=end_index. But it has some exceptional cases, including
|
// start_index..=end_index. But it has some exceptional cases, including
|
||||||
// this important and valid one:
|
// this important and valid one:
|
||||||
// 0..=0: the first partition in the new epoch when crossing epochs
|
// 0..=0: the first partition in the new epoch when crossing epochs
|
||||||
fn pubkey_range_from_partition(
|
pub fn pubkey_range_from_partition(
|
||||||
(start_index, end_index, partition_count): Partition,
|
(start_index, end_index, partition_count): Partition,
|
||||||
) -> RangeInclusive<Pubkey> {
|
) -> RangeInclusive<Pubkey> {
|
||||||
assert!(start_index <= end_index);
|
assert!(start_index <= end_index);
|
||||||
@ -4496,13 +4497,15 @@ impl Bank {
|
|||||||
Pubkey::new_from_array(start_pubkey)..=Pubkey::new_from_array(end_pubkey)
|
Pubkey::new_from_array(start_pubkey)..=Pubkey::new_from_array(end_pubkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fixed_cycle_partitions(&self) -> Vec<Partition> {
|
pub fn get_partitions(
|
||||||
let slot_count_in_two_day = self.slot_count_in_two_day();
|
slot: Slot,
|
||||||
|
parent_slot: Slot,
|
||||||
let parent_cycle = self.parent_slot() / slot_count_in_two_day;
|
slot_count_in_two_day: SlotCount,
|
||||||
let current_cycle = self.slot() / slot_count_in_two_day;
|
) -> Vec<Partition> {
|
||||||
let mut parent_cycle_index = self.parent_slot() % slot_count_in_two_day;
|
let parent_cycle = parent_slot / slot_count_in_two_day;
|
||||||
let current_cycle_index = self.slot() % slot_count_in_two_day;
|
let current_cycle = slot / slot_count_in_two_day;
|
||||||
|
let mut parent_cycle_index = parent_slot % slot_count_in_two_day;
|
||||||
|
let current_cycle_index = slot % slot_count_in_two_day;
|
||||||
let mut partitions = vec![];
|
let mut partitions = vec![];
|
||||||
if parent_cycle < current_cycle {
|
if parent_cycle < current_cycle {
|
||||||
if current_cycle_index > 0 {
|
if current_cycle_index > 0 {
|
||||||
@ -4531,6 +4534,11 @@ impl Bank {
|
|||||||
partitions
|
partitions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fixed_cycle_partitions(&self) -> Vec<Partition> {
|
||||||
|
let slot_count_in_two_day = self.slot_count_in_two_day();
|
||||||
|
Self::get_partitions(self.slot(), self.parent_slot(), slot_count_in_two_day)
|
||||||
|
}
|
||||||
|
|
||||||
fn variable_cycle_partitions(&self) -> Vec<Partition> {
|
fn variable_cycle_partitions(&self) -> Vec<Partition> {
|
||||||
let (current_epoch, current_slot_index) = self.get_epoch_and_slot_index(self.slot());
|
let (current_epoch, current_slot_index) = self.get_epoch_and_slot_index(self.slot());
|
||||||
let (parent_epoch, mut parent_slot_index) =
|
let (parent_epoch, mut parent_slot_index) =
|
||||||
@ -4722,11 +4730,15 @@ impl Bank {
|
|||||||
&& self.slot_count_per_normal_epoch() < self.slot_count_in_two_day()
|
&& self.slot_count_per_normal_epoch() < self.slot_count_in_two_day()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn slot_count_in_two_day(&self) -> SlotCount {
|
||||||
|
Self::slot_count_in_two_day_helper(self.ticks_per_slot)
|
||||||
|
}
|
||||||
|
|
||||||
// This value is specially chosen to align with slots per epoch in mainnet-beta and testnet
|
// This value is specially chosen to align with slots per epoch in mainnet-beta and testnet
|
||||||
// Also, assume 500GB account data set as the extreme, then for 2 day (=48 hours) to collect
|
// Also, assume 500GB account data set as the extreme, then for 2 day (=48 hours) to collect
|
||||||
// rent eagerly, we'll consume 5.7 MB/s IO bandwidth, bidirectionally.
|
// rent eagerly, we'll consume 5.7 MB/s IO bandwidth, bidirectionally.
|
||||||
fn slot_count_in_two_day(&self) -> SlotCount {
|
pub fn slot_count_in_two_day_helper(ticks_per_slot: SlotCount) -> SlotCount {
|
||||||
2 * DEFAULT_TICKS_PER_SECOND * SECONDS_PER_DAY / self.ticks_per_slot
|
2 * DEFAULT_TICKS_PER_SECOND * SECONDS_PER_DAY / ticks_per_slot
|
||||||
}
|
}
|
||||||
|
|
||||||
fn slot_count_per_normal_epoch(&self) -> SlotCount {
|
fn slot_count_per_normal_epoch(&self) -> SlotCount {
|
||||||
|
@ -61,11 +61,13 @@ impl RentCollector {
|
|||||||
address: &Pubkey,
|
address: &Pubkey,
|
||||||
account: &mut AccountSharedData,
|
account: &mut AccountSharedData,
|
||||||
rent_for_sysvars: bool,
|
rent_for_sysvars: bool,
|
||||||
|
filler_account_suffix: Option<&Pubkey>,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
if account.executable() // executable accounts must be rent-exempt balance
|
if account.executable() // executable accounts must be rent-exempt balance
|
||||||
|| account.rent_epoch() > self.epoch
|
|| account.rent_epoch() > self.epoch
|
||||||
|| (!rent_for_sysvars && sysvar::check_id(account.owner()))
|
|| (!rent_for_sysvars && sysvar::check_id(account.owner()))
|
||||||
|| *address == incinerator::id()
|
|| *address == incinerator::id()
|
||||||
|
|| crate::accounts_db::AccountsDb::is_filler_account_helper(address, filler_account_suffix)
|
||||||
{
|
{
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
@ -120,7 +122,7 @@ impl RentCollector {
|
|||||||
) -> u64 {
|
) -> u64 {
|
||||||
// initialize rent_epoch as created at this epoch
|
// initialize rent_epoch as created at this epoch
|
||||||
account.set_rent_epoch(self.epoch);
|
account.set_rent_epoch(self.epoch);
|
||||||
self.collect_from_existing_account(address, account, rent_for_sysvars)
|
self.collect_from_existing_account(address, account, rent_for_sysvars, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,6 +164,7 @@ mod tests {
|
|||||||
&solana_sdk::pubkey::new_rand(),
|
&solana_sdk::pubkey::new_rand(),
|
||||||
&mut existing_account,
|
&mut existing_account,
|
||||||
true,
|
true,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
assert!(existing_account.lamports() < old_lamports);
|
assert!(existing_account.lamports() < old_lamports);
|
||||||
assert_eq!(existing_account.lamports() + collected, old_lamports);
|
assert_eq!(existing_account.lamports() + collected, old_lamports);
|
||||||
@ -188,7 +191,7 @@ mod tests {
|
|||||||
let rent_collector = RentCollector::default().clone_with_epoch(epoch);
|
let rent_collector = RentCollector::default().clone_with_epoch(epoch);
|
||||||
|
|
||||||
// first mark account as being collected while being rent-exempt
|
// first mark account as being collected while being rent-exempt
|
||||||
collected = rent_collector.collect_from_existing_account(&pubkey, &mut account, true);
|
collected = rent_collector.collect_from_existing_account(&pubkey, &mut account, true, None);
|
||||||
assert_eq!(account.lamports(), huge_lamports);
|
assert_eq!(account.lamports(), huge_lamports);
|
||||||
assert_eq!(collected, 0);
|
assert_eq!(collected, 0);
|
||||||
|
|
||||||
@ -196,7 +199,7 @@ mod tests {
|
|||||||
account.set_lamports(tiny_lamports);
|
account.set_lamports(tiny_lamports);
|
||||||
|
|
||||||
// ... and trigger another rent collection on the same epoch and check that rent is working
|
// ... and trigger another rent collection on the same epoch and check that rent is working
|
||||||
collected = rent_collector.collect_from_existing_account(&pubkey, &mut account, true);
|
collected = rent_collector.collect_from_existing_account(&pubkey, &mut account, true, None);
|
||||||
assert_eq!(account.lamports(), tiny_lamports - collected);
|
assert_eq!(account.lamports(), tiny_lamports - collected);
|
||||||
assert_ne!(collected, 0);
|
assert_ne!(collected, 0);
|
||||||
}
|
}
|
||||||
@ -216,12 +219,14 @@ mod tests {
|
|||||||
let rent_collector = RentCollector::default().clone_with_epoch(epoch);
|
let rent_collector = RentCollector::default().clone_with_epoch(epoch);
|
||||||
|
|
||||||
// old behavior: sysvars are special-cased
|
// old behavior: sysvars are special-cased
|
||||||
let collected = rent_collector.collect_from_existing_account(&pubkey, &mut account, false);
|
let collected =
|
||||||
|
rent_collector.collect_from_existing_account(&pubkey, &mut account, false, None);
|
||||||
assert_eq!(account.lamports(), tiny_lamports);
|
assert_eq!(account.lamports(), tiny_lamports);
|
||||||
assert_eq!(collected, 0);
|
assert_eq!(collected, 0);
|
||||||
|
|
||||||
// new behavior: sysvars are NOT special-cased
|
// new behavior: sysvars are NOT special-cased
|
||||||
let collected = rent_collector.collect_from_existing_account(&pubkey, &mut account, true);
|
let collected =
|
||||||
|
rent_collector.collect_from_existing_account(&pubkey, &mut account, true, None);
|
||||||
assert_eq!(account.lamports(), 0);
|
assert_eq!(account.lamports(), 0);
|
||||||
assert_eq!(collected, 1);
|
assert_eq!(collected, 1);
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,6 @@ use {
|
|||||||
clock::{Epoch, Slot, UnixTimestamp},
|
clock::{Epoch, Slot, UnixTimestamp},
|
||||||
epoch_schedule::EpochSchedule,
|
epoch_schedule::EpochSchedule,
|
||||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||||
genesis_config::ClusterType,
|
|
||||||
genesis_config::GenesisConfig,
|
genesis_config::GenesisConfig,
|
||||||
hard_forks::HardForks,
|
hard_forks::HardForks,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
@ -347,7 +346,7 @@ where
|
|||||||
snapshot_accounts_db_fields,
|
snapshot_accounts_db_fields,
|
||||||
account_paths,
|
account_paths,
|
||||||
unpacked_append_vec_map,
|
unpacked_append_vec_map,
|
||||||
&genesis_config.cluster_type,
|
genesis_config,
|
||||||
account_secondary_indexes,
|
account_secondary_indexes,
|
||||||
caching_enabled,
|
caching_enabled,
|
||||||
limit_load_slot_count_from_snapshot,
|
limit_load_slot_count_from_snapshot,
|
||||||
@ -402,7 +401,7 @@ fn reconstruct_accountsdb_from_fields<E>(
|
|||||||
snapshot_accounts_db_fields: SnapshotAccountsDbFields<E>,
|
snapshot_accounts_db_fields: SnapshotAccountsDbFields<E>,
|
||||||
account_paths: &[PathBuf],
|
account_paths: &[PathBuf],
|
||||||
unpacked_append_vec_map: UnpackedAppendVecMap,
|
unpacked_append_vec_map: UnpackedAppendVecMap,
|
||||||
cluster_type: &ClusterType,
|
genesis_config: &GenesisConfig,
|
||||||
account_secondary_indexes: AccountSecondaryIndexes,
|
account_secondary_indexes: AccountSecondaryIndexes,
|
||||||
caching_enabled: bool,
|
caching_enabled: bool,
|
||||||
limit_load_slot_count_from_snapshot: Option<usize>,
|
limit_load_slot_count_from_snapshot: Option<usize>,
|
||||||
@ -416,7 +415,7 @@ where
|
|||||||
{
|
{
|
||||||
let mut accounts_db = AccountsDb::new_with_config(
|
let mut accounts_db = AccountsDb::new_with_config(
|
||||||
account_paths.to_vec(),
|
account_paths.to_vec(),
|
||||||
cluster_type,
|
&genesis_config.cluster_type,
|
||||||
account_secondary_indexes,
|
account_secondary_indexes,
|
||||||
caching_enabled,
|
caching_enabled,
|
||||||
shrink_ratio,
|
shrink_ratio,
|
||||||
@ -536,6 +535,7 @@ where
|
|||||||
.write_version
|
.write_version
|
||||||
.fetch_add(snapshot_version, Ordering::Relaxed);
|
.fetch_add(snapshot_version, Ordering::Relaxed);
|
||||||
accounts_db.generate_index(limit_load_slot_count_from_snapshot, verify_index);
|
accounts_db.generate_index(limit_load_slot_count_from_snapshot, verify_index);
|
||||||
|
accounts_db.maybe_add_filler_accounts(genesis_config.ticks_per_slot());
|
||||||
|
|
||||||
let mut measure_notify = Measure::start("accounts_notify");
|
let mut measure_notify = Measure::start("accounts_notify");
|
||||||
accounts_db.notify_account_restore_from_snapshot();
|
accounts_db.notify_account_restore_from_snapshot();
|
||||||
|
@ -77,7 +77,10 @@ where
|
|||||||
snapshot_accounts_db_fields,
|
snapshot_accounts_db_fields,
|
||||||
account_paths,
|
account_paths,
|
||||||
unpacked_append_vec_map,
|
unpacked_append_vec_map,
|
||||||
&ClusterType::Development,
|
&GenesisConfig {
|
||||||
|
cluster_type: ClusterType::Development,
|
||||||
|
..GenesisConfig::default()
|
||||||
|
},
|
||||||
AccountSecondaryIndexes::default(),
|
AccountSecondaryIndexes::default(),
|
||||||
false,
|
false,
|
||||||
None,
|
None,
|
||||||
|
@ -1381,6 +1381,12 @@ pub fn main() {
|
|||||||
May be specified multiple times. \
|
May be specified multiple times. \
|
||||||
[default: [ledger]/accounts_index]"),
|
[default: [ledger]/accounts_index]"),
|
||||||
)
|
)
|
||||||
|
.arg(Arg::with_name("accounts_filler_count")
|
||||||
|
.long("accounts-filler-count")
|
||||||
|
.value_name("COUNT")
|
||||||
|
.validator(is_parsable::<usize>)
|
||||||
|
.takes_value(true)
|
||||||
|
.help("How many accounts to add to stress the system. Accounts are ignored in operations related to correctness."))
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("accounts_db_test_hash_calculation")
|
Arg::with_name("accounts_db_test_hash_calculation")
|
||||||
.long("accounts-db-test-hash-calculation")
|
.long("accounts-db-test-hash-calculation")
|
||||||
@ -1949,9 +1955,11 @@ pub fn main() {
|
|||||||
accounts_index_config.drives = Some(accounts_index_paths);
|
accounts_index_config.drives = Some(accounts_index_paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let filler_account_count = value_t!(matches, "accounts_filler_count", usize).ok();
|
||||||
let accounts_db_config = Some(AccountsDbConfig {
|
let accounts_db_config = Some(AccountsDbConfig {
|
||||||
index: Some(accounts_index_config),
|
index: Some(accounts_index_config),
|
||||||
accounts_hash_cache_path: Some(ledger_path.clone()),
|
accounts_hash_cache_path: Some(ledger_path.clone()),
|
||||||
|
filler_account_count,
|
||||||
});
|
});
|
||||||
|
|
||||||
let accountsdb_repl_service_config = if matches.is_present("enable_accountsdb_repl") {
|
let accountsdb_repl_service_config = if matches.is_present("enable_accountsdb_repl") {
|
||||||
|
Reference in New Issue
Block a user