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:
Alexander Meißner 2023-11-23 09:17:21 +01:00 committed by GitHub
parent 6d703edd2e
commit 4ee5078e5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 164 additions and 261 deletions

View File

@ -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]

View File

@ -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
}