Evict program cache using transaction usage counter (#31927)
* Evict program cache using tx usage counter * address review feedback * address review feedback
This commit is contained in:
parent
4b0514d9b1
commit
37ebb709e6
|
@ -735,7 +735,7 @@ impl<'a> InvokeContext<'a> {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
.ok_or(InstructionError::UnsupportedProgramId)?;
|
.ok_or(InstructionError::UnsupportedProgramId)?;
|
||||||
entry.usage_counter.fetch_add(1, Ordering::Relaxed);
|
entry.ix_usage_counter.fetch_add(1, Ordering::Relaxed);
|
||||||
|
|
||||||
let program_id = *instruction_context.get_last_program_key(self.transaction_context)?;
|
let program_id = *instruction_context.get_last_program_key(self.transaction_context)?;
|
||||||
self.transaction_context
|
self.transaction_context
|
||||||
|
|
|
@ -105,8 +105,10 @@ pub struct LoadedProgram {
|
||||||
pub effective_slot: Slot,
|
pub effective_slot: Slot,
|
||||||
/// Optional expiration slot for this entry, after which it is treated as non-existent
|
/// Optional expiration slot for this entry, after which it is treated as non-existent
|
||||||
pub maybe_expiration_slot: Option<Slot>,
|
pub maybe_expiration_slot: Option<Slot>,
|
||||||
/// How often this entry was used
|
/// How often this entry was used by a transaction
|
||||||
pub usage_counter: AtomicU64,
|
pub tx_usage_counter: AtomicU64,
|
||||||
|
/// How often this entry was used by a transaction
|
||||||
|
pub ix_usage_counter: AtomicU64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
@ -264,8 +266,9 @@ impl LoadedProgram {
|
||||||
account_size,
|
account_size,
|
||||||
effective_slot,
|
effective_slot,
|
||||||
maybe_expiration_slot,
|
maybe_expiration_slot,
|
||||||
usage_counter: AtomicU64::new(0),
|
tx_usage_counter: AtomicU64::new(0),
|
||||||
program,
|
program,
|
||||||
|
ix_usage_counter: AtomicU64::new(0),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,7 +279,8 @@ impl LoadedProgram {
|
||||||
deployment_slot: self.deployment_slot,
|
deployment_slot: self.deployment_slot,
|
||||||
effective_slot: self.effective_slot,
|
effective_slot: self.effective_slot,
|
||||||
maybe_expiration_slot: self.maybe_expiration_slot,
|
maybe_expiration_slot: self.maybe_expiration_slot,
|
||||||
usage_counter: AtomicU64::new(self.usage_counter.load(Ordering::Relaxed)),
|
tx_usage_counter: AtomicU64::new(self.tx_usage_counter.load(Ordering::Relaxed)),
|
||||||
|
ix_usage_counter: AtomicU64::new(self.tx_usage_counter.load(Ordering::Relaxed)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,8 +299,9 @@ impl LoadedProgram {
|
||||||
account_size,
|
account_size,
|
||||||
effective_slot: deployment_slot,
|
effective_slot: deployment_slot,
|
||||||
maybe_expiration_slot: None,
|
maybe_expiration_slot: None,
|
||||||
usage_counter: AtomicU64::new(0),
|
tx_usage_counter: AtomicU64::new(0),
|
||||||
program: LoadedProgramType::Builtin(program),
|
program: LoadedProgramType::Builtin(program),
|
||||||
|
ix_usage_counter: AtomicU64::new(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,7 +314,8 @@ impl LoadedProgram {
|
||||||
deployment_slot: slot,
|
deployment_slot: slot,
|
||||||
effective_slot: slot,
|
effective_slot: slot,
|
||||||
maybe_expiration_slot,
|
maybe_expiration_slot,
|
||||||
usage_counter: AtomicU64::default(),
|
tx_usage_counter: AtomicU64::default(),
|
||||||
|
ix_usage_counter: AtomicU64::default(),
|
||||||
};
|
};
|
||||||
debug_assert!(tombstone.is_tombstone());
|
debug_assert!(tombstone.is_tombstone());
|
||||||
tombstone
|
tombstone
|
||||||
|
@ -444,8 +450,14 @@ impl LoadedPrograms {
|
||||||
if matches!(existing.program, LoadedProgramType::Unloaded) {
|
if matches!(existing.program, LoadedProgramType::Unloaded) {
|
||||||
// The unloaded program is getting reloaded
|
// The unloaded program is getting reloaded
|
||||||
// Copy over the usage counter to the new entry
|
// Copy over the usage counter to the new entry
|
||||||
entry.usage_counter.store(
|
let mut usage_count = existing.tx_usage_counter.load(Ordering::Relaxed);
|
||||||
existing.usage_counter.load(Ordering::Relaxed),
|
saturating_add_assign!(
|
||||||
|
usage_count,
|
||||||
|
entry.tx_usage_counter.load(Ordering::Relaxed)
|
||||||
|
);
|
||||||
|
entry.tx_usage_counter.store(usage_count, Ordering::Relaxed);
|
||||||
|
entry.ix_usage_counter.store(
|
||||||
|
existing.ix_usage_counter.load(Ordering::Relaxed),
|
||||||
Ordering::Relaxed,
|
Ordering::Relaxed,
|
||||||
);
|
);
|
||||||
second_level.swap_remove(entry_index);
|
second_level.swap_remove(entry_index);
|
||||||
|
@ -548,11 +560,11 @@ impl LoadedPrograms {
|
||||||
pub fn extract<S: WorkingSlot>(
|
pub fn extract<S: WorkingSlot>(
|
||||||
&self,
|
&self,
|
||||||
working_slot: &S,
|
working_slot: &S,
|
||||||
keys: impl Iterator<Item = (Pubkey, LoadedProgramMatchCriteria)>,
|
keys: impl Iterator<Item = (Pubkey, (LoadedProgramMatchCriteria, u64))>,
|
||||||
) -> (LoadedProgramsForTxBatch, Vec<Pubkey>) {
|
) -> (LoadedProgramsForTxBatch, Vec<(Pubkey, u64)>) {
|
||||||
let mut missing = Vec::new();
|
let mut missing = Vec::new();
|
||||||
let found = keys
|
let found = keys
|
||||||
.filter_map(|(key, match_criteria)| {
|
.filter_map(|(key, (match_criteria, count))| {
|
||||||
if let Some(second_level) = self.entries.get(&key) {
|
if let Some(second_level) = self.entries.get(&key) {
|
||||||
for entry in second_level.iter().rev() {
|
for entry in second_level.iter().rev() {
|
||||||
let current_slot = working_slot.current_slot();
|
let current_slot = working_slot.current_slot();
|
||||||
|
@ -561,11 +573,15 @@ impl LoadedPrograms {
|
||||||
|| working_slot.is_ancestor(entry.deployment_slot)
|
|| working_slot.is_ancestor(entry.deployment_slot)
|
||||||
{
|
{
|
||||||
if !Self::is_entry_usable(entry, current_slot, &match_criteria) {
|
if !Self::is_entry_usable(entry, current_slot, &match_criteria) {
|
||||||
missing.push(key);
|
missing.push((key, count));
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if current_slot >= entry.effective_slot {
|
if current_slot >= entry.effective_slot {
|
||||||
|
let mut usage_count =
|
||||||
|
entry.tx_usage_counter.load(Ordering::Relaxed);
|
||||||
|
saturating_add_assign!(usage_count, count);
|
||||||
|
entry.tx_usage_counter.store(usage_count, Ordering::Relaxed);
|
||||||
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) {
|
||||||
// Found a program entry on the current fork, but it's not effective
|
// Found a program entry on the current fork, but it's not effective
|
||||||
|
@ -582,7 +598,7 @@ impl LoadedPrograms {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
missing.push(key);
|
missing.push((key, count));
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
.collect::<HashMap<Pubkey, Arc<LoadedProgram>>>();
|
.collect::<HashMap<Pubkey, Arc<LoadedProgram>>>();
|
||||||
|
@ -628,7 +644,7 @@ impl LoadedPrograms {
|
||||||
| LoadedProgramType::Builtin(_) => None,
|
| LoadedProgramType::Builtin(_) => None,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.sorted_by_cached_key(|(_id, program)| program.usage_counter.load(Ordering::Relaxed))
|
.sorted_by_cached_key(|(_id, program)| program.tx_usage_counter.load(Ordering::Relaxed))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let num_to_unload = sorted_candidates
|
let num_to_unload = sorted_candidates
|
||||||
|
@ -690,7 +706,7 @@ impl LoadedPrograms {
|
||||||
for (id, program) in remove {
|
for (id, program) in remove {
|
||||||
if let Some(entries) = self.entries.get_mut(id) {
|
if let Some(entries) = self.entries.get_mut(id) {
|
||||||
if let Some(candidate) = entries.iter_mut().find(|entry| entry == &program) {
|
if let Some(candidate) = entries.iter_mut().find(|entry| entry == &program) {
|
||||||
if candidate.usage_counter.load(Ordering::Relaxed) == 1 {
|
if candidate.tx_usage_counter.load(Ordering::Relaxed) == 1 {
|
||||||
self.stats.one_hit_wonders.fetch_add(1, Ordering::Relaxed);
|
self.stats.one_hit_wonders.fetch_add(1, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
self.stats
|
self.stats
|
||||||
|
@ -758,7 +774,8 @@ mod tests {
|
||||||
deployment_slot,
|
deployment_slot,
|
||||||
effective_slot,
|
effective_slot,
|
||||||
maybe_expiration_slot: None,
|
maybe_expiration_slot: None,
|
||||||
usage_counter: AtomicU64::default(),
|
tx_usage_counter: AtomicU64::default(),
|
||||||
|
ix_usage_counter: AtomicU64::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -935,7 +952,7 @@ mod tests {
|
||||||
.flat_map(|(id, cached_programs)| {
|
.flat_map(|(id, cached_programs)| {
|
||||||
cached_programs.iter().filter_map(|program| {
|
cached_programs.iter().filter_map(|program| {
|
||||||
matches!(program.program, LoadedProgramType::Unloaded)
|
matches!(program.program, LoadedProgramType::Unloaded)
|
||||||
.then_some((*id, program.usage_counter.load(Ordering::Relaxed)))
|
.then_some((*id, program.tx_usage_counter.load(Ordering::Relaxed)))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Vec<(Pubkey, u64)>>();
|
.collect::<Vec<(Pubkey, u64)>>();
|
||||||
|
@ -990,7 +1007,7 @@ mod tests {
|
||||||
programs.iter().for_each(|program| {
|
programs.iter().for_each(|program| {
|
||||||
if matches!(program.program, LoadedProgramType::Unloaded) {
|
if matches!(program.program, LoadedProgramType::Unloaded) {
|
||||||
// Test that the usage counter is retained for the unloaded program
|
// Test that the usage counter is retained for the unloaded program
|
||||||
assert_eq!(program.usage_counter.load(Ordering::Relaxed), 10);
|
assert_eq!(program.tx_usage_counter.load(Ordering::Relaxed), 10);
|
||||||
assert_eq!(program.deployment_slot, 0);
|
assert_eq!(program.deployment_slot, 0);
|
||||||
assert_eq!(program.effective_slot, 2);
|
assert_eq!(program.effective_slot, 2);
|
||||||
}
|
}
|
||||||
|
@ -1011,7 +1028,7 @@ mod tests {
|
||||||
&& program.effective_slot == 2
|
&& program.effective_slot == 2
|
||||||
{
|
{
|
||||||
// Test that the usage counter was correctly updated.
|
// Test that the usage counter was correctly updated.
|
||||||
assert_eq!(program.usage_counter.load(Ordering::Relaxed), 10);
|
assert_eq!(program.tx_usage_counter.load(Ordering::Relaxed), 10);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -1253,7 +1270,8 @@ mod tests {
|
||||||
deployment_slot,
|
deployment_slot,
|
||||||
effective_slot,
|
effective_slot,
|
||||||
maybe_expiration_slot: None,
|
maybe_expiration_slot: None,
|
||||||
usage_counter,
|
tx_usage_counter: usage_counter,
|
||||||
|
ix_usage_counter: AtomicU64::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1349,10 +1367,10 @@ mod tests {
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&working_slot,
|
||||||
vec![
|
vec![
|
||||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
(program2, (LoadedProgramMatchCriteria::NoCriteria, 2)),
|
||||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
(program3, (LoadedProgramMatchCriteria::NoCriteria, 3)),
|
||||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
(program4, (LoadedProgramMatchCriteria::NoCriteria, 4)),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
@ -1360,18 +1378,18 @@ mod tests {
|
||||||
assert!(match_slot(&found, &program1, 20, 22));
|
assert!(match_slot(&found, &program1, 20, 22));
|
||||||
assert!(match_slot(&found, &program4, 0, 22));
|
assert!(match_slot(&found, &program4, 0, 22));
|
||||||
|
|
||||||
assert!(missing.contains(&program2));
|
assert!(missing.contains(&(program2, 2)));
|
||||||
assert!(missing.contains(&program3));
|
assert!(missing.contains(&(program3, 3)));
|
||||||
|
|
||||||
// Testing fork 0 - 5 - 11 - 15 - 16 with current slot at 16
|
// Testing fork 0 - 5 - 11 - 15 - 16 with current slot at 16
|
||||||
let mut working_slot = TestWorkingSlot::new(15, &[0, 5, 11, 15, 16, 18, 19, 23]);
|
let mut working_slot = TestWorkingSlot::new(15, &[0, 5, 11, 15, 16, 18, 19, 23]);
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&working_slot,
|
||||||
vec![
|
vec![
|
||||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
@ -1388,17 +1406,17 @@ mod tests {
|
||||||
));
|
));
|
||||||
assert_eq!(tombstone.deployment_slot, 15);
|
assert_eq!(tombstone.deployment_slot, 15);
|
||||||
|
|
||||||
assert!(missing.contains(&program3));
|
assert!(missing.contains(&(program3, 1)));
|
||||||
|
|
||||||
// Testing the same fork above, but current slot is now 18 (equal to effective slot of program4).
|
// Testing the same fork above, but current slot is now 18 (equal to effective slot of program4).
|
||||||
working_slot.update_slot(18);
|
working_slot.update_slot(18);
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&working_slot,
|
||||||
vec![
|
vec![
|
||||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
@ -1409,17 +1427,17 @@ mod tests {
|
||||||
// The effective slot of program4 deployed in slot 15 is 18. So it should be usable in slot 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(&found, &program4, 15, 18));
|
||||||
|
|
||||||
assert!(missing.contains(&program3));
|
assert!(missing.contains(&(program3, 1)));
|
||||||
|
|
||||||
// Testing the same fork above, but current slot is now 23 (future slot than effective slot of program4).
|
// Testing the same fork above, but current slot is now 23 (future slot than effective slot of program4).
|
||||||
working_slot.update_slot(23);
|
working_slot.update_slot(23);
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&working_slot,
|
||||||
vec![
|
vec![
|
||||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
@ -1430,17 +1448,17 @@ mod tests {
|
||||||
// The effective slot of program4 deployed in slot 15 is 19. So it should be usable in slot 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(&found, &program4, 15, 23));
|
||||||
|
|
||||||
assert!(missing.contains(&program3));
|
assert!(missing.contains(&(program3, 1)));
|
||||||
|
|
||||||
// Testing fork 0 - 5 - 11 - 15 - 16 with current slot at 11
|
// Testing fork 0 - 5 - 11 - 15 - 16 with current slot at 11
|
||||||
let working_slot = TestWorkingSlot::new(11, &[0, 5, 11, 15, 16]);
|
let working_slot = TestWorkingSlot::new(11, &[0, 5, 11, 15, 16]);
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&working_slot,
|
||||||
vec![
|
vec![
|
||||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
@ -1455,7 +1473,7 @@ mod tests {
|
||||||
assert_eq!(tombstone.deployment_slot, 11);
|
assert_eq!(tombstone.deployment_slot, 11);
|
||||||
assert!(match_slot(&found, &program4, 5, 11));
|
assert!(match_slot(&found, &program4, 5, 11));
|
||||||
|
|
||||||
assert!(missing.contains(&program3));
|
assert!(missing.contains(&(program3, 1)));
|
||||||
|
|
||||||
// The following is a special case, where there's an expiration slot
|
// The following is a special case, where there's an expiration slot
|
||||||
let test_program = Arc::new(LoadedProgram {
|
let test_program = Arc::new(LoadedProgram {
|
||||||
|
@ -1464,7 +1482,8 @@ mod tests {
|
||||||
deployment_slot: 19,
|
deployment_slot: 19,
|
||||||
effective_slot: 19,
|
effective_slot: 19,
|
||||||
maybe_expiration_slot: Some(21),
|
maybe_expiration_slot: Some(21),
|
||||||
usage_counter: AtomicU64::default(),
|
tx_usage_counter: AtomicU64::default(),
|
||||||
|
ix_usage_counter: AtomicU64::default(),
|
||||||
});
|
});
|
||||||
assert!(!cache.replenish(program4, test_program).0);
|
assert!(!cache.replenish(program4, test_program).0);
|
||||||
|
|
||||||
|
@ -1473,10 +1492,10 @@ mod tests {
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&working_slot,
|
||||||
vec![
|
vec![
|
||||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
@ -1486,7 +1505,7 @@ mod tests {
|
||||||
// Program4 deployed at slot 19 should not be expired yet
|
// Program4 deployed at slot 19 should not be expired yet
|
||||||
assert!(match_slot(&found, &program4, 19, 19));
|
assert!(match_slot(&found, &program4, 19, 19));
|
||||||
|
|
||||||
assert!(missing.contains(&program3));
|
assert!(missing.contains(&(program3, 1)));
|
||||||
|
|
||||||
// Testing fork 0 - 5 - 11 - 15 - 16 - 19 - 21 - 23 with current slot at 21
|
// 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.
|
// This would cause program4 deployed at slot 19 to be expired.
|
||||||
|
@ -1494,10 +1513,10 @@ mod tests {
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&working_slot,
|
||||||
vec![
|
vec![
|
||||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
@ -1505,8 +1524,8 @@ mod tests {
|
||||||
assert!(match_slot(&found, &program1, 0, 21));
|
assert!(match_slot(&found, &program1, 0, 21));
|
||||||
assert!(match_slot(&found, &program2, 11, 21));
|
assert!(match_slot(&found, &program2, 11, 21));
|
||||||
|
|
||||||
assert!(missing.contains(&program3));
|
assert!(missing.contains(&(program3, 1)));
|
||||||
assert!(missing.contains(&program4));
|
assert!(missing.contains(&(program4, 1)));
|
||||||
|
|
||||||
// Remove the expired entry to let the rest of the test continue
|
// Remove the expired entry to let the rest of the test continue
|
||||||
if let Some(programs) = cache.entries.get_mut(&program4) {
|
if let Some(programs) = cache.entries.get_mut(&program4) {
|
||||||
|
@ -1535,10 +1554,10 @@ mod tests {
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&working_slot,
|
||||||
vec![
|
vec![
|
||||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
@ -1548,17 +1567,17 @@ mod tests {
|
||||||
assert!(match_slot(&found, &program2, 11, 22));
|
assert!(match_slot(&found, &program2, 11, 22));
|
||||||
assert!(match_slot(&found, &program4, 15, 22));
|
assert!(match_slot(&found, &program4, 15, 22));
|
||||||
|
|
||||||
assert!(missing.contains(&program3));
|
assert!(missing.contains(&(program3, 1)));
|
||||||
|
|
||||||
// Testing fork 0 - 5 - 11 - 25 - 27 with current slot at 27
|
// Testing fork 0 - 5 - 11 - 25 - 27 with current slot at 27
|
||||||
let working_slot = TestWorkingSlot::new(27, &[11, 25, 27]);
|
let working_slot = TestWorkingSlot::new(27, &[11, 25, 27]);
|
||||||
let (found, _missing) = cache.extract(
|
let (found, _missing) = cache.extract(
|
||||||
&working_slot,
|
&working_slot,
|
||||||
vec![
|
vec![
|
||||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
@ -1590,10 +1609,10 @@ mod tests {
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&working_slot,
|
||||||
vec![
|
vec![
|
||||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
@ -1603,7 +1622,7 @@ mod tests {
|
||||||
assert!(match_slot(&found, &program4, 15, 23));
|
assert!(match_slot(&found, &program4, 15, 23));
|
||||||
|
|
||||||
// program3 was deployed on slot 25, which has been pruned
|
// program3 was deployed on slot 25, which has been pruned
|
||||||
assert!(missing.contains(&program3));
|
assert!(missing.contains(&(program3, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1646,9 +1665,9 @@ mod tests {
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&working_slot,
|
||||||
vec![
|
vec![
|
||||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
@ -1656,7 +1675,7 @@ mod tests {
|
||||||
assert!(match_slot(&found, &program1, 0, 12));
|
assert!(match_slot(&found, &program1, 0, 12));
|
||||||
assert!(match_slot(&found, &program2, 11, 12));
|
assert!(match_slot(&found, &program2, 11, 12));
|
||||||
|
|
||||||
assert!(missing.contains(&program3));
|
assert!(missing.contains(&(program3, 1)));
|
||||||
|
|
||||||
// Test the same fork, but request the program modified at a later slot than what's in the cache.
|
// Test the same fork, but request the program modified at a later slot than what's in the cache.
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
|
@ -1664,21 +1683,21 @@ mod tests {
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
program1,
|
program1,
|
||||||
LoadedProgramMatchCriteria::DeployedOnOrAfterSlot(5),
|
(LoadedProgramMatchCriteria::DeployedOnOrAfterSlot(5), 1),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
program2,
|
program2,
|
||||||
LoadedProgramMatchCriteria::DeployedOnOrAfterSlot(5),
|
(LoadedProgramMatchCriteria::DeployedOnOrAfterSlot(5), 1),
|
||||||
),
|
),
|
||||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(match_slot(&found, &program2, 11, 12));
|
assert!(match_slot(&found, &program2, 11, 12));
|
||||||
|
|
||||||
assert!(missing.contains(&program1));
|
assert!(missing.contains(&(program1, 1)));
|
||||||
assert!(missing.contains(&program3));
|
assert!(missing.contains(&(program3, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1723,7 +1742,8 @@ mod tests {
|
||||||
deployment_slot: 11,
|
deployment_slot: 11,
|
||||||
effective_slot: 11,
|
effective_slot: 11,
|
||||||
maybe_expiration_slot: Some(15),
|
maybe_expiration_slot: Some(15),
|
||||||
usage_counter: AtomicU64::default(),
|
tx_usage_counter: AtomicU64::default(),
|
||||||
|
ix_usage_counter: AtomicU64::default(),
|
||||||
});
|
});
|
||||||
assert!(!cache.replenish(program1, test_program).0);
|
assert!(!cache.replenish(program1, test_program).0);
|
||||||
|
|
||||||
|
@ -1732,9 +1752,9 @@ mod tests {
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&working_slot,
|
||||||
vec![
|
vec![
|
||||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
@ -1743,7 +1763,7 @@ mod tests {
|
||||||
assert!(match_slot(&found, &program1, 11, 12));
|
assert!(match_slot(&found, &program1, 11, 12));
|
||||||
assert!(match_slot(&found, &program2, 11, 12));
|
assert!(match_slot(&found, &program2, 11, 12));
|
||||||
|
|
||||||
assert!(missing.contains(&program3));
|
assert!(missing.contains(&(program3, 1)));
|
||||||
|
|
||||||
// Testing fork 0 - 5 - 11 - 12 - 15 - 16 - 19 - 21 - 23 with current slot at 15
|
// 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.
|
// This would cause program4 deployed at slot 15 to be expired.
|
||||||
|
@ -1751,17 +1771,17 @@ mod tests {
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&working_slot,
|
||||||
vec![
|
vec![
|
||||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||||
]
|
]
|
||||||
.into_iter(),
|
.into_iter(),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(match_slot(&found, &program2, 11, 15));
|
assert!(match_slot(&found, &program2, 11, 15));
|
||||||
|
|
||||||
assert!(missing.contains(&program1));
|
assert!(missing.contains(&(program1, 1)));
|
||||||
assert!(missing.contains(&program3));
|
assert!(missing.contains(&(program3, 1)));
|
||||||
|
|
||||||
// Test that the program still exists in the cache, even though it is expired.
|
// Test that the program still exists in the cache, even though it is expired.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1816,7 +1836,7 @@ mod tests {
|
||||||
let working_slot = TestWorkingSlot::new(20, &[0, 10, 20]);
|
let working_slot = TestWorkingSlot::new(20, &[0, 10, 20]);
|
||||||
let (found, _missing) = cache.extract(
|
let (found, _missing) = cache.extract(
|
||||||
&working_slot,
|
&working_slot,
|
||||||
vec![(program1, LoadedProgramMatchCriteria::NoCriteria)].into_iter(),
|
vec![(program1, (LoadedProgramMatchCriteria::NoCriteria, 1))].into_iter(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// The cache should have the program deployed at slot 0
|
// The cache should have the program deployed at slot 0
|
||||||
|
@ -1838,7 +1858,8 @@ mod tests {
|
||||||
deployment_slot: 0,
|
deployment_slot: 0,
|
||||||
effective_slot: 0,
|
effective_slot: 0,
|
||||||
maybe_expiration_slot: None,
|
maybe_expiration_slot: None,
|
||||||
usage_counter: AtomicU64::default(),
|
tx_usage_counter: AtomicU64::default(),
|
||||||
|
ix_usage_counter: AtomicU64::default(),
|
||||||
});
|
});
|
||||||
assert!(!LoadedPrograms::is_entry_usable(
|
assert!(!LoadedPrograms::is_entry_usable(
|
||||||
&unloaded_entry,
|
&unloaded_entry,
|
||||||
|
@ -1934,7 +1955,8 @@ mod tests {
|
||||||
deployment_slot: 0,
|
deployment_slot: 0,
|
||||||
effective_slot: 1,
|
effective_slot: 1,
|
||||||
maybe_expiration_slot: Some(2),
|
maybe_expiration_slot: Some(2),
|
||||||
usage_counter: AtomicU64::default(),
|
tx_usage_counter: AtomicU64::default(),
|
||||||
|
ix_usage_counter: AtomicU64::default(),
|
||||||
});
|
});
|
||||||
|
|
||||||
assert!(LoadedPrograms::is_entry_usable(
|
assert!(LoadedPrograms::is_entry_usable(
|
||||||
|
|
|
@ -205,8 +205,14 @@ macro_rules! deploy_program {
|
||||||
Arc::new(program_runtime_environment),
|
Arc::new(program_runtime_environment),
|
||||||
)?;
|
)?;
|
||||||
if let Some(old_entry) = find_program_in_cache($invoke_context, &$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);
|
executor.tx_usage_counter.store(
|
||||||
executor.usage_counter.store(usage_counter, Ordering::Relaxed);
|
old_entry.tx_usage_counter.load(Ordering::Relaxed),
|
||||||
|
Ordering::Relaxed
|
||||||
|
);
|
||||||
|
executor.ix_usage_counter.store(
|
||||||
|
old_entry.ix_usage_counter.load(Ordering::Relaxed),
|
||||||
|
Ordering::Relaxed
|
||||||
|
);
|
||||||
}
|
}
|
||||||
$drop
|
$drop
|
||||||
load_program_metrics.program_id = $program_id.to_string();
|
load_program_metrics.program_id = $program_id.to_string();
|
||||||
|
@ -562,7 +568,7 @@ fn process_instruction_inner(
|
||||||
get_or_create_executor_time.as_us()
|
get_or_create_executor_time.as_us()
|
||||||
);
|
);
|
||||||
|
|
||||||
executor.usage_counter.fetch_add(1, Ordering::Relaxed);
|
executor.ix_usage_counter.fetch_add(1, Ordering::Relaxed);
|
||||||
match &executor.program {
|
match &executor.program {
|
||||||
LoadedProgramType::FailedVerification
|
LoadedProgramType::FailedVerification
|
||||||
| LoadedProgramType::Closed
|
| LoadedProgramType::Closed
|
||||||
|
@ -4128,7 +4134,8 @@ mod tests {
|
||||||
deployment_slot: 0,
|
deployment_slot: 0,
|
||||||
effective_slot: 0,
|
effective_slot: 0,
|
||||||
maybe_expiration_slot: None,
|
maybe_expiration_slot: None,
|
||||||
usage_counter: AtomicU64::new(100),
|
tx_usage_counter: AtomicU64::new(100),
|
||||||
|
ix_usage_counter: AtomicU64::new(100),
|
||||||
};
|
};
|
||||||
invoke_context
|
invoke_context
|
||||||
.programs_modified_by_tx
|
.programs_modified_by_tx
|
||||||
|
@ -4145,7 +4152,14 @@ mod tests {
|
||||||
.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);
|
||||||
assert_eq!(updated_program.usage_counter.load(Ordering::Relaxed), 100);
|
assert_eq!(
|
||||||
|
updated_program.tx_usage_counter.load(Ordering::Relaxed),
|
||||||
|
100
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
updated_program.ix_usage_counter.load(Ordering::Relaxed),
|
||||||
|
100
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -4159,7 +4173,8 @@ mod tests {
|
||||||
deployment_slot: 0,
|
deployment_slot: 0,
|
||||||
effective_slot: 0,
|
effective_slot: 0,
|
||||||
maybe_expiration_slot: None,
|
maybe_expiration_slot: None,
|
||||||
usage_counter: AtomicU64::new(100),
|
tx_usage_counter: AtomicU64::new(100),
|
||||||
|
ix_usage_counter: AtomicU64::new(100),
|
||||||
};
|
};
|
||||||
invoke_context
|
invoke_context
|
||||||
.programs_modified_by_tx
|
.programs_modified_by_tx
|
||||||
|
@ -4177,6 +4192,7 @@ mod tests {
|
||||||
.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);
|
||||||
assert_eq!(program2.usage_counter.load(Ordering::Relaxed), 0);
|
assert_eq!(program2.tx_usage_counter.load(Ordering::Relaxed), 0);
|
||||||
|
assert_eq!(program2.ix_usage_counter.load(Ordering::Relaxed), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -608,7 +608,9 @@ pub fn process_instruction_inner(
|
||||||
get_or_create_executor_time.as_us()
|
get_or_create_executor_time.as_us()
|
||||||
);
|
);
|
||||||
drop(program);
|
drop(program);
|
||||||
loaded_program.usage_counter.fetch_add(1, Ordering::Relaxed);
|
loaded_program
|
||||||
|
.ix_usage_counter
|
||||||
|
.fetch_add(1, Ordering::Relaxed);
|
||||||
match &loaded_program.program {
|
match &loaded_program.program {
|
||||||
LoadedProgramType::FailedVerification
|
LoadedProgramType::FailedVerification
|
||||||
| LoadedProgramType::Closed
|
| LoadedProgramType::Closed
|
||||||
|
|
|
@ -62,7 +62,10 @@ use {
|
||||||
solana_system_program::{get_system_account_kind, SystemAccountKind},
|
solana_system_program::{get_system_account_kind, SystemAccountKind},
|
||||||
std::{
|
std::{
|
||||||
cmp::Reverse,
|
cmp::Reverse,
|
||||||
collections::{hash_map, BinaryHeap, HashMap, HashSet},
|
collections::{
|
||||||
|
hash_map::{self, Entry},
|
||||||
|
BinaryHeap, HashMap, HashSet,
|
||||||
|
},
|
||||||
num::NonZeroUsize,
|
num::NonZeroUsize,
|
||||||
ops::RangeBounds,
|
ops::RangeBounds,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
|
@ -291,7 +294,7 @@ impl Accounts {
|
||||||
key: &Pubkey,
|
key: &Pubkey,
|
||||||
feature_set: &FeatureSet,
|
feature_set: &FeatureSet,
|
||||||
program: &LoadedProgram,
|
program: &LoadedProgram,
|
||||||
program_accounts: &HashMap<Pubkey, &Pubkey>,
|
program_accounts: &HashMap<Pubkey, (&Pubkey, u64)>,
|
||||||
) -> Result<AccountSharedData> {
|
) -> Result<AccountSharedData> {
|
||||||
// Check for tombstone
|
// Check for tombstone
|
||||||
let result = match &program.program {
|
let result = match &program.program {
|
||||||
|
@ -314,7 +317,7 @@ impl Accounts {
|
||||||
// So the account data is not needed. Return a dummy AccountSharedData with meta
|
// So the account data is not needed. Return a dummy AccountSharedData with meta
|
||||||
// information.
|
// information.
|
||||||
let mut program_account = AccountSharedData::default();
|
let mut program_account = AccountSharedData::default();
|
||||||
let program_owner = program_accounts
|
let (program_owner, _count) = program_accounts
|
||||||
.get(key)
|
.get(key)
|
||||||
.ok_or(TransactionError::AccountNotFound)?;
|
.ok_or(TransactionError::AccountNotFound)?;
|
||||||
program_account.set_owner(**program_owner);
|
program_account.set_owner(**program_owner);
|
||||||
|
@ -333,7 +336,7 @@ impl Accounts {
|
||||||
feature_set: &FeatureSet,
|
feature_set: &FeatureSet,
|
||||||
account_overrides: Option<&AccountOverrides>,
|
account_overrides: Option<&AccountOverrides>,
|
||||||
_reward_interval: RewardInterval,
|
_reward_interval: RewardInterval,
|
||||||
program_accounts: &HashMap<Pubkey, &Pubkey>,
|
program_accounts: &HashMap<Pubkey, (&Pubkey, u64)>,
|
||||||
loaded_programs: &LoadedProgramsForTxBatch,
|
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
|
||||||
|
@ -637,8 +640,8 @@ impl Accounts {
|
||||||
lock_results: &mut [TransactionCheckResult],
|
lock_results: &mut [TransactionCheckResult],
|
||||||
program_owners: &[&'a Pubkey],
|
program_owners: &[&'a Pubkey],
|
||||||
hash_queue: &BlockhashQueue,
|
hash_queue: &BlockhashQueue,
|
||||||
) -> HashMap<Pubkey, &'a Pubkey> {
|
) -> HashMap<Pubkey, (&'a Pubkey, u64)> {
|
||||||
let mut result = HashMap::new();
|
let mut result: HashMap<Pubkey, (&'a Pubkey, u64)> = HashMap::new();
|
||||||
lock_results.iter_mut().zip(txs).for_each(|etx| {
|
lock_results.iter_mut().zip(txs).for_each(|etx| {
|
||||||
if let ((Ok(()), nonce), tx) = etx {
|
if let ((Ok(()), nonce), tx) = etx {
|
||||||
if nonce
|
if nonce
|
||||||
|
@ -649,19 +652,26 @@ impl Accounts {
|
||||||
})
|
})
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
tx.message().account_keys().iter().for_each(|key| {
|
tx.message()
|
||||||
if !result.contains_key(key) {
|
.account_keys()
|
||||||
if let Ok(index) = self.accounts_db.account_matches_owners(
|
.iter()
|
||||||
ancestors,
|
.for_each(|key| match result.entry(*key) {
|
||||||
key,
|
Entry::Occupied(mut entry) => {
|
||||||
program_owners,
|
let (_, count) = entry.get_mut();
|
||||||
) {
|
saturating_add_assign!(*count, 1);
|
||||||
program_owners
|
|
||||||
.get(index)
|
|
||||||
.and_then(|owner| result.insert(*key, *owner));
|
|
||||||
}
|
}
|
||||||
}
|
Entry::Vacant(entry) => {
|
||||||
});
|
if let Ok(index) = self.accounts_db.account_matches_owners(
|
||||||
|
ancestors,
|
||||||
|
key,
|
||||||
|
program_owners,
|
||||||
|
) {
|
||||||
|
program_owners
|
||||||
|
.get(index)
|
||||||
|
.map(|owner| entry.insert((*owner, 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// If the transaction's nonce account was not valid, and blockhash is not found,
|
// If the transaction's nonce account was not valid, and blockhash is not found,
|
||||||
// the transaction will fail to process. Let's not load any programs from the
|
// the transaction will fail to process. Let's not load any programs from the
|
||||||
|
@ -686,7 +696,7 @@ impl Accounts {
|
||||||
fee_structure: &FeeStructure,
|
fee_structure: &FeeStructure,
|
||||||
account_overrides: Option<&AccountOverrides>,
|
account_overrides: Option<&AccountOverrides>,
|
||||||
in_reward_interval: RewardInterval,
|
in_reward_interval: RewardInterval,
|
||||||
program_accounts: &HashMap<Pubkey, &Pubkey>,
|
program_accounts: &HashMap<Pubkey, (&Pubkey, u64)>,
|
||||||
loaded_programs: &LoadedProgramsForTxBatch,
|
loaded_programs: &LoadedProgramsForTxBatch,
|
||||||
) -> Vec<TransactionLoadResult> {
|
) -> Vec<TransactionLoadResult> {
|
||||||
txs.iter()
|
txs.iter()
|
||||||
|
@ -2088,13 +2098,13 @@ mod tests {
|
||||||
programs
|
programs
|
||||||
.get(&account3_pubkey)
|
.get(&account3_pubkey)
|
||||||
.expect("failed to find the program account"),
|
.expect("failed to find the program account"),
|
||||||
&&program1_pubkey
|
&(&program1_pubkey, 2)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
programs
|
programs
|
||||||
.get(&account4_pubkey)
|
.get(&account4_pubkey)
|
||||||
.expect("failed to find the program account"),
|
.expect("failed to find the program account"),
|
||||||
&&program2_pubkey
|
&(&program2_pubkey, 1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2197,7 +2207,7 @@ mod tests {
|
||||||
programs
|
programs
|
||||||
.get(&account3_pubkey)
|
.get(&account3_pubkey)
|
||||||
.expect("failed to find the program account"),
|
.expect("failed to find the program account"),
|
||||||
&&program1_pubkey
|
&(&program1_pubkey, 1)
|
||||||
);
|
);
|
||||||
assert_eq!(lock_results[1].0, Err(TransactionError::BlockhashNotFound));
|
assert_eq!(lock_results[1].0, Err(TransactionError::BlockhashNotFound));
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,7 +181,7 @@ use {
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{
|
atomic::{
|
||||||
AtomicBool, AtomicI64, AtomicU64, AtomicUsize,
|
AtomicBool, AtomicI64, AtomicU64, AtomicUsize,
|
||||||
Ordering::{AcqRel, Acquire, Relaxed},
|
Ordering::{self, AcqRel, Acquire, Relaxed},
|
||||||
},
|
},
|
||||||
Arc, LockResult, RwLock, RwLockReadGuard, RwLockWriteGuard,
|
Arc, LockResult, RwLock, RwLockReadGuard, RwLockWriteGuard,
|
||||||
},
|
},
|
||||||
|
@ -4407,26 +4407,31 @@ impl Bank {
|
||||||
|
|
||||||
fn replenish_program_cache(
|
fn replenish_program_cache(
|
||||||
&self,
|
&self,
|
||||||
program_accounts_map: &HashMap<Pubkey, &Pubkey>,
|
program_accounts_map: &HashMap<Pubkey, (&Pubkey, u64)>,
|
||||||
) -> LoadedProgramsForTxBatch {
|
) -> LoadedProgramsForTxBatch {
|
||||||
let programs_and_slots: Vec<(Pubkey, LoadedProgramMatchCriteria)> =
|
let programs_and_slots: Vec<(Pubkey, (LoadedProgramMatchCriteria, u64))> =
|
||||||
if self.check_program_modification_slot {
|
if self.check_program_modification_slot {
|
||||||
program_accounts_map
|
program_accounts_map
|
||||||
.keys()
|
.iter()
|
||||||
.map(|pubkey| {
|
.map(|(pubkey, (_, count))| {
|
||||||
(
|
(
|
||||||
*pubkey,
|
*pubkey,
|
||||||
self.program_modification_slot(pubkey)
|
(
|
||||||
.map_or(LoadedProgramMatchCriteria::Tombstone, |slot| {
|
self.program_modification_slot(pubkey)
|
||||||
LoadedProgramMatchCriteria::DeployedOnOrAfterSlot(slot)
|
.map_or(LoadedProgramMatchCriteria::Tombstone, |slot| {
|
||||||
}),
|
LoadedProgramMatchCriteria::DeployedOnOrAfterSlot(slot)
|
||||||
|
}),
|
||||||
|
*count,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
program_accounts_map
|
program_accounts_map
|
||||||
.keys()
|
.iter()
|
||||||
.map(|pubkey| (*pubkey, LoadedProgramMatchCriteria::NoCriteria))
|
.map(|(pubkey, (_, count))| {
|
||||||
|
(*pubkey, (LoadedProgramMatchCriteria::NoCriteria, *count))
|
||||||
|
})
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4439,7 +4444,7 @@ impl Bank {
|
||||||
// Load missing programs while global cache is unlocked
|
// Load missing programs while global cache is unlocked
|
||||||
let missing_programs: Vec<(Pubkey, Arc<LoadedProgram>)> = missing_programs
|
let missing_programs: Vec<(Pubkey, Arc<LoadedProgram>)> = missing_programs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|key| {
|
.map(|(key, count)| {
|
||||||
let program = self.load_program(key).unwrap_or_else(|err| {
|
let program = self.load_program(key).unwrap_or_else(|err| {
|
||||||
// Create a tombstone for the program in the cache
|
// Create a tombstone for the program in the cache
|
||||||
debug!("Failed to load program {}, error {:?}", key, err);
|
debug!("Failed to load program {}, error {:?}", key, err);
|
||||||
|
@ -4448,6 +4453,7 @@ impl Bank {
|
||||||
LoadedProgramType::FailedVerification,
|
LoadedProgramType::FailedVerification,
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
|
program.tx_usage_counter.store(*count, Ordering::Relaxed);
|
||||||
(*key, program)
|
(*key, program)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -4539,7 +4545,7 @@ impl Bank {
|
||||||
);
|
);
|
||||||
let native_loader = native_loader::id();
|
let native_loader = native_loader::id();
|
||||||
for builtin_program in self.builtin_programs.iter() {
|
for builtin_program in self.builtin_programs.iter() {
|
||||||
program_accounts_map.insert(*builtin_program, &native_loader);
|
program_accounts_map.insert(*builtin_program, (&native_loader, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
let programs_loaded_for_tx_batch = Rc::new(RefCell::new(
|
let programs_loaded_for_tx_batch = Rc::new(RefCell::new(
|
||||||
|
|
Loading…
Reference in New Issue