Replace executor cache usage with LoadedPrograms cache (#31462)

* Replace executor cache usage with LoadedPrograms cache

* clippy fixes

* update cache with updated programs

* fixes

* more cleanup

* update tx batch cache with the tx results

* address review comments

* handle program closing backward compatibility

* handle unloaded programs during extraction
This commit is contained in:
Pankaj Garg 2023-05-09 15:44:38 -07:00 committed by GitHub
parent fb7ba97afc
commit 2210af60ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 233 additions and 129 deletions

View File

@ -15,7 +15,9 @@ use {
}, },
solana_program_runtime::{ solana_program_runtime::{
invoke_context::InvokeContext, invoke_context::InvokeContext,
loaded_programs::{LoadProgramMetrics, LoadedProgram, LoadedProgramType}, loaded_programs::{
LoadProgramMetrics, LoadedProgram, LoadedProgramType, DELAY_VISIBILITY_SLOT_OFFSET,
},
with_mock_invoke_context, with_mock_invoke_context,
}, },
solana_rbpf::{ solana_rbpf::{
@ -416,6 +418,9 @@ pub fn run(ledger_path: &Path, matches: &ArgMatches<'_>) {
bank.get_builtin_programs() bank.get_builtin_programs()
); );
// Adding `DELAY_VISIBILITY_SLOT_OFFSET` to slots to accommodate for delay visibility of the program
let mut loaded_programs =
LoadedProgramsForTxBatch::new(bank.slot() + DELAY_VISIBILITY_SLOT_OFFSET);
for key in cached_account_keys { for key in cached_account_keys {
let program = bank.load_program(&key, true).unwrap_or_else(|err| { let program = bank.load_program(&key, true).unwrap_or_else(|err| {
// Create a tombstone for the program in the cache // Create a tombstone for the program in the cache
@ -426,11 +431,9 @@ pub fn run(ledger_path: &Path, matches: &ArgMatches<'_>) {
)) ))
}); });
debug!("Loaded program {}", key); debug!("Loaded program {}", key);
invoke_context loaded_programs.replenish(key, program);
.tx_executor_cache
.borrow_mut()
.set(key, program, false, false, 0);
} }
invoke_context.programs_loaded_for_tx_batch = Rc::new(RefCell::new(loaded_programs));
invoke_context invoke_context
.transaction_context .transaction_context

View File

@ -3,9 +3,8 @@ use {
accounts_data_meter::AccountsDataMeter, accounts_data_meter::AccountsDataMeter,
builtin_program::{BuiltinPrograms, ProcessInstructionWithContext}, builtin_program::{BuiltinPrograms, ProcessInstructionWithContext},
compute_budget::ComputeBudget, compute_budget::ComputeBudget,
executor_cache::TransactionExecutorCache,
ic_logger_msg, ic_msg, ic_logger_msg, ic_msg,
loaded_programs::LoadedProgramType, loaded_programs::{LoadedProgramType, LoadedProgramsForTxBatch},
log_collector::LogCollector, log_collector::LogCollector,
pre_account::PreAccount, pre_account::PreAccount,
stable_log, stable_log,
@ -160,7 +159,9 @@ pub struct InvokeContext<'a> {
current_compute_budget: ComputeBudget, current_compute_budget: ComputeBudget,
compute_meter: RefCell<u64>, compute_meter: RefCell<u64>,
accounts_data_meter: AccountsDataMeter, accounts_data_meter: AccountsDataMeter,
pub tx_executor_cache: Rc<RefCell<TransactionExecutorCache>>, pub programs_loaded_for_tx_batch: Rc<RefCell<LoadedProgramsForTxBatch>>,
pub programs_modified_by_tx: Rc<RefCell<LoadedProgramsForTxBatch>>,
pub programs_updated_only_for_global_cache: Rc<RefCell<LoadedProgramsForTxBatch>>,
pub feature_set: Arc<FeatureSet>, pub feature_set: Arc<FeatureSet>,
pub timings: ExecuteDetailsTimings, pub timings: ExecuteDetailsTimings,
pub blockhash: Hash, pub blockhash: Hash,
@ -178,7 +179,9 @@ impl<'a> InvokeContext<'a> {
sysvar_cache: &'a SysvarCache, sysvar_cache: &'a SysvarCache,
log_collector: Option<Rc<RefCell<LogCollector>>>, log_collector: Option<Rc<RefCell<LogCollector>>>,
compute_budget: ComputeBudget, compute_budget: ComputeBudget,
tx_executor_cache: Rc<RefCell<TransactionExecutorCache>>, programs_loaded_for_tx_batch: Rc<RefCell<LoadedProgramsForTxBatch>>,
programs_modified_by_tx: Rc<RefCell<LoadedProgramsForTxBatch>>,
programs_updated_only_for_global_cache: Rc<RefCell<LoadedProgramsForTxBatch>>,
feature_set: Arc<FeatureSet>, feature_set: Arc<FeatureSet>,
blockhash: Hash, blockhash: Hash,
lamports_per_signature: u64, lamports_per_signature: u64,
@ -195,7 +198,9 @@ impl<'a> InvokeContext<'a> {
compute_budget, compute_budget,
compute_meter: RefCell::new(compute_budget.compute_unit_limit), compute_meter: RefCell::new(compute_budget.compute_unit_limit),
accounts_data_meter: AccountsDataMeter::new(prev_accounts_data_len), accounts_data_meter: AccountsDataMeter::new(prev_accounts_data_len),
tx_executor_cache, programs_loaded_for_tx_batch,
programs_modified_by_tx,
programs_updated_only_for_global_cache,
feature_set, feature_set,
timings: ExecuteDetailsTimings::default(), timings: ExecuteDetailsTimings::default(),
blockhash, blockhash,
@ -902,8 +907,8 @@ macro_rules! with_mock_invoke_context_and_builtin_programs {
}, },
std::{cell::RefCell, rc::Rc, sync::Arc}, std::{cell::RefCell, rc::Rc, sync::Arc},
$crate::{ $crate::{
compute_budget::ComputeBudget, executor_cache::TransactionExecutorCache, compute_budget::ComputeBudget, invoke_context::InvokeContext,
invoke_context::InvokeContext, log_collector::LogCollector, loaded_programs::LoadedProgramsForTxBatch, log_collector::LogCollector,
sysvar_cache::SysvarCache, sysvar_cache::SysvarCache,
}, },
}; };
@ -940,7 +945,9 @@ macro_rules! with_mock_invoke_context_and_builtin_programs {
&sysvar_cache, &sysvar_cache,
Some(LogCollector::new_ref()), Some(LogCollector::new_ref()),
compute_budget, compute_budget,
Rc::new(RefCell::new(TransactionExecutorCache::default())), Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
Hash::default(), Hash::default(),
0, 0,

View File

@ -281,9 +281,16 @@ pub struct LoadedProgramsForTxBatch {
} }
impl LoadedProgramsForTxBatch { impl LoadedProgramsForTxBatch {
pub fn new(slot: Slot) -> Self {
Self {
entries: HashMap::new(),
slot,
}
}
/// Refill the cache with a single entry. It's typically called during transaction loading, and /// Refill the cache with a single entry. It's typically called during transaction loading, and
/// transaction processing (for program management instructions). /// transaction processing (for program management instructions).
/// The replaces the existing entry (if any) with the provided entry. The return value contains /// It replaces the existing entry (if any) with the provided entry. The return value contains
/// `true` if an entry existed. /// `true` if an entry existed.
/// The function also returns the newly inserted value. /// The function also returns the newly inserted value.
pub fn replenish( pub fn replenish(
@ -291,7 +298,6 @@ impl LoadedProgramsForTxBatch {
key: Pubkey, key: Pubkey,
entry: Arc<LoadedProgram>, entry: Arc<LoadedProgram>,
) -> (bool, Arc<LoadedProgram>) { ) -> (bool, Arc<LoadedProgram>) {
debug_assert!(entry.effective_slot <= self.slot);
(self.entries.insert(key, entry.clone()).is_some(), entry) (self.entries.insert(key, entry.clone()).is_some(), entry)
} }
@ -314,6 +320,16 @@ impl LoadedProgramsForTxBatch {
pub fn slot(&self) -> Slot { pub fn slot(&self) -> Slot {
self.slot self.slot
} }
pub fn set_slot_for_tests(&mut self, slot: Slot) {
self.slot = slot;
}
pub fn merge(&mut self, other: &Self) {
other.entries.iter().for_each(|(key, entry)| {
self.replenish(*key, entry.clone());
})
}
} }
pub enum LoadedProgramMatchCriteria { pub enum LoadedProgramMatchCriteria {
@ -453,6 +469,12 @@ impl LoadedPrograms {
return None; return None;
} }
if matches!(entry.program, LoadedProgramType::Unloaded) {
// The program was unloaded. Consider it as missing, so it can be reloaded.
missing.push(key);
return None;
}
if current_slot >= entry.effective_slot { if current_slot >= entry.effective_slot {
return Some((key, entry.clone())); return Some((key, entry.clone()));
} else if entry.is_implicit_delay_visibility_tombstone(current_slot) { } else if entry.is_implicit_delay_visibility_tombstone(current_slot) {
@ -483,6 +505,12 @@ impl LoadedPrograms {
) )
} }
pub fn merge(&mut self, tx_batch_cache: &LoadedProgramsForTxBatch) {
tx_batch_cache.entries.iter().for_each(|(key, entry)| {
self.replenish(*key, entry.clone());
})
}
/// Unloads programs which were used infrequently /// Unloads programs which were used infrequently
pub fn sort_and_unload(&mut self, shrink_to: PercentageInteger) { pub fn sort_and_unload(&mut self, shrink_to: PercentageInteger) {
let sorted_candidates: Vec<(Pubkey, Arc<LoadedProgram>)> = self let sorted_candidates: Vec<(Pubkey, Arc<LoadedProgram>)> = self

View File

@ -10,7 +10,9 @@ use {
compute_budget::ComputeBudget, compute_budget::ComputeBudget,
ic_logger_msg, ic_msg, ic_logger_msg, ic_msg,
invoke_context::{BpfAllocator, InvokeContext, SyscallContext}, invoke_context::{BpfAllocator, InvokeContext, SyscallContext},
loaded_programs::{LoadProgramMetrics, LoadedProgram, LoadedProgramType}, loaded_programs::{
LoadProgramMetrics, LoadedProgram, LoadedProgramType, DELAY_VISIBILITY_SLOT_OFFSET,
},
log_collector::LogCollector, log_collector::LogCollector,
stable_log, stable_log,
sysvar_cache::get_sysvar_with_account_check, sysvar_cache::get_sysvar_with_account_check,
@ -91,7 +93,7 @@ pub fn load_program_from_bytes(
register_syscalls_time.stop(); register_syscalls_time.stop();
load_program_metrics.register_syscalls_us = register_syscalls_time.as_us(); load_program_metrics.register_syscalls_us = register_syscalls_time.as_us();
let effective_slot = if feature_set.is_active(&delay_visibility_of_program_deployment::id()) { let effective_slot = if feature_set.is_active(&delay_visibility_of_program_deployment::id()) {
deployment_slot.saturating_add(1) deployment_slot.saturating_add(DELAY_VISIBILITY_SLOT_OFFSET)
} else { } else {
deployment_slot deployment_slot
}; };
@ -190,12 +192,27 @@ pub fn load_program_from_account(
Ok((loaded_program, Some(load_program_metrics))) Ok((loaded_program, Some(load_program_metrics)))
} }
fn find_program_in_cache(
invoke_context: &InvokeContext,
pubkey: &Pubkey,
) -> Option<Arc<LoadedProgram>> {
// First lookup the cache of the programs modified by the current transaction. If not found, lookup
// the cache of the cache of the programs that are loaded for the transaction batch.
invoke_context
.programs_modified_by_tx
.borrow()
.find(pubkey)
.or_else(|| {
invoke_context
.programs_loaded_for_tx_batch
.borrow()
.find(pubkey)
})
}
macro_rules! deploy_program { macro_rules! deploy_program {
($invoke_context:expr, $program_id:expr, $loader_key:expr, ($invoke_context:expr, $program_id:expr, $loader_key:expr,
$account_size:expr, $slot:expr, $drop:expr, $new_programdata:expr $(,)?) => {{ $account_size:expr, $slot:expr, $drop:expr, $new_programdata:expr $(,)?) => {{
let delay_visibility_of_program_deployment = $invoke_context
.feature_set
.is_active(&delay_visibility_of_program_deployment::id());
let mut load_program_metrics = LoadProgramMetrics::default(); let mut load_program_metrics = LoadProgramMetrics::default();
let executor = load_program_from_bytes( let executor = load_program_from_bytes(
&$invoke_context.feature_set, &$invoke_context.feature_set,
@ -209,20 +226,14 @@ macro_rules! deploy_program {
true, /* reject_deployment_of_broken_elfs */ true, /* reject_deployment_of_broken_elfs */
false, /* debugging_features */ false, /* debugging_features */
)?; )?;
if let Some(old_entry) = $invoke_context.tx_executor_cache.borrow().get(&$program_id) { if let Some(old_entry) = find_program_in_cache($invoke_context, &$program_id) {
let usage_counter = old_entry.usage_counter.load(Ordering::Relaxed); let usage_counter = old_entry.usage_counter.load(Ordering::Relaxed);
executor.usage_counter.store(usage_counter, Ordering::Relaxed); executor.usage_counter.store(usage_counter, Ordering::Relaxed);
} }
$drop $drop
load_program_metrics.program_id = $program_id.to_string(); load_program_metrics.program_id = $program_id.to_string();
load_program_metrics.submit_datapoint(&mut $invoke_context.timings); load_program_metrics.submit_datapoint(&mut $invoke_context.timings);
$invoke_context.tx_executor_cache.borrow_mut().set( $invoke_context.programs_modified_by_tx.borrow_mut().replenish($program_id, Arc::new(executor));
$program_id,
Arc::new(executor),
true,
delay_visibility_of_program_deployment,
$slot,
);
}}; }};
} }
@ -562,10 +573,7 @@ fn process_instruction_inner(
} }
let mut get_or_create_executor_time = Measure::start("get_or_create_executor_time"); let mut get_or_create_executor_time = Measure::start("get_or_create_executor_time");
let executor = invoke_context let executor = find_program_in_cache(invoke_context, program_account.get_key())
.tx_executor_cache
.borrow()
.get(program_account.get_key())
.ok_or(InstructionError::InvalidAccountData)?; .ok_or(InstructionError::InvalidAccountData)?;
if executor.is_tombstone() { if executor.is_tombstone() {
@ -1265,15 +1273,32 @@ fn process_loader_upgradeable_instruction(
instruction_context, instruction_context,
&log_collector, &log_collector,
)?; )?;
let clock = invoke_context.get_sysvar_cache().get_clock()?;
if invoke_context if invoke_context
.feature_set .feature_set
.is_active(&delay_visibility_of_program_deployment::id()) .is_active(&delay_visibility_of_program_deployment::id())
{ {
let clock = invoke_context.get_sysvar_cache().get_clock()?;
invoke_context invoke_context
.tx_executor_cache .programs_modified_by_tx
.borrow_mut() .borrow_mut()
.set_tombstone(program_key, clock.slot); .replenish(
program_key,
Arc::new(LoadedProgram::new_tombstone(
clock.slot,
LoadedProgramType::Closed,
)),
);
} else {
invoke_context
.programs_updated_only_for_global_cache
.borrow_mut()
.replenish(
program_key,
Arc::new(LoadedProgram::new_tombstone(
clock.slot,
LoadedProgramType::Closed,
)),
);
} }
} }
_ => { _ => {
@ -1661,7 +1686,10 @@ fn execute<'a, 'b: 'a>(
} }
pub mod test_utils { pub mod test_utils {
use {super::*, solana_sdk::account::ReadableAccount}; use {
super::*, solana_program_runtime::loaded_programs::DELAY_VISIBILITY_SLOT_OFFSET,
solana_sdk::account::ReadableAccount,
};
pub fn load_all_invoked_programs(invoke_context: &mut InvokeContext) { pub fn load_all_invoked_programs(invoke_context: &mut InvokeContext) {
let num_accounts = invoke_context.transaction_context.get_number_of_accounts(); let num_accounts = invoke_context.transaction_context.get_number_of_accounts();
@ -1693,8 +1721,9 @@ pub mod test_utils {
true, true,
false, false,
) { ) {
let mut cache = invoke_context.tx_executor_cache.borrow_mut(); let mut cache = invoke_context.programs_modified_by_tx.borrow_mut();
cache.set(*pubkey, Arc::new(loaded_program), true, false, 0) cache.set_slot_for_tests(DELAY_VISIBILITY_SLOT_OFFSET);
cache.replenish(*pubkey, Arc::new(loaded_program));
} }
} }
} }
@ -4096,13 +4125,10 @@ mod tests {
maybe_expiration_slot: None, maybe_expiration_slot: None,
usage_counter: AtomicU64::new(100), usage_counter: AtomicU64::new(100),
}; };
invoke_context.tx_executor_cache.borrow_mut().set( invoke_context
program_id, .programs_modified_by_tx
Arc::new(program), .borrow_mut()
false, .replenish(program_id, Arc::new(program));
false,
0,
);
assert!(matches!( assert!(matches!(
deploy_test_program(&mut invoke_context, program_id,), deploy_test_program(&mut invoke_context, program_id,),
@ -4110,9 +4136,9 @@ mod tests {
)); ));
let updated_program = invoke_context let updated_program = invoke_context
.tx_executor_cache .programs_modified_by_tx
.borrow() .borrow()
.get(&program_id) .find(&program_id)
.expect("Didn't find upgraded program in the cache"); .expect("Didn't find upgraded program in the cache");
assert_eq!(updated_program.deployment_slot, 2); assert_eq!(updated_program.deployment_slot, 2);
@ -4132,13 +4158,10 @@ mod tests {
maybe_expiration_slot: None, maybe_expiration_slot: None,
usage_counter: AtomicU64::new(100), usage_counter: AtomicU64::new(100),
}; };
invoke_context.tx_executor_cache.borrow_mut().set( invoke_context
program_id, .programs_modified_by_tx
Arc::new(program), .borrow_mut()
false, .replenish(program_id, Arc::new(program));
false,
0,
);
let program_id2 = Pubkey::new_unique(); let program_id2 = Pubkey::new_unique();
assert!(matches!( assert!(matches!(
@ -4147,9 +4170,9 @@ mod tests {
)); ));
let program2 = invoke_context let program2 = invoke_context
.tx_executor_cache .programs_modified_by_tx
.borrow() .borrow()
.get(&program_id2) .find(&program_id2)
.expect("Didn't find upgraded program in the cache"); .expect("Didn't find upgraded program in the cache");
assert_eq!(program2.deployment_slot, 2); assert_eq!(program2.deployment_slot, 2);

View File

@ -379,7 +379,10 @@ fn test_program_sbf_loader_deprecated() {
let bank = Bank::new_for_tests(&genesis_config); let bank = Bank::new_for_tests(&genesis_config);
let program_id = create_program(&bank, &bpf_loader_deprecated::id(), program); let program_id = create_program(&bank, &bpf_loader_deprecated::id(), program);
let bank_client = BankClient::new(bank); let mut bank_client = BankClient::new(bank);
bank_client
.advance_slot(1, &Pubkey::default())
.expect("Failed to advance the slot");
let account_metas = vec![AccountMeta::new(mint_keypair.pubkey(), true)]; let account_metas = vec![AccountMeta::new(mint_keypair.pubkey(), true)];
let instruction = Instruction::new_with_bytes(program_id, &[1], account_metas); let instruction = Instruction::new_with_bytes(program_id, &[1], account_metas);
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
@ -2065,8 +2068,10 @@ fn test_program_sbf_invoke_in_same_tx_as_redeployment() {
], ],
); );
// load_upgradeable_program sets clock sysvar to 1, which causes the program to be effective
// after 2 slots. So we need to advance the bank client by 2 slots here.
let bank = bank_client let bank = bank_client
.advance_slot(1, &Pubkey::default()) .advance_slot(2, &Pubkey::default())
.expect("Failed to advance slot"); .expect("Failed to advance slot");
// Prepare redeployment // Prepare redeployment
@ -2160,8 +2165,10 @@ fn test_program_sbf_invoke_in_same_tx_as_undeployment() {
], ],
); );
// load_upgradeable_program sets clock sysvar to 1, which causes the program to be effective
// after 2 slots. So we need to advance the bank client by 2 slots here.
let bank = bank_client let bank = bank_client
.advance_slot(1, &Pubkey::default()) .advance_slot(2, &Pubkey::default())
.expect("Failed to advance slot"); .expect("Failed to advance slot");
// Prepare undeployment // Prepare undeployment
@ -3904,7 +3911,10 @@ fn test_program_sbf_inner_instruction_alignment_checks() {
// invoke unaligned program, which will call aligned program twice, // invoke unaligned program, which will call aligned program twice,
// unaligned should be allowed once invoke completes // unaligned should be allowed once invoke completes
let bank_client = BankClient::new(bank); let mut bank_client = BankClient::new(bank);
bank_client
.advance_slot(1, &Pubkey::default())
.expect("Failed to advance the slot");
let mut instruction = Instruction::new_with_bytes( let mut instruction = Instruction::new_with_bytes(
inner_instruction_alignment_check, inner_instruction_alignment_check,
&[0], &[0],

View File

@ -26,7 +26,7 @@ use {
solana_address_lookup_table_program::{error::AddressLookupError, state::AddressLookupTable}, solana_address_lookup_table_program::{error::AddressLookupError, state::AddressLookupTable},
solana_program_runtime::{ solana_program_runtime::{
compute_budget::{self, ComputeBudget}, compute_budget::{self, ComputeBudget},
loaded_programs::{LoadedProgram, LoadedProgramType}, loaded_programs::{LoadedProgram, LoadedProgramType, LoadedProgramsForTxBatch},
}, },
solana_sdk::{ solana_sdk::{
account::{Account, AccountSharedData, ReadableAccount, WritableAccount}, account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
@ -337,7 +337,7 @@ impl Accounts {
feature_set: &FeatureSet, feature_set: &FeatureSet,
account_overrides: Option<&AccountOverrides>, account_overrides: Option<&AccountOverrides>,
program_accounts: &HashMap<Pubkey, &Pubkey>, program_accounts: &HashMap<Pubkey, &Pubkey>,
loaded_programs: &HashMap<Pubkey, Arc<LoadedProgram>>, loaded_programs: &LoadedProgramsForTxBatch,
) -> Result<LoadedTransaction> { ) -> Result<LoadedTransaction> {
// NOTE: this check will never fail because `tx` is sanitized // NOTE: this check will never fail because `tx` is sanitized
if tx.signatures().is_empty() && fee != 0 { if tx.signatures().is_empty() && fee != 0 {
@ -390,7 +390,7 @@ impl Accounts {
(account_override.data().len(), account_override.clone(), 0) (account_override.data().len(), account_override.clone(), 0)
} else if let Some(program) = (!instruction_account && !message.is_writable(i)) } else if let Some(program) = (!instruction_account && !message.is_writable(i))
.then_some(()) .then_some(())
.and_then(|_| loaded_programs.get(key)) .and_then(|_| loaded_programs.find(key))
{ {
// This condition block does special handling for accounts that are passed // This condition block does special handling for accounts that are passed
// as instruction account to any of the instructions in the transaction. // as instruction account to any of the instructions in the transaction.
@ -401,7 +401,7 @@ impl Accounts {
Self::account_shared_data_from_program( Self::account_shared_data_from_program(
key, key,
feature_set, feature_set,
program, program.as_ref(),
program_accounts, program_accounts,
) )
.map(|program_account| (program.account_size, program_account, 0))? .map(|program_account| (program.account_size, program_account, 0))?
@ -695,7 +695,7 @@ impl Accounts {
fee_structure: &FeeStructure, fee_structure: &FeeStructure,
account_overrides: Option<&AccountOverrides>, account_overrides: Option<&AccountOverrides>,
program_accounts: &HashMap<Pubkey, &Pubkey>, program_accounts: &HashMap<Pubkey, &Pubkey>,
loaded_programs: &HashMap<Pubkey, Arc<LoadedProgram>>, loaded_programs: &LoadedProgramsForTxBatch,
) -> Vec<TransactionLoadResult> { ) -> Vec<TransactionLoadResult> {
txs.iter() txs.iter()
.zip(lock_results) .zip(lock_results)
@ -1470,9 +1470,8 @@ mod tests {
}, },
assert_matches::assert_matches, assert_matches::assert_matches,
solana_address_lookup_table_program::state::LookupTableMeta, solana_address_lookup_table_program::state::LookupTableMeta,
solana_program_runtime::{ solana_program_runtime::prioritization_fee::{
executor_cache::TransactionExecutorCache, PrioritizationFeeDetails, PrioritizationFeeType,
prioritization_fee::{PrioritizationFeeDetails, PrioritizationFeeType},
}, },
solana_sdk::{ solana_sdk::{
account::{AccountSharedData, WritableAccount}, account::{AccountSharedData, WritableAccount},
@ -1525,7 +1524,10 @@ mod tests {
executed_units: 0, executed_units: 0,
accounts_data_len_delta: 0, accounts_data_len_delta: 0,
}, },
tx_executor_cache: Rc::new(RefCell::new(TransactionExecutorCache::default())), programs_modified_by_tx: Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
programs_updated_only_for_global_cache: Rc::new(RefCell::new(
LoadedProgramsForTxBatch::default(),
)),
} }
} }
@ -1563,7 +1565,7 @@ mod tests {
fee_structure, fee_structure,
None, None,
&HashMap::new(), &HashMap::new(),
&HashMap::new(), &LoadedProgramsForTxBatch::default(),
) )
} }
@ -3367,7 +3369,7 @@ mod tests {
&FeeStructure::default(), &FeeStructure::default(),
account_overrides, account_overrides,
&HashMap::new(), &HashMap::new(),
&HashMap::new(), &LoadedProgramsForTxBatch::default(),
) )
} }

View File

@ -33,6 +33,8 @@
//! It offers a high-level API that signs transactions //! It offers a high-level API that signs transactions
//! on behalf of the caller, and a low-level API for when they have //! on behalf of the caller, and a low-level API for when they have
//! already been signed and verified. //! already been signed and verified.
#[cfg(test)]
use solana_program_runtime::executor_cache::TransactionExecutorCache;
#[allow(deprecated)] #[allow(deprecated)]
use solana_sdk::recent_blockhashes_account; use solana_sdk::recent_blockhashes_account;
pub use solana_sdk::reward_type::RewardType; pub use solana_sdk::reward_type::RewardType;
@ -95,7 +97,7 @@ use {
accounts_data_meter::MAX_ACCOUNTS_DATA_LEN, accounts_data_meter::MAX_ACCOUNTS_DATA_LEN,
builtin_program::BuiltinPrograms, builtin_program::BuiltinPrograms,
compute_budget::{self, ComputeBudget}, compute_budget::{self, ComputeBudget},
executor_cache::{BankExecutorCache, TransactionExecutorCache, MAX_CACHED_EXECUTORS}, executor_cache::{BankExecutorCache, MAX_CACHED_EXECUTORS},
loaded_programs::{ loaded_programs::{
LoadedProgram, LoadedProgramMatchCriteria, LoadedProgramType, LoadedPrograms, LoadedProgram, LoadedProgramMatchCriteria, LoadedProgramType, LoadedPrograms,
LoadedProgramsForTxBatch, WorkingSlot, LoadedProgramsForTxBatch, WorkingSlot,
@ -321,7 +323,8 @@ pub struct TransactionExecutionDetails {
pub enum TransactionExecutionResult { pub enum TransactionExecutionResult {
Executed { Executed {
details: TransactionExecutionDetails, details: TransactionExecutionDetails,
tx_executor_cache: Rc<RefCell<TransactionExecutorCache>>, programs_modified_by_tx: Rc<RefCell<LoadedProgramsForTxBatch>>,
programs_updated_only_for_global_cache: Rc<RefCell<LoadedProgramsForTxBatch>>,
}, },
NotExecuted(TransactionError), NotExecuted(TransactionError),
} }
@ -4108,6 +4111,7 @@ impl Bank {
} }
/// Add executors back to the bank's cache if they were missing and not re-/deployed /// Add executors back to the bank's cache if they were missing and not re-/deployed
#[cfg(test)]
fn store_executors_which_added_to_the_cache( fn store_executors_which_added_to_the_cache(
&self, &self,
tx_executor_cache: &RefCell<TransactionExecutorCache>, tx_executor_cache: &RefCell<TransactionExecutorCache>,
@ -4124,6 +4128,7 @@ impl Bank {
} }
/// Add re-/deployed executors to the bank's cache /// Add re-/deployed executors to the bank's cache
#[cfg(test)]
fn store_executors_which_were_deployed( fn store_executors_which_were_deployed(
&self, &self,
tx_executor_cache: &RefCell<TransactionExecutorCache>, tx_executor_cache: &RefCell<TransactionExecutorCache>,
@ -4174,7 +4179,6 @@ impl Bank {
} }
} }
#[allow(dead_code)] // Preparation for BankExecutorCache rework
pub fn load_program( pub fn load_program(
&self, &self,
pubkey: &Pubkey, pubkey: &Pubkey,
@ -4263,7 +4267,7 @@ impl Bank {
timings: &mut ExecuteTimings, timings: &mut ExecuteTimings,
error_counters: &mut TransactionErrorMetrics, error_counters: &mut TransactionErrorMetrics,
log_messages_bytes_limit: Option<usize>, log_messages_bytes_limit: Option<usize>,
tx_executor_cache: Rc<RefCell<TransactionExecutorCache>>, programs_loaded_for_tx_batch: Rc<RefCell<LoadedProgramsForTxBatch>>,
) -> TransactionExecutionResult { ) -> TransactionExecutionResult {
let prev_accounts_data_len = self.load_accounts_data_size(); let prev_accounts_data_len = self.load_accounts_data_size();
let transaction_accounts = std::mem::take(&mut loaded_transaction.accounts); let transaction_accounts = std::mem::take(&mut loaded_transaction.accounts);
@ -4313,7 +4317,10 @@ impl Bank {
let (blockhash, lamports_per_signature) = self.last_blockhash_and_lamports_per_signature(); let (blockhash, lamports_per_signature) = self.last_blockhash_and_lamports_per_signature();
let mut executed_units = 0u64; let mut executed_units = 0u64;
let programs_modified_by_tx =
Rc::new(RefCell::new(LoadedProgramsForTxBatch::new(self.slot)));
let programs_updated_only_for_global_cache =
Rc::new(RefCell::new(LoadedProgramsForTxBatch::new(self.slot)));
let mut process_message_time = Measure::start("process_message_time"); let mut process_message_time = Measure::start("process_message_time");
let process_result = MessageProcessor::process_message( let process_result = MessageProcessor::process_message(
&self.builtin_programs, &self.builtin_programs,
@ -4322,7 +4329,9 @@ impl Bank {
&mut transaction_context, &mut transaction_context,
self.rent_collector.rent, self.rent_collector.rent,
log_collector.clone(), log_collector.clone(),
tx_executor_cache.clone(), programs_loaded_for_tx_batch,
programs_modified_by_tx.clone(),
programs_updated_only_for_global_cache.clone(),
self.feature_set.clone(), self.feature_set.clone(),
compute_budget, compute_budget,
timings, timings,
@ -4339,15 +4348,6 @@ impl Bank {
process_message_time.as_us() process_message_time.as_us()
); );
let mut store_executors_which_added_to_the_cache_time =
Measure::start("store_executors_which_added_to_the_cache_time");
self.store_executors_which_added_to_the_cache(&tx_executor_cache);
store_executors_which_added_to_the_cache_time.stop();
saturating_add_assign!(
timings.execute_accessories.update_executors_us,
store_executors_which_added_to_the_cache_time.as_us()
);
let status = process_result let status = process_result
.and_then(|info| { .and_then(|info| {
let post_account_state_info = let post_account_state_info =
@ -4435,7 +4435,8 @@ impl Bank {
executed_units, executed_units,
accounts_data_len_delta, accounts_data_len_delta,
}, },
tx_executor_cache, programs_modified_by_tx,
programs_updated_only_for_global_cache,
} }
} }
@ -4448,8 +4449,7 @@ impl Bank {
|| self.cluster_type() != ClusterType::MainnetBeta || self.cluster_type() != ClusterType::MainnetBeta
} }
#[allow(dead_code)] // Preparation for BankExecutorCache rework fn replenish_program_cache(
fn load_and_get_programs_from_cache(
&self, &self,
program_accounts_map: &HashMap<Pubkey, &Pubkey>, program_accounts_map: &HashMap<Pubkey, &Pubkey>,
) -> LoadedProgramsForTxBatch { ) -> LoadedProgramsForTxBatch {
@ -4507,6 +4507,7 @@ impl Bank {
loaded_programs_for_txs loaded_programs_for_txs
} }
#[allow(dead_code)] // Preparation for BankExecutorCache rework
fn replenish_executor_cache( fn replenish_executor_cache(
&self, &self,
program_accounts_map: &HashMap<Pubkey, &Pubkey>, program_accounts_map: &HashMap<Pubkey, &Pubkey>,
@ -4626,17 +4627,9 @@ impl Bank {
&self.blockhash_queue.read().unwrap(), &self.blockhash_queue.read().unwrap(),
); );
// The following code is currently commented out. This is how the new cache will let programs_loaded_for_tx_batch = Rc::new(RefCell::new(
// finally be used, once rest of the code blocks are in place. self.replenish_program_cache(&program_accounts_map),
/* ));
let loaded_programs_map =
self.load_and_get_programs_from_cache(&program_accounts_map);
*/
let loaded_programs_map = self.replenish_executor_cache(&program_accounts_map);
let tx_executor_cache = Rc::new(RefCell::new(TransactionExecutorCache::new(
loaded_programs_map.clone().into_iter(),
)));
let mut load_time = Measure::start("accounts_load"); let mut load_time = Measure::start("accounts_load");
let mut loaded_transactions = self.rc.accounts.load_accounts( let mut loaded_transactions = self.rc.accounts.load_accounts(
@ -4650,7 +4643,7 @@ impl Bank {
&self.fee_structure, &self.fee_structure,
account_overrides, account_overrides,
&program_accounts_map, &program_accounts_map,
&loaded_programs_map, &programs_loaded_for_tx_batch.borrow(),
); );
load_time.stop(); load_time.stop();
@ -4696,7 +4689,7 @@ impl Bank {
compute_budget compute_budget
}; };
self.execute_loaded_transaction( let result = self.execute_loaded_transaction(
tx, tx,
loaded_transaction, loaded_transaction,
compute_budget, compute_budget,
@ -4707,8 +4700,25 @@ impl Bank {
timings, timings,
&mut error_counters, &mut error_counters,
log_messages_bytes_limit, log_messages_bytes_limit,
tx_executor_cache.clone(), programs_loaded_for_tx_batch.clone(),
) );
if let TransactionExecutionResult::Executed {
details,
programs_modified_by_tx,
programs_updated_only_for_global_cache: _,
} = &result
{
// Update batch specific cache of the loaded programs with the modifications
// made by the transaction, if it executed successfully.
if details.status.is_ok() {
programs_loaded_for_tx_batch
.borrow_mut()
.merge(&programs_modified_by_tx.borrow());
}
}
result
} }
}) })
.collect(); .collect();
@ -5193,11 +5203,14 @@ impl Bank {
for execution_result in &execution_results { for execution_result in &execution_results {
if let TransactionExecutionResult::Executed { if let TransactionExecutionResult::Executed {
details, details,
tx_executor_cache, programs_modified_by_tx,
programs_updated_only_for_global_cache,
} = execution_result } = execution_result
{ {
if details.status.is_ok() { if details.status.is_ok() {
self.store_executors_which_were_deployed(tx_executor_cache); let mut cache = self.loaded_programs_cache.write().unwrap();
cache.merge(&programs_modified_by_tx.borrow());
cache.merge(&programs_updated_only_for_global_cache.borrow());
} }
} }
} }

View File

@ -38,7 +38,7 @@ use {
declare_process_instruction, declare_process_instruction,
executor_cache::TransactionExecutorCache, executor_cache::TransactionExecutorCache,
invoke_context::mock_process_instruction, invoke_context::mock_process_instruction,
loaded_programs::{LoadedProgram, LoadedProgramType}, loaded_programs::{LoadedProgram, LoadedProgramType, DELAY_VISIBILITY_SLOT_OFFSET},
prioritization_fee::{PrioritizationFeeDetails, PrioritizationFeeType}, prioritization_fee::{PrioritizationFeeDetails, PrioritizationFeeType},
timings::ExecuteTimings, timings::ExecuteTimings,
}, },
@ -176,7 +176,10 @@ fn new_execution_result(
executed_units: 0, executed_units: 0,
accounts_data_len_delta: 0, accounts_data_len_delta: 0,
}, },
tx_executor_cache: Rc::new(RefCell::new(TransactionExecutorCache::default())), programs_modified_by_tx: Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
programs_updated_only_for_global_cache: Rc::new(RefCell::new(
LoadedProgramsForTxBatch::default(),
)),
} }
} }
@ -7771,14 +7774,9 @@ fn test_bpf_loader_upgradeable_deploy_with_max_len() {
Ok(()), Ok(()),
solana_bpf_loader_program::process_instruction, solana_bpf_loader_program::process_instruction,
|invoke_context| { |invoke_context| {
let mut cache = invoke_context.tx_executor_cache.borrow_mut(); let mut cache = invoke_context.programs_modified_by_tx.borrow_mut();
cache.set( cache.set_slot_for_tests(bank.slot() + DELAY_VISIBILITY_SLOT_OFFSET);
program_keypair.pubkey(), cache.replenish(program_keypair.pubkey(), loaded_program.clone());
loaded_program.clone(),
true,
false,
0,
);
}, },
|_invoke_context| {}, |_invoke_context| {},
); );
@ -11506,7 +11504,7 @@ fn test_rent_state_list_len() {
&FeeStructure::default(), &FeeStructure::default(),
None, None,
&HashMap::new(), &HashMap::new(),
&HashMap::new(), &LoadedProgramsForTxBatch::default(),
); );
let compute_budget = bank.runtime_config.compute_budget.unwrap_or_else(|| { let compute_budget = bank.runtime_config.compute_budget.unwrap_or_else(|| {

View File

@ -4,8 +4,8 @@ use {
solana_program_runtime::{ solana_program_runtime::{
builtin_program::BuiltinPrograms, builtin_program::BuiltinPrograms,
compute_budget::ComputeBudget, compute_budget::ComputeBudget,
executor_cache::TransactionExecutorCache,
invoke_context::InvokeContext, invoke_context::InvokeContext,
loaded_programs::LoadedProgramsForTxBatch,
log_collector::LogCollector, log_collector::LogCollector,
sysvar_cache::SysvarCache, sysvar_cache::SysvarCache,
timings::{ExecuteDetailsTimings, ExecuteTimings}, timings::{ExecuteDetailsTimings, ExecuteTimings},
@ -58,7 +58,9 @@ impl MessageProcessor {
transaction_context: &mut TransactionContext, transaction_context: &mut TransactionContext,
rent: Rent, rent: Rent,
log_collector: Option<Rc<RefCell<LogCollector>>>, log_collector: Option<Rc<RefCell<LogCollector>>>,
tx_executor_cache: Rc<RefCell<TransactionExecutorCache>>, programs_loaded_for_tx_batch: Rc<RefCell<LoadedProgramsForTxBatch>>,
programs_modified_by_tx: Rc<RefCell<LoadedProgramsForTxBatch>>,
programs_updated_only_for_global_cache: Rc<RefCell<LoadedProgramsForTxBatch>>,
feature_set: Arc<FeatureSet>, feature_set: Arc<FeatureSet>,
compute_budget: ComputeBudget, compute_budget: ComputeBudget,
timings: &mut ExecuteTimings, timings: &mut ExecuteTimings,
@ -75,7 +77,9 @@ impl MessageProcessor {
sysvar_cache, sysvar_cache,
log_collector, log_collector,
compute_budget, compute_budget,
tx_executor_cache, programs_loaded_for_tx_batch,
programs_modified_by_tx,
programs_updated_only_for_global_cache,
feature_set, feature_set,
blockhash, blockhash,
lamports_per_signature, lamports_per_signature,
@ -272,7 +276,8 @@ mod tests {
let mut transaction_context = let mut transaction_context =
TransactionContext::new(accounts, Some(Rent::default()), 1, 3); TransactionContext::new(accounts, Some(Rent::default()), 1, 3);
let program_indices = vec![vec![2]]; let program_indices = vec![vec![2]];
let tx_executor_cache = Rc::new(RefCell::new(TransactionExecutorCache::default())); let programs_loaded_for_tx_batch =
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default()));
let account_keys = (0..transaction_context.get_number_of_accounts()) let account_keys = (0..transaction_context.get_number_of_accounts())
.map(|index| { .map(|index| {
*transaction_context *transaction_context
@ -308,7 +313,9 @@ mod tests {
&mut transaction_context, &mut transaction_context,
rent_collector.rent, rent_collector.rent,
None, None,
tx_executor_cache.clone(), programs_loaded_for_tx_batch.clone(),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::default(), ComputeBudget::default(),
&mut ExecuteTimings::default(), &mut ExecuteTimings::default(),
@ -358,7 +365,9 @@ mod tests {
&mut transaction_context, &mut transaction_context,
rent_collector.rent, rent_collector.rent,
None, None,
tx_executor_cache.clone(), programs_loaded_for_tx_batch.clone(),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::default(), ComputeBudget::default(),
&mut ExecuteTimings::default(), &mut ExecuteTimings::default(),
@ -398,7 +407,9 @@ mod tests {
&mut transaction_context, &mut transaction_context,
rent_collector.rent, rent_collector.rent,
None, None,
tx_executor_cache, programs_loaded_for_tx_batch,
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::default(), ComputeBudget::default(),
&mut ExecuteTimings::default(), &mut ExecuteTimings::default(),
@ -496,7 +507,8 @@ mod tests {
let mut transaction_context = let mut transaction_context =
TransactionContext::new(accounts, Some(Rent::default()), 1, 3); TransactionContext::new(accounts, Some(Rent::default()), 1, 3);
let program_indices = vec![vec![2]]; let program_indices = vec![vec![2]];
let tx_executor_cache = Rc::new(RefCell::new(TransactionExecutorCache::default())); let programs_loaded_for_tx_batch =
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default()));
let account_metas = vec![ let account_metas = vec![
AccountMeta::new( AccountMeta::new(
*transaction_context.get_key_of_account_at_index(0).unwrap(), *transaction_context.get_key_of_account_at_index(0).unwrap(),
@ -529,7 +541,9 @@ mod tests {
&mut transaction_context, &mut transaction_context,
rent_collector.rent, rent_collector.rent,
None, None,
tx_executor_cache.clone(), programs_loaded_for_tx_batch.clone(),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::default(), ComputeBudget::default(),
&mut ExecuteTimings::default(), &mut ExecuteTimings::default(),
@ -563,7 +577,9 @@ mod tests {
&mut transaction_context, &mut transaction_context,
rent_collector.rent, rent_collector.rent,
None, None,
tx_executor_cache.clone(), programs_loaded_for_tx_batch.clone(),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::default(), ComputeBudget::default(),
&mut ExecuteTimings::default(), &mut ExecuteTimings::default(),
@ -594,7 +610,9 @@ mod tests {
&mut transaction_context, &mut transaction_context,
rent_collector.rent, rent_collector.rent,
None, None,
tx_executor_cache, programs_loaded_for_tx_batch,
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::default(), ComputeBudget::default(),
&mut ExecuteTimings::default(), &mut ExecuteTimings::default(),
@ -668,7 +686,9 @@ mod tests {
&mut transaction_context, &mut transaction_context,
RentCollector::default().rent, RentCollector::default().rent,
None, None,
Rc::new(RefCell::new(TransactionExecutorCache::default())), Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::default(), ComputeBudget::default(),
&mut ExecuteTimings::default(), &mut ExecuteTimings::default(),