1use {
2 crate::{
3 account_info::AccountInfo,
4 account_storage::meta::StoredAccountMeta,
5 accounts_db::AccountsFileId,
6 append_vec::{AppendVec, AppendVecError, IndexInfo},
7 storable_accounts::StorableAccounts,
8 tiered_storage::{
9 error::TieredStorageError, hot::HOT_FORMAT, index::IndexOffset, TieredStorage,
10 },
11 },
12 solana_sdk::{account::AccountSharedData, clock::Slot, pubkey::Pubkey},
13 std::{
14 mem,
15 path::{Path, PathBuf},
16 },
17 thiserror::Error,
18};
19
20pub const ALIGN_BOUNDARY_OFFSET: usize = mem::size_of::<u64>();
23#[macro_export]
24macro_rules! u64_align {
25 ($addr: expr) => {
26 ($addr + (ALIGN_BOUNDARY_OFFSET - 1)) & !(ALIGN_BOUNDARY_OFFSET - 1)
27 };
28}
29
30#[derive(Error, Debug)]
31pub enum AccountsFileError {
33 #[error("I/O error: {0}")]
34 Io(#[from] std::io::Error),
35
36 #[error("AppendVecError: {0}")]
37 AppendVecError(#[from] AppendVecError),
38
39 #[error("TieredStorageError: {0}")]
40 TieredStorageError(#[from] TieredStorageError),
41}
42
43#[derive(Error, Debug, PartialEq, Eq)]
44pub enum MatchAccountOwnerError {
45 #[error("The account owner does not match with the provided list")]
46 NoMatch,
47 #[error("Unable to load the account")]
48 UnableToLoad,
49}
50
51#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
52pub enum StorageAccess {
53 #[default]
54 Mmap,
56 File,
58}
59
60pub type Result<T> = std::result::Result<T, AccountsFileError>;
61
62#[derive(Debug)]
63pub enum AccountsFile {
66 AppendVec(AppendVec),
67 TieredStorage(TieredStorage),
68}
69
70impl AccountsFile {
71 pub fn new_from_file(
76 path: impl Into<PathBuf>,
77 current_len: usize,
78 storage_access: StorageAccess,
79 ) -> Result<(Self, usize)> {
80 let (av, num_accounts) = AppendVec::new_from_file(path, current_len, storage_access)?;
81 Ok((Self::AppendVec(av), num_accounts))
82 }
83
84 pub(crate) fn can_append(&self) -> bool {
86 match self {
87 Self::AppendVec(av) => av.can_append(),
88 Self::TieredStorage(_) => false,
90 }
91 }
92
93 pub(crate) fn reopen_as_readonly(&self) -> Option<Self> {
95 match self {
96 Self::AppendVec(av) => av.reopen_as_readonly().map(Self::AppendVec),
97 Self::TieredStorage(_) => None,
98 }
99 }
100
101 pub fn flush(&self) -> Result<()> {
102 match self {
103 Self::AppendVec(av) => av.flush(),
104 Self::TieredStorage(_) => Ok(()),
105 }
106 }
107
108 pub fn reset(&self) {
109 match self {
110 Self::AppendVec(av) => av.reset(),
111 Self::TieredStorage(_) => {}
112 }
113 }
114
115 pub fn remaining_bytes(&self) -> u64 {
116 match self {
117 Self::AppendVec(av) => av.remaining_bytes(),
118 Self::TieredStorage(ts) => ts.capacity().saturating_sub(ts.len() as u64),
119 }
120 }
121
122 pub fn len(&self) -> usize {
123 match self {
124 Self::AppendVec(av) => av.len(),
125 Self::TieredStorage(ts) => ts.len(),
126 }
127 }
128
129 pub fn is_empty(&self) -> bool {
130 match self {
131 Self::AppendVec(av) => av.is_empty(),
132 Self::TieredStorage(ts) => ts.is_empty(),
133 }
134 }
135
136 pub fn capacity(&self) -> u64 {
137 match self {
138 Self::AppendVec(av) => av.capacity(),
139 Self::TieredStorage(ts) => ts.capacity(),
140 }
141 }
142
143 pub fn file_name(slot: Slot, id: AccountsFileId) -> String {
144 format!("{slot}.{id}")
145 }
146
147 pub fn get_stored_account_meta_callback<Ret>(
149 &self,
150 offset: usize,
151 callback: impl for<'local> FnMut(StoredAccountMeta<'local>) -> Ret,
152 ) -> Option<Ret> {
153 match self {
154 Self::AppendVec(av) => av.get_stored_account_meta_callback(offset, callback),
155 Self::TieredStorage(ts) => ts
159 .reader()?
160 .get_stored_account_meta_callback(
161 IndexOffset(AccountInfo::get_reduced_offset(offset)),
162 callback,
163 )
164 .ok()?,
165 }
166 }
167
168 pub(crate) fn get_account_shared_data(&self, offset: usize) -> Option<AccountSharedData> {
170 match self {
171 Self::AppendVec(av) => av.get_account_shared_data(offset),
172 Self::TieredStorage(ts) => {
173 let index_offset = IndexOffset(AccountInfo::get_reduced_offset(offset));
177 ts.reader()?.get_account_shared_data(index_offset).ok()?
178 }
179 }
180 }
181
182 pub fn account_matches_owners(
183 &self,
184 offset: usize,
185 owners: &[Pubkey],
186 ) -> std::result::Result<usize, MatchAccountOwnerError> {
187 match self {
188 Self::AppendVec(av) => av.account_matches_owners(offset, owners),
189 Self::TieredStorage(ts) => {
193 let Some(reader) = ts.reader() else {
194 return Err(MatchAccountOwnerError::UnableToLoad);
195 };
196 reader.account_matches_owners(
197 IndexOffset(AccountInfo::get_reduced_offset(offset)),
198 owners,
199 )
200 }
201 }
202 }
203
204 pub fn path(&self) -> &Path {
206 match self {
207 Self::AppendVec(av) => av.path(),
208 Self::TieredStorage(ts) => ts.path(),
209 }
210 }
211
212 pub fn scan_accounts(&self, callback: impl for<'local> FnMut(StoredAccountMeta<'local>)) {
214 match self {
215 Self::AppendVec(av) => av.scan_accounts(callback),
216 Self::TieredStorage(ts) => {
217 if let Some(reader) = ts.reader() {
218 _ = reader.scan_accounts(callback);
219 }
220 }
221 }
222 }
223
224 pub(crate) fn get_account_sizes(&self, sorted_offsets: &[usize]) -> Vec<usize> {
226 match self {
227 Self::AppendVec(av) => av.get_account_sizes(sorted_offsets),
228 Self::TieredStorage(ts) => ts
229 .reader()
230 .and_then(|reader| reader.get_account_sizes(sorted_offsets).ok())
231 .unwrap_or_default(),
232 }
233 }
234
235 pub(crate) fn scan_index(&self, callback: impl FnMut(IndexInfo)) {
237 match self {
238 Self::AppendVec(av) => av.scan_index(callback),
239 Self::TieredStorage(ts) => {
240 if let Some(reader) = ts.reader() {
241 _ = reader.scan_index(callback);
242 }
243 }
244 }
245 }
246
247 pub fn scan_pubkeys(&self, callback: impl FnMut(&Pubkey)) {
249 match self {
250 Self::AppendVec(av) => av.scan_pubkeys(callback),
251 Self::TieredStorage(ts) => {
252 if let Some(reader) = ts.reader() {
253 _ = reader.scan_pubkeys(callback);
254 }
255 }
256 }
257 }
258
259 pub fn append_accounts<'a>(
267 &self,
268 accounts: &impl StorableAccounts<'a>,
269 skip: usize,
270 ) -> Option<StoredAccountsInfo> {
271 match self {
272 Self::AppendVec(av) => av.append_accounts(accounts, skip),
273 Self::TieredStorage(ts) => ts
277 .write_accounts(accounts, skip, &HOT_FORMAT)
278 .map(|mut stored_accounts_info| {
279 stored_accounts_info.offsets.iter_mut().for_each(|offset| {
280 *offset = AccountInfo::reduced_offset_to_offset(*offset as u32);
281 });
282 stored_accounts_info
283 })
284 .ok(),
285 }
286 }
287
288 pub fn internals_for_archive(&self) -> InternalsForArchive {
290 match self {
291 Self::AppendVec(av) => av.internals_for_archive(),
292 Self::TieredStorage(ts) => InternalsForArchive::Mmap(
293 ts.reader()
294 .expect("must be a reader when archiving")
295 .data_for_archive(),
296 ),
297 }
298 }
299}
300
301#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
303pub enum AccountsFileProvider {
304 #[default]
305 AppendVec,
306 HotStorage,
307}
308
309impl AccountsFileProvider {
310 pub fn new_writable(&self, path: impl Into<PathBuf>, file_size: u64) -> AccountsFile {
311 match self {
312 Self::AppendVec => {
313 AccountsFile::AppendVec(AppendVec::new(path, true, file_size as usize))
314 }
315 Self::HotStorage => AccountsFile::TieredStorage(TieredStorage::new_writable(path)),
316 }
317 }
318}
319
320#[derive(Debug)]
322pub enum InternalsForArchive<'a> {
323 Mmap(&'a [u8]),
325 FileIo(&'a Path),
327}
328
329#[derive(Debug)]
331pub struct StoredAccountsInfo {
332 pub offsets: Vec<usize>,
334 pub size: usize,
336}
337
338#[cfg(test)]
339pub mod tests {
340 use crate::accounts_file::AccountsFile;
341 impl AccountsFile {
342 pub(crate) fn set_current_len_for_tests(&self, len: usize) {
343 match self {
344 Self::AppendVec(av) => av.set_current_len_for_tests(len),
345 Self::TieredStorage(_) => {}
346 }
347 }
348 }
349}