move towards accounts index being dynamic (#19032)
This commit is contained in:
committed by
GitHub
parent
06e08c4840
commit
31a620c42b
@ -11920,8 +11920,14 @@ pub mod tests {
|
|||||||
];
|
];
|
||||||
// make sure accounts are in 2 different bins
|
// make sure accounts are in 2 different bins
|
||||||
assert!(
|
assert!(
|
||||||
crate::accounts_index::get_bin_pubkey(&keys[0])
|
accounts
|
||||||
!= crate::accounts_index::get_bin_pubkey(&keys[1])
|
.accounts_index
|
||||||
|
.bin_calculator
|
||||||
|
.bin_from_pubkey(&keys[0])
|
||||||
|
!= accounts
|
||||||
|
.accounts_index
|
||||||
|
.bin_calculator
|
||||||
|
.bin_from_pubkey(&keys[1])
|
||||||
);
|
);
|
||||||
let account = AccountSharedData::new(1, 1, AccountSharedData::default().owner());
|
let account = AccountSharedData::new(1, 1, AccountSharedData::default().owner());
|
||||||
let account_big = AccountSharedData::new(1, 1000, AccountSharedData::default().owner());
|
let account_big = AccountSharedData::new(1, 1000, AccountSharedData::default().owner());
|
||||||
|
@ -2,6 +2,7 @@ use crate::{
|
|||||||
ancestors::Ancestors,
|
ancestors::Ancestors,
|
||||||
contains::Contains,
|
contains::Contains,
|
||||||
inline_spl_token_v2_0::{self, SPL_TOKEN_ACCOUNT_MINT_OFFSET, SPL_TOKEN_ACCOUNT_OWNER_OFFSET},
|
inline_spl_token_v2_0::{self, SPL_TOKEN_ACCOUNT_MINT_OFFSET, SPL_TOKEN_ACCOUNT_OWNER_OFFSET},
|
||||||
|
pubkey_bins::PubkeyBinCalculator16,
|
||||||
secondary_index::*,
|
secondary_index::*,
|
||||||
};
|
};
|
||||||
use bv::BitVec;
|
use bv::BitVec;
|
||||||
@ -31,7 +32,7 @@ use std::{
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub const ITER_BATCH_SIZE: usize = 1000;
|
pub const ITER_BATCH_SIZE: usize = 1000;
|
||||||
const BINS: usize = 16;
|
const BINS_DEFAULT: usize = 16;
|
||||||
pub type ScanResult<T> = Result<T, ScanError>;
|
pub type ScanResult<T> = Result<T, ScanError>;
|
||||||
pub type SlotList<T> = Vec<(Slot, T)>;
|
pub type SlotList<T> = Vec<(Slot, T)>;
|
||||||
pub type SlotSlice<'s, T> = &'s [(Slot, T)];
|
pub type SlotSlice<'s, T> = &'s [(Slot, T)];
|
||||||
@ -527,6 +528,7 @@ pub struct AccountsIndexRootsStats {
|
|||||||
|
|
||||||
pub struct AccountsIndexIterator<'a, T> {
|
pub struct AccountsIndexIterator<'a, T> {
|
||||||
account_maps: &'a LockMapTypeSlice<T>,
|
account_maps: &'a LockMapTypeSlice<T>,
|
||||||
|
bin_calculator: &'a PubkeyBinCalculator16,
|
||||||
start_bound: Bound<Pubkey>,
|
start_bound: Bound<Pubkey>,
|
||||||
end_bound: Bound<Pubkey>,
|
end_bound: Bound<Pubkey>,
|
||||||
is_finished: bool,
|
is_finished: bool,
|
||||||
@ -541,21 +543,23 @@ impl<'a, T> AccountsIndexIterator<'a, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bin_from_bound(bound: &Bound<Pubkey>, unbounded_bin: usize) -> usize {
|
fn bin_from_bound(&self, bound: &Bound<Pubkey>, unbounded_bin: usize) -> usize {
|
||||||
match bound {
|
match bound {
|
||||||
Bound::Included(bound) | Bound::Excluded(bound) => get_bin_pubkey(bound),
|
Bound::Included(bound) | Bound::Excluded(bound) => {
|
||||||
|
self.bin_calculator.bin_from_pubkey(bound)
|
||||||
|
}
|
||||||
Bound::Unbounded => unbounded_bin,
|
Bound::Unbounded => unbounded_bin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_bin(&self) -> usize {
|
fn start_bin(&self) -> usize {
|
||||||
// start in bin where 'start_bound' would exist
|
// start in bin where 'start_bound' would exist
|
||||||
Self::bin_from_bound(&self.start_bound, 0)
|
self.bin_from_bound(&self.start_bound, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end_bin_inclusive(&self) -> usize {
|
fn end_bin_inclusive(&self) -> usize {
|
||||||
// end in bin where 'end_bound' would exist
|
// end in bin where 'end_bound' would exist
|
||||||
Self::bin_from_bound(&self.end_bound, usize::MAX)
|
self.bin_from_bound(&self.end_bound, usize::MAX)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bin_start_and_range(&self) -> (usize, usize) {
|
fn bin_start_and_range(&self) -> (usize, usize) {
|
||||||
@ -574,7 +578,7 @@ impl<'a, T> AccountsIndexIterator<'a, T> {
|
|||||||
(start_bin, bin_range)
|
(start_bin, bin_range)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new<R>(account_maps: &'a LockMapTypeSlice<T>, range: Option<R>) -> Self
|
pub fn new<R>(index: &'a AccountsIndex<T>, range: Option<R>) -> Self
|
||||||
where
|
where
|
||||||
R: RangeBounds<Pubkey>,
|
R: RangeBounds<Pubkey>,
|
||||||
{
|
{
|
||||||
@ -587,8 +591,9 @@ impl<'a, T> AccountsIndexIterator<'a, T> {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|r| Self::clone_bound(r.end_bound()))
|
.map(|r| Self::clone_bound(r.end_bound()))
|
||||||
.unwrap_or(Unbounded),
|
.unwrap_or(Unbounded),
|
||||||
account_maps,
|
account_maps: &index.account_maps,
|
||||||
is_finished: false,
|
is_finished: false,
|
||||||
|
bin_calculator: &index.bin_calculator,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -627,11 +632,6 @@ pub trait ZeroLamport {
|
|||||||
fn is_zero_lamport(&self) -> bool;
|
fn is_zero_lamport(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_bin_pubkey(pubkey: &Pubkey) -> usize {
|
|
||||||
let byte_of_pubkey_to_bin = 0; // TODO: this should not be 0. For now it needs to be due to requests for in-order pubkeys
|
|
||||||
(pubkey.as_ref()[byte_of_pubkey_to_bin] as usize) * BINS / ((u8::MAX as usize) + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
type MapType<T> = AccountMap<Pubkey, AccountMapEntry<T>>;
|
type MapType<T> = AccountMap<Pubkey, AccountMapEntry<T>>;
|
||||||
type LockMapType<T> = Vec<RwLock<MapType<T>>>;
|
type LockMapType<T> = Vec<RwLock<MapType<T>>>;
|
||||||
type LockMapTypeSlice<T> = [RwLock<MapType<T>>];
|
type LockMapTypeSlice<T> = [RwLock<MapType<T>>];
|
||||||
@ -657,6 +657,7 @@ impl ScanSlotTracker {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AccountsIndex<T> {
|
pub struct AccountsIndex<T> {
|
||||||
pub account_maps: LockMapType<T>,
|
pub account_maps: LockMapType<T>,
|
||||||
|
pub bin_calculator: PubkeyBinCalculator16,
|
||||||
program_id_index: SecondaryIndex<DashMapSecondaryIndexEntry>,
|
program_id_index: SecondaryIndex<DashMapSecondaryIndexEntry>,
|
||||||
spl_token_mint_index: SecondaryIndex<DashMapSecondaryIndexEntry>,
|
spl_token_mint_index: SecondaryIndex<DashMapSecondaryIndexEntry>,
|
||||||
spl_token_owner_index: SecondaryIndex<RwLockSecondaryIndexEntry>,
|
spl_token_owner_index: SecondaryIndex<RwLockSecondaryIndexEntry>,
|
||||||
@ -677,11 +678,13 @@ pub struct AccountsIndex<T> {
|
|||||||
|
|
||||||
impl<T> Default for AccountsIndex<T> {
|
impl<T> Default for AccountsIndex<T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
let bins = BINS_DEFAULT;
|
||||||
Self {
|
Self {
|
||||||
account_maps: (0..BINS)
|
account_maps: (0..bins)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|_| RwLock::new(AccountMap::default()))
|
.map(|_| RwLock::new(AccountMap::default()))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
|
bin_calculator: PubkeyBinCalculator16::new(bins),
|
||||||
program_id_index: SecondaryIndex::<DashMapSecondaryIndexEntry>::new(
|
program_id_index: SecondaryIndex::<DashMapSecondaryIndexEntry>::new(
|
||||||
"program_id_index_stats",
|
"program_id_index_stats",
|
||||||
),
|
),
|
||||||
@ -706,7 +709,7 @@ impl<
|
|||||||
where
|
where
|
||||||
R: RangeBounds<Pubkey>,
|
R: RangeBounds<Pubkey>,
|
||||||
{
|
{
|
||||||
AccountsIndexIterator::new(&self.account_maps, range)
|
AccountsIndexIterator::new(self, range)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_checked_scan_accounts<F, R>(
|
fn do_checked_scan_accounts<F, R>(
|
||||||
@ -1041,7 +1044,7 @@ impl<
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_account_write_entry(&self, pubkey: &Pubkey) -> Option<WriteAccountMapEntry<T>> {
|
fn get_account_write_entry(&self, pubkey: &Pubkey) -> Option<WriteAccountMapEntry<T>> {
|
||||||
self.account_maps[get_bin_pubkey(pubkey)]
|
self.account_maps[self.bin_calculator.bin_from_pubkey(pubkey)]
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get(pubkey)
|
.get(pubkey)
|
||||||
@ -1300,7 +1303,9 @@ impl<
|
|||||||
ancestors: Option<&Ancestors>,
|
ancestors: Option<&Ancestors>,
|
||||||
max_root: Option<Slot>,
|
max_root: Option<Slot>,
|
||||||
) -> AccountIndexGetResult<'_, T> {
|
) -> AccountIndexGetResult<'_, T> {
|
||||||
let read_lock = self.account_maps[get_bin_pubkey(pubkey)].read().unwrap();
|
let read_lock = self.account_maps[self.bin_calculator.bin_from_pubkey(pubkey)]
|
||||||
|
.read()
|
||||||
|
.unwrap();
|
||||||
let account = read_lock
|
let account = read_lock
|
||||||
.get(pubkey)
|
.get(pubkey)
|
||||||
.cloned()
|
.cloned()
|
||||||
@ -1395,11 +1400,19 @@ impl<
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_account_maps_write_lock(&self, pubkey: &Pubkey) -> AccountMapsWriteLock<T> {
|
fn get_account_maps_write_lock(&self, pubkey: &Pubkey) -> AccountMapsWriteLock<T> {
|
||||||
self.account_maps[get_bin_pubkey(pubkey)].write().unwrap()
|
self.account_maps[self.bin_calculator.bin_from_pubkey(pubkey)]
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_account_maps_read_lock(&self, pubkey: &Pubkey) -> AccountMapsReadLock<T> {
|
pub(crate) fn get_account_maps_read_lock(&self, pubkey: &Pubkey) -> AccountMapsReadLock<T> {
|
||||||
self.account_maps[get_bin_pubkey(pubkey)].read().unwrap()
|
self.account_maps[self.bin_calculator.bin_from_pubkey(pubkey)]
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bins(&self) -> usize {
|
||||||
|
self.account_maps.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same functionally to upsert, but:
|
// Same functionally to upsert, but:
|
||||||
@ -1417,13 +1430,14 @@ impl<
|
|||||||
) -> (Vec<Pubkey>, u64) {
|
) -> (Vec<Pubkey>, u64) {
|
||||||
// big enough so not likely to re-allocate, small enough to not over-allocate by too much
|
// big enough so not likely to re-allocate, small enough to not over-allocate by too much
|
||||||
// this assumes the largest bin contains twice the expected amount of the average size per bin
|
// this assumes the largest bin contains twice the expected amount of the average size per bin
|
||||||
let expected_items_per_bin = item_len * 2 / BINS;
|
let bins = self.bins();
|
||||||
let mut binned = (0..BINS)
|
let expected_items_per_bin = item_len * 2 / bins;
|
||||||
|
let mut binned = (0..bins)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|pubkey_bin| (pubkey_bin, Vec::with_capacity(expected_items_per_bin)))
|
.map(|pubkey_bin| (pubkey_bin, Vec::with_capacity(expected_items_per_bin)))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
items.for_each(|(pubkey, account_info)| {
|
items.for_each(|(pubkey, account_info)| {
|
||||||
let bin = get_bin_pubkey(&pubkey);
|
let bin = self.bin_calculator.bin_from_pubkey(&pubkey);
|
||||||
// this value is equivalent to what update() below would have created if we inserted a new item
|
// this value is equivalent to what update() below would have created if we inserted a new item
|
||||||
let info = WriteAccountMapEntry::new_entry_after_update(slot, account_info);
|
let info = WriteAccountMapEntry::new_entry_after_update(slot, account_info);
|
||||||
binned[bin].1.push((pubkey, info));
|
binned[bin].1.push((pubkey, info));
|
||||||
@ -3840,28 +3854,21 @@ pub mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_bin_start_and_range() {
|
fn test_bin_start_and_range() {
|
||||||
let index = AccountsIndex::<bool>::default();
|
let index = AccountsIndex::<bool>::default();
|
||||||
let iter = AccountsIndexIterator::new(&index.account_maps, None::<RangeInclusive<Pubkey>>);
|
let iter = AccountsIndexIterator::new(&index, None::<RangeInclusive<Pubkey>>);
|
||||||
assert_eq!((0, usize::MAX), iter.bin_start_and_range());
|
assert_eq!((0, usize::MAX), iter.bin_start_and_range());
|
||||||
|
|
||||||
let key_0 = Pubkey::new(&[0; 32]);
|
let key_0 = Pubkey::new(&[0; 32]);
|
||||||
let key_ff = Pubkey::new(&[0xff; 32]);
|
let key_ff = Pubkey::new(&[0xff; 32]);
|
||||||
|
|
||||||
let iter = AccountsIndexIterator::new(
|
let iter = AccountsIndexIterator::new(&index, Some(RangeInclusive::new(key_0, key_ff)));
|
||||||
&index.account_maps,
|
let bins = index.bins();
|
||||||
Some(RangeInclusive::new(key_0, key_ff)),
|
assert_eq!((0, bins), iter.bin_start_and_range());
|
||||||
);
|
let iter = AccountsIndexIterator::new(&index, Some(RangeInclusive::new(key_ff, key_0)));
|
||||||
assert_eq!((0, BINS), iter.bin_start_and_range());
|
assert_eq!((bins - 1, 0), iter.bin_start_and_range());
|
||||||
let iter = AccountsIndexIterator::new(
|
let iter = AccountsIndexIterator::new(&index, Some((Included(key_0), Unbounded)));
|
||||||
&index.account_maps,
|
|
||||||
Some(RangeInclusive::new(key_ff, key_0)),
|
|
||||||
);
|
|
||||||
assert_eq!((BINS - 1, 0), iter.bin_start_and_range());
|
|
||||||
let iter =
|
|
||||||
AccountsIndexIterator::new(&index.account_maps, Some((Included(key_0), Unbounded)));
|
|
||||||
assert_eq!((0, usize::MAX), iter.bin_start_and_range());
|
assert_eq!((0, usize::MAX), iter.bin_start_and_range());
|
||||||
let iter =
|
let iter = AccountsIndexIterator::new(&index, Some((Included(key_ff), Unbounded)));
|
||||||
AccountsIndexIterator::new(&index.account_maps, Some((Included(key_ff), Unbounded)));
|
assert_eq!((bins - 1, usize::MAX), iter.bin_start_and_range());
|
||||||
assert_eq!((BINS - 1, usize::MAX), iter.bin_start_and_range());
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(0..2)
|
(0..2)
|
||||||
@ -3876,36 +3883,32 @@ pub mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_start_end_bin() {
|
fn test_start_end_bin() {
|
||||||
let index = AccountsIndex::<bool>::default();
|
let index = AccountsIndex::<bool>::default();
|
||||||
let iter = AccountsIndexIterator::new(&index.account_maps, None::<RangeInclusive<Pubkey>>);
|
assert_eq!(index.bins(), BINS_DEFAULT);
|
||||||
|
let iter = AccountsIndexIterator::new(&index, None::<RangeInclusive<Pubkey>>);
|
||||||
assert_eq!(iter.start_bin(), 0); // no range, so 0
|
assert_eq!(iter.start_bin(), 0); // no range, so 0
|
||||||
assert_eq!(iter.end_bin_inclusive(), usize::MAX); // no range, so max
|
assert_eq!(iter.end_bin_inclusive(), usize::MAX); // no range, so max
|
||||||
|
|
||||||
let key = Pubkey::new(&[0; 32]);
|
let key = Pubkey::new(&[0; 32]);
|
||||||
let iter =
|
let iter = AccountsIndexIterator::new(&index, Some(RangeInclusive::new(key, key)));
|
||||||
AccountsIndexIterator::new(&index.account_maps, Some(RangeInclusive::new(key, key)));
|
|
||||||
assert_eq!(iter.start_bin(), 0); // start at pubkey 0, so 0
|
assert_eq!(iter.start_bin(), 0); // start at pubkey 0, so 0
|
||||||
assert_eq!(iter.end_bin_inclusive(), 0); // end at pubkey 0, so 0
|
assert_eq!(iter.end_bin_inclusive(), 0); // end at pubkey 0, so 0
|
||||||
let iter =
|
let iter = AccountsIndexIterator::new(&index, Some((Included(key), Excluded(key))));
|
||||||
AccountsIndexIterator::new(&index.account_maps, Some((Included(key), Excluded(key))));
|
|
||||||
assert_eq!(iter.start_bin(), 0); // start at pubkey 0, so 0
|
assert_eq!(iter.start_bin(), 0); // start at pubkey 0, so 0
|
||||||
assert_eq!(iter.end_bin_inclusive(), 0); // end at pubkey 0, so 0
|
assert_eq!(iter.end_bin_inclusive(), 0); // end at pubkey 0, so 0
|
||||||
let iter =
|
let iter = AccountsIndexIterator::new(&index, Some((Excluded(key), Excluded(key))));
|
||||||
AccountsIndexIterator::new(&index.account_maps, Some((Excluded(key), Excluded(key))));
|
|
||||||
assert_eq!(iter.start_bin(), 0); // start at pubkey 0, so 0
|
assert_eq!(iter.start_bin(), 0); // start at pubkey 0, so 0
|
||||||
assert_eq!(iter.end_bin_inclusive(), 0); // end at pubkey 0, so 0
|
assert_eq!(iter.end_bin_inclusive(), 0); // end at pubkey 0, so 0
|
||||||
|
|
||||||
let key = Pubkey::new(&[0xff; 32]);
|
let key = Pubkey::new(&[0xff; 32]);
|
||||||
let iter =
|
let iter = AccountsIndexIterator::new(&index, Some(RangeInclusive::new(key, key)));
|
||||||
AccountsIndexIterator::new(&index.account_maps, Some(RangeInclusive::new(key, key)));
|
let bins = index.bins();
|
||||||
assert_eq!(iter.start_bin(), BINS - 1); // start at highest possible pubkey, so BINS - 1
|
assert_eq!(iter.start_bin(), bins - 1); // start at highest possible pubkey, so bins - 1
|
||||||
assert_eq!(iter.end_bin_inclusive(), BINS - 1);
|
assert_eq!(iter.end_bin_inclusive(), bins - 1);
|
||||||
let iter =
|
let iter = AccountsIndexIterator::new(&index, Some((Included(key), Excluded(key))));
|
||||||
AccountsIndexIterator::new(&index.account_maps, Some((Included(key), Excluded(key))));
|
assert_eq!(iter.start_bin(), bins - 1); // start at highest possible pubkey, so bins - 1
|
||||||
assert_eq!(iter.start_bin(), BINS - 1); // start at highest possible pubkey, so BINS - 1
|
assert_eq!(iter.end_bin_inclusive(), bins - 1);
|
||||||
assert_eq!(iter.end_bin_inclusive(), BINS - 1);
|
let iter = AccountsIndexIterator::new(&index, Some((Excluded(key), Excluded(key))));
|
||||||
let iter =
|
assert_eq!(iter.start_bin(), bins - 1); // start at highest possible pubkey, so bins - 1
|
||||||
AccountsIndexIterator::new(&index.account_maps, Some((Excluded(key), Excluded(key))));
|
assert_eq!(iter.end_bin_inclusive(), bins - 1);
|
||||||
assert_eq!(iter.start_bin(), BINS - 1); // start at highest possible pubkey, so BINS - 1
|
|
||||||
assert_eq!(iter.end_bin_inclusive(), BINS - 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
|
|
||||||
pub(crate) struct PubkeyBinCalculator16 {
|
#[derive(Debug)]
|
||||||
|
pub struct PubkeyBinCalculator16 {
|
||||||
// how many bits from the first 2 bytes to shift away to ignore when calculating bin
|
// how many bits from the first 2 bytes to shift away to ignore when calculating bin
|
||||||
shift_bits: u32,
|
shift_bits: u32,
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user