account storage is not in sync with the index after gc (#3914)
* account storage is not in sync with the index after gc * builds * clippy fmt * test * purge dead forks on store * rm println * also fixed count_stores * comments
This commit is contained in:
parent
349e8a9462
commit
101d6b92ee
|
@ -1,6 +1,6 @@
|
||||||
use crate::accounts_db::{
|
use crate::accounts_db::{
|
||||||
get_paths_vec, AccountInfo, AccountStorageSlice, AccountsDB, ErrorCounters,
|
get_paths_vec, AccountInfo, AccountStorage, AccountsDB, ErrorCounters, InstructionAccounts,
|
||||||
InstructionAccounts, InstructionLoaders,
|
InstructionLoaders,
|
||||||
};
|
};
|
||||||
use crate::accounts_index::{AccountsIndex, Fork};
|
use crate::accounts_index::{AccountsIndex, Fork};
|
||||||
use crate::append_vec::StoredAccount;
|
use crate::append_vec::StoredAccount;
|
||||||
|
@ -140,7 +140,7 @@ impl Accounts {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_tx_accounts(
|
fn load_tx_accounts(
|
||||||
storage: &AccountStorageSlice,
|
storage: &AccountStorage,
|
||||||
ancestors: &HashMap<Fork, usize>,
|
ancestors: &HashMap<Fork, usize>,
|
||||||
accounts_index: &AccountsIndex<AccountInfo>,
|
accounts_index: &AccountsIndex<AccountInfo>,
|
||||||
tx: &Transaction,
|
tx: &Transaction,
|
||||||
|
@ -180,7 +180,7 @@ impl Accounts {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_executable_accounts(
|
fn load_executable_accounts(
|
||||||
storage: &AccountStorageSlice,
|
storage: &AccountStorage,
|
||||||
ancestors: &HashMap<Fork, usize>,
|
ancestors: &HashMap<Fork, usize>,
|
||||||
accounts_index: &AccountsIndex<AccountInfo>,
|
accounts_index: &AccountsIndex<AccountInfo>,
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
|
@ -222,7 +222,7 @@ impl Accounts {
|
||||||
|
|
||||||
/// For each program_id in the transaction, load its loaders.
|
/// For each program_id in the transaction, load its loaders.
|
||||||
fn load_loaders(
|
fn load_loaders(
|
||||||
storage: &AccountStorageSlice,
|
storage: &AccountStorage,
|
||||||
ancestors: &HashMap<Fork, usize>,
|
ancestors: &HashMap<Fork, usize>,
|
||||||
accounts_index: &AccountsIndex<AccountInfo>,
|
accounts_index: &AccountsIndex<AccountInfo>,
|
||||||
tx: &Transaction,
|
tx: &Transaction,
|
||||||
|
|
|
@ -63,8 +63,7 @@ pub struct AccountInfo {
|
||||||
}
|
}
|
||||||
/// An offset into the AccountsDB::storage vector
|
/// An offset into the AccountsDB::storage vector
|
||||||
type AppendVecId = usize;
|
type AppendVecId = usize;
|
||||||
type AccountStorage = Vec<Arc<AccountStorageEntry>>;
|
pub type AccountStorage = HashMap<usize, Arc<AccountStorageEntry>>;
|
||||||
pub type AccountStorageSlice = [Arc<AccountStorageEntry>];
|
|
||||||
pub type InstructionAccounts = Vec<Account>;
|
pub type InstructionAccounts = Vec<Account>;
|
||||||
pub type InstructionLoaders = Vec<Vec<(Pubkey, Account)>>;
|
pub type InstructionLoaders = Vec<Vec<(Pubkey, Account)>>;
|
||||||
|
|
||||||
|
@ -87,6 +86,8 @@ impl From<usize> for AccountStorageStatus {
|
||||||
|
|
||||||
/// Persistent storage structure holding the accounts
|
/// Persistent storage structure holding the accounts
|
||||||
pub struct AccountStorageEntry {
|
pub struct AccountStorageEntry {
|
||||||
|
id: AppendVecId,
|
||||||
|
|
||||||
fork_id: Fork,
|
fork_id: Fork,
|
||||||
|
|
||||||
/// storage holding the accounts
|
/// storage holding the accounts
|
||||||
|
@ -110,6 +111,7 @@ impl AccountStorageEntry {
|
||||||
let accounts = AppendVec::new(&path.join(ACCOUNT_DATA_FILE), true, file_size as usize);
|
let accounts = AppendVec::new(&path.join(ACCOUNT_DATA_FILE), true, file_size as usize);
|
||||||
|
|
||||||
AccountStorageEntry {
|
AccountStorageEntry {
|
||||||
|
id,
|
||||||
fork_id,
|
fork_id,
|
||||||
accounts,
|
accounts,
|
||||||
count: AtomicUsize::new(0),
|
count: AtomicUsize::new(0),
|
||||||
|
@ -129,13 +131,10 @@ impl AccountStorageEntry {
|
||||||
self.count.fetch_add(1, Ordering::Relaxed);
|
self.count.fetch_add(1, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_account(&self) -> bool {
|
fn remove_account(&self) {
|
||||||
if self.count.fetch_sub(1, Ordering::Relaxed) == 1 {
|
if self.count.fetch_sub(1, Ordering::Relaxed) == 1 {
|
||||||
self.accounts.reset();
|
self.accounts.reset();
|
||||||
self.set_status(AccountStorageStatus::StorageAvailable);
|
self.set_status(AccountStorageStatus::StorageAvailable);
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,7 +170,7 @@ impl AccountsDB {
|
||||||
let paths = get_paths_vec(&paths);
|
let paths = get_paths_vec(&paths);
|
||||||
AccountsDB {
|
AccountsDB {
|
||||||
accounts_index: RwLock::new(AccountsIndex::default()),
|
accounts_index: RwLock::new(AccountsIndex::default()),
|
||||||
storage: RwLock::new(vec![]),
|
storage: RwLock::new(HashMap::new()),
|
||||||
next_id: AtomicUsize::new(0),
|
next_id: AtomicUsize::new(0),
|
||||||
write_version: AtomicUsize::new(0),
|
write_version: AtomicUsize::new(0),
|
||||||
paths,
|
paths,
|
||||||
|
@ -193,7 +192,7 @@ impl AccountsDB {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_accounts(&self, fork: Fork) -> bool {
|
pub fn has_accounts(&self, fork: Fork) -> bool {
|
||||||
for x in self.storage.read().unwrap().iter() {
|
for x in self.storage.read().unwrap().values() {
|
||||||
if x.fork_id == fork && x.count.load(Ordering::Relaxed) > 0 {
|
if x.fork_id == fork && x.count.load(Ordering::Relaxed) > 0 {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -213,7 +212,7 @@ impl AccountsDB {
|
||||||
.storage
|
.storage
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.values()
|
||||||
.filter(|store| store.fork_id == fork_id)
|
.filter(|store| store.fork_id == fork_id)
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -231,7 +230,7 @@ impl AccountsDB {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load(
|
pub fn load(
|
||||||
storage: &AccountStorageSlice,
|
storage: &AccountStorage,
|
||||||
ancestors: &HashMap<Fork, usize>,
|
ancestors: &HashMap<Fork, usize>,
|
||||||
accounts_index: &AccountsIndex<AccountInfo>,
|
accounts_index: &AccountsIndex<AccountInfo>,
|
||||||
pubkey: &Pubkey,
|
pubkey: &Pubkey,
|
||||||
|
@ -239,7 +238,7 @@ impl AccountsDB {
|
||||||
let info = accounts_index.get(pubkey, ancestors)?;
|
let info = accounts_index.get(pubkey, ancestors)?;
|
||||||
//TODO: thread this as a ref
|
//TODO: thread this as a ref
|
||||||
storage
|
storage
|
||||||
.get(info.id)
|
.get(&info.id)
|
||||||
.and_then(|store| Some(store.accounts.get_account(info.offset)?.0.clone_account()))
|
.and_then(|store| Some(store.accounts.get_account(info.offset)?.0.clone_account()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,82 +248,51 @@ impl AccountsDB {
|
||||||
Self::load(&storage, ancestors, &accounts_index, pubkey)
|
Self::load(&storage, ancestors, &accounts_index, pubkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_storage_id(&self, fork_id: Fork, start: usize, current: usize) -> usize {
|
fn get_exclusive_storage(&self, fork_id: Fork) -> Arc<AccountStorageEntry> {
|
||||||
let mut id = current;
|
let mut stores = self.storage.write().unwrap();
|
||||||
let len: usize;
|
let mut candidates: Vec<Arc<AccountStorageEntry>> = stores
|
||||||
{
|
.values()
|
||||||
let stores = self.storage.read().unwrap();
|
.filter_map(|x| {
|
||||||
len = stores.len();
|
if x.get_status() == AccountStorageStatus::StorageAvailable && x.fork_id == fork_id
|
||||||
if len > 0 {
|
{
|
||||||
if id == std::usize::MAX {
|
Some(x.clone())
|
||||||
id = start % len;
|
|
||||||
if stores[id].get_status() == AccountStorageStatus::StorageAvailable {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
stores[id].set_status(AccountStorageStatus::StorageFull);
|
None
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
if candidates.is_empty() {
|
||||||
|
let path_idx = thread_rng().gen_range(0, self.paths.len());
|
||||||
|
let storage = self.new_storage_entry(fork_id, &self.paths[path_idx]);
|
||||||
|
candidates.push(Arc::new(storage));
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
let rv = thread_rng().gen_range(0, candidates.len());
|
||||||
id = (id + 1) % len;
|
stores.remove(&candidates[rv].id);
|
||||||
if fork_id == stores[id].fork_id
|
candidates[rv].clone()
|
||||||
&& stores[id].get_status() == AccountStorageStatus::StorageAvailable
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if id == start % len {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len == 0 || id == start % len {
|
|
||||||
let mut stores = self.storage.write().unwrap();
|
|
||||||
// check if new store was already created
|
|
||||||
if stores.len() == len {
|
|
||||||
let path_idx = thread_rng().gen_range(0, self.paths.len());
|
|
||||||
let storage = self.new_storage_entry(fork_id, &self.paths[path_idx]);
|
|
||||||
stores.push(Arc::new(storage));
|
|
||||||
}
|
|
||||||
id = stores.len() - 1;
|
|
||||||
}
|
|
||||||
id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_account(&self, fork_id: Fork, pubkey: &Pubkey, account: &Account) -> (usize, usize) {
|
fn append_account(
|
||||||
let offset: usize;
|
&self,
|
||||||
let start = self.next_id.fetch_add(1, Ordering::Relaxed);
|
storage: &Arc<AccountStorageEntry>,
|
||||||
let mut id = self.get_storage_id(fork_id, start, std::usize::MAX);
|
pubkey: &Pubkey,
|
||||||
|
account: &Account,
|
||||||
// Even if no lamports, need to preserve the account owner so
|
) -> Option<usize> {
|
||||||
// we can update the vote_accounts correctly if this account is purged
|
let write_version = self.write_version.fetch_add(1, Ordering::Relaxed) as u64;
|
||||||
// when squashing.
|
let meta = StorageMeta {
|
||||||
let acc = &mut account.clone();
|
write_version,
|
||||||
|
pubkey: *pubkey,
|
||||||
|
data_len: account.data.len() as u64,
|
||||||
|
};
|
||||||
if account.lamports == 0 {
|
if account.lamports == 0 {
|
||||||
acc.data.resize(0, 0);
|
// Even if no lamports, need to preserve the account owner so
|
||||||
|
// we can update the vote_accounts correctly as roots move forward
|
||||||
|
let account = &mut account.clone();
|
||||||
|
account.data.resize(0, 0);
|
||||||
|
storage.accounts.append_account(meta, account)
|
||||||
|
} else {
|
||||||
|
storage.accounts.append_account(meta, account)
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
|
||||||
let result: Option<usize>;
|
|
||||||
{
|
|
||||||
let accounts = &self.storage.read().unwrap()[id];
|
|
||||||
let write_version = self.write_version.fetch_add(1, Ordering::Relaxed) as u64;
|
|
||||||
let meta = StorageMeta {
|
|
||||||
write_version,
|
|
||||||
pubkey: *pubkey,
|
|
||||||
data_len: account.data.len() as u64,
|
|
||||||
};
|
|
||||||
result = accounts.accounts.append_account(meta, account);
|
|
||||||
accounts.add_account();
|
|
||||||
}
|
|
||||||
if let Some(val) = result {
|
|
||||||
offset = val;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
id = self.get_storage_id(fork_id, start, id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(id, offset)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn purge_fork(&self, fork: Fork) {
|
pub fn purge_fork(&self, fork: Fork) {
|
||||||
|
@ -332,55 +300,107 @@ impl AccountsDB {
|
||||||
let is_root = self.accounts_index.read().unwrap().is_root(fork);
|
let is_root = self.accounts_index.read().unwrap().is_root(fork);
|
||||||
trace!("PURGING {} {}", fork, is_root);
|
trace!("PURGING {} {}", fork, is_root);
|
||||||
if !is_root {
|
if !is_root {
|
||||||
self.storage.write().unwrap().retain(|x| {
|
self.storage.write().unwrap().retain(|_, v| {
|
||||||
trace!("PURGING {} {}", x.fork_id, fork);
|
trace!("PURGING {} {}", v.fork_id, fork);
|
||||||
x.fork_id != fork
|
v.fork_id != fork
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn store_accounts(&self, fork_id: Fork, accounts: &[(&Pubkey, &Account)]) -> Vec<AccountInfo> {
|
||||||
|
let mut storage = self.get_exclusive_storage(fork_id);
|
||||||
|
let mut infos = vec![];
|
||||||
|
for (pubkey, account) in accounts {
|
||||||
|
loop {
|
||||||
|
let rv = self.append_account(&storage, pubkey, account);
|
||||||
|
if let Some(offset) = rv {
|
||||||
|
storage.add_account();
|
||||||
|
infos.push(AccountInfo {
|
||||||
|
id: storage.id,
|
||||||
|
offset,
|
||||||
|
lamports: account.lamports,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
storage.set_status(AccountStorageStatus::StorageFull);
|
||||||
|
self.storage.write().unwrap().insert(storage.id, storage);
|
||||||
|
storage = self.get_exclusive_storage(fork_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.storage.write().unwrap().insert(storage.id, storage);
|
||||||
|
infos
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_index(
|
||||||
|
&self,
|
||||||
|
fork_id: Fork,
|
||||||
|
infos: Vec<AccountInfo>,
|
||||||
|
accounts: &[(&Pubkey, &Account)],
|
||||||
|
) -> Vec<(Fork, AccountInfo)> {
|
||||||
|
let mut index = self.accounts_index.write().unwrap();
|
||||||
|
let mut reclaims = vec![];
|
||||||
|
for (i, info) in infos.into_iter().enumerate() {
|
||||||
|
let key = &accounts[i].0;
|
||||||
|
reclaims.extend(index.insert(fork_id, key, info).into_iter())
|
||||||
|
}
|
||||||
|
reclaims
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_dead_accounts(&self, reclaims: Vec<(Fork, AccountInfo)>) -> HashSet<Fork> {
|
||||||
|
let storage = self.storage.read().unwrap();
|
||||||
|
for (fork_id, account_info) in reclaims {
|
||||||
|
if let Some(store) = storage.get(&account_info.id) {
|
||||||
|
assert_eq!(
|
||||||
|
fork_id, store.fork_id,
|
||||||
|
"AccountDB::accounts_index corrupted. Storage should only point to one fork"
|
||||||
|
);
|
||||||
|
store.remove_account();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//TODO: performance here could be improved if AccountsDB::storage was organized by fork
|
||||||
|
let dead_forks: HashSet<Fork> = storage
|
||||||
|
.values()
|
||||||
|
.filter_map(|x| {
|
||||||
|
if x.count.load(Ordering::Relaxed) == 0 {
|
||||||
|
Some(x.fork_id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let live_forks: HashSet<Fork> = storage
|
||||||
|
.values()
|
||||||
|
.filter_map(|x| {
|
||||||
|
if x.count.load(Ordering::Relaxed) > 0 {
|
||||||
|
Some(x.fork_id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
dead_forks.difference(&live_forks).cloned().collect()
|
||||||
|
}
|
||||||
|
fn cleanup_dead_forks(&self, dead_forks: &mut HashSet<Fork>) {
|
||||||
|
let mut index = self.accounts_index.write().unwrap();
|
||||||
|
// a fork is not totally dead until it is older than the root
|
||||||
|
dead_forks.retain(|fork| *fork < index.last_root);
|
||||||
|
for fork in dead_forks.iter() {
|
||||||
|
index.cleanup_dead_fork(*fork);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Store the account update.
|
/// Store the account update.
|
||||||
pub fn store(&self, fork_id: Fork, accounts: &[(&Pubkey, &Account)]) {
|
pub fn store(&self, fork_id: Fork, accounts: &[(&Pubkey, &Account)]) {
|
||||||
//TODO; these blocks should be separate functions and unit tested
|
let infos = self.store_accounts(fork_id, accounts);
|
||||||
let infos: Vec<_> = accounts
|
let reclaims = self.update_index(fork_id, infos, accounts);
|
||||||
.iter()
|
trace!("reclaim: {}", reclaims.len());
|
||||||
.map(|(pubkey, account)| {
|
let mut dead_forks = self.remove_dead_accounts(reclaims);
|
||||||
let (id, offset) = self.append_account(fork_id, pubkey, account);
|
trace!("dead_forks: {}", dead_forks.len());
|
||||||
AccountInfo {
|
self.cleanup_dead_forks(&mut dead_forks);
|
||||||
id,
|
trace!("purge_forks: {}", dead_forks.len());
|
||||||
offset,
|
for fork in dead_forks {
|
||||||
lamports: account.lamports,
|
self.purge_fork(fork);
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let reclaims: Vec<(Fork, AccountInfo)> = {
|
|
||||||
let mut index = self.accounts_index.write().unwrap();
|
|
||||||
let mut reclaims = vec![];
|
|
||||||
for (i, info) in infos.into_iter().enumerate() {
|
|
||||||
let key = &accounts[i].0;
|
|
||||||
reclaims.extend(index.insert(fork_id, key, info).into_iter())
|
|
||||||
}
|
|
||||||
reclaims
|
|
||||||
};
|
|
||||||
|
|
||||||
let dead_forks: HashSet<Fork> = {
|
|
||||||
let stores = self.storage.read().unwrap();
|
|
||||||
let mut cleared_forks: HashSet<Fork> = HashSet::new();
|
|
||||||
for (fork_id, account_info) in reclaims {
|
|
||||||
let cleared = stores[account_info.id].remove_account();
|
|
||||||
if cleared {
|
|
||||||
cleared_forks.insert(fork_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let live_forks: HashSet<Fork> = stores.iter().map(|x| x.fork_id).collect();
|
|
||||||
cleared_forks.difference(&live_forks).cloned().collect()
|
|
||||||
};
|
|
||||||
{
|
|
||||||
let mut index = self.accounts_index.write().unwrap();
|
|
||||||
for fork in dead_forks {
|
|
||||||
index.cleanup_dead_fork(fork);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -569,7 +589,6 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
fn test_accountsdb_count_stores() {
|
fn test_accountsdb_count_stores() {
|
||||||
let paths = get_tmp_accounts_path!();
|
let paths = get_tmp_accounts_path!();
|
||||||
let db = AccountsDB::new(&paths.paths);
|
let db = AccountsDB::new(&paths.paths);
|
||||||
|
@ -592,15 +611,15 @@ mod tests {
|
||||||
{
|
{
|
||||||
let stores = db.storage.read().unwrap();
|
let stores = db.storage.read().unwrap();
|
||||||
assert_eq!(stores.len(), 2);
|
assert_eq!(stores.len(), 2);
|
||||||
assert_eq!(stores[0].count.load(Ordering::Relaxed), 2);
|
assert_eq!(stores[&0].count.load(Ordering::Relaxed), 2);
|
||||||
assert_eq!(stores[1].count.load(Ordering::Relaxed), 2);
|
assert_eq!(stores[&1].count.load(Ordering::Relaxed), 2);
|
||||||
}
|
}
|
||||||
db.add_root(1);
|
db.add_root(1);
|
||||||
{
|
{
|
||||||
let stores = db.storage.read().unwrap();
|
let stores = db.storage.read().unwrap();
|
||||||
assert_eq!(stores.len(), 2);
|
assert_eq!(stores.len(), 2);
|
||||||
assert_eq!(stores[0].count.load(Ordering::Relaxed), 2);
|
assert_eq!(stores[&0].count.load(Ordering::Relaxed), 2);
|
||||||
assert_eq!(stores[1].count.load(Ordering::Relaxed), 2);
|
assert_eq!(stores[&1].count.load(Ordering::Relaxed), 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -675,10 +694,10 @@ mod tests {
|
||||||
let stores = accounts.storage.read().unwrap();
|
let stores = accounts.storage.read().unwrap();
|
||||||
assert_eq!(stores.len(), 1);
|
assert_eq!(stores.len(), 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stores[0].get_status(),
|
stores[&0].get_status(),
|
||||||
AccountStorageStatus::StorageAvailable
|
AccountStorageStatus::StorageAvailable
|
||||||
);
|
);
|
||||||
stores[0].count.load(Ordering::Relaxed) == count
|
stores[&0].count.load(Ordering::Relaxed) == count
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_accounts(accounts: &AccountsDB, pubkeys: &Vec<Pubkey>, fork: Fork) {
|
fn check_accounts(accounts: &AccountsDB, pubkeys: &Vec<Pubkey>, fork: Fork) {
|
||||||
|
@ -745,7 +764,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut append_vec_histogram = HashMap::new();
|
let mut append_vec_histogram = HashMap::new();
|
||||||
for storage in accounts.storage.read().unwrap().iter() {
|
for storage in accounts.storage.read().unwrap().values() {
|
||||||
*append_vec_histogram.entry(storage.fork_id).or_insert(0) += 1;
|
*append_vec_histogram.entry(storage.fork_id).or_insert(0) += 1;
|
||||||
}
|
}
|
||||||
for count in append_vec_histogram.values() {
|
for count in append_vec_histogram.values() {
|
||||||
|
@ -754,7 +773,6 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
fn test_account_grow() {
|
fn test_account_grow() {
|
||||||
let paths = get_tmp_accounts_path!();
|
let paths = get_tmp_accounts_path!();
|
||||||
let accounts = AccountsDB::new(&paths.paths);
|
let accounts = AccountsDB::new(&paths.paths);
|
||||||
|
@ -769,8 +787,8 @@ mod tests {
|
||||||
{
|
{
|
||||||
let stores = accounts.storage.read().unwrap();
|
let stores = accounts.storage.read().unwrap();
|
||||||
assert_eq!(stores.len(), 1);
|
assert_eq!(stores.len(), 1);
|
||||||
assert_eq!(stores[0].count.load(Ordering::Relaxed), 1);
|
assert_eq!(stores[&0].count.load(Ordering::Relaxed), 1);
|
||||||
assert_eq!(stores[0].get_status(), status[0]);
|
assert_eq!(stores[&0].get_status(), status[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let pubkey2 = Pubkey::new_rand();
|
let pubkey2 = Pubkey::new_rand();
|
||||||
|
@ -779,10 +797,10 @@ mod tests {
|
||||||
{
|
{
|
||||||
let stores = accounts.storage.read().unwrap();
|
let stores = accounts.storage.read().unwrap();
|
||||||
assert_eq!(stores.len(), 2);
|
assert_eq!(stores.len(), 2);
|
||||||
assert_eq!(stores[0].count.load(Ordering::Relaxed), 1);
|
assert_eq!(stores[&0].count.load(Ordering::Relaxed), 1);
|
||||||
assert_eq!(stores[0].get_status(), status[1]);
|
assert_eq!(stores[&0].get_status(), status[1]);
|
||||||
assert_eq!(stores[1].count.load(Ordering::Relaxed), 1);
|
assert_eq!(stores[&1].count.load(Ordering::Relaxed), 1);
|
||||||
assert_eq!(stores[1].get_status(), status[0]);
|
assert_eq!(stores[&1].get_status(), status[0]);
|
||||||
}
|
}
|
||||||
let ancestors = vec![(0, 0)].into_iter().collect();
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||||
assert_eq!(accounts.load_slow(&ancestors, &pubkey1).unwrap(), account1);
|
assert_eq!(accounts.load_slow(&ancestors, &pubkey1).unwrap(), account1);
|
||||||
|
@ -794,12 +812,12 @@ mod tests {
|
||||||
{
|
{
|
||||||
let stores = accounts.storage.read().unwrap();
|
let stores = accounts.storage.read().unwrap();
|
||||||
assert_eq!(stores.len(), 3);
|
assert_eq!(stores.len(), 3);
|
||||||
assert_eq!(stores[0].count.load(Ordering::Relaxed), count[index]);
|
assert_eq!(stores[&0].count.load(Ordering::Relaxed), count[index]);
|
||||||
assert_eq!(stores[0].get_status(), status[0]);
|
assert_eq!(stores[&0].get_status(), status[0]);
|
||||||
assert_eq!(stores[1].count.load(Ordering::Relaxed), 1);
|
assert_eq!(stores[&1].count.load(Ordering::Relaxed), 1);
|
||||||
assert_eq!(stores[1].get_status(), status[1]);
|
assert_eq!(stores[&1].get_status(), status[1]);
|
||||||
assert_eq!(stores[2].count.load(Ordering::Relaxed), count[index ^ 1]);
|
assert_eq!(stores[&2].count.load(Ordering::Relaxed), count[index ^ 1]);
|
||||||
assert_eq!(stores[2].get_status(), status[0]);
|
assert_eq!(stores[&2].get_status(), status[0]);
|
||||||
}
|
}
|
||||||
let ancestors = vec![(0, 0)].into_iter().collect();
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||||
assert_eq!(accounts.load_slow(&ancestors, &pubkey1).unwrap(), account1);
|
assert_eq!(accounts.load_slow(&ancestors, &pubkey1).unwrap(), account1);
|
||||||
|
@ -831,4 +849,41 @@ mod tests {
|
||||||
assert!(accounts.load_slow(&ancestors, &pubkeys[0]).is_some());
|
assert!(accounts.load_slow(&ancestors, &pubkeys[0]).is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lazy_gc_fork() {
|
||||||
|
//This test is pedantic
|
||||||
|
//A fork is purged when a non root bank is cleaned up. If a fork is behind root but it is
|
||||||
|
//not root, it means we are retaining dead banks.
|
||||||
|
let paths = get_tmp_accounts_path!();
|
||||||
|
let accounts = AccountsDB::new(&paths.paths);
|
||||||
|
let pubkey = Pubkey::new_rand();
|
||||||
|
let account = Account::new(1, 0, &Account::default().owner);
|
||||||
|
//store an account
|
||||||
|
accounts.store(0, &[(&pubkey, &account)]);
|
||||||
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||||
|
let info = accounts
|
||||||
|
.accounts_index
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&pubkey, &ancestors)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
//fork 0 is behind root, but it is not root, therefore it is purged
|
||||||
|
accounts.add_root(1);
|
||||||
|
assert!(accounts.accounts_index.read().unwrap().is_purged(0));
|
||||||
|
|
||||||
|
//fork is still there, since gc is lazy
|
||||||
|
assert!(accounts.storage.read().unwrap().get(&info.id).is_some());
|
||||||
|
|
||||||
|
//store causes cleanup
|
||||||
|
accounts.store(1, &[(&pubkey, &account)]);
|
||||||
|
|
||||||
|
//fork is gone
|
||||||
|
assert!(accounts.storage.read().unwrap().get(&info.id).is_none());
|
||||||
|
|
||||||
|
//new value is there
|
||||||
|
let ancestors = vec![(1, 1)].into_iter().collect();
|
||||||
|
assert_eq!(accounts.load_slow(&ancestors, &pubkey), Some(account));;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ pub struct AccountsIndex<T> {
|
||||||
account_maps: HashMap<Pubkey, Vec<(Fork, T)>>,
|
account_maps: HashMap<Pubkey, Vec<(Fork, T)>>,
|
||||||
roots: HashSet<Fork>,
|
roots: HashSet<Fork>,
|
||||||
//This value that needs to be stored to recover the index from AppendVec
|
//This value that needs to be stored to recover the index from AppendVec
|
||||||
last_root: Fork,
|
pub last_root: Fork,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> AccountsIndex<T> {
|
impl<T: Clone> AccountsIndex<T> {
|
||||||
|
@ -59,7 +59,7 @@ impl<T: Clone> AccountsIndex<T> {
|
||||||
};
|
};
|
||||||
rv
|
rv
|
||||||
}
|
}
|
||||||
fn is_purged(&self, fork: Fork) -> bool {
|
pub fn is_purged(&self, fork: Fork) -> bool {
|
||||||
!self.is_root(fork) && fork < self.last_root
|
!self.is_root(fork) && fork < self.last_root
|
||||||
}
|
}
|
||||||
pub fn is_root(&self, fork: Fork) -> bool {
|
pub fn is_root(&self, fork: Fork) -> bool {
|
||||||
|
|
Loading…
Reference in New Issue