use {
crate::accounts_db::{AccountStorageEntry, AccountsFileId},
dashmap::DashMap,
solana_sdk::clock::Slot,
std::sync::Arc,
};
pub mod meta;
#[derive(Clone, Debug)]
pub struct AccountStorageReference {
pub storage: Arc<AccountStorageEntry>,
pub id: AccountsFileId,
}
pub type AccountStorageMap = DashMap<Slot, AccountStorageReference>;
#[derive(Default, Debug)]
pub struct AccountStorage {
map: AccountStorageMap,
shrink_in_progress_map: DashMap<Slot, Arc<AccountStorageEntry>>,
}
impl AccountStorage {
pub(crate) fn get_account_storage_entry(
&self,
slot: Slot,
store_id: AccountsFileId,
) -> Option<Arc<AccountStorageEntry>> {
let lookup_in_map = || {
self.map
.get(&slot)
.and_then(|r| (r.id == store_id).then_some(Arc::clone(&r.storage)))
};
lookup_in_map()
.or_else(|| {
self.shrink_in_progress_map.get(&slot).and_then(|entry| {
(entry.value().id() == store_id).then(|| Arc::clone(entry.value()))
})
})
.or_else(lookup_in_map)
}
pub(crate) fn no_shrink_in_progress(&self) -> bool {
self.shrink_in_progress_map.is_empty()
}
pub fn get_slot_storage_entry(&self, slot: Slot) -> Option<Arc<AccountStorageEntry>> {
assert!(
self.no_shrink_in_progress(),
"self.no_shrink_in_progress(): {slot}"
);
self.get_slot_storage_entry_shrinking_in_progress_ok(slot)
}
pub(crate) fn replace_storage_with_equivalent(
&self,
slot: Slot,
storage: Arc<AccountStorageEntry>,
) {
assert_eq!(storage.slot(), slot);
if let Some(mut existing_storage) = self.map.get_mut(&slot) {
assert_eq!(slot, existing_storage.value().storage.slot());
existing_storage.value_mut().storage = storage;
}
}
pub(crate) fn get_slot_storage_entry_shrinking_in_progress_ok(
&self,
slot: Slot,
) -> Option<Arc<AccountStorageEntry>> {
self.map.get(&slot).map(|entry| Arc::clone(&entry.storage))
}
pub(crate) fn all_slots(&self) -> Vec<Slot> {
assert!(self.no_shrink_in_progress());
self.map.iter().map(|iter_item| *iter_item.key()).collect()
}
#[cfg(test)]
pub(crate) fn is_empty_entry(&self, slot: Slot) -> bool {
assert!(
self.no_shrink_in_progress(),
"self.no_shrink_in_progress(): {slot}"
);
self.map.get(&slot).is_none()
}
pub fn initialize(&mut self, all_storages: AccountStorageMap) {
assert!(self.map.is_empty());
assert!(self.no_shrink_in_progress());
self.map.extend(all_storages)
}
pub(crate) fn remove(
&self,
slot: &Slot,
shrink_can_be_active: bool,
) -> Option<Arc<AccountStorageEntry>> {
assert!(shrink_can_be_active || self.shrink_in_progress_map.is_empty());
self.map.remove(slot).map(|(_, entry)| entry.storage)
}
pub(crate) fn iter(&self) -> AccountStorageIter<'_> {
assert!(self.no_shrink_in_progress());
AccountStorageIter::new(self)
}
pub(crate) fn insert(&self, slot: Slot, store: Arc<AccountStorageEntry>) {
assert!(
self.no_shrink_in_progress(),
"self.no_shrink_in_progress(): {slot}"
);
assert!(self
.map
.insert(
slot,
AccountStorageReference {
id: store.id(),
storage: store,
}
)
.is_none());
}
pub(crate) fn shrinking_in_progress(
&self,
slot: Slot,
new_store: Arc<AccountStorageEntry>,
) -> ShrinkInProgress<'_> {
let shrinking_store = Arc::clone(
&self
.map
.get(&slot)
.expect("no pre-existing storage for shrinking slot")
.value()
.storage,
);
assert!(
self.shrink_in_progress_map
.insert(slot, Arc::clone(&new_store))
.is_none(),
"duplicate call"
);
ShrinkInProgress {
storage: self,
slot,
new_store,
old_store: shrinking_store,
}
}
#[cfg(test)]
pub(crate) fn len(&self) -> usize {
self.map.len()
}
}
pub struct AccountStorageIter<'a> {
iter: dashmap::iter::Iter<'a, Slot, AccountStorageReference>,
}
impl<'a> AccountStorageIter<'a> {
pub fn new(storage: &'a AccountStorage) -> Self {
Self {
iter: storage.map.iter(),
}
}
}
impl<'a> Iterator for AccountStorageIter<'a> {
type Item = (Slot, Arc<AccountStorageEntry>);
fn next(&mut self) -> Option<Self::Item> {
if let Some(entry) = self.iter.next() {
let slot = entry.key();
let store = entry.value();
return Some((*slot, Arc::clone(&store.storage)));
}
None
}
}
#[derive(Debug)]
pub struct ShrinkInProgress<'a> {
storage: &'a AccountStorage,
old_store: Arc<AccountStorageEntry>,
new_store: Arc<AccountStorageEntry>,
slot: Slot,
}
impl<'a> Drop for ShrinkInProgress<'a> {
fn drop(&mut self) {
assert_eq!(
self.storage
.map
.insert(
self.slot,
AccountStorageReference {
storage: Arc::clone(&self.new_store),
id: self.new_store.id()
}
)
.map(|store| store.id),
Some(self.old_store.id())
);
assert!(self
.storage
.shrink_in_progress_map
.remove(&self.slot)
.is_some());
}
}
impl<'a> ShrinkInProgress<'a> {
pub fn new_storage(&self) -> &Arc<AccountStorageEntry> {
&self.new_store
}
pub(crate) fn old_storage(&self) -> &Arc<AccountStorageEntry> {
&self.old_store
}
}
#[cfg_attr(feature = "frozen-abi", derive(AbiExample, AbiEnumVisitor))]
#[derive(Debug, Eq, PartialEq, Copy, Clone, Deserialize, Serialize)]
pub enum AccountStorageStatus {
Available = 0,
Full = 1,
Candidate = 2,
}
impl Default for AccountStorageStatus {
fn default() -> Self {
Self::Available
}
}
#[cfg(test)]
pub(crate) mod tests {
use {super::*, crate::accounts_file::AccountsFileProvider, std::path::Path};
#[test]
fn test_shrink_in_progress() {
let storage = AccountStorage::default();
let slot = 0;
let id = 0;
assert!(storage.get_account_storage_entry(slot, id).is_none());
let common_store_path = Path::new("");
let store_file_size = 4000;
let store_file_size2 = store_file_size * 2;
let entry = Arc::new(AccountStorageEntry::new(
common_store_path,
slot,
id,
store_file_size,
AccountsFileProvider::AppendVec,
));
let entry2 = Arc::new(AccountStorageEntry::new(
common_store_path,
slot,
id,
store_file_size2,
AccountsFileProvider::AppendVec,
));
storage
.map
.insert(slot, AccountStorageReference { id, storage: entry });
assert_eq!(
store_file_size,
storage
.get_account_storage_entry(slot, id)
.map(|entry| entry.accounts.capacity())
.unwrap_or_default()
);
storage.shrink_in_progress_map.insert(slot, entry2);
assert_eq!(
store_file_size,
storage
.get_account_storage_entry(slot, id)
.map(|entry| entry.accounts.capacity())
.unwrap_or_default()
);
storage.map.remove(&slot).unwrap();
assert_eq!(
store_file_size2,
storage
.get_account_storage_entry(slot, id)
.map(|entry| entry.accounts.capacity())
.unwrap_or_default()
);
}
impl AccountStorage {
fn get_test_storage_with_id(&self, id: AccountsFileId) -> Arc<AccountStorageEntry> {
let slot = 0;
let common_store_path = Path::new("");
let store_file_size = 4000;
Arc::new(AccountStorageEntry::new(
common_store_path,
slot,
id,
store_file_size,
AccountsFileProvider::AppendVec,
))
}
fn get_test_storage(&self) -> Arc<AccountStorageEntry> {
self.get_test_storage_with_id(0)
}
}
#[test]
#[should_panic(expected = "self.no_shrink_in_progress()")]
fn test_get_slot_storage_entry_fail() {
let storage = AccountStorage::default();
storage
.shrink_in_progress_map
.insert(0, storage.get_test_storage());
storage.get_slot_storage_entry(0);
}
#[test]
#[should_panic(expected = "self.no_shrink_in_progress()")]
fn test_all_slots_fail() {
let storage = AccountStorage::default();
storage
.shrink_in_progress_map
.insert(0, storage.get_test_storage());
storage.all_slots();
}
#[test]
#[should_panic(expected = "self.no_shrink_in_progress()")]
fn test_initialize_fail() {
let mut storage = AccountStorage::default();
storage
.shrink_in_progress_map
.insert(0, storage.get_test_storage());
storage.initialize(AccountStorageMap::default());
}
#[test]
#[should_panic(expected = "shrink_can_be_active || self.shrink_in_progress_map.is_empty()")]
fn test_remove_fail() {
let storage = AccountStorage::default();
storage
.shrink_in_progress_map
.insert(0, storage.get_test_storage());
storage.remove(&0, false);
}
#[test]
#[should_panic(expected = "self.no_shrink_in_progress()")]
fn test_iter_fail() {
let storage = AccountStorage::default();
storage
.shrink_in_progress_map
.insert(0, storage.get_test_storage());
storage.iter();
}
#[test]
#[should_panic(expected = "self.no_shrink_in_progress()")]
fn test_insert_fail() {
let storage = AccountStorage::default();
let sample = storage.get_test_storage();
storage.shrink_in_progress_map.insert(0, sample.clone());
storage.insert(0, sample);
}
#[test]
#[should_panic(expected = "duplicate call")]
fn test_shrinking_in_progress_fail3() {
let storage = AccountStorage::default();
let sample = storage.get_test_storage();
storage.map.insert(
0,
AccountStorageReference {
id: 0,
storage: sample.clone(),
},
);
storage.shrink_in_progress_map.insert(0, sample.clone());
storage.shrinking_in_progress(0, sample);
}
#[test]
#[should_panic(expected = "duplicate call")]
fn test_shrinking_in_progress_fail4() {
let storage = AccountStorage::default();
let sample_to_shrink = storage.get_test_storage();
let sample = storage.get_test_storage();
storage.map.insert(
0,
AccountStorageReference {
id: 0,
storage: sample_to_shrink,
},
);
let _shrinking_in_progress = storage.shrinking_in_progress(0, sample.clone());
storage.shrinking_in_progress(0, sample);
}
#[test]
fn test_shrinking_in_progress_second_call() {
let storage = AccountStorage::default();
let slot = 0;
let id_to_shrink = 1;
let id_shrunk = 0;
let sample_to_shrink = storage.get_test_storage_with_id(id_to_shrink);
let sample = storage.get_test_storage();
storage.map.insert(
slot,
AccountStorageReference {
id: id_to_shrink,
storage: sample_to_shrink,
},
);
let shrinking_in_progress = storage.shrinking_in_progress(slot, sample.clone());
assert!(storage.map.contains_key(&slot));
assert_eq!(id_to_shrink, storage.map.get(&slot).unwrap().storage.id());
assert_eq!(
(slot, id_shrunk),
storage
.shrink_in_progress_map
.iter()
.next()
.map(|r| (*r.key(), r.value().id()))
.unwrap()
);
drop(shrinking_in_progress);
assert!(storage.map.contains_key(&slot));
assert_eq!(id_shrunk, storage.map.get(&slot).unwrap().storage.id());
assert!(storage.shrink_in_progress_map.is_empty());
storage.shrinking_in_progress(slot, sample);
}
#[test]
#[should_panic(expected = "no pre-existing storage for shrinking slot")]
fn test_shrinking_in_progress_fail1() {
let storage = AccountStorage::default();
let sample = storage.get_test_storage();
storage.shrinking_in_progress(0, sample);
}
#[test]
#[should_panic(expected = "no pre-existing storage for shrinking slot")]
fn test_shrinking_in_progress_fail2() {
let storage = AccountStorage::default();
let sample = storage.get_test_storage();
storage.shrinking_in_progress(0, sample);
}
#[test]
fn test_missing() {
let storage = AccountStorage::default();
let sample = storage.get_test_storage();
let id = sample.id();
let missing_id = 9999;
let slot = sample.slot();
assert!(storage.get_account_storage_entry(slot, id).is_none());
assert!(storage
.get_account_storage_entry(slot, missing_id)
.is_none());
storage.map.insert(
slot,
AccountStorageReference {
id,
storage: sample.clone(),
},
);
assert!(storage.get_account_storage_entry(slot, id).is_some());
assert!(storage
.get_account_storage_entry(slot, missing_id)
.is_none());
storage
.shrink_in_progress_map
.insert(slot, Arc::clone(&sample));
assert!(storage
.get_account_storage_entry(slot, missing_id)
.is_none());
assert!(storage.get_account_storage_entry(slot, id).is_some());
storage.map.remove(&slot);
assert!(storage
.get_account_storage_entry(slot, missing_id)
.is_none());
assert!(storage.get_account_storage_entry(slot, id).is_some());
}
}