Make account shrink configurable #17544 (backport #17778) (#18013)

* Make account shrink configurable #17544 (#17778)

1. Added both options for measuring space usage using total accounts usage and for individual store shrink ratio using an enum. Validator CLI options: --accounts-shrink-optimize-total-space and --accounts-shrink-ratio
2. Added code for selecting candidates based on total usage in a separate function select_candidates_by_total_usage
3. Added unit tests for the new functions added
4. The default implementations is kept at 0.8 shrink ratio with --accounts-shrink-optimize-total-space set to true

Fixes #17544

(cherry picked from commit 269d995832)

# Conflicts:
#	core/tests/snapshots.rs
#	ledger/src/bank_forks_utils.rs
#	runtime/src/accounts_db.rs
#	runtime/src/snapshot_utils.rs

* fix some merge errors

Co-authored-by: Lijun Wang <83639177+lijunwangs@users.noreply.github.com>
Co-authored-by: Jeff Washington (jwash) <wash678@gmail.com>
This commit is contained in:
mergify[bot]
2021-06-17 17:32:03 +00:00
committed by GitHub
parent b472dac6b3
commit e9c234d89f
15 changed files with 404 additions and 17 deletions

View File

@@ -6,6 +6,7 @@ use rayon::prelude::*;
use solana_measure::measure::Measure;
use solana_runtime::{
accounts::{create_test_accounts, update_accounts_bench, Accounts},
accounts_db::AccountShrinkThreshold,
accounts_index::AccountSecondaryIndexes,
ancestors::Ancestors,
};
@@ -64,6 +65,7 @@ fn main() {
&ClusterType::Testnet,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
println!("Creating {} accounts", num_accounts);
let mut create_time = Measure::start("create accounts");

View File

@@ -37,6 +37,7 @@ use solana_runtime::{
accounts_background_service::{
AbsRequestHandler, AbsRequestSender, AccountsBackgroundService, SnapshotRequestHandler,
},
accounts_db::AccountShrinkThreshold,
bank_forks::{BankForks, SnapshotConfig},
commitment::BlockCommitmentCache,
vote_sender_types::ReplayVoteSender,
@@ -88,6 +89,7 @@ pub struct TvuConfig {
pub rocksdb_compaction_interval: Option<u64>,
pub rocksdb_max_compaction_jitter: Option<u64>,
pub wait_for_vote_to_start_leader: bool,
pub accounts_shrink_ratio: AccountShrinkThreshold,
}
impl Tvu {

View File

@@ -52,6 +52,7 @@ use solana_rpc::{
transaction_status_service::TransactionStatusService,
};
use solana_runtime::{
accounts_db::AccountShrinkThreshold,
accounts_index::AccountSecondaryIndexes,
bank::Bank,
bank_forks::{BankForks, SnapshotConfig},
@@ -138,6 +139,7 @@ pub struct ValidatorConfig {
pub tpu_coalesce_ms: u64,
pub validator_exit: Arc<RwLock<Exit>>,
pub no_wait_for_vote_to_start_leader: bool,
pub accounts_shrink_ratio: AccountShrinkThreshold,
}
impl Default for ValidatorConfig {
@@ -194,6 +196,7 @@ impl Default for ValidatorConfig {
tpu_coalesce_ms: DEFAULT_TPU_COALESCE_MS,
validator_exit: Arc::new(RwLock::new(Exit::default())),
no_wait_for_vote_to_start_leader: true,
accounts_shrink_ratio: AccountShrinkThreshold::default(),
}
}
}
@@ -717,6 +720,7 @@ impl Validator {
rocksdb_compaction_interval: config.rocksdb_compaction_interval,
rocksdb_max_compaction_jitter: config.rocksdb_compaction_interval,
wait_for_vote_to_start_leader,
accounts_shrink_ratio: config.accounts_shrink_ratio,
},
&max_slots,
);
@@ -1093,6 +1097,7 @@ fn new_banks_from_ledger(
debug_keys: config.debug_keys.clone(),
account_indexes: config.account_indexes.clone(),
accounts_db_caching_enabled: config.accounts_db_caching_enabled,
shrink_ratio: config.accounts_shrink_ratio,
..blockstore_processor::ProcessOptions::default()
};

View File

@@ -106,6 +106,7 @@ mod tests {
None,
AccountSecondaryIndexes::default(),
false,
accounts_db::AccountShrinkThreshold::default(),
false,
);
bank0.freeze();
@@ -167,6 +168,7 @@ mod tests {
AccountSecondaryIndexes::default(),
false,
None,
accounts_db::AccountShrinkThreshold::default(),
check_hash_calculation,
)
.unwrap();

View File

@@ -142,6 +142,7 @@ fn load_from_snapshot(
process_options.account_indexes.clone(),
process_options.accounts_db_caching_enabled,
process_options.limit_load_slot_count_from_snapshot,
process_options.shrink_ratio,
process_options.accounts_db_test_hash_calculation,
)
.expect("Load from snapshot failed");

View File

@@ -16,6 +16,7 @@ use solana_measure::measure::Measure;
use solana_metrics::{datapoint_error, inc_new_counter_debug};
use solana_rayon_threadlimit::get_thread_count;
use solana_runtime::{
accounts_db::AccountShrinkThreshold,
accounts_index::AccountSecondaryIndexes,
bank::{
Bank, ExecuteTimings, InnerInstructionsList, RentDebits, TransactionBalancesSet,
@@ -374,6 +375,7 @@ pub struct ProcessOptions {
pub limit_load_slot_count_from_snapshot: Option<usize>,
pub allow_dead_slots: bool,
pub accounts_db_test_hash_calculation: bool,
pub shrink_ratio: AccountShrinkThreshold,
}
pub fn process_blockstore(
@@ -401,6 +403,7 @@ pub fn process_blockstore(
Some(&crate::builtins::get(opts.bpf_jit)),
opts.account_indexes.clone(),
opts.accounts_db_caching_enabled,
opts.shrink_ratio,
false,
);
let bank0 = Arc::new(bank0);
@@ -3097,6 +3100,7 @@ pub mod tests {
None,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
false,
);
*bank.epoch_schedule()

View File

@@ -56,6 +56,7 @@ pub fn safe_clone_config(config: &ValidatorConfig) -> ValidatorConfig {
validator_exit: Arc::new(RwLock::new(Exit::default())),
poh_hashes_per_batch: config.poh_hashes_per_batch,
no_wait_for_vote_to_start_leader: config.no_wait_for_vote_to_start_leader,
accounts_shrink_ratio: config.accounts_shrink_ratio,
}
}

View File

@@ -8,6 +8,7 @@ use rand::Rng;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use solana_runtime::{
accounts::{create_test_accounts, AccountAddressFilter, Accounts},
accounts_db::AccountShrinkThreshold,
accounts_index::AccountSecondaryIndexes,
ancestors::Ancestors,
bank::*,
@@ -59,6 +60,7 @@ fn test_accounts_create(bencher: &mut Bencher) {
None,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
false,
);
bencher.iter(|| {
@@ -79,6 +81,7 @@ fn test_accounts_squash(bencher: &mut Bencher) {
None,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
false,
));
let mut pubkeys: Vec<Pubkey> = vec![];
@@ -105,6 +108,7 @@ fn test_accounts_hash_bank_hash(bencher: &mut Bencher) {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
let mut pubkeys: Vec<Pubkey> = vec![];
let num_accounts = 60_000;
@@ -131,6 +135,7 @@ fn test_update_accounts_hash(bencher: &mut Bencher) {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
let mut pubkeys: Vec<Pubkey> = vec![];
create_test_accounts(&accounts, &mut pubkeys, 50_000, 0);
@@ -148,6 +153,7 @@ fn test_accounts_delta_hash(bencher: &mut Bencher) {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
let mut pubkeys: Vec<Pubkey> = vec![];
create_test_accounts(&accounts, &mut pubkeys, 100_000, 0);
@@ -164,6 +170,7 @@ fn bench_delete_dependencies(bencher: &mut Bencher) {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
let mut old_pubkey = Pubkey::default();
let zero_account = AccountSharedData::new(0, 0, AccountSharedData::default().owner());
@@ -197,6 +204,7 @@ fn store_accounts_with_possible_contention<F: 'static>(
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
));
let num_keys = 1000;
let slot = 0;
@@ -326,6 +334,7 @@ fn setup_bench_dashmap_iter() -> (Arc<Accounts>, DashMap<Pubkey, (AccountSharedD
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
));
let dashmap = DashMap::new();
@@ -380,6 +389,7 @@ fn bench_load_largest_accounts(b: &mut Bencher) {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
let mut rng = rand::thread_rng();
for _ in 0..10_000 {

View File

@@ -1,6 +1,7 @@
use crate::{
accounts_db::{
AccountsDb, BankHashInfo, ErrorCounters, LoadHint, LoadedAccount, ScanStorageResult,
AccountShrinkThreshold, AccountsDb, BankHashInfo, ErrorCounters, LoadHint, LoadedAccount,
ScanStorageResult,
},
accounts_index::{AccountSecondaryIndexes, IndexKey},
ancestors::Ancestors,
@@ -117,12 +118,17 @@ pub enum AccountAddressFilter {
}
impl Accounts {
pub fn new(paths: Vec<PathBuf>, cluster_type: &ClusterType) -> Self {
pub fn new(
paths: Vec<PathBuf>,
cluster_type: &ClusterType,
shrink_ratio: AccountShrinkThreshold,
) -> Self {
Self::new_with_config(
paths,
cluster_type,
AccountSecondaryIndexes::default(),
false,
shrink_ratio,
)
}
@@ -131,6 +137,7 @@ impl Accounts {
cluster_type: &ClusterType,
account_indexes: AccountSecondaryIndexes,
caching_enabled: bool,
shrink_ratio: AccountShrinkThreshold,
) -> Self {
Self {
accounts_db: Arc::new(AccountsDb::new_with_config(
@@ -138,6 +145,7 @@ impl Accounts {
cluster_type,
account_indexes,
caching_enabled,
shrink_ratio,
)),
account_locks: Mutex::new(AccountLocks::default()),
}
@@ -1125,6 +1133,7 @@ mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
for ka in ka.iter() {
accounts.store_slow_uncached(0, &ka.0, &ka.1);
@@ -1662,6 +1671,7 @@ mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
// Load accounts owned by various programs into AccountsDb
@@ -1690,6 +1700,7 @@ mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
let mut error_counters = ErrorCounters::default();
let ancestors = vec![(0, 0)].into_iter().collect();
@@ -1713,6 +1724,7 @@ mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
accounts.bank_hash_at(1);
}
@@ -1734,6 +1746,7 @@ mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
accounts.store_slow_uncached(0, &keypair0.pubkey(), &account0);
accounts.store_slow_uncached(0, &keypair1.pubkey(), &account1);
@@ -1860,6 +1873,7 @@ mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
accounts.store_slow_uncached(0, &keypair0.pubkey(), &account0);
accounts.store_slow_uncached(0, &keypair1.pubkey(), &account1);
@@ -2010,6 +2024,7 @@ mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
{
accounts
@@ -2062,6 +2077,7 @@ mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
let mut old_pubkey = Pubkey::default();
let zero_account = AccountSharedData::new(0, 0, AccountSharedData::default().owner());
@@ -2109,6 +2125,7 @@ mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
let instructions_key = solana_sdk::sysvar::instructions::id();
@@ -2394,6 +2411,7 @@ mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
let collected_accounts = accounts.collect_accounts_to_store(
txs.iter(),
@@ -2513,6 +2531,7 @@ mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
let collected_accounts = accounts.collect_accounts_to_store(
txs.iter(),
@@ -2547,6 +2566,7 @@ mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
let pubkey0 = Pubkey::new_unique();

View File

@@ -82,7 +82,6 @@ const SCAN_SLOT_PAR_ITER_THRESHOLD: usize = 4000;
pub const DEFAULT_FILE_SIZE: u64 = PAGE_SIZE * 1024;
pub const DEFAULT_NUM_THREADS: u32 = 8;
pub const DEFAULT_NUM_DIRS: u32 = 4;
pub const SHRINK_RATIO: f64 = 0.80;
// A specially reserved storage id just for entries in the cache, so that
// operations that take a storage entry can maintain a common interface
@@ -114,6 +113,31 @@ lazy_static! {
pub static ref FROZEN_ACCOUNT_PANIC: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
}
#[derive(Debug, Clone, Copy)]
pub enum AccountShrinkThreshold {
/// Measure the total space sparseness across all candididates
/// And select the candidiates by using the top sparse account storage entries to shrink.
/// The value is the overall shrink threshold measured as ratio of the total live bytes
/// over the total bytes.
TotalSpace { shrink_ratio: f64 },
/// Use the following option to shrink all stores whose alive ratio is below
/// the specified threshold.
IndividalStore { shrink_ratio: f64 },
}
pub const DEFAULT_ACCOUNTS_SHRINK_OPTIMIZE_TOTAL_SPACE: bool = true;
pub const DEFAULT_ACCOUNTS_SHRINK_RATIO: f64 = 0.80;
// The default extra account space in percentage from the ideal target
const DEFAULT_ACCOUNTS_SHRINK_THRESHOLD_OPTION: AccountShrinkThreshold =
AccountShrinkThreshold::TotalSpace {
shrink_ratio: DEFAULT_ACCOUNTS_SHRINK_RATIO,
};
impl Default for AccountShrinkThreshold {
fn default() -> AccountShrinkThreshold {
DEFAULT_ACCOUNTS_SHRINK_THRESHOLD_OPTION
}
}
pub enum ScanStorageResult<R, B> {
Cached(Vec<R>),
Stored(B),
@@ -896,6 +920,8 @@ pub struct AccountsDb {
/// by `remove_unrooted_slot()`. Used to ensure `remove_unrooted_slots(slots)`
/// can safely clear the set of unrooted slots `slots`.
remove_unrooted_slots_synchronization: RemoveUnrootedSlotsSynchronization,
shrink_ratio: AccountShrinkThreshold,
}
#[derive(Debug, Default)]
@@ -1339,6 +1365,7 @@ impl Default for AccountsDb {
load_limit: AtomicU64::default(),
is_bank_drop_callback_enabled: AtomicBool::default(),
remove_unrooted_slots_synchronization: RemoveUnrootedSlotsSynchronization::default(),
shrink_ratio: AccountShrinkThreshold::default(),
}
}
}
@@ -1353,6 +1380,7 @@ impl AccountsDb {
cluster_type,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
)
}
@@ -1361,6 +1389,7 @@ impl AccountsDb {
cluster_type: &ClusterType,
account_indexes: AccountSecondaryIndexes,
caching_enabled: bool,
shrink_ratio: AccountShrinkThreshold,
) -> Self {
let mut new = if !paths.is_empty() {
Self {
@@ -1369,6 +1398,7 @@ impl AccountsDb {
cluster_type: Some(*cluster_type),
account_indexes,
caching_enabled,
shrink_ratio,
..Self::default()
}
} else {
@@ -1381,6 +1411,7 @@ impl AccountsDb {
cluster_type: Some(*cluster_type),
account_indexes,
caching_enabled,
shrink_ratio,
..Self::default()
}
};
@@ -2316,15 +2347,105 @@ impl AccountsDb {
self.accounts_index.all_roots()
}
pub fn shrink_candidate_slots(&self) -> usize {
let shrink_slots = std::mem::take(&mut *self.shrink_candidate_slots.lock().unwrap());
let num_candidates = shrink_slots.len();
/// Given the input `ShrinkCandidates`, this function sorts the stores by their alive ratio
/// in increasing order with the most sparse entries in the front. It will then simulate the
/// shrinking by working on the most sparse entries first and if the overall alive ratio is
/// achieved, it will stop and return the filtered-down candidates.
fn select_candidates_by_total_usage(
&self,
shrink_slots: &ShrinkCandidates,
shrink_ratio: f64,
) -> ShrinkCandidates {
struct StoreUsageInfo {
slot: Slot,
alive_ratio: f64,
store: Arc<AccountStorageEntry>,
}
let mut measure = Measure::start("select_top_sparse_storage_entries-ms");
let mut store_usage: Vec<StoreUsageInfo> = Vec::with_capacity(shrink_slots.len());
let mut total_alive_bytes: u64 = 0;
let mut candidates_count: usize = 0;
let mut total_bytes: u64 = 0;
for (slot, slot_shrink_candidates) in shrink_slots {
candidates_count += slot_shrink_candidates.len();
for store in slot_shrink_candidates.values() {
total_alive_bytes += Self::page_align(store.alive_bytes() as u64);
total_bytes += store.total_bytes();
let alive_ratio = Self::page_align(store.alive_bytes() as u64) as f64
/ store.total_bytes() as f64;
store_usage.push(StoreUsageInfo {
slot: *slot,
alive_ratio,
store: store.clone(),
});
}
}
store_usage.sort_by(|a, b| {
a.alive_ratio
.partial_cmp(&b.alive_ratio)
.unwrap_or(std::cmp::Ordering::Equal)
});
// Working from the beginning of store_usage which are the most sparse and see when we can stop
// shrinking while still achieving the overall goals.
let mut shrink_slots: ShrinkCandidates = HashMap::new();
for usage in &store_usage {
let alive_ratio = (total_alive_bytes as f64) / (total_bytes as f64);
if alive_ratio > shrink_ratio {
// we have reached our goal, stop
debug!(
"Shrinking goal can be achieved at slot {:?}, total_alive_bytes: {:?} \
total_bytes: {:?}, alive_ratio: {:}, shrink_ratio: {:?}",
usage.slot, total_alive_bytes, total_bytes, alive_ratio, shrink_ratio
);
break;
}
let store = &usage.store;
let current_store_size = store.total_bytes();
let after_shrink_size = Self::page_align(store.alive_bytes() as u64);
let bytes_saved = current_store_size.saturating_sub(after_shrink_size);
total_bytes -= bytes_saved;
shrink_slots
.entry(usage.slot)
.or_default()
.insert(store.append_vec_id(), store.clone());
}
measure.stop();
inc_new_counter_info!(
"select_top_sparse_storage_entries-ms",
measure.as_ms() as usize
);
inc_new_counter_info!("select_top_sparse_storage_entries-seeds", candidates_count);
shrink_slots
}
pub fn shrink_candidate_slots(&self) -> usize {
let shrink_candidates_slots =
std::mem::take(&mut *self.shrink_candidate_slots.lock().unwrap());
let shrink_slots = {
if let AccountShrinkThreshold::TotalSpace { shrink_ratio } = self.shrink_ratio {
self.select_candidates_by_total_usage(&shrink_candidates_slots, shrink_ratio)
} else {
shrink_candidates_slots
}
};
let mut measure_shrink_all_candidates = Measure::start("shrink_all_candidate_slots-ms");
let num_candidates = shrink_slots.len();
let mut shrink_candidates_count: usize = 0;
for (slot, slot_shrink_candidates) in shrink_slots {
shrink_candidates_count += slot_shrink_candidates.len();
let mut measure = Measure::start("shrink_candidate_slots-ms");
self.do_shrink_slot_stores(slot, slot_shrink_candidates.values(), false);
measure.stop();
inc_new_counter_info!("shrink_candidate_slots-ms", measure.as_ms() as usize);
}
measure_shrink_all_candidates.stop();
inc_new_counter_info!(
"shrink_all_candidate_slots-ms",
measure_shrink_all_candidates.as_ms() as usize
);
inc_new_counter_info!("shrink_all_candidate_slots-count", shrink_candidates_count);
num_candidates
}
@@ -4968,6 +5089,18 @@ impl AccountsDb {
true
}
fn is_candidate_for_shrink(&self, store: &Arc<AccountStorageEntry>) -> bool {
match self.shrink_ratio {
AccountShrinkThreshold::TotalSpace { shrink_ratio: _ } => {
Self::page_align(store.alive_bytes() as u64) < store.total_bytes()
}
AccountShrinkThreshold::IndividalStore { shrink_ratio } => {
(Self::page_align(store.alive_bytes() as u64) as f64 / store.total_bytes() as f64)
< shrink_ratio
}
}
}
fn remove_dead_accounts(
&self,
reclaims: SlotSlice<AccountInfo>,
@@ -5003,9 +5136,7 @@ impl AccountsDb {
dead_slots.insert(*slot);
} else if self.caching_enabled
&& Self::is_shrinking_productive(*slot, &[store.clone()])
&& (Self::page_align(store.alive_bytes() as u64) as f64
/ store.total_bytes() as f64)
< SHRINK_RATIO
&& self.is_candidate_for_shrink(&store)
{
// Checking that this single storage entry is ready for shrinking,
// should be a sufficient indication that the slot is ready to be shrunk
@@ -7484,6 +7615,7 @@ pub mod tests {
&ClusterType::Development,
spl_token_mint_index_enabled(),
false,
AccountShrinkThreshold::default(),
);
let pubkey1 = solana_sdk::pubkey::new_rand();
let pubkey2 = solana_sdk::pubkey::new_rand();
@@ -9289,6 +9421,99 @@ pub mod tests {
);
}
#[test]
fn test_select_candidates_by_total_usage() {
solana_logger::setup();
// case 1: no candidates
let accounts = AccountsDb::new_single();
let mut candidates: ShrinkCandidates = HashMap::new();
let output_candidates =
accounts.select_candidates_by_total_usage(&candidates, DEFAULT_ACCOUNTS_SHRINK_RATIO);
assert_eq!(0, output_candidates.len());
// case 2: two candidates, only one selected
let dummy_path = Path::new("");
let dummy_slot = 12;
let dummy_size = 2 * PAGE_SIZE;
let dummy_id1 = 22;
let entry1 = Arc::new(AccountStorageEntry::new(
&dummy_path,
dummy_slot,
dummy_id1,
dummy_size,
));
entry1.alive_bytes.store(8000, Ordering::Relaxed);
candidates
.entry(dummy_slot)
.or_default()
.insert(entry1.append_vec_id(), entry1.clone());
let dummy_id2 = 44;
let entry2 = Arc::new(AccountStorageEntry::new(
&dummy_path,
dummy_slot,
dummy_id2,
dummy_size,
));
entry2.alive_bytes.store(3000, Ordering::Relaxed);
candidates
.entry(dummy_slot)
.or_default()
.insert(entry2.append_vec_id(), entry2.clone());
let output_candidates =
accounts.select_candidates_by_total_usage(&candidates, DEFAULT_ACCOUNTS_SHRINK_RATIO);
assert_eq!(1, output_candidates.len());
assert_eq!(1, output_candidates[&dummy_slot].len());
assert!(output_candidates[&dummy_slot].contains(&entry2.append_vec_id()));
// case 3: two candidates, both are selected
candidates.clear();
let dummy_size = 4 * PAGE_SIZE;
let dummy_id1 = 22;
let entry1 = Arc::new(AccountStorageEntry::new(
&dummy_path,
dummy_slot,
dummy_id1,
dummy_size,
));
entry1.alive_bytes.store(3500, Ordering::Relaxed);
candidates
.entry(dummy_slot)
.or_default()
.insert(entry1.append_vec_id(), entry1.clone());
let dummy_id2 = 44;
let dummy_slot2 = 44;
let entry2 = Arc::new(AccountStorageEntry::new(
&dummy_path,
dummy_slot2,
dummy_id2,
dummy_size,
));
entry2.alive_bytes.store(3000, Ordering::Relaxed);
candidates
.entry(dummy_slot2)
.or_default()
.insert(entry2.append_vec_id(), entry2.clone());
let output_candidates =
accounts.select_candidates_by_total_usage(&candidates, DEFAULT_ACCOUNTS_SHRINK_RATIO);
assert_eq!(2, output_candidates.len());
assert_eq!(1, output_candidates[&dummy_slot].len());
assert_eq!(1, output_candidates[&dummy_slot2].len());
assert!(output_candidates[&dummy_slot].contains(&entry1.append_vec_id()));
assert!(output_candidates[&dummy_slot2].contains(&entry2.append_vec_id()));
}
#[test]
fn test_shrink_stale_slots_skipped() {
solana_logger::setup();
@@ -9787,6 +10012,7 @@ pub mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
caching_enabled,
AccountShrinkThreshold::default(),
));
let account_key = Pubkey::new_unique();
@@ -9834,6 +10060,7 @@ pub mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
caching_enabled,
AccountShrinkThreshold::default(),
));
let account_key = Pubkey::new_unique();
@@ -9882,6 +10109,7 @@ pub mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
caching_enabled,
AccountShrinkThreshold::default(),
));
let zero_lamport_account_key = Pubkey::new_unique();
@@ -10013,6 +10241,7 @@ pub mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
caching_enabled,
AccountShrinkThreshold::default(),
));
let account_key = Pubkey::new_unique();
let account_key2 = Pubkey::new_unique();
@@ -10117,6 +10346,7 @@ pub mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
caching_enabled,
AccountShrinkThreshold::default(),
);
let slot: Slot = 0;
let num_keys = 10;
@@ -10171,6 +10401,7 @@ pub mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
caching_enabled,
AccountShrinkThreshold::default(),
));
let slots: Vec<_> = (0..num_slots as Slot).into_iter().collect();
let stall_slot = num_slots as Slot;
@@ -10569,6 +10800,7 @@ pub mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
caching_enabled,
AccountShrinkThreshold::default(),
);
let account_key1 = Pubkey::new_unique();
let account_key2 = Pubkey::new_unique();
@@ -10831,6 +11063,7 @@ pub mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
caching_enabled,
AccountShrinkThreshold::default(),
);
db.load_delay = RACY_SLEEP_MS;
let db = Arc::new(db);
@@ -10902,6 +11135,7 @@ pub mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
caching_enabled,
AccountShrinkThreshold::default(),
);
db.load_delay = RACY_SLEEP_MS;
let db = Arc::new(db);
@@ -10977,6 +11211,7 @@ pub mod tests {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
caching_enabled,
AccountShrinkThreshold::default(),
);
let db = Arc::new(db);
let num_cached_slots = 100;
@@ -11249,4 +11484,34 @@ pub mod tests {
let stores = vec![Arc::new(s1), Arc::new(s2)];
assert!(AccountsDb::is_shrinking_productive(0, &stores));
}
#[test]
fn test_is_candidate_for_shrink() {
solana_logger::setup();
let mut accounts = AccountsDb::new_single();
let dummy_path = Path::new("");
let dummy_size = 2 * PAGE_SIZE;
let entry = Arc::new(AccountStorageEntry::new(&dummy_path, 0, 1, dummy_size));
match accounts.shrink_ratio {
AccountShrinkThreshold::TotalSpace { shrink_ratio } => {
assert_eq!(
(DEFAULT_ACCOUNTS_SHRINK_RATIO * 100.) as u64,
(shrink_ratio * 100.) as u64
)
}
AccountShrinkThreshold::IndividalStore { shrink_ratio: _ } => {
panic!("Expect the default to be TotalSpace")
}
}
entry.alive_bytes.store(3000, Ordering::Relaxed);
assert!(accounts.is_candidate_for_shrink(&entry));
entry.alive_bytes.store(5000, Ordering::Relaxed);
assert!(!accounts.is_candidate_for_shrink(&entry));
accounts.shrink_ratio = AccountShrinkThreshold::TotalSpace { shrink_ratio: 0.3 };
entry.alive_bytes.store(3000, Ordering::Relaxed);
assert!(accounts.is_candidate_for_shrink(&entry));
accounts.shrink_ratio = AccountShrinkThreshold::IndividalStore { shrink_ratio: 0.3 };
assert!(!accounts.is_candidate_for_shrink(&entry));
}
}

View File

@@ -38,7 +38,7 @@ use crate::{
AccountAddressFilter, Accounts, TransactionAccountDeps, TransactionAccounts,
TransactionLoadResult, TransactionLoaders,
},
accounts_db::{ErrorCounters, SnapshotStorages},
accounts_db::{AccountShrinkThreshold, ErrorCounters, SnapshotStorages},
accounts_index::{AccountSecondaryIndexes, IndexKey},
ancestors::{Ancestors, AncestorsForSerialization},
blockhash_queue::BlockhashQueue,
@@ -1010,6 +1010,7 @@ impl Bank {
None,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
false,
)
}
@@ -1023,6 +1024,7 @@ impl Bank {
None,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
false,
);
@@ -1035,6 +1037,7 @@ impl Bank {
genesis_config: &GenesisConfig,
account_indexes: AccountSecondaryIndexes,
accounts_db_caching_enabled: bool,
shrink_ratio: AccountShrinkThreshold,
) -> Self {
Self::new_with_paths(
&genesis_config,
@@ -1044,6 +1047,7 @@ impl Bank {
None,
account_indexes,
accounts_db_caching_enabled,
shrink_ratio,
false,
)
}
@@ -1056,6 +1060,7 @@ impl Bank {
additional_builtins: Option<&Builtins>,
account_indexes: AccountSecondaryIndexes,
accounts_db_caching_enabled: bool,
shrink_ratio: AccountShrinkThreshold,
debug_do_not_add_builtins: bool,
) -> Self {
let mut bank = Self::default();
@@ -1068,6 +1073,7 @@ impl Bank {
&genesis_config.cluster_type,
account_indexes,
accounts_db_caching_enabled,
shrink_ratio,
));
bank.process_genesis_config(genesis_config);
bank.finish_init(
@@ -5316,7 +5322,7 @@ pub(crate) mod tests {
use super::*;
use crate::{
accounts_background_service::{AbsRequestHandler, SendDroppedBankCallback},
accounts_db::SHRINK_RATIO,
accounts_db::DEFAULT_ACCOUNTS_SHRINK_RATIO,
accounts_index::{AccountIndex, AccountMap, AccountSecondaryIndexes, ITER_BATCH_SIZE},
ancestors::Ancestors,
genesis_utils::{
@@ -9219,6 +9225,7 @@ pub(crate) mod tests {
&genesis_config,
account_indexes,
false,
AccountShrinkThreshold::default(),
));
let address = Pubkey::new_unique();
@@ -10668,6 +10675,7 @@ pub(crate) mod tests {
&genesis_config,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
));
bank0.restore_old_behavior_for_fragile_tests();
goto_end_of_slot(Arc::<Bank>::get_mut(&mut bank0).unwrap());
@@ -10679,11 +10687,13 @@ pub(crate) mod tests {
.accounts
.scan_slot(0, |stored_account| Some(stored_account.stored_size()));
// Create an account such that it takes SHRINK_RATIO of the total account space for
// Create an account such that it takes DEFAULT_ACCOUNTS_SHRINK_RATIO of the total account space for
// the slot, so when it gets pruned, the storage entry will become a shrink candidate.
let bank0_total_size: usize = sizes.into_iter().sum();
let pubkey0_size = (bank0_total_size as f64 / (1.0 - SHRINK_RATIO)).ceil();
assert!(pubkey0_size / (pubkey0_size + bank0_total_size as f64) > SHRINK_RATIO);
let pubkey0_size = (bank0_total_size as f64 / (1.0 - DEFAULT_ACCOUNTS_SHRINK_RATIO)).ceil();
assert!(
pubkey0_size / (pubkey0_size + bank0_total_size as f64) > DEFAULT_ACCOUNTS_SHRINK_RATIO
);
pubkey0_size as usize
}
@@ -10701,6 +10711,7 @@ pub(crate) mod tests {
&genesis_config,
AccountSecondaryIndexes::default(),
true,
AccountShrinkThreshold::default(),
));
bank0.restore_old_behavior_for_fragile_tests();
@@ -11920,6 +11931,7 @@ pub(crate) mod tests {
&genesis_config,
AccountSecondaryIndexes::default(),
accounts_db_caching_enabled,
AccountShrinkThreshold::default(),
));
bank0.set_callback(drop_callback);

View File

@@ -1,7 +1,9 @@
use {
crate::{
accounts::Accounts,
accounts_db::{AccountStorageEntry, AccountsDb, AppendVecId, BankHashInfo},
accounts_db::{
AccountShrinkThreshold, AccountStorageEntry, AccountsDb, AppendVecId, BankHashInfo,
},
accounts_index::AccountSecondaryIndexes,
ancestors::Ancestors,
append_vec::{AppendVec, StoredMetaWriteVersion},
@@ -137,6 +139,7 @@ pub(crate) fn bank_from_stream<R>(
account_indexes: AccountSecondaryIndexes,
caching_enabled: bool,
limit_load_slot_count_from_snapshot: Option<usize>,
shrink_ratio: AccountShrinkThreshold,
) -> std::result::Result<Bank, Error>
where
R: Read,
@@ -157,6 +160,7 @@ where
account_indexes,
caching_enabled,
limit_load_slot_count_from_snapshot,
shrink_ratio,
)?;
Ok(bank)
}};
@@ -247,6 +251,7 @@ fn reconstruct_bank_from_fields<E>(
account_indexes: AccountSecondaryIndexes,
caching_enabled: bool,
limit_load_slot_count_from_snapshot: Option<usize>,
shrink_ratio: AccountShrinkThreshold,
) -> Result<Bank, Error>
where
E: SerializableStorage + std::marker::Sync,
@@ -259,6 +264,7 @@ where
account_indexes,
caching_enabled,
limit_load_slot_count_from_snapshot,
shrink_ratio,
)?;
accounts_db.freeze_accounts(
&Ancestors::from(&bank_fields.ancestors),
@@ -289,6 +295,7 @@ fn reconstruct_accountsdb_from_fields<E>(
account_indexes: AccountSecondaryIndexes,
caching_enabled: bool,
limit_load_slot_count_from_snapshot: Option<usize>,
shrink_ratio: AccountShrinkThreshold,
) -> Result<AccountsDb, Error>
where
E: SerializableStorage + std::marker::Sync,
@@ -298,6 +305,7 @@ where
cluster_type,
account_indexes,
caching_enabled,
shrink_ratio,
);
let AccountsDbFields(storage, version, slot, bank_hash_info) = accounts_db_fields;

View File

@@ -3,7 +3,7 @@ use {
super::*,
crate::{
accounts::{create_test_accounts, Accounts},
accounts_db::get_temp_accounts_paths,
accounts_db::{get_temp_accounts_paths, AccountShrinkThreshold},
bank::{Bank, StatusCacheRc},
hardened_unpack::UnpackedAppendVecMap,
},
@@ -74,6 +74,7 @@ where
AccountSecondaryIndexes::default(),
false,
None,
AccountShrinkThreshold::default(),
)
}
@@ -129,6 +130,7 @@ fn test_accounts_serialize_style(serde_style: SerdeStyle) {
&ClusterType::Development,
AccountSecondaryIndexes::default(),
false,
AccountShrinkThreshold::default(),
);
let mut pubkeys: Vec<Pubkey> = vec![];
@@ -228,6 +230,7 @@ fn test_bank_serialize_style(serde_style: SerdeStyle) {
AccountSecondaryIndexes::default(),
false,
None,
AccountShrinkThreshold::default(),
)
.unwrap();
dbank.src = ref_sc;

View File

@@ -1,6 +1,6 @@
use {
crate::{
accounts_db::AccountsDb,
accounts_db::{AccountShrinkThreshold, AccountsDb},
accounts_index::AccountSecondaryIndexes,
bank::{Bank, BankSlotDelta, Builtins},
bank_forks::ArchiveFormat,
@@ -612,6 +612,7 @@ pub fn bank_from_archive<P: AsRef<Path>>(
account_indexes: AccountSecondaryIndexes,
accounts_db_caching_enabled: bool,
limit_load_slot_count_from_snapshot: Option<usize>,
shrink_ratio: AccountShrinkThreshold,
test_hash_calculation: bool,
) -> Result<(Bank, BankFromArchiveTimings)> {
let unpack_dir = tempfile::Builder::new()
@@ -646,6 +647,7 @@ pub fn bank_from_archive<P: AsRef<Path>>(
account_indexes,
accounts_db_caching_enabled,
limit_load_slot_count_from_snapshot,
shrink_ratio,
)?;
measure.stop();
@@ -814,6 +816,7 @@ fn rebuild_bank_from_snapshots(
account_indexes: AccountSecondaryIndexes,
accounts_db_caching_enabled: bool,
limit_load_slot_count_from_snapshot: Option<usize>,
shrink_ratio: AccountShrinkThreshold,
) -> Result<Bank> {
info!("snapshot version: {}", snapshot_version);
@@ -850,6 +853,7 @@ fn rebuild_bank_from_snapshots(
account_indexes,
accounts_db_caching_enabled,
limit_load_slot_count_from_snapshot,
shrink_ratio,
),
}?)
})?;

View File

@@ -39,6 +39,10 @@ use {
solana_poh::poh_service,
solana_rpc::{rpc::JsonRpcConfig, rpc_pubsub_service::PubSubConfig},
solana_runtime::{
accounts_db::{
AccountShrinkThreshold, DEFAULT_ACCOUNTS_SHRINK_OPTIMIZE_TOTAL_SPACE,
DEFAULT_ACCOUNTS_SHRINK_RATIO,
},
accounts_index::{
AccountIndex, AccountSecondaryIndexes, AccountSecondaryIndexesIncludeExclude,
},
@@ -1011,6 +1015,9 @@ pub fn main() {
let default_max_snapshot_to_retain = &DEFAULT_MAX_SNAPSHOTS_TO_RETAIN.to_string();
let default_min_snapshot_download_speed = &DEFAULT_MIN_SNAPSHOT_DOWNLOAD_SPEED.to_string();
let default_max_snapshot_download_abort = &MAX_SNAPSHOT_DOWNLOAD_ABORT.to_string();
let default_accounts_shrink_optimize_total_space =
&DEFAULT_ACCOUNTS_SHRINK_OPTIMIZE_TOTAL_SPACE.to_string();
let default_accounts_shrink_ratio = &DEFAULT_ACCOUNTS_SHRINK_RATIO.to_string();
let matches = App::new(crate_name!()).about(crate_description!())
.version(solana_version::version!())
@@ -1789,6 +1796,29 @@ pub fn main() {
.conflicts_with("no_accounts_db_caching")
.hidden(true)
)
.arg(
Arg::with_name("accounts_shrink_optimize_total_space")
.long("accounts-shrink-optimize-total-space")
.takes_value(true)
.value_name("BOOLEAN")
.default_value(default_accounts_shrink_optimize_total_space)
.help("When this is set to true, the system will shrink the most \
sparse accounts and when the overall shrink ratio is above \
the specified accounts-shrink-ratio, the shrink will stop and \
it will skip all other less sparse accounts."),
)
.arg(
Arg::with_name("accounts_shrink_ratio")
.long("accounts-shrink-ratio")
.takes_value(true)
.value_name("RATIO")
.default_value(default_accounts_shrink_ratio)
.help("Specifies the shrink ratio for the accounts to be shrunk. \
The shrink ratio is defined as the ratio of the bytes alive over the \
total bytes used. If the account's shrink ratio is less than this ratio \
it becomes a candidate for shrinking. The value must between 0. and 1.0 \
inclusive."),
)
.arg(
Arg::with_name("no_duplicate_instance_check")
.long("no-duplicate-instance-check")
@@ -2087,6 +2117,23 @@ pub fn main() {
let account_indexes = process_account_indexes(&matches);
let restricted_repair_only_mode = matches.is_present("restricted_repair_only_mode");
let accounts_shrink_optimize_total_space =
value_t_or_exit!(matches, "accounts_shrink_optimize_total_space", bool);
let shrink_ratio = value_t_or_exit!(matches, "accounts_shrink_ratio", f64);
if !(0.0..=1.0).contains(&shrink_ratio) {
eprintln!(
"The specified account-shrink-ratio is invalid, it must be between 0. and 1.0 inclusive: {}",
shrink_ratio
);
exit(1);
}
let accounts_shrink_ratio = if accounts_shrink_optimize_total_space {
AccountShrinkThreshold::TotalSpace { shrink_ratio }
} else {
AccountShrinkThreshold::IndividalStore { shrink_ratio }
};
let mut validator_config = ValidatorConfig {
require_tower: matches.is_present("require_tower"),
tower_path: value_t!(matches, "tower", PathBuf).ok(),
@@ -2189,6 +2236,7 @@ pub fn main() {
accounts_db_use_index_hash_calculation: matches.is_present("accounts_db_index_hashing"),
tpu_coalesce_ms,
no_wait_for_vote_to_start_leader: matches.is_present("no_wait_for_vote_to_start_leader"),
accounts_shrink_ratio,
..ValidatorConfig::default()
};