diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index 2b1291561..a40eff3d0 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -2251,7 +2251,7 @@ fn main() { if remove_stake_accounts { for (address, mut account) in bank - .get_program_accounts(&stake::program::id(), ScanConfig::default()) + .get_program_accounts(&stake::program::id(), &ScanConfig::default()) .unwrap() .into_iter() { @@ -2275,7 +2275,7 @@ fn main() { if !vote_accounts_to_destake.is_empty() { for (address, mut account) in bank - .get_program_accounts(&stake::program::id(), ScanConfig::default()) + .get_program_accounts(&stake::program::id(), &ScanConfig::default()) .unwrap() .into_iter() { @@ -2313,7 +2313,10 @@ fn main() { // Delete existing vote accounts for (address, mut account) in bank - .get_program_accounts(&solana_vote_program::id(), ScanConfig::default()) + .get_program_accounts( + &solana_vote_program::id(), + &ScanConfig::default(), + ) .unwrap() .into_iter() { diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index a26243517..e299ea11f 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -1840,14 +1840,16 @@ impl JsonRpcRequestProcessor { // accounts. account.owner() == program_id && filter_closure(account) }, - ScanConfig::default(), + &ScanConfig::default(), + bank.byte_limit_for_scans(), ) .map_err(|e| RpcCustomError::ScanError { message: e.to_string(), })?) } else { + // this path does not need to provide a mb limit because we only want to support secondary indexes Ok(bank - .get_filtered_program_accounts(program_id, filter_closure, ScanConfig::default()) + .get_filtered_program_accounts(program_id, filter_closure, &ScanConfig::default()) .map_err(|e| RpcCustomError::ScanError { message: e.to_string(), })?) @@ -1901,7 +1903,8 @@ impl JsonRpcRequestProcessor { } }) }, - ScanConfig::default(), + &ScanConfig::default(), + bank.byte_limit_for_scans(), ) .map_err(|e| RpcCustomError::ScanError { message: e.to_string(), @@ -1957,7 +1960,8 @@ impl JsonRpcRequestProcessor { } }) }, - ScanConfig::default(), + &ScanConfig::default(), + bank.byte_limit_for_scans(), ) .map_err(|e| RpcCustomError::ScanError { message: e.to_string(), diff --git a/runtime/benches/accounts.rs b/runtime/benches/accounts.rs index 84e2565a4..37e0189be 100644 --- a/runtime/benches/accounts.rs +++ b/runtime/benches/accounts.rs @@ -267,7 +267,7 @@ fn bench_concurrent_scan_write(bencher: &mut Bencher) { &Ancestors::default(), 0, AccountSharedData::default().owner(), - ScanConfig::default(), + &ScanConfig::default(), ) .unwrap(), ); diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index 862ae920a..732c77b7e 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -4,7 +4,7 @@ use crate::{ LoadHint, LoadedAccount, ScanStorageResult, ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS, ACCOUNTS_DB_CONFIG_FOR_TESTING, }, - accounts_index::{AccountSecondaryIndexes, IndexKey, ScanConfig, ScanResult}, + accounts_index::{AccountSecondaryIndexes, IndexKey, ScanConfig, ScanError, ScanResult}, accounts_update_notifier_interface::AccountsUpdateNotifier, ancestors::Ancestors, bank::{ @@ -41,6 +41,7 @@ use std::{ collections::{hash_map, BinaryHeap, HashMap, HashSet}, ops::RangeBounds, path::PathBuf, + sync::atomic::{AtomicUsize, Ordering}, sync::{Arc, Mutex}, }; @@ -665,7 +666,7 @@ impl Accounts { collector.push(Reverse((account.lamports(), *pubkey))); } }, - ScanConfig::default(), + &ScanConfig::default(), )?; Ok(account_balances .into_sorted_vec() @@ -743,7 +744,7 @@ impl Accounts { ancestors: &Ancestors, bank_id: BankId, program_id: &Pubkey, - config: ScanConfig, + config: &ScanConfig, ) -> ScanResult> { self.accounts_db.scan_accounts( ancestors, @@ -763,7 +764,7 @@ impl Accounts { bank_id: BankId, program_id: &Pubkey, filter: F, - config: ScanConfig, + config: &ScanConfig, ) -> ScanResult> { self.accounts_db.scan_accounts( ancestors, @@ -783,21 +784,51 @@ impl Accounts { bank_id: BankId, index_key: &IndexKey, filter: F, - config: ScanConfig, + config: &ScanConfig, + byte_limit_for_scan: Option, ) -> ScanResult> { - self.accounts_db + let sum = AtomicUsize::default(); + let config = ScanConfig { + abort: Some(config.abort.as_ref().map(Arc::clone).unwrap_or_default()), + collect_all_unsorted: config.collect_all_unsorted, + }; + let result = self + .accounts_db .index_scan_accounts( ancestors, bank_id, *index_key, |collector: &mut Vec<(Pubkey, AccountSharedData)>, some_account_tuple| { Self::load_while_filtering(collector, some_account_tuple, |account| { - filter(account) - }) + let use_account = filter(account); + if use_account { + if let Some(byte_limit_for_scan) = byte_limit_for_scan.as_ref() { + let added = account.data().len() + + std::mem::size_of::() + + std::mem::size_of::(); + if sum + .fetch_add(added, Ordering::Relaxed) + .saturating_add(added) + > *byte_limit_for_scan + { + // total size of results exceeds size limit, so abort scan + config.abort(); + } + } + } + use_account + }); }, - config, + &config, ) - .map(|result| result.0) + .map(|result| result.0); + if config.is_aborted() { + ScanResult::Err(ScanError::Aborted( + "The accumulated scan results exceeded the limit".to_string(), + )) + } else { + result + } } pub fn account_indexes_include_key(&self, key: &Pubkey) -> bool { @@ -819,7 +850,7 @@ impl Accounts { collector.push((*pubkey, account, slot)) } }, - ScanConfig::default(), + &ScanConfig::default(), ) } @@ -841,7 +872,7 @@ impl Accounts { "load_to_collect_rent_eagerly_scan_elapsed", ancestors, range, - ScanConfig::new(true), + &ScanConfig::new(true), |collector: &mut Vec<(Pubkey, AccountSharedData)>, option| { Self::load_while_filtering(collector, option, |_| true) }, diff --git a/runtime/src/accounts_db.rs b/runtime/src/accounts_db.rs index 9cc936cec..e10c93448 100644 --- a/runtime/src/accounts_db.rs +++ b/runtime/src/accounts_db.rs @@ -3060,7 +3060,7 @@ impl AccountsDb { ancestors: &Ancestors, bank_id: BankId, scan_func: F, - config: ScanConfig, + config: &ScanConfig, ) -> ScanResult where F: Fn(&mut A, Option<(&Pubkey, AccountSharedData, Slot)>), @@ -3090,7 +3090,7 @@ impl AccountsDb { metric_name: &'static str, ancestors: &Ancestors, scan_func: F, - config: ScanConfig, + config: &ScanConfig, ) -> A where F: Fn(&mut A, (&Pubkey, LoadedAccount, Slot)), @@ -3118,7 +3118,7 @@ impl AccountsDb { metric_name: &'static str, ancestors: &Ancestors, range: R, - config: ScanConfig, + config: &ScanConfig, scan_func: F, ) -> A where @@ -3159,7 +3159,7 @@ impl AccountsDb { bank_id: BankId, index_key: IndexKey, scan_func: F, - config: ScanConfig, + config: &ScanConfig, ) -> ScanResult<(A, bool)> where F: Fn(&mut A, Option<(&Pubkey, AccountSharedData, Slot)>), @@ -8049,7 +8049,7 @@ pub mod tests { |accounts: &mut Vec, option| { accounts.push(option.1.take_account()); }, - ScanConfig::default(), + &ScanConfig::default(), ); assert_eq!(accounts, vec![account1]); } @@ -8959,7 +8959,7 @@ pub mod tests { |key, _| { found_accounts.insert(*key); }, - ScanConfig::default(), + &ScanConfig::default(), ) .unwrap(); assert_eq!(found_accounts.len(), 2); @@ -8980,7 +8980,7 @@ pub mod tests { |collection: &mut HashSet, account| { collection.insert(*account.unwrap().0); }, - ScanConfig::default(), + &ScanConfig::default(), ) .unwrap(); assert!(!found_accounts.1); @@ -8999,7 +8999,7 @@ pub mod tests { |collection: &mut HashSet, account| { collection.insert(*account.unwrap().0); }, - ScanConfig::default(), + &ScanConfig::default(), ) .unwrap(); assert!(found_accounts.1); @@ -9033,7 +9033,7 @@ pub mod tests { bank_id, IndexKey::SplTokenMint(mint_key), |key, _| found_accounts.push(*key), - ScanConfig::default(), + &ScanConfig::default(), ) .unwrap(); assert_eq!(found_accounts, vec![pubkey2]); @@ -9589,7 +9589,7 @@ pub mod tests { |accounts: &mut Vec, option| { accounts.push(option.1.take_account()); }, - ScanConfig::default(), + &ScanConfig::default(), ); assert_eq!(accounts, vec![account0]); @@ -9600,7 +9600,7 @@ pub mod tests { |accounts: &mut Vec, option| { accounts.push(option.1.take_account()); }, - ScanConfig::default(), + &ScanConfig::default(), ); assert_eq!(accounts.len(), 2); } @@ -11774,7 +11774,7 @@ pub mod tests { } } }, - ScanConfig::default(), + &ScanConfig::default(), ) .unwrap(); }) diff --git a/runtime/src/accounts_index.rs b/runtime/src/accounts_index.rs index 2caedca0f..6c94874ce 100644 --- a/runtime/src/accounts_index.rs +++ b/runtime/src/accounts_index.rs @@ -45,6 +45,7 @@ pub const ACCOUNTS_INDEX_CONFIG_FOR_TESTING: AccountsIndexConfig = AccountsIndex drives: None, index_limit_mb: Some(1), ages_to_stay_in_cache: None, + scan_results_limit_bytes: None, }; pub const ACCOUNTS_INDEX_CONFIG_FOR_BENCHMARKS: AccountsIndexConfig = AccountsIndexConfig { bins: Some(BINS_FOR_BENCHMARKS), @@ -52,6 +53,7 @@ pub const ACCOUNTS_INDEX_CONFIG_FOR_BENCHMARKS: AccountsIndexConfig = AccountsIn drives: None, index_limit_mb: None, ages_to_stay_in_cache: None, + scan_results_limit_bytes: None, }; pub type ScanResult = Result; pub type SlotList = Vec<(Slot, T)>; @@ -62,11 +64,11 @@ pub type AccountMap = Arc>; #[derive(Debug, Default)] pub struct ScanConfig { /// checked by the scan. When true, abort scan. - abort: Option>, + pub abort: Option>, /// true to allow return of all matching items and allow them to be unsorted. /// This is more efficient. - collect_all_unsorted: bool, + pub collect_all_unsorted: bool, } impl ScanConfig { @@ -77,6 +79,12 @@ impl ScanConfig { } } + pub fn abort(&self) { + if let Some(abort) = self.abort.as_ref() { + abort.store(true, Ordering::Relaxed) + } + } + /// true if scan should abort pub fn is_aborted(&self) -> bool { if let Some(abort) = self.abort.as_ref() { @@ -104,6 +112,8 @@ pub trait IndexValue: pub enum ScanError { #[error("Node detected it replayed bad version of slot {slot:?} with id {bank_id:?}, thus the scan on said slot was aborted")] SlotRemoved { slot: Slot, bank_id: BankId }, + #[error("scan aborted: {0}")] + Aborted(String), } enum ScanTypes> { @@ -138,6 +148,7 @@ pub struct AccountsIndexConfig { pub drives: Option>, pub index_limit_mb: Option, pub ages_to_stay_in_cache: Option, + pub scan_results_limit_bytes: Option, } #[derive(Debug, Default, Clone)] @@ -802,6 +813,9 @@ pub struct AccountsIndex { pub removed_bank_ids: Mutex>, storage: AccountsIndexStorage, + + /// when a scan's accumulated data exceeds this limit, abort the scan + pub scan_results_limit_bytes: Option, } impl AccountsIndex { @@ -810,6 +824,9 @@ impl AccountsIndex { } pub fn new(config: Option) -> Self { + let scan_results_limit_bytes = config + .as_ref() + .and_then(|config| config.scan_results_limit_bytes); let (account_maps, bin_calculator, storage) = Self::allocate_accounts_index(config); Self { account_maps, @@ -827,6 +844,7 @@ impl AccountsIndex { ongoing_scan_roots: RwLock::>::default(), removed_bank_ids: Mutex::>::default(), storage, + scan_results_limit_bytes, } } @@ -865,7 +883,7 @@ impl AccountsIndex { scan_bank_id: BankId, func: F, scan_type: ScanTypes, - config: ScanConfig, + config: &ScanConfig, ) -> Result<(), ScanError> where F: FnMut(&Pubkey, (&T, Slot)), @@ -1085,7 +1103,7 @@ impl AccountsIndex { ancestors: &Ancestors, func: F, range: Option, - config: ScanConfig, + config: &ScanConfig, ) where F: FnMut(&Pubkey, (&T, Slot)), R: RangeBounds + std::fmt::Debug, @@ -1103,7 +1121,7 @@ impl AccountsIndex { mut func: F, range: Option, max_root: Option, - config: ScanConfig, + config: &ScanConfig, ) where F: FnMut(&Pubkey, (&T, Slot)), R: RangeBounds + std::fmt::Debug, @@ -1166,7 +1184,7 @@ impl AccountsIndex { index: &SecondaryIndex, index_key: &Pubkey, max_root: Option, - config: ScanConfig, + config: &ScanConfig, ) where F: FnMut(&Pubkey, (&T, Slot)), { @@ -1236,7 +1254,7 @@ impl AccountsIndex { ancestors: &Ancestors, scan_bank_id: BankId, func: F, - config: ScanConfig, + config: &ScanConfig, ) -> Result<(), ScanError> where F: FnMut(&Pubkey, (&T, Slot)), @@ -1257,7 +1275,7 @@ impl AccountsIndex { metric_name: &'static str, ancestors: &Ancestors, func: F, - config: ScanConfig, + config: &ScanConfig, ) where F: FnMut(&Pubkey, (&T, Slot)), { @@ -1276,7 +1294,7 @@ impl AccountsIndex { metric_name: &'static str, ancestors: &Ancestors, range: R, - config: ScanConfig, + config: &ScanConfig, func: F, ) where F: FnMut(&Pubkey, (&T, Slot)), @@ -1293,7 +1311,7 @@ impl AccountsIndex { scan_bank_id: BankId, index_key: IndexKey, func: F, - config: ScanConfig, + config: &ScanConfig, ) -> Result<(), ScanError> where F: FnMut(&Pubkey, (&T, Slot)), @@ -2693,7 +2711,7 @@ pub mod tests { "", &ancestors, |_pubkey, _index| num += 1, - ScanConfig::default(), + &ScanConfig::default(), ); assert_eq!(num, 0); } @@ -2771,7 +2789,7 @@ pub mod tests { "", &ancestors, |_pubkey, _index| num += 1, - ScanConfig::default(), + &ScanConfig::default(), ); assert_eq!(num, 0); } @@ -2810,7 +2828,7 @@ pub mod tests { "", &ancestors, |_pubkey, _index| num += 1, - ScanConfig::default(), + &ScanConfig::default(), ); assert_eq!(num, 0); ancestors.insert(slot, 0); @@ -2820,7 +2838,7 @@ pub mod tests { "", &ancestors, |_pubkey, _index| num += 1, - ScanConfig::default(), + &ScanConfig::default(), ); assert_eq!(num, 1); @@ -2839,7 +2857,7 @@ pub mod tests { "", &ancestors, |_pubkey, _index| num += 1, - ScanConfig::default(), + &ScanConfig::default(), ); assert_eq!(num, 0); ancestors.insert(slot, 0); @@ -2849,7 +2867,7 @@ pub mod tests { "", &ancestors, |_pubkey, _index| num += 1, - ScanConfig::default(), + &ScanConfig::default(), ); assert_eq!(num, 1); } @@ -3036,7 +3054,7 @@ pub mod tests { "", &ancestors, |_pubkey, _index| num += 1, - ScanConfig::default(), + &ScanConfig::default(), ); assert_eq!(num, 0); ancestors.insert(slot, 0); @@ -3045,7 +3063,7 @@ pub mod tests { "", &ancestors, |_pubkey, _index| num += 1, - ScanConfig::default(), + &ScanConfig::default(), ); assert_eq!(num, 1); } @@ -3075,7 +3093,7 @@ pub mod tests { "", &ancestors, |_pubkey, _index| num += 1, - ScanConfig::default(), + &ScanConfig::default(), ); assert_eq!(num, 0); } @@ -3112,7 +3130,7 @@ pub mod tests { }; num += 1 }, - ScanConfig::default(), + &ScanConfig::default(), ); assert_eq!(num, 1); assert!(found_key); @@ -3185,7 +3203,7 @@ pub mod tests { "", &ancestors, pubkey_range, - ScanConfig::default(), + &ScanConfig::default(), |pubkey, _index| { scanned_keys.insert(*pubkey); }, @@ -3264,7 +3282,7 @@ pub mod tests { |pubkey, _index| { scanned_keys.insert(*pubkey); }, - ScanConfig::default(), + &ScanConfig::default(), ); assert_eq!(scanned_keys.len(), num_pubkeys); } @@ -3570,7 +3588,7 @@ pub mod tests { }; num += 1 }, - ScanConfig::default(), + &ScanConfig::default(), ); assert_eq!(num, 1); assert!(found_key); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index defa110cf..73c539534 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -1504,6 +1504,14 @@ impl Bank { new } + pub fn byte_limit_for_scans(&self) -> Option { + self.rc + .accounts + .accounts_db + .accounts_index + .scan_results_limit_bytes + } + /// Returns all ancestors excluding self.slot. pub(crate) fn proper_ancestors(&self) -> impl Iterator + '_ { self.ancestors @@ -5241,7 +5249,7 @@ impl Bank { pub fn get_program_accounts( &self, program_id: &Pubkey, - config: ScanConfig, + config: &ScanConfig, ) -> ScanResult> { self.rc .accounts @@ -5252,7 +5260,7 @@ impl Bank { &self, program_id: &Pubkey, filter: F, - config: ScanConfig, + config: &ScanConfig, ) -> ScanResult> { self.rc.accounts.load_by_program_with_filter( &self.ancestors, @@ -5267,7 +5275,8 @@ impl Bank { &self, index_key: &IndexKey, filter: F, - config: ScanConfig, + config: &ScanConfig, + byte_limit_for_scan: Option, ) -> ScanResult> { self.rc.accounts.load_by_index_key_with_filter( &self.ancestors, @@ -5275,6 +5284,7 @@ impl Bank { index_key, filter, config, + byte_limit_for_scan, ) } @@ -10485,13 +10495,13 @@ pub(crate) mod tests { bank1.squash(); assert_eq!( bank0 - .get_program_accounts(&program_id, ScanConfig::default(),) + .get_program_accounts(&program_id, &ScanConfig::default(),) .unwrap(), vec![(pubkey0, account0.clone())] ); assert_eq!( bank1 - .get_program_accounts(&program_id, ScanConfig::default(),) + .get_program_accounts(&program_id, &ScanConfig::default(),) .unwrap(), vec![(pubkey0, account0)] ); @@ -10513,14 +10523,14 @@ pub(crate) mod tests { bank3.squash(); assert_eq!( bank1 - .get_program_accounts(&program_id, ScanConfig::default(),) + .get_program_accounts(&program_id, &ScanConfig::default(),) .unwrap() .len(), 2 ); assert_eq!( bank3 - .get_program_accounts(&program_id, ScanConfig::default(),) + .get_program_accounts(&program_id, &ScanConfig::default(),) .unwrap() .len(), 2 @@ -10548,7 +10558,8 @@ pub(crate) mod tests { .get_filtered_indexed_accounts( &IndexKey::ProgramId(program_id), |_| true, - ScanConfig::default(), + &ScanConfig::default(), + None, ) .unwrap(); assert_eq!(indexed_accounts.len(), 1); @@ -10565,7 +10576,8 @@ pub(crate) mod tests { .get_filtered_indexed_accounts( &IndexKey::ProgramId(program_id), |_| true, - ScanConfig::default(), + &ScanConfig::default(), + None, ) .unwrap(); assert_eq!(indexed_accounts.len(), 1); @@ -10574,7 +10586,8 @@ pub(crate) mod tests { .get_filtered_indexed_accounts( &IndexKey::ProgramId(another_program_id), |_| true, - ScanConfig::default(), + &ScanConfig::default(), + None, ) .unwrap(); assert_eq!(indexed_accounts.len(), 1); @@ -10585,7 +10598,8 @@ pub(crate) mod tests { .get_filtered_indexed_accounts( &IndexKey::ProgramId(program_id), |account| account.owner() == &program_id, - ScanConfig::default(), + &ScanConfig::default(), + None, ) .unwrap(); assert!(indexed_accounts.is_empty()); @@ -10593,7 +10607,8 @@ pub(crate) mod tests { .get_filtered_indexed_accounts( &IndexKey::ProgramId(another_program_id), |account| account.owner() == &another_program_id, - ScanConfig::default(), + &ScanConfig::default(), + None, ) .unwrap(); assert_eq!(indexed_accounts.len(), 1); @@ -13228,7 +13243,7 @@ pub(crate) mod tests { Bank::new_from_parent(&bank1, &Pubkey::default(), bank1.first_slot_in_next_epoch()); assert_eq!( bank2 - .get_program_accounts(&sysvar::id(), ScanConfig::default(),) + .get_program_accounts(&sysvar::id(), &ScanConfig::default(),) .unwrap() .len(), 8 @@ -13240,7 +13255,7 @@ pub(crate) mod tests { // no sysvar should be deleted due to rent assert_eq!( bank2 - .get_program_accounts(&sysvar::id(), ScanConfig::default(),) + .get_program_accounts(&sysvar::id(), &ScanConfig::default(),) .unwrap() .len(), 8 @@ -13278,7 +13293,7 @@ pub(crate) mod tests { { let sysvars = bank1 - .get_program_accounts(&sysvar::id(), ScanConfig::default()) + .get_program_accounts(&sysvar::id(), &ScanConfig::default()) .unwrap(); assert_eq!(sysvars.len(), 8); assert!(sysvars @@ -13315,7 +13330,7 @@ pub(crate) mod tests { { let sysvars = bank2 - .get_program_accounts(&sysvar::id(), ScanConfig::default()) + .get_program_accounts(&sysvar::id(), &ScanConfig::default()) .unwrap(); assert_eq!(sysvars.len(), 8); assert!(sysvars @@ -13394,7 +13409,7 @@ pub(crate) mod tests { ); { let sysvars = bank1 - .get_program_accounts(&sysvar::id(), ScanConfig::default()) + .get_program_accounts(&sysvar::id(), &ScanConfig::default()) .unwrap(); assert_eq!(sysvars.len(), 9); assert!(sysvars @@ -13431,7 +13446,7 @@ pub(crate) mod tests { ); { let sysvars = bank2 - .get_program_accounts(&sysvar::id(), ScanConfig::default()) + .get_program_accounts(&sysvar::id(), &ScanConfig::default()) .unwrap(); assert_eq!(sysvars.len(), 9); assert!(sysvars @@ -13802,7 +13817,7 @@ pub(crate) mod tests { { info!("scanning program accounts for slot {}", bank_to_scan.slot()); let accounts_result = bank_to_scan - .get_program_accounts(&program_id, ScanConfig::default()); + .get_program_accounts(&program_id, &ScanConfig::default()); let _ = scan_finished_sender.send(bank_to_scan.bank_id()); num_banks_scanned.fetch_add(1, Relaxed); match (&acceptable_scan_results, accounts_result.is_err()) { diff --git a/runtime/src/non_circulating_supply.rs b/runtime/src/non_circulating_supply.rs index ae53f65ad..6d66a6b48 100644 --- a/runtime/src/non_circulating_supply.rs +++ b/runtime/src/non_circulating_supply.rs @@ -28,7 +28,7 @@ pub fn calculate_non_circulating_supply(bank: &Arc) -> ScanResult) -> ScanResult) + .takes_value(true) + .help("How large accumulated results from an accounts index scan can become. If this is exceeded, the scan aborts."), + ) .arg( Arg::with_name("accounts_index_memory_limit_mb") .long("accounts-index-memory-limit-mb") @@ -2117,6 +2125,12 @@ pub fn main() { accounts_index_config.drives = Some(accounts_index_paths); } + const MB: usize = 1_024 * 1_024; + accounts_index_config.scan_results_limit_bytes = + value_t!(matches, "accounts_index_scan_results_limit_mb", usize) + .ok() + .map(|mb| mb * MB); + let filler_account_count = value_t!(matches, "accounts_filler_count", usize).ok(); let mut accounts_db_config = AccountsDbConfig { index: Some(accounts_index_config),