2020-03-25 21:08:56 -07:00
|
|
|
use solana_sdk::{clock::Slot, pubkey::Pubkey};
|
2020-03-23 08:50:23 -07:00
|
|
|
use std::sync::atomic::{AtomicU64, Ordering};
|
2020-01-28 17:03:20 -08:00
|
|
|
use std::{
|
2020-05-13 00:22:14 -07:00
|
|
|
collections::{BTreeMap, HashMap, HashSet},
|
|
|
|
ops::RangeBounds,
|
2020-01-28 17:03:20 -08:00
|
|
|
sync::{RwLock, RwLockReadGuard},
|
|
|
|
};
|
2019-04-15 17:15:50 -07:00
|
|
|
|
2020-03-25 21:08:56 -07:00
|
|
|
pub type SlotList<T> = Vec<(Slot, T)>;
|
|
|
|
pub type SlotSlice<'s, T> = &'s [(Slot, T)];
|
2020-04-26 19:07:03 -07:00
|
|
|
pub type Ancestors = HashMap<Slot, usize>;
|
|
|
|
|
2020-03-12 22:14:37 -07:00
|
|
|
pub type RefCount = u64;
|
2020-03-23 08:50:23 -07:00
|
|
|
type AccountMapEntry<T> = (AtomicU64, RwLock<SlotList<T>>);
|
2020-06-25 07:11:33 -07:00
|
|
|
pub type AccountMap<K, V> = BTreeMap<K, V>;
|
2019-04-15 17:15:50 -07:00
|
|
|
|
2019-06-25 07:21:45 -07:00
|
|
|
#[derive(Debug, Default)]
|
2019-04-15 17:15:50 -07:00
|
|
|
pub struct AccountsIndex<T> {
|
2020-06-25 07:11:33 -07:00
|
|
|
pub account_maps: AccountMap<Pubkey, AccountMapEntry<T>>,
|
2019-05-30 21:31:35 -07:00
|
|
|
|
2019-10-23 22:01:22 -07:00
|
|
|
pub roots: HashSet<Slot>,
|
2020-03-02 21:57:25 -08:00
|
|
|
pub uncleaned_roots: HashSet<Slot>,
|
2020-06-11 22:51:43 -07:00
|
|
|
pub previous_uncleaned_roots: HashSet<Slot>,
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
|
|
|
|
2020-05-13 00:22:14 -07:00
|
|
|
impl<'a, T: 'a + Clone> AccountsIndex<T> {
|
|
|
|
fn do_scan_accounts<F, I>(&self, ancestors: &Ancestors, mut func: F, iter: I)
|
2019-07-10 22:06:32 -07:00
|
|
|
where
|
2020-06-08 17:38:14 -07:00
|
|
|
F: FnMut(&Pubkey, (&T, Slot)),
|
2020-05-13 00:22:14 -07:00
|
|
|
I: Iterator<Item = (&'a Pubkey, &'a AccountMapEntry<T>)>,
|
2019-07-10 22:06:32 -07:00
|
|
|
{
|
2020-05-13 00:22:14 -07:00
|
|
|
for (pubkey, list) in iter {
|
2020-03-23 08:50:23 -07:00
|
|
|
let list_r = &list.1.read().unwrap();
|
2020-09-28 16:04:46 -07:00
|
|
|
if let Some(index) = self.latest_slot(Some(ancestors), &list_r, None) {
|
2019-07-20 17:58:39 -07:00
|
|
|
func(pubkey, (&list_r[index].1, list_r[index].0));
|
2019-07-10 22:06:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-13 00:22:14 -07:00
|
|
|
/// call func with every pubkey and index visible from a given set of ancestors
|
2020-06-08 17:38:14 -07:00
|
|
|
pub(crate) fn scan_accounts<F>(&self, ancestors: &Ancestors, func: F)
|
2020-05-13 00:22:14 -07:00
|
|
|
where
|
2020-06-08 17:38:14 -07:00
|
|
|
F: FnMut(&Pubkey, (&T, Slot)),
|
2020-05-13 00:22:14 -07:00
|
|
|
{
|
|
|
|
self.do_scan_accounts(ancestors, func, self.account_maps.iter());
|
|
|
|
}
|
|
|
|
|
|
|
|
/// call func with every pubkey and index visible from a given set of ancestors with range
|
2020-06-08 17:38:14 -07:00
|
|
|
pub(crate) fn range_scan_accounts<F, R>(&self, ancestors: &Ancestors, range: R, func: F)
|
2020-05-13 00:22:14 -07:00
|
|
|
where
|
2020-06-08 17:38:14 -07:00
|
|
|
F: FnMut(&Pubkey, (&T, Slot)),
|
2020-05-13 00:22:14 -07:00
|
|
|
R: RangeBounds<Pubkey>,
|
|
|
|
{
|
|
|
|
self.do_scan_accounts(ancestors, func, self.account_maps.range(range));
|
|
|
|
}
|
|
|
|
|
2020-03-25 21:08:56 -07:00
|
|
|
fn get_rooted_entries(&self, slice: SlotSlice<T>) -> SlotList<T> {
|
|
|
|
slice
|
|
|
|
.iter()
|
2019-10-23 22:01:22 -07:00
|
|
|
.filter(|(slot, _)| self.is_root(*slot))
|
2019-10-23 12:46:48 -07:00
|
|
|
.cloned()
|
2019-12-02 09:51:05 -08:00
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2020-07-02 22:25:17 -07:00
|
|
|
// returns the rooted entries and the storage ref count
|
|
|
|
pub fn would_purge(&self, pubkey: &Pubkey) -> (SlotList<T>, RefCount) {
|
|
|
|
let (ref_count, slots_list) = self.account_maps.get(&pubkey).unwrap();
|
|
|
|
let slots_list_r = &slots_list.read().unwrap();
|
|
|
|
(
|
|
|
|
self.get_rooted_entries(&slots_list_r),
|
|
|
|
ref_count.load(Ordering::Relaxed),
|
|
|
|
)
|
2019-12-02 09:51:05 -08:00
|
|
|
}
|
|
|
|
|
2019-12-11 11:11:31 -08:00
|
|
|
// filter any rooted entries and return them along with a bool that indicates
|
|
|
|
// if this account has no more entries.
|
2020-03-25 21:08:56 -07:00
|
|
|
pub fn purge(&self, pubkey: &Pubkey) -> (SlotList<T>, bool) {
|
2020-03-23 08:50:23 -07:00
|
|
|
let list = &mut self.account_maps.get(&pubkey).unwrap().1.write().unwrap();
|
2019-12-02 09:51:05 -08:00
|
|
|
let reclaims = self.get_rooted_entries(&list);
|
2019-10-23 22:01:22 -07:00
|
|
|
list.retain(|(slot, _)| !self.is_root(*slot));
|
2019-12-11 11:11:31 -08:00
|
|
|
(reclaims, list.is_empty())
|
2019-10-23 12:46:48 -07:00
|
|
|
}
|
|
|
|
|
2020-07-02 22:25:17 -07:00
|
|
|
pub fn purge_exact(&self, pubkey: &Pubkey, slots: HashSet<Slot>) -> (SlotList<T>, bool) {
|
|
|
|
let list = &mut self.account_maps.get(&pubkey).unwrap().1.write().unwrap();
|
|
|
|
let reclaims = list
|
|
|
|
.iter()
|
|
|
|
.filter(|(slot, _)| slots.contains(&slot))
|
|
|
|
.cloned()
|
|
|
|
.collect();
|
|
|
|
list.retain(|(slot, _)| !slots.contains(slot));
|
|
|
|
(reclaims, list.is_empty())
|
|
|
|
}
|
|
|
|
|
2020-09-28 16:04:46 -07:00
|
|
|
// Given a SlotSlice `L`, a list of ancestors and a maximum slot, find the latest element
|
|
|
|
// in `L`, where the slot `S < max_slot`, and `S` is an ancestor or root.
|
|
|
|
fn latest_slot(
|
|
|
|
&self,
|
|
|
|
ancestors: Option<&Ancestors>,
|
|
|
|
slice: SlotSlice<T>,
|
|
|
|
max_slot: Option<Slot>,
|
|
|
|
) -> Option<usize> {
|
|
|
|
let mut current_max = 0;
|
|
|
|
let max_slot = max_slot.unwrap_or(std::u64::MAX);
|
|
|
|
|
2019-04-15 17:15:50 -07:00
|
|
|
let mut rv = None;
|
2020-03-25 21:08:56 -07:00
|
|
|
for (i, (slot, _t)) in slice.iter().rev().enumerate() {
|
2020-09-28 16:04:46 -07:00
|
|
|
if *slot >= current_max
|
|
|
|
&& *slot <= max_slot
|
|
|
|
&& self.is_ancestor_or_root(ancestors, *slot)
|
2020-06-08 17:38:14 -07:00
|
|
|
{
|
2020-03-25 21:08:56 -07:00
|
|
|
rv = Some((slice.len() - 1) - i);
|
2020-09-28 16:04:46 -07:00
|
|
|
current_max = *slot;
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
|
|
|
}
|
2020-09-28 16:04:46 -07:00
|
|
|
|
2019-04-15 17:15:50 -07:00
|
|
|
rv
|
|
|
|
}
|
|
|
|
|
2020-09-28 16:04:46 -07:00
|
|
|
// Checks that the given slot is either:
|
|
|
|
// 1) in the `ancestors` set
|
|
|
|
// 2) or is a root
|
|
|
|
fn is_ancestor_or_root(&self, ancestors: Option<&Ancestors>, slot: Slot) -> bool {
|
|
|
|
ancestors.map_or(false, |ancestors| ancestors.contains_key(&slot)) || (self.is_root(slot))
|
|
|
|
}
|
|
|
|
|
2019-07-10 22:06:32 -07:00
|
|
|
/// Get an account
|
|
|
|
/// The latest account that appears in `ancestors` or `roots` is returned.
|
2020-06-08 17:38:14 -07:00
|
|
|
pub(crate) fn get(
|
2019-07-20 17:58:39 -07:00
|
|
|
&self,
|
|
|
|
pubkey: &Pubkey,
|
2020-06-08 17:38:14 -07:00
|
|
|
ancestors: Option<&Ancestors>,
|
2020-09-28 16:04:46 -07:00
|
|
|
max_root: Option<Slot>,
|
2020-03-23 08:50:23 -07:00
|
|
|
) -> Option<(RwLockReadGuard<SlotList<T>>, usize)> {
|
2019-07-20 17:58:39 -07:00
|
|
|
self.account_maps.get(pubkey).and_then(|list| {
|
2020-03-23 08:50:23 -07:00
|
|
|
let list_r = list.1.read().unwrap();
|
|
|
|
let lock = &list_r;
|
2020-09-28 16:04:46 -07:00
|
|
|
let found_index = self.latest_slot(ancestors, &lock, max_root)?;
|
2020-03-12 22:14:37 -07:00
|
|
|
Some((list_r, found_index))
|
2019-07-20 17:58:39 -07:00
|
|
|
})
|
2019-07-10 22:06:32 -07:00
|
|
|
}
|
|
|
|
|
2020-10-03 15:18:58 -07:00
|
|
|
// Get the maximum root <= `max_allowed_root` from the given `slice`
|
|
|
|
fn get_max_root(
|
|
|
|
roots: &HashSet<Slot>,
|
|
|
|
slice: SlotSlice<T>,
|
|
|
|
max_allowed_root: Option<Slot>,
|
|
|
|
) -> Slot {
|
2019-06-13 17:35:16 -07:00
|
|
|
let mut max_root = 0;
|
2020-03-25 21:08:56 -07:00
|
|
|
for (f, _) in slice.iter() {
|
2020-10-03 15:18:58 -07:00
|
|
|
if let Some(max_allowed_root) = max_allowed_root {
|
|
|
|
if *f > max_allowed_root {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2019-06-13 17:35:16 -07:00
|
|
|
if *f > max_root && roots.contains(f) {
|
|
|
|
max_root = *f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
max_root
|
|
|
|
}
|
|
|
|
|
2019-06-10 18:15:39 -07:00
|
|
|
pub fn insert(
|
|
|
|
&mut self,
|
2019-10-23 22:01:22 -07:00
|
|
|
slot: Slot,
|
2019-06-10 18:15:39 -07:00
|
|
|
pubkey: &Pubkey,
|
|
|
|
account_info: T,
|
2020-03-25 21:08:56 -07:00
|
|
|
reclaims: &mut SlotList<T>,
|
2019-06-10 18:15:39 -07:00
|
|
|
) {
|
2020-03-25 21:08:56 -07:00
|
|
|
self.account_maps
|
2019-06-10 18:15:39 -07:00
|
|
|
.entry(*pubkey)
|
2020-03-25 21:08:56 -07:00
|
|
|
.or_insert_with(|| (AtomicU64::new(0), RwLock::new(SlotList::with_capacity(32))));
|
2019-10-23 22:01:22 -07:00
|
|
|
self.update(slot, pubkey, account_info, reclaims);
|
2019-07-20 17:58:39 -07:00
|
|
|
}
|
2019-06-13 17:35:16 -07:00
|
|
|
|
2019-07-20 17:58:39 -07:00
|
|
|
// Try to update an item in account_maps. If the account is not
|
|
|
|
// already present, then the function will return back Some(account_info) which
|
|
|
|
// the caller can then take the write lock and do an 'insert' with the item.
|
|
|
|
// It returns None if the item is already present and thus successfully updated.
|
|
|
|
pub fn update(
|
|
|
|
&self,
|
2019-10-23 22:01:22 -07:00
|
|
|
slot: Slot,
|
2019-07-20 17:58:39 -07:00
|
|
|
pubkey: &Pubkey,
|
|
|
|
account_info: T,
|
2020-03-25 21:08:56 -07:00
|
|
|
reclaims: &mut SlotList<T>,
|
2019-07-20 17:58:39 -07:00
|
|
|
) -> Option<T> {
|
|
|
|
if let Some(lock) = self.account_maps.get(pubkey) {
|
2020-09-25 18:54:48 -07:00
|
|
|
let list = &mut lock.1.write().unwrap();
|
|
|
|
// filter out other dirty entries from the same slot
|
|
|
|
let mut same_slot_previous_updates: Vec<(usize, (Slot, &T))> = list
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.filter_map(|(i, (s, value))| {
|
|
|
|
if *s == slot {
|
|
|
|
Some((i, (*s, value)))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
assert!(same_slot_previous_updates.len() <= 1);
|
|
|
|
|
|
|
|
if let Some((list_index, (s, previous_update_value))) = same_slot_previous_updates.pop()
|
|
|
|
{
|
|
|
|
reclaims.push((s, previous_update_value.clone()));
|
|
|
|
list.remove(list_index);
|
|
|
|
} else {
|
|
|
|
// Only increment ref count if the account was not prevously updated in this slot
|
|
|
|
lock.0.fetch_add(1, Ordering::Relaxed);
|
|
|
|
}
|
2020-03-25 21:08:56 -07:00
|
|
|
list.push((slot, account_info));
|
2019-08-23 08:55:51 -07:00
|
|
|
None
|
2019-07-20 17:58:39 -07:00
|
|
|
} else {
|
2019-08-23 08:55:51 -07:00
|
|
|
Some(account_info)
|
2019-07-20 17:58:39 -07:00
|
|
|
}
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
2019-05-30 21:31:35 -07:00
|
|
|
|
2020-03-12 22:14:37 -07:00
|
|
|
pub fn unref_from_storage(&self, pubkey: &Pubkey) {
|
2020-03-25 21:08:56 -07:00
|
|
|
let locked_entry = self.account_maps.get(pubkey);
|
|
|
|
if let Some(entry) = locked_entry {
|
|
|
|
entry.0.fetch_sub(1, Ordering::Relaxed);
|
2020-03-12 22:14:37 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn ref_count_from_storage(&self, pubkey: &Pubkey) -> RefCount {
|
2020-03-25 21:08:56 -07:00
|
|
|
let locked_entry = self.account_maps.get(pubkey);
|
|
|
|
if let Some(entry) = locked_entry {
|
|
|
|
entry.0.load(Ordering::Relaxed)
|
2020-03-12 22:14:37 -07:00
|
|
|
} else {
|
|
|
|
0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-28 16:04:46 -07:00
|
|
|
fn purge_older_root_entries(
|
|
|
|
&self,
|
|
|
|
list: &mut SlotList<T>,
|
|
|
|
reclaims: &mut SlotList<T>,
|
|
|
|
max_clean_root: Option<Slot>,
|
|
|
|
) {
|
2020-03-02 21:57:25 -08:00
|
|
|
let roots = &self.roots;
|
|
|
|
|
2020-10-03 15:18:58 -07:00
|
|
|
let max_root = Self::get_max_root(roots, &list, max_clean_root);
|
2020-03-02 21:57:25 -08:00
|
|
|
|
|
|
|
reclaims.extend(
|
2020-03-25 21:08:56 -07:00
|
|
|
list.iter()
|
2020-03-02 21:57:25 -08:00
|
|
|
.filter(|(slot, _)| Self::can_purge(max_root, *slot))
|
|
|
|
.cloned(),
|
|
|
|
);
|
2020-03-25 21:08:56 -07:00
|
|
|
list.retain(|(slot, _)| !Self::can_purge(max_root, *slot));
|
2020-03-02 21:57:25 -08:00
|
|
|
}
|
|
|
|
|
2020-09-28 16:04:46 -07:00
|
|
|
pub fn clean_rooted_entries(
|
|
|
|
&self,
|
|
|
|
pubkey: &Pubkey,
|
|
|
|
reclaims: &mut SlotList<T>,
|
|
|
|
max_clean_root: Option<Slot>,
|
|
|
|
) {
|
2020-03-25 21:08:56 -07:00
|
|
|
if let Some(locked_entry) = self.account_maps.get(pubkey) {
|
|
|
|
let mut list = locked_entry.1.write().unwrap();
|
2020-09-28 16:04:46 -07:00
|
|
|
self.purge_older_root_entries(&mut list, reclaims, max_clean_root);
|
2020-03-02 21:57:25 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-05 14:07:21 -07:00
|
|
|
pub fn clean_unrooted_entries_by_slot(
|
|
|
|
&self,
|
|
|
|
purge_slot: Slot,
|
|
|
|
pubkey: &Pubkey,
|
|
|
|
reclaims: &mut SlotList<T>,
|
|
|
|
) {
|
|
|
|
if let Some(entry) = self.account_maps.get(pubkey) {
|
|
|
|
let mut list = entry.1.write().unwrap();
|
|
|
|
list.retain(|(slot, entry)| {
|
|
|
|
if *slot == purge_slot {
|
|
|
|
reclaims.push((*slot, entry.clone()));
|
|
|
|
}
|
|
|
|
*slot != purge_slot
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-23 22:01:22 -07:00
|
|
|
pub fn add_index(&mut self, slot: Slot, pubkey: &Pubkey, account_info: T) {
|
2019-07-20 17:58:39 -07:00
|
|
|
let entry = self
|
|
|
|
.account_maps
|
|
|
|
.entry(*pubkey)
|
2020-03-23 08:50:23 -07:00
|
|
|
.or_insert_with(|| (AtomicU64::new(1), RwLock::new(vec![])));
|
|
|
|
entry.1.write().unwrap().push((slot, account_info));
|
2019-05-30 21:31:35 -07:00
|
|
|
}
|
|
|
|
|
2019-10-23 22:01:22 -07:00
|
|
|
pub fn can_purge(max_root: Slot, slot: Slot) -> bool {
|
|
|
|
slot < max_root
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
2019-06-10 18:15:39 -07:00
|
|
|
|
2019-10-23 22:01:22 -07:00
|
|
|
pub fn is_root(&self, slot: Slot) -> bool {
|
|
|
|
self.roots.contains(&slot)
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
2019-06-10 18:15:39 -07:00
|
|
|
|
2019-10-23 22:01:22 -07:00
|
|
|
pub fn add_root(&mut self, slot: Slot) {
|
|
|
|
self.roots.insert(slot);
|
2020-03-02 21:57:25 -08:00
|
|
|
self.uncleaned_roots.insert(slot);
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
2019-10-23 22:01:22 -07:00
|
|
|
/// Remove the slot when the storage for the slot is freed
|
|
|
|
/// Accounts no longer reference this slot.
|
2020-03-02 21:57:25 -08:00
|
|
|
pub fn clean_dead_slot(&mut self, slot: Slot) {
|
2019-10-23 22:01:22 -07:00
|
|
|
self.roots.remove(&slot);
|
2020-03-02 21:57:25 -08:00
|
|
|
self.uncleaned_roots.remove(&slot);
|
2020-06-11 22:51:43 -07:00
|
|
|
self.previous_uncleaned_roots.remove(&slot);
|
|
|
|
}
|
|
|
|
|
2020-09-28 16:04:46 -07:00
|
|
|
pub fn reset_uncleaned_roots(&mut self, max_clean_root: Option<Slot>) -> HashSet<Slot> {
|
|
|
|
let mut cleaned_roots = HashSet::new();
|
|
|
|
self.uncleaned_roots.retain(|root| {
|
|
|
|
let is_cleaned = max_clean_root
|
|
|
|
.map(|max_clean_root| *root <= max_clean_root)
|
|
|
|
.unwrap_or(true);
|
|
|
|
if is_cleaned {
|
|
|
|
cleaned_roots.insert(*root);
|
|
|
|
}
|
|
|
|
// Only keep the slots that have yet to be cleaned
|
|
|
|
!is_cleaned
|
|
|
|
});
|
|
|
|
std::mem::replace(&mut self.previous_uncleaned_roots, cleaned_roots)
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2020-02-20 13:28:55 -08:00
|
|
|
use solana_sdk::signature::{Keypair, Signer};
|
2019-04-15 17:15:50 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_get_empty() {
|
|
|
|
let key = Keypair::new();
|
|
|
|
let index = AccountsIndex::<bool>::default();
|
2019-07-10 22:06:32 -07:00
|
|
|
let ancestors = HashMap::new();
|
2020-09-28 16:04:46 -07:00
|
|
|
assert!(index.get(&key.pubkey(), Some(&ancestors), None).is_none());
|
|
|
|
assert!(index.get(&key.pubkey(), None, None).is_none());
|
2019-07-10 22:06:32 -07:00
|
|
|
|
|
|
|
let mut num = 0;
|
|
|
|
index.scan_accounts(&ancestors, |_pubkey, _index| num += 1);
|
|
|
|
assert_eq!(num, 0);
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_insert_no_ancestors() {
|
|
|
|
let key = Keypair::new();
|
|
|
|
let mut index = AccountsIndex::<bool>::default();
|
2019-06-10 18:15:39 -07:00
|
|
|
let mut gc = Vec::new();
|
|
|
|
index.insert(0, &key.pubkey(), true, &mut gc);
|
2019-04-15 17:15:50 -07:00
|
|
|
assert!(gc.is_empty());
|
|
|
|
|
2019-07-10 22:06:32 -07:00
|
|
|
let ancestors = HashMap::new();
|
2020-09-28 16:04:46 -07:00
|
|
|
assert!(index.get(&key.pubkey(), Some(&ancestors), None).is_none());
|
|
|
|
assert!(index.get(&key.pubkey(), None, None).is_none());
|
2019-07-10 22:06:32 -07:00
|
|
|
|
|
|
|
let mut num = 0;
|
|
|
|
index.scan_accounts(&ancestors, |_pubkey, _index| num += 1);
|
|
|
|
assert_eq!(num, 0);
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_insert_wrong_ancestors() {
|
|
|
|
let key = Keypair::new();
|
|
|
|
let mut index = AccountsIndex::<bool>::default();
|
2019-06-10 18:15:39 -07:00
|
|
|
let mut gc = Vec::new();
|
|
|
|
index.insert(0, &key.pubkey(), true, &mut gc);
|
2019-04-15 17:15:50 -07:00
|
|
|
assert!(gc.is_empty());
|
|
|
|
|
|
|
|
let ancestors = vec![(1, 1)].into_iter().collect();
|
2020-09-28 16:04:46 -07:00
|
|
|
assert!(index.get(&key.pubkey(), Some(&ancestors), None).is_none());
|
2019-07-10 22:06:32 -07:00
|
|
|
|
|
|
|
let mut num = 0;
|
|
|
|
index.scan_accounts(&ancestors, |_pubkey, _index| num += 1);
|
|
|
|
assert_eq!(num, 0);
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_insert_with_ancestors() {
|
|
|
|
let key = Keypair::new();
|
|
|
|
let mut index = AccountsIndex::<bool>::default();
|
2019-06-10 18:15:39 -07:00
|
|
|
let mut gc = Vec::new();
|
|
|
|
index.insert(0, &key.pubkey(), true, &mut gc);
|
2019-04-15 17:15:50 -07:00
|
|
|
assert!(gc.is_empty());
|
|
|
|
|
|
|
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
2020-09-28 16:04:46 -07:00
|
|
|
let (list, idx) = index.get(&key.pubkey(), Some(&ancestors), None).unwrap();
|
2020-03-23 08:50:23 -07:00
|
|
|
assert_eq!(list[idx], (0, true));
|
2019-07-10 22:06:32 -07:00
|
|
|
|
|
|
|
let mut num = 0;
|
|
|
|
let mut found_key = false;
|
|
|
|
index.scan_accounts(&ancestors, |pubkey, _index| {
|
|
|
|
if pubkey == &key.pubkey() {
|
|
|
|
found_key = true
|
|
|
|
};
|
|
|
|
num += 1
|
|
|
|
});
|
|
|
|
assert_eq!(num, 1);
|
|
|
|
assert!(found_key);
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_is_root() {
|
|
|
|
let mut index = AccountsIndex::<bool>::default();
|
|
|
|
assert!(!index.is_root(0));
|
|
|
|
index.add_root(0);
|
|
|
|
assert!(index.is_root(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_insert_with_root() {
|
|
|
|
let key = Keypair::new();
|
|
|
|
let mut index = AccountsIndex::<bool>::default();
|
2019-06-10 18:15:39 -07:00
|
|
|
let mut gc = Vec::new();
|
|
|
|
index.insert(0, &key.pubkey(), true, &mut gc);
|
2019-04-15 17:15:50 -07:00
|
|
|
assert!(gc.is_empty());
|
|
|
|
|
|
|
|
index.add_root(0);
|
2020-09-28 16:04:46 -07:00
|
|
|
let (list, idx) = index.get(&key.pubkey(), None, None).unwrap();
|
2020-03-23 08:50:23 -07:00
|
|
|
assert_eq!(list[idx], (0, true));
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
|
|
|
|
2019-04-28 10:27:37 -07:00
|
|
|
#[test]
|
2020-03-02 21:57:25 -08:00
|
|
|
fn test_clean_first() {
|
2019-04-28 10:27:37 -07:00
|
|
|
let mut index = AccountsIndex::<bool>::default();
|
|
|
|
index.add_root(0);
|
|
|
|
index.add_root(1);
|
2020-03-02 21:57:25 -08:00
|
|
|
index.clean_dead_slot(0);
|
2019-04-15 17:15:50 -07:00
|
|
|
assert!(index.is_root(1));
|
|
|
|
assert!(!index.is_root(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-03-02 21:57:25 -08:00
|
|
|
fn test_clean_last() {
|
2019-10-23 22:01:22 -07:00
|
|
|
//this behavior might be undefined, clean up should only occur on older slots
|
2019-04-15 17:15:50 -07:00
|
|
|
let mut index = AccountsIndex::<bool>::default();
|
|
|
|
index.add_root(0);
|
2019-04-28 10:27:37 -07:00
|
|
|
index.add_root(1);
|
2020-03-02 21:57:25 -08:00
|
|
|
index.clean_dead_slot(1);
|
2019-04-15 17:15:50 -07:00
|
|
|
assert!(!index.is_root(1));
|
|
|
|
assert!(index.is_root(0));
|
|
|
|
}
|
|
|
|
|
2020-03-02 21:57:25 -08:00
|
|
|
#[test]
|
|
|
|
fn test_clean_and_unclean_slot() {
|
|
|
|
let mut index = AccountsIndex::<bool>::default();
|
|
|
|
assert_eq!(0, index.uncleaned_roots.len());
|
2020-06-11 22:51:43 -07:00
|
|
|
index.add_root(0);
|
2020-03-02 21:57:25 -08:00
|
|
|
index.add_root(1);
|
2020-06-11 22:51:43 -07:00
|
|
|
assert_eq!(2, index.uncleaned_roots.len());
|
|
|
|
|
|
|
|
assert_eq!(0, index.previous_uncleaned_roots.len());
|
2020-09-28 16:04:46 -07:00
|
|
|
index.reset_uncleaned_roots(None);
|
2020-06-11 22:51:43 -07:00
|
|
|
assert_eq!(2, index.roots.len());
|
2020-03-02 21:57:25 -08:00
|
|
|
assert_eq!(0, index.uncleaned_roots.len());
|
2020-06-11 22:51:43 -07:00
|
|
|
assert_eq!(2, index.previous_uncleaned_roots.len());
|
|
|
|
|
|
|
|
index.add_root(2);
|
|
|
|
index.add_root(3);
|
|
|
|
assert_eq!(4, index.roots.len());
|
|
|
|
assert_eq!(2, index.uncleaned_roots.len());
|
|
|
|
assert_eq!(2, index.previous_uncleaned_roots.len());
|
|
|
|
|
|
|
|
index.clean_dead_slot(1);
|
|
|
|
assert_eq!(3, index.roots.len());
|
|
|
|
assert_eq!(2, index.uncleaned_roots.len());
|
|
|
|
assert_eq!(1, index.previous_uncleaned_roots.len());
|
|
|
|
|
|
|
|
index.clean_dead_slot(2);
|
|
|
|
assert_eq!(2, index.roots.len());
|
|
|
|
assert_eq!(1, index.uncleaned_roots.len());
|
|
|
|
assert_eq!(1, index.previous_uncleaned_roots.len());
|
2020-03-02 21:57:25 -08:00
|
|
|
}
|
|
|
|
|
2019-04-15 17:15:50 -07:00
|
|
|
#[test]
|
|
|
|
fn test_update_last_wins() {
|
|
|
|
let key = Keypair::new();
|
|
|
|
let mut index = AccountsIndex::<bool>::default();
|
|
|
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
2019-06-10 18:15:39 -07:00
|
|
|
let mut gc = Vec::new();
|
|
|
|
index.insert(0, &key.pubkey(), true, &mut gc);
|
2019-04-15 17:15:50 -07:00
|
|
|
assert!(gc.is_empty());
|
2020-09-28 16:04:46 -07:00
|
|
|
let (list, idx) = index.get(&key.pubkey(), Some(&ancestors), None).unwrap();
|
2020-03-23 08:50:23 -07:00
|
|
|
assert_eq!(list[idx], (0, true));
|
2019-07-20 17:58:39 -07:00
|
|
|
drop(list);
|
2019-04-15 17:15:50 -07:00
|
|
|
|
2019-06-10 18:15:39 -07:00
|
|
|
let mut gc = Vec::new();
|
|
|
|
index.insert(0, &key.pubkey(), false, &mut gc);
|
2019-04-15 17:15:50 -07:00
|
|
|
assert_eq!(gc, vec![(0, true)]);
|
2020-09-28 16:04:46 -07:00
|
|
|
let (list, idx) = index.get(&key.pubkey(), Some(&ancestors), None).unwrap();
|
2020-03-23 08:50:23 -07:00
|
|
|
assert_eq!(list[idx], (0, false));
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2019-10-23 22:01:22 -07:00
|
|
|
fn test_update_new_slot() {
|
2019-07-20 17:58:39 -07:00
|
|
|
solana_logger::setup();
|
2019-04-15 17:15:50 -07:00
|
|
|
let key = Keypair::new();
|
|
|
|
let mut index = AccountsIndex::<bool>::default();
|
|
|
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
2019-06-10 18:15:39 -07:00
|
|
|
let mut gc = Vec::new();
|
|
|
|
index.insert(0, &key.pubkey(), true, &mut gc);
|
2019-04-15 17:15:50 -07:00
|
|
|
assert!(gc.is_empty());
|
2019-06-10 18:15:39 -07:00
|
|
|
index.insert(1, &key.pubkey(), false, &mut gc);
|
2019-04-15 17:15:50 -07:00
|
|
|
assert!(gc.is_empty());
|
2020-09-28 16:04:46 -07:00
|
|
|
let (list, idx) = index.get(&key.pubkey(), Some(&ancestors), None).unwrap();
|
2020-03-23 08:50:23 -07:00
|
|
|
assert_eq!(list[idx], (0, true));
|
2019-04-15 17:15:50 -07:00
|
|
|
let ancestors = vec![(1, 0)].into_iter().collect();
|
2020-09-28 16:04:46 -07:00
|
|
|
let (list, idx) = index.get(&key.pubkey(), Some(&ancestors), None).unwrap();
|
2020-03-23 08:50:23 -07:00
|
|
|
assert_eq!(list[idx], (1, false));
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2019-10-23 22:01:22 -07:00
|
|
|
fn test_update_gc_purged_slot() {
|
2019-04-15 17:15:50 -07:00
|
|
|
let key = Keypair::new();
|
|
|
|
let mut index = AccountsIndex::<bool>::default();
|
2019-06-10 18:15:39 -07:00
|
|
|
let mut gc = Vec::new();
|
|
|
|
index.insert(0, &key.pubkey(), true, &mut gc);
|
2019-04-15 17:15:50 -07:00
|
|
|
assert!(gc.is_empty());
|
2019-06-10 18:15:39 -07:00
|
|
|
index.insert(1, &key.pubkey(), false, &mut gc);
|
2019-06-13 17:35:16 -07:00
|
|
|
index.insert(2, &key.pubkey(), true, &mut gc);
|
|
|
|
index.insert(3, &key.pubkey(), true, &mut gc);
|
|
|
|
index.add_root(0);
|
|
|
|
index.add_root(1);
|
|
|
|
index.add_root(3);
|
|
|
|
index.insert(4, &key.pubkey(), true, &mut gc);
|
2020-09-28 16:04:46 -07:00
|
|
|
|
|
|
|
// Updating index should not purge older roots, only purges
|
|
|
|
// previous updates within the same slot
|
|
|
|
assert_eq!(gc, vec![]);
|
|
|
|
let (list, idx) = index.get(&key.pubkey(), None, None).unwrap();
|
2020-03-23 08:50:23 -07:00
|
|
|
assert_eq!(list[idx], (3, true));
|
2019-07-10 22:06:32 -07:00
|
|
|
|
|
|
|
let mut num = 0;
|
|
|
|
let mut found_key = false;
|
2020-06-08 17:38:14 -07:00
|
|
|
index.scan_accounts(&Ancestors::new(), |pubkey, _index| {
|
2019-07-10 22:06:32 -07:00
|
|
|
if pubkey == &key.pubkey() {
|
|
|
|
found_key = true;
|
|
|
|
assert_eq!(_index, (&true, 3));
|
|
|
|
};
|
|
|
|
num += 1
|
|
|
|
});
|
|
|
|
assert_eq!(num, 1);
|
|
|
|
assert!(found_key);
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|
2019-12-11 11:11:31 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_purge() {
|
|
|
|
let key = Keypair::new();
|
|
|
|
let mut index = AccountsIndex::<u64>::default();
|
|
|
|
let mut gc = Vec::new();
|
|
|
|
assert_eq!(Some(12), index.update(1, &key.pubkey(), 12, &mut gc));
|
|
|
|
|
|
|
|
index.insert(1, &key.pubkey(), 12, &mut gc);
|
|
|
|
|
|
|
|
assert_eq!(None, index.update(1, &key.pubkey(), 10, &mut gc));
|
|
|
|
|
|
|
|
let purges = index.purge(&key.pubkey());
|
|
|
|
assert_eq!(purges, (vec![], false));
|
|
|
|
index.add_root(1);
|
|
|
|
|
|
|
|
let purges = index.purge(&key.pubkey());
|
|
|
|
assert_eq!(purges, (vec![(1, 10)], true));
|
|
|
|
|
|
|
|
assert_eq!(None, index.update(1, &key.pubkey(), 9, &mut gc));
|
|
|
|
}
|
2020-09-28 16:04:46 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_latest_slot() {
|
|
|
|
let slot_slice = vec![(0, true), (5, true), (3, true), (7, true)];
|
|
|
|
let mut index = AccountsIndex::<bool>::default();
|
|
|
|
|
|
|
|
// No ancestors, no root, should return None
|
|
|
|
assert!(index.latest_slot(None, &slot_slice, None).is_none());
|
|
|
|
|
|
|
|
// Given a root, should return the root
|
|
|
|
index.add_root(5);
|
|
|
|
assert_eq!(index.latest_slot(None, &slot_slice, None).unwrap(), 1);
|
|
|
|
|
|
|
|
// Given a maximum -= root, should still return the root
|
|
|
|
assert_eq!(index.latest_slot(None, &slot_slice, Some(5)).unwrap(), 1);
|
|
|
|
|
|
|
|
// Given a maximum < root, should filter out the root
|
|
|
|
assert!(index.latest_slot(None, &slot_slice, Some(4)).is_none());
|
|
|
|
|
|
|
|
// Given a maximum, should filter out the ancestors > maximum
|
|
|
|
let ancestors: HashMap<Slot, usize> = vec![(3, 1), (7, 1)].into_iter().collect();
|
|
|
|
assert_eq!(
|
|
|
|
index
|
|
|
|
.latest_slot(Some(&ancestors), &slot_slice, Some(4))
|
|
|
|
.unwrap(),
|
|
|
|
2
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
index
|
|
|
|
.latest_slot(Some(&ancestors), &slot_slice, Some(7))
|
|
|
|
.unwrap(),
|
|
|
|
3
|
|
|
|
);
|
|
|
|
|
|
|
|
// Given no maximum, should just return the greatest ancestor or root
|
|
|
|
assert_eq!(
|
|
|
|
index
|
|
|
|
.latest_slot(Some(&ancestors), &slot_slice, None)
|
|
|
|
.unwrap(),
|
|
|
|
3
|
|
|
|
);
|
|
|
|
|
|
|
|
// Because the given maximum `m == root`, ancestors > root
|
|
|
|
assert_eq!(
|
|
|
|
index
|
|
|
|
.latest_slot(Some(&ancestors), &slot_slice, Some(5))
|
|
|
|
.unwrap(),
|
|
|
|
1
|
|
|
|
);
|
|
|
|
}
|
2020-10-03 15:18:58 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_purge_older_root_entries() {
|
|
|
|
// No roots, should be no reclaims
|
|
|
|
let mut index = AccountsIndex::<bool>::default();
|
|
|
|
let mut slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
|
|
|
|
let mut reclaims = vec![];
|
|
|
|
index.purge_older_root_entries(&mut slot_list, &mut reclaims, None);
|
|
|
|
assert!(reclaims.is_empty());
|
|
|
|
assert_eq!(slot_list, vec![(1, true), (2, true), (5, true), (9, true)]);
|
|
|
|
|
|
|
|
// Add a later root, earlier slots should be reclaimed
|
|
|
|
slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
|
|
|
|
index.add_root(1);
|
|
|
|
// Note 2 is not a root
|
|
|
|
index.add_root(5);
|
|
|
|
reclaims = vec![];
|
|
|
|
index.purge_older_root_entries(&mut slot_list, &mut reclaims, None);
|
|
|
|
assert_eq!(reclaims, vec![(1, true), (2, true)]);
|
|
|
|
assert_eq!(slot_list, vec![(5, true), (9, true)]);
|
|
|
|
|
|
|
|
// Add a later root that is not in the list, should not affect the outcome
|
|
|
|
slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
|
|
|
|
index.add_root(6);
|
|
|
|
reclaims = vec![];
|
|
|
|
index.purge_older_root_entries(&mut slot_list, &mut reclaims, None);
|
|
|
|
assert_eq!(reclaims, vec![(1, true), (2, true)]);
|
|
|
|
assert_eq!(slot_list, vec![(5, true), (9, true)]);
|
|
|
|
|
|
|
|
// Pass a max root >= than any root in the slot list, should not affect
|
|
|
|
// outcome
|
|
|
|
slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
|
|
|
|
reclaims = vec![];
|
|
|
|
index.purge_older_root_entries(&mut slot_list, &mut reclaims, Some(6));
|
|
|
|
assert_eq!(reclaims, vec![(1, true), (2, true)]);
|
|
|
|
assert_eq!(slot_list, vec![(5, true), (9, true)]);
|
|
|
|
|
|
|
|
// Pass a max root, earlier slots should be reclaimed
|
|
|
|
slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
|
|
|
|
reclaims = vec![];
|
|
|
|
index.purge_older_root_entries(&mut slot_list, &mut reclaims, Some(5));
|
|
|
|
assert_eq!(reclaims, vec![(1, true), (2, true)]);
|
|
|
|
assert_eq!(slot_list, vec![(5, true), (9, true)]);
|
|
|
|
|
|
|
|
// Pass a max root 2. This means the latest root < 2 is 1 because 2 is not a root
|
|
|
|
// so nothing will be purged
|
|
|
|
slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
|
|
|
|
reclaims = vec![];
|
|
|
|
index.purge_older_root_entries(&mut slot_list, &mut reclaims, Some(2));
|
|
|
|
assert!(reclaims.is_empty());
|
|
|
|
assert_eq!(slot_list, vec![(1, true), (2, true), (5, true), (9, true)]);
|
|
|
|
|
|
|
|
// Pass a max root 1. This means the latest root < 3 is 1 because 2 is not a root
|
|
|
|
// so nothing will be purged
|
|
|
|
slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
|
|
|
|
reclaims = vec![];
|
|
|
|
index.purge_older_root_entries(&mut slot_list, &mut reclaims, Some(1));
|
|
|
|
assert!(reclaims.is_empty());
|
|
|
|
assert_eq!(slot_list, vec![(1, true), (2, true), (5, true), (9, true)]);
|
|
|
|
|
|
|
|
// Pass a max root that doesn't exist in the list but is greater than
|
|
|
|
// some of the roots in the list, shouldn't return those smaller roots
|
|
|
|
slot_list = vec![(1, true), (2, true), (5, true), (9, true)];
|
|
|
|
reclaims = vec![];
|
|
|
|
index.purge_older_root_entries(&mut slot_list, &mut reclaims, Some(7));
|
|
|
|
assert_eq!(reclaims, vec![(1, true), (2, true)]);
|
|
|
|
assert_eq!(slot_list, vec![(5, true), (9, true)]);
|
|
|
|
}
|
2019-04-15 17:15:50 -07:00
|
|
|
}
|