Skip to content

Commit

Permalink
Refactors stored accounts info (solana-labs#887)
Browse files Browse the repository at this point in the history
  • Loading branch information
brooksprumo authored Apr 18, 2024
1 parent e43338f commit ee113de
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 96 deletions.
14 changes: 7 additions & 7 deletions accounts-db/benches/append_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ extern crate test;
use {
rand::{thread_rng, Rng},
solana_accounts_db::{
account_storage::meta::{StoredAccountInfo, StoredMeta},
account_storage::meta::StoredMeta,
accounts_file::StoredAccountsInfo,
append_vec::{
test_utils::{create_test_account, get_append_vec_path},
AppendVec,
Expand All @@ -29,13 +30,12 @@ fn append_account(
vec: &AppendVec,
storage_meta: StoredMeta,
account: &AccountSharedData,
) -> Option<StoredAccountInfo> {
) -> Option<StoredAccountsInfo> {
let slot_ignored = Slot::MAX;
let accounts = [(&storage_meta.pubkey, account)];
let slice = &accounts[..];
let storable_accounts = (slot_ignored, slice);
let res = vec.append_accounts(&storable_accounts, 0);
res.and_then(|res| res.first().cloned())
vec.append_accounts(&storable_accounts, 0)
}

#[bench]
Expand All @@ -54,7 +54,7 @@ fn add_test_accounts(vec: &AppendVec, size: usize) -> Vec<(usize, usize)> {
(0..size)
.filter_map(|sample| {
let (meta, account) = create_test_account(sample);
append_account(vec, meta, &account).map(|info| (sample, info.offset))
append_account(vec, meta, &account).map(|info| (sample, info.offsets[0]))
})
.collect()
}
Expand Down Expand Up @@ -100,7 +100,7 @@ fn append_vec_concurrent_append_read(bencher: &mut Bencher) {
let sample = indexes1.lock().unwrap().len();
let (meta, account) = create_test_account(sample);
if let Some(info) = append_account(&vec1, meta, &account) {
indexes1.lock().unwrap().push((sample, info.offset))
indexes1.lock().unwrap().push((sample, info.offsets[0]))
} else {
break;
}
Expand Down Expand Up @@ -140,7 +140,7 @@ fn append_vec_concurrent_read_append(bencher: &mut Bencher) {
let sample: usize = thread_rng().gen_range(0..256);
let (meta, account) = create_test_account(sample);
if let Some(info) = append_account(&vec, meta, &account) {
indexes.lock().unwrap().push((sample, info.offset))
indexes.lock().unwrap().push((sample, info.offsets[0]))
}
});
}
4 changes: 2 additions & 2 deletions accounts-db/benches/bench_accounts_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ fn bench_write_accounts_file(c: &mut Criterion) {
},
|append_vec| {
let res = append_vec.append_accounts(&storable_accounts, 0).unwrap();
let accounts_written_count = res.len();
let accounts_written_count = res.offsets.len();
assert_eq!(accounts_written_count, accounts_count);
},
BatchSize::SmallInput,
Expand All @@ -72,7 +72,7 @@ fn bench_write_accounts_file(c: &mut Criterion) {
},
|hot_storage| {
let res = hot_storage.write_accounts(&storable_accounts, 0).unwrap();
let accounts_written_count = res.len();
let accounts_written_count = res.offsets.len();
assert_eq!(accounts_written_count, accounts_count);
},
BatchSize::SmallInput,
Expand Down
6 changes: 0 additions & 6 deletions accounts-db/src/account_storage/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,6 @@ use {
};

pub type StoredMetaWriteVersion = u64;
// A tuple that stores offset and size respectively
#[derive(Debug, Clone)]
pub struct StoredAccountInfo {
pub offset: usize,
pub size: usize,
}

lazy_static! {
static ref DEFAULT_ACCOUNT_HASH: AccountHash = AccountHash(Hash::default());
Expand Down
37 changes: 25 additions & 12 deletions accounts-db/src/accounts_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1138,10 +1138,11 @@ impl AccountStorageEntry {
self.accounts.get_account_shared_data(offset)
}

fn add_account(&self, num_bytes: usize) {
fn add_accounts(&self, num_accounts: usize, 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);
self.approx_store_count.fetch_add(1, Ordering::Relaxed);
*count_and_status = (count_and_status.0 + num_accounts, count_and_status.1);
self.approx_store_count
.fetch_add(num_accounts, Ordering::Relaxed);
self.alive_bytes.fetch_add(num_bytes, Ordering::SeqCst);
}

Expand Down Expand Up @@ -5976,12 +5977,12 @@ impl AccountsDb {
let mut total_append_accounts_us = 0;
while infos.len() < accounts_and_meta_to_store.len() {
let mut append_accounts = Measure::start("append_accounts");
let rvs = storage
let stored_accounts_info = storage
.accounts
.append_accounts(accounts_and_meta_to_store, infos.len());
append_accounts.stop();
total_append_accounts_us += append_accounts.as_us();
if rvs.is_none() {
let Some(stored_accounts_info) = stored_accounts_info else {
storage.set_status(AccountStorageStatus::Full);

// See if an account overflows the append vecs in the slot.
Expand All @@ -6005,18 +6006,21 @@ impl AccountsDb {
},
);
continue;
}
};

let store_id = storage.append_vec_id();
for (i, stored_account_info) in rvs.unwrap().into_iter().enumerate() {
storage.add_account(stored_account_info.size);

for (i, offset) in stored_accounts_info.offsets.iter().enumerate() {
infos.push(AccountInfo::new(
StorageLocation::AppendVec(store_id, stored_account_info.offset),
StorageLocation::AppendVec(store_id, *offset),
accounts_and_meta_to_store
.account_default_if_zero_lamport(i, |account| account.lamports()),
));
}
storage.add_accounts(
stored_accounts_info.offsets.len(),
stored_accounts_info.size,
);

// restore the state to available
storage.set_status(AccountStorageStatus::Available);
}
Expand Down Expand Up @@ -9614,6 +9618,12 @@ pub mod tests {
}
}

impl AccountStorageEntry {
fn add_account(&self, num_bytes: usize) {
self.add_accounts(1, num_bytes)
}
}

impl CurrentAncientAccountsFile {
/// note this requires that 'slot_and_accounts_file' is Some
fn append_vec_id(&self) -> AccountsFileId {
Expand Down Expand Up @@ -10673,12 +10683,15 @@ pub mod tests {
.unwrap();
if mark_alive {
// updates 'alive_bytes' on the storage
storage.add_account(stored_accounts_info[0].size);
storage.add_account(stored_accounts_info.size);
}

if let Some(index) = add_to_index {
let account_info = AccountInfo::new(
StorageLocation::AppendVec(storage.append_vec_id(), stored_accounts_info[0].offset),
StorageLocation::AppendVec(
storage.append_vec_id(),
stored_accounts_info.offsets[0],
),
account.lamports(),
);
index.upsert(
Expand Down
21 changes: 15 additions & 6 deletions accounts-db/src/accounts_file.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use {
crate::{
account_info::AccountInfo,
account_storage::meta::{StoredAccountInfo, StoredAccountMeta},
account_storage::meta::StoredAccountMeta,
accounts_db::AccountsFileId,
append_vec::{AppendVec, AppendVecError, IndexInfo},
storable_accounts::StorableAccounts,
Expand Down Expand Up @@ -268,19 +268,19 @@ impl AccountsFile {
&self,
accounts: &impl StorableAccounts<'a>,
skip: usize,
) -> Option<Vec<StoredAccountInfo>> {
) -> Option<StoredAccountsInfo> {
match self {
Self::AppendVec(av) => av.append_accounts(accounts, skip),
// Note: The conversion here is needed as the AccountsDB currently
// assumes all offsets are multiple of 8 while TieredStorage uses
// IndexOffset that is equivalent to AccountInfo::reduced_offset.
Self::TieredStorage(ts) => ts
.write_accounts(accounts, skip, &HOT_FORMAT)
.map(|mut infos| {
infos.iter_mut().for_each(|info| {
info.offset = AccountInfo::reduced_offset_to_offset(info.offset as u32);
.map(|mut stored_accounts_info| {
stored_accounts_info.offsets.iter_mut().for_each(|offset| {
*offset = AccountInfo::reduced_offset_to_offset(*offset as u32);
});
infos
stored_accounts_info
})
.ok(),
}
Expand Down Expand Up @@ -344,6 +344,15 @@ impl AccountsFileProvider {
}
}

/// Information after storing accounts
#[derive(Debug)]
pub struct StoredAccountsInfo {
/// offset in the storage where each account was stored
pub offsets: Vec<usize>,
/// total size of all the stored accounts
pub size: usize,
}

#[cfg(test)]
pub mod tests {
use crate::accounts_file::AccountsFile;
Expand Down
41 changes: 19 additions & 22 deletions accounts-db/src/append_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
use {
crate::{
account_storage::meta::{
AccountMeta, StoredAccountInfo, StoredAccountMeta, StoredMeta, StoredMetaWriteVersion,
AccountMeta, StoredAccountMeta, StoredMeta, StoredMetaWriteVersion,
},
accounts_file::{
AccountsFileError, MatchAccountOwnerError, Result, StoredAccountsInfo,
ALIGN_BOUNDARY_OFFSET,
},
accounts_file::{AccountsFileError, MatchAccountOwnerError, Result, ALIGN_BOUNDARY_OFFSET},
accounts_hash::AccountHash,
storable_accounts::StorableAccounts,
u64_align,
Expand Down Expand Up @@ -748,14 +751,14 @@ impl AppendVec {
&self,
accounts: &impl StorableAccounts<'a>,
skip: usize,
) -> Option<Vec<StoredAccountInfo>> {
) -> Option<StoredAccountsInfo> {
let _lock = self.append_lock.lock().unwrap();
let default_hash: Hash = Hash::default(); // [0_u8; 32];
let mut offset = self.len();
let len = accounts.len();
// Here we have `len - skip` number of accounts. The +1 extra capacity
// is for storing the aligned offset of the last entry to that is used
// to compute the StoredAccountInfo of the last entry.
// is for storing the aligned offset of the last-plus-one entry,
// which is used to compute the size of the last stored account.
let offsets_len = len - skip + 1;
let mut offsets = Vec::with_capacity(offsets_len);
let mut stop = false;
Expand Down Expand Up @@ -787,31 +790,25 @@ impl AppendVec {
(hash_ptr, mem::size_of::<AccountHash>()),
(data_ptr, data_len),
];
if let Some(res) = self.append_ptrs_locked(&mut offset, &ptrs) {
offsets.push(res)
if let Some(start_offset) = self.append_ptrs_locked(&mut offset, &ptrs) {
offsets.push(start_offset)
} else {
stop = true;
}
});
}

if offsets.is_empty() {
None
} else {
let mut rv = Vec::with_capacity(offsets.len());

// The last entry in this offset needs to be the u64 aligned offset, because that's
(!offsets.is_empty()).then(|| {
// The last entry in the offsets needs to be the u64 aligned `offset`, because that's
// where the *next* entry will begin to be stored.
// This is used to compute the size of the last stored account; make sure to remove
// it afterwards!
offsets.push(u64_align!(offset));
for offsets in offsets.windows(2) {
rv.push(StoredAccountInfo {
offset: offsets[0],
size: offsets[1] - offsets[0],
});
}
let size = offsets.windows(2).map(|offset| offset[1] - offset[0]).sum();
offsets.pop();

Some(rv)
}
StoredAccountsInfo { offsets, size }
})
}

/// Returns a slice suitable for use when archiving append vecs
Expand Down Expand Up @@ -846,7 +843,7 @@ pub mod tests {
let storable_accounts = (slot_ignored, slice);

self.append_accounts(&storable_accounts, 0)
.map(|res| res[0].offset)
.map(|res| res.offsets[0])
}
}

Expand Down
38 changes: 31 additions & 7 deletions accounts-db/src/tiered_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub mod readable;
mod test_utils;

use {
crate::{account_storage::meta::StoredAccountInfo, storable_accounts::StorableAccounts},
crate::{accounts_file::StoredAccountsInfo, storable_accounts::StorableAccounts},
error::TieredStorageError,
footer::{AccountBlockFormat, AccountMetaFormat},
hot::{HotStorageWriter, HOT_FORMAT},
Expand Down Expand Up @@ -110,7 +110,7 @@ impl TieredStorage {
accounts: &impl StorableAccounts<'a>,
skip: usize,
format: &TieredStorageFormat,
) -> TieredStorageResult<Vec<StoredAccountInfo>> {
) -> TieredStorageResult<StoredAccountsInfo> {
let was_written = self.already_written.swap(true, Ordering::AcqRel);

if was_written {
Expand Down Expand Up @@ -196,7 +196,7 @@ mod tests {
/// to persist non-account blocks such as footer, index block, etc.
fn write_zero_accounts(
tiered_storage: &TieredStorage,
expected_result: TieredStorageResult<Vec<StoredAccountInfo>>,
expected_result: TieredStorageResult<StoredAccountsInfo>,
) {
let slot_ignored = Slot::MAX;
let account_refs = Vec::<(&Pubkey, &AccountSharedData)>::new();
Expand Down Expand Up @@ -239,7 +239,13 @@ mod tests {
assert_eq!(tiered_storage.path(), tiered_storage_path);
assert_eq!(tiered_storage.len(), 0);

write_zero_accounts(&tiered_storage, Ok(vec![]));
write_zero_accounts(
&tiered_storage,
Ok(StoredAccountsInfo {
offsets: vec![],
size: 0,
}),
);
}

let tiered_storage_readonly = TieredStorage::new_readonly(&tiered_storage_path).unwrap();
Expand All @@ -265,7 +271,13 @@ mod tests {
let tiered_storage_path = temp_dir.path().join("test_write_accounts_twice");

let tiered_storage = TieredStorage::new_writable(&tiered_storage_path);
write_zero_accounts(&tiered_storage, Ok(vec![]));
write_zero_accounts(
&tiered_storage,
Ok(StoredAccountsInfo {
offsets: vec![],
size: 0,
}),
);
// Expect AttemptToUpdateReadOnly error as write_accounts can only
// be invoked once.
write_zero_accounts(
Expand All @@ -283,15 +295,27 @@ mod tests {
let tiered_storage_path = temp_dir.path().join("test_remove_on_drop");
{
let tiered_storage = TieredStorage::new_writable(&tiered_storage_path);
write_zero_accounts(&tiered_storage, Ok(vec![]));
write_zero_accounts(
&tiered_storage,
Ok(StoredAccountsInfo {
offsets: vec![],
size: 0,
}),
);
}
// expect the file does not exists as it has been removed on drop
assert!(!tiered_storage_path.try_exists().unwrap());

{
let tiered_storage =
ManuallyDrop::new(TieredStorage::new_writable(&tiered_storage_path));
write_zero_accounts(&tiered_storage, Ok(vec![]));
write_zero_accounts(
&tiered_storage,
Ok(StoredAccountsInfo {
offsets: vec![],
size: 0,
}),
);
}
// expect the file exists as we have ManuallyDrop this time.
assert!(tiered_storage_path.try_exists().unwrap());
Expand Down
Loading

0 comments on commit ee113de

Please sign in to comment.