From 40030e104dacb10d0d41f0bd88160328e7f3d3c5 Mon Sep 17 00:00:00 2001 From: Yueh-Hsuan Chiang Date: Sat, 3 Feb 2024 00:01:25 -0800 Subject: [PATCH] [TieredStorage] Add AccountsFile::TieredStorage --- accounts-db/src/accounts_file.rs | 77 +++++++++++++++++++++++++- accounts-db/src/ancient_append_vecs.rs | 3 + 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/accounts-db/src/accounts_file.rs b/accounts-db/src/accounts_file.rs index 4f373333ae7450..df79797606ba8a 100644 --- a/accounts-db/src/accounts_file.rs +++ b/accounts-db/src/accounts_file.rs @@ -7,7 +7,9 @@ use { accounts_hash::AccountHash, append_vec::{AppendVec, AppendVecError}, storable_accounts::StorableAccounts, - tiered_storage::error::TieredStorageError, + tiered_storage::{ + error::TieredStorageError, hot::HOT_FORMAT, index::IndexOffset, TieredStorage, + }, }, solana_sdk::{account::ReadableAccount, clock::Slot, pubkey::Pubkey}, std::{ @@ -56,6 +58,7 @@ pub type Result = std::result::Result; /// under different formats. pub enum AccountsFile { AppendVec(AppendVec), + TieredStorage(TieredStorage), } impl AccountsFile { @@ -64,43 +67,80 @@ impl AccountsFile { /// The second element of the returned tuple is the number of accounts in the /// accounts file. pub fn new_from_file(path: impl AsRef, current_len: usize) -> Result<(Self, usize)> { - let (av, num_accounts) = AppendVec::new_from_file(path, current_len)?; - Ok((Self::AppendVec(av), num_accounts)) + match TieredStorage::new_readonly(path.as_ref()) { + Ok(tiered_storage) => { + // unwrap() note: TieredStorage::new_readonly() is guaranteed to have a valid + // reader instance when opening with new_readonly. + let num_accounts = tiered_storage.reader().unwrap().num_accounts(); + Ok((Self::TieredStorage(tiered_storage), num_accounts)) + } + Err(TieredStorageError::MagicNumberMismatch(_, _)) => { + // In case of MagicNumberMismatch, we can assume that this is not + // a tiered-storage file. + let (av, num_accounts) = AppendVec::new_from_file(path, current_len)?; + Ok((Self::AppendVec(av), num_accounts)) + } + Err(e) => Err(AccountsFileError::TieredStorageError(e)), + } } 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) => { + if ts.is_read_only() { + 0 + } else { + u64::MAX + } + } } } pub fn len(&self) -> usize { match self { Self::AppendVec(av) => av.len(), + Self::TieredStorage(ts) => ts.file_size().unwrap_or(0) as usize, } } pub fn is_empty(&self) -> bool { match self { Self::AppendVec(av) => av.is_empty(), + Self::TieredStorage(ts) => ts.file_size().unwrap_or(0) == 0, } } pub fn capacity(&self) -> u64 { match self { Self::AppendVec(av) => av.capacity(), + Self::TieredStorage(ts) => { + // As TieredStorage doesn't have the concept about capacity, + // here we return the file size when the file was written. + if ts.is_read_only() { + return ts.file_size().unwrap_or(0); + } + // Or u64::MAX, indicating it can accept as many data as + // possible. + // TODO: while TieredStorage isn't directly capped by size, it + // has maximum numbers of accounts and owners. A proper API + // is needed to capture this. + u64::MAX + } } } @@ -114,6 +154,16 @@ impl AccountsFile { pub fn get_account(&self, index: usize) -> Option<(StoredAccountMeta<'_>, usize)> { match self { Self::AppendVec(av) => av.get_account(index), + Self::TieredStorage(ts) => { + if let Some(reader) = ts.reader() { + if let Ok(Some((metas, index_offset))) = + reader.get_account(IndexOffset(index as u32)) + { + return Some((metas, index_offset.0 as usize)); + } + } + None + } } } @@ -124,6 +174,12 @@ impl AccountsFile { ) -> std::result::Result { 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(offset as u32), owners) + } } } @@ -131,6 +187,7 @@ impl AccountsFile { pub fn get_path(&self) -> PathBuf { match self { Self::AppendVec(av) => av.get_path(), + Self::TieredStorage(ts) => ts.path().to_path_buf(), } } @@ -143,6 +200,14 @@ impl AccountsFile { pub fn accounts(&self, offset: usize) -> Vec { match self { Self::AppendVec(av) => av.accounts(offset), + Self::TieredStorage(ts) => { + let Some(reader) = ts.reader() else { + return vec![]; + }; + reader + .accounts(IndexOffset(offset as u32)) + .unwrap_or_default() + } } } @@ -166,6 +231,11 @@ impl AccountsFile { ) -> Option> { match self { Self::AppendVec(av) => av.append_accounts(accounts, skip), + // Currently we only support HOT_FORMAT. If we later want to use + // a different format, then we will need a way to pass-in it. + // TODO: consider adding function like write_accounts_to_hot_storage() or something + // to hide implementation detail. + Self::TieredStorage(ts) => ts.write_accounts(accounts, skip, &HOT_FORMAT).ok(), } } } @@ -204,6 +274,7 @@ pub mod tests { 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(_) => {} } } } diff --git a/accounts-db/src/ancient_append_vecs.rs b/accounts-db/src/ancient_append_vecs.rs index 3925b21e69f586..99f948b939d0bd 100644 --- a/accounts-db/src/ancient_append_vecs.rs +++ b/accounts-db/src/ancient_append_vecs.rs @@ -968,6 +968,9 @@ pub const fn get_ancient_append_vec_capacity() -> u64 { pub fn is_ancient(storage: &AccountsFile) -> bool { match storage { AccountsFile::AppendVec(storage) => storage.capacity() >= get_ancient_append_vec_capacity(), + AccountsFile::TieredStorage(ts) => { + ts.file_size().unwrap_or(0) >= get_ancient_append_vec_capacity() + } } }