use {
crate::{
account_info::AccountInfo,
account_storage::meta::StoredAccountMeta,
accounts_db::AccountsFileId,
append_vec::{AppendVec, AppendVecError, IndexInfo},
storable_accounts::StorableAccounts,
tiered_storage::{
error::TieredStorageError, hot::HOT_FORMAT, index::IndexOffset, TieredStorage,
},
},
solana_sdk::{account::AccountSharedData, clock::Slot, pubkey::Pubkey},
std::{
mem,
path::{Path, PathBuf},
},
thiserror::Error,
};
pub const ALIGN_BOUNDARY_OFFSET: usize = mem::size_of::<u64>();
#[macro_export]
macro_rules! u64_align {
($addr: expr) => {
($addr + (ALIGN_BOUNDARY_OFFSET - 1)) & !(ALIGN_BOUNDARY_OFFSET - 1)
};
}
#[derive(Error, Debug)]
pub enum AccountsFileError {
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("AppendVecError: {0}")]
AppendVecError(#[from] AppendVecError),
#[error("TieredStorageError: {0}")]
TieredStorageError(#[from] TieredStorageError),
}
#[derive(Error, Debug, PartialEq, Eq)]
pub enum MatchAccountOwnerError {
#[error("The account owner does not match with the provided list")]
NoMatch,
#[error("Unable to load the account")]
UnableToLoad,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum StorageAccess {
#[default]
Mmap,
File,
}
pub type Result<T> = std::result::Result<T, AccountsFileError>;
#[derive(Debug)]
pub enum AccountsFile {
AppendVec(AppendVec),
TieredStorage(TieredStorage),
}
impl AccountsFile {
pub fn new_from_file(
path: impl Into<PathBuf>,
current_len: usize,
storage_access: StorageAccess,
) -> Result<(Self, usize)> {
let (av, num_accounts) = AppendVec::new_from_file(path, current_len, storage_access)?;
Ok((Self::AppendVec(av), num_accounts))
}
pub(crate) fn can_append(&self) -> bool {
match self {
Self::AppendVec(av) => av.can_append(),
Self::TieredStorage(_) => false,
}
}
pub(crate) fn reopen_as_readonly(&self) -> Option<Self> {
match self {
Self::AppendVec(av) => av.reopen_as_readonly().map(Self::AppendVec),
Self::TieredStorage(_) => None,
}
}
pub fn flush(&self) -> Result<()> {
match self {
Self::AppendVec(av) => av.flush(),
Self::TieredStorage(_) => Ok(()),
}
}
pub fn reset(&self) {
match self {
Self::AppendVec(av) => av.reset(),
Self::TieredStorage(_) => {}
}
}
pub fn remaining_bytes(&self) -> u64 {
match self {
Self::AppendVec(av) => av.remaining_bytes(),
Self::TieredStorage(ts) => ts.capacity().saturating_sub(ts.len() as u64),
}
}
pub fn len(&self) -> usize {
match self {
Self::AppendVec(av) => av.len(),
Self::TieredStorage(ts) => ts.len(),
}
}
pub fn is_empty(&self) -> bool {
match self {
Self::AppendVec(av) => av.is_empty(),
Self::TieredStorage(ts) => ts.is_empty(),
}
}
pub fn capacity(&self) -> u64 {
match self {
Self::AppendVec(av) => av.capacity(),
Self::TieredStorage(ts) => ts.capacity(),
}
}
pub fn file_name(slot: Slot, id: AccountsFileId) -> String {
format!("{slot}.{id}")
}
pub fn get_stored_account_meta_callback<Ret>(
&self,
offset: usize,
callback: impl for<'local> FnMut(StoredAccountMeta<'local>) -> Ret,
) -> Option<Ret> {
match self {
Self::AppendVec(av) => av.get_stored_account_meta_callback(offset, callback),
Self::TieredStorage(ts) => ts
.reader()?
.get_stored_account_meta_callback(
IndexOffset(AccountInfo::get_reduced_offset(offset)),
callback,
)
.ok()?,
}
}
pub(crate) fn get_account_shared_data(&self, offset: usize) -> Option<AccountSharedData> {
match self {
Self::AppendVec(av) => av.get_account_shared_data(offset),
Self::TieredStorage(ts) => {
let index_offset = IndexOffset(AccountInfo::get_reduced_offset(offset));
ts.reader()?.get_account_shared_data(index_offset).ok()?
}
}
}
pub fn account_matches_owners(
&self,
offset: usize,
owners: &[Pubkey],
) -> std::result::Result<usize, MatchAccountOwnerError> {
match self {
Self::AppendVec(av) => av.account_matches_owners(offset, owners),
Self::TieredStorage(ts) => {
let Some(reader) = ts.reader() else {
return Err(MatchAccountOwnerError::UnableToLoad);
};
reader.account_matches_owners(
IndexOffset(AccountInfo::get_reduced_offset(offset)),
owners,
)
}
}
}
pub fn path(&self) -> &Path {
match self {
Self::AppendVec(av) => av.path(),
Self::TieredStorage(ts) => ts.path(),
}
}
pub(crate) fn scan_accounts(
&self,
callback: impl for<'local> FnMut(StoredAccountMeta<'local>),
) {
match self {
Self::AppendVec(av) => av.scan_accounts(callback),
Self::TieredStorage(ts) => {
if let Some(reader) = ts.reader() {
_ = reader.scan_accounts(callback);
}
}
}
}
pub(crate) fn get_account_sizes(&self, sorted_offsets: &[usize]) -> Vec<usize> {
match self {
Self::AppendVec(av) => av.get_account_sizes(sorted_offsets),
Self::TieredStorage(ts) => ts
.reader()
.and_then(|reader| reader.get_account_sizes(sorted_offsets).ok())
.unwrap_or_default(),
}
}
pub(crate) fn scan_index(&self, callback: impl FnMut(IndexInfo)) {
match self {
Self::AppendVec(av) => av.scan_index(callback),
Self::TieredStorage(ts) => {
if let Some(reader) = ts.reader() {
_ = reader.scan_index(callback);
}
}
}
}
pub fn scan_pubkeys(&self, callback: impl FnMut(&Pubkey)) {
match self {
Self::AppendVec(av) => av.scan_pubkeys(callback),
Self::TieredStorage(ts) => {
if let Some(reader) = ts.reader() {
_ = reader.scan_pubkeys(callback);
}
}
}
}
pub fn append_accounts<'a>(
&self,
accounts: &impl StorableAccounts<'a>,
skip: usize,
) -> Option<StoredAccountsInfo> {
match self {
Self::AppendVec(av) => av.append_accounts(accounts, skip),
Self::TieredStorage(ts) => ts
.write_accounts(accounts, skip, &HOT_FORMAT)
.map(|mut stored_accounts_info| {
stored_accounts_info.offsets.iter_mut().for_each(|offset| {
*offset = AccountInfo::reduced_offset_to_offset(*offset as u32);
});
stored_accounts_info
})
.ok(),
}
}
pub fn internals_for_archive(&self) -> InternalsForArchive {
match self {
Self::AppendVec(av) => av.internals_for_archive(),
Self::TieredStorage(ts) => InternalsForArchive::Mmap(
ts.reader()
.expect("must be a reader when archiving")
.data_for_archive(),
),
}
}
}
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub enum AccountsFileProvider {
#[default]
AppendVec,
HotStorage,
}
impl AccountsFileProvider {
pub fn new_writable(&self, path: impl Into<PathBuf>, file_size: u64) -> AccountsFile {
match self {
Self::AppendVec => {
AccountsFile::AppendVec(AppendVec::new(path, true, file_size as usize))
}
Self::HotStorage => AccountsFile::TieredStorage(TieredStorage::new_writable(path)),
}
}
}
#[derive(Debug)]
pub enum InternalsForArchive<'a> {
Mmap(&'a [u8]),
FileIo(&'a Path),
}
#[derive(Debug)]
pub struct StoredAccountsInfo {
pub offsets: Vec<usize>,
pub size: usize,
}
#[cfg(test)]
pub mod tests {
use crate::accounts_file::AccountsFile;
impl AccountsFile {
pub(crate) fn set_current_len_for_tests(&self, len: usize) {
match self {
Self::AppendVec(av) => av.set_current_len_for_tests(len),
Self::TieredStorage(_) => {}
}
}
}
}