solana_accounts_db/
account_storage.rs

1//! Manage the map of slot -> append vec
2
3use {
4    crate::accounts_db::{AccountStorageEntry, AccountsFileId},
5    dashmap::DashMap,
6    solana_sdk::clock::Slot,
7    std::sync::Arc,
8};
9
10pub mod meta;
11
12#[derive(Clone, Debug)]
13pub struct AccountStorageReference {
14    /// the single storage for a given slot
15    pub storage: Arc<AccountStorageEntry>,
16    /// id can be read from 'storage', but it is an atomic read.
17    /// id will never change while a storage is held, so we store it separately here for faster runtime lookup in 'get_account_storage_entry'
18    pub id: AccountsFileId,
19}
20
21pub type AccountStorageMap = DashMap<Slot, AccountStorageReference>;
22
23#[derive(Default, Debug)]
24pub struct AccountStorage {
25    /// map from Slot -> the single append vec for the slot
26    map: AccountStorageMap,
27    /// while shrink is operating on a slot, there can be 2 append vecs active for that slot
28    /// Once the index has been updated to only refer to the new append vec, the single entry for the slot in 'map' can be updated.
29    /// Entries in 'shrink_in_progress_map' can be found by 'get_account_storage_entry'
30    shrink_in_progress_map: DashMap<Slot, Arc<AccountStorageEntry>>,
31}
32
33impl AccountStorage {
34    /// Return the append vec in 'slot' and with id='store_id'.
35    /// can look in 'map' and 'shrink_in_progress_map' to find the specified append vec
36    /// when shrinking begins, shrinking_in_progress is called.
37    /// This fn looks in 'map' first, then in 'shrink_in_progress_map', then in 'map' again because
38    /// 'shrinking_in_progress' first inserts the new append vec into 'shrink_in_progress_map'
39    /// Then, when 'shrink_in_progress' is dropped,
40    /// the old append vec is replaced in 'map' with the new append vec
41    /// then the new append vec is dropped from 'shrink_in_progress_map'.
42    /// So, it is possible for a race with this fn and dropping 'shrink_in_progress'.
43    /// Callers to this function have 2 choices:
44    /// 1. hold the account index read lock for the pubkey so that the account index entry cannot be changed prior to or during this call. (scans do this)
45    /// 2. expect to be ready to start over and read the index again if this function returns None
46    ///
47    /// Operations like shrinking or write cache flushing may have updated the index between when the caller read the index and called this function to
48    /// load from the append vec specified in the index.
49    ///
50    /// In practice, this fn will return the entry from the map in the very first lookup unless a shrink is in progress.
51    /// The third lookup will only be called if a requesting thread exactly interposes itself between the 2 map manipulations in the drop of 'shrink_in_progress'.
52    pub(crate) fn get_account_storage_entry(
53        &self,
54        slot: Slot,
55        store_id: AccountsFileId,
56    ) -> Option<Arc<AccountStorageEntry>> {
57        let lookup_in_map = || {
58            self.map
59                .get(&slot)
60                .and_then(|r| (r.id == store_id).then_some(Arc::clone(&r.storage)))
61        };
62
63        lookup_in_map()
64            .or_else(|| {
65                self.shrink_in_progress_map.get(&slot).and_then(|entry| {
66                    (entry.value().id() == store_id).then(|| Arc::clone(entry.value()))
67                })
68            })
69            .or_else(lookup_in_map)
70    }
71
72    /// returns true if shrink in progress is NOT active
73    pub(crate) fn no_shrink_in_progress(&self) -> bool {
74        self.shrink_in_progress_map.is_empty()
75    }
76
77    /// return the append vec for 'slot' if it exists
78    /// This is only ever called when shrink is not possibly running and there is a max of 1 append vec per slot.
79    pub fn get_slot_storage_entry(&self, slot: Slot) -> Option<Arc<AccountStorageEntry>> {
80        assert!(
81            self.no_shrink_in_progress(),
82            "self.no_shrink_in_progress(): {slot}"
83        );
84        self.get_slot_storage_entry_shrinking_in_progress_ok(slot)
85    }
86
87    pub(crate) fn replace_storage_with_equivalent(
88        &self,
89        slot: Slot,
90        storage: Arc<AccountStorageEntry>,
91    ) {
92        assert_eq!(storage.slot(), slot);
93        if let Some(mut existing_storage) = self.map.get_mut(&slot) {
94            assert_eq!(slot, existing_storage.value().storage.slot());
95            existing_storage.value_mut().storage = storage;
96        }
97    }
98
99    /// return the append vec for 'slot' if it exists
100    pub(crate) fn get_slot_storage_entry_shrinking_in_progress_ok(
101        &self,
102        slot: Slot,
103    ) -> Option<Arc<AccountStorageEntry>> {
104        self.map.get(&slot).map(|entry| Arc::clone(&entry.storage))
105    }
106
107    pub(crate) fn all_slots(&self) -> Vec<Slot> {
108        assert!(self.no_shrink_in_progress());
109        self.map.iter().map(|iter_item| *iter_item.key()).collect()
110    }
111
112    /// returns true if there is no entry for 'slot'
113    #[cfg(test)]
114    pub(crate) fn is_empty_entry(&self, slot: Slot) -> bool {
115        assert!(
116            self.no_shrink_in_progress(),
117            "self.no_shrink_in_progress(): {slot}"
118        );
119        self.map.get(&slot).is_none()
120    }
121
122    /// initialize the storage map to 'all_storages'
123    pub fn initialize(&mut self, all_storages: AccountStorageMap) {
124        assert!(self.map.is_empty());
125        assert!(self.no_shrink_in_progress());
126        self.map.extend(all_storages)
127    }
128
129    /// remove the append vec at 'slot'
130    /// returns the current contents
131    pub(crate) fn remove(
132        &self,
133        slot: &Slot,
134        shrink_can_be_active: bool,
135    ) -> Option<Arc<AccountStorageEntry>> {
136        assert!(shrink_can_be_active || self.shrink_in_progress_map.is_empty());
137        self.map.remove(slot).map(|(_, entry)| entry.storage)
138    }
139
140    /// iterate through all (slot, append-vec)
141    pub(crate) fn iter(&self) -> AccountStorageIter<'_> {
142        assert!(self.no_shrink_in_progress());
143        AccountStorageIter::new(self)
144    }
145
146    pub(crate) fn insert(&self, slot: Slot, store: Arc<AccountStorageEntry>) {
147        assert!(
148            self.no_shrink_in_progress(),
149            "self.no_shrink_in_progress(): {slot}"
150        );
151        assert!(self
152            .map
153            .insert(
154                slot,
155                AccountStorageReference {
156                    id: store.id(),
157                    storage: store,
158                }
159            )
160            .is_none());
161    }
162
163    /// called when shrinking begins on a slot and append vec.
164    /// When 'ShrinkInProgress' is dropped by caller, the old store will be replaced with 'new_store' in the storage map.
165    /// Fails if there are no existing stores at the slot.
166    /// 'new_store' will be replacing the current store at 'slot' in 'map'
167    /// So, insert 'new_store' into 'shrink_in_progress_map'.
168    /// This allows tx processing loads to find the items in 'shrink_in_progress_map' after the index is updated and item is now located in 'new_store'.
169    pub(crate) fn shrinking_in_progress(
170        &self,
171        slot: Slot,
172        new_store: Arc<AccountStorageEntry>,
173    ) -> ShrinkInProgress<'_> {
174        let shrinking_store = Arc::clone(
175            &self
176                .map
177                .get(&slot)
178                .expect("no pre-existing storage for shrinking slot")
179                .value()
180                .storage,
181        );
182
183        // insert 'new_store' into 'shrink_in_progress_map'
184        assert!(
185            self.shrink_in_progress_map
186                .insert(slot, Arc::clone(&new_store))
187                .is_none(),
188            "duplicate call"
189        );
190
191        ShrinkInProgress {
192            storage: self,
193            slot,
194            new_store,
195            old_store: shrinking_store,
196        }
197    }
198
199    #[cfg(test)]
200    pub(crate) fn len(&self) -> usize {
201        self.map.len()
202    }
203
204    /// Returns the (slot, storage) tuples where `predicate` returns `true`
205    ///
206    /// This function is useful when not all storages are desired,
207    /// as storages are only Arc::cloned if they pass the predicate.
208    ///
209    /// # Panics
210    ///
211    /// Panics if `shrink` is in progress.
212    pub fn get_if(
213        &self,
214        predicate: impl Fn(&Slot, &AccountStorageEntry) -> bool,
215    ) -> Box<[(Slot, Arc<AccountStorageEntry>)]> {
216        assert!(self.no_shrink_in_progress());
217        self.map
218            .iter()
219            .filter_map(|entry| {
220                let slot = entry.key();
221                let storage = &entry.value().storage;
222                predicate(slot, storage).then(|| (*slot, Arc::clone(storage)))
223            })
224            .collect()
225    }
226}
227
228/// iterate contents of AccountStorage without exposing internals
229pub struct AccountStorageIter<'a> {
230    iter: dashmap::iter::Iter<'a, Slot, AccountStorageReference>,
231}
232
233impl<'a> AccountStorageIter<'a> {
234    pub fn new(storage: &'a AccountStorage) -> Self {
235        Self {
236            iter: storage.map.iter(),
237        }
238    }
239}
240
241impl<'a> Iterator for AccountStorageIter<'a> {
242    type Item = (Slot, Arc<AccountStorageEntry>);
243
244    fn next(&mut self) -> Option<Self::Item> {
245        if let Some(entry) = self.iter.next() {
246            let slot = entry.key();
247            let store = entry.value();
248            return Some((*slot, Arc::clone(&store.storage)));
249        }
250        None
251    }
252}
253
254/// exists while there is a shrink in progress
255/// keeps track of the 'new_store' being created and the 'old_store' being replaced.
256#[derive(Debug)]
257pub struct ShrinkInProgress<'a> {
258    storage: &'a AccountStorage,
259    /// old store which will be shrunk and replaced
260    old_store: Arc<AccountStorageEntry>,
261    /// newly shrunk store with a subset of contents from 'old_store'
262    new_store: Arc<AccountStorageEntry>,
263    slot: Slot,
264}
265
266/// called when the shrink is no longer in progress. This means we can release the old append vec and update the map of slot -> append vec
267impl<'a> Drop for ShrinkInProgress<'a> {
268    fn drop(&mut self) {
269        assert_eq!(
270            self.storage
271                .map
272                .insert(
273                    self.slot,
274                    AccountStorageReference {
275                        storage: Arc::clone(&self.new_store),
276                        id: self.new_store.id()
277                    }
278                )
279                .map(|store| store.id),
280            Some(self.old_store.id())
281        );
282
283        // The new store can be removed from 'shrink_in_progress_map'
284        assert!(self
285            .storage
286            .shrink_in_progress_map
287            .remove(&self.slot)
288            .is_some());
289    }
290}
291
292impl<'a> ShrinkInProgress<'a> {
293    pub fn new_storage(&self) -> &Arc<AccountStorageEntry> {
294        &self.new_store
295    }
296    pub(crate) fn old_storage(&self) -> &Arc<AccountStorageEntry> {
297        &self.old_store
298    }
299}
300
301#[cfg_attr(feature = "frozen-abi", derive(AbiExample, AbiEnumVisitor))]
302#[derive(Debug, Eq, PartialEq, Copy, Clone, Deserialize, Serialize)]
303pub enum AccountStorageStatus {
304    Available = 0,
305    Full = 1,
306    Candidate = 2,
307}
308
309impl Default for AccountStorageStatus {
310    fn default() -> Self {
311        Self::Available
312    }
313}
314
315#[cfg(test)]
316pub(crate) mod tests {
317    use {
318        super::*,
319        crate::accounts_file::AccountsFileProvider,
320        std::{iter, path::Path},
321    };
322
323    #[test]
324    fn test_shrink_in_progress() {
325        // test that we check in order map then shrink_in_progress_map
326        let storage = AccountStorage::default();
327        let slot = 0;
328        let id = 0;
329        // empty everything
330        assert!(storage.get_account_storage_entry(slot, id).is_none());
331
332        // add a map store
333        let common_store_path = Path::new("");
334        let store_file_size = 4000;
335        let store_file_size2 = store_file_size * 2;
336        // 2 append vecs with same id, but different sizes
337        let entry = Arc::new(AccountStorageEntry::new(
338            common_store_path,
339            slot,
340            id,
341            store_file_size,
342            AccountsFileProvider::AppendVec,
343        ));
344        let entry2 = Arc::new(AccountStorageEntry::new(
345            common_store_path,
346            slot,
347            id,
348            store_file_size2,
349            AccountsFileProvider::AppendVec,
350        ));
351        storage
352            .map
353            .insert(slot, AccountStorageReference { id, storage: entry });
354
355        // look in map
356        assert_eq!(
357            store_file_size,
358            storage
359                .get_account_storage_entry(slot, id)
360                .map(|entry| entry.accounts.capacity())
361                .unwrap_or_default()
362        );
363
364        // look in shrink_in_progress_map
365        storage.shrink_in_progress_map.insert(slot, entry2);
366
367        // look in map
368        assert_eq!(
369            store_file_size,
370            storage
371                .get_account_storage_entry(slot, id)
372                .map(|entry| entry.accounts.capacity())
373                .unwrap_or_default()
374        );
375
376        // remove from map
377        storage.map.remove(&slot).unwrap();
378
379        // look in shrink_in_progress_map
380        assert_eq!(
381            store_file_size2,
382            storage
383                .get_account_storage_entry(slot, id)
384                .map(|entry| entry.accounts.capacity())
385                .unwrap_or_default()
386        );
387    }
388
389    impl AccountStorage {
390        fn get_test_storage_with_id(&self, id: AccountsFileId) -> Arc<AccountStorageEntry> {
391            let slot = 0;
392            // add a map store
393            let common_store_path = Path::new("");
394            let store_file_size = 4000;
395            Arc::new(AccountStorageEntry::new(
396                common_store_path,
397                slot,
398                id,
399                store_file_size,
400                AccountsFileProvider::AppendVec,
401            ))
402        }
403        fn get_test_storage(&self) -> Arc<AccountStorageEntry> {
404            self.get_test_storage_with_id(0)
405        }
406    }
407
408    #[test]
409    #[should_panic(expected = "self.no_shrink_in_progress()")]
410    fn test_get_slot_storage_entry_fail() {
411        let storage = AccountStorage::default();
412        storage
413            .shrink_in_progress_map
414            .insert(0, storage.get_test_storage());
415        storage.get_slot_storage_entry(0);
416    }
417
418    #[test]
419    #[should_panic(expected = "self.no_shrink_in_progress()")]
420    fn test_all_slots_fail() {
421        let storage = AccountStorage::default();
422        storage
423            .shrink_in_progress_map
424            .insert(0, storage.get_test_storage());
425        storage.all_slots();
426    }
427
428    #[test]
429    #[should_panic(expected = "self.no_shrink_in_progress()")]
430    fn test_initialize_fail() {
431        let mut storage = AccountStorage::default();
432        storage
433            .shrink_in_progress_map
434            .insert(0, storage.get_test_storage());
435        storage.initialize(AccountStorageMap::default());
436    }
437
438    #[test]
439    #[should_panic(expected = "shrink_can_be_active || self.shrink_in_progress_map.is_empty()")]
440    fn test_remove_fail() {
441        let storage = AccountStorage::default();
442        storage
443            .shrink_in_progress_map
444            .insert(0, storage.get_test_storage());
445        storage.remove(&0, false);
446    }
447
448    #[test]
449    #[should_panic(expected = "self.no_shrink_in_progress()")]
450    fn test_iter_fail() {
451        let storage = AccountStorage::default();
452        storage
453            .shrink_in_progress_map
454            .insert(0, storage.get_test_storage());
455        storage.iter();
456    }
457
458    #[test]
459    #[should_panic(expected = "self.no_shrink_in_progress()")]
460    fn test_insert_fail() {
461        let storage = AccountStorage::default();
462        let sample = storage.get_test_storage();
463        storage.shrink_in_progress_map.insert(0, sample.clone());
464        storage.insert(0, sample);
465    }
466
467    #[test]
468    #[should_panic(expected = "duplicate call")]
469    fn test_shrinking_in_progress_fail3() {
470        // already entry in shrink_in_progress_map
471        let storage = AccountStorage::default();
472        let sample = storage.get_test_storage();
473        storage.map.insert(
474            0,
475            AccountStorageReference {
476                id: 0,
477                storage: sample.clone(),
478            },
479        );
480        storage.shrink_in_progress_map.insert(0, sample.clone());
481        storage.shrinking_in_progress(0, sample);
482    }
483
484    #[test]
485    #[should_panic(expected = "duplicate call")]
486    fn test_shrinking_in_progress_fail4() {
487        // already called 'shrink_in_progress' on this slot and it is still active
488        let storage = AccountStorage::default();
489        let sample_to_shrink = storage.get_test_storage();
490        let sample = storage.get_test_storage();
491        storage.map.insert(
492            0,
493            AccountStorageReference {
494                id: 0,
495                storage: sample_to_shrink,
496            },
497        );
498        let _shrinking_in_progress = storage.shrinking_in_progress(0, sample.clone());
499        storage.shrinking_in_progress(0, sample);
500    }
501
502    #[test]
503    fn test_shrinking_in_progress_second_call() {
504        // already called 'shrink_in_progress' on this slot, but it finished, so we succeed
505        // verify data structures during and after shrink and then with subsequent shrink call
506        let storage = AccountStorage::default();
507        let slot = 0;
508        let id_to_shrink = 1;
509        let id_shrunk = 0;
510        let sample_to_shrink = storage.get_test_storage_with_id(id_to_shrink);
511        let sample = storage.get_test_storage();
512        storage.map.insert(
513            slot,
514            AccountStorageReference {
515                id: id_to_shrink,
516                storage: sample_to_shrink,
517            },
518        );
519        let shrinking_in_progress = storage.shrinking_in_progress(slot, sample.clone());
520        assert!(storage.map.contains_key(&slot));
521        assert_eq!(id_to_shrink, storage.map.get(&slot).unwrap().storage.id());
522        assert_eq!(
523            (slot, id_shrunk),
524            storage
525                .shrink_in_progress_map
526                .iter()
527                .next()
528                .map(|r| (*r.key(), r.value().id()))
529                .unwrap()
530        );
531        drop(shrinking_in_progress);
532        assert!(storage.map.contains_key(&slot));
533        assert_eq!(id_shrunk, storage.map.get(&slot).unwrap().storage.id());
534        assert!(storage.shrink_in_progress_map.is_empty());
535        storage.shrinking_in_progress(slot, sample);
536    }
537
538    #[test]
539    #[should_panic(expected = "no pre-existing storage for shrinking slot")]
540    fn test_shrinking_in_progress_fail1() {
541        // nothing in slot currently
542        let storage = AccountStorage::default();
543        let sample = storage.get_test_storage();
544        storage.shrinking_in_progress(0, sample);
545    }
546
547    #[test]
548    #[should_panic(expected = "no pre-existing storage for shrinking slot")]
549    fn test_shrinking_in_progress_fail2() {
550        // nothing in slot currently, but there is an empty map entry
551        let storage = AccountStorage::default();
552        let sample = storage.get_test_storage();
553        storage.shrinking_in_progress(0, sample);
554    }
555
556    #[test]
557    fn test_missing() {
558        // already called 'shrink_in_progress' on this slot, but it finished, so we succeed
559        // verify data structures during and after shrink and then with subsequent shrink call
560        let storage = AccountStorage::default();
561        let sample = storage.get_test_storage();
562        let id = sample.id();
563        let missing_id = 9999;
564        let slot = sample.slot();
565        // id is missing since not in maps at all
566        assert!(storage.get_account_storage_entry(slot, id).is_none());
567        // missing should always be missing
568        assert!(storage
569            .get_account_storage_entry(slot, missing_id)
570            .is_none());
571        storage.map.insert(
572            slot,
573            AccountStorageReference {
574                id,
575                storage: sample.clone(),
576            },
577        );
578        // id is found in map
579        assert!(storage.get_account_storage_entry(slot, id).is_some());
580        assert!(storage
581            .get_account_storage_entry(slot, missing_id)
582            .is_none());
583        storage
584            .shrink_in_progress_map
585            .insert(slot, Arc::clone(&sample));
586        // id is found in map
587        assert!(storage
588            .get_account_storage_entry(slot, missing_id)
589            .is_none());
590        assert!(storage.get_account_storage_entry(slot, id).is_some());
591        storage.map.remove(&slot);
592        // id is found in shrink_in_progress_map
593        assert!(storage
594            .get_account_storage_entry(slot, missing_id)
595            .is_none());
596        assert!(storage.get_account_storage_entry(slot, id).is_some());
597    }
598
599    #[test]
600    fn test_get_if() {
601        let storage = AccountStorage::default();
602        assert!(storage.get_if(|_, _| true).is_empty());
603
604        // add some entries
605        let ids = [123, 456, 789];
606        for id in ids {
607            let slot = id as Slot;
608            let entry = AccountStorageEntry::new(
609                Path::new(""),
610                slot,
611                id,
612                5000,
613                AccountsFileProvider::AppendVec,
614            );
615            storage.map.insert(
616                slot,
617                AccountStorageReference {
618                    id,
619                    storage: entry.into(),
620                },
621            );
622        }
623
624        // look 'em up
625        for id in ids {
626            let found = storage.get_if(|slot, _| *slot == id as Slot);
627            assert!(found
628                .iter()
629                .map(|(slot, _)| *slot)
630                .eq(iter::once(id as Slot)));
631        }
632
633        assert!(storage.get_if(|_, _| false).is_empty());
634        assert_eq!(storage.get_if(|_, _| true).len(), ids.len());
635    }
636
637    #[test]
638    #[should_panic(expected = "self.no_shrink_in_progress()")]
639    fn test_get_if_fail() {
640        let storage = AccountStorage::default();
641        storage
642            .shrink_in_progress_map
643            .insert(0, storage.get_test_storage());
644        storage.get_if(|_, _| true);
645    }
646}