Avoid early clean and bad snapshot by ref-counting (#8724)
* Avoid early clean and bad snapshot by ref-counting * Add measure * Clean ups * clean ups
This commit is contained in:
@ -736,13 +736,17 @@ impl AccountsDB {
|
|||||||
|
|
||||||
// Only keep purges where the entire history of the account in the root set
|
// Only keep purges where the entire history of the account in the root set
|
||||||
// can be purged. All AppendVecs for those updates are dead.
|
// can be purged. All AppendVecs for those updates are dead.
|
||||||
purges.retain(|_pubkey, account_infos| {
|
purges.retain(|pubkey, account_infos| {
|
||||||
|
let mut would_unref_count = 0;
|
||||||
for (_slot_id, account_info) in account_infos {
|
for (_slot_id, account_info) in account_infos {
|
||||||
if *store_counts.get(&account_info.store_id).unwrap() != 0 {
|
if *store_counts.get(&account_info.store_id).unwrap() == 0 {
|
||||||
|
would_unref_count += 1;
|
||||||
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
|
||||||
|
would_unref_count == accounts_index.ref_count_from_storage(&pubkey)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Recalculate reclaims with new purge set
|
// Recalculate reclaims with new purge set
|
||||||
@ -872,10 +876,10 @@ impl AccountsDB {
|
|||||||
pubkey: &Pubkey,
|
pubkey: &Pubkey,
|
||||||
) -> Option<(Account, Slot)> {
|
) -> Option<(Account, Slot)> {
|
||||||
let (lock, index) = accounts_index.get(pubkey, ancestors)?;
|
let (lock, index) = accounts_index.get(pubkey, ancestors)?;
|
||||||
let slot = lock[index].0;
|
let slot = lock.1[index].0;
|
||||||
//TODO: thread this as a ref
|
//TODO: thread this as a ref
|
||||||
if let Some(slot_storage) = storage.0.get(&slot) {
|
if let Some(slot_storage) = storage.0.get(&slot) {
|
||||||
let info = &lock[index].1;
|
let info = &lock.1[index].1;
|
||||||
slot_storage
|
slot_storage
|
||||||
.get(&info.store_id)
|
.get(&info.store_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()))
|
||||||
@ -1178,7 +1182,7 @@ impl AccountsDB {
|
|||||||
.par_iter()
|
.par_iter()
|
||||||
.filter_map(|pubkey| {
|
.filter_map(|pubkey| {
|
||||||
if let Some((list, index)) = accounts_index.get(pubkey, ancestors) {
|
if let Some((list, index)) = accounts_index.get(pubkey, ancestors) {
|
||||||
let (slot, account_info) = &list[index];
|
let (slot, account_info) = &list.1[index];
|
||||||
if account_info.lamports != 0 {
|
if account_info.lamports != 0 {
|
||||||
storage
|
storage
|
||||||
.0
|
.0
|
||||||
@ -1353,6 +1357,20 @@ impl AccountsDB {
|
|||||||
fn clean_dead_slots(&self, dead_slots: &mut HashSet<Slot>) {
|
fn clean_dead_slots(&self, dead_slots: &mut HashSet<Slot>) {
|
||||||
if !dead_slots.is_empty() {
|
if !dead_slots.is_empty() {
|
||||||
{
|
{
|
||||||
|
let mut measure = Measure::start("clean_dead_slots-ms");
|
||||||
|
let index = self.accounts_index.read().unwrap();
|
||||||
|
let storage = self.storage.read().unwrap();
|
||||||
|
for slot in dead_slots.iter() {
|
||||||
|
for store in storage.0.get(slot).unwrap().values() {
|
||||||
|
for account in store.accounts.accounts(0) {
|
||||||
|
index.unref_from_storage(&account.meta.pubkey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drop(index);
|
||||||
|
measure.stop();
|
||||||
|
inc_new_counter_info!("clean_dead_slots-unref-ms", measure.as_ms() as usize);
|
||||||
|
|
||||||
let mut index = self.accounts_index.write().unwrap();
|
let mut index = self.accounts_index.write().unwrap();
|
||||||
for slot in dead_slots.iter() {
|
for slot in dead_slots.iter() {
|
||||||
index.clean_dead_slot(*slot);
|
index.clean_dead_slot(*slot);
|
||||||
@ -1499,7 +1517,7 @@ impl AccountsDB {
|
|||||||
|
|
||||||
let mut counts = HashMap::new();
|
let mut counts = HashMap::new();
|
||||||
for slot_list in accounts_index.account_maps.values() {
|
for slot_list in accounts_index.account_maps.values() {
|
||||||
for (_slot, account_entry) in slot_list.read().unwrap().iter() {
|
for (_slot, account_entry) in slot_list.read().unwrap().1.iter() {
|
||||||
*counts.entry(account_entry.store_id).or_insert(0) += 1;
|
*counts.entry(account_entry.store_id).or_insert(0) += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1526,6 +1544,7 @@ impl AccountsDB {
|
|||||||
pub mod tests {
|
pub mod tests {
|
||||||
// TODO: all the bank tests are bank specific, issue: 2194
|
// TODO: all the bank tests are bank specific, issue: 2194
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::accounts_index::RefCount;
|
||||||
use crate::append_vec::AccountMeta;
|
use crate::append_vec::AccountMeta;
|
||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
use bincode::serialize_into;
|
use bincode::serialize_into;
|
||||||
@ -1988,7 +2007,7 @@ pub mod tests {
|
|||||||
let id = {
|
let id = {
|
||||||
let index = accounts.accounts_index.read().unwrap();
|
let index = accounts.accounts_index.read().unwrap();
|
||||||
let (list, idx) = index.get(&pubkey, &ancestors).unwrap();
|
let (list, idx) = index.get(&pubkey, &ancestors).unwrap();
|
||||||
list[idx].1.store_id
|
list.1[idx].1.store_id
|
||||||
};
|
};
|
||||||
accounts.add_root(1);
|
accounts.add_root(1);
|
||||||
|
|
||||||
@ -2018,6 +2037,13 @@ pub mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ref_count_for_pubkey(&self, pubkey: &Pubkey) -> RefCount {
|
||||||
|
self.accounts_index
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.ref_count_from_storage(&pubkey)
|
||||||
|
}
|
||||||
|
|
||||||
fn uncleaned_root_count(&self) -> usize {
|
fn uncleaned_root_count(&self) -> usize {
|
||||||
self.accounts_index.read().unwrap().uncleaned_roots.len()
|
self.accounts_index.read().unwrap().uncleaned_roots.len()
|
||||||
}
|
}
|
||||||
@ -2288,6 +2314,11 @@ pub mod tests {
|
|||||||
assert_eq!((account.lamports, slot), (expected_lamports, slot));
|
assert_eq!((account.lamports, slot), (expected_lamports, slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assert_not_load_account(accounts: &AccountsDB, slot: Slot, pubkey: Pubkey) {
|
||||||
|
let ancestors = vec![(slot, 0)].into_iter().collect();
|
||||||
|
assert!(accounts.load_slow(&ancestors, &pubkey).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
fn reconstruct_accounts_db_via_serialization(accounts: &AccountsDB, slot: Slot) -> AccountsDB {
|
fn reconstruct_accounts_db_via_serialization(accounts: &AccountsDB, slot: Slot) -> AccountsDB {
|
||||||
let mut writer = Cursor::new(vec![]);
|
let mut writer = Cursor::new(vec![]);
|
||||||
let snapshot_storages = accounts.get_snapshot_storages(slot);
|
let snapshot_storages = accounts.get_snapshot_storages(slot);
|
||||||
@ -2372,6 +2403,7 @@ pub mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.1
|
||||||
.len(),
|
.len(),
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
@ -3026,4 +3058,94 @@ pub mod tests {
|
|||||||
assert_load_account(&accounts, current_slot, purged_pubkey1, 0);
|
assert_load_account(&accounts, current_slot, purged_pubkey1, 0);
|
||||||
assert_load_account(&accounts, current_slot, purged_pubkey2, 0);
|
assert_load_account(&accounts, current_slot, purged_pubkey2, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_accounts_clean_after_snapshot_restore_then_old_revives() {
|
||||||
|
solana_logger::setup();
|
||||||
|
let old_lamport = 223;
|
||||||
|
let zero_lamport = 0;
|
||||||
|
let no_data = 0;
|
||||||
|
let dummy_lamport = 999999;
|
||||||
|
let owner = Account::default().owner;
|
||||||
|
|
||||||
|
let account = Account::new(old_lamport, no_data, &owner);
|
||||||
|
let account2 = Account::new(old_lamport + 100_001, no_data, &owner);
|
||||||
|
let account3 = Account::new(old_lamport + 100_002, no_data, &owner);
|
||||||
|
let dummy_account = Account::new(dummy_lamport, no_data, &owner);
|
||||||
|
let zero_lamport_account = Account::new(zero_lamport, no_data, &owner);
|
||||||
|
|
||||||
|
let pubkey1 = Pubkey::new_rand();
|
||||||
|
let pubkey2 = Pubkey::new_rand();
|
||||||
|
let dummy_pubkey = Pubkey::new_rand();
|
||||||
|
|
||||||
|
let mut current_slot = 0;
|
||||||
|
let accounts = AccountsDB::new_single();
|
||||||
|
|
||||||
|
// A: Initialize AccountsDB with pubkey1 and pubkey2
|
||||||
|
current_slot += 1;
|
||||||
|
accounts.store(current_slot, &[(&pubkey1, &account)]);
|
||||||
|
accounts.store(current_slot, &[(&pubkey2, &account)]);
|
||||||
|
accounts.add_root(current_slot);
|
||||||
|
|
||||||
|
// B: Test multiple updates to pubkey1 in a single slot/storage
|
||||||
|
current_slot += 1;
|
||||||
|
assert_eq!(0, accounts.store_count_for_slot(current_slot));
|
||||||
|
assert_eq!(1, accounts.ref_count_for_pubkey(&pubkey1));
|
||||||
|
accounts.store(current_slot, &[(&pubkey1, &account2)]);
|
||||||
|
accounts.store(current_slot, &[(&pubkey1, &account2)]);
|
||||||
|
assert_eq!(1, accounts.store_count_for_slot(current_slot));
|
||||||
|
assert_eq!(3, accounts.ref_count_for_pubkey(&pubkey1));
|
||||||
|
accounts.add_root(current_slot);
|
||||||
|
|
||||||
|
// C: Yet more update to trigger lazy clean of step A
|
||||||
|
current_slot += 1;
|
||||||
|
assert_eq!(3, accounts.ref_count_for_pubkey(&pubkey1));
|
||||||
|
accounts.store(current_slot, &[(&pubkey1, &account3)]);
|
||||||
|
assert_eq!(4, accounts.ref_count_for_pubkey(&pubkey1));
|
||||||
|
accounts.add_root(current_slot);
|
||||||
|
|
||||||
|
// D: Make pubkey1 0-lamport; also triggers clean of step B
|
||||||
|
current_slot += 1;
|
||||||
|
assert_eq!(4, accounts.ref_count_for_pubkey(&pubkey1));
|
||||||
|
accounts.store(current_slot, &[(&pubkey1, &zero_lamport_account)]);
|
||||||
|
assert_eq!(
|
||||||
|
3, /* == 4 - 2 + 1 */
|
||||||
|
accounts.ref_count_for_pubkey(&pubkey1)
|
||||||
|
);
|
||||||
|
accounts.add_root(current_slot);
|
||||||
|
|
||||||
|
// E: Avoid missing bank hash error
|
||||||
|
current_slot += 1;
|
||||||
|
accounts.store(current_slot, &[(&dummy_pubkey, &dummy_account)]);
|
||||||
|
accounts.add_root(current_slot);
|
||||||
|
|
||||||
|
assert_load_account(&accounts, current_slot, pubkey1, zero_lamport);
|
||||||
|
assert_load_account(&accounts, current_slot, pubkey2, old_lamport);
|
||||||
|
assert_load_account(&accounts, current_slot, dummy_pubkey, dummy_lamport);
|
||||||
|
|
||||||
|
// At this point, there is no index entries for A and B
|
||||||
|
// If step C and step D should be purged, snapshot restore would cause
|
||||||
|
// pubkey1 to be revived as the state of step A.
|
||||||
|
// So, prevent that from happening by introducing refcount
|
||||||
|
accounts.clean_accounts();
|
||||||
|
let accounts = reconstruct_accounts_db_via_serialization(&accounts, current_slot);
|
||||||
|
accounts.clean_accounts();
|
||||||
|
|
||||||
|
assert_load_account(&accounts, current_slot, pubkey1, zero_lamport);
|
||||||
|
assert_load_account(&accounts, current_slot, pubkey2, old_lamport);
|
||||||
|
assert_load_account(&accounts, current_slot, dummy_pubkey, dummy_lamport);
|
||||||
|
|
||||||
|
// F: Finally, make Step A cleanable
|
||||||
|
current_slot += 1;
|
||||||
|
accounts.store(current_slot, &[(&pubkey2, &account)]);
|
||||||
|
accounts.add_root(current_slot);
|
||||||
|
|
||||||
|
// Do clean
|
||||||
|
accounts.clean_accounts();
|
||||||
|
|
||||||
|
// Ensure pubkey2 is cleaned from the index finally
|
||||||
|
assert_not_load_account(&accounts, current_slot, pubkey1);
|
||||||
|
assert_load_account(&accounts, current_slot, pubkey2, old_lamport);
|
||||||
|
assert_load_account(&accounts, current_slot, dummy_pubkey, dummy_lamport);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,12 @@ use std::{
|
|||||||
|
|
||||||
pub type Slot = u64;
|
pub type Slot = u64;
|
||||||
type SlotList<T> = Vec<(Slot, T)>;
|
type SlotList<T> = Vec<(Slot, T)>;
|
||||||
|
pub type RefCount = u64;
|
||||||
|
type AccountMapEntry<T> = (RefCount, SlotList<T>);
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct AccountsIndex<T> {
|
pub struct AccountsIndex<T> {
|
||||||
pub account_maps: HashMap<Pubkey, RwLock<SlotList<T>>>,
|
pub account_maps: HashMap<Pubkey, RwLock<AccountMapEntry<T>>>,
|
||||||
|
|
||||||
pub roots: HashSet<Slot>,
|
pub roots: HashSet<Slot>,
|
||||||
pub uncleaned_roots: HashSet<Slot>,
|
pub uncleaned_roots: HashSet<Slot>,
|
||||||
@ -22,7 +24,7 @@ impl<T: Clone> AccountsIndex<T> {
|
|||||||
F: FnMut(&Pubkey, (&T, Slot)) -> (),
|
F: FnMut(&Pubkey, (&T, Slot)) -> (),
|
||||||
{
|
{
|
||||||
for (pubkey, list) in self.account_maps.iter() {
|
for (pubkey, list) in self.account_maps.iter() {
|
||||||
let list_r = list.read().unwrap();
|
let list_r = &list.read().unwrap().1;
|
||||||
if let Some(index) = self.latest_slot(ancestors, &list_r) {
|
if let Some(index) = self.latest_slot(ancestors, &list_r) {
|
||||||
func(pubkey, (&list_r[index].1, list_r[index].0));
|
func(pubkey, (&list_r[index].1, list_r[index].0));
|
||||||
}
|
}
|
||||||
@ -37,14 +39,14 @@ impl<T: Clone> AccountsIndex<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn would_purge(&self, pubkey: &Pubkey) -> Vec<(Slot, T)> {
|
pub fn would_purge(&self, pubkey: &Pubkey) -> Vec<(Slot, T)> {
|
||||||
let list = self.account_maps.get(&pubkey).unwrap().read().unwrap();
|
let list = &self.account_maps.get(&pubkey).unwrap().read().unwrap().1;
|
||||||
self.get_rooted_entries(&list)
|
self.get_rooted_entries(&list)
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter any rooted entries and return them along with a bool that indicates
|
// filter any rooted entries and return them along with a bool that indicates
|
||||||
// if this account has no more entries.
|
// if this account has no more entries.
|
||||||
pub fn purge(&self, pubkey: &Pubkey) -> (Vec<(Slot, T)>, bool) {
|
pub fn purge(&self, pubkey: &Pubkey) -> (Vec<(Slot, T)>, bool) {
|
||||||
let mut list = self.account_maps.get(&pubkey).unwrap().write().unwrap();
|
let list = &mut self.account_maps.get(&pubkey).unwrap().write().unwrap().1;
|
||||||
let reclaims = self.get_rooted_entries(&list);
|
let reclaims = self.get_rooted_entries(&list);
|
||||||
list.retain(|(slot, _)| !self.is_root(*slot));
|
list.retain(|(slot, _)| !self.is_root(*slot));
|
||||||
(reclaims, list.is_empty())
|
(reclaims, list.is_empty())
|
||||||
@ -70,11 +72,12 @@ impl<T: Clone> AccountsIndex<T> {
|
|||||||
&self,
|
&self,
|
||||||
pubkey: &Pubkey,
|
pubkey: &Pubkey,
|
||||||
ancestors: &HashMap<Slot, usize>,
|
ancestors: &HashMap<Slot, usize>,
|
||||||
) -> Option<(RwLockReadGuard<SlotList<T>>, usize)> {
|
) -> Option<(RwLockReadGuard<AccountMapEntry<T>>, usize)> {
|
||||||
self.account_maps.get(pubkey).and_then(|list| {
|
self.account_maps.get(pubkey).and_then(|list| {
|
||||||
let lock = list.read().unwrap();
|
let list_r = list.read().unwrap();
|
||||||
|
let lock = &list_r.1;
|
||||||
let found_index = self.latest_slot(ancestors, &lock)?;
|
let found_index = self.latest_slot(ancestors, &lock)?;
|
||||||
Some((lock, found_index))
|
Some((list_r, found_index))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +101,7 @@ impl<T: Clone> AccountsIndex<T> {
|
|||||||
let _slot_vec = self
|
let _slot_vec = self
|
||||||
.account_maps
|
.account_maps
|
||||||
.entry(*pubkey)
|
.entry(*pubkey)
|
||||||
.or_insert_with(|| RwLock::new(Vec::with_capacity(32)));
|
.or_insert_with(|| RwLock::new((0 as RefCount, Vec::with_capacity(32))));
|
||||||
self.update(slot, pubkey, account_info, reclaims);
|
self.update(slot, pubkey, account_info, reclaims);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,14 +117,15 @@ impl<T: Clone> AccountsIndex<T> {
|
|||||||
reclaims: &mut Vec<(Slot, T)>,
|
reclaims: &mut Vec<(Slot, T)>,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
if let Some(lock) = self.account_maps.get(pubkey) {
|
if let Some(lock) = self.account_maps.get(pubkey) {
|
||||||
let mut slot_vec = lock.write().unwrap();
|
let slot_vec = &mut lock.write().unwrap();
|
||||||
// filter out other dirty entries
|
// filter out other dirty entries
|
||||||
reclaims.extend(slot_vec.iter().filter(|(f, _)| *f == slot).cloned());
|
reclaims.extend(slot_vec.1.iter().filter(|(f, _)| *f == slot).cloned());
|
||||||
slot_vec.retain(|(f, _)| *f != slot);
|
slot_vec.1.retain(|(f, _)| *f != slot);
|
||||||
|
|
||||||
slot_vec.push((slot, account_info));
|
slot_vec.0 += 1 as RefCount;
|
||||||
|
slot_vec.1.push((slot, account_info));
|
||||||
// now, do lazy clean
|
// now, do lazy clean
|
||||||
self.purge_older_root_entries(&mut slot_vec, reclaims);
|
self.purge_older_root_entries(&mut slot_vec.1, reclaims);
|
||||||
|
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@ -129,6 +133,23 @@ impl<T: Clone> AccountsIndex<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn unref_from_storage(&self, pubkey: &Pubkey) {
|
||||||
|
let locked_slot_vec = self.account_maps.get(pubkey);
|
||||||
|
if let Some(slot_vec) = locked_slot_vec {
|
||||||
|
let mut slot_vec = slot_vec.write().unwrap();
|
||||||
|
slot_vec.0 -= 1 as RefCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ref_count_from_storage(&self, pubkey: &Pubkey) -> RefCount {
|
||||||
|
let locked_slot_vec = self.account_maps.get(pubkey);
|
||||||
|
if let Some(slot_vec) = locked_slot_vec {
|
||||||
|
slot_vec.read().unwrap().0
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn purge_older_root_entries(
|
fn purge_older_root_entries(
|
||||||
&self,
|
&self,
|
||||||
slot_vec: &mut Vec<(Slot, T)>,
|
slot_vec: &mut Vec<(Slot, T)>,
|
||||||
@ -150,7 +171,7 @@ impl<T: Clone> AccountsIndex<T> {
|
|||||||
pub fn clean_rooted_entries(&self, pubkey: &Pubkey, reclaims: &mut Vec<(Slot, T)>) {
|
pub fn clean_rooted_entries(&self, pubkey: &Pubkey, reclaims: &mut Vec<(Slot, T)>) {
|
||||||
if let Some(lock) = self.account_maps.get(pubkey) {
|
if let Some(lock) = self.account_maps.get(pubkey) {
|
||||||
let mut slot_vec = lock.write().unwrap();
|
let mut slot_vec = lock.write().unwrap();
|
||||||
self.purge_older_root_entries(&mut slot_vec, reclaims);
|
self.purge_older_root_entries(&mut slot_vec.1, reclaims);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,8 +179,8 @@ impl<T: Clone> AccountsIndex<T> {
|
|||||||
let entry = self
|
let entry = self
|
||||||
.account_maps
|
.account_maps
|
||||||
.entry(*pubkey)
|
.entry(*pubkey)
|
||||||
.or_insert_with(|| RwLock::new(vec![]));
|
.or_insert_with(|| RwLock::new((1 as RefCount, vec![])));
|
||||||
entry.write().unwrap().push((slot, account_info));
|
entry.write().unwrap().1.push((slot, account_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_purge(max_root: Slot, slot: Slot) -> bool {
|
pub fn can_purge(max_root: Slot, slot: Slot) -> bool {
|
||||||
@ -241,7 +262,7 @@ mod tests {
|
|||||||
|
|
||||||
let ancestors = vec![(0, 0)].into_iter().collect();
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||||
let (list, idx) = index.get(&key.pubkey(), &ancestors).unwrap();
|
let (list, idx) = index.get(&key.pubkey(), &ancestors).unwrap();
|
||||||
assert_eq!(list[idx], (0, true));
|
assert_eq!(list.1[idx], (0, true));
|
||||||
|
|
||||||
let mut num = 0;
|
let mut num = 0;
|
||||||
let mut found_key = false;
|
let mut found_key = false;
|
||||||
@ -274,7 +295,7 @@ mod tests {
|
|||||||
let ancestors = vec![].into_iter().collect();
|
let ancestors = vec![].into_iter().collect();
|
||||||
index.add_root(0);
|
index.add_root(0);
|
||||||
let (list, idx) = index.get(&key.pubkey(), &ancestors).unwrap();
|
let (list, idx) = index.get(&key.pubkey(), &ancestors).unwrap();
|
||||||
assert_eq!(list[idx], (0, true));
|
assert_eq!(list.1[idx], (0, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -317,14 +338,14 @@ mod tests {
|
|||||||
index.insert(0, &key.pubkey(), true, &mut gc);
|
index.insert(0, &key.pubkey(), true, &mut gc);
|
||||||
assert!(gc.is_empty());
|
assert!(gc.is_empty());
|
||||||
let (list, idx) = index.get(&key.pubkey(), &ancestors).unwrap();
|
let (list, idx) = index.get(&key.pubkey(), &ancestors).unwrap();
|
||||||
assert_eq!(list[idx], (0, true));
|
assert_eq!(list.1[idx], (0, true));
|
||||||
drop(list);
|
drop(list);
|
||||||
|
|
||||||
let mut gc = Vec::new();
|
let mut gc = Vec::new();
|
||||||
index.insert(0, &key.pubkey(), false, &mut gc);
|
index.insert(0, &key.pubkey(), false, &mut gc);
|
||||||
assert_eq!(gc, vec![(0, true)]);
|
assert_eq!(gc, vec![(0, true)]);
|
||||||
let (list, idx) = index.get(&key.pubkey(), &ancestors).unwrap();
|
let (list, idx) = index.get(&key.pubkey(), &ancestors).unwrap();
|
||||||
assert_eq!(list[idx], (0, false));
|
assert_eq!(list.1[idx], (0, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -339,10 +360,10 @@ mod tests {
|
|||||||
index.insert(1, &key.pubkey(), false, &mut gc);
|
index.insert(1, &key.pubkey(), false, &mut gc);
|
||||||
assert!(gc.is_empty());
|
assert!(gc.is_empty());
|
||||||
let (list, idx) = index.get(&key.pubkey(), &ancestors).unwrap();
|
let (list, idx) = index.get(&key.pubkey(), &ancestors).unwrap();
|
||||||
assert_eq!(list[idx], (0, true));
|
assert_eq!(list.1[idx], (0, true));
|
||||||
let ancestors = vec![(1, 0)].into_iter().collect();
|
let ancestors = vec![(1, 0)].into_iter().collect();
|
||||||
let (list, idx) = index.get(&key.pubkey(), &ancestors).unwrap();
|
let (list, idx) = index.get(&key.pubkey(), &ancestors).unwrap();
|
||||||
assert_eq!(list[idx], (1, false));
|
assert_eq!(list.1[idx], (1, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -362,7 +383,7 @@ mod tests {
|
|||||||
assert_eq!(gc, vec![(0, true), (1, false), (2, true)]);
|
assert_eq!(gc, vec![(0, true), (1, false), (2, true)]);
|
||||||
let ancestors = vec![].into_iter().collect();
|
let ancestors = vec![].into_iter().collect();
|
||||||
let (list, idx) = index.get(&key.pubkey(), &ancestors).unwrap();
|
let (list, idx) = index.get(&key.pubkey(), &ancestors).unwrap();
|
||||||
assert_eq!(list[idx], (3, true));
|
assert_eq!(list.1[idx], (3, true));
|
||||||
|
|
||||||
let mut num = 0;
|
let mut num = 0;
|
||||||
let mut found_key = false;
|
let mut found_key = false;
|
||||||
|
Reference in New Issue
Block a user