Skip to content

Commit

Permalink
add get_stored_account to append vec (solana-labs#508)
Browse files Browse the repository at this point in the history
* add get_stored_account to append vec

* Update accounts-db/src/append_vec.rs

Co-authored-by: Brooks <brooks@prumo.org>

* renames

* accountshash -> accounthash

---------

Co-authored-by: Brooks <brooks@prumo.org>
  • Loading branch information
jeffwashington and brooksprumo authored Apr 2, 2024
1 parent 9099901 commit c59143b
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 5 deletions.
7 changes: 5 additions & 2 deletions accounts-db/src/accounts_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -814,8 +814,7 @@ impl<'a> LoadedAccountAccessor<'a> {
// from the storage map after we grabbed the storage entry, the recycler should not
// reset the storage entry until we drop the reference to the storage entry.
maybe_storage_entry
.get_stored_account_meta(*offset)
.map(|account| account.to_account_shared_data())
.get_stored_account(*offset)
.expect("If a storage entry was found in the storage map, it must not have been reset yet")
}
_ => self.check_and_get_loaded_account().take_account(),
Expand Down Expand Up @@ -1150,6 +1149,10 @@ impl AccountStorageEntry {
Some(self.accounts.get_account(offset)?.0)
}

fn get_stored_account(&self, offset: usize) -> Option<AccountSharedData> {
self.accounts.get_stored_account(offset)
}

fn add_account(&self, num_bytes: usize) {
let mut count_and_status = self.count_and_status.lock_write();
*count_and_status = (count_and_status.0 + 1, count_and_status.1);
Expand Down
14 changes: 13 additions & 1 deletion accounts-db/src/accounts_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ use {
error::TieredStorageError, hot::HOT_FORMAT, index::IndexOffset, TieredStorage,
},
},
solana_sdk::{account::ReadableAccount, clock::Slot, pubkey::Pubkey},
solana_sdk::{
account::{AccountSharedData, ReadableAccount},
clock::Slot,
pubkey::Pubkey,
},
std::{borrow::Borrow, mem, path::PathBuf},
thiserror::Error,
};
Expand Down Expand Up @@ -133,6 +137,14 @@ impl AccountsFile {
}
}

/// return an `AccountSharedData` for an account at `offset`, if any. Otherwise return None.
pub(crate) fn get_stored_account(&self, offset: usize) -> Option<AccountSharedData> {
match self {
Self::AppendVec(av) => av.get_stored_account(offset),
Self::TieredStorage(_) => unimplemented!(),
}
}

pub fn account_matches_owners(
&self,
offset: usize,
Expand Down
67 changes: 65 additions & 2 deletions accounts-db/src/append_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use {
log::*,
memmap2::MmapMut,
solana_sdk::{
account::{AccountSharedData, ReadableAccount},
account::{AccountSharedData, ReadableAccount, WritableAccount},
clock::Slot,
pubkey::Pubkey,
stake_history::Epoch,
Expand Down Expand Up @@ -516,6 +516,24 @@ impl AppendVec {
))
}

/// return an `AccountSharedData` for an account at `offset`.
/// This fn can efficiently return exactly what is needed by a caller.
pub(crate) fn get_stored_account(&self, offset: usize) -> Option<AccountSharedData> {
let (meta, next) = self.get_type::<StoredMeta>(offset)?;
let (account_meta, next) = self.get_type::<AccountMeta>(next)?;
let next = next + std::mem::size_of::<AccountHash>();
let (data, _next) = self.get_slice(next, meta.data_len as usize)?;
Some(AccountSharedData::create(
account_meta.lamports,
data.to_vec(),
account_meta.owner,
account_meta.executable,
account_meta.rent_epoch,
))
}

/// note this fn can return account meta for an account whose fields have been truncated (ie. if `len` isn't long enough.)
/// This fn doesn't even load the data_len field, so this fn does not know how big `len` needs to be.
fn get_account_meta(&self, offset: usize) -> Option<&AccountMeta> {
// Skip over StoredMeta data in the account
let offset = offset.checked_add(mem::size_of::<StoredMeta>())?;
Expand Down Expand Up @@ -552,7 +570,21 @@ impl AppendVec {
&self,
offset: usize,
) -> Option<(StoredMeta, solana_sdk::account::AccountSharedData)> {
let (stored_account, _) = self.get_account(offset)?;
let r1 = self.get_account(offset);
let r2 = self.get_stored_account(offset);
let r3 = self.get_account_meta(offset);
if r1.is_some() {
// r3 can return Some when r1 and r2 do not
assert!(r3.is_some());
}
if let Some(r2) = r2.as_ref() {
let meta = r3.unwrap();
assert_eq!(meta.executable, r2.executable());
assert_eq!(meta.owner, *r2.owner());
assert_eq!(meta.lamports, r2.lamports());
assert_eq!(meta.rent_epoch, r2.rent_epoch());
}
let (stored_account, _) = r1?;
let meta = stored_account.meta().clone();
Some((meta, stored_account.to_account_shared_data()))
}
Expand Down Expand Up @@ -1044,6 +1076,37 @@ pub mod tests {
let account = create_test_account(0);
let index = av.append_account_test(&account).unwrap();
assert_eq!(av.get_account_test(index).unwrap(), account);
truncate_and_test(av, index);
}

/// truncate `av` and make sure that we fail to get an account. This verifies that the eof
/// code is working correctly.
fn truncate_and_test(av: AppendVec, index: usize) {
// truncate the hash, 1 byte at a time
let hash = std::mem::size_of::<AccountHash>();
for _ in 0..hash {
av.current_len.fetch_sub(1, Ordering::Relaxed);
assert_eq!(av.get_account_test(index), None);
}
// truncate 1 byte into the AccountMeta
av.current_len.fetch_sub(1, Ordering::Relaxed);
assert_eq!(av.get_account_test(index), None);
}

#[test]
fn test_append_vec_one_with_data() {
let path = get_append_vec_path("test_append");
let av = AppendVec::new(&path.path, true, 1024 * 1024);
let data_len = 1;
let account = create_test_account(data_len);
let index = av.append_account_test(&account).unwrap();
// make the append vec 1 byte too short. we should get `None` since the append vec was truncated
assert_eq!(
STORE_META_OVERHEAD + data_len,
av.current_len.load(Ordering::Relaxed)
);
assert_eq!(av.get_account_test(index).unwrap(), account);
truncate_and_test(av, index);
}

#[test]
Expand Down

0 comments on commit c59143b

Please sign in to comment.