use {
crate::serde_snapshot::SerializedAccountsFileId,
dashmap::DashMap,
rayon::iter::{IntoParallelRefIterator, ParallelIterator},
serde::{Deserialize, Serialize},
solana_accounts_db::{
ObsoleteAccountItem, ObsoleteAccounts, account_info::Offset,
account_storage_entry::AccountStorageEntry, accounts_db::AccountsFileId,
},
solana_clock::Slot,
std::sync::Arc,
};
#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
#[derive(Debug, Default, Serialize, Deserialize)]
struct SerdeObsoleteAccounts {
pub id: SerializedAccountsFileId,
pub bytes: u64,
pub accounts: Vec<(Offset, usize, Slot)>,
}
impl SerdeObsoleteAccounts {
fn new_from_storage_entry_at_slot(storage: &AccountStorageEntry, snapshot_slot: Slot) -> Self {
let accounts = storage
.obsolete_accounts_for_snapshots(snapshot_slot)
.accounts
.into_iter()
.map(|item| (item.offset, item.data_len, item.slot))
.collect();
SerdeObsoleteAccounts {
id: storage.id() as SerializedAccountsFileId,
bytes: storage.get_obsolete_bytes(Some(snapshot_slot)) as u64,
accounts,
}
}
}
#[cfg_attr(
feature = "frozen-abi",
derive(AbiExample),
frozen_abi(digest = "12qimMBghYs9dL4nhws7Xe1B5MXWBV75VTMTGLHxevYE")
)]
#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct SerdeObsoleteAccountsMap {
map: DashMap<Slot, SerdeObsoleteAccounts>,
}
impl SerdeObsoleteAccountsMap {
pub(crate) fn new_from_storages(
snapshot_storages: &[Arc<AccountStorageEntry>],
snapshot_slot: Slot,
) -> Self {
let map = DashMap::with_capacity(snapshot_storages.len());
snapshot_storages.par_iter().for_each(|storage| {
map.insert(
storage.slot(),
SerdeObsoleteAccounts::new_from_storage_entry_at_slot(storage, snapshot_slot),
);
});
SerdeObsoleteAccountsMap { map }
}
pub(crate) fn remove(&self, slot: &Slot) -> Option<(ObsoleteAccounts, AccountsFileId, usize)> {
self.map.remove(slot).map(|(_, entry)| {
let accounts = entry
.accounts
.into_iter()
.map(|(offset, data_len, slot)| ObsoleteAccountItem {
offset,
data_len,
slot,
})
.collect();
(
ObsoleteAccounts { accounts },
entry.id as AccountsFileId,
entry.bytes as usize,
)
})
}
}
#[cfg(test)]
mod test {
use {
super::*,
crate::serde_snapshot::{deserialize_from, serialize_into},
std::io::{BufReader, BufWriter, Cursor},
test_case::test_case,
};
#[test_case(0, 0)]
#[test_case(1, 0)]
#[test_case(10, 15)]
fn test_serialize_and_deserialize_obsolete_accounts(
num_storages: u64,
num_obsolete_accounts_per_storage: usize,
) {
let obsolete_accounts = DashMap::<Slot, ObsoleteAccounts>::new();
for slot in 1..=num_storages {
let obsolete_accounts_list = ObsoleteAccounts {
accounts: (0..num_obsolete_accounts_per_storage)
.map(|j| ObsoleteAccountItem {
offset: j as Offset,
data_len: j * 10,
slot: slot + 1,
})
.collect(),
};
obsolete_accounts.insert(slot, obsolete_accounts_list);
}
let map = obsolete_accounts
.iter()
.map(|entry| {
let accounts = entry
.value()
.accounts
.iter()
.map(|item| (item.offset, item.data_len, item.slot))
.collect();
let serde_obsolete_accounts = SerdeObsoleteAccounts {
id: *entry.key() as SerializedAccountsFileId,
bytes: num_obsolete_accounts_per_storage as u64 * 1000,
accounts,
};
(*entry.key(), serde_obsolete_accounts)
})
.collect();
let obsolete_accounts_map = SerdeObsoleteAccountsMap { map };
let mut buf = Vec::new();
let cursor = Cursor::new(&mut buf);
let mut writer = BufWriter::new(cursor);
serialize_into(&mut writer, &obsolete_accounts_map).unwrap();
drop(writer);
let cursor = Cursor::new(buf.as_slice());
let mut reader = BufReader::new(cursor);
let deserialized_obsolete_accounts: SerdeObsoleteAccountsMap =
deserialize_from(&mut reader).unwrap();
assert_eq!(
deserialized_obsolete_accounts.map.len(),
obsolete_accounts.len()
);
for (slot, obsolete_accounts) in obsolete_accounts {
let deserialized_obsolete_accounts =
deserialized_obsolete_accounts.remove(&slot).unwrap();
assert_eq!(obsolete_accounts, deserialized_obsolete_accounts.0);
}
}
}