1#[cfg(feature = "shuttle-test")]
2use shuttle::sync::{Arc, Mutex};
3#[cfg(not(feature = "shuttle-test"))]
4use std::sync::{Arc, Mutex};
5use {
6 log::*,
7 smallvec::SmallVec,
8 solana_accounts_db::ancestors::Ancestors,
9 solana_clock::{MAX_RECENT_BLOCKHASHES, Slot},
10 solana_hash::Hash,
11 std::{
12 collections::{HashMap, HashSet, hash_map::Entry},
13 num::{NonZero, NonZeroUsize},
14 },
15};
16
17const MAX_ROOT_ENTRIES: usize = MAX_RECENT_BLOCKHASHES;
21
22const CACHED_KEY_SIZE: usize = 20;
24
25pub type ForkStatus<T> = SmallVec<[(Slot, T); 1]>;
27
28pub(crate) type KeySlice = [u8; CACHED_KEY_SIZE];
30
31type KeyMap<T> = HashMap<KeySlice, ForkStatus<T>>;
32
33pub type Status<T> =
35 Arc<Mutex<HashMap<Hash, (usize, Vec<(KeySlice, T)>), solana_hash::HashHasherBuilder>>>;
36
37type KeyStatusMap<T> = HashMap<Hash, (Slot, usize, KeyMap<T>), solana_hash::HashHasherBuilder>;
40
41type SlotDeltaMap<T> = HashMap<Slot, Status<T>>;
43
44pub type SlotDelta<T> = (Slot, bool, Status<T>);
47
48#[derive(Clone, Debug)]
49pub struct StatusCache<T: Clone> {
50 cache: KeyStatusMap<T>,
53 roots: HashSet<Slot>,
54 max_root_entries: NonZeroUsize,
55 slot_deltas: SlotDeltaMap<T>,
58}
59
60impl<T: Clone> Default for StatusCache<T> {
61 fn default() -> Self {
62 Self {
63 cache: HashMap::default(),
64 roots: HashSet::from([0]),
66 max_root_entries: NonZero::new(MAX_ROOT_ENTRIES).unwrap(),
67 slot_deltas: HashMap::default(),
68 }
69 }
70}
71
72impl<T: Clone> StatusCache<T> {
73 pub fn clear_slot_entries(&mut self, slot: Slot) {
82 let slot_deltas = self.slot_deltas.remove(&slot);
83 if let Some(slot_deltas) = slot_deltas {
84 let slot_deltas = slot_deltas.lock().unwrap();
85 let mut warned = false;
86 for (blockhash, (_, key_list)) in slot_deltas.iter() {
87 if let Entry::Occupied(mut o_blockhash_entries) = self.cache.entry(*blockhash) {
92 let (_, _, all_hash_maps) = o_blockhash_entries.get_mut();
93
94 for (key_slice, _) in key_list {
95 if let Entry::Occupied(mut o_key_list) = all_hash_maps.entry(*key_slice) {
96 let key_list = o_key_list.get_mut();
97 key_list.retain(|(updated_slot, _)| *updated_slot != slot);
98 if key_list.is_empty() {
99 o_key_list.remove_entry();
100 }
101 } else if !warned {
102 warn!(
123 "signature found more than once in the same slot, this means \
124 we're clearing a dead slot: {slot}"
125 );
126 warned = true;
127 }
128 }
129
130 if all_hash_maps.is_empty() {
131 o_blockhash_entries.remove_entry();
132 }
133 } else {
134 panic!("Blockhash must exist if it exists in self.slot_deltas, slot: {slot}")
135 }
136 }
137 }
138 }
139
140 pub fn get_status<K: AsRef<[u8]>>(
143 &self,
144 key: K,
145 transaction_blockhash: &Hash,
146 ancestors: &Ancestors,
147 ) -> Option<(Slot, T)> {
148 let map = self.cache.get(transaction_blockhash)?;
149 let (_, index, keymap) = map;
150 let max_key_index = key.as_ref().len().saturating_sub(CACHED_KEY_SIZE + 1);
151 let index = (*index).min(max_key_index);
152 let key_slice: &[u8; CACHED_KEY_SIZE] =
153 arrayref::array_ref![key.as_ref(), index, CACHED_KEY_SIZE];
154 if let Some(stored_forks) = keymap.get(key_slice) {
155 let res = stored_forks
156 .iter()
157 .find(|(f, _)| ancestors.contains_key(f) || self.roots.contains(f))
158 .cloned();
159 if res.is_some() {
160 return res;
161 }
162 }
163 None
164 }
165
166 pub fn get_status_any_blockhash<K: AsRef<[u8]>>(
170 &self,
171 key: K,
172 ancestors: &Ancestors,
173 ) -> Option<(Slot, T)> {
174 self.cache.keys().find_map(|blockhash| {
175 trace!("get_status_any_blockhash: trying {blockhash}");
176 self.get_status(&key, blockhash, ancestors)
177 })
178 }
179
180 pub fn add_root(&mut self, fork: Slot) {
185 self.roots.insert(fork);
186 self.purge_roots();
187 }
188
189 pub fn add_roots<I: IntoIterator<Item = Slot>>(&mut self, forks: I) {
190 self.roots.extend(forks);
191 self.purge_roots();
192 }
193
194 pub fn roots(&self) -> &HashSet<Slot> {
195 &self.roots
196 }
197
198 pub fn max_root_entries(&self) -> usize {
199 self.max_root_entries.into()
200 }
201
202 pub fn set_max_root_entries(&mut self, max_root_entries: NonZeroUsize) {
203 self.max_root_entries = max_root_entries;
204 self.purge_roots();
205 }
206
207 pub fn insert<K: AsRef<[u8]>>(
209 &mut self,
210 transaction_blockhash: &Hash,
211 key: K,
212 slot: Slot,
213 res: T,
214 ) {
215 let max_key_index = key.as_ref().len().saturating_sub(CACHED_KEY_SIZE + 1);
216
217 let (max_slot, key_index, hash_map) = self
219 .cache
220 .entry(*transaction_blockhash)
221 .or_insert_with(|| (slot, 0, HashMap::new()));
222
223 *max_slot = std::cmp::max(slot, *max_slot);
225
226 let key_index = (*key_index).min(max_key_index);
228 let mut key_slice = [0u8; CACHED_KEY_SIZE];
229 key_slice.clone_from_slice(&key.as_ref()[key_index..key_index + CACHED_KEY_SIZE]);
230
231 let forks = hash_map.entry(key_slice).or_default();
234 forks.push((slot, res.clone()));
235
236 self.add_to_slot_delta(transaction_blockhash, slot, key_index, key_slice, res);
237 }
238
239 pub fn purge_roots(&mut self) {
240 let max_root_entries = self.max_root_entries();
241 if self.roots.len() > max_root_entries {
242 let num_roots_to_purge = self.roots.len() - max_root_entries;
243 let mut roots = self
244 .roots
245 .iter()
246 .copied()
247 .collect::<SmallVec<[Slot; 0x200]>>();
248 let (_, cutoff, _) = roots.select_nth_unstable(num_roots_to_purge - 1);
249 let cutoff = *cutoff;
250
251 self.roots.retain(|root| *root > cutoff);
252 self.cache.retain(|_, (fork, _, _)| *fork > cutoff);
253 self.slot_deltas.retain(|slot, _| *slot > cutoff);
254 }
255 }
256
257 #[cfg(feature = "dev-context-only-utils")]
258 pub fn clear(&mut self) {
259 for v in self.cache.values_mut() {
260 v.2 = HashMap::new();
261 }
262
263 self.slot_deltas
264 .iter_mut()
265 .for_each(|(_, status)| status.lock().unwrap().clear());
266 }
267
268 pub fn root_slot_deltas(&self) -> Vec<SlotDelta<T>> {
276 self.roots()
277 .iter()
278 .map(|root| {
279 (
280 *root,
281 true, self.slot_deltas.get(root).cloned().unwrap_or_default(),
283 )
284 })
285 .collect()
286 }
287
288 pub fn append(&mut self, slot_deltas: &[SlotDelta<T>]) {
293 for (slot, is_root, statuses) in slot_deltas {
294 statuses
295 .lock()
296 .unwrap()
297 .iter()
298 .for_each(|(tx_hash, (key_index, statuses))| {
299 for (key_slice, res) in statuses.iter() {
300 self.insert_with_slice(tx_hash, *slot, *key_index, *key_slice, res.clone())
301 }
302 });
303 if *is_root {
304 self.add_root(*slot);
305 }
306 }
307 }
308
309 fn insert_with_slice(
310 &mut self,
311 transaction_blockhash: &Hash,
312 slot: Slot,
313 key_index: usize,
314 key_slice: [u8; CACHED_KEY_SIZE],
315 res: T,
316 ) {
317 let hash_map =
318 self.cache
319 .entry(*transaction_blockhash)
320 .or_insert((slot, key_index, HashMap::new()));
321 hash_map.0 = std::cmp::max(slot, hash_map.0);
322
323 let forks = hash_map.2.entry(key_slice).or_default();
324 forks.push((slot, res.clone()));
325
326 self.add_to_slot_delta(transaction_blockhash, slot, key_index, key_slice, res);
327 }
328
329 fn add_to_slot_delta(
331 &mut self,
332 transaction_blockhash: &Hash,
333 slot: Slot,
334 key_index: usize,
335 key_slice: [u8; CACHED_KEY_SIZE],
336 res: T,
337 ) {
338 let mut fork_entry = self.slot_deltas.entry(slot).or_default().lock().unwrap();
339 let (_key_index, hash_entry) = fork_entry
340 .entry(*transaction_blockhash)
341 .or_insert((key_index, vec![]));
342 hash_entry.push((key_slice, res))
343 }
344}
345
346#[cfg(test)]
347mod tests {
348 use {super::*, solana_sha256_hasher::hash, solana_signature::Signature};
349
350 type BankStatusCache = StatusCache<()>;
351
352 impl<T: Clone> StatusCache<T> {
353 fn from_slot_deltas(slot_deltas: &[SlotDelta<T>]) -> Self {
354 let mut cache = Self::default();
355 cache.append(slot_deltas);
356 cache
357 }
358 }
359
360 impl<T: Clone + PartialEq> PartialEq for StatusCache<T> {
361 fn eq(&self, other: &Self) -> bool {
362 self.roots == other.roots
363 && self
364 .cache
365 .iter()
366 .all(|(hash, (slot, key_index, hash_map))| {
367 if let Some((other_slot, other_key_index, other_hash_map)) =
368 other.cache.get(hash)
369 && slot == other_slot
370 && key_index == other_key_index
371 {
372 return hash_map.iter().all(|(slice, fork_map)| {
373 if let Some(other_fork_map) = other_hash_map.get(slice) {
374 return fork_map.last() == other_fork_map.last();
377 }
378 false
379 });
380 }
381 false
382 })
383 }
384 }
385
386 #[test]
387 fn test_empty_has_no_sigs() {
388 let sig = Signature::default();
389 let blockhash = hash(Hash::default().as_ref());
390 let status_cache = BankStatusCache::default();
391 assert_eq!(
392 status_cache.get_status(sig, &blockhash, &Ancestors::default()),
393 None
394 );
395 assert_eq!(
396 status_cache.get_status_any_blockhash(sig, &Ancestors::default()),
397 None
398 );
399 }
400
401 #[test]
402 fn test_find_sig_with_ancestor_fork() {
403 let sig = Signature::default();
404 let mut status_cache = BankStatusCache::default();
405 let blockhash = hash(Hash::default().as_ref());
406 let ancestors = Ancestors::from(vec![0]);
407 status_cache.insert(&blockhash, sig, 0, ());
408 assert_eq!(
409 status_cache.get_status(sig, &blockhash, &ancestors),
410 Some((0, ()))
411 );
412 assert_eq!(
413 status_cache.get_status_any_blockhash(sig, &ancestors),
414 Some((0, ()))
415 );
416 }
417
418 #[test]
419 fn test_find_sig_without_ancestor_fork() {
420 let sig = Signature::default();
421 let mut status_cache = BankStatusCache::default();
422 let blockhash = hash(Hash::default().as_ref());
423 let ancestors = Ancestors::default();
424 status_cache.insert(&blockhash, sig, 1, ());
425 assert_eq!(status_cache.get_status(sig, &blockhash, &ancestors), None);
426 assert_eq!(status_cache.get_status_any_blockhash(sig, &ancestors), None);
427 }
428
429 #[test]
430 fn test_find_sig_with_root_ancestor_fork() {
431 let sig = Signature::default();
432 let mut status_cache = BankStatusCache::default();
433 let blockhash = hash(Hash::default().as_ref());
434 let ancestors = Ancestors::default();
435 status_cache.insert(&blockhash, sig, 0, ());
436 status_cache.add_root(0);
437 assert_eq!(
438 status_cache.get_status(sig, &blockhash, &ancestors),
439 Some((0, ()))
440 );
441 }
442
443 #[test]
444 fn test_insert_picks_latest_blockhash_fork() {
445 let sig = Signature::default();
446 let mut status_cache = BankStatusCache::default();
447 let blockhash = hash(Hash::default().as_ref());
448 let ancestors = Ancestors::from(vec![0]);
449 status_cache.insert(&blockhash, sig, 0, ());
450 status_cache.insert(&blockhash, sig, 1, ());
451 for i in 0..=status_cache.max_root_entries() {
452 status_cache.add_root(i as u64);
453 }
454 assert!(
455 status_cache
456 .get_status(sig, &blockhash, &ancestors)
457 .is_some()
458 );
459 }
460
461 #[test]
462 fn test_root_expires() {
463 let sig = Signature::default();
464 let mut status_cache = BankStatusCache::default();
465 let blockhash = hash(Hash::default().as_ref());
466 let ancestors = Ancestors::default();
467 status_cache.insert(&blockhash, sig, 0, ());
468 for i in 0..=status_cache.max_root_entries() {
469 status_cache.add_root(i as u64);
470 }
471 assert_eq!(status_cache.get_status(sig, &blockhash, &ancestors), None);
472 }
473
474 #[test]
475 fn test_max_root_entries() {
476 const INITIAL_MAX_ROOT_ENTRIES: usize = 4;
477 const GROWN_MAX_ROOT_ENTRIES: usize = 6;
478 const SHRUNK_MAX_ROOT_ENTRIES: usize = 3;
479
480 let mut status_cache = BankStatusCache::default();
481 let ancestors = Ancestors::default();
482 let oldest_sig = Signature::from([1_u8; 64]);
483 let oldest_blockhash = Hash::new_unique();
484 let newest_sig = Signature::from([2_u8; 64]);
485 let newest_blockhash = Hash::new_unique();
486
487 status_cache.set_max_root_entries(NonZeroUsize::new(INITIAL_MAX_ROOT_ENTRIES).unwrap());
488
489 status_cache.insert(&oldest_blockhash, oldest_sig, 0, ());
490 status_cache.insert(&newest_blockhash, newest_sig, 3, ());
491 for root in 1..INITIAL_MAX_ROOT_ENTRIES as Slot {
492 status_cache.add_root(root);
493 }
494
495 assert_eq!(status_cache.roots(), &HashSet::from([0_u64, 1, 2, 3]));
496 assert_eq!(
497 status_cache.get_status(oldest_sig, &oldest_blockhash, &ancestors),
498 Some((0, ()))
499 );
500 assert_eq!(
501 status_cache.get_status(newest_sig, &newest_blockhash, &ancestors),
502 Some((3, ()))
503 );
504
505 status_cache.set_max_root_entries(NonZeroUsize::new(GROWN_MAX_ROOT_ENTRIES).unwrap());
506 for root in INITIAL_MAX_ROOT_ENTRIES as Slot..GROWN_MAX_ROOT_ENTRIES as Slot {
507 status_cache.add_root(root);
508 }
509
510 assert_eq!(status_cache.roots(), &HashSet::from([0_u64, 1, 2, 3, 4, 5]));
511 assert_eq!(
512 status_cache.get_status(oldest_sig, &oldest_blockhash, &ancestors),
513 Some((0, ()))
514 );
515 assert_eq!(
516 status_cache.get_status(newest_sig, &newest_blockhash, &ancestors),
517 Some((3, ()))
518 );
519
520 status_cache.set_max_root_entries(NonZeroUsize::new(SHRUNK_MAX_ROOT_ENTRIES).unwrap());
521
522 assert_eq!(status_cache.roots(), &HashSet::from([3_u64, 4, 5]));
523 assert_eq!(
524 status_cache.get_status(oldest_sig, &oldest_blockhash, &ancestors),
525 None
526 );
527 assert_eq!(
528 status_cache.get_status(newest_sig, &newest_blockhash, &ancestors),
529 Some((3, ()))
530 );
531 assert!(!status_cache.cache.contains_key(&oldest_blockhash));
532 assert!(status_cache.cache.contains_key(&newest_blockhash));
533 assert!(!status_cache.slot_deltas.contains_key(&0));
534 assert!(status_cache.slot_deltas.contains_key(&3));
535 }
536
537 #[test]
538 fn test_clear_signatures_sigs_are_gone() {
539 let sig = Signature::default();
540 let mut status_cache = BankStatusCache::default();
541 let blockhash = hash(Hash::default().as_ref());
542 let ancestors = Ancestors::default();
543 status_cache.insert(&blockhash, sig, 0, ());
544 status_cache.add_root(0);
545 status_cache.clear();
546 assert_eq!(status_cache.get_status(sig, &blockhash, &ancestors), None);
547 }
548
549 #[test]
550 fn test_clear_signatures_insert_works() {
551 let sig = Signature::default();
552 let mut status_cache = BankStatusCache::default();
553 let blockhash = hash(Hash::default().as_ref());
554 let ancestors = Ancestors::default();
555 status_cache.add_root(0);
556 status_cache.clear();
557 status_cache.insert(&blockhash, sig, 0, ());
558 assert!(
559 status_cache
560 .get_status(sig, &blockhash, &ancestors)
561 .is_some()
562 );
563 }
564
565 #[test]
566 fn test_signatures_slice() {
567 let sig = Signature::default();
568 let mut status_cache = BankStatusCache::default();
569 let blockhash = hash(Hash::default().as_ref());
570 status_cache.clear();
571 status_cache.insert(&blockhash, sig, 0, ());
572 let (_, index, sig_map) = status_cache.cache.get(&blockhash).unwrap();
573 let sig_slice: &[u8; CACHED_KEY_SIZE] =
574 arrayref::array_ref![sig.as_ref(), *index, CACHED_KEY_SIZE];
575 assert!(sig_map.get(sig_slice).is_some());
576 }
577
578 #[test]
579 fn test_slot_deltas() {
580 let sig = Signature::default();
581 let mut status_cache = BankStatusCache::default();
582 let blockhash = hash(Hash::default().as_ref());
583 status_cache.clear();
584 status_cache.insert(&blockhash, sig, 0, ());
585 assert!(status_cache.roots().contains(&0));
586 let slot_deltas = status_cache.root_slot_deltas();
587 let cache = StatusCache::from_slot_deltas(&slot_deltas);
588 assert_eq!(cache, status_cache);
589 let slot_deltas = cache.root_slot_deltas();
590 let cache = StatusCache::from_slot_deltas(&slot_deltas);
591 assert_eq!(cache, status_cache);
592 }
593
594 #[test]
595 fn test_roots_deltas() {
596 let sig = Signature::default();
597 let mut status_cache = BankStatusCache::default();
598 let blockhash = hash(Hash::default().as_ref());
599 let blockhash2 = hash(blockhash.as_ref());
600 status_cache.insert(&blockhash, sig, 0, ());
601 status_cache.insert(&blockhash, sig, 1, ());
602 status_cache.insert(&blockhash2, sig, 1, ());
603 for i in 0..=status_cache.max_root_entries() {
604 status_cache.add_root(i as u64);
605 }
606 assert_eq!(status_cache.slot_deltas.len(), 1);
607 assert!(status_cache.slot_deltas.contains_key(&1));
608 let slot_deltas = status_cache.root_slot_deltas();
609 let cache = StatusCache::from_slot_deltas(&slot_deltas);
610 assert_eq!(cache, status_cache);
611 }
612
613 #[test]
614 fn test_clear_slot_signatures() {
615 let sig = Signature::default();
616 let mut status_cache = BankStatusCache::default();
617 let blockhash = hash(Hash::default().as_ref());
618 let blockhash2 = hash(blockhash.as_ref());
619 status_cache.insert(&blockhash, sig, 0, ());
620 status_cache.insert(&blockhash, sig, 1, ());
621 status_cache.insert(&blockhash2, sig, 1, ());
622
623 let mut ancestors0 = Ancestors::default();
624 ancestors0.insert(0);
625 let mut ancestors1 = Ancestors::default();
626 ancestors1.insert(1);
627
628 assert!(
630 status_cache
631 .get_status(sig, &blockhash, &ancestors0)
632 .is_some()
633 );
634 status_cache.clear_slot_entries(0);
635 assert!(
636 status_cache
637 .get_status(sig, &blockhash, &ancestors0)
638 .is_none()
639 );
640 assert!(
641 status_cache
642 .get_status(sig, &blockhash, &ancestors1)
643 .is_some()
644 );
645 assert!(
646 status_cache
647 .get_status(sig, &blockhash2, &ancestors1)
648 .is_some()
649 );
650
651 assert!(!status_cache.slot_deltas.contains_key(&0));
654 assert!(status_cache.slot_deltas.contains_key(&1));
655
656 status_cache.clear_slot_entries(1);
658 assert!(status_cache.slot_deltas.is_empty());
659 assert!(
660 status_cache
661 .get_status(sig, &blockhash, &ancestors1)
662 .is_none()
663 );
664 assert!(
665 status_cache
666 .get_status(sig, &blockhash2, &ancestors1)
667 .is_none()
668 );
669 assert!(status_cache.cache.is_empty());
670 }
671
672 #[test]
673 fn test_clear_invalid_slot_signatures() {
674 let mut status_cache = BankStatusCache::default();
675 let blockhash = hash(Hash::default().as_ref());
676 let sig = Signature::default();
677 status_cache.insert(&blockhash, sig, 0, ());
678 status_cache.insert(&blockhash, sig, 0, ());
681 status_cache.clear_slot_entries(0);
683 }
684
685 #[test]
688 fn test_different_sized_keys() {
689 let mut status_cache = BankStatusCache::default();
690 let ancestors = Ancestors::from(vec![0]);
691 let blockhash = Hash::default();
692 for _ in 0..100 {
693 let blockhash = hash(blockhash.as_ref());
694 let sig_key = Signature::default();
695 let hash_key = Hash::new_unique();
696 status_cache.insert(&blockhash, sig_key, 0, ());
697 status_cache.insert(&blockhash, hash_key, 0, ());
698 assert!(
699 status_cache
700 .get_status(sig_key, &blockhash, &ancestors)
701 .is_some()
702 );
703 assert!(
704 status_cache
705 .get_status(hash_key, &blockhash, &ancestors)
706 .is_some()
707 );
708 }
709 }
710}
711
712#[cfg(all(test, feature = "shuttle-test"))]
713mod shuttle_tests {
714 use {super::*, shuttle::sync::RwLock};
715
716 type BankStatusCache = RwLock<StatusCache<()>>;
717
718 const CLEAR_DFS_ITERATIONS: Option<usize> = None;
719 const CLEAR_RANDOM_ITERATIONS: usize = 20000;
720 const PURGE_DFS_ITERATIONS: Option<usize> = None;
721 const PURGE_RANDOM_ITERATIONS: usize = 8000;
722 const INSERT_DFS_ITERATIONS: Option<usize> = Some(20000);
723 const INSERT_RANDOM_ITERATIONS: usize = 20000;
724
725 fn do_test_shuttle_clear_slots_blockhash_overlap() {
726 let status_cache = Arc::new(BankStatusCache::default());
727
728 let blockhash1 = Hash::new_from_array([1; 32]);
729
730 let key1 = Hash::new_from_array([3; 32]);
731 let key2 = Hash::new_from_array([4; 32]);
732
733 status_cache
734 .write()
735 .unwrap()
736 .insert(&blockhash1, key1, 1, ());
737 let th_clear = shuttle::thread::spawn({
738 let status_cache = status_cache.clone();
739 move || {
740 status_cache.write().unwrap().clear_slot_entries(1);
741 }
742 });
743
744 let th_insert = shuttle::thread::spawn({
745 let status_cache = status_cache.clone();
746 move || {
747 status_cache
749 .write()
750 .unwrap()
751 .insert(&blockhash1, key2, 2, ());
752 }
753 });
754
755 th_clear.join().unwrap();
756 th_insert.join().unwrap();
757
758 let mut ancestors2 = Ancestors::default();
759 ancestors2.insert(2);
760
761 assert!(
762 status_cache
763 .read()
764 .unwrap()
765 .get_status(key2, &blockhash1, &ancestors2)
766 .is_some()
767 );
768 }
769 #[test]
770 fn test_shuttle_clear_slots_blockhash_overlap_random() {
771 shuttle::check_random(
772 do_test_shuttle_clear_slots_blockhash_overlap,
773 CLEAR_RANDOM_ITERATIONS,
774 );
775 }
776
777 #[test]
778 fn test_shuttle_clear_slots_blockhash_overlap_dfs() {
779 shuttle::check_dfs(
780 do_test_shuttle_clear_slots_blockhash_overlap,
781 CLEAR_DFS_ITERATIONS,
782 );
783 }
784
785 fn do_test_shuttle_purge_nonce_overlap() {
789 let status_cache = Arc::new(BankStatusCache::default());
790 let max_root_entries = status_cache.read().unwrap().max_root_entries();
792 for i in 0..max_root_entries {
793 status_cache.write().unwrap().add_root(i as u64);
794 }
795
796 let blockhash1 = Hash::new_from_array([1; 32]);
797
798 let key1 = Hash::new_from_array([3; 32]);
799 let key2 = Hash::new_from_array([4; 32]);
800
801 status_cache
803 .write()
804 .unwrap()
805 .insert(&blockhash1, key1, 0, ());
806
807 let th_purge = shuttle::thread::spawn({
808 let status_cache = status_cache.clone();
809 move || {
810 status_cache
811 .write()
812 .unwrap()
813 .add_root(max_root_entries as Slot + 1);
814 }
815 });
816
817 let th_insert = shuttle::thread::spawn({
818 let status_cache = status_cache.clone();
819 move || {
820 status_cache.write().unwrap().insert(
822 &blockhash1,
823 key2,
824 max_root_entries as Slot + 2,
825 (),
826 );
827 }
828 });
829 th_purge.join().unwrap();
830 th_insert.join().unwrap();
831
832 let mut ancestors2 = Ancestors::default();
833 ancestors2.insert(max_root_entries as Slot + 2);
834
835 assert!(
836 status_cache
837 .read()
838 .unwrap()
839 .get_status(key1, &blockhash1, &ancestors2)
840 .is_none()
841 );
842 assert!(
843 status_cache
844 .read()
845 .unwrap()
846 .get_status(key2, &blockhash1, &ancestors2)
847 .is_some()
848 );
849 }
850
851 #[test]
852 fn test_shuttle_purge_nonce_overlap_random() {
853 shuttle::check_random(do_test_shuttle_purge_nonce_overlap, PURGE_RANDOM_ITERATIONS);
854 }
855
856 #[test]
857 fn test_shuttle_purge_nonce_overlap_dfs() {
858 shuttle::check_dfs(do_test_shuttle_purge_nonce_overlap, PURGE_DFS_ITERATIONS);
859 }
860
861 fn do_test_shuttle_concurrent_inserts() {
862 let status_cache = Arc::new(BankStatusCache::default());
863 let blockhash1 = Hash::new_from_array([42; 32]);
864 let blockhash2 = Hash::new_from_array([43; 32]);
865 const N_INSERTS: u8 = 50;
866
867 let mut handles = Vec::with_capacity(N_INSERTS as usize);
868 for i in 0..N_INSERTS {
869 let status_cache = status_cache.clone();
870 let slot = (i % 3) + 1;
871 let bh = if i % 2 == 0 { blockhash1 } else { blockhash2 };
872 handles.push(shuttle::thread::spawn(move || {
873 let key = Hash::new_from_array([i; 32]);
874 status_cache
875 .write()
876 .unwrap()
877 .insert(&bh, key, slot as Slot, ());
878 }));
879 }
880
881 for handle in handles {
882 handle.join().unwrap();
883 }
884
885 let mut ancestors = Ancestors::default();
886 ancestors.insert(1);
887 ancestors.insert(2);
888 ancestors.insert(3);
889
890 for i in 0..N_INSERTS {
892 let key = Hash::new_from_array([i; 32]);
893 let bh = if i % 2 == 0 { blockhash1 } else { blockhash2 };
894 assert!(
895 status_cache
896 .read()
897 .unwrap()
898 .get_status(key, &bh, &ancestors)
899 .is_some(),
900 "missing key {}",
901 i
902 );
903 }
904 }
905
906 #[test]
907 fn test_shuttle_concurrent_inserts_dfs() {
908 shuttle::check_dfs(do_test_shuttle_concurrent_inserts, INSERT_DFS_ITERATIONS);
909 }
910
911 #[test]
912 fn test_shuttle_concurrent_inserts_random() {
913 shuttle::check_random(do_test_shuttle_concurrent_inserts, INSERT_RANDOM_ITERATIONS);
914 }
915}