solana_accounts_db/
obsolete_accounts.rs

1use {crate::account_info::Offset, solana_clock::Slot};
2
3#[derive(Debug, Clone, PartialEq)]
4pub struct ObsoleteAccountItem {
5    /// Offset of the account in the account storage entry
6    pub offset: Offset,
7    /// Length of the account data
8    pub data_len: usize,
9    /// Slot when the account was marked obsolete
10    pub slot: Slot,
11}
12
13#[derive(Debug, Clone, PartialEq, Default)]
14pub struct ObsoleteAccounts {
15    pub accounts: Vec<ObsoleteAccountItem>,
16}
17
18impl ObsoleteAccounts {
19    /// Marks the accounts at the given offsets as obsolete
20    pub fn mark_accounts_obsolete(
21        &mut self,
22        newly_obsolete_accounts: impl ExactSizeIterator<Item = (Offset, usize)>,
23        slot: Slot,
24    ) {
25        self.accounts.reserve(newly_obsolete_accounts.len());
26
27        for (offset, data_len) in newly_obsolete_accounts {
28            self.accounts.push(ObsoleteAccountItem {
29                offset,
30                data_len,
31                slot,
32            });
33        }
34    }
35
36    /// Returns the accounts that were marked obsolete as of the passed in slot
37    /// or earlier. If slot is None, then slot will be assumed to be the max root
38    /// and all obsolete accounts will be returned.
39    pub fn filter_obsolete_accounts(
40        &self,
41        slot: Option<Slot>,
42    ) -> impl Iterator<Item = (Offset, usize)> + '_ {
43        self.accounts
44            .iter()
45            .filter(move |obsolete_account| slot.is_none_or(|s| obsolete_account.slot <= s))
46            .map(|obsolete_account| (obsolete_account.offset, obsolete_account.data_len))
47    }
48
49    /// Returns the accounts that were marked obsolete as of the passed in slot
50    /// or earlier. Returned data includes the slots that the accounts were marked
51    /// obsolete at
52    pub fn obsolete_accounts_for_snapshots(&self, slot: Slot) -> ObsoleteAccounts {
53        let filtered_accounts = self
54            .accounts
55            .iter()
56            .filter(|account| account.slot <= slot)
57            .cloned()
58            .collect();
59
60        ObsoleteAccounts {
61            accounts: filtered_accounts,
62        }
63    }
64}
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn test_mark_accounts_obsolete() {
71        let mut obsolete_accounts = ObsoleteAccounts::default();
72        let new_accounts = vec![(10, 100), (20, 200), (30, 300)];
73        let slot: Slot = 42;
74
75        obsolete_accounts.mark_accounts_obsolete(new_accounts.into_iter(), slot);
76
77        let expected_accounts = vec![(10, 100), (20, 200), (30, 300)];
78
79        let actual_accounts: Vec<_> = obsolete_accounts
80            .accounts
81            .iter()
82            .map(|item| (item.offset, item.data_len))
83            .collect();
84
85        assert_eq!(actual_accounts, expected_accounts);
86    }
87
88    #[test]
89    fn test_filter_obsolete_accounts() {
90        let mut obsolete_accounts = ObsoleteAccounts::default();
91        let new_accounts = vec![(10, 100, 40), (20, 200, 42), (30, 300, 44)]
92            .into_iter()
93            .map(|(offset, data_len, slot)| ObsoleteAccountItem {
94                offset,
95                data_len,
96                slot,
97            })
98            .collect::<Vec<_>>();
99
100        // Mark accounts obsolete with different slots
101        new_accounts.into_iter().for_each(|item| {
102            obsolete_accounts
103                .mark_accounts_obsolete([(item.offset, item.data_len)].into_iter(), item.slot)
104        });
105
106        // Filter accounts obsolete as of slot 42
107        let filtered_accounts: Vec<_> = obsolete_accounts
108            .filter_obsolete_accounts(Some(42))
109            .collect();
110
111        assert_eq!(filtered_accounts, vec![(10, 100), (20, 200)]);
112
113        // Filter accounts obsolete passing in no slot (i.e., all obsolete accounts)
114        let filtered_accounts: Vec<_> = obsolete_accounts.filter_obsolete_accounts(None).collect();
115
116        assert_eq!(filtered_accounts, vec![(10, 100), (20, 200), (30, 300)]);
117    }
118
119    #[test]
120    fn test_obsolete_accounts_for_snapshots() {
121        let mut obsolete_accounts = ObsoleteAccounts::default();
122        let new_accounts = vec![(10, 100, 40), (20, 200, 42), (30, 300, 44)]
123            .into_iter()
124            .map(|(offset, data_len, slot)| ObsoleteAccountItem {
125                offset,
126                data_len,
127                slot,
128            })
129            .collect::<Vec<_>>();
130
131        // Mark accounts obsolete with different slots
132        new_accounts.iter().for_each(|item| {
133            obsolete_accounts
134                .mark_accounts_obsolete([(item.offset, item.data_len)].into_iter(), item.slot)
135        });
136
137        // Filter accounts obsolete as of slot 42
138        let obsolete_accounts_for_snapshots = obsolete_accounts.obsolete_accounts_for_snapshots(42);
139
140        let expected_accounts: Vec<_> = new_accounts
141            .iter()
142            .filter(|account| account.slot <= 42)
143            .cloned()
144            .collect();
145
146        assert_eq!(obsolete_accounts_for_snapshots.accounts, expected_accounts);
147
148        // Filter accounts obsolete passing in no slot (i.e., all obsolete accounts)
149        let obsolete_accounts_for_snapshots =
150            obsolete_accounts.obsolete_accounts_for_snapshots(100);
151
152        assert_eq!(obsolete_accounts_for_snapshots.accounts, new_accounts);
153    }
154}