solana_accounts_db/
accounts_index.rs

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; // we want > 1, but each bin is a few disk files with a disk based index, so fewer is better
53pub const BINS_FOR_BENCHMARKS: usize = 8192;
54// The unsafe is safe because we're using a fixed, known non-zero value
55pub 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/// values returned from `insert_new_if_missing_into_primary_index()`
78#[derive(Default, Debug, PartialEq, Eq)]
79pub(crate) struct InsertNewIfMissingIntoPrimaryIndexInfo {
80    /// number of accounts inserted in the index
81    pub count: usize,
82    /// Number of accounts added to the index that didn't already exist in the index
83    pub num_did_not_exist: u64,
84    /// Number of accounts added to the index that already existed, and were in-mem
85    pub num_existed_in_mem: u64,
86    /// Number of accounts added to the index that already existed, and were on-disk
87    pub num_existed_on_disk: u64,
88}
89
90#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
91/// which accounts `scan` should load from disk
92pub enum ScanFilter {
93    /// Scan both in-memory and on-disk index
94    #[default]
95    All,
96
97    /// abnormal = ref_count != 1 or slot list.len() != 1
98    /// Scan only in-memory index and skip on-disk index
99    OnlyAbnormal,
100
101    /// Similar to `OnlyAbnormal but also check on-disk index to verify the
102    /// entry on-disk is indeed normal.
103    OnlyAbnormalWithVerify,
104
105    /// Similar to `OnlyAbnormal but mark entries in memory as not found
106    /// if they are normal
107    /// This removes the possibility of any race conditions with index
108    /// flushing and simulates the system running an uncached disk index
109    /// where nothing 'normal' is ever held in the in memory index as far as
110    /// callers are concerned. This could also be a  correct/ideal future api
111    /// to similarly provide consistency and remove race condition behavior.
112    OnlyAbnormalTest,
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116/// how accounts index 'upsert' should handle reclaims
117pub enum UpsertReclaim {
118    /// previous entry for this slot in the index is expected to be cached, so irrelevant to reclaims
119    PreviousSlotEntryWasCached,
120    /// previous entry for this slot in the index may need to be reclaimed, so return it.
121    /// reclaims is the only output of upsert, requiring a synchronous execution
122    PopulateReclaims,
123    /// overwrite existing data in the same slot and do not return in 'reclaims'
124    IgnoreReclaims,
125    // Reclaim all older versions of the account from the index and return
126    // in the 'reclaims'
127    ReclaimOldSlots,
128}
129
130#[derive(Debug)]
131pub struct ScanConfig {
132    /// checked by the scan. When true, abort scan.
133    pub abort: Option<Arc<AtomicBool>>,
134
135    /// In what order should items be scanned?
136    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    /// mark the scan as aborted
157    pub fn abort(&self) {
158        if let Some(abort) = self.abort.as_ref() {
159            abort.store(true, Ordering::Relaxed)
160        }
161    }
162
163    /// use existing 'abort' if available, otherwise allocate one
164    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    /// true if scan should abort
172    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/// In what order should items be scanned?
182///
183/// Users should prefer `Unsorted`, unless required otherwise,
184/// as sorting incurs additional runtime cost.
185#[derive(Debug, Copy, Clone, Eq, PartialEq)]
186pub enum ScanOrder {
187    /// Scan items in any order
188    Unsorted,
189    /// Scan items in sorted order
190    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/// specification of how much memory in-mem portion of account index can use
221#[derive(Debug, Copy, Clone)]
222pub enum IndexLimitMb {
223    /// use disk index while keeping a minimal amount in-mem
224    Minimal,
225    /// in-mem-only was specified, no disk index
226    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    /// if the entry is not in the in-memory index, do not add it unless the entry becomes dirty
270    OnlyKeepInMemoryIfDirty,
271    /// keep the entry in the in-memory index
272    KeepInMemory,
273    /// reduce refcount by 1
274    Unref,
275    /// reduce refcount by 1 and assert that ref_count = 0 after unref
276    UnrefAssert0,
277    /// reduce refcount by 1 and log if ref_count != 0 after unref
278    UnrefLog0,
279}
280
281#[derive(Debug)]
282/// T: account info type to interact in in-memory items
283/// U: account info type to be persisted to disk
284pub 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    // Each scan has some latest slot `S` that is the tip of the fork the scan
293    // is iterating over. The unique id of that slot `S` is recorded here (note we don't use
294    // `S` as the id because there can be more than one version of a slot `S`). If a fork
295    // is abandoned, all of the slots on that fork up to `S` will be removed via
296    // `AccountsDb::remove_unrooted_slots()`. When the scan finishes, it'll realize that the
297    // results of the scan may have been corrupted by `remove_unrooted_slots` and abort its results.
298    //
299    // `removed_bank_ids` tracks all the slot ids that were removed via `remove_unrooted_slots()` so any attempted scans
300    // on any of these slots fails. This is safe to purge once the associated Bank is dropped and
301    // scanning the fork with that Bank at the tip is no longer possible.
302    pub removed_bank_ids: Mutex<HashSet<BankId>>,
303
304    storage: AccountsIndexStorage<T, U>,
305
306    /// when a scan's accumulated data exceeds this limit, abort the scan
307    pub scan_results_limit_bytes: Option<usize>,
308
309    pub purge_older_root_entries_one_slot_list: AtomicUsize,
310
311    /// # roots added since last check
312    pub roots_added: AtomicUsize,
313    /// # roots removed since last check
314    pub roots_removed: AtomicUsize,
315    /// # scans active currently
316    pub active_scans: AtomicUsize,
317    /// # of slots between latest max and latest scan
318    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    /// return the bin index for a given pubkey
355    fn bin_from_pubkey(&self, pubkey: &Pubkey) -> usize {
356        self.bin_calculator.bin_from_pubkey(pubkey)
357    }
358
359    /// returns the start bin and the end bin (inclusive) to scan.
360    ///
361    /// Note that start_bin maybe larger than highest bin index. Therefore, the
362    /// caller should not assume that start_bin is a valid bin index. So don't
363    /// index into `account_maps` with start_bin. Use `start_bin..=end_bin` to
364    /// iterate over the bins.
365    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                // check if start == self.account_maps[start_bin].highest_pubkey(), then
373                // we should return start_bin + 1
374                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                // check if end == self.account_maps[end_bin].lowest_pubkey(), then
388                // we should return end_bin - 1
389                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        // create bin_calculator early to verify # bins is reasonable
413        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    /// is the accounts index using disk as a backing store
434    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                // This lock is also grabbed by clean_accounts(), so clean
469                // has at most cleaned up to the current `max_root` (since
470                // clean only happens *after* BankForks::set_root() which sets
471                // the `max_root`)
472                .ongoing_scan_roots
473                .write()
474                .unwrap();
475            // `max_root()` grabs a lock while
476            // the `ongoing_scan_roots` lock is held,
477            // make sure inverse doesn't happen to avoid
478            // deadlock
479            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        // First we show that for any bank `B` that is a descendant of
495        // the current `max_root`, it must be true that and `B.ancestors.contains(max_root)`,
496        // regardless of the pattern of `squash()` behavior, where `ancestors` is the set
497        // of ancestors that is tracked in each bank.
498        //
499        // Proof: At startup, if starting from a snapshot, generate_index() adds all banks
500        // in the snapshot to the index via `add_root()` and so `max_root` will be the
501        // greatest of these. Thus, so the claim holds at startup since there are no
502        // descendants of `max_root`.
503        //
504        // Now we proceed by induction on each `BankForks::set_root()`.
505        // Assume the claim holds when the `max_root` is `R`. Call the set of
506        // descendants of `R` present in BankForks `R_descendants`.
507        //
508        // Then for any banks `B` in `R_descendants`, it must be that `B.ancestors.contains(S)`,
509        // where `S` is any ancestor of `B` such that `S >= R`.
510        //
511        // For example:
512        //          `R` -> `A` -> `C` -> `B`
513        // Then `B.ancestors == {R, A, C}`
514        //
515        // Next we call `BankForks::set_root()` at some descendant of `R`, `R_new`,
516        // where `R_new > R`.
517        //
518        // When we squash `R_new`, `max_root` in the AccountsIndex here is now set to `R_new`,
519        // and all nondescendants of `R_new` are pruned.
520        //
521        // Now consider any outstanding references to banks in the system that are descended from
522        // `max_root == R_new`. Take any one of these references and call it `B`. Because `B` is
523        // a descendant of `R_new`, this means `B` was also a descendant of `R`. Thus `B`
524        // must be a member of `R_descendants` because `B` was constructed and added to
525        // BankForks before the `set_root`.
526        //
527        // This means by the guarantees of `R_descendants` described above, because
528        // `R_new` is an ancestor of `B`, and `R < R_new < B`, then `B.ancestors.contains(R_new)`.
529        //
530        // Now until the next `set_root`, any new banks constructed from `new_from_parent` will
531        // also have `max_root == R_new` in their ancestor set, so the claim holds for those descendants
532        // as well. Once the next `set_root` happens, we once again update `max_root` and the same
533        // inductive argument can be applied again to show the claim holds.
534
535        // Check that the `max_root` is present in `ancestors`. From the proof above, if
536        // `max_root` is not present in `ancestors`, this means the bank `B` with the
537        // given `ancestors` is not descended from `max_root, which means
538        // either:
539        // 1) `B` is on a different fork or
540        // 2) `B` is an ancestor of `max_root`.
541        // In both cases we can ignore the given ancestors and instead just rely on the roots
542        // present as `max_root` indicates the roots present in the index are more up to date
543        // than the ancestors given.
544        let empty = Ancestors::default();
545        let ancestors = if ancestors.contains_key(&max_root) {
546            ancestors
547        } else {
548            /*
549            This takes of edge cases like:
550
551            Diagram 1:
552
553                        slot 0
554                          |
555                        slot 1
556                      /        \
557                 slot 2         |
558                    |       slot 3 (max root)
559            slot 4 (scan)
560
561            By the time the scan on slot 4 is called, slot 2 may already have been
562            cleaned by a clean on slot 3, but slot 4 may not have been cleaned.
563            The state in slot 2 would have been purged and is not saved in any roots.
564            In this case, a scan on slot 4 wouldn't accurately reflect the state when bank 4
565            was frozen. In cases like this, we default to a scan on the latest roots by
566            removing all `ancestors`.
567            */
568            &empty
569        };
570
571        /*
572        Now there are two cases, either `ancestors` is empty or nonempty:
573
574        1) If ancestors is empty, then this is the same as a scan on a rooted bank,
575        and `ongoing_scan_roots` provides protection against cleanup of roots necessary
576        for the scan, and  passing `Some(max_root)` to `do_scan_accounts()` ensures newer
577        roots don't appear in the scan.
578
579        2) If ancestors is non-empty, then from the `ancestors_contains(&max_root)` above, we know
580        that the fork structure must look something like:
581
582        Diagram 2:
583
584                Build fork structure:
585                        slot 0
586                          |
587                    slot 1 (max_root)
588                    /            \
589             slot 2              |
590                |            slot 3 (potential newer max root)
591              slot 4
592                |
593             slot 5 (scan)
594
595        Consider both types of ancestors, ancestor <= `max_root` and
596        ancestor > `max_root`, where `max_root == 1` as illustrated above.
597
598        a) The set of `ancestors <= max_root` are all rooted, which means their state
599        is protected by the same guarantees as 1).
600
601        b) As for the `ancestors > max_root`, those banks have at least one reference discoverable
602        through the chain of `Bank::BankRc::parent` starting from the calling bank. For instance
603        bank 5's parent reference keeps bank 4 alive, which will prevent the `Bank::drop()` from
604        running and cleaning up bank 4. Furthermore, no cleans can happen past the saved max_root == 1,
605        so a potential newer max root at 3 will not clean up any of the ancestors > 1, so slot 4
606        will not be cleaned in the middle of the scan either. (NOTE similar reasoning is employed for
607        assert!() justification in AccountsDb::retry_to_get_account_accessor)
608        */
609        match scan_type {
610            ScanTypes::Unindexed(range) => {
611                // Pass "" not to log metrics, so RPC doesn't get spammy
612                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        // If the fork with tip at bank `scan_bank_id` was removed during our scan, then the scan
657        // may have been corrupted, so abort the results.
658        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    // Scan accounts and return latest version of each account that is either:
690    // 1) rooted or
691    // 2) present in ancestors
692    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        // TODO: expand to use mint index to find the `pubkey_list` below more efficiently
710        // instead of scanning the entire range
711        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    /// Gets the index's entry for `pubkey` and applies `callback` to it
794    ///
795    /// If `callback`'s boolean return value is true, add this entry to the in-mem cache.
796    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    /// Gets the index's entry for `pubkey`, with `ancestors` and `max_root`,
805    /// and applies `callback` to it
806    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    /// Gets the account info (and slot) in `entry`, with `ancestors` and `max_root`,
823    /// and applies `callback` to it
824    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    /// Gets the index's entry for `pubkey` and clones it
837    ///
838    /// Prefer `get_and_then()` whenever possible.
839    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    /// Is `pubkey` in the index?
845    pub fn contains(&self, pubkey: &Pubkey) -> bool {
846        self.get_and_then(pubkey, |entry| (false, entry.is_some()))
847    }
848
849    /// Is `pubkey`, with `ancestors` and `max_root`, in the index?
850    #[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    /// Remove keys from the account index if the key's slot list is empty.
871    /// Returns the keys that were removed from the index. These keys should not be accessed again in the current code path.
872    #[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                    // Note it's only safe to remove all the entries for this key
885                    // because we have the lock for this key's entry in the AccountsIndex,
886                    // so no other thread is also updating the index
887                    self.purge_secondary_indexes_by_inner_key(key, account_indexes);
888                }
889            }
890        }
891        pubkeys_removed_from_accounts_index
892    }
893
894    /// call func with every pubkey and index visible from a given set of ancestors
895    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        // Pass "" not to log metrics, so RPC doesn't get spammy
906        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    /// call func with every pubkey and index visible from a given set of ancestors
935    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        // Pass "" not to log metrics, so RPC doesn't get spammy
947        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    /// returns true if, after this fn call:
972    /// accounts index entry for `pubkey` has an empty slot list
973    /// or `pubkey` does not exist in accounts index
974    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    // Given a SlotList `L`, a list of ancestors and a maximum slot, find the latest element
1003    // in `L`, where the slot `S` is an ancestor or root, and if `S` is a root, then `S <= max_root`
1004    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    /// get stats related to startup
1048    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    /// Scan AccountsIndex for a given iterator of Pubkeys.
1061    ///
1062    /// This fn takes 4 arguments.
1063    ///  - an iterator of pubkeys to scan
1064    ///  - callback fn to run for each pubkey in the accounts index
1065    ///  - avoid_callback_result. If it is Some(default), then callback is ignored and
1066    ///    default is returned instead.
1067    ///  - provide_entry_in_callback. If true, populate the ref of the Arc of the
1068    ///    index entry to `callback` fn. Otherwise, provide None.
1069    ///
1070    /// The `callback` fn must return `AccountsIndexScanResult`, which is
1071    /// used to indicates whether the AccountIndex Entry should be added to
1072    /// in-memory cache. The `callback` fn takes in 3 arguments:
1073    ///   - the first an immutable ref of the pubkey,
1074    ///   - the second an option of the SlotList and RefCount
1075    ///   - the third an option of the AccountMapEntry, which is only populated
1076    ///     when `provide_entry_in_callback` is true. Otherwise, it will be
1077    ///     None.
1078    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(); // too big, won't match
1095        pubkeys.into_iter().for_each(|pubkey| {
1096            let bin = self.bin_calculator.bin_from_pubkey(pubkey);
1097            if bin != last_bin {
1098                // cannot re-use lock since next pubkey is in a different bin than previous one
1099                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                    // SAFETY: The caller must ensure that if `provide_entry_in_callback` is true, and
1162                    // if it's possible for `callback` to clone the entry Arc, then it must also add
1163                    // the entry to the in-mem cache if the entry is made dirty.
1164                    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                                        // Account was found in memory, but is a single ref single slot account
1182                                        // For testing purposes, return None as this can be treated like
1183                                        // a normal account that was flushed to storage.
1184                                        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    // Get the maximum root <= `max_allowed_root` from the given `slot_list`
1205    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    /// log any secondary index counts, if non-zero
1264    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        // Note because of the below check below on the account data length, when an
1298        // account hits zero lamports and is reset to AccountSharedData::Default, then we skip
1299        // the below updates to the secondary indexes.
1300        //
1301        // Skipping means not updating secondary index to mark the account as missing.
1302        // This doesn't introduce false positives during a scan because the caller to scan
1303        // provides the ancestors to check. So even if a zero-lamport account is not yet
1304        // removed from the secondary index, the scan function will:
1305        // 1) consult the primary index via `get(&pubkey, Some(ancestors), max_root)`
1306        // and find the zero-lamport version
1307        // 2) When the fetch from storage occurs, it will return AccountSharedData::Default
1308        // (as persisted tombstone for snapshots). This will then ultimately be
1309        // filtered out by post-scan filters, like in `get_filtered_spl_token_accounts_by_owner()`.
1310
1311        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    // Same functionally to upsert, but:
1336    // 1. operates on a batch of items
1337    // 2. holds the write lock for the duration of adding the items
1338    // Can save time when inserting lots of new keys.
1339    // But, does NOT update secondary index
1340    // This is designed to be called at startup time.
1341    // returns (insertion_time_us, InsertNewIfMissingIntoPrimaryIndexInfo)
1342    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        // accumulated stats after inserting pubkeys into the index
1354        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        // offset bin processing in the 'binned' array by a random amount.
1359        // This results in calls to insert_new_entry_if_missing_with_lock from different threads starting at different bins to avoid
1360        // lock contention.
1361        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            // Find the smallest index with the same pubkey bin
1375            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 only considers non-duplicate accounts
1391            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                // not using disk buckets, so just write to in-mem
1398                // this is no longer the default case
1399                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    /// use Vec<> because the internal vecs are already allocated per bin
1450    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    /// Updates the given pubkey at the given slot with the new account information.
1468    /// on return, the index's previous account info may be returned in 'reclaims' depending on 'previous_slot_entry_was_cached'
1469    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        // vast majority of updates are to item already in accounts index, so store as raw to avoid unnecessary allocations
1481        let store_raw = true;
1482
1483        // We don't atomically update both primary index and secondary index together.
1484        // This certainly creates a small time window with inconsistent state across the two indexes.
1485        // However, this is acceptable because:
1486        //
1487        //  - A strict consistent view at any given moment of time is not necessary, because the only
1488        //  use case for the secondary index is `scan`, and `scans` are only supported/require consistency
1489        //  on frozen banks, and this inconsistency is only possible on working banks.
1490        //
1491        //  - The secondary index is never consulted as primary source of truth for gets/stores.
1492        //  So, what the accounts_index sees alone is sufficient as a source of truth for other non-scan
1493        //  account operations.
1494        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                // Note that we have a root that is inclusive here.
1558                // Calling a function that expects 'exclusive'
1559                // This is expected behavior for this call.
1560                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    /// return true if pubkey was removed from the accounts index
1572    ///  or does not exist in the accounts index
1573    /// This means it should NOT be unref'd later.
1574    #[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 the slot list is empty, remove the pubkey from `account_maps`. Make sure to grab the
1591        // lock and double check the slot list is still empty, because another writer could have
1592        // locked and inserted the pubkey in-between when `is_slot_list_empty=true` and the call to
1593        // remove() below.
1594        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    /// Clean the slot list by removing all slot_list items older than the max_slot
1602    /// Decrease the reference count of the entry by the number of removed accounts.
1603    /// Returns the slot and account_info of the remaining entry in the slot list
1604    /// Note: This must only be called on startup, and reclaims
1605    /// must be reclaimed.
1606    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            // keep the newest entry, and reclaim all others
1622            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        // Unref
1633        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        // Return the last entry in the slot list, which is the only one
1643        *slot_list
1644            .last()
1645            .expect("Slot list should have at least one entry after cleaning")
1646    }
1647
1648    /// Cleans and unrefs all older rooted entries for each pubkey in the accounts index.
1649    /// Calls passed in callback on the remaining slot entry
1650    /// All pubkeys must be from a single bin
1651    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, // no pubkeys to process, return
1661        };
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    /// When can an entry be purged?
1676    ///
1677    /// If we get a slot update where slot != newest_root_in_slot_list for an account where slot <
1678    /// max_clean_root_exclusive, then we know it's safe to delete because:
1679    ///
1680    /// a) If slot < newest_root_in_slot_list, then we know the update is outdated by a later rooted
1681    /// update, namely the one in newest_root_in_slot_list
1682    ///
1683    /// b) If slot > newest_root_in_slot_list, then because slot < max_clean_root_exclusive and we know there are
1684    /// no roots in the slot list between newest_root_in_slot_list and max_clean_root_exclusive, (otherwise there
1685    /// would be a bigger newest_root_in_slot_list, which is a contradiction), then we know slot must be
1686    /// an unrooted slot less than max_clean_root_exclusive and thus safe to clean as well.
1687    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    /// Given a list of slots, return a new list of only the slots that are rooted
1696    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        // `AccountsDb::flush_accounts_cache()` relies on roots being added in order
1721        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        // 'slot' is a root, so it is both 'root' and 'original'
1728        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    /// Remove the slot when the storage for the slot is freed
1740    /// Accounts no longer reference this slot.
1741    /// return true if slot was a root
1742    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    // These functions/fields are only usable from a dev context (i.e. tests and benches)
1773    #[cfg(feature = "dev-context-only-utils")]
1774    // filter any rooted entries and return them along with a bool that indicates
1775    // if this account has no more entries. Note this does not update the secondary
1776    // indexes!
1777    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            // Check that we're actually testing the correct variant
1830            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            // Check that we're actually testing the correct variant
1840            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            // clone the AccountMapEntryInner into a new Arc
1854            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        // not zero lamports
2041        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            // convert to raw
2085            let (slot2, account_info2) = entry.into();
2086            // recreate using extracted raw
2087            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            // Ensure the pubkey is in the desired bin
2178            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                // account_info type that IS cached
2219                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                // account_info type that is NOT cached
2238                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            // This is an illegal combination when we are using queued lazy inserts.
2293            // Cached items don't ever leave the in-mem cache.
2294            // But the queued lazy insert code relies on there being nothing in the in-mem cache.
2295            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 // in-mem only
2307        };
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                // insert first entry for pubkey. This will use new_entry_after_update and not call update.
2314                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        // verify the added entry matches expected
2337        {
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                // insert second entry for pubkey. This will use update and NOT use new_entry_after_update.
2358                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                // this has the effect of aging out everything in the in-mem cache
2371                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        // There should be reclaims if entries are uncached and old slots are being reclaimed
2386        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            // account_info type that IS cached
2435            test_new_entry_code_paths_helper([1.0, 2.0], true, upsert_method, use_disk);
2436        } else {
2437            // account_info type that is NOT cached
2438            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            // non-cached
2521            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            // reclaimed
2549            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                // since IgnoreReclaims, we should expect reclaims to be empty
2560                UpsertReclaim::IgnoreReclaims,
2561            );
2562            // reclaims is ignored
2563            assert!(reclaims.is_empty());
2564        }
2565        {
2566            // cached
2567            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            // No reclaims, since the entry replaced was cached
2595            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                // since IgnoreReclaims, we should expect reclaims to be empty
2605                UpsertReclaim::IgnoreReclaims,
2606            );
2607            // reclaims is ignored
2608            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        //this behavior might be undefined, clean up should only occur on older slots
2772        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        // Updating index should not purge older roots, only purges
2953        // previous updates within the same slot
2954        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        // No reclaims should be returned on the first item
2997        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        // Cached item should not be reclaimed
3010        assert!(reclaims.is_empty());
3011
3012        // Slot list should only have a single entry
3013        // Using brackets to limit scope of read lock
3014        {
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        // Uncached item should be returned as reclaim
3032        assert!(!reclaims.is_empty());
3033
3034        // Slot list should only have a single entry
3035        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        // No ancestors, no root, should return None
3101        assert!(index.latest_slot(None, &slot_slice, None).is_none());
3102
3103        // Given a root, should return the root
3104        index.add_root(5);
3105        assert_eq!(index.latest_slot(None, &slot_slice, None).unwrap(), 1);
3106
3107        // Given a max_root == root, should still return the root
3108        assert_eq!(index.latest_slot(None, &slot_slice, Some(5)).unwrap(), 1);
3109
3110        // Given a max_root < root, should filter out the root
3111        assert!(index.latest_slot(None, &slot_slice, Some(4)).is_none());
3112
3113        // Given a max_root, should filter out roots < max_root, but specified
3114        // ancestors should not be affected
3115        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        // Given no max_root, should just return the greatest ancestor or root
3130        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        // No roots, should be no reclaims
3155        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        // Insert slots into secondary index
3163        for slot in &slots {
3164            index.upsert(
3165                *slot,
3166                *slot,
3167                &account_key,
3168                // Make sure these accounts are added to secondary index
3169                &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        // Only one top level index entry exists
3184        assert_eq!(secondary_index.index.get(&index_key).unwrap().len(), 1);
3185
3186        // In the reverse index, one account maps across multiple slots
3187        // to the same top level key
3188        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        // Insert multiple older items into the slot list
3221        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        // Insert an item newer than the one that we will reclaim old slots on
3237        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        // Reclaim all older slots
3254        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        // Verify that older items are reclaimed
3266        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        // Verify that the item added is in in the slot list
3273        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        // Verify that the newer item remains in the slot list
3288        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        // Insert an uncached account at slot 0 and an cached account at slot 1
3312        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        // Now insert a cached account at slot 2
3335        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        // Replace the cached account at slot 2 with a uncached account
3347        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        // Verify that the slot list is length two and consists of the cached account at slot 1
3359        // and the uncached account at slot 2
3360        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        // Verify that the uncached account at slot 0 was reclaimed
3383        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        // No roots, should be no reclaims
3417        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        // Add a later root, earlier slots should be reclaimed
3425        slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
3426        index.add_root(1);
3427        // Note 2 is not a root
3428        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        // Add a later root that is not in the list, should not affect the outcome
3435        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        // Pass a max root >= than any root in the slot list, should not affect
3443        // outcome
3444        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        // Pass a max root, earlier slots should be reclaimed
3451        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        // Pass a max root 2. This means the latest root < 2 is 1 because 2 is not a root
3458        // so nothing will be purged
3459        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        // Pass a max root 1. This means the latest root < 3 is 1 because 2 is not a root
3466        // so nothing will be purged
3467        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        // Pass a max root that doesn't exist in the list but is greater than
3474        // some of the roots in the list, shouldn't return those smaller roots
3475        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        // Check secondary index has unique mapping from secondary index key
3490        // to the account key and slot
3491        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        // Check reverse index contains all of the `secondary_index_keys`
3498        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        // Wrong program id
3522        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        // Wrong account data size
3536        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        // Just right. Inserting the same index multiple times should be ok
3552        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        // included
3562        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        // not-excluded
3581        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        // Everything should be deleted
3601        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        // First write one mint index
3661        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        // Now write a different mint index for the same account
3673        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        // Both pubkeys will now be present in the index
3685        check_secondary_index_mapping_correct(
3686            secondary_index,
3687            &[secondary_key1, secondary_key2],
3688            &account_key,
3689        );
3690
3691        // If a later slot also introduces secondary_key1, then it should still exist in the index
3692        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        // If we set a root at `later_slot`, and clean, then even though the account with secondary_key1
3706        // was outdated by the update in the later slot, the primary account key is still alive,
3707        // so both secondary keys will still be kept alive.
3708        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        // Removing the remaining entry for this pubkey in the index should mark the
3720        // pubkey as dead and finally remove all the secondary indexes
3721        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    /// Type that supports caching for tests. Used to test upsert behaviour
3787    /// when the slot list has mixed cached and uncached items.
3788    #[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            // Return self value as whether the item is cached or not
3795            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        // no roots, so always 0
3813        {
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            // no newest root
3856            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            // It is invalid to reclaim older slots if the slot being upserted
3880            // is unrooted
3881            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        // check refcount BEFORE the unref
3916        assert_eq!(entry.ref_count(), 1);
3917        // first time, ref count was at 1, we can unref once. Unref should return 1.
3918        assert_eq!(entry.unref(), 1);
3919        // check refcount AFTER the unref
3920        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        // make ref count be zero
3935        assert_eq!(entry.unref(), 1);
3936        assert_eq!(entry.ref_count(), 0);
3937
3938        // unref when already at zero should panic
3939        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        // return true if we don't know anything about 'key_unknown'
3953        // the item did not exist in the accounts index at all, so index is up to date
3954        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        // none for max root because we don't want to delete the entry yet
3960        assert!(!index.clean_rooted_entries(&key, &mut gc, None));
3961        // this is because of inclusive vs exclusive in the call to can_purge_older_entries
3962        assert!(!index.clean_rooted_entries(&key, &mut gc, Some(slot1)));
3963        // this will delete the entry because it is <= max_root_inclusive and NOT a root
3964        // note this has to be slot2 because of inclusive vs exclusive in the call to can_purge_older_entries
3965        {
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        // re-add it
3972        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); // no range, so 0
4061        assert_eq!(end, BINS_FOR_TESTING - 1); // no range, so last bin
4062
4063        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); // start at pubkey 0, so 0
4067        assert_eq!(end, 0); // end at pubkey 0, so 0
4068
4069        let range = (Included(key), Excluded(key));
4070        let (start, end) = index.bin_start_end_inclusive(&range);
4071        assert_eq!(start, 0); // start at pubkey 0, so 0
4072        assert_eq!(end, 0); // end at pubkey 0, so 0
4073
4074        let range = (Excluded(key), Excluded(key));
4075        let (start, end) = index.bin_start_end_inclusive(&range);
4076        assert_eq!(start, 0); // start at pubkey 0, so 0
4077        assert_eq!(end, 0); // end at pubkey 0, so 0
4078
4079        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); // start at highest possible pubkey, so bins - 1
4084        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); // start at highest possible pubkey, so bins - 1
4089        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); // Exclude the highest possible pubkey, so start should be "bins"
4094        assert_eq!(end, bins - 1); // End should be the last bin index
4095    }
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()); // not allocated
4112            assert!(!config.is_aborted());
4113            config.abort(); // has no effect
4114            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}