solana_runtime/
root_bank_cache.rs

1//! A wrapper around a root `Bank` that only loads from bank forks if the root has been updated.
2//! This can be useful to avoid read-locking the bank forks until the root has been updated.
3//!
4
5use {
6    crate::{
7        bank::Bank,
8        bank_forks::{BankForks, ReadOnlyAtomicSlot},
9    },
10    std::sync::{Arc, RwLock, Weak},
11};
12
13/// Cached root bank that only loads from bank forks if the root has been updated.
14#[derive(Clone)]
15pub struct RootBankCache {
16    bank_forks: Arc<RwLock<BankForks>>,
17    cached_root_bank: Weak<Bank>,
18    root_slot: ReadOnlyAtomicSlot,
19}
20
21impl RootBankCache {
22    pub fn new(bank_forks: Arc<RwLock<BankForks>>) -> Self {
23        let (cached_root_bank, root_slot) = {
24            let lock = bank_forks.read().unwrap();
25            (Arc::downgrade(&lock.root_bank()), lock.get_atomic_root())
26        };
27        Self {
28            bank_forks,
29            cached_root_bank,
30            root_slot,
31        }
32    }
33
34    pub fn root_bank(&mut self) -> Arc<Bank> {
35        match self.cached_root_bank.upgrade() {
36            Some(cached_root_bank) if cached_root_bank.slot() == self.root_slot.get() => {
37                cached_root_bank
38            }
39            _ => {
40                let root_bank = self.bank_forks.read().unwrap().root_bank();
41                self.cached_root_bank = Arc::downgrade(&root_bank);
42                root_bank
43            }
44        }
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use {
51        super::*,
52        crate::{
53            accounts_background_service::AbsRequestSender,
54            bank_forks::BankForks,
55            genesis_utils::{create_genesis_config, GenesisConfigInfo},
56        },
57        solana_pubkey::Pubkey,
58    };
59
60    #[test]
61    fn test_root_bank_cache() {
62        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
63        let bank = Bank::new_for_tests(&genesis_config);
64        let bank_forks = BankForks::new_rw_arc(bank);
65
66        let mut root_bank_cache = RootBankCache::new(bank_forks.clone());
67
68        let bank = bank_forks.read().unwrap().root_bank();
69        assert_eq!(bank, root_bank_cache.root_bank());
70
71        {
72            let child_bank = Bank::new_from_parent(bank.clone(), &Pubkey::default(), 1);
73            bank_forks.write().unwrap().insert(child_bank);
74
75            // cached slot is still 0 since we have not set root
76            let cached_root_bank = root_bank_cache.cached_root_bank.upgrade().unwrap();
77            assert_eq!(bank.slot(), cached_root_bank.slot());
78        }
79        {
80            bank_forks
81                .write()
82                .unwrap()
83                .set_root(1, &AbsRequestSender::default(), None)
84                .unwrap();
85            let bank = bank_forks.read().unwrap().root_bank();
86
87            // cached slot and bank are not updated until we call `root_bank()`
88            let cached_root_bank = root_bank_cache.cached_root_bank.upgrade().unwrap();
89            assert!(bank.slot() != cached_root_bank.slot());
90            assert!(bank != cached_root_bank);
91            assert_eq!(bank, root_bank_cache.root_bank());
92
93            // cached slot and bank are updated
94            let cached_root_bank = root_bank_cache.cached_root_bank.upgrade().unwrap();
95            assert_eq!(bank.slot(), cached_root_bank.slot());
96            assert_eq!(bank, cached_root_bank);
97            assert_eq!(bank, root_bank_cache.root_bank());
98        }
99    }
100}