Refactor - `ExtractedPrograms` (#34205)
* Puts ExtractedPrograms into Arc<Mutex<>>. * Merges ExtractedPrograms::missing and ExtractedPrograms::unloaded. * Unifies missing entry insertion in LoadedPrograms::extract().
This commit is contained in:
parent
6d703edd2e
commit
4ee5078e5f
|
@ -25,7 +25,7 @@ use {
|
|||
fmt::{Debug, Formatter},
|
||||
sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
Arc, RwLock,
|
||||
Arc, Mutex, RwLock,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -511,8 +511,7 @@ pub struct LoadedProgramsForTxBatch {
|
|||
|
||||
pub struct ExtractedPrograms {
|
||||
pub loaded: LoadedProgramsForTxBatch,
|
||||
pub missing: Vec<(Pubkey, u64)>,
|
||||
pub unloaded: Vec<(Pubkey, u64)>,
|
||||
pub missing: HashMap<Pubkey, (u64, bool)>,
|
||||
}
|
||||
|
||||
impl LoadedProgramsForTxBatch {
|
||||
|
@ -795,13 +794,21 @@ impl<FG: ForkGraph> LoadedPrograms<FG> {
|
|||
&self,
|
||||
working_slot: &S,
|
||||
keys: impl Iterator<Item = (Pubkey, (LoadedProgramMatchCriteria, u64))>,
|
||||
) -> ExtractedPrograms {
|
||||
) -> Arc<Mutex<ExtractedPrograms>> {
|
||||
let environments = self.get_environments_for_epoch(working_slot.current_epoch());
|
||||
let mut missing = Vec::new();
|
||||
let mut unloaded = Vec::new();
|
||||
let current_slot = working_slot.current_slot();
|
||||
let found = keys
|
||||
let extracted = Arc::new(Mutex::new(ExtractedPrograms {
|
||||
loaded: LoadedProgramsForTxBatch {
|
||||
entries: HashMap::new(),
|
||||
slot: current_slot,
|
||||
environments: environments.clone(),
|
||||
},
|
||||
missing: HashMap::new(),
|
||||
}));
|
||||
let mut extracting = extracted.lock().unwrap();
|
||||
extracting.loaded.entries = keys
|
||||
.filter_map(|(key, (match_criteria, count))| {
|
||||
let mut reloading = false;
|
||||
if let Some(second_level) = self.entries.get(&key) {
|
||||
for entry in second_level.iter().rev() {
|
||||
let is_ancestor = if let Some(fork_graph) = &self.fork_graph {
|
||||
|
@ -824,19 +831,15 @@ impl<FG: ForkGraph> LoadedPrograms<FG> {
|
|||
|| is_ancestor
|
||||
{
|
||||
if current_slot >= entry.effective_slot {
|
||||
if !Self::is_entry_usable(entry, current_slot, &match_criteria) {
|
||||
missing.push((key, count));
|
||||
return None;
|
||||
}
|
||||
|
||||
if !Self::matches_environment(entry, environments) {
|
||||
missing.push((key, count));
|
||||
return None;
|
||||
if !Self::is_entry_usable(entry, current_slot, &match_criteria)
|
||||
|| !Self::matches_environment(entry, environments)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if let LoadedProgramType::Unloaded(_environment) = &entry.program {
|
||||
unloaded.push((key, count));
|
||||
return None;
|
||||
reloading = true;
|
||||
break;
|
||||
}
|
||||
|
||||
let mut usage_count =
|
||||
|
@ -859,26 +862,18 @@ impl<FG: ForkGraph> LoadedPrograms<FG> {
|
|||
}
|
||||
}
|
||||
}
|
||||
missing.push((key, count));
|
||||
extracting.missing.insert(key, (count, reloading));
|
||||
None
|
||||
})
|
||||
.collect::<HashMap<Pubkey, Arc<LoadedProgram>>>();
|
||||
|
||||
self.stats
|
||||
.misses
|
||||
.fetch_add(missing.len() as u64, Ordering::Relaxed);
|
||||
.fetch_add(extracting.missing.len() as u64, Ordering::Relaxed);
|
||||
self.stats
|
||||
.hits
|
||||
.fetch_add(found.len() as u64, Ordering::Relaxed);
|
||||
ExtractedPrograms {
|
||||
loaded: LoadedProgramsForTxBatch {
|
||||
entries: found,
|
||||
slot: current_slot,
|
||||
environments: environments.clone(),
|
||||
},
|
||||
missing,
|
||||
unloaded,
|
||||
}
|
||||
.fetch_add(extracting.loaded.entries.len() as u64, Ordering::Relaxed);
|
||||
drop(extracting);
|
||||
extracted
|
||||
}
|
||||
|
||||
pub fn merge(&mut self, tx_batch_cache: &LoadedProgramsForTxBatch) {
|
||||
|
@ -1010,7 +1005,7 @@ mod tests {
|
|||
use {
|
||||
crate::loaded_programs::{
|
||||
BlockRelation, ExtractedPrograms, ForkGraph, LoadedProgram, LoadedProgramMatchCriteria,
|
||||
LoadedProgramType, LoadedPrograms, LoadedProgramsForTxBatch, ProgramRuntimeEnvironment,
|
||||
LoadedProgramType, LoadedPrograms, ProgramRuntimeEnvironment,
|
||||
ProgramRuntimeEnvironments, WorkingSlot, DELAY_VISIBILITY_SLOT_OFFSET,
|
||||
},
|
||||
assert_matches::assert_matches,
|
||||
|
@ -1024,7 +1019,7 @@ mod tests {
|
|||
ops::ControlFlow,
|
||||
sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
Arc, RwLock,
|
||||
Arc, Mutex, RwLock,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -1630,18 +1625,33 @@ mod tests {
|
|||
}
|
||||
|
||||
fn match_slot(
|
||||
table: &LoadedProgramsForTxBatch,
|
||||
extracted: &Arc<Mutex<ExtractedPrograms>>,
|
||||
program: &Pubkey,
|
||||
deployment_slot: Slot,
|
||||
working_slot: Slot,
|
||||
) -> bool {
|
||||
assert_eq!(table.slot, working_slot);
|
||||
table
|
||||
let extracted = extracted.lock().unwrap();
|
||||
assert_eq!(extracted.loaded.slot, working_slot);
|
||||
extracted
|
||||
.loaded
|
||||
.find(program)
|
||||
.map(|entry| entry.deployment_slot == deployment_slot)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn match_missing(
|
||||
extracted: &Arc<Mutex<ExtractedPrograms>>,
|
||||
key: &Pubkey,
|
||||
reload: bool,
|
||||
) -> bool {
|
||||
let extracted = extracted.lock().unwrap();
|
||||
extracted
|
||||
.missing
|
||||
.get(key)
|
||||
.filter(|(_count, reloading)| *reloading == reload)
|
||||
.is_some()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fork_extract_and_prune() {
|
||||
let mut cache = new_mock_cache::<TestForkGraphSpecific>();
|
||||
|
@ -1720,11 +1730,7 @@ mod tests {
|
|||
// 23
|
||||
|
||||
// Testing fork 0 - 10 - 12 - 22 with current slot at 22
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(22),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -1735,19 +1741,14 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 20, 22));
|
||||
assert!(match_slot(&found, &program4, 0, 22));
|
||||
assert!(match_slot(&extracted, &program1, 20, 22));
|
||||
assert!(match_slot(&extracted, &program4, 0, 22));
|
||||
|
||||
assert!(missing.contains(&(program2, 2)));
|
||||
assert!(missing.contains(&(program3, 3)));
|
||||
assert!(unloaded.is_empty());
|
||||
assert!(match_missing(&extracted, &program2, false));
|
||||
assert!(match_missing(&extracted, &program3, false));
|
||||
|
||||
// Testing fork 0 - 5 - 11 - 15 - 16 with current slot at 15
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
// Testing fork 0 - 5 - 11 - 15 - 16 with current slot at 16
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(15),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -1758,24 +1759,24 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0, 15));
|
||||
assert!(match_slot(&found, &program2, 11, 15));
|
||||
assert!(match_slot(&extracted, &program1, 0, 15));
|
||||
assert!(match_slot(&extracted, &program2, 11, 15));
|
||||
|
||||
// The effective slot of program4 deployed in slot 15 is 19. So it should not be usable in slot 16.
|
||||
// A delay visibility tombstone should be returned here.
|
||||
let tombstone = found.find(&program4).expect("Failed to find the tombstone");
|
||||
let tombstone = extracted
|
||||
.lock()
|
||||
.unwrap()
|
||||
.loaded
|
||||
.find(&program4)
|
||||
.expect("Failed to find the tombstone");
|
||||
assert_matches!(tombstone.program, LoadedProgramType::DelayVisibility);
|
||||
assert_eq!(tombstone.deployment_slot, 15);
|
||||
|
||||
assert!(missing.contains(&(program3, 1)));
|
||||
assert!(unloaded.is_empty());
|
||||
assert!(match_missing(&extracted, &program3, false));
|
||||
|
||||
// Testing the same fork above, but current slot is now 18 (equal to effective slot of program4).
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(18),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -1786,21 +1787,16 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0, 18));
|
||||
assert!(match_slot(&found, &program2, 11, 18));
|
||||
assert!(match_slot(&extracted, &program1, 0, 18));
|
||||
assert!(match_slot(&extracted, &program2, 11, 18));
|
||||
|
||||
// The effective slot of program4 deployed in slot 15 is 18. So it should be usable in slot 18.
|
||||
assert!(match_slot(&found, &program4, 15, 18));
|
||||
assert!(match_slot(&extracted, &program4, 15, 18));
|
||||
|
||||
assert!(missing.contains(&(program3, 1)));
|
||||
assert!(unloaded.is_empty());
|
||||
assert!(match_missing(&extracted, &program3, false));
|
||||
|
||||
// Testing the same fork above, but current slot is now 23 (future slot than effective slot of program4).
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(23),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -1811,21 +1807,16 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0, 23));
|
||||
assert!(match_slot(&found, &program2, 11, 23));
|
||||
assert!(match_slot(&extracted, &program1, 0, 23));
|
||||
assert!(match_slot(&extracted, &program2, 11, 23));
|
||||
|
||||
// The effective slot of program4 deployed in slot 15 is 19. So it should be usable in slot 23.
|
||||
assert!(match_slot(&found, &program4, 15, 23));
|
||||
assert!(match_slot(&extracted, &program4, 15, 23));
|
||||
|
||||
assert!(missing.contains(&(program3, 1)));
|
||||
assert!(unloaded.is_empty());
|
||||
assert!(match_missing(&extracted, &program3, false));
|
||||
|
||||
// Testing fork 0 - 5 - 11 - 15 - 16 with current slot at 11
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(11),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -1836,15 +1827,19 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0, 11));
|
||||
assert!(match_slot(&extracted, &program1, 0, 11));
|
||||
// program2 was updated at slot 11, but is not effective till slot 12. The result should contain a tombstone.
|
||||
let tombstone = found.find(&program2).expect("Failed to find the tombstone");
|
||||
let tombstone = extracted
|
||||
.lock()
|
||||
.unwrap()
|
||||
.loaded
|
||||
.find(&program2)
|
||||
.expect("Failed to find the tombstone");
|
||||
assert_matches!(tombstone.program, LoadedProgramType::DelayVisibility);
|
||||
assert_eq!(tombstone.deployment_slot, 11);
|
||||
assert!(match_slot(&found, &program4, 5, 11));
|
||||
assert!(match_slot(&extracted, &program4, 5, 11));
|
||||
|
||||
assert!(missing.contains(&(program3, 1)));
|
||||
assert!(unloaded.is_empty());
|
||||
assert!(match_missing(&extracted, &program3, false));
|
||||
|
||||
// The following is a special case, where there's an expiration slot
|
||||
let test_program = Arc::new(LoadedProgram {
|
||||
|
@ -1859,11 +1854,7 @@ mod tests {
|
|||
assert!(!cache.replenish(program4, test_program).0);
|
||||
|
||||
// Testing fork 0 - 5 - 11 - 15 - 16 - 19 - 21 - 23 with current slot at 19
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(19),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -1874,21 +1865,16 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0, 19));
|
||||
assert!(match_slot(&found, &program2, 11, 19));
|
||||
assert!(match_slot(&extracted, &program1, 0, 19));
|
||||
assert!(match_slot(&extracted, &program2, 11, 19));
|
||||
// Program4 deployed at slot 19 should not be expired yet
|
||||
assert!(match_slot(&found, &program4, 19, 19));
|
||||
assert!(match_slot(&extracted, &program4, 19, 19));
|
||||
|
||||
assert!(missing.contains(&(program3, 1)));
|
||||
assert!(unloaded.is_empty());
|
||||
assert!(match_missing(&extracted, &program3, false));
|
||||
|
||||
// Testing fork 0 - 5 - 11 - 15 - 16 - 19 - 21 - 23 with current slot at 21
|
||||
// This would cause program4 deployed at slot 19 to be expired.
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(21),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -1899,12 +1885,11 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0, 21));
|
||||
assert!(match_slot(&found, &program2, 11, 21));
|
||||
assert!(match_slot(&extracted, &program1, 0, 21));
|
||||
assert!(match_slot(&extracted, &program2, 11, 21));
|
||||
|
||||
assert!(missing.contains(&(program3, 1)));
|
||||
assert!(missing.contains(&(program4, 1)));
|
||||
assert!(unloaded.is_empty());
|
||||
assert!(match_missing(&extracted, &program3, false));
|
||||
assert!(match_missing(&extracted, &program4, false));
|
||||
|
||||
// Remove the expired entry to let the rest of the test continue
|
||||
if let Some(programs) = cache.entries.get_mut(&program4) {
|
||||
|
@ -1928,12 +1913,8 @@ mod tests {
|
|||
// |
|
||||
// 23
|
||||
|
||||
// Testing fork 11 - 15 - 16- 19 - 22 with root at 5 and current slot at 21
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
// Testing fork 11 - 15 - 16- 19 - 22 with root at 5 and current slot at 22
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(21),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -1945,19 +1926,14 @@ mod tests {
|
|||
);
|
||||
|
||||
// Since the fork was pruned, we should not find the entry deployed at slot 20.
|
||||
assert!(match_slot(&found, &program1, 0, 21));
|
||||
assert!(match_slot(&found, &program2, 11, 21));
|
||||
assert!(match_slot(&found, &program4, 15, 21));
|
||||
assert!(match_slot(&extracted, &program1, 0, 21));
|
||||
assert!(match_slot(&extracted, &program2, 11, 21));
|
||||
assert!(match_slot(&extracted, &program4, 15, 21));
|
||||
|
||||
assert!(missing.contains(&(program3, 1)));
|
||||
assert!(unloaded.is_empty());
|
||||
assert!(match_missing(&extracted, &program3, false));
|
||||
|
||||
// Testing fork 0 - 5 - 11 - 25 - 27 with current slot at 27
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing: _,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(27),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -1968,11 +1944,10 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(unloaded.is_empty());
|
||||
assert!(match_slot(&found, &program1, 0, 27));
|
||||
assert!(match_slot(&found, &program2, 11, 27));
|
||||
assert!(match_slot(&found, &program3, 25, 27));
|
||||
assert!(match_slot(&found, &program4, 5, 27));
|
||||
assert!(match_slot(&extracted, &program1, 0, 27));
|
||||
assert!(match_slot(&extracted, &program2, 11, 27));
|
||||
assert!(match_slot(&extracted, &program3, 25, 27));
|
||||
assert!(match_slot(&extracted, &program4, 5, 27));
|
||||
|
||||
cache.prune(15, 0);
|
||||
|
||||
|
@ -1992,11 +1967,7 @@ mod tests {
|
|||
// 23
|
||||
|
||||
// Testing fork 16, 19, 23, with root at 15, current slot at 23
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(23),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -2007,13 +1978,12 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0, 23));
|
||||
assert!(match_slot(&found, &program2, 11, 23));
|
||||
assert!(match_slot(&found, &program4, 15, 23));
|
||||
assert!(match_slot(&extracted, &program1, 0, 23));
|
||||
assert!(match_slot(&extracted, &program2, 11, 23));
|
||||
assert!(match_slot(&extracted, &program4, 15, 23));
|
||||
|
||||
// program3 was deployed on slot 25, which has been pruned
|
||||
assert!(missing.contains(&(program3, 1)));
|
||||
assert!(unloaded.is_empty());
|
||||
assert!(match_missing(&extracted, &program3, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2055,11 +2025,7 @@ mod tests {
|
|||
assert!(!cache.replenish(program3, new_test_loaded_program(25, 26)).0);
|
||||
|
||||
// Testing fork 0 - 5 - 11 - 15 - 16 - 19 - 21 - 23 with current slot at 19
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(12),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -2069,18 +2035,13 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0, 12));
|
||||
assert!(match_slot(&found, &program2, 11, 12));
|
||||
assert!(match_slot(&extracted, &program1, 0, 12));
|
||||
assert!(match_slot(&extracted, &program2, 11, 12));
|
||||
|
||||
assert!(missing.contains(&(program3, 1)));
|
||||
assert!(unloaded.is_empty());
|
||||
assert!(match_missing(&extracted, &program3, false));
|
||||
|
||||
// Test the same fork, but request the program modified at a later slot than what's in the cache.
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(12),
|
||||
vec![
|
||||
(
|
||||
|
@ -2096,11 +2057,10 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program2, 11, 12));
|
||||
assert!(match_slot(&extracted, &program2, 11, 12));
|
||||
|
||||
assert!(missing.contains(&(program1, 1)));
|
||||
assert!(missing.contains(&(program3, 1)));
|
||||
assert!(unloaded.is_empty());
|
||||
assert!(match_missing(&extracted, &program1, false));
|
||||
assert!(match_missing(&extracted, &program3, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2159,11 +2119,7 @@ mod tests {
|
|||
);
|
||||
|
||||
// Testing fork 0 - 5 - 11 - 15 - 16 - 19 - 21 - 23 with current slot at 19
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(19),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -2173,18 +2129,13 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0, 19));
|
||||
assert!(match_slot(&found, &program2, 11, 19));
|
||||
assert!(match_slot(&extracted, &program1, 0, 19));
|
||||
assert!(match_slot(&extracted, &program2, 11, 19));
|
||||
|
||||
assert!(missing.contains(&(program3, 1)));
|
||||
assert!(unloaded.is_empty());
|
||||
assert!(match_missing(&extracted, &program3, false));
|
||||
|
||||
// Testing fork 0 - 5 - 11 - 25 - 27 with current slot at 27
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(27),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -2194,18 +2145,13 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0, 27));
|
||||
assert!(match_slot(&found, &program2, 11, 27));
|
||||
assert!(match_slot(&extracted, &program1, 0, 27));
|
||||
assert!(match_slot(&extracted, &program2, 11, 27));
|
||||
|
||||
assert!(unloaded.contains(&(program3, 1)));
|
||||
assert!(missing.is_empty());
|
||||
assert!(match_missing(&extracted, &program3, true));
|
||||
|
||||
// Testing fork 0 - 10 - 20 - 22 with current slot at 22
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(22),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -2215,10 +2161,10 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 20, 22));
|
||||
assert!(match_slot(&extracted, &program1, 20, 22));
|
||||
|
||||
assert!(missing.contains(&(program2, 1)));
|
||||
assert!(unloaded.contains(&(program3, 1)));
|
||||
assert!(match_missing(&extracted, &program2, false));
|
||||
assert!(match_missing(&extracted, &program3, true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2271,11 +2217,7 @@ mod tests {
|
|||
assert!(!cache.replenish(program1, test_program).0);
|
||||
|
||||
// Testing fork 0 - 5 - 11 - 15 - 16 - 19 - 21 - 23 with current slot at 19
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(12),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -2286,19 +2228,14 @@ mod tests {
|
|||
);
|
||||
|
||||
// Program1 deployed at slot 11 should not be expired yet
|
||||
assert!(match_slot(&found, &program1, 11, 12));
|
||||
assert!(match_slot(&found, &program2, 11, 12));
|
||||
assert!(match_slot(&extracted, &program1, 11, 12));
|
||||
assert!(match_slot(&extracted, &program2, 11, 12));
|
||||
|
||||
assert!(missing.contains(&(program3, 1)));
|
||||
assert!(unloaded.is_empty());
|
||||
assert!(match_missing(&extracted, &program3, false));
|
||||
|
||||
// Testing fork 0 - 5 - 11 - 12 - 15 - 16 - 19 - 21 - 23 with current slot at 15
|
||||
// This would cause program4 deployed at slot 15 to be expired.
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(15),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -2307,12 +2244,11 @@ mod tests {
|
|||
]
|
||||
.into_iter(),
|
||||
);
|
||||
assert!(unloaded.is_empty());
|
||||
|
||||
assert!(match_slot(&found, &program2, 11, 15));
|
||||
assert!(match_slot(&extracted, &program2, 11, 15));
|
||||
|
||||
assert!(missing.contains(&(program1, 1)));
|
||||
assert!(missing.contains(&(program3, 1)));
|
||||
assert!(match_missing(&extracted, &program1, false));
|
||||
assert!(match_missing(&extracted, &program3, false));
|
||||
|
||||
// Test that the program still exists in the cache, even though it is expired.
|
||||
assert_eq!(
|
||||
|
@ -2366,19 +2302,17 @@ mod tests {
|
|||
|
||||
cache.prune(10, 0);
|
||||
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing: _,
|
||||
unloaded,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(20),
|
||||
vec![(program1, (LoadedProgramMatchCriteria::NoCriteria, 1))].into_iter(),
|
||||
);
|
||||
assert!(unloaded.is_empty());
|
||||
|
||||
// The cache should have the program deployed at slot 0
|
||||
assert_eq!(
|
||||
found
|
||||
extracted
|
||||
.lock()
|
||||
.unwrap()
|
||||
.loaded
|
||||
.entries
|
||||
.get(&program1)
|
||||
.expect("Did not find the program")
|
||||
|
@ -2414,11 +2348,7 @@ mod tests {
|
|||
let program2 = Pubkey::new_unique();
|
||||
assert!(!cache.replenish(program2, new_test_loaded_program(10, 11)).0);
|
||||
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing: _,
|
||||
unloaded: _,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(20),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -2427,14 +2357,10 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0, 20));
|
||||
assert!(match_slot(&found, &program2, 10, 20));
|
||||
assert!(match_slot(&extracted, &program1, 0, 20));
|
||||
assert!(match_slot(&extracted, &program2, 10, 20));
|
||||
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded: _,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(6),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -2443,18 +2369,14 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 5, 6));
|
||||
assert!(missing.contains(&(program2, 1)));
|
||||
assert!(match_slot(&extracted, &program1, 5, 6));
|
||||
assert!(match_missing(&extracted, &program2, false));
|
||||
|
||||
// Pruning slot 5 will remove program1 entry deployed at slot 5.
|
||||
// On fork chaining from slot 5, the entry deployed at slot 0 will become visible.
|
||||
cache.prune_by_deployment_slot(5);
|
||||
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing: _,
|
||||
unloaded: _,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(20),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -2463,14 +2385,10 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0, 20));
|
||||
assert!(match_slot(&found, &program2, 10, 20));
|
||||
assert!(match_slot(&extracted, &program1, 0, 20));
|
||||
assert!(match_slot(&extracted, &program2, 10, 20));
|
||||
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing,
|
||||
unloaded: _,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(6),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -2479,18 +2397,14 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0, 6));
|
||||
assert!(missing.contains(&(program2, 1)));
|
||||
assert!(match_slot(&extracted, &program1, 0, 6));
|
||||
assert!(match_missing(&extracted, &program2, false));
|
||||
|
||||
// Pruning slot 10 will remove program2 entry deployed at slot 10.
|
||||
// As there is no other entry for program2, extract() will return it as missing.
|
||||
cache.prune_by_deployment_slot(10);
|
||||
|
||||
let ExtractedPrograms {
|
||||
loaded: found,
|
||||
missing: _,
|
||||
unloaded: _,
|
||||
} = cache.extract(
|
||||
let extracted = cache.extract(
|
||||
&TestWorkingSlot(20),
|
||||
vec![
|
||||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
|
@ -2499,8 +2413,8 @@ mod tests {
|
|||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0, 20));
|
||||
assert!(missing.contains(&(program2, 1)));
|
||||
assert!(match_slot(&extracted, &program1, 0, 20));
|
||||
assert!(match_missing(&extracted, &program2, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -5071,28 +5071,23 @@ impl Bank {
|
|||
let ExtractedPrograms {
|
||||
loaded: mut loaded_programs_for_txs,
|
||||
missing,
|
||||
unloaded,
|
||||
} = {
|
||||
// Lock the global cache to figure out which programs need to be loaded
|
||||
let loaded_programs_cache = self.loaded_programs_cache.read().unwrap();
|
||||
loaded_programs_cache.extract(self, programs_and_slots.into_iter())
|
||||
Mutex::into_inner(
|
||||
Arc::into_inner(
|
||||
loaded_programs_cache.extract(self, programs_and_slots.into_iter()),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
// Load missing programs while global cache is unlocked
|
||||
let missing_programs: Vec<(Pubkey, Arc<LoadedProgram>)> = missing
|
||||
.iter()
|
||||
.map(|(key, count)| {
|
||||
let program = self.load_program(key, false, None);
|
||||
program.tx_usage_counter.store(*count, Ordering::Relaxed);
|
||||
(*key, program)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Reload unloaded programs while global cache is unlocked
|
||||
let unloaded_programs: Vec<(Pubkey, Arc<LoadedProgram>)> = unloaded
|
||||
.iter()
|
||||
.map(|(key, count)| {
|
||||
let program = self.load_program(key, true, None);
|
||||
.map(|(key, (count, reloading))| {
|
||||
let program = self.load_program(key, *reloading, None);
|
||||
program.tx_usage_counter.store(*count, Ordering::Relaxed);
|
||||
(*key, program)
|
||||
})
|
||||
|
@ -5105,12 +5100,6 @@ impl Bank {
|
|||
// Use the returned entry as that might have been deduplicated globally
|
||||
loaded_programs_for_txs.replenish(key, entry);
|
||||
}
|
||||
for (key, program) in unloaded_programs {
|
||||
let (_was_occupied, entry) = loaded_programs_cache.replenish(key, program);
|
||||
// Use the returned entry as that might have been deduplicated globally
|
||||
loaded_programs_for_txs.replenish(key, entry);
|
||||
}
|
||||
|
||||
loaded_programs_for_txs
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue