1mod account_map_entry;
2pub(crate) mod in_mem_accounts_index;
3mod iter;
4mod roots_tracker;
5mod secondary;
6use {
7 crate::{
8 accounts_index_storage::{AccountsIndexStorage, Startup},
9 ancestors::Ancestors,
10 bucket_map_holder::Age,
11 bucket_map_holder_stats::BucketMapHolderStats,
12 contains::Contains,
13 is_zero_lamport::IsZeroLamport,
14 pubkey_bins::PubkeyBinCalculator24,
15 rolling_bit_field::RollingBitField,
16 },
17 account_map_entry::{AccountMapEntry, PreAllocatedAccountMapEntry},
18 in_mem_accounts_index::{
19 ExistedLocation, InMemAccountsIndex, InsertNewEntryResults, StartupStats,
20 },
21 iter::{AccountsIndexPubkeyIterOrder, AccountsIndexPubkeyIterator},
22 log::*,
23 rand::{thread_rng, Rng},
24 rayon::iter::{IntoParallelIterator, ParallelIterator},
25 roots_tracker::RootsTracker,
26 secondary::{RwLockSecondaryIndexEntry, SecondaryIndex, SecondaryIndexEntry},
27 solana_account::ReadableAccount,
28 solana_clock::{BankId, Slot},
29 solana_measure::measure::Measure,
30 solana_pubkey::Pubkey,
31 std::{
32 collections::{btree_map::BTreeMap, HashSet},
33 fmt::Debug,
34 num::NonZeroUsize,
35 ops::{Bound, Range, RangeBounds},
36 path::PathBuf,
37 sync::{
38 atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering},
39 Arc, Mutex, RwLock,
40 },
41 },
42 thiserror::Error,
43};
44pub use {
45 iter::ITER_BATCH_SIZE,
46 secondary::{
47 AccountIndex, AccountSecondaryIndexes, AccountSecondaryIndexesIncludeExclude, IndexKey,
48 },
49};
50
51pub const BINS_DEFAULT: usize = 8192;
52pub const BINS_FOR_TESTING: usize = 2; pub const BINS_FOR_BENCHMARKS: usize = 8192;
54pub const FLUSH_THREADS_TESTING: NonZeroUsize = NonZeroUsize::new(1).unwrap();
56pub const ACCOUNTS_INDEX_CONFIG_FOR_TESTING: AccountsIndexConfig = AccountsIndexConfig {
57 bins: Some(BINS_FOR_TESTING),
58 num_flush_threads: Some(FLUSH_THREADS_TESTING),
59 drives: None,
60 index_limit_mb: IndexLimitMb::Minimal,
61 ages_to_stay_in_cache: None,
62 scan_results_limit_bytes: None,
63};
64pub const ACCOUNTS_INDEX_CONFIG_FOR_BENCHMARKS: AccountsIndexConfig = AccountsIndexConfig {
65 bins: Some(BINS_FOR_BENCHMARKS),
66 num_flush_threads: Some(FLUSH_THREADS_TESTING),
67 drives: None,
68 index_limit_mb: IndexLimitMb::Minimal,
69 ages_to_stay_in_cache: None,
70 scan_results_limit_bytes: None,
71};
72pub type ScanResult<T> = Result<T, ScanError>;
73pub type SlotList<T> = Vec<(Slot, T)>;
74pub type RefCount = u64;
75pub type AtomicRefCount = AtomicU64;
76
77#[derive(Default, Debug, PartialEq, Eq)]
79pub(crate) struct InsertNewIfMissingIntoPrimaryIndexInfo {
80 pub count: usize,
82 pub num_did_not_exist: u64,
84 pub num_existed_in_mem: u64,
86 pub num_existed_on_disk: u64,
88}
89
90#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
91pub enum ScanFilter {
93 #[default]
95 All,
96
97 OnlyAbnormal,
100
101 OnlyAbnormalWithVerify,
104
105 OnlyAbnormalTest,
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub enum UpsertReclaim {
118 PreviousSlotEntryWasCached,
120 PopulateReclaims,
123 IgnoreReclaims,
125 ReclaimOldSlots,
128}
129
130#[derive(Debug)]
131pub struct ScanConfig {
132 pub abort: Option<Arc<AtomicBool>>,
134
135 pub scan_order: ScanOrder,
137}
138
139impl Default for ScanConfig {
140 fn default() -> Self {
141 Self {
142 abort: None,
143 scan_order: ScanOrder::Unsorted,
144 }
145 }
146}
147
148impl ScanConfig {
149 pub fn new(scan_order: ScanOrder) -> Self {
150 Self {
151 scan_order,
152 ..Default::default()
153 }
154 }
155
156 pub fn abort(&self) {
158 if let Some(abort) = self.abort.as_ref() {
159 abort.store(true, Ordering::Relaxed)
160 }
161 }
162
163 pub fn recreate_with_abort(&self) -> Self {
165 ScanConfig {
166 abort: Some(self.abort.clone().unwrap_or_default()),
167 scan_order: self.scan_order,
168 }
169 }
170
171 pub fn is_aborted(&self) -> bool {
173 if let Some(abort) = self.abort.as_ref() {
174 abort.load(Ordering::Relaxed)
175 } else {
176 false
177 }
178 }
179}
180
181#[derive(Debug, Copy, Clone, Eq, PartialEq)]
186pub enum ScanOrder {
187 Unsorted,
189 Sorted,
191}
192
193pub trait IsCached {
194 fn is_cached(&self) -> bool;
195}
196
197pub trait IndexValue: 'static + IsCached + IsZeroLamport + DiskIndexValue {}
198
199pub trait DiskIndexValue:
200 'static + Clone + Debug + PartialEq + Copy + Default + Sync + Send
201{
202}
203
204#[derive(Error, Debug, PartialEq, Eq)]
205pub enum ScanError {
206 #[error(
207 "Node detected it replayed bad version of slot {slot:?} with id {bank_id:?}, thus the \
208 scan on said slot was aborted"
209 )]
210 SlotRemoved { slot: Slot, bank_id: BankId },
211 #[error("scan aborted: {0}")]
212 Aborted(String),
213}
214
215enum ScanTypes<R: RangeBounds<Pubkey>> {
216 Unindexed(Option<R>),
217 Indexed(IndexKey),
218}
219
220#[derive(Debug, Copy, Clone)]
222pub enum IndexLimitMb {
223 Minimal,
225 InMemOnly,
227}
228
229#[derive(Debug, Clone)]
230pub struct AccountsIndexConfig {
231 pub bins: Option<usize>,
232 pub num_flush_threads: Option<NonZeroUsize>,
233 pub drives: Option<Vec<PathBuf>>,
234 pub index_limit_mb: IndexLimitMb,
235 pub ages_to_stay_in_cache: Option<Age>,
236 pub scan_results_limit_bytes: Option<usize>,
237}
238
239impl Default for AccountsIndexConfig {
240 fn default() -> Self {
241 Self {
242 bins: None,
243 num_flush_threads: None,
244 drives: None,
245 index_limit_mb: IndexLimitMb::Minimal,
246 ages_to_stay_in_cache: None,
247 scan_results_limit_bytes: None,
248 }
249 }
250}
251
252pub fn default_num_flush_threads() -> NonZeroUsize {
253 NonZeroUsize::new(std::cmp::max(2, num_cpus::get() / 4)).expect("non-zero system threads")
254}
255
256#[derive(Debug, Default)]
257pub struct AccountsIndexRootsStats {
258 pub roots_len: Option<usize>,
259 pub uncleaned_roots_len: Option<usize>,
260 pub roots_range: Option<u64>,
261 pub rooted_cleaned_count: usize,
262 pub unrooted_cleaned_count: usize,
263 pub clean_unref_from_storage_us: u64,
264 pub clean_dead_slot_us: u64,
265}
266
267#[derive(Copy, Clone)]
268pub enum AccountsIndexScanResult {
269 OnlyKeepInMemoryIfDirty,
271 KeepInMemory,
273 Unref,
275 UnrefAssert0,
277 UnrefLog0,
279}
280
281#[derive(Debug)]
282pub struct AccountsIndex<T: IndexValue, U: DiskIndexValue + From<T> + Into<T>> {
285 pub account_maps: Box<[Arc<InMemAccountsIndex<T, U>>]>,
286 pub bin_calculator: PubkeyBinCalculator24,
287 program_id_index: SecondaryIndex<RwLockSecondaryIndexEntry>,
288 spl_token_mint_index: SecondaryIndex<RwLockSecondaryIndexEntry>,
289 spl_token_owner_index: SecondaryIndex<RwLockSecondaryIndexEntry>,
290 pub roots_tracker: RwLock<RootsTracker>,
291 ongoing_scan_roots: RwLock<BTreeMap<Slot, u64>>,
292 pub removed_bank_ids: Mutex<HashSet<BankId>>,
303
304 storage: AccountsIndexStorage<T, U>,
305
306 pub scan_results_limit_bytes: Option<usize>,
308
309 pub purge_older_root_entries_one_slot_list: AtomicUsize,
310
311 pub roots_added: AtomicUsize,
313 pub roots_removed: AtomicUsize,
315 pub active_scans: AtomicUsize,
317 pub max_distance_to_min_scan_slot: AtomicU64,
319}
320
321impl<T: IndexValue, U: DiskIndexValue + From<T> + Into<T>> AccountsIndex<T, U> {
322 pub fn default_for_tests() -> Self {
323 Self::new(&ACCOUNTS_INDEX_CONFIG_FOR_TESTING, Arc::default())
324 }
325
326 pub fn new(config: &AccountsIndexConfig, exit: Arc<AtomicBool>) -> Self {
327 let scan_results_limit_bytes = config.scan_results_limit_bytes;
328 let (account_maps, bin_calculator, storage) = Self::allocate_accounts_index(config, exit);
329 Self {
330 purge_older_root_entries_one_slot_list: AtomicUsize::default(),
331 account_maps,
332 bin_calculator,
333 program_id_index: SecondaryIndex::<RwLockSecondaryIndexEntry>::new(
334 "program_id_index_stats",
335 ),
336 spl_token_mint_index: SecondaryIndex::<RwLockSecondaryIndexEntry>::new(
337 "spl_token_mint_index_stats",
338 ),
339 spl_token_owner_index: SecondaryIndex::<RwLockSecondaryIndexEntry>::new(
340 "spl_token_owner_index_stats",
341 ),
342 roots_tracker: RwLock::<RootsTracker>::default(),
343 ongoing_scan_roots: RwLock::<BTreeMap<Slot, u64>>::default(),
344 removed_bank_ids: Mutex::<HashSet<BankId>>::default(),
345 storage,
346 scan_results_limit_bytes,
347 roots_added: AtomicUsize::default(),
348 roots_removed: AtomicUsize::default(),
349 active_scans: AtomicUsize::default(),
350 max_distance_to_min_scan_slot: AtomicU64::default(),
351 }
352 }
353
354 fn bin_from_pubkey(&self, pubkey: &Pubkey) -> usize {
356 self.bin_calculator.bin_from_pubkey(pubkey)
357 }
358
359 fn bin_start_end_inclusive<R>(&self, range: &R) -> (usize, usize)
366 where
367 R: RangeBounds<Pubkey>,
368 {
369 let start_bin = match range.start_bound() {
370 Bound::Included(start) => self.bin_from_pubkey(start),
371 Bound::Excluded(start) => {
372 let start_bin = self.bin_from_pubkey(start);
375 if start == &self.account_maps[start_bin].highest_pubkey {
376 start_bin + 1
377 } else {
378 start_bin
379 }
380 }
381 Bound::Unbounded => 0,
382 };
383
384 let end_bin_inclusive = match range.end_bound() {
385 Bound::Included(end) => self.bin_from_pubkey(end),
386 Bound::Excluded(end) => {
387 let end_bin = self.bin_from_pubkey(end);
390 if end == &self.account_maps[end_bin].lowest_pubkey {
391 end_bin.saturating_sub(1)
392 } else {
393 end_bin
394 }
395 }
396 Bound::Unbounded => self.account_maps.len().saturating_sub(1),
397 };
398
399 (start_bin, end_bin_inclusive)
400 }
401
402 #[allow(clippy::type_complexity)]
403 fn allocate_accounts_index(
404 config: &AccountsIndexConfig,
405 exit: Arc<AtomicBool>,
406 ) -> (
407 Box<[Arc<InMemAccountsIndex<T, U>>]>,
408 PubkeyBinCalculator24,
409 AccountsIndexStorage<T, U>,
410 ) {
411 let bins = config.bins.unwrap_or(BINS_DEFAULT);
412 let bin_calculator = PubkeyBinCalculator24::new(bins);
414 let storage = AccountsIndexStorage::new(bins, config, exit);
415
416 let account_maps: Box<_> = (0..bins)
417 .map(|bin| Arc::clone(&storage.in_mem[bin]))
418 .collect();
419 (account_maps, bin_calculator, storage)
420 }
421
422 fn iter<'a, R>(
423 &'a self,
424 range: Option<&'a R>,
425 iter_order: AccountsIndexPubkeyIterOrder,
426 ) -> AccountsIndexPubkeyIterator<'a, T, U>
427 where
428 R: RangeBounds<Pubkey>,
429 {
430 AccountsIndexPubkeyIterator::new(self, range, iter_order)
431 }
432
433 pub fn is_disk_index_enabled(&self) -> bool {
435 self.storage.storage.is_disk_index_enabled()
436 }
437
438 fn min_ongoing_scan_root_from_btree(ongoing_scan_roots: &BTreeMap<Slot, u64>) -> Option<Slot> {
439 ongoing_scan_roots.keys().next().cloned()
440 }
441
442 fn do_checked_scan_accounts<F, R>(
443 &self,
444 metric_name: &'static str,
445 ancestors: &Ancestors,
446 scan_bank_id: BankId,
447 func: F,
448 scan_type: ScanTypes<R>,
449 config: &ScanConfig,
450 ) -> Result<(), ScanError>
451 where
452 F: FnMut(&Pubkey, (&T, Slot)),
453 R: RangeBounds<Pubkey> + std::fmt::Debug,
454 {
455 {
456 let locked_removed_bank_ids = self.removed_bank_ids.lock().unwrap();
457 if locked_removed_bank_ids.contains(&scan_bank_id) {
458 return Err(ScanError::SlotRemoved {
459 slot: ancestors.max_slot(),
460 bank_id: scan_bank_id,
461 });
462 }
463 }
464
465 self.active_scans.fetch_add(1, Ordering::Relaxed);
466 let max_root = {
467 let mut w_ongoing_scan_roots = self
468 .ongoing_scan_roots
473 .write()
474 .unwrap();
475 let max_root_inclusive = self.max_root_inclusive();
480 if let Some(min_ongoing_scan_root) =
481 Self::min_ongoing_scan_root_from_btree(&w_ongoing_scan_roots)
482 {
483 if min_ongoing_scan_root < max_root_inclusive {
484 let current = max_root_inclusive - min_ongoing_scan_root;
485 self.max_distance_to_min_scan_slot
486 .fetch_max(current, Ordering::Relaxed);
487 }
488 }
489 *w_ongoing_scan_roots.entry(max_root_inclusive).or_default() += 1;
490
491 max_root_inclusive
492 };
493
494 let empty = Ancestors::default();
545 let ancestors = if ancestors.contains_key(&max_root) {
546 ancestors
547 } else {
548 &empty
569 };
570
571 match scan_type {
610 ScanTypes::Unindexed(range) => {
611 self.do_scan_accounts(metric_name, ancestors, func, range, Some(max_root), config);
613 }
614 ScanTypes::Indexed(IndexKey::ProgramId(program_id)) => {
615 self.do_scan_secondary_index(
616 ancestors,
617 func,
618 &self.program_id_index,
619 &program_id,
620 Some(max_root),
621 config,
622 );
623 }
624 ScanTypes::Indexed(IndexKey::SplTokenMint(mint_key)) => {
625 self.do_scan_secondary_index(
626 ancestors,
627 func,
628 &self.spl_token_mint_index,
629 &mint_key,
630 Some(max_root),
631 config,
632 );
633 }
634 ScanTypes::Indexed(IndexKey::SplTokenOwner(owner_key)) => {
635 self.do_scan_secondary_index(
636 ancestors,
637 func,
638 &self.spl_token_owner_index,
639 &owner_key,
640 Some(max_root),
641 config,
642 );
643 }
644 }
645
646 {
647 self.active_scans.fetch_sub(1, Ordering::Relaxed);
648 let mut ongoing_scan_roots = self.ongoing_scan_roots.write().unwrap();
649 let count = ongoing_scan_roots.get_mut(&max_root).unwrap();
650 *count -= 1;
651 if *count == 0 {
652 ongoing_scan_roots.remove(&max_root);
653 }
654 }
655
656 let was_scan_corrupted = self
659 .removed_bank_ids
660 .lock()
661 .unwrap()
662 .contains(&scan_bank_id);
663
664 if was_scan_corrupted {
665 Err(ScanError::SlotRemoved {
666 slot: ancestors.max_slot(),
667 bank_id: scan_bank_id,
668 })
669 } else {
670 Ok(())
671 }
672 }
673
674 #[cfg(feature = "dev-context-only-utils")]
675 pub fn do_unchecked_scan_accounts<F, R>(
676 &self,
677 metric_name: &'static str,
678 ancestors: &Ancestors,
679 func: F,
680 range: Option<R>,
681 config: &ScanConfig,
682 ) where
683 F: FnMut(&Pubkey, (&T, Slot)),
684 R: RangeBounds<Pubkey> + std::fmt::Debug,
685 {
686 self.do_scan_accounts(metric_name, ancestors, func, range, None, config);
687 }
688
689 fn do_scan_accounts<F, R>(
693 &self,
694 metric_name: &'static str,
695 ancestors: &Ancestors,
696 mut func: F,
697 range: Option<R>,
698 max_root: Option<Slot>,
699 config: &ScanConfig,
700 ) where
701 F: FnMut(&Pubkey, (&T, Slot)),
702 R: RangeBounds<Pubkey> + std::fmt::Debug,
703 {
704 let returns_items = match config.scan_order {
705 ScanOrder::Unsorted => AccountsIndexPubkeyIterOrder::Unsorted,
706 ScanOrder::Sorted => AccountsIndexPubkeyIterOrder::Sorted,
707 };
708
709 let mut total_elapsed_timer = Measure::start("total");
712 let mut num_keys_iterated = 0;
713 let mut latest_slot_elapsed = 0;
714 let mut load_account_elapsed = 0;
715 let mut read_lock_elapsed = 0;
716 let mut iterator_elapsed = 0;
717 let mut iterator_timer = Measure::start("iterator_elapsed");
718
719 for pubkeys in self.iter(range.as_ref(), returns_items) {
720 iterator_timer.stop();
721 iterator_elapsed += iterator_timer.as_us();
722 for pubkey in pubkeys {
723 num_keys_iterated += 1;
724 self.get_and_then(&pubkey, |entry| {
725 if let Some(list) = entry {
726 let mut read_lock_timer = Measure::start("read_lock");
727 let list_r = &list.slot_list.read().unwrap();
728 read_lock_timer.stop();
729 read_lock_elapsed += read_lock_timer.as_us();
730 let mut latest_slot_timer = Measure::start("latest_slot");
731 if let Some(index) = self.latest_slot(Some(ancestors), list_r, max_root) {
732 latest_slot_timer.stop();
733 latest_slot_elapsed += latest_slot_timer.as_us();
734 let mut load_account_timer = Measure::start("load_account");
735 func(&pubkey, (&list_r[index].1, list_r[index].0));
736 load_account_timer.stop();
737 load_account_elapsed += load_account_timer.as_us();
738 }
739 }
740 let add_to_in_mem_cache = false;
741 (add_to_in_mem_cache, ())
742 });
743 if config.is_aborted() {
744 return;
745 }
746 }
747 iterator_timer = Measure::start("iterator_elapsed");
748 }
749
750 total_elapsed_timer.stop();
751 if !metric_name.is_empty() {
752 datapoint_info!(
753 metric_name,
754 ("total_elapsed", total_elapsed_timer.as_us(), i64),
755 ("latest_slot_elapsed", latest_slot_elapsed, i64),
756 ("read_lock_elapsed", read_lock_elapsed, i64),
757 ("load_account_elapsed", load_account_elapsed, i64),
758 ("iterator_elapsed", iterator_elapsed, i64),
759 ("num_keys_iterated", num_keys_iterated, i64),
760 )
761 }
762 }
763
764 fn do_scan_secondary_index<
765 F,
766 SecondaryIndexEntryType: SecondaryIndexEntry + Default + Sync + Send,
767 >(
768 &self,
769 ancestors: &Ancestors,
770 mut func: F,
771 index: &SecondaryIndex<SecondaryIndexEntryType>,
772 index_key: &Pubkey,
773 max_root: Option<Slot>,
774 config: &ScanConfig,
775 ) where
776 F: FnMut(&Pubkey, (&T, Slot)),
777 {
778 for pubkey in index.get(index_key) {
779 if config.is_aborted() {
780 break;
781 }
782 if let Some(entry) = self.get_cloned(&pubkey) {
783 self.get_account_info_with_and_then(
784 &entry,
785 Some(ancestors),
786 max_root,
787 |(slot, account_info)| func(&pubkey, (&account_info, slot)),
788 );
789 };
790 }
791 }
792
793 pub fn get_and_then<R>(
797 &self,
798 pubkey: &Pubkey,
799 callback: impl FnOnce(Option<&AccountMapEntry<T>>) -> (bool, R),
800 ) -> R {
801 self.get_bin(pubkey).get_internal_inner(pubkey, callback)
802 }
803
804 pub(crate) fn get_with_and_then<R>(
807 &self,
808 pubkey: &Pubkey,
809 ancestors: Option<&Ancestors>,
810 max_root: Option<Slot>,
811 should_add_to_in_mem_cache: bool,
812 callback: impl FnOnce((Slot, T)) -> R,
813 ) -> Option<R> {
814 self.get_and_then(pubkey, |entry| {
815 let callback_result = entry.and_then(|entry| {
816 self.get_account_info_with_and_then(entry, ancestors, max_root, callback)
817 });
818 (should_add_to_in_mem_cache, callback_result)
819 })
820 }
821
822 pub(crate) fn get_account_info_with_and_then<R>(
825 &self,
826 entry: &AccountMapEntry<T>,
827 ancestors: Option<&Ancestors>,
828 max_root: Option<Slot>,
829 callback: impl FnOnce((Slot, T)) -> R,
830 ) -> Option<R> {
831 let slot_list = entry.slot_list.read().unwrap();
832 self.latest_slot(ancestors, &slot_list, max_root)
833 .map(|found_index| callback(slot_list[found_index]))
834 }
835
836 pub fn get_cloned(&self, pubkey: &Pubkey) -> Option<Arc<AccountMapEntry<T>>> {
840 self.get_bin(pubkey)
841 .get_internal_cloned(pubkey, |entry| entry)
842 }
843
844 pub fn contains(&self, pubkey: &Pubkey) -> bool {
846 self.get_and_then(pubkey, |entry| (false, entry.is_some()))
847 }
848
849 #[cfg(test)]
851 pub(crate) fn contains_with(
852 &self,
853 pubkey: &Pubkey,
854 ancestors: Option<&Ancestors>,
855 max_root: Option<Slot>,
856 ) -> bool {
857 self.get_with_and_then(pubkey, ancestors, max_root, false, |_| ())
858 .is_some()
859 }
860
861 fn slot_list_mut<RT>(
862 &self,
863 pubkey: &Pubkey,
864 user_fn: impl FnOnce(&mut SlotList<T>) -> RT,
865 ) -> Option<RT> {
866 let read_lock = self.get_bin(pubkey);
867 read_lock.slot_list_mut(pubkey, user_fn)
868 }
869
870 #[must_use]
873 pub fn handle_dead_keys(
874 &self,
875 dead_keys: &[&Pubkey],
876 account_indexes: &AccountSecondaryIndexes,
877 ) -> HashSet<Pubkey> {
878 let mut pubkeys_removed_from_accounts_index = HashSet::default();
879 if !dead_keys.is_empty() {
880 for key in dead_keys.iter() {
881 let w_index = self.get_bin(key);
882 if w_index.remove_if_slot_list_empty(**key) {
883 pubkeys_removed_from_accounts_index.insert(**key);
884 self.purge_secondary_indexes_by_inner_key(key, account_indexes);
888 }
889 }
890 }
891 pubkeys_removed_from_accounts_index
892 }
893
894 pub(crate) fn scan_accounts<F>(
896 &self,
897 ancestors: &Ancestors,
898 scan_bank_id: BankId,
899 func: F,
900 config: &ScanConfig,
901 ) -> Result<(), ScanError>
902 where
903 F: FnMut(&Pubkey, (&T, Slot)),
904 {
905 self.do_checked_scan_accounts(
907 "",
908 ancestors,
909 scan_bank_id,
910 func,
911 ScanTypes::Unindexed(None::<Range<Pubkey>>),
912 config,
913 )
914 }
915
916 #[cfg(feature = "dev-context-only-utils")]
917 pub(crate) fn unchecked_scan_accounts<F>(
918 &self,
919 metric_name: &'static str,
920 ancestors: &Ancestors,
921 func: F,
922 config: &ScanConfig,
923 ) where
924 F: FnMut(&Pubkey, (&T, Slot)),
925 {
926 self.do_unchecked_scan_accounts(
927 metric_name,
928 ancestors,
929 func,
930 None::<Range<Pubkey>>,
931 config,
932 );
933 }
934 pub(crate) fn index_scan_accounts<F>(
936 &self,
937 ancestors: &Ancestors,
938 scan_bank_id: BankId,
939 index_key: IndexKey,
940 func: F,
941 config: &ScanConfig,
942 ) -> Result<(), ScanError>
943 where
944 F: FnMut(&Pubkey, (&T, Slot)),
945 {
946 self.do_checked_scan_accounts(
948 "",
949 ancestors,
950 scan_bank_id,
951 func,
952 ScanTypes::<Range<Pubkey>>::Indexed(index_key),
953 config,
954 )
955 }
956
957 pub fn get_rooted_entries(
958 &self,
959 slot_list: &[(Slot, T)],
960 max_inclusive: Option<Slot>,
961 ) -> SlotList<T> {
962 let max_inclusive = max_inclusive.unwrap_or(Slot::MAX);
963 let lock = &self.roots_tracker.read().unwrap().alive_roots;
964 slot_list
965 .iter()
966 .filter(|(slot, _)| *slot <= max_inclusive && lock.contains(slot))
967 .cloned()
968 .collect()
969 }
970
971 pub(crate) fn purge_exact<'a, C>(
975 &'a self,
976 pubkey: &Pubkey,
977 slots_to_purge: &'a C,
978 reclaims: &mut SlotList<T>,
979 ) -> bool
980 where
981 C: Contains<'a, Slot>,
982 {
983 self.slot_list_mut(pubkey, |slot_list| {
984 slot_list.retain(|(slot, item)| {
985 let should_purge = slots_to_purge.contains(slot);
986 if should_purge {
987 reclaims.push((*slot, *item));
988 false
989 } else {
990 true
991 }
992 });
993 slot_list.is_empty()
994 })
995 .unwrap_or(true)
996 }
997
998 pub fn min_ongoing_scan_root(&self) -> Option<Slot> {
999 Self::min_ongoing_scan_root_from_btree(&self.ongoing_scan_roots.read().unwrap())
1000 }
1001
1002 pub(crate) fn latest_slot(
1005 &self,
1006 ancestors: Option<&Ancestors>,
1007 slot_list: &[(Slot, T)],
1008 max_root_inclusive: Option<Slot>,
1009 ) -> Option<usize> {
1010 let mut current_max = 0;
1011 let mut rv = None;
1012 if let Some(ancestors) = ancestors {
1013 if !ancestors.is_empty() {
1014 for (i, (slot, _t)) in slot_list.iter().rev().enumerate() {
1015 if (rv.is_none() || *slot > current_max) && ancestors.contains_key(slot) {
1016 rv = Some(i);
1017 current_max = *slot;
1018 }
1019 }
1020 }
1021 }
1022
1023 let max_root_inclusive = max_root_inclusive.unwrap_or(Slot::MAX);
1024 let mut tracker = None;
1025
1026 for (i, (slot, _t)) in slot_list.iter().rev().enumerate() {
1027 if (rv.is_none() || *slot > current_max) && *slot <= max_root_inclusive {
1028 let lock = match tracker {
1029 Some(inner) => inner,
1030 None => self.roots_tracker.read().unwrap(),
1031 };
1032 if lock.alive_roots.contains(slot) {
1033 rv = Some(i);
1034 current_max = *slot;
1035 }
1036 tracker = Some(lock);
1037 }
1038 }
1039
1040 rv.map(|index| slot_list.len() - 1 - index)
1041 }
1042
1043 pub(crate) fn bucket_map_holder_stats(&self) -> &BucketMapHolderStats {
1044 &self.storage.storage.stats
1045 }
1046
1047 pub(crate) fn get_startup_stats(&self) -> &StartupStats {
1049 &self.storage.storage.startup_stats
1050 }
1051
1052 pub fn set_startup(&self, value: Startup) {
1053 self.storage.set_startup(value);
1054 }
1055
1056 pub fn get_startup_remaining_items_to_flush_estimate(&self) -> usize {
1057 self.storage.get_startup_remaining_items_to_flush_estimate()
1058 }
1059
1060 pub(crate) fn scan<'a, F, I>(
1079 &self,
1080 pubkeys: I,
1081 mut callback: F,
1082 avoid_callback_result: Option<AccountsIndexScanResult>,
1083 provide_entry_in_callback: bool,
1084 filter: ScanFilter,
1085 ) where
1086 F: FnMut(
1087 &'a Pubkey,
1088 Option<(&SlotList<T>, RefCount)>,
1089 Option<&Arc<AccountMapEntry<T>>>,
1090 ) -> AccountsIndexScanResult,
1091 I: Iterator<Item = &'a Pubkey>,
1092 {
1093 let mut lock = None;
1094 let mut last_bin = self.bins(); pubkeys.into_iter().for_each(|pubkey| {
1096 let bin = self.bin_calculator.bin_from_pubkey(pubkey);
1097 if bin != last_bin {
1098 lock = Some(&self.account_maps[bin]);
1100 last_bin = bin;
1101 }
1102
1103 let mut internal_callback = |entry: Option<&Arc<AccountMapEntry<T>>>| {
1104 let mut cache = false;
1105 match entry {
1106 Some(locked_entry) => {
1107 let result = if let Some(result) = avoid_callback_result.as_ref() {
1108 *result
1109 } else {
1110 let slot_list = &locked_entry.slot_list.read().unwrap();
1111 callback(
1112 pubkey,
1113 Some((slot_list, locked_entry.ref_count())),
1114 provide_entry_in_callback.then_some(locked_entry),
1115 )
1116 };
1117 cache = match result {
1118 AccountsIndexScanResult::Unref => {
1119 locked_entry.unref();
1120 true
1121 }
1122 AccountsIndexScanResult::UnrefAssert0 => {
1123 assert_eq!(
1124 locked_entry.unref(),
1125 1,
1126 "ref count expected to be zero, but is {}! {pubkey}, {:?}",
1127 locked_entry.ref_count(),
1128 locked_entry.slot_list.read().unwrap(),
1129 );
1130 true
1131 }
1132 AccountsIndexScanResult::UnrefLog0 => {
1133 let old_ref = locked_entry.unref();
1134 if old_ref != 1 {
1135 info!(
1136 "Unexpected unref {pubkey} with {old_ref} {:?}, expect \
1137 old_ref to be 1",
1138 locked_entry.slot_list.read().unwrap()
1139 );
1140 datapoint_warn!(
1141 "accounts_db-unexpected-unref-zero",
1142 ("old_ref", old_ref, i64),
1143 ("pubkey", pubkey.to_string(), String),
1144 );
1145 }
1146 true
1147 }
1148 AccountsIndexScanResult::KeepInMemory => true,
1149 AccountsIndexScanResult::OnlyKeepInMemoryIfDirty => false,
1150 };
1151 }
1152 None => {
1153 avoid_callback_result.unwrap_or_else(|| callback(pubkey, None, None));
1154 }
1155 }
1156 (cache, ())
1157 };
1158
1159 match filter {
1160 ScanFilter::All => {
1161 lock.as_ref()
1165 .unwrap()
1166 .get_internal(pubkey, internal_callback);
1167 }
1168 ScanFilter::OnlyAbnormal
1169 | ScanFilter::OnlyAbnormalWithVerify
1170 | ScanFilter::OnlyAbnormalTest => {
1171 let found =
1172 lock.as_ref()
1173 .unwrap()
1174 .get_only_in_mem(pubkey, false, |mut entry| {
1175 if entry.is_some() && matches!(filter, ScanFilter::OnlyAbnormalTest)
1176 {
1177 let local_entry = entry.unwrap();
1178 if local_entry.ref_count() == 1
1179 && local_entry.slot_list.read().unwrap().len() == 1
1180 {
1181 entry = None;
1185 }
1186 }
1187 internal_callback(entry);
1188 entry.is_some()
1189 });
1190 if !found && matches!(filter, ScanFilter::OnlyAbnormalWithVerify) {
1191 lock.as_ref().unwrap().get_internal(pubkey, |entry| {
1192 assert!(entry.is_some(), "{pubkey}, entry: {entry:?}");
1193 let entry = entry.unwrap();
1194 assert_eq!(entry.ref_count(), 1, "{pubkey}");
1195 assert_eq!(entry.slot_list.read().unwrap().len(), 1, "{pubkey}");
1196 (false, ())
1197 });
1198 }
1199 }
1200 }
1201 });
1202 }
1203
1204 fn get_newest_root_in_slot_list(
1206 alive_roots: &RollingBitField,
1207 slot_list: &[(Slot, T)],
1208 max_allowed_root_inclusive: Option<Slot>,
1209 ) -> Slot {
1210 slot_list
1211 .iter()
1212 .map(|(slot, _)| slot)
1213 .filter(|slot| max_allowed_root_inclusive.is_none_or(|max_root| **slot <= max_root))
1214 .filter(|slot| alive_roots.contains(slot))
1215 .max()
1216 .copied()
1217 .unwrap_or(0)
1218 }
1219
1220 fn update_spl_token_secondary_indexes<G: spl_generic_token::token::GenericTokenAccount>(
1221 &self,
1222 token_id: &Pubkey,
1223 pubkey: &Pubkey,
1224 account_owner: &Pubkey,
1225 account_data: &[u8],
1226 account_indexes: &AccountSecondaryIndexes,
1227 ) {
1228 if *account_owner == *token_id {
1229 if account_indexes.contains(&AccountIndex::SplTokenOwner) {
1230 if let Some(owner_key) = G::unpack_account_owner(account_data) {
1231 if account_indexes.include_key(owner_key) {
1232 self.spl_token_owner_index.insert(owner_key, pubkey);
1233 }
1234 }
1235 }
1236
1237 if account_indexes.contains(&AccountIndex::SplTokenMint) {
1238 if let Some(mint_key) = G::unpack_account_mint(account_data) {
1239 if account_indexes.include_key(mint_key) {
1240 self.spl_token_mint_index.insert(mint_key, pubkey);
1241 }
1242 }
1243 }
1244 }
1245 }
1246
1247 pub fn get_index_key_size(&self, index: &AccountIndex, index_key: &Pubkey) -> Option<usize> {
1248 match index {
1249 AccountIndex::ProgramId => self.program_id_index.index.get(index_key).map(|x| x.len()),
1250 AccountIndex::SplTokenOwner => self
1251 .spl_token_owner_index
1252 .index
1253 .get(index_key)
1254 .map(|x| x.len()),
1255 AccountIndex::SplTokenMint => self
1256 .spl_token_mint_index
1257 .index
1258 .get(index_key)
1259 .map(|x| x.len()),
1260 }
1261 }
1262
1263 pub(crate) fn log_secondary_indexes(&self) {
1265 if !self.program_id_index.index.is_empty() {
1266 info!("secondary index: {:?}", AccountIndex::ProgramId);
1267 self.program_id_index.log_contents();
1268 }
1269 if !self.spl_token_mint_index.index.is_empty() {
1270 info!("secondary index: {:?}", AccountIndex::SplTokenMint);
1271 self.spl_token_mint_index.log_contents();
1272 }
1273 if !self.spl_token_owner_index.index.is_empty() {
1274 info!("secondary index: {:?}", AccountIndex::SplTokenOwner);
1275 self.spl_token_owner_index.log_contents();
1276 }
1277 }
1278
1279 pub(crate) fn update_secondary_indexes(
1280 &self,
1281 pubkey: &Pubkey,
1282 account: &impl ReadableAccount,
1283 account_indexes: &AccountSecondaryIndexes,
1284 ) {
1285 if account_indexes.is_empty() {
1286 return;
1287 }
1288
1289 let account_owner = account.owner();
1290 let account_data = account.data();
1291
1292 if account_indexes.contains(&AccountIndex::ProgramId)
1293 && account_indexes.include_key(account_owner)
1294 {
1295 self.program_id_index.insert(account_owner, pubkey);
1296 }
1297 self.update_spl_token_secondary_indexes::<spl_generic_token::token::Account>(
1312 &spl_generic_token::token::id(),
1313 pubkey,
1314 account_owner,
1315 account_data,
1316 account_indexes,
1317 );
1318 self.update_spl_token_secondary_indexes::<spl_generic_token::token_2022::Account>(
1319 &spl_generic_token::token_2022::id(),
1320 pubkey,
1321 account_owner,
1322 account_data,
1323 account_indexes,
1324 );
1325 }
1326
1327 pub(crate) fn get_bin(&self, pubkey: &Pubkey) -> &InMemAccountsIndex<T, U> {
1328 &self.account_maps[self.bin_calculator.bin_from_pubkey(pubkey)]
1329 }
1330
1331 pub fn bins(&self) -> usize {
1332 self.account_maps.len()
1333 }
1334
1335 pub(crate) fn insert_new_if_missing_into_primary_index(
1343 &self,
1344 slot: Slot,
1345 mut items: Vec<(Pubkey, T)>,
1346 ) -> (u64, InsertNewIfMissingIntoPrimaryIndexInfo) {
1347 let mut insert_time = Measure::start("insert_into_primary_index");
1348
1349 let use_disk = self.storage.storage.is_disk_index_enabled();
1350
1351 let mut count = 0;
1352
1353 let mut num_did_not_exist = 0;
1355 let mut num_existed_in_mem = 0;
1356 let mut num_existed_on_disk = 0;
1357
1358 let bins = self.bins();
1362 let random_bin_offset = thread_rng().gen_range(0..bins);
1363 let bin_calc = self.bin_calculator;
1364 items.sort_unstable_by(|(pubkey_a, _), (pubkey_b, _)| {
1365 ((bin_calc.bin_from_pubkey(pubkey_a) + random_bin_offset) % bins)
1366 .cmp(&((bin_calc.bin_from_pubkey(pubkey_b) + random_bin_offset) % bins))
1367 .then_with(|| pubkey_a.cmp(pubkey_b))
1368 });
1369
1370 while !items.is_empty() {
1371 let mut start_index = items.len() - 1;
1372 let mut last_pubkey = &items[start_index].0;
1373 let pubkey_bin = bin_calc.bin_from_pubkey(last_pubkey);
1374 while start_index > 0 {
1376 let next = start_index - 1;
1377 let next_pubkey = &items[next].0;
1378 assert_ne!(
1379 next_pubkey, last_pubkey,
1380 "Accounts may only be stored once per slot: {slot}"
1381 );
1382 if bin_calc.bin_from_pubkey(next_pubkey) != pubkey_bin {
1383 break;
1384 }
1385 start_index = next;
1386 last_pubkey = next_pubkey;
1387 }
1388
1389 let r_account_maps = &self.account_maps[pubkey_bin];
1390 count += items.len() - start_index;
1392
1393 let items = items.drain(start_index..);
1394 if use_disk {
1395 r_account_maps.startup_insert_only(slot, items);
1396 } else {
1397 let mut duplicates_from_in_memory = vec![];
1400 items.for_each(|(pubkey, account_info)| {
1401 let new_entry = PreAllocatedAccountMapEntry::new(
1402 slot,
1403 account_info,
1404 &self.storage.storage,
1405 use_disk,
1406 );
1407 match r_account_maps.insert_new_entry_if_missing_with_lock(pubkey, new_entry) {
1408 InsertNewEntryResults::DidNotExist => {
1409 num_did_not_exist += 1;
1410 }
1411 InsertNewEntryResults::Existed {
1412 other_slot,
1413 location,
1414 } => {
1415 if let Some(other_slot) = other_slot {
1416 duplicates_from_in_memory.push((other_slot, pubkey));
1417 }
1418 duplicates_from_in_memory.push((slot, pubkey));
1419
1420 match location {
1421 ExistedLocation::InMem => {
1422 num_existed_in_mem += 1;
1423 }
1424 ExistedLocation::OnDisk => {
1425 num_existed_on_disk += 1;
1426 }
1427 }
1428 }
1429 }
1430 });
1431
1432 r_account_maps
1433 .startup_update_duplicates_from_in_memory_only(duplicates_from_in_memory);
1434 }
1435 }
1436 insert_time.stop();
1437
1438 (
1439 insert_time.as_us(),
1440 InsertNewIfMissingIntoPrimaryIndexInfo {
1441 count,
1442 num_did_not_exist,
1443 num_existed_in_mem,
1444 num_existed_on_disk,
1445 },
1446 )
1447 }
1448
1449 pub(crate) fn populate_and_retrieve_duplicate_keys_from_startup(
1451 &self,
1452 f: impl Fn(Vec<(Slot, Pubkey)>) + Sync + Send,
1453 ) {
1454 (0..self.bins())
1455 .into_par_iter()
1456 .map(|pubkey_bin| {
1457 let r_account_maps = &self.account_maps[pubkey_bin];
1458 if self.storage.storage.is_disk_index_enabled() {
1459 r_account_maps.populate_and_retrieve_duplicate_keys_from_startup()
1460 } else {
1461 r_account_maps.startup_take_duplicates_from_in_memory_only()
1462 }
1463 })
1464 .for_each(f);
1465 }
1466
1467 pub fn upsert(
1470 &self,
1471 new_slot: Slot,
1472 old_slot: Slot,
1473 pubkey: &Pubkey,
1474 account: &impl ReadableAccount,
1475 account_indexes: &AccountSecondaryIndexes,
1476 account_info: T,
1477 reclaims: &mut SlotList<T>,
1478 reclaim: UpsertReclaim,
1479 ) {
1480 let store_raw = true;
1482
1483 let new_item = PreAllocatedAccountMapEntry::new(
1495 new_slot,
1496 account_info,
1497 &self.storage.storage,
1498 store_raw,
1499 );
1500 let map = self.get_bin(pubkey);
1501
1502 map.upsert(pubkey, new_item, Some(old_slot), reclaims, reclaim);
1503 self.update_secondary_indexes(pubkey, account, account_indexes);
1504 }
1505
1506 pub fn ref_count_from_storage(&self, pubkey: &Pubkey) -> RefCount {
1507 let map = self.get_bin(pubkey);
1508 map.get_internal_inner(pubkey, |entry| {
1509 (
1510 false,
1511 entry.map(|entry| entry.ref_count()).unwrap_or_default(),
1512 )
1513 })
1514 }
1515
1516 fn purge_secondary_indexes_by_inner_key(
1517 &self,
1518 inner_key: &Pubkey,
1519 account_indexes: &AccountSecondaryIndexes,
1520 ) {
1521 if account_indexes.contains(&AccountIndex::ProgramId) {
1522 self.program_id_index.remove_by_inner_key(inner_key);
1523 }
1524
1525 if account_indexes.contains(&AccountIndex::SplTokenOwner) {
1526 self.spl_token_owner_index.remove_by_inner_key(inner_key);
1527 }
1528
1529 if account_indexes.contains(&AccountIndex::SplTokenMint) {
1530 self.spl_token_mint_index.remove_by_inner_key(inner_key);
1531 }
1532 }
1533
1534 fn purge_older_root_entries(
1535 &self,
1536 slot_list: &mut SlotList<T>,
1537 reclaims: &mut SlotList<T>,
1538 max_clean_root_inclusive: Option<Slot>,
1539 ) {
1540 if slot_list.len() <= 1 {
1541 self.purge_older_root_entries_one_slot_list
1542 .fetch_add(1, Ordering::Relaxed);
1543 }
1544 let newest_root_in_slot_list;
1545 let max_clean_root_inclusive = {
1546 let roots_tracker = &self.roots_tracker.read().unwrap();
1547 newest_root_in_slot_list = Self::get_newest_root_in_slot_list(
1548 &roots_tracker.alive_roots,
1549 slot_list,
1550 max_clean_root_inclusive,
1551 );
1552 max_clean_root_inclusive.unwrap_or_else(|| roots_tracker.alive_roots.max_inclusive())
1553 };
1554
1555 slot_list.retain(|(slot, value)| {
1556 let should_purge = Self::can_purge_older_entries(
1557 max_clean_root_inclusive,
1561 newest_root_in_slot_list,
1562 *slot,
1563 ) && !value.is_cached();
1564 if should_purge {
1565 reclaims.push((*slot, *value));
1566 }
1567 !should_purge
1568 });
1569 }
1570
1571 #[must_use]
1575 pub fn clean_rooted_entries(
1576 &self,
1577 pubkey: &Pubkey,
1578 reclaims: &mut SlotList<T>,
1579 max_clean_root_inclusive: Option<Slot>,
1580 ) -> bool {
1581 let mut is_slot_list_empty = false;
1582 let missing_in_accounts_index = self
1583 .slot_list_mut(pubkey, |slot_list| {
1584 self.purge_older_root_entries(slot_list, reclaims, max_clean_root_inclusive);
1585 is_slot_list_empty = slot_list.is_empty();
1586 })
1587 .is_none();
1588
1589 let mut removed = false;
1590 if is_slot_list_empty {
1595 let w_maps = self.get_bin(pubkey);
1596 removed = w_maps.remove_if_slot_list_empty(*pubkey);
1597 }
1598 removed || missing_in_accounts_index
1599 }
1600
1601 fn clean_and_unref_slot_list_on_startup(
1607 &self,
1608 entry: &AccountMapEntry<T>,
1609 reclaims: &mut SlotList<T>,
1610 ) -> (u64, T) {
1611 let mut slot_list = entry.slot_list.write().unwrap();
1612 let max_slot = slot_list
1613 .iter()
1614 .map(|(slot, _account)| *slot)
1615 .max()
1616 .expect("Slot list has entries");
1617
1618 let mut reclaim_count = 0;
1619
1620 slot_list.retain(|(slot, value)| {
1621 if *slot < max_slot {
1623 assert!(!value.is_cached(), "Unsafe to reclaim cached entries");
1624 reclaims.push((*slot, *value));
1625 reclaim_count += 1;
1626 false
1627 } else {
1628 true
1629 }
1630 });
1631
1632 entry.unref_by_count(reclaim_count);
1634 assert_eq!(
1635 entry.ref_count(),
1636 1,
1637 "ref count should be one after cleaning all entries"
1638 );
1639
1640 entry.set_dirty(true);
1641
1642 *slot_list
1644 .last()
1645 .expect("Slot list should have at least one entry after cleaning")
1646 }
1647
1648 pub fn clean_and_unref_rooted_entries_by_bin(
1652 &self,
1653 pubkeys_by_bin: &[Pubkey],
1654 callback: impl Fn(Slot, T),
1655 ) -> SlotList<T> {
1656 let mut reclaims = Vec::new();
1657
1658 let map = match pubkeys_by_bin.first() {
1659 Some(pubkey) => self.get_bin(pubkey),
1660 None => return reclaims, };
1662
1663 for pubkey in pubkeys_by_bin {
1664 map.get_internal_inner(pubkey, |entry| {
1665 let entry = entry.expect("Expected entry to exist in accounts index");
1666 let (slot, account_info) =
1667 self.clean_and_unref_slot_list_on_startup(entry, &mut reclaims);
1668 callback(slot, account_info);
1669 (false, ())
1670 });
1671 }
1672 reclaims
1673 }
1674
1675 fn can_purge_older_entries(
1688 max_clean_root_exclusive: Slot,
1689 newest_root_in_slot_list: Slot,
1690 slot: Slot,
1691 ) -> bool {
1692 slot < max_clean_root_exclusive && slot != newest_root_in_slot_list
1693 }
1694
1695 pub fn get_rooted_from_list<'a>(&self, slots: impl Iterator<Item = &'a Slot>) -> Vec<Slot> {
1697 let roots_tracker = self.roots_tracker.read().unwrap();
1698 slots
1699 .filter_map(|s| {
1700 if roots_tracker.alive_roots.contains(s) {
1701 Some(*s)
1702 } else {
1703 None
1704 }
1705 })
1706 .collect()
1707 }
1708
1709 pub fn is_alive_root(&self, slot: Slot) -> bool {
1710 self.roots_tracker
1711 .read()
1712 .unwrap()
1713 .alive_roots
1714 .contains(&slot)
1715 }
1716
1717 pub fn add_root(&self, slot: Slot) {
1718 self.roots_added.fetch_add(1, Ordering::Relaxed);
1719 let mut w_roots_tracker = self.roots_tracker.write().unwrap();
1720 assert!(
1722 slot >= w_roots_tracker.alive_roots.max_inclusive(),
1723 "Roots must be added in order: {} < {}",
1724 slot,
1725 w_roots_tracker.alive_roots.max_inclusive()
1726 );
1727 w_roots_tracker.alive_roots.insert(slot);
1729 }
1730
1731 pub fn max_root_inclusive(&self) -> Slot {
1732 self.roots_tracker
1733 .read()
1734 .unwrap()
1735 .alive_roots
1736 .max_inclusive()
1737 }
1738
1739 pub fn clean_dead_slot(&self, slot: Slot) -> bool {
1743 let mut w_roots_tracker = self.roots_tracker.write().unwrap();
1744 if !w_roots_tracker.alive_roots.remove(&slot) {
1745 false
1746 } else {
1747 drop(w_roots_tracker);
1748 self.roots_removed.fetch_add(1, Ordering::Relaxed);
1749 true
1750 }
1751 }
1752
1753 pub(crate) fn update_roots_stats(&self, stats: &mut AccountsIndexRootsStats) {
1754 let roots_tracker = self.roots_tracker.read().unwrap();
1755 stats.roots_len = Some(roots_tracker.alive_roots.len());
1756 stats.roots_range = Some(roots_tracker.alive_roots.range_width());
1757 }
1758
1759 pub fn min_alive_root(&self) -> Option<Slot> {
1760 self.roots_tracker.read().unwrap().min_alive_root()
1761 }
1762
1763 pub fn num_alive_roots(&self) -> usize {
1764 self.roots_tracker.read().unwrap().alive_roots.len()
1765 }
1766
1767 pub fn all_alive_roots(&self) -> Vec<Slot> {
1768 let tracker = self.roots_tracker.read().unwrap();
1769 tracker.alive_roots.get_all()
1770 }
1771
1772 #[cfg(feature = "dev-context-only-utils")]
1774 pub fn purge_roots(&self, pubkey: &Pubkey) -> (SlotList<T>, bool) {
1778 self.slot_list_mut(pubkey, |slot_list| {
1779 let reclaims = self.get_rooted_entries(slot_list, None);
1780 slot_list.retain(|(slot, _)| !self.is_alive_root(*slot));
1781 (reclaims, slot_list.is_empty())
1782 })
1783 .unwrap()
1784 }
1785}
1786
1787#[cfg(test)]
1788pub mod tests {
1789 use {
1790 super::*,
1791 crate::bucket_map_holder::{AtomicAge, BucketMapHolder},
1792 account_map_entry::AccountMapEntryMeta,
1793 secondary::DashMapSecondaryIndexEntry,
1794 solana_account::{AccountSharedData, WritableAccount},
1795 solana_pubkey::PUBKEY_BYTES,
1796 spl_generic_token::{spl_token_ids, token::SPL_TOKEN_ACCOUNT_OWNER_OFFSET},
1797 std::ops::{
1798 Bound::{Excluded, Included, Unbounded},
1799 RangeInclusive,
1800 },
1801 test_case::test_matrix,
1802 };
1803
1804 pub enum SecondaryIndexTypes<'a> {
1805 RwLock(&'a SecondaryIndex<RwLockSecondaryIndexEntry>),
1806 DashMap(&'a SecondaryIndex<DashMapSecondaryIndexEntry>),
1807 }
1808
1809 pub fn spl_token_mint_index_enabled() -> AccountSecondaryIndexes {
1810 let mut account_indexes = HashSet::new();
1811 account_indexes.insert(AccountIndex::SplTokenMint);
1812 AccountSecondaryIndexes {
1813 indexes: account_indexes,
1814 keys: None,
1815 }
1816 }
1817
1818 pub fn spl_token_owner_index_enabled() -> AccountSecondaryIndexes {
1819 let mut account_indexes = HashSet::new();
1820 account_indexes.insert(AccountIndex::SplTokenOwner);
1821 AccountSecondaryIndexes {
1822 indexes: account_indexes,
1823 keys: None,
1824 }
1825 }
1826
1827 fn create_spl_token_mint_secondary_index_state() -> (usize, usize, AccountSecondaryIndexes) {
1828 {
1829 let index = AccountsIndex::<bool, bool>::default_for_tests();
1831 let _type_check = SecondaryIndexTypes::RwLock(&index.spl_token_mint_index);
1832 }
1833
1834 (0, PUBKEY_BYTES, spl_token_mint_index_enabled())
1835 }
1836
1837 fn create_spl_token_owner_secondary_index_state() -> (usize, usize, AccountSecondaryIndexes) {
1838 {
1839 let index = AccountsIndex::<bool, bool>::default_for_tests();
1841 let _type_check = SecondaryIndexTypes::RwLock(&index.spl_token_owner_index);
1842 }
1843
1844 (
1845 SPL_TOKEN_ACCOUNT_OWNER_OFFSET,
1846 SPL_TOKEN_ACCOUNT_OWNER_OFFSET + PUBKEY_BYTES,
1847 spl_token_owner_index_enabled(),
1848 )
1849 }
1850
1851 impl<T: IndexValue> Clone for PreAllocatedAccountMapEntry<T> {
1852 fn clone(&self) -> Self {
1853 match self {
1855 PreAllocatedAccountMapEntry::Entry(entry) => {
1856 let (slot, account_info) = entry.slot_list.read().unwrap()[0];
1857 let meta = AccountMapEntryMeta {
1858 dirty: AtomicBool::new(entry.dirty()),
1859 age: AtomicAge::new(entry.age()),
1860 };
1861 PreAllocatedAccountMapEntry::Entry(Arc::new(AccountMapEntry::new(
1862 vec![(slot, account_info)],
1863 entry.ref_count(),
1864 meta,
1865 )))
1866 }
1867 PreAllocatedAccountMapEntry::Raw(raw) => PreAllocatedAccountMapEntry::Raw(*raw),
1868 }
1869 }
1870 }
1871
1872 #[test]
1873 fn test_get_empty() {
1874 let key = solana_pubkey::new_rand();
1875 let index = AccountsIndex::<bool, bool>::default_for_tests();
1876 let ancestors = Ancestors::default();
1877 let key = &key;
1878 assert!(!index.contains_with(key, Some(&ancestors), None));
1879 assert!(!index.contains_with(key, None, None));
1880
1881 let mut num = 0;
1882 index.unchecked_scan_accounts(
1883 "",
1884 &ancestors,
1885 |_pubkey, _index| num += 1,
1886 &ScanConfig::default(),
1887 );
1888 assert_eq!(num, 0);
1889 }
1890
1891 #[test]
1892 fn test_secondary_index_include_exclude() {
1893 let pk1 = Pubkey::new_unique();
1894 let pk2 = Pubkey::new_unique();
1895 let mut index = AccountSecondaryIndexes::default();
1896
1897 assert!(!index.contains(&AccountIndex::ProgramId));
1898 index.indexes.insert(AccountIndex::ProgramId);
1899 assert!(index.contains(&AccountIndex::ProgramId));
1900 assert!(index.include_key(&pk1));
1901 assert!(index.include_key(&pk2));
1902
1903 let exclude = false;
1904 index.keys = Some(AccountSecondaryIndexesIncludeExclude {
1905 keys: [pk1].iter().cloned().collect::<HashSet<_>>(),
1906 exclude,
1907 });
1908 assert!(index.include_key(&pk1));
1909 assert!(!index.include_key(&pk2));
1910
1911 let exclude = true;
1912 index.keys = Some(AccountSecondaryIndexesIncludeExclude {
1913 keys: [pk1].iter().cloned().collect::<HashSet<_>>(),
1914 exclude,
1915 });
1916 assert!(!index.include_key(&pk1));
1917 assert!(index.include_key(&pk2));
1918
1919 let exclude = true;
1920 index.keys = Some(AccountSecondaryIndexesIncludeExclude {
1921 keys: [pk1, pk2].iter().cloned().collect::<HashSet<_>>(),
1922 exclude,
1923 });
1924 assert!(!index.include_key(&pk1));
1925 assert!(!index.include_key(&pk2));
1926
1927 let exclude = false;
1928 index.keys = Some(AccountSecondaryIndexesIncludeExclude {
1929 keys: [pk1, pk2].iter().cloned().collect::<HashSet<_>>(),
1930 exclude,
1931 });
1932 assert!(index.include_key(&pk1));
1933 assert!(index.include_key(&pk2));
1934 }
1935
1936 const UPSERT_RECLAIM_TEST_DEFAULT: UpsertReclaim = UpsertReclaim::PopulateReclaims;
1937
1938 #[test]
1939 fn test_insert_no_ancestors() {
1940 let key = solana_pubkey::new_rand();
1941 let index = AccountsIndex::<bool, bool>::default_for_tests();
1942 let mut gc = Vec::new();
1943 index.upsert(
1944 0,
1945 0,
1946 &key,
1947 &AccountSharedData::default(),
1948 &AccountSecondaryIndexes::default(),
1949 true,
1950 &mut gc,
1951 UPSERT_RECLAIM_TEST_DEFAULT,
1952 );
1953 assert!(gc.is_empty());
1954
1955 let ancestors = Ancestors::default();
1956 assert!(!index.contains_with(&key, Some(&ancestors), None));
1957 assert!(!index.contains_with(&key, None, None));
1958
1959 let mut num = 0;
1960 index.unchecked_scan_accounts(
1961 "",
1962 &ancestors,
1963 |_pubkey, _index| num += 1,
1964 &ScanConfig::default(),
1965 );
1966 assert_eq!(num, 0);
1967 }
1968
1969 type AccountInfoTest = f64;
1970
1971 impl IndexValue for AccountInfoTest {}
1972 impl DiskIndexValue for AccountInfoTest {}
1973 impl IsCached for AccountInfoTest {
1974 fn is_cached(&self) -> bool {
1975 true
1976 }
1977 }
1978
1979 impl IsZeroLamport for AccountInfoTest {
1980 fn is_zero_lamport(&self) -> bool {
1981 true
1982 }
1983 }
1984
1985 #[test]
1986 #[should_panic(expected = "Accounts may only be stored once per slot:")]
1987 fn test_insert_duplicates() {
1988 let key = solana_pubkey::new_rand();
1989 let pubkey = &key;
1990 let slot = 0;
1991 let mut ancestors = Ancestors::default();
1992 ancestors.insert(slot, 0);
1993
1994 let account_info = true;
1995 let index = AccountsIndex::<bool, bool>::default_for_tests();
1996 let account_info2: bool = !account_info;
1997 let items = vec![(*pubkey, account_info), (*pubkey, account_info2)];
1998 index.set_startup(Startup::Startup);
1999 let (_, _result) = index.insert_new_if_missing_into_primary_index(slot, items);
2000 }
2001
2002 #[test]
2003 fn test_insert_new_with_lock_no_ancestors() {
2004 let key = solana_pubkey::new_rand();
2005 let pubkey = &key;
2006 let slot = 0;
2007
2008 let index = AccountsIndex::<bool, bool>::default_for_tests();
2009 let account_info = true;
2010 let items = vec![(*pubkey, account_info)];
2011 index.set_startup(Startup::Startup);
2012 let expected_len = items.len();
2013 let (_, result) = index.insert_new_if_missing_into_primary_index(slot, items);
2014 assert_eq!(result.count, expected_len);
2015 index.set_startup(Startup::Normal);
2016
2017 let mut ancestors = Ancestors::default();
2018 assert!(!index.contains_with(pubkey, Some(&ancestors), None));
2019 assert!(!index.contains_with(pubkey, None, None));
2020
2021 let mut num = 0;
2022 index.unchecked_scan_accounts(
2023 "",
2024 &ancestors,
2025 |_pubkey, _index| num += 1,
2026 &ScanConfig::default(),
2027 );
2028 assert_eq!(num, 0);
2029 ancestors.insert(slot, 0);
2030 assert!(index.contains_with(pubkey, Some(&ancestors), None));
2031 assert_eq!(index.ref_count_from_storage(pubkey), 1);
2032 index.unchecked_scan_accounts(
2033 "",
2034 &ancestors,
2035 |_pubkey, _index| num += 1,
2036 &ScanConfig::default(),
2037 );
2038 assert_eq!(num, 1);
2039
2040 let index = AccountsIndex::<bool, bool>::default_for_tests();
2042 let account_info = false;
2043 let items = vec![(*pubkey, account_info)];
2044 index.set_startup(Startup::Startup);
2045 let expected_len = items.len();
2046 let (_, result) = index.insert_new_if_missing_into_primary_index(slot, items);
2047 assert_eq!(result.count, expected_len);
2048 index.set_startup(Startup::Normal);
2049
2050 let mut ancestors = Ancestors::default();
2051 assert!(!index.contains_with(pubkey, Some(&ancestors), None));
2052 assert!(!index.contains_with(pubkey, None, None));
2053
2054 let mut num = 0;
2055 index.unchecked_scan_accounts(
2056 "",
2057 &ancestors,
2058 |_pubkey, _index| num += 1,
2059 &ScanConfig::default(),
2060 );
2061 assert_eq!(num, 0);
2062 ancestors.insert(slot, 0);
2063 assert!(index.contains_with(pubkey, Some(&ancestors), None));
2064 assert_eq!(index.ref_count_from_storage(pubkey), 1);
2065 index.unchecked_scan_accounts(
2066 "",
2067 &ancestors,
2068 |_pubkey, _index| num += 1,
2069 &ScanConfig::default(),
2070 );
2071 assert_eq!(num, 1);
2072 }
2073
2074 fn get_pre_allocated<T: IndexValue>(
2075 slot: Slot,
2076 account_info: T,
2077 storage: &Arc<BucketMapHolder<T, T>>,
2078 store_raw: bool,
2079 to_raw_first: bool,
2080 ) -> PreAllocatedAccountMapEntry<T> {
2081 let entry = PreAllocatedAccountMapEntry::new(slot, account_info, storage, store_raw);
2082
2083 if to_raw_first {
2084 let (slot2, account_info2) = entry.into();
2086 PreAllocatedAccountMapEntry::new(slot2, account_info2, storage, store_raw)
2088 } else {
2089 entry
2090 }
2091 }
2092
2093 #[test]
2094 fn test_clean_and_unref_rooted_entries_by_bin_empty() {
2095 let index: AccountsIndex<bool, bool> = AccountsIndex::<bool, bool>::default_for_tests();
2096 let pubkeys_by_bin: Vec<Pubkey> = vec![];
2097
2098 let reclaims =
2099 index.clean_and_unref_rooted_entries_by_bin(&pubkeys_by_bin, |_slot, _info| {});
2100
2101 assert!(reclaims.is_empty());
2102 }
2103
2104 #[test]
2105 fn test_clean_and_unref_rooted_entries_by_bin_single_entry() {
2106 let index = AccountsIndex::<bool, bool>::default_for_tests();
2107 let pubkey = solana_pubkey::new_rand();
2108 let slot = 0;
2109 let account_info = true;
2110
2111 let mut gc = Vec::new();
2112 index.upsert(
2113 slot,
2114 slot,
2115 &pubkey,
2116 &AccountSharedData::default(),
2117 &AccountSecondaryIndexes::default(),
2118 account_info,
2119 &mut gc,
2120 UPSERT_RECLAIM_TEST_DEFAULT,
2121 );
2122
2123 assert!(gc.is_empty());
2124
2125 let reclaims = index.clean_and_unref_rooted_entries_by_bin(&[pubkey], |slot, info| {
2126 assert_eq!(slot, 0);
2127 assert!(info);
2128 });
2129
2130 assert_eq!(reclaims.len(), 0);
2131 }
2132
2133 #[test]
2134 fn test_clean_and_unref_rooted_entries_by_bin_with_reclaim() {
2135 let index = AccountsIndex::<u64, u64>::default_for_tests();
2136 let pubkey = solana_pubkey::new_rand();
2137 let slot1 = 0;
2138 let slot2 = 1;
2139 let account_info1 = 0;
2140 let account_info2 = 1;
2141
2142 let mut gc = Vec::new();
2143 for (slot, account_info) in [(slot1, account_info1), (slot2, account_info2)] {
2144 index.upsert(
2145 slot,
2146 slot,
2147 &pubkey,
2148 &AccountSharedData::default(),
2149 &AccountSecondaryIndexes::default(),
2150 account_info,
2151 &mut gc,
2152 UpsertReclaim::IgnoreReclaims,
2153 );
2154 }
2155
2156 assert!(gc.is_empty());
2157
2158 let reclaims = index.clean_and_unref_rooted_entries_by_bin(&[pubkey], |slot, info| {
2159 assert_eq!(slot, slot2);
2160 assert_eq!(info, account_info2);
2161 });
2162
2163 assert_eq!(reclaims, vec![(slot1, account_info1)]);
2164 }
2165
2166 #[test]
2167 fn test_clean_and_unref_rooted_entries_by_bin_multiple_pubkeys() {
2168 let index: AccountsIndex<bool, bool> = AccountsIndex::<bool, bool>::default_for_tests();
2169 let bin_index = 0;
2170 let mut pubkeys = Vec::new();
2171 let mut expected_reclaims = Vec::new();
2172 let mut gc: Vec<(u64, bool)> = Vec::new();
2173
2174 while pubkeys.len() < 10 {
2175 let new_pubkey = solana_pubkey::new_rand();
2176
2177 if index.bin_calculator.bin_from_pubkey(&new_pubkey) == bin_index {
2179 pubkeys.push(new_pubkey);
2180 }
2181 }
2182
2183 for (i, pubkey) in pubkeys.iter().enumerate() {
2184 let num_inserts: u64 = i as u64 % 4 + 1;
2185
2186 for slot in 0..num_inserts {
2187 if slot > 0 {
2188 expected_reclaims.push((slot - 1, true));
2189 }
2190 index.upsert(
2191 slot,
2192 slot,
2193 pubkey,
2194 &AccountSharedData::default(),
2195 &AccountSecondaryIndexes::default(),
2196 true,
2197 &mut gc,
2198 UpsertReclaim::IgnoreReclaims,
2199 );
2200 }
2201 }
2202
2203 assert!(gc.is_empty());
2204
2205 let mut reclaims = index.clean_and_unref_rooted_entries_by_bin(&pubkeys, |_slot, _info| {});
2206 reclaims.sort_unstable();
2207 expected_reclaims.sort_unstable();
2208
2209 assert!(!reclaims.is_empty());
2210 assert_eq!(reclaims, expected_reclaims);
2211 }
2212
2213 #[test]
2214 fn test_new_entry() {
2215 for store_raw in [false, true] {
2216 for to_raw_first in [false, true] {
2217 let slot = 0;
2218 let account_info = AccountInfoTest::default();
2220 let index = AccountsIndex::default_for_tests();
2221
2222 let new_entry = get_pre_allocated(
2223 slot,
2224 account_info,
2225 &index.storage.storage,
2226 store_raw,
2227 to_raw_first,
2228 )
2229 .into_account_map_entry(&index.storage.storage);
2230 assert_eq!(new_entry.ref_count(), 0);
2231 assert_eq!(new_entry.slot_list.read().unwrap().capacity(), 1);
2232 assert_eq!(
2233 new_entry.slot_list.read().unwrap().to_vec(),
2234 vec![(slot, account_info)]
2235 );
2236
2237 let account_info = true;
2239 let index = AccountsIndex::default_for_tests();
2240
2241 let new_entry = get_pre_allocated(
2242 slot,
2243 account_info,
2244 &index.storage.storage,
2245 store_raw,
2246 to_raw_first,
2247 )
2248 .into_account_map_entry(&index.storage.storage);
2249 assert_eq!(new_entry.ref_count(), 1);
2250 assert_eq!(new_entry.slot_list.read().unwrap().capacity(), 1);
2251 assert_eq!(
2252 new_entry.slot_list.read().unwrap().to_vec(),
2253 vec![(slot, account_info)]
2254 );
2255 }
2256 }
2257 }
2258
2259 #[test]
2260 fn test_batch_insert() {
2261 let slot0 = 0;
2262 let key0 = solana_pubkey::new_rand();
2263 let key1 = solana_pubkey::new_rand();
2264
2265 let index = AccountsIndex::<bool, bool>::default_for_tests();
2266 let account_infos = [true, false];
2267
2268 index.set_startup(Startup::Startup);
2269 let items = vec![(key0, account_infos[0]), (key1, account_infos[1])];
2270 let expected_len = items.len();
2271 let (_, result) = index.insert_new_if_missing_into_primary_index(slot0, items);
2272 assert_eq!(result.count, expected_len);
2273 index.set_startup(Startup::Normal);
2274
2275 for (i, key) in [key0, key1].iter().enumerate() {
2276 let entry = index.get_cloned(key).unwrap();
2277 assert_eq!(entry.ref_count(), 1);
2278 assert_eq!(
2279 entry.slot_list.read().unwrap().as_slice(),
2280 &[(slot0, account_infos[i])],
2281 );
2282 }
2283 }
2284
2285 fn test_new_entry_code_paths_helper<T: IndexValue>(
2286 account_infos: [T; 2],
2287 is_cached: bool,
2288 upsert_method: Option<UpsertReclaim>,
2289 use_disk: bool,
2290 ) {
2291 if is_cached && upsert_method.is_none() {
2292 return;
2296 }
2297
2298 let slot0 = 0;
2299 let slot1 = 1;
2300 let key = solana_pubkey::new_rand();
2301
2302 let mut config = ACCOUNTS_INDEX_CONFIG_FOR_TESTING;
2303 config.index_limit_mb = if use_disk {
2304 IndexLimitMb::Minimal
2305 } else {
2306 IndexLimitMb::InMemOnly };
2308 let index = AccountsIndex::<T, T>::new(&config, Arc::default());
2309 let mut gc = Vec::new();
2310
2311 match upsert_method {
2312 Some(upsert_method) => {
2313 index.upsert(
2315 slot0,
2316 slot0,
2317 &key,
2318 &AccountSharedData::default(),
2319 &AccountSecondaryIndexes::default(),
2320 account_infos[0],
2321 &mut gc,
2322 upsert_method,
2323 );
2324 }
2325 None => {
2326 let items = vec![(key, account_infos[0])];
2327 index.set_startup(Startup::Startup);
2328 let expected_len = items.len();
2329 let (_, result) = index.insert_new_if_missing_into_primary_index(slot0, items);
2330 assert_eq!(result.count, expected_len);
2331 index.set_startup(Startup::Normal);
2332 }
2333 }
2334 assert!(gc.is_empty());
2335
2336 {
2338 let entry = index.get_cloned(&key).unwrap();
2339 let slot_list = entry.slot_list.read().unwrap();
2340 assert_eq!(entry.ref_count(), u64::from(!is_cached));
2341 assert_eq!(slot_list.as_slice(), &[(slot0, account_infos[0])]);
2342 let new_entry = PreAllocatedAccountMapEntry::new(
2343 slot0,
2344 account_infos[0],
2345 &index.storage.storage,
2346 false,
2347 )
2348 .into_account_map_entry(&index.storage.storage);
2349 assert_eq!(
2350 slot_list.as_slice(),
2351 new_entry.slot_list.read().unwrap().as_slice(),
2352 );
2353 }
2354
2355 match upsert_method {
2356 Some(upsert_method) => {
2357 index.upsert(
2359 slot1,
2360 slot1,
2361 &key,
2362 &AccountSharedData::default(),
2363 &AccountSecondaryIndexes::default(),
2364 account_infos[1],
2365 &mut gc,
2366 upsert_method,
2367 );
2368 }
2369 None => {
2370 for _ in 0..5 {
2372 index.set_startup(Startup::Startup);
2373 index.set_startup(Startup::Normal);
2374 }
2375
2376 let items = vec![(key, account_infos[1])];
2377 index.set_startup(Startup::Startup);
2378 let expected_len = items.len();
2379 let (_, result) = index.insert_new_if_missing_into_primary_index(slot1, items);
2380 assert_eq!(result.count, expected_len);
2381 index.set_startup(Startup::Normal);
2382 }
2383 }
2384
2385 let should_have_reclaims =
2387 upsert_method == Some(UpsertReclaim::ReclaimOldSlots) && !is_cached;
2388
2389 if should_have_reclaims {
2390 assert!(!gc.is_empty());
2391 assert_eq!(gc.len(), 1);
2392 assert_eq!(gc[0], (slot0, account_infos[0]));
2393 } else {
2394 assert!(gc.is_empty());
2395 }
2396
2397 index.populate_and_retrieve_duplicate_keys_from_startup(|_slot_keys| {});
2398
2399 let entry = index.get_cloned(&key).unwrap();
2400 let slot_list = entry.slot_list.read().unwrap();
2401
2402 if should_have_reclaims {
2403 assert_eq!(entry.ref_count(), 1);
2404 assert_eq!(slot_list.as_slice(), &[(slot1, account_infos[1])],);
2405 } else {
2406 assert_eq!(entry.ref_count(), if is_cached { 0 } else { 2 });
2407 assert_eq!(
2408 slot_list.as_slice(),
2409 &[(slot0, account_infos[0]), (slot1, account_infos[1])],
2410 );
2411 }
2412
2413 let new_entry = PreAllocatedAccountMapEntry::new(
2414 slot1,
2415 account_infos[1],
2416 &index.storage.storage,
2417 false,
2418 );
2419
2420 assert_eq!(slot_list.last().unwrap(), &new_entry.into());
2421 }
2422
2423 #[test_matrix(
2424 [false, true],
2425 [None, Some(UpsertReclaim::PopulateReclaims), Some(UpsertReclaim::ReclaimOldSlots)],
2426 [true, false]
2427 )]
2428 fn test_new_entry_and_update_code_paths(
2429 use_disk: bool,
2430 upsert_method: Option<UpsertReclaim>,
2431 is_cached: bool,
2432 ) {
2433 if is_cached {
2434 test_new_entry_code_paths_helper([1.0, 2.0], true, upsert_method, use_disk);
2436 } else {
2437 test_new_entry_code_paths_helper([1, 2], false, upsert_method, use_disk);
2439 }
2440 }
2441
2442 #[test]
2443 fn test_insert_with_lock_no_ancestors() {
2444 let key = solana_pubkey::new_rand();
2445 let index = AccountsIndex::<bool, bool>::default_for_tests();
2446 let slot = 0;
2447 let account_info = true;
2448
2449 let new_entry =
2450 PreAllocatedAccountMapEntry::new(slot, account_info, &index.storage.storage, false);
2451 assert_eq!(0, account_maps_stats_len(&index));
2452 assert_eq!((slot, account_info), new_entry.clone().into());
2453
2454 assert_eq!(0, account_maps_stats_len(&index));
2455 let r_account_maps = index.get_bin(&key);
2456 r_account_maps.upsert(
2457 &key,
2458 new_entry,
2459 None,
2460 &mut SlotList::default(),
2461 UPSERT_RECLAIM_TEST_DEFAULT,
2462 );
2463 assert_eq!(1, account_maps_stats_len(&index));
2464
2465 let mut ancestors = Ancestors::default();
2466 assert!(!index.contains_with(&key, Some(&ancestors), None));
2467 assert!(!index.contains_with(&key, None, None));
2468
2469 let mut num = 0;
2470 index.unchecked_scan_accounts(
2471 "",
2472 &ancestors,
2473 |_pubkey, _index| num += 1,
2474 &ScanConfig::default(),
2475 );
2476 assert_eq!(num, 0);
2477 ancestors.insert(slot, 0);
2478 assert!(index.contains_with(&key, Some(&ancestors), None));
2479 index.unchecked_scan_accounts(
2480 "",
2481 &ancestors,
2482 |_pubkey, _index| num += 1,
2483 &ScanConfig::default(),
2484 );
2485 assert_eq!(num, 1);
2486 }
2487
2488 #[test]
2489 fn test_insert_wrong_ancestors() {
2490 let key = solana_pubkey::new_rand();
2491 let index = AccountsIndex::<bool, bool>::default_for_tests();
2492 let mut gc = Vec::new();
2493 index.upsert(
2494 0,
2495 0,
2496 &key,
2497 &AccountSharedData::default(),
2498 &AccountSecondaryIndexes::default(),
2499 true,
2500 &mut gc,
2501 UPSERT_RECLAIM_TEST_DEFAULT,
2502 );
2503 assert!(gc.is_empty());
2504
2505 let ancestors = vec![(1, 1)].into_iter().collect();
2506 assert!(!index.contains_with(&key, Some(&ancestors), None));
2507
2508 let mut num = 0;
2509 index.unchecked_scan_accounts(
2510 "",
2511 &ancestors,
2512 |_pubkey, _index| num += 1,
2513 &ScanConfig::default(),
2514 );
2515 assert_eq!(num, 0);
2516 }
2517 #[test]
2518 fn test_insert_ignore_reclaims() {
2519 {
2520 let key = solana_pubkey::new_rand();
2522 let index = AccountsIndex::<u64, u64>::default_for_tests();
2523 let mut reclaims = Vec::new();
2524 let slot = 0;
2525 let value = 1;
2526 assert!(!value.is_cached());
2527 index.upsert(
2528 slot,
2529 slot,
2530 &key,
2531 &AccountSharedData::default(),
2532 &AccountSecondaryIndexes::default(),
2533 value,
2534 &mut reclaims,
2535 UpsertReclaim::PopulateReclaims,
2536 );
2537 assert!(reclaims.is_empty());
2538 index.upsert(
2539 slot,
2540 slot,
2541 &key,
2542 &AccountSharedData::default(),
2543 &AccountSecondaryIndexes::default(),
2544 value,
2545 &mut reclaims,
2546 UpsertReclaim::PopulateReclaims,
2547 );
2548 assert!(!reclaims.is_empty());
2550 reclaims.clear();
2551 index.upsert(
2552 slot,
2553 slot,
2554 &key,
2555 &AccountSharedData::default(),
2556 &AccountSecondaryIndexes::default(),
2557 value,
2558 &mut reclaims,
2559 UpsertReclaim::IgnoreReclaims,
2561 );
2562 assert!(reclaims.is_empty());
2564 }
2565 {
2566 let key = solana_pubkey::new_rand();
2568 let index = AccountsIndex::<AccountInfoTest, AccountInfoTest>::default_for_tests();
2569 let mut reclaims = Vec::new();
2570 let slot = 0;
2571 let value = 1.0;
2572 assert!(value.is_cached());
2573 index.upsert(
2574 slot,
2575 slot,
2576 &key,
2577 &AccountSharedData::default(),
2578 &AccountSecondaryIndexes::default(),
2579 value,
2580 &mut reclaims,
2581 UpsertReclaim::PopulateReclaims,
2582 );
2583 assert!(reclaims.is_empty());
2584 index.upsert(
2585 slot,
2586 slot,
2587 &key,
2588 &AccountSharedData::default(),
2589 &AccountSecondaryIndexes::default(),
2590 value,
2591 &mut reclaims,
2592 UpsertReclaim::PopulateReclaims,
2593 );
2594 assert!(reclaims.is_empty());
2596 index.upsert(
2597 slot,
2598 slot,
2599 &key,
2600 &AccountSharedData::default(),
2601 &AccountSecondaryIndexes::default(),
2602 value,
2603 &mut reclaims,
2604 UpsertReclaim::IgnoreReclaims,
2606 );
2607 assert!(reclaims.is_empty());
2609 }
2610 }
2611
2612 #[test]
2613 fn test_insert_with_ancestors() {
2614 let key = solana_pubkey::new_rand();
2615 let index = AccountsIndex::<bool, bool>::default_for_tests();
2616 let mut gc = Vec::new();
2617 index.upsert(
2618 0,
2619 0,
2620 &key,
2621 &AccountSharedData::default(),
2622 &AccountSecondaryIndexes::default(),
2623 true,
2624 &mut gc,
2625 UPSERT_RECLAIM_TEST_DEFAULT,
2626 );
2627 assert!(gc.is_empty());
2628
2629 let ancestors = vec![(0, 0)].into_iter().collect();
2630 index
2631 .get_with_and_then(
2632 &key,
2633 Some(&ancestors),
2634 None,
2635 false,
2636 |(slot, account_info)| {
2637 assert_eq!(slot, 0);
2638 assert!(account_info);
2639 },
2640 )
2641 .unwrap();
2642
2643 let mut num = 0;
2644 let mut found_key = false;
2645 index.unchecked_scan_accounts(
2646 "",
2647 &ancestors,
2648 |pubkey, _index| {
2649 if pubkey == &key {
2650 found_key = true
2651 };
2652 num += 1
2653 },
2654 &ScanConfig::default(),
2655 );
2656 assert_eq!(num, 1);
2657 assert!(found_key);
2658 }
2659
2660 fn setup_accounts_index_keys(num_pubkeys: usize) -> (AccountsIndex<bool, bool>, Vec<Pubkey>) {
2661 let index = AccountsIndex::<bool, bool>::default_for_tests();
2662 let root_slot = 0;
2663
2664 let mut pubkeys: Vec<Pubkey> = std::iter::repeat_with(|| {
2665 let new_pubkey = solana_pubkey::new_rand();
2666 index.upsert(
2667 root_slot,
2668 root_slot,
2669 &new_pubkey,
2670 &AccountSharedData::default(),
2671 &AccountSecondaryIndexes::default(),
2672 true,
2673 &mut vec![],
2674 UPSERT_RECLAIM_TEST_DEFAULT,
2675 );
2676 new_pubkey
2677 })
2678 .take(num_pubkeys.saturating_sub(1))
2679 .collect();
2680
2681 if num_pubkeys != 0 {
2682 pubkeys.push(Pubkey::default());
2683 index.upsert(
2684 root_slot,
2685 root_slot,
2686 &Pubkey::default(),
2687 &AccountSharedData::default(),
2688 &AccountSecondaryIndexes::default(),
2689 true,
2690 &mut vec![],
2691 UPSERT_RECLAIM_TEST_DEFAULT,
2692 );
2693 }
2694
2695 index.add_root(root_slot);
2696
2697 (index, pubkeys)
2698 }
2699
2700 fn run_test_scan_accounts(num_pubkeys: usize) {
2701 let (index, _) = setup_accounts_index_keys(num_pubkeys);
2702 let ancestors = Ancestors::default();
2703
2704 let mut scanned_keys = HashSet::new();
2705 index.unchecked_scan_accounts(
2706 "",
2707 &ancestors,
2708 |pubkey, _index| {
2709 scanned_keys.insert(*pubkey);
2710 },
2711 &ScanConfig::default(),
2712 );
2713 assert_eq!(scanned_keys.len(), num_pubkeys);
2714 }
2715
2716 #[test]
2717 fn test_scan_accounts() {
2718 run_test_scan_accounts(0);
2719 run_test_scan_accounts(1);
2720 run_test_scan_accounts(ITER_BATCH_SIZE * 10);
2721 run_test_scan_accounts(ITER_BATCH_SIZE * 10 - 1);
2722 run_test_scan_accounts(ITER_BATCH_SIZE * 10 + 1);
2723 }
2724
2725 #[test]
2726 fn test_is_alive_root() {
2727 let index = AccountsIndex::<bool, bool>::default_for_tests();
2728 assert!(!index.is_alive_root(0));
2729 index.add_root(0);
2730 assert!(index.is_alive_root(0));
2731 }
2732
2733 #[test]
2734 fn test_insert_with_root() {
2735 let key = solana_pubkey::new_rand();
2736 let index = AccountsIndex::<bool, bool>::default_for_tests();
2737 let mut gc = Vec::new();
2738 index.upsert(
2739 0,
2740 0,
2741 &key,
2742 &AccountSharedData::default(),
2743 &AccountSecondaryIndexes::default(),
2744 true,
2745 &mut gc,
2746 UPSERT_RECLAIM_TEST_DEFAULT,
2747 );
2748 assert!(gc.is_empty());
2749
2750 index.add_root(0);
2751 index
2752 .get_with_and_then(&key, None, None, false, |(slot, account_info)| {
2753 assert_eq!(slot, 0);
2754 assert!(account_info);
2755 })
2756 .unwrap();
2757 }
2758
2759 #[test]
2760 fn test_clean_first() {
2761 let index = AccountsIndex::<bool, bool>::default_for_tests();
2762 index.add_root(0);
2763 index.add_root(1);
2764 index.clean_dead_slot(0);
2765 assert!(index.is_alive_root(1));
2766 assert!(!index.is_alive_root(0));
2767 }
2768
2769 #[test]
2770 fn test_clean_last() {
2771 let index = AccountsIndex::<bool, bool>::default_for_tests();
2773 index.add_root(0);
2774 index.add_root(1);
2775 index.clean_dead_slot(1);
2776 assert!(!index.is_alive_root(1));
2777 assert!(index.is_alive_root(0));
2778 }
2779
2780 #[test]
2781 fn test_update_last_wins() {
2782 let key = solana_pubkey::new_rand();
2783 let index = AccountsIndex::<u64, u64>::default_for_tests();
2784 let ancestors = vec![(0, 0)].into_iter().collect();
2785 let mut gc = Vec::new();
2786 index.upsert(
2787 0,
2788 0,
2789 &key,
2790 &AccountSharedData::default(),
2791 &AccountSecondaryIndexes::default(),
2792 1,
2793 &mut gc,
2794 UPSERT_RECLAIM_TEST_DEFAULT,
2795 );
2796 assert!(gc.is_empty());
2797 index
2798 .get_with_and_then(
2799 &key,
2800 Some(&ancestors),
2801 None,
2802 false,
2803 |(slot, account_info)| {
2804 assert_eq!(slot, 0);
2805 assert_eq!(account_info, 1);
2806 },
2807 )
2808 .unwrap();
2809
2810 let mut gc = Vec::new();
2811 index.upsert(
2812 0,
2813 0,
2814 &key,
2815 &AccountSharedData::default(),
2816 &AccountSecondaryIndexes::default(),
2817 0,
2818 &mut gc,
2819 UPSERT_RECLAIM_TEST_DEFAULT,
2820 );
2821 assert_eq!(gc, vec![(0, 1)]);
2822 index
2823 .get_with_and_then(
2824 &key,
2825 Some(&ancestors),
2826 None,
2827 false,
2828 |(slot, account_info)| {
2829 assert_eq!(slot, 0);
2830 assert_eq!(account_info, 0);
2831 },
2832 )
2833 .unwrap();
2834 }
2835
2836 #[test]
2837 fn test_update_new_slot() {
2838 solana_logger::setup();
2839 let key = solana_pubkey::new_rand();
2840 let index = AccountsIndex::<bool, bool>::default_for_tests();
2841 let ancestors = vec![(0, 0)].into_iter().collect();
2842 let mut gc = Vec::new();
2843 index.upsert(
2844 0,
2845 0,
2846 &key,
2847 &AccountSharedData::default(),
2848 &AccountSecondaryIndexes::default(),
2849 true,
2850 &mut gc,
2851 UpsertReclaim::PopulateReclaims,
2852 );
2853 assert!(gc.is_empty());
2854 index.upsert(
2855 1,
2856 1,
2857 &key,
2858 &AccountSharedData::default(),
2859 &AccountSecondaryIndexes::default(),
2860 false,
2861 &mut gc,
2862 UpsertReclaim::PopulateReclaims,
2863 );
2864 assert!(gc.is_empty());
2865 index
2866 .get_with_and_then(
2867 &key,
2868 Some(&ancestors),
2869 None,
2870 false,
2871 |(slot, account_info)| {
2872 assert_eq!(slot, 0);
2873 assert!(account_info);
2874 },
2875 )
2876 .unwrap();
2877 let ancestors = vec![(1, 0)].into_iter().collect();
2878 index
2879 .get_with_and_then(
2880 &key,
2881 Some(&ancestors),
2882 None,
2883 false,
2884 |(slot, account_info)| {
2885 assert_eq!(slot, 1);
2886 assert!(!account_info);
2887 },
2888 )
2889 .unwrap();
2890 }
2891
2892 #[test]
2893 fn test_update_gc_purged_slot() {
2894 let key = solana_pubkey::new_rand();
2895 let index = AccountsIndex::<bool, bool>::default_for_tests();
2896 let mut gc = Vec::new();
2897 index.upsert(
2898 0,
2899 0,
2900 &key,
2901 &AccountSharedData::default(),
2902 &AccountSecondaryIndexes::default(),
2903 true,
2904 &mut gc,
2905 UpsertReclaim::PopulateReclaims,
2906 );
2907 assert!(gc.is_empty());
2908 index.upsert(
2909 1,
2910 1,
2911 &key,
2912 &AccountSharedData::default(),
2913 &AccountSecondaryIndexes::default(),
2914 false,
2915 &mut gc,
2916 UpsertReclaim::PopulateReclaims,
2917 );
2918 index.upsert(
2919 2,
2920 2,
2921 &key,
2922 &AccountSharedData::default(),
2923 &AccountSecondaryIndexes::default(),
2924 true,
2925 &mut gc,
2926 UpsertReclaim::PopulateReclaims,
2927 );
2928 index.upsert(
2929 3,
2930 3,
2931 &key,
2932 &AccountSharedData::default(),
2933 &AccountSecondaryIndexes::default(),
2934 true,
2935 &mut gc,
2936 UpsertReclaim::PopulateReclaims,
2937 );
2938 index.add_root(0);
2939 index.add_root(1);
2940 index.add_root(3);
2941 index.upsert(
2942 4,
2943 4,
2944 &key,
2945 &AccountSharedData::default(),
2946 &AccountSecondaryIndexes::default(),
2947 true,
2948 &mut gc,
2949 UpsertReclaim::PopulateReclaims,
2950 );
2951
2952 assert_eq!(gc, vec![]);
2955 index
2956 .get_with_and_then(&key, None, None, false, |(slot, account_info)| {
2957 assert_eq!(slot, 3);
2958 assert!(account_info);
2959 })
2960 .unwrap();
2961
2962 let mut num = 0;
2963 let mut found_key = false;
2964 index.unchecked_scan_accounts(
2965 "",
2966 &Ancestors::default(),
2967 |pubkey, index| {
2968 if pubkey == &key {
2969 found_key = true;
2970 assert_eq!(index, (&true, 3));
2971 };
2972 num += 1
2973 },
2974 &ScanConfig::default(),
2975 );
2976 assert_eq!(num, 1);
2977 assert!(found_key);
2978 }
2979
2980 #[test]
2981 fn test_upsert_reclaims() {
2982 let key = solana_pubkey::new_rand();
2983 let index =
2984 AccountsIndex::<CacheableIndexValueTest, CacheableIndexValueTest>::default_for_tests();
2985 let mut reclaims = Vec::new();
2986 index.upsert(
2987 0,
2988 0,
2989 &key,
2990 &AccountSharedData::default(),
2991 &AccountSecondaryIndexes::default(),
2992 CacheableIndexValueTest(true),
2993 &mut reclaims,
2994 UPSERT_RECLAIM_TEST_DEFAULT,
2995 );
2996 assert!(reclaims.is_empty());
2998
2999 index.upsert(
3000 0,
3001 0,
3002 &key,
3003 &AccountSharedData::default(),
3004 &AccountSecondaryIndexes::default(),
3005 CacheableIndexValueTest(false),
3006 &mut reclaims,
3007 UPSERT_RECLAIM_TEST_DEFAULT,
3008 );
3009 assert!(reclaims.is_empty());
3011
3012 {
3015 let entry = index.get_cloned(&key).unwrap();
3016 let slot_list = entry.slot_list.read().unwrap();
3017 assert_eq!(slot_list.len(), 1);
3018 }
3019
3020 index.upsert(
3021 0,
3022 0,
3023 &key,
3024 &AccountSharedData::default(),
3025 &AccountSecondaryIndexes::default(),
3026 CacheableIndexValueTest(false),
3027 &mut reclaims,
3028 UPSERT_RECLAIM_TEST_DEFAULT,
3029 );
3030
3031 assert!(!reclaims.is_empty());
3033
3034 let entry = index.get_cloned(&key).unwrap();
3036 let slot_list = entry.slot_list.read().unwrap();
3037 assert_eq!(slot_list.len(), 1);
3038 }
3039
3040 fn account_maps_stats_len<T: IndexValue>(index: &AccountsIndex<T, T>) -> usize {
3041 index.storage.storage.stats.total_count()
3042 }
3043
3044 #[test]
3045 fn test_purge() {
3046 let key = solana_pubkey::new_rand();
3047 let index = AccountsIndex::<u64, u64>::default_for_tests();
3048 let mut gc = Vec::new();
3049 assert_eq!(0, account_maps_stats_len(&index));
3050 index.upsert(
3051 1,
3052 1,
3053 &key,
3054 &AccountSharedData::default(),
3055 &AccountSecondaryIndexes::default(),
3056 12,
3057 &mut gc,
3058 UPSERT_RECLAIM_TEST_DEFAULT,
3059 );
3060 assert_eq!(1, account_maps_stats_len(&index));
3061
3062 index.upsert(
3063 1,
3064 1,
3065 &key,
3066 &AccountSharedData::default(),
3067 &AccountSecondaryIndexes::default(),
3068 10,
3069 &mut gc,
3070 UPSERT_RECLAIM_TEST_DEFAULT,
3071 );
3072 assert_eq!(1, account_maps_stats_len(&index));
3073
3074 let purges = index.purge_roots(&key);
3075 assert_eq!(purges, (vec![], false));
3076 index.add_root(1);
3077
3078 let purges = index.purge_roots(&key);
3079 assert_eq!(purges, (vec![(1, 10)], true));
3080
3081 assert_eq!(1, account_maps_stats_len(&index));
3082 index.upsert(
3083 1,
3084 1,
3085 &key,
3086 &AccountSharedData::default(),
3087 &AccountSecondaryIndexes::default(),
3088 9,
3089 &mut gc,
3090 UPSERT_RECLAIM_TEST_DEFAULT,
3091 );
3092 assert_eq!(1, account_maps_stats_len(&index));
3093 }
3094
3095 #[test]
3096 fn test_latest_slot() {
3097 let slot_slice = vec![(0, true), (5, true), (3, true), (7, true)];
3098 let index = AccountsIndex::<bool, bool>::default_for_tests();
3099
3100 assert!(index.latest_slot(None, &slot_slice, None).is_none());
3102
3103 index.add_root(5);
3105 assert_eq!(index.latest_slot(None, &slot_slice, None).unwrap(), 1);
3106
3107 assert_eq!(index.latest_slot(None, &slot_slice, Some(5)).unwrap(), 1);
3109
3110 assert!(index.latest_slot(None, &slot_slice, Some(4)).is_none());
3112
3113 let ancestors = vec![(3, 1), (7, 1)].into_iter().collect();
3116 assert_eq!(
3117 index
3118 .latest_slot(Some(&ancestors), &slot_slice, Some(4))
3119 .unwrap(),
3120 3
3121 );
3122 assert_eq!(
3123 index
3124 .latest_slot(Some(&ancestors), &slot_slice, Some(7))
3125 .unwrap(),
3126 3
3127 );
3128
3129 assert_eq!(
3131 index
3132 .latest_slot(Some(&ancestors), &slot_slice, None)
3133 .unwrap(),
3134 3
3135 );
3136 }
3137
3138 fn make_empty_token_account_data() -> Vec<u8> {
3139 const SPL_TOKEN_INITIALIZED_OFFSET: usize = 108;
3140 let mut data = vec![0; spl_generic_token::token::Account::get_packed_len()];
3141 data[SPL_TOKEN_INITIALIZED_OFFSET] = 1;
3142 data
3143 }
3144
3145 fn run_test_purge_exact_secondary_index<
3146 SecondaryIndexEntryType: SecondaryIndexEntry + Default + Sync + Send,
3147 >(
3148 index: &AccountsIndex<bool, bool>,
3149 secondary_index: &SecondaryIndex<SecondaryIndexEntryType>,
3150 key_start: usize,
3151 key_end: usize,
3152 secondary_indexes: &AccountSecondaryIndexes,
3153 ) {
3154 let slots = vec![1, 2, 5, 9];
3156 let index_key = Pubkey::new_unique();
3157 let account_key = Pubkey::new_unique();
3158
3159 let mut account_data = make_empty_token_account_data();
3160 account_data[key_start..key_end].clone_from_slice(&(index_key.to_bytes()));
3161
3162 for slot in &slots {
3164 index.upsert(
3165 *slot,
3166 *slot,
3167 &account_key,
3168 &AccountSharedData::create(
3170 0,
3171 account_data.to_vec(),
3172 spl_generic_token::token::id(),
3173 false,
3174 0,
3175 ),
3176 secondary_indexes,
3177 true,
3178 &mut vec![],
3179 UPSERT_RECLAIM_TEST_DEFAULT,
3180 );
3181 }
3182
3183 assert_eq!(secondary_index.index.get(&index_key).unwrap().len(), 1);
3185
3186 assert_eq!(
3189 secondary_index
3190 .reverse_index
3191 .get(&account_key)
3192 .unwrap()
3193 .value()
3194 .read()
3195 .unwrap()
3196 .len(),
3197 1
3198 );
3199
3200 index.purge_exact(
3201 &account_key,
3202 &slots.into_iter().collect::<HashSet<Slot>>(),
3203 &mut vec![],
3204 );
3205
3206 let _ = index.handle_dead_keys(&[&account_key], secondary_indexes);
3207 assert!(secondary_index.index.is_empty());
3208 assert!(secondary_index.reverse_index.is_empty());
3209 }
3210
3211 #[test]
3212 fn test_reclaim_older_items_in_slot_list() {
3213 solana_logger::setup();
3214 let key = solana_pubkey::new_rand();
3215 let index = AccountsIndex::<u64, u64>::default_for_tests();
3216 let mut gc = Vec::new();
3217 let reclaim_slot = 5;
3218 let account_value = 50;
3219
3220 for slot in 0..reclaim_slot {
3222 index.upsert(
3223 slot,
3224 slot,
3225 &key,
3226 &AccountSharedData::default(),
3227 &AccountSecondaryIndexes::default(),
3228 slot,
3229 &mut gc,
3230 UpsertReclaim::IgnoreReclaims,
3231 );
3232 }
3233 let entry = index.get_cloned(&key).unwrap();
3234 assert_eq!(entry.slot_list.read().unwrap().len(), reclaim_slot as usize);
3235
3236 index.upsert(
3238 reclaim_slot + 1,
3239 reclaim_slot + 1,
3240 &key,
3241 &AccountSharedData::default(),
3242 &AccountSecondaryIndexes::default(),
3243 account_value + 1,
3244 &mut gc,
3245 UpsertReclaim::IgnoreReclaims,
3246 );
3247 let entry = index.get_cloned(&key).unwrap();
3248 assert_eq!(
3249 entry.slot_list.read().unwrap().len(),
3250 (reclaim_slot + 1) as usize
3251 );
3252
3253 index.upsert(
3255 reclaim_slot,
3256 reclaim_slot,
3257 &key,
3258 &AccountSharedData::default(),
3259 &AccountSecondaryIndexes::default(),
3260 account_value,
3261 &mut gc,
3262 UpsertReclaim::ReclaimOldSlots,
3263 );
3264
3265 assert_eq!(gc.len(), reclaim_slot as usize);
3267 for (slot, value) in gc.iter() {
3268 assert!(*slot < reclaim_slot);
3269 assert_eq!(*value, *slot);
3270 }
3271
3272 let ancestors = vec![(reclaim_slot, 0)].into_iter().collect();
3274 index
3275 .get_with_and_then(
3276 &key,
3277 Some(&ancestors),
3278 None,
3279 false,
3280 |(slot, account_info)| {
3281 assert_eq!(slot, reclaim_slot);
3282 assert_eq!(account_info, account_value);
3283 },
3284 )
3285 .unwrap();
3286
3287 let ancestors = vec![((reclaim_slot + 1), 0)].into_iter().collect();
3289 index
3290 .get_with_and_then(
3291 &key,
3292 Some(&ancestors),
3293 None,
3294 false,
3295 |(slot, account_info)| {
3296 assert_eq!(slot, reclaim_slot + 1);
3297 assert_eq!(account_info, account_value + 1);
3298 },
3299 )
3300 .unwrap();
3301 }
3302
3303 #[test]
3304 fn test_reclaim_do_not_reclaim_cached_other_slot() {
3305 solana_logger::setup();
3306 let key = solana_pubkey::new_rand();
3307 let index =
3308 AccountsIndex::<CacheableIndexValueTest, CacheableIndexValueTest>::default_for_tests();
3309 let mut gc = Vec::new();
3310
3311 index.upsert(
3313 0,
3314 0,
3315 &key,
3316 &AccountSharedData::default(),
3317 &AccountSecondaryIndexes::default(),
3318 CacheableIndexValueTest(false),
3319 &mut gc,
3320 UpsertReclaim::IgnoreReclaims,
3321 );
3322
3323 index.upsert(
3324 1,
3325 1,
3326 &key,
3327 &AccountSharedData::default(),
3328 &AccountSecondaryIndexes::default(),
3329 CacheableIndexValueTest(true),
3330 &mut gc,
3331 UpsertReclaim::IgnoreReclaims,
3332 );
3333
3334 index.upsert(
3336 2,
3337 2,
3338 &key,
3339 &AccountSharedData::default(),
3340 &AccountSecondaryIndexes::default(),
3341 CacheableIndexValueTest(true),
3342 &mut gc,
3343 UpsertReclaim::IgnoreReclaims,
3344 );
3345
3346 index.upsert(
3348 2,
3349 2,
3350 &key,
3351 &AccountSharedData::default(),
3352 &AccountSecondaryIndexes::default(),
3353 CacheableIndexValueTest(false),
3354 &mut gc,
3355 UpsertReclaim::ReclaimOldSlots,
3356 );
3357
3358 let entry = index.get_cloned(&key).unwrap();
3361 assert_eq!(entry.slot_list.read().unwrap().len(), 2);
3362 assert_eq!(
3363 entry.slot_list.read().unwrap()[0],
3364 PreAllocatedAccountMapEntry::new(
3365 1,
3366 CacheableIndexValueTest(true),
3367 &index.storage.storage,
3368 false
3369 )
3370 .into()
3371 );
3372 assert_eq!(
3373 entry.slot_list.read().unwrap()[1],
3374 PreAllocatedAccountMapEntry::new(
3375 2,
3376 CacheableIndexValueTest(false),
3377 &index.storage.storage,
3378 false
3379 )
3380 .into()
3381 );
3382 assert_eq!(gc.len(), 1);
3384 assert_eq!(gc[0], (0, CacheableIndexValueTest(false)));
3385 }
3386
3387 #[test]
3388 fn test_purge_exact_spl_token_mint_secondary_index() {
3389 let (key_start, key_end, secondary_indexes) = create_spl_token_mint_secondary_index_state();
3390 let index = AccountsIndex::<bool, bool>::default_for_tests();
3391 run_test_purge_exact_secondary_index(
3392 &index,
3393 &index.spl_token_mint_index,
3394 key_start,
3395 key_end,
3396 &secondary_indexes,
3397 );
3398 }
3399
3400 #[test]
3401 fn test_purge_exact_spl_token_owner_secondary_index() {
3402 let (key_start, key_end, secondary_indexes) =
3403 create_spl_token_owner_secondary_index_state();
3404 let index = AccountsIndex::<bool, bool>::default_for_tests();
3405 run_test_purge_exact_secondary_index(
3406 &index,
3407 &index.spl_token_owner_index,
3408 key_start,
3409 key_end,
3410 &secondary_indexes,
3411 );
3412 }
3413
3414 #[test]
3415 fn test_purge_older_root_entries() {
3416 let index = AccountsIndex::<bool, bool>::default_for_tests();
3418 let mut slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
3419 let mut reclaims = vec![];
3420 index.purge_older_root_entries(&mut slot_list, &mut reclaims, None);
3421 assert!(reclaims.is_empty());
3422 assert_eq!(slot_list, vec![(1, true), (2, true), (5, true), (9, true)]);
3423
3424 slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
3426 index.add_root(1);
3427 index.add_root(5);
3429 reclaims = vec![];
3430 index.purge_older_root_entries(&mut slot_list, &mut reclaims, None);
3431 assert_eq!(reclaims, vec![(1, true), (2, true)]);
3432 assert_eq!(slot_list, vec![(5, true), (9, true)]);
3433
3434 slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
3436 index.add_root(6);
3437 reclaims = vec![];
3438 index.purge_older_root_entries(&mut slot_list, &mut reclaims, None);
3439 assert_eq!(reclaims, vec![(1, true), (2, true)]);
3440 assert_eq!(slot_list, vec![(5, true), (9, true)]);
3441
3442 slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
3445 reclaims = vec![];
3446 index.purge_older_root_entries(&mut slot_list, &mut reclaims, Some(6));
3447 assert_eq!(reclaims, vec![(1, true), (2, true)]);
3448 assert_eq!(slot_list, vec![(5, true), (9, true)]);
3449
3450 slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
3452 reclaims = vec![];
3453 index.purge_older_root_entries(&mut slot_list, &mut reclaims, Some(5));
3454 assert_eq!(reclaims, vec![(1, true), (2, true)]);
3455 assert_eq!(slot_list, vec![(5, true), (9, true)]);
3456
3457 slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
3460 reclaims = vec![];
3461 index.purge_older_root_entries(&mut slot_list, &mut reclaims, Some(2));
3462 assert!(reclaims.is_empty());
3463 assert_eq!(slot_list, vec![(1, true), (2, true), (5, true), (9, true)]);
3464
3465 slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
3468 reclaims = vec![];
3469 index.purge_older_root_entries(&mut slot_list, &mut reclaims, Some(1));
3470 assert!(reclaims.is_empty());
3471 assert_eq!(slot_list, vec![(1, true), (2, true), (5, true), (9, true)]);
3472
3473 slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
3476 reclaims = vec![];
3477 index.purge_older_root_entries(&mut slot_list, &mut reclaims, Some(7));
3478 assert_eq!(reclaims, vec![(1, true), (2, true)]);
3479 assert_eq!(slot_list, vec![(5, true), (9, true)]);
3480 }
3481
3482 fn check_secondary_index_mapping_correct<SecondaryIndexEntryType>(
3483 secondary_index: &SecondaryIndex<SecondaryIndexEntryType>,
3484 secondary_index_keys: &[Pubkey],
3485 account_key: &Pubkey,
3486 ) where
3487 SecondaryIndexEntryType: SecondaryIndexEntry + Default + Sync + Send,
3488 {
3489 for secondary_index_key in secondary_index_keys {
3492 assert_eq!(secondary_index.index.len(), secondary_index_keys.len());
3493 let account_key_map = secondary_index.get(secondary_index_key);
3494 assert_eq!(account_key_map.len(), 1);
3495 assert_eq!(account_key_map, vec![*account_key]);
3496 }
3497 let secondary_index_key_map = secondary_index.reverse_index.get(account_key).unwrap();
3499 assert_eq!(
3500 &*secondary_index_key_map.value().read().unwrap(),
3501 secondary_index_keys
3502 );
3503 }
3504
3505 fn run_test_spl_token_secondary_indexes<
3506 SecondaryIndexEntryType: SecondaryIndexEntry + Default + Sync + Send,
3507 >(
3508 token_id: &Pubkey,
3509 index: &AccountsIndex<bool, bool>,
3510 secondary_index: &SecondaryIndex<SecondaryIndexEntryType>,
3511 key_start: usize,
3512 key_end: usize,
3513 secondary_indexes: &AccountSecondaryIndexes,
3514 ) {
3515 let mut secondary_indexes = secondary_indexes.clone();
3516 let account_key = Pubkey::new_unique();
3517 let index_key = Pubkey::new_unique();
3518 let mut account_data = make_empty_token_account_data();
3519 account_data[key_start..key_end].clone_from_slice(&(index_key.to_bytes()));
3520
3521 index.upsert(
3523 0,
3524 0,
3525 &account_key,
3526 &AccountSharedData::create(0, account_data.to_vec(), Pubkey::default(), false, 0),
3527 &secondary_indexes,
3528 true,
3529 &mut vec![],
3530 UPSERT_RECLAIM_TEST_DEFAULT,
3531 );
3532 assert!(secondary_index.index.is_empty());
3533 assert!(secondary_index.reverse_index.is_empty());
3534
3535 index.upsert(
3537 0,
3538 0,
3539 &account_key,
3540 &AccountSharedData::create(0, account_data[1..].to_vec(), *token_id, false, 0),
3541 &secondary_indexes,
3542 true,
3543 &mut vec![],
3544 UPSERT_RECLAIM_TEST_DEFAULT,
3545 );
3546 assert!(secondary_index.index.is_empty());
3547 assert!(secondary_index.reverse_index.is_empty());
3548
3549 secondary_indexes.keys = None;
3550
3551 for _ in 0..2 {
3553 index.update_secondary_indexes(
3554 &account_key,
3555 &AccountSharedData::create(0, account_data.to_vec(), *token_id, false, 0),
3556 &secondary_indexes,
3557 );
3558 check_secondary_index_mapping_correct(secondary_index, &[index_key], &account_key);
3559 }
3560
3561 assert!(!secondary_index.index.is_empty());
3563 assert!(!secondary_index.reverse_index.is_empty());
3564
3565 secondary_indexes.keys = Some(AccountSecondaryIndexesIncludeExclude {
3566 keys: [index_key].iter().cloned().collect::<HashSet<_>>(),
3567 exclude: false,
3568 });
3569 secondary_index.index.clear();
3570 secondary_index.reverse_index.clear();
3571 index.update_secondary_indexes(
3572 &account_key,
3573 &AccountSharedData::create(0, account_data.to_vec(), *token_id, false, 0),
3574 &secondary_indexes,
3575 );
3576 assert!(!secondary_index.index.is_empty());
3577 assert!(!secondary_index.reverse_index.is_empty());
3578 check_secondary_index_mapping_correct(secondary_index, &[index_key], &account_key);
3579
3580 secondary_indexes.keys = Some(AccountSecondaryIndexesIncludeExclude {
3582 keys: [].iter().cloned().collect::<HashSet<_>>(),
3583 exclude: true,
3584 });
3585 secondary_index.index.clear();
3586 secondary_index.reverse_index.clear();
3587 index.update_secondary_indexes(
3588 &account_key,
3589 &AccountSharedData::create(0, account_data.to_vec(), *token_id, false, 0),
3590 &secondary_indexes,
3591 );
3592 assert!(!secondary_index.index.is_empty());
3593 assert!(!secondary_index.reverse_index.is_empty());
3594 check_secondary_index_mapping_correct(secondary_index, &[index_key], &account_key);
3595
3596 secondary_indexes.keys = None;
3597
3598 index.slot_list_mut(&account_key, |slot_list| slot_list.clear());
3599
3600 let _ = index.handle_dead_keys(&[&account_key], &secondary_indexes);
3602 assert!(secondary_index.index.is_empty());
3603 assert!(secondary_index.reverse_index.is_empty());
3604 }
3605
3606 #[test]
3607 fn test_spl_token_mint_secondary_index() {
3608 let (key_start, key_end, secondary_indexes) = create_spl_token_mint_secondary_index_state();
3609 let index = AccountsIndex::<bool, bool>::default_for_tests();
3610 for token_id in &spl_token_ids() {
3611 run_test_spl_token_secondary_indexes(
3612 token_id,
3613 &index,
3614 &index.spl_token_mint_index,
3615 key_start,
3616 key_end,
3617 &secondary_indexes,
3618 );
3619 }
3620 }
3621
3622 #[test]
3623 fn test_spl_token_owner_secondary_index() {
3624 let (key_start, key_end, secondary_indexes) =
3625 create_spl_token_owner_secondary_index_state();
3626 let index = AccountsIndex::<bool, bool>::default_for_tests();
3627 for token_id in &spl_token_ids() {
3628 run_test_spl_token_secondary_indexes(
3629 token_id,
3630 &index,
3631 &index.spl_token_owner_index,
3632 key_start,
3633 key_end,
3634 &secondary_indexes,
3635 );
3636 }
3637 }
3638
3639 fn run_test_secondary_indexes_same_slot_and_forks<
3640 SecondaryIndexEntryType: SecondaryIndexEntry + Default + Sync + Send,
3641 >(
3642 token_id: &Pubkey,
3643 index: &AccountsIndex<bool, bool>,
3644 secondary_index: &SecondaryIndex<SecondaryIndexEntryType>,
3645 index_key_start: usize,
3646 index_key_end: usize,
3647 secondary_indexes: &AccountSecondaryIndexes,
3648 ) {
3649 let account_key = Pubkey::new_unique();
3650 let secondary_key1 = Pubkey::new_unique();
3651 let secondary_key2 = Pubkey::new_unique();
3652 let slot = 1;
3653 let mut account_data1 = make_empty_token_account_data();
3654 account_data1[index_key_start..index_key_end]
3655 .clone_from_slice(&(secondary_key1.to_bytes()));
3656 let mut account_data2 = make_empty_token_account_data();
3657 account_data2[index_key_start..index_key_end]
3658 .clone_from_slice(&(secondary_key2.to_bytes()));
3659
3660 index.upsert(
3662 slot,
3663 slot,
3664 &account_key,
3665 &AccountSharedData::create(0, account_data1.to_vec(), *token_id, false, 0),
3666 secondary_indexes,
3667 true,
3668 &mut vec![],
3669 UPSERT_RECLAIM_TEST_DEFAULT,
3670 );
3671
3672 index.upsert(
3674 slot,
3675 slot,
3676 &account_key,
3677 &AccountSharedData::create(0, account_data2.to_vec(), *token_id, false, 0),
3678 secondary_indexes,
3679 true,
3680 &mut vec![],
3681 UPSERT_RECLAIM_TEST_DEFAULT,
3682 );
3683
3684 check_secondary_index_mapping_correct(
3686 secondary_index,
3687 &[secondary_key1, secondary_key2],
3688 &account_key,
3689 );
3690
3691 let later_slot = slot + 1;
3693 index.upsert(
3694 later_slot,
3695 later_slot,
3696 &account_key,
3697 &AccountSharedData::create(0, account_data1.to_vec(), *token_id, false, 0),
3698 secondary_indexes,
3699 true,
3700 &mut vec![],
3701 UPSERT_RECLAIM_TEST_DEFAULT,
3702 );
3703 assert_eq!(secondary_index.get(&secondary_key1), vec![account_key]);
3704
3705 index.add_root(later_slot);
3709 index.slot_list_mut(&account_key, |slot_list| {
3710 index.purge_older_root_entries(slot_list, &mut vec![], None)
3711 });
3712
3713 check_secondary_index_mapping_correct(
3714 secondary_index,
3715 &[secondary_key1, secondary_key2],
3716 &account_key,
3717 );
3718
3719 let mut reclaims = vec![];
3722 index.purge_exact(&account_key, &later_slot, &mut reclaims);
3723 let _ = index.handle_dead_keys(&[&account_key], secondary_indexes);
3724 assert!(secondary_index.index.is_empty());
3725 assert!(secondary_index.reverse_index.is_empty());
3726 }
3727
3728 #[test]
3729 fn test_spl_token_mint_secondary_index_same_slot_and_forks() {
3730 let (key_start, key_end, account_index) = create_spl_token_mint_secondary_index_state();
3731 let index = AccountsIndex::<bool, bool>::default_for_tests();
3732 for token_id in &spl_token_ids() {
3733 run_test_secondary_indexes_same_slot_and_forks(
3734 token_id,
3735 &index,
3736 &index.spl_token_mint_index,
3737 key_start,
3738 key_end,
3739 &account_index,
3740 );
3741 }
3742 }
3743
3744 #[test]
3745 fn test_rwlock_secondary_index_same_slot_and_forks() {
3746 let (key_start, key_end, account_index) = create_spl_token_owner_secondary_index_state();
3747 let index = AccountsIndex::<bool, bool>::default_for_tests();
3748 for token_id in &spl_token_ids() {
3749 run_test_secondary_indexes_same_slot_and_forks(
3750 token_id,
3751 &index,
3752 &index.spl_token_owner_index,
3753 key_start,
3754 key_end,
3755 &account_index,
3756 );
3757 }
3758 }
3759
3760 impl IndexValue for bool {}
3761 impl IndexValue for u64 {}
3762 impl DiskIndexValue for bool {}
3763 impl DiskIndexValue for u64 {}
3764 impl IsCached for bool {
3765 fn is_cached(&self) -> bool {
3766 false
3767 }
3768 }
3769 impl IsCached for u64 {
3770 fn is_cached(&self) -> bool {
3771 false
3772 }
3773 }
3774 impl IsZeroLamport for bool {
3775 fn is_zero_lamport(&self) -> bool {
3776 false
3777 }
3778 }
3779
3780 impl IsZeroLamport for u64 {
3781 fn is_zero_lamport(&self) -> bool {
3782 false
3783 }
3784 }
3785
3786 #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
3789 struct CacheableIndexValueTest(bool);
3790 impl IndexValue for CacheableIndexValueTest {}
3791 impl DiskIndexValue for CacheableIndexValueTest {}
3792 impl IsCached for CacheableIndexValueTest {
3793 fn is_cached(&self) -> bool {
3794 self.0
3796 }
3797 }
3798 impl IsZeroLamport for CacheableIndexValueTest {
3799 fn is_zero_lamport(&self) -> bool {
3800 false
3801 }
3802 }
3803
3804 #[test]
3805 fn test_get_newest_root_in_slot_list() {
3806 let index = AccountsIndex::<bool, bool>::default_for_tests();
3807 let return_0 = 0;
3808 let slot1 = 1;
3809 let slot2 = 2;
3810 let slot99 = 99;
3811
3812 {
3814 let roots_tracker = &index.roots_tracker.read().unwrap();
3815 let slot_list = Vec::<(Slot, bool)>::default();
3816 assert_eq!(
3817 return_0,
3818 AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
3819 &roots_tracker.alive_roots,
3820 &slot_list,
3821 Some(slot1),
3822 )
3823 );
3824 assert_eq!(
3825 return_0,
3826 AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
3827 &roots_tracker.alive_roots,
3828 &slot_list,
3829 Some(slot2),
3830 )
3831 );
3832 assert_eq!(
3833 return_0,
3834 AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
3835 &roots_tracker.alive_roots,
3836 &slot_list,
3837 Some(slot99),
3838 )
3839 );
3840 }
3841
3842 index.add_root(slot2);
3843
3844 {
3845 let roots_tracker = &index.roots_tracker.read().unwrap();
3846 let slot_list = vec![(slot2, true)];
3847 assert_eq!(
3848 slot2,
3849 AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
3850 &roots_tracker.alive_roots,
3851 &slot_list,
3852 Some(slot2),
3853 )
3854 );
3855 assert_eq!(
3857 return_0,
3858 AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
3859 &roots_tracker.alive_roots,
3860 &slot_list,
3861 Some(slot1),
3862 )
3863 );
3864 assert_eq!(
3865 slot2,
3866 AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
3867 &roots_tracker.alive_roots,
3868 &slot_list,
3869 Some(slot99),
3870 )
3871 );
3872 }
3873 }
3874
3875 impl<T: IndexValue> AccountsIndex<T, T> {
3876 fn upsert_simple_test(&self, key: &Pubkey, slot: Slot, value: T) {
3877 let mut gc = Vec::new();
3878
3879 let reclaim_method = if self.is_alive_root(slot) {
3882 UPSERT_RECLAIM_TEST_DEFAULT
3883 } else {
3884 UpsertReclaim::IgnoreReclaims
3885 };
3886
3887 self.upsert(
3888 slot,
3889 slot,
3890 key,
3891 &AccountSharedData::default(),
3892 &AccountSecondaryIndexes::default(),
3893 value,
3894 &mut gc,
3895 reclaim_method,
3896 );
3897 assert!(gc.is_empty());
3898 }
3899
3900 pub fn clear_roots(&self) {
3901 self.roots_tracker.write().unwrap().alive_roots.clear()
3902 }
3903 }
3904
3905 #[test]
3906 fn test_unref() {
3907 let value = true;
3908 let key = solana_pubkey::new_rand();
3909 let index = AccountsIndex::<bool, bool>::default_for_tests();
3910 let slot1 = 1;
3911
3912 index.upsert_simple_test(&key, slot1, value);
3913
3914 let entry = index.get_cloned(&key).unwrap();
3915 assert_eq!(entry.ref_count(), 1);
3917 assert_eq!(entry.unref(), 1);
3919 assert_eq!(entry.ref_count(), 0);
3921 }
3922
3923 #[test]
3924 #[should_panic(expected = "decremented ref count below zero")]
3925 fn test_illegal_unref() {
3926 let value = true;
3927 let key = solana_pubkey::new_rand();
3928 let index = AccountsIndex::<bool, bool>::default_for_tests();
3929 let slot1 = 1;
3930
3931 index.upsert_simple_test(&key, slot1, value);
3932
3933 let entry = index.get_cloned(&key).unwrap();
3934 assert_eq!(entry.unref(), 1);
3936 assert_eq!(entry.ref_count(), 0);
3937
3938 entry.unref();
3940 }
3941
3942 #[test]
3943 fn test_clean_rooted_entries_return() {
3944 solana_logger::setup();
3945 let value = true;
3946 let key = solana_pubkey::new_rand();
3947 let key_unknown = solana_pubkey::new_rand();
3948 let index = AccountsIndex::<bool, bool>::default_for_tests();
3949 let slot1 = 1;
3950
3951 let mut gc = Vec::new();
3952 assert!(index.clean_rooted_entries(&key_unknown, &mut gc, None));
3955
3956 index.upsert_simple_test(&key, slot1, value);
3957
3958 let slot2 = 2;
3959 assert!(!index.clean_rooted_entries(&key, &mut gc, None));
3961 assert!(!index.clean_rooted_entries(&key, &mut gc, Some(slot1)));
3963 {
3966 let mut gc = Vec::new();
3967 assert!(index.clean_rooted_entries(&key, &mut gc, Some(slot2)));
3968 assert_eq!(gc, vec![(slot1, value)]);
3969 }
3970
3971 index.upsert_simple_test(&key, slot1, value);
3973
3974 index.add_root(slot1);
3975 assert!(!index.clean_rooted_entries(&key, &mut gc, Some(slot2)));
3976 index.upsert_simple_test(&key, slot2, value);
3977
3978 {
3979 let account_map_entry = index.get_cloned(&key).unwrap();
3980 let slot_list = account_map_entry.slot_list.read().unwrap();
3981 assert_eq!(2, slot_list.len());
3982 assert_eq!(&[(slot1, value), (slot2, value)], slot_list.as_slice());
3983 }
3984 assert!(!index.clean_rooted_entries(&key, &mut gc, Some(slot2)));
3985 assert_eq!(
3986 2,
3987 index
3988 .get_cloned(&key)
3989 .unwrap()
3990 .slot_list
3991 .read()
3992 .unwrap()
3993 .len()
3994 );
3995 assert!(gc.is_empty());
3996 {
3997 {
3998 let roots_tracker = &index.roots_tracker.read().unwrap();
3999 let slot_list = vec![(slot2, value)];
4000 assert_eq!(
4001 0,
4002 AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
4003 &roots_tracker.alive_roots,
4004 &slot_list,
4005 None,
4006 )
4007 );
4008 }
4009 index.add_root(slot2);
4010 {
4011 let roots_tracker = &index.roots_tracker.read().unwrap();
4012 let slot_list = vec![(slot2, value)];
4013 assert_eq!(
4014 slot2,
4015 AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
4016 &roots_tracker.alive_roots,
4017 &slot_list,
4018 None,
4019 )
4020 );
4021 assert_eq!(
4022 0,
4023 AccountsIndex::<bool, bool>::get_newest_root_in_slot_list(
4024 &roots_tracker.alive_roots,
4025 &slot_list,
4026 Some(0),
4027 )
4028 );
4029 }
4030 }
4031
4032 assert!(gc.is_empty());
4033 assert!(!index.clean_rooted_entries(&key, &mut gc, Some(slot2)));
4034 assert_eq!(gc, vec![(slot1, value)]);
4035 gc.clear();
4036 index.clean_dead_slot(slot2);
4037 let slot3 = 3;
4038 assert!(index.clean_rooted_entries(&key, &mut gc, Some(slot3)));
4039 assert_eq!(gc, vec![(slot2, value)]);
4040 }
4041
4042 #[test]
4043 fn test_handle_dead_keys_return() {
4044 let key = solana_pubkey::new_rand();
4045 let index = AccountsIndex::<bool, bool>::default_for_tests();
4046
4047 assert_eq!(
4048 index.handle_dead_keys(&[&key], &AccountSecondaryIndexes::default()),
4049 vec![key].into_iter().collect::<HashSet<_>>()
4050 );
4051 }
4052
4053 #[test]
4054 fn test_start_end_bin() {
4055 let index = AccountsIndex::<bool, bool>::default_for_tests();
4056 assert_eq!(index.bins(), BINS_FOR_TESTING);
4057
4058 let range = (Unbounded::<Pubkey>, Unbounded);
4059 let (start, end) = index.bin_start_end_inclusive(&range);
4060 assert_eq!(start, 0); assert_eq!(end, BINS_FOR_TESTING - 1); let key = Pubkey::from([0; 32]);
4064 let range = RangeInclusive::new(key, key);
4065 let (start, end) = index.bin_start_end_inclusive(&range);
4066 assert_eq!(start, 0); assert_eq!(end, 0); let range = (Included(key), Excluded(key));
4070 let (start, end) = index.bin_start_end_inclusive(&range);
4071 assert_eq!(start, 0); assert_eq!(end, 0); let range = (Excluded(key), Excluded(key));
4075 let (start, end) = index.bin_start_end_inclusive(&range);
4076 assert_eq!(start, 0); assert_eq!(end, 0); let key = Pubkey::from([0xff; 32]);
4080 let range = RangeInclusive::new(key, key);
4081 let (start, end) = index.bin_start_end_inclusive(&range);
4082 let bins = index.bins();
4083 assert_eq!(start, bins - 1); assert_eq!(end, bins - 1);
4085
4086 let range = (Included(key), Excluded(key));
4087 let (start, end) = index.bin_start_end_inclusive(&range);
4088 assert_eq!(start, bins - 1); assert_eq!(end, bins - 1);
4090
4091 let range = (Excluded(key), Excluded(key));
4092 let (start, end) = index.bin_start_end_inclusive(&range);
4093 assert_eq!(start, bins); assert_eq!(end, bins - 1); }
4096
4097 #[test]
4098 #[should_panic(expected = "bins.is_power_of_two()")]
4099 #[allow(clippy::field_reassign_with_default)]
4100 fn test_illegal_bins() {
4101 let mut config = AccountsIndexConfig::default();
4102 config.bins = Some(3);
4103 AccountsIndex::<bool, bool>::new(&config, Arc::default());
4104 }
4105
4106 #[test]
4107 fn test_scan_config() {
4108 for scan_order in [ScanOrder::Sorted, ScanOrder::Unsorted] {
4109 let config = ScanConfig::new(scan_order);
4110 assert_eq!(config.scan_order, scan_order);
4111 assert!(config.abort.is_none()); assert!(!config.is_aborted());
4113 config.abort(); assert!(!config.is_aborted());
4115 }
4116
4117 let config = ScanConfig::new(ScanOrder::Sorted);
4118 assert_eq!(config.scan_order, ScanOrder::Sorted);
4119 assert!(config.abort.is_none());
4120
4121 let config = ScanConfig::default();
4122 assert_eq!(config.scan_order, ScanOrder::Unsorted);
4123 assert!(config.abort.is_none());
4124
4125 let config = config.recreate_with_abort();
4126 assert!(config.abort.is_some());
4127 assert!(!config.is_aborted());
4128 config.abort();
4129 assert!(config.is_aborted());
4130
4131 let config = config.recreate_with_abort();
4132 assert!(config.is_aborted());
4133 }
4134}