@@ -25,7 +25,7 @@ use std::{
},
sync::{
atomic::{AtomicU64, Ordering},
Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard,
Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard,
},
};
use thiserror::Error;
@@ -38,8 +38,6 @@ pub type SlotSlice<'s, T> = &'s [(Slot, T)];
pub type RefCount = u64;
pub type AccountMap<K, V> = BTreeMap<K, V>;

type AccountMapEntry<T> = Arc<AccountMapEntryInner<T>>;

pub trait IsCached {
fn is_cached(&self) -> bool;
}
@@ -109,46 +107,71 @@ impl AccountSecondaryIndexes {
}

#[derive(Debug)]
pub struct AccountMapEntryInner<T> {
pub struct AccountMapEntry<T> {
ref_count: AtomicU64,
pub slot_list: RwLock<SlotList<T>>,
pub slot_list: SlotList<T>,
}

impl<T> AccountMapEntryInner<T> {
impl<T: Clone> AccountMapEntry<T> {
pub fn ref_count(&self) -> u64 {
self.ref_count.load(Ordering::Relaxed)
}
}

pub enum AccountIndexGetResult<'a, T: 'static> {
Found(ReadAccountMapEntry<T>, usize),
Found(ReadAccountMapEntry<'a, T>, usize),
NotFoundOnFork,
Missing(AccountMapsReadLock<'a, T>),
}

pub enum AccountIndexGetResultInternal<'a, T: 'static> {
Found(ReadAccountMapEntry<'a, T>),
Missing(AccountMapsReadLock<'a, T>),
}

#[self_referencing]
pub struct ReadAccountMapEntry<T: 'static> {
owned_entry: AccountMapEntry<T>,
#[borrows(owned_entry)]
pub struct ReadAccountMapEntry<'a, T: 'static> {
lock: AccountMapsReadLock<'a, T>,
pubkey: &'a Pubkey,
#[borrows(lock, pubkey)]
#[covariant]
slot_list_guard: RwLockReadGuard<'this, SlotList<T>>,
owned_entry: Option<&'this AccountMapEntry<T>>,
}

impl<T: Clone> ReadAccountMapEntry<T> {
pub fn from_account_map_entry(account_map_entry: AccountMapEntry<T>) -> Self {
ReadAccountMapEntryBuilder {
owned_entry: account_map_entry,
slot_list_guard_builder: |lock| lock.slot_list.read().unwrap(),
impl<'a, T: Clone> ReadAccountMapEntry<'a, T> {
pub fn new_with_lock(
pubkey: &'a Pubkey,
lock: AccountMapsReadLock<'a, T>,
) -> AccountIndexGetResultInternal<'a, T> {
let result = ReadAccountMapEntryBuilder {
lock,
pubkey,
owned_entry_builder: |lock, pubkey| lock.get(pubkey),
}
.build();
let exists = result.borrow_owned_entry().is_some();
if exists {
AccountIndexGetResultInternal::Found(result)
} else {
AccountIndexGetResultInternal::Missing(result.destroy())
}
.build()
}

pub fn destroy(self) -> AccountMapsReadLock<'a, T> {
let lock = Self::into_heads(self);
lock.lock
}

fn get(&self) -> &AccountMapEntry<T> {
self.borrow_owned_entry().unwrap()
}

pub fn slot_list(&self) -> &SlotList<T> {
&*self.borrow_slot_list_guard()
&self.get().slot_list
}

pub fn ref_count(&self) -> &AtomicU64 {
&self.borrow_owned_entry().ref_count
&self.get().ref_count
}

pub fn unref(&self) {
@@ -161,35 +184,99 @@ impl<T: Clone> ReadAccountMapEntry<T> {
}

#[self_referencing]
pub struct WriteAccountMapEntry<T: 'static> {
owned_entry: AccountMapEntry<T>,
#[borrows(owned_entry)]
pub struct WriteAccountMapEntry<'a, 'b: 'a, T: 'static> {
lock: AccountMapsWriteLock<'b, T>,
pubkey: &'a Pubkey,
#[borrows(mut lock, pubkey)]
#[covariant]
slot_list_guard: RwLockWriteGuard<'this, SlotList<T>>,
owned_entry: (
Option<std::collections::btree_map::OccupiedEntry<'this, Pubkey, AccountMapEntry<T>>>,
Option<std::collections::btree_map::VacantEntry<'this, Pubkey, AccountMapEntry<T>>>,
),
}

impl<T: 'static + Clone + IsCached> WriteAccountMapEntry<T> {
pub fn from_account_map_entry(account_map_entry: AccountMapEntry<T>) -> Self {
WriteAccountMapEntryBuilder {
owned_entry: account_map_entry,
slot_list_guard_builder: |lock| lock.slot_list.write().unwrap(),
impl<'a, 'b: 'a, T: 'static + Clone + IsCached> WriteAccountMapEntry<'a, 'b, T> {
pub fn new_with_lock(
pubkey: &'a Pubkey,
lock: AccountMapsWriteLock<'b, T>,
allow_vacant: bool,
) -> Option<Self> {
let result = WriteAccountMapEntryBuilder {
lock,
pubkey,
owned_entry_builder: |lock, pubkey| match lock.entry(**pubkey) {
Entry::Occupied(occupied) => (Some(occupied), None),
Entry::Vacant(vacant) => (None, Some(vacant)),
},
}
.build();
if allow_vacant || result.is_occupied() {
Some(result)
} else {
None
}
.build()
}

pub fn destroy(self) -> AccountMapsWriteLock<'b, T> {
let lock = Self::into_heads(self);
lock.lock
}

pub fn pubkey(&self) -> &Pubkey {
self.borrow_pubkey()
}

pub fn is_occupied(&self) -> bool {
// rename to is_occupied
let eg = &self.borrow_owned_entry();
eg.0.is_some()
}

pub fn insert(mut self, new_entry: AccountMapEntry<T>) -> AccountMapsWriteLock<'b, T> {
assert!(!self.is_occupied());
self.with_mut(|fields| {
let f = fields.owned_entry;
let vacant = f.1.take().unwrap();

vacant.insert(new_entry);
});
self.destroy()
}

pub fn get(
&'a self,
) -> Option<&'a std::collections::btree_map::OccupiedEntry<'a, Pubkey, AccountMapEntry<T>>>
{
self.borrow_owned_entry().0.as_ref()
}

pub fn slot_list(&mut self) -> &SlotList<T> {
&*self.borrow_slot_list_guard()
&self.get().unwrap().get().slot_list
}

pub fn slot_list_mut<RT>(
&mut self,
user: impl for<'this> FnOnce(&mut RwLockWriteGuard<'this, SlotList<T>>) -> RT,
) -> RT {
self.with_slot_list_guard_mut(user)
user: impl for<'this> FnOnce(&mut SlotList<T>) -> RT,
) -> Option<RT> {
self.with_mut(|fields| {
let f = fields.owned_entry;
f.0.as_mut().map(|entry| {
let x = entry.get_mut();
user(&mut x.slot_list)
})
})
}

pub fn ref_count(&self) -> &AtomicU64 {
&self.borrow_owned_entry().ref_count
&self.get().unwrap().get().ref_count
}

pub fn unref(&self) {
self.ref_count().fetch_sub(1, Ordering::Relaxed);
}

pub fn addref(&self) {
self.ref_count().fetch_add(1, Ordering::Relaxed);
}

// create an entry that is equivalent to this process:
@@ -198,10 +285,10 @@ impl<T: 'static + Clone + IsCached> WriteAccountMapEntry<T> {
// This code is called when the first entry [ie. (slot,account_info)] for a pubkey is inserted into the index.
pub fn new_entry_after_update(slot: Slot, account_info: T) -> AccountMapEntry<T> {
let ref_count = if account_info.is_cached() { 0 } else { 1 };
Arc::new(AccountMapEntryInner {
AccountMapEntry {
ref_count: AtomicU64::new(ref_count),
slot_list: RwLock::new(vec![(slot, account_info)]),
})
slot_list: vec![(slot, account_info)],
}
}

// Try to update an item in the slot list the given `slot` If an item for the slot
@@ -571,22 +658,22 @@ impl<'a, T> AccountsIndexIterator<'a, T> {
}

impl<'a, T: 'static + Clone> Iterator for AccountsIndexIterator<'a, T> {
type Item = Vec<(Pubkey, AccountMapEntry<T>)>;
type Item = Vec<Pubkey>;
fn next(&mut self) -> Option<Self::Item> {
if self.is_finished {
return None;
}

let start_bin = self.start_bin();
let mut chunk: Vec<(Pubkey, AccountMapEntry<T>)> = Vec::with_capacity(ITER_BATCH_SIZE);
let mut chunk: Vec<Pubkey> = Vec::with_capacity(ITER_BATCH_SIZE);
'outer: for i in self.account_maps.iter().skip(start_bin) {
for (pubkey, account_map_entry) in
for (pubkey, _account_map_entry) in
i.read().unwrap().range((self.start_bound, self.end_bound))
{
if chunk.len() >= ITER_BATCH_SIZE {
break 'outer;
}
let item = (*pubkey, account_map_entry.clone());
let item = *pubkey;
chunk.push(item);
}
}
@@ -596,7 +683,7 @@ impl<'a, T: 'static + Clone> Iterator for AccountsIndexIterator<'a, T> {
return None;
}

self.start_bound = Excluded(chunk.last().unwrap().0);
self.start_bound = Excluded(*chunk.last().unwrap());
Some(chunk)
}
}
@@ -935,29 +1022,34 @@ impl<
// instead of scanning the entire range
let mut total_elapsed_timer = Measure::start("total");
let mut num_keys_iterated = 0;
let mut latest_slot_elapsed = 0;
let mut load_account_elapsed = 0;
let mut read_lock_elapsed = 0;
let mut iterator_elapsed = 0;
let mut iterator_timer = Measure::start("iterator_elapsed");
for pubkey_list in self.iter(range) {
iterator_timer.stop();
iterator_elapsed += iterator_timer.as_us();
for (pubkey, list) in pubkey_list {
for pubkey in pubkey_list {
num_keys_iterated += 1;
let mut read_lock_timer = Measure::start("read_lock");
let list_r = &list.slot_list.read().unwrap();
let list_r = self.get(&pubkey, Some(ancestors), max_root);
read_lock_timer.stop();
read_lock_elapsed += read_lock_timer.as_us();
let mut latest_slot_timer = Measure::start("latest_slot");
if let Some(index) = self.latest_slot(Some(ancestors), list_r, max_root) {
latest_slot_timer.stop();
latest_slot_elapsed += latest_slot_timer.as_us();
let mut load_account_timer = Measure::start("load_account");
func(&pubkey, (&list_r[index].1, list_r[index].0));
load_account_timer.stop();
load_account_elapsed += load_account_timer.as_us();
let result;
if let AccountIndexGetResult::Found(locked_entry, index) = &list_r {
let slot_list = locked_entry.slot_list();
result = Some(slot_list[*index].clone())
} else {
continue;
}
drop(list_r);
let result = result.unwrap();

let mut load_account_timer = Measure::start("load_account");
//let list_item = &slot_list[index];
func(&pubkey, (&result.1, result.0));
load_account_timer.stop();
load_account_elapsed += load_account_timer.as_us();
}
iterator_timer = Measure::start("iterator_elapsed");
}
@@ -967,7 +1059,6 @@ impl<
datapoint_info!(
metric_name,
("total_elapsed", total_elapsed_timer.as_us(), i64),
("latest_slot_elapsed", latest_slot_elapsed, i64),
("read_lock_elapsed", read_lock_elapsed, i64),
("load_account_elapsed", load_account_elapsed, i64),
("iterator_elapsed", iterator_elapsed, i64),
@@ -1003,82 +1094,88 @@ impl<
}
}

pub fn get_account_read_entry(&self, pubkey: &Pubkey) -> Option<ReadAccountMapEntry<T>> {
pub fn get_account_read_entry<'a>(
&'a self,
pubkey: &'a Pubkey,
) -> Option<ReadAccountMapEntry<'a, T>> {
let lock = self.get_account_maps_read_lock(pubkey);
self.get_account_read_entry_with_lock(pubkey, &lock)
match self.get_account_read_entry_with_lock(pubkey, lock) {
AccountIndexGetResultInternal::Found(result) => Some(result),
_ => None,
}
}

pub fn get_account_read_entry_with_lock(
pub fn get_account_read_entry_with_lock<'a>(
&self,
pubkey: &Pubkey,
lock: &AccountMapsReadLock<'_, T>,
) -> Option<ReadAccountMapEntry<T>> {
lock.get(pubkey)
.cloned()
.map(ReadAccountMapEntry::from_account_map_entry)
pubkey: &'a Pubkey,
lock: AccountMapsReadLock<'a, T>,
) -> AccountIndexGetResultInternal<'a, T> {
ReadAccountMapEntry::new_with_lock(pubkey, lock)
}

fn get_account_write_entry(&self, pubkey: &Pubkey) -> Option<WriteAccountMapEntry<T>> {
self.account_maps[get_bin_pubkey(pubkey)]
.read()
.unwrap()
.get(pubkey)
.cloned()
.map(WriteAccountMapEntry::from_account_map_entry)
pub fn get_account_write_entry<'a, 'b>(
&'a self,
pubkey: &'b Pubkey,
) -> Option<WriteAccountMapEntry<'b, 'a, T>> {
let lock = self.get_account_maps_write_lock(pubkey);
let allow_vacant = false;
Self::get_account_write_entry_with_lock(pubkey, lock, allow_vacant)
}

fn insert_new_entry_if_missing(
&self,
pubkey: &Pubkey,
slot: Slot,
info: T,
w_account_maps: Option<&mut AccountMapsWriteLock<T>>,
) -> Option<(WriteAccountMapEntry<T>, T)> {
let new_entry = WriteAccountMapEntry::new_entry_after_update(slot, info);
match w_account_maps {
Some(w_account_maps) => {
self.insert_new_entry_if_missing_with_lock(*pubkey, w_account_maps, new_entry)
}
None => {
let mut w_account_maps = self.get_account_maps_write_lock(pubkey);
self.insert_new_entry_if_missing_with_lock(*pubkey, &mut w_account_maps, new_entry)
}
}
.map(|x| (x.0, x.1))
fn get_account_write_entry_with_lock<'a, 'b>(
pubkey: &'b Pubkey,
lock: AccountMapsWriteLock<'a, T>,
allow_vacant: bool,
) -> Option<WriteAccountMapEntry<'b, 'a, T>> {
WriteAccountMapEntry::new_with_lock(pubkey, lock, allow_vacant)
}

// return None if item was created new
// return true if item was created new
// if entry for pubkey already existed, return Some(entry). Caller needs to call entry.update.
fn insert_new_entry_if_missing_with_lock(
fn upsert_with_lock<'a>(
&self,
pubkey: Pubkey,
w_account_maps: &mut AccountMapsWriteLock<T>,
new_entry: AccountMapEntry<T>,
) -> Option<(WriteAccountMapEntry<T>, T, Pubkey)> {
let account_entry = w_account_maps.entry(pubkey);
match account_entry {
Entry::Occupied(account_entry) => Some((
WriteAccountMapEntry::from_account_map_entry(account_entry.get().clone()),
// extract the new account_info from the unused 'new_entry'
new_entry.slot_list.write().unwrap().remove(0).1,
*account_entry.key(),
)),
Entry::Vacant(account_entry) => {
account_entry.insert(new_entry);
None
}
w_account_maps: AccountMapsWriteLock<'a, T>,
mut new_entry: AccountMapEntry<T>,
reclaims: &mut SlotList<T>,
) -> (bool, AccountMapsWriteLock<'a, T>) {
let allow_vacant = true;
let mut result =
WriteAccountMapEntry::new_with_lock(&pubkey, w_account_maps, allow_vacant).unwrap();

if result.is_occupied() {
let (slot, account_info) = new_entry.slot_list.remove(0);
result.update(slot, account_info, reclaims);
(false, result.destroy())
} else {
// entry did not exist
(true, result.insert(new_entry))
}
}

fn get_account_write_entry_else_create(
// return None if item was created new
// if entry for pubkey already existed, return Some(entry). Caller needs to call entry.update.
fn upsert_with_lock_pubkey_result<'a>(
// rename: maybe insert_or_update
&self,
pubkey: &Pubkey,
slot: Slot,
info: T,
) -> Option<(WriteAccountMapEntry<T>, T)> {
match self.get_account_write_entry(pubkey) {
Some(w_account_entry) => Some((w_account_entry, info)),
None => self.insert_new_entry_if_missing(pubkey, slot, info, None),
pubkey: Pubkey,
w_account_maps: AccountMapsWriteLock<'a, T>,
mut new_entry: AccountMapEntry<T>,
reclaims: &mut SlotList<T>,
) -> (Option<Pubkey>, AccountMapsWriteLock<'a, T>) {
let allow_vacant = true;
let mut result =
WriteAccountMapEntry::new_with_lock(&pubkey, w_account_maps, allow_vacant).unwrap();

if result.is_occupied() {
let (slot, account_info) = new_entry.slot_list.remove(0);
result.update(slot, account_info, reclaims);
let r = result.destroy();
(Some(pubkey), r)
} else {
// entry did not exist
let r = result.insert(new_entry);
(None, r)
}
}

@@ -1091,7 +1188,7 @@ impl<
for key in dead_keys.iter() {
let mut w_index = self.get_account_maps_write_lock(key);
if let btree_map::Entry::Occupied(index_entry) = w_index.entry(**key) {
if index_entry.get().slot_list.read().unwrap().is_empty() {
if index_entry.get().slot_list.is_empty() {
index_entry.remove();

// Note it's only safe to remove all the entries for this key
@@ -1203,18 +1300,24 @@ impl<
C: Contains<'a, Slot>,
{
if let Some(mut write_account_map_entry) = self.get_account_write_entry(pubkey) {
write_account_map_entry.slot_list_mut(|slot_list| {
slot_list.retain(|(slot, item)| {
let should_purge = slots_to_purge.contains(slot);
if should_purge {
reclaims.push((*slot, item.clone()));
false
} else {
true
}
});
slot_list.is_empty()
})
if write_account_map_entry.is_occupied() {
write_account_map_entry
.slot_list_mut(|slot_list| {
slot_list.retain(|(slot, item)| {
let should_purge = slots_to_purge.contains(slot);
if should_purge {
reclaims.push((*slot, item.clone()));
false
} else {
true
}
});
slot_list.is_empty()
})
.unwrap() // will always be Some because we checked for is_occupied above
} else {
true
}
} else {
true
}
@@ -1272,29 +1375,27 @@ impl<

/// Get an account
/// The latest account that appears in `ancestors` or `roots` is returned.
pub(crate) fn get(
&self,
pubkey: &Pubkey,
pub(crate) fn get<'a>(
&'a self,
pubkey: &'a Pubkey,
ancestors: Option<&Ancestors>,
max_root: Option<Slot>,
) -> AccountIndexGetResult<'_, T> {
) -> AccountIndexGetResult<'a, T> {
let read_lock = self.account_maps[get_bin_pubkey(pubkey)].read().unwrap();
let account = read_lock
.get(pubkey)
.cloned()
.map(ReadAccountMapEntry::from_account_map_entry);
let account = self.get_account_read_entry_with_lock(pubkey, read_lock);

match account {
Some(locked_entry) => {
drop(read_lock);
AccountIndexGetResultInternal::Found(locked_entry) => {
let slot_list = locked_entry.slot_list();
let found_index = self.latest_slot(ancestors, slot_list, max_root);
match found_index {
Some(found_index) => AccountIndexGetResult::Found(locked_entry, found_index),
None => AccountIndexGetResult::NotFoundOnFork,
}
}
None => AccountIndexGetResult::Missing(read_lock),
AccountIndexGetResultInternal::Missing(read_lock) => {
AccountIndexGetResult::Missing(read_lock)
}
}
}

@@ -1372,7 +1473,7 @@ impl<
}
}

fn get_account_maps_write_lock(&self, pubkey: &Pubkey) -> AccountMapsWriteLock<T> {
fn get_account_maps_write_lock<'a>(&'a self, pubkey: &Pubkey) -> AccountMapsWriteLock<'a, T> {
self.account_maps[get_bin_pubkey(pubkey)].write().unwrap()
}

@@ -1420,17 +1521,19 @@ impl<
let mut duplicate_keys = Vec::with_capacity(items.len() / 10);
let mut w_account_maps = self.account_maps[pubkey_bin].write().unwrap();
let mut insert_time = Measure::start("insert_into_primary_index");
items.into_iter().for_each(|(pubkey, new_item)| {
let already_exists = self.insert_new_entry_if_missing_with_lock(
// for loop because of capture of w_account_maps
for (pubkey, new_item) in items.into_iter() {
let (already_exists, returned_lock) = self.upsert_with_lock_pubkey_result(
pubkey,
&mut w_account_maps,
w_account_maps,
new_item,
&mut _reclaims,
);
if let Some((mut w_account_entry, account_info, pubkey)) = already_exists {
w_account_entry.update(slot, account_info, &mut _reclaims);
w_account_maps = returned_lock; // re-use the lock
if let Some(pubkey) = already_exists {
duplicate_keys.push(pubkey);
}
});
}
insert_time.stop();
insertion_time.fetch_add(insert_time.as_us(), Ordering::Relaxed);
duplicate_keys
@@ -1466,15 +1569,11 @@ impl<
// - The secondary index is never consulted as primary source of truth for gets/stores.
// So, what the accounts_index sees alone is sufficient as a source of truth for other non-scan
// account operations.
if let Some((mut w_account_entry, account_info)) =
self.get_account_write_entry_else_create(pubkey, slot, account_info)
{
w_account_entry.update(slot, account_info, reclaims);
false
} else {
true
}
};
let new_item = WriteAccountMapEntry::new_entry_after_update(slot, account_info);
let w_account_maps = self.get_account_maps_write_lock(pubkey);
self.upsert_with_lock(*pubkey, w_account_maps, new_item, reclaims)
}
.0;
self.update_secondary_indexes(pubkey, account_owner, account_data, account_indexes);
is_newly_inserted
}
@@ -1554,7 +1653,7 @@ impl<
if is_slot_list_empty {
let mut w_maps = self.get_account_maps_write_lock(pubkey);
if let Some(x) = w_maps.get(pubkey) {
if x.slot_list.read().unwrap().is_empty() {
if x.slot_list.is_empty() {
w_maps.remove(pubkey);
}
}
@@ -1736,11 +1835,13 @@ impl<
// indexes!
pub fn purge_roots(&self, pubkey: &Pubkey) -> (SlotList<T>, bool) {
let mut write_account_map_entry = self.get_account_write_entry(pubkey).unwrap();
write_account_map_entry.slot_list_mut(|slot_list| {
let reclaims = self.get_rooted_entries(slot_list, None);
slot_list.retain(|(slot, _)| !self.is_root(*slot));
(reclaims, slot_list.is_empty())
})
write_account_map_entry
.slot_list_mut(|slot_list: &mut SlotList<T>| {
let reclaims = self.get_rooted_entries(slot_list, None);
slot_list.retain(|(slot, _)| !self.is_root(*slot));
(reclaims, slot_list.is_empty())
})
.unwrap()
}
}

@@ -1774,7 +1875,7 @@ pub mod tests {
}

impl<'a, T: 'static> AccountIndexGetResult<'a, T> {
pub fn unwrap(self) -> (ReadAccountMapEntry<T>, usize) {
pub fn unwrap(self) -> (ReadAccountMapEntry<'a, T>, usize) {
match self {
AccountIndexGetResult::Found(lock, size) => (lock, size),
_ => {
@@ -1791,7 +1892,10 @@ pub mod tests {
matches!(self, AccountIndexGetResult::Found(_lock, _size))
}

pub fn map<V, F: FnOnce((ReadAccountMapEntry<T>, usize)) -> V>(self, f: F) -> Option<V> {
pub fn map<V, F: FnOnce((ReadAccountMapEntry<'a, T>, usize)) -> V>(
self,
f: F,
) -> Option<V> {
match self {
AccountIndexGetResult::Found(lock, size) => Some(f((lock, size))),
_ => None,
@@ -2468,8 +2572,9 @@ pub mod tests {
let key = Keypair::new();
let index = AccountsIndex::<bool>::default();
let ancestors = Ancestors::default();
assert!(index.get(&key.pubkey(), Some(&ancestors), None).is_none());
assert!(index.get(&key.pubkey(), None, None).is_none());
let key = &key.pubkey();
assert!(index.get(key, Some(&ancestors), None).is_none());
assert!(index.get(key, None, None).is_none());

let mut num = 0;
index.unchecked_scan_accounts("", &ancestors, |_pubkey, _index| num += 1);
@@ -2611,22 +2716,16 @@ pub mod tests {

let new_entry = WriteAccountMapEntry::new_entry_after_update(slot, account_info);
assert_eq!(new_entry.ref_count.load(Ordering::Relaxed), 0);
assert_eq!(new_entry.slot_list.read().unwrap().capacity(), 1);
assert_eq!(
new_entry.slot_list.read().unwrap().to_vec(),
vec![(slot, account_info)]
);
assert_eq!(new_entry.slot_list.capacity(), 1);
assert_eq!(new_entry.slot_list.to_vec(), vec![(slot, account_info)]);

// account_info type that is NOT cached
let account_info = true;

let new_entry = WriteAccountMapEntry::new_entry_after_update(slot, account_info);
assert_eq!(new_entry.ref_count.load(Ordering::Relaxed), 1);
assert_eq!(new_entry.slot_list.read().unwrap().capacity(), 1);
assert_eq!(
new_entry.slot_list.read().unwrap().to_vec(),
vec![(slot, account_info)]
);
assert_eq!(new_entry.slot_list.capacity(), 1);
assert_eq!(new_entry.slot_list.to_vec(), vec![(slot, account_info)]);
}

#[test]
@@ -2697,10 +2796,7 @@ pub mod tests {
assert_eq!(entry.slot_list().to_vec(), expected);
let new_entry =
WriteAccountMapEntry::new_entry_after_update(slot0, account_infos[0].clone());
assert_eq!(
entry.slot_list().to_vec(),
new_entry.slot_list.read().unwrap().to_vec(),
);
assert_eq!(entry.slot_list().to_vec(), new_entry.slot_list.to_vec(),);
}

// insert second entry for pubkey. This will use update and NOT use new_entry_after_update.
@@ -2728,9 +2824,12 @@ pub mod tests {
};

let entry = if *lock {
index
.get_account_read_entry_with_lock(&key, read_lock.as_ref().unwrap())
.unwrap()
match index.get_account_read_entry_with_lock(&key, read_lock.unwrap()) {
AccountIndexGetResultInternal::Found(entry) => entry,
_ => {
panic!("unexpected");
}
}
} else {
index.get_account_read_entry(&key).unwrap()
};
@@ -2749,7 +2848,7 @@ pub mod tests {

let new_entry =
WriteAccountMapEntry::new_entry_after_update(slot1, account_infos[1].clone());
assert_eq!(entry.slot_list()[1], new_entry.slot_list.read().unwrap()[0],);
assert_eq!(entry.slot_list()[1], new_entry.slot_list[0],);
}
}

@@ -2772,14 +2871,15 @@ pub mod tests {
let account_info = true;

let new_entry = WriteAccountMapEntry::new_entry_after_update(slot, account_info);
let mut w_account_maps = index.get_account_maps_write_lock(&key.pubkey());
let write = index.insert_new_entry_if_missing_with_lock(
let w_account_maps = index.get_account_maps_write_lock(&key.pubkey());
let write = index.upsert_with_lock(
key.pubkey(),
&mut w_account_maps,
w_account_maps,
new_entry,
&mut SlotList::default(),
);
assert!(write.is_none());
drop(w_account_maps);
assert!(write.0);
drop(write);

let mut ancestors = Ancestors::default();
assert!(index.get(&key.pubkey(), Some(&ancestors), None).is_none());
@@ -2835,13 +2935,14 @@ pub mod tests {
assert!(gc.is_empty());

let ancestors = vec![(0, 0)].into_iter().collect();
let (list, idx) = index.get(&key.pubkey(), Some(&ancestors), None).unwrap();
let key = &key.pubkey();
let (list, idx) = index.get(key, Some(&ancestors), None).unwrap();
assert_eq!(list.slot_list()[idx], (0, true));

let mut num = 0;
let mut found_key = false;
index.unchecked_scan_accounts("", &ancestors, |pubkey, _index| {
if pubkey == &key.pubkey() {
if pubkey == key {
found_key = true
};
num += 1
@@ -3040,7 +3141,8 @@ pub mod tests {
assert!(gc.is_empty());

index.add_root(0, false);
let (list, idx) = index.get(&key.pubkey(), None, None).unwrap();
let key = &key.pubkey();
let (list, idx) = index.get(key, None, None).unwrap();
assert_eq!(list.slot_list()[idx], (0, true));
}

@@ -3152,22 +3254,23 @@ pub mod tests {
&mut gc,
);
assert!(gc.is_empty());
let (list, idx) = index.get(&key.pubkey(), Some(&ancestors), None).unwrap();
let key = &key.pubkey();
let (list, idx) = index.get(key, Some(&ancestors), None).unwrap();
assert_eq!(list.slot_list()[idx], (0, true));
drop(list);

let mut gc = Vec::new();
index.upsert(
0,
&key.pubkey(),
key,
&Pubkey::default(),
&[],
&AccountSecondaryIndexes::default(),
false,
&mut gc,
);
assert_eq!(gc, vec![(0, true)]);
let (list, idx) = index.get(&key.pubkey(), Some(&ancestors), None).unwrap();
let (list, idx) = index.get(key, Some(&ancestors), None).unwrap();
assert_eq!(list.slot_list()[idx], (0, false));
}

@@ -3178,9 +3281,10 @@ pub mod tests {
let index = AccountsIndex::<bool>::default();
let ancestors = vec![(0, 0)].into_iter().collect();
let mut gc = Vec::new();
let key = &key.pubkey();
index.upsert(
0,
&key.pubkey(),
key,
&Pubkey::default(),
&[],
&AccountSecondaryIndexes::default(),
@@ -3190,18 +3294,18 @@ pub mod tests {
assert!(gc.is_empty());
index.upsert(
1,
&key.pubkey(),
key,
&Pubkey::default(),
&[],
&AccountSecondaryIndexes::default(),
false,
&mut gc,
);
assert!(gc.is_empty());
let (list, idx) = index.get(&key.pubkey(), Some(&ancestors), None).unwrap();
let (list, idx) = index.get(key, Some(&ancestors), None).unwrap();
assert_eq!(list.slot_list()[idx], (0, true));
let ancestors = vec![(1, 0)].into_iter().collect();
let (list, idx) = index.get(&key.pubkey(), Some(&ancestors), None).unwrap();
let (list, idx) = index.get(key, Some(&ancestors), None).unwrap();
assert_eq!(list.slot_list()[idx], (1, false));
}

@@ -3210,9 +3314,10 @@ pub mod tests {
let key = Keypair::new();
let index = AccountsIndex::<bool>::default();
let mut gc = Vec::new();
let key = &key.pubkey();
index.upsert(
0,
&key.pubkey(),
key,
&Pubkey::default(),
&[],
&AccountSecondaryIndexes::default(),
@@ -3222,7 +3327,7 @@ pub mod tests {
assert!(gc.is_empty());
index.upsert(
1,
&key.pubkey(),
key,
&Pubkey::default(),
&[],
&AccountSecondaryIndexes::default(),
@@ -3231,7 +3336,7 @@ pub mod tests {
);
index.upsert(
2,
&key.pubkey(),
key,
&Pubkey::default(),
&[],
&AccountSecondaryIndexes::default(),
@@ -3240,7 +3345,7 @@ pub mod tests {
);
index.upsert(
3,
&key.pubkey(),
key,
&Pubkey::default(),
&[],
&AccountSecondaryIndexes::default(),
@@ -3252,7 +3357,7 @@ pub mod tests {
index.add_root(3, false);
index.upsert(
4,
&key.pubkey(),
key,
&Pubkey::default(),
&[],
&AccountSecondaryIndexes::default(),
@@ -3263,13 +3368,13 @@ pub mod tests {
// 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();
let (list, idx) = index.get(key, None, None).unwrap();
assert_eq!(list.slot_list()[idx], (3, true));

let mut num = 0;
let mut found_key = false;
index.unchecked_scan_accounts("", &Ancestors::default(), |pubkey, _index| {
if pubkey == &key.pubkey() {
if pubkey == key {
found_key = true;
assert_eq!(_index, (&true, 3));
};
@@ -3284,9 +3389,10 @@ pub mod tests {
let key = Keypair::new();
let index = AccountsIndex::<u64>::default();
let mut gc = Vec::new();
let key = &key.pubkey();
assert!(index.upsert(
1,
&key.pubkey(),
key,
&Pubkey::default(),
&[],
&AccountSecondaryIndexes::default(),
@@ -3296,24 +3402,24 @@ pub mod tests {

assert!(!index.upsert(
1,
&key.pubkey(),
key,
&Pubkey::default(),
&[],
&AccountSecondaryIndexes::default(),
10,
&mut gc
));

let purges = index.purge_roots(&key.pubkey());
let purges = index.purge_roots(key);
assert_eq!(purges, (vec![], false));
index.add_root(1, false);

let purges = index.purge_roots(&key.pubkey());
let purges = index.purge_roots(key);
assert_eq!(purges, (vec![(1, 10)], true));

assert!(!index.upsert(
1,
&key.pubkey(),
key,
&Pubkey::default(),
&[],
&AccountSecondaryIndexes::default(),