AcctIdx: introduce BucketApi for access to a specific bucket (#20359)
This commit is contained in:
committed by
GitHub
parent
fb8a7cfa92
commit
8da2eb980a
134
bucket_map/src/bucket_api.rs
Normal file
134
bucket_map/src/bucket_api.rs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
use crate::bucket::Bucket;
|
||||||
|
use crate::bucket_item::BucketItem;
|
||||||
|
use crate::bucket_map::BucketMapError;
|
||||||
|
use crate::bucket_stats::BucketMapStats;
|
||||||
|
use crate::{MaxSearch, RefCount};
|
||||||
|
use solana_sdk::pubkey::Pubkey;
|
||||||
|
use std::ops::RangeBounds;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::{RwLock, RwLockWriteGuard};
|
||||||
|
|
||||||
|
type LockedBucket<T> = Arc<RwLock<Option<Bucket<T>>>>;
|
||||||
|
|
||||||
|
pub struct BucketApi<T: Clone + Copy> {
|
||||||
|
drives: Arc<Vec<PathBuf>>,
|
||||||
|
max_search: MaxSearch,
|
||||||
|
pub stats: Arc<BucketMapStats>,
|
||||||
|
|
||||||
|
bucket: LockedBucket<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone + Copy> BucketApi<T> {
|
||||||
|
pub fn new(
|
||||||
|
drives: Arc<Vec<PathBuf>>,
|
||||||
|
max_search: MaxSearch,
|
||||||
|
stats: Arc<BucketMapStats>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
drives,
|
||||||
|
max_search,
|
||||||
|
stats,
|
||||||
|
bucket: Arc::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the items for bucket
|
||||||
|
pub fn items_in_range<R>(&self, range: &Option<&R>) -> Vec<BucketItem<T>>
|
||||||
|
where
|
||||||
|
R: RangeBounds<Pubkey>,
|
||||||
|
{
|
||||||
|
self.bucket
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.as_ref()
|
||||||
|
.map(|bucket| bucket.items_in_range(range))
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the Pubkeys
|
||||||
|
pub fn keys(&self) -> Vec<Pubkey> {
|
||||||
|
self.bucket
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.as_ref()
|
||||||
|
.map_or_else(Vec::default, |bucket| bucket.keys())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the values for Pubkey `key`
|
||||||
|
pub fn read_value(&self, key: &Pubkey) -> Option<(Vec<T>, RefCount)> {
|
||||||
|
self.bucket.read().unwrap().as_ref().and_then(|bucket| {
|
||||||
|
bucket
|
||||||
|
.read_value(key)
|
||||||
|
.map(|(value, ref_count)| (value.to_vec(), ref_count))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bucket_len(&self) -> u64 {
|
||||||
|
self.bucket
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.as_ref()
|
||||||
|
.map(|bucket| bucket.bucket_len())
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_key(&self, key: &Pubkey) {
|
||||||
|
let mut bucket = self.get_write_bucket();
|
||||||
|
if let Some(bucket) = bucket.as_mut() {
|
||||||
|
bucket.delete_key(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_write_bucket(&self) -> RwLockWriteGuard<Option<Bucket<T>>> {
|
||||||
|
let mut bucket = self.bucket.write().unwrap();
|
||||||
|
if bucket.is_none() {
|
||||||
|
*bucket = Some(Bucket::new(
|
||||||
|
Arc::clone(&self.drives),
|
||||||
|
self.max_search,
|
||||||
|
Arc::clone(&self.stats),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
bucket
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addref(&self, key: &Pubkey) -> Option<RefCount> {
|
||||||
|
self.get_write_bucket()
|
||||||
|
.as_mut()
|
||||||
|
.and_then(|bucket| bucket.addref(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unref(&self, key: &Pubkey) -> Option<RefCount> {
|
||||||
|
self.get_write_bucket()
|
||||||
|
.as_mut()
|
||||||
|
.and_then(|bucket| bucket.unref(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&self, pubkey: &Pubkey, value: (&[T], RefCount)) {
|
||||||
|
let mut bucket = self.get_write_bucket();
|
||||||
|
bucket.as_mut().unwrap().insert(pubkey, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grow(&self, err: BucketMapError) {
|
||||||
|
let mut bucket = self.get_write_bucket();
|
||||||
|
bucket.as_mut().unwrap().grow(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update<F>(&self, key: &Pubkey, updatefn: F)
|
||||||
|
where
|
||||||
|
F: Fn(Option<(&[T], RefCount)>) -> Option<(Vec<T>, RefCount)>,
|
||||||
|
{
|
||||||
|
let mut bucket = self.get_write_bucket();
|
||||||
|
bucket.as_mut().unwrap().update(key, updatefn)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_write(
|
||||||
|
&self,
|
||||||
|
pubkey: &Pubkey,
|
||||||
|
value: (&[T], RefCount),
|
||||||
|
) -> Result<(), BucketMapError> {
|
||||||
|
let mut bucket = self.get_write_bucket();
|
||||||
|
bucket.as_mut().unwrap().try_write(pubkey, value.0, value.1)
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,14 @@
|
|||||||
//! BucketMap is a mostly contention free concurrent map backed by MmapMut
|
//! BucketMap is a mostly contention free concurrent map backed by MmapMut
|
||||||
|
|
||||||
use crate::bucket::Bucket;
|
use crate::bucket_api::BucketApi;
|
||||||
use crate::bucket_item::BucketItem;
|
|
||||||
use crate::bucket_stats::BucketMapStats;
|
use crate::bucket_stats::BucketMapStats;
|
||||||
use crate::{MaxSearch, RefCount};
|
use crate::{MaxSearch, RefCount};
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::ops::RangeBounds;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::{RwLock, RwLockWriteGuard};
|
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
@ -33,10 +30,9 @@ impl BucketMapConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct BucketMap<T: Clone + Copy + Debug> {
|
pub struct BucketMap<T: Clone + Copy + Debug> {
|
||||||
buckets: Vec<RwLock<Option<Bucket<T>>>>,
|
buckets: Vec<Arc<BucketApi<T>>>,
|
||||||
drives: Arc<Vec<PathBuf>>,
|
drives: Arc<Vec<PathBuf>>,
|
||||||
max_buckets_pow2: u8,
|
max_buckets_pow2: u8,
|
||||||
max_search: MaxSearch,
|
|
||||||
pub stats: Arc<BucketMapStats>,
|
pub stats: Arc<BucketMapStats>,
|
||||||
pub temp_dir: Option<TempDir>,
|
pub temp_dir: Option<TempDir>,
|
||||||
}
|
}
|
||||||
@ -71,8 +67,6 @@ impl<T: Clone + Copy + Debug> BucketMap<T> {
|
|||||||
config.max_buckets.is_power_of_two(),
|
config.max_buckets.is_power_of_two(),
|
||||||
"Max number of buckets must be a power of two"
|
"Max number of buckets must be a power of two"
|
||||||
);
|
);
|
||||||
let mut buckets = Vec::with_capacity(config.max_buckets);
|
|
||||||
buckets.resize_with(config.max_buckets, || RwLock::new(None));
|
|
||||||
let stats = Arc::new(BucketMapStats::default());
|
let stats = Arc::new(BucketMapStats::default());
|
||||||
// this should be <= 1 << DEFAULT_CAPACITY or we end up searching the same items over and over - probably not a big deal since it is so small anyway
|
// this should be <= 1 << DEFAULT_CAPACITY or we end up searching the same items over and over - probably not a big deal since it is so small anyway
|
||||||
const MAX_SEARCH: MaxSearch = 32;
|
const MAX_SEARCH: MaxSearch = 32;
|
||||||
@ -88,6 +82,15 @@ impl<T: Clone + Copy + Debug> BucketMap<T> {
|
|||||||
});
|
});
|
||||||
let drives = Arc::new(drives);
|
let drives = Arc::new(drives);
|
||||||
|
|
||||||
|
let mut buckets = Vec::with_capacity(config.max_buckets);
|
||||||
|
buckets.resize_with(config.max_buckets, || {
|
||||||
|
Arc::new(BucketApi::new(
|
||||||
|
Arc::clone(&drives),
|
||||||
|
max_search,
|
||||||
|
Arc::clone(&stats),
|
||||||
|
))
|
||||||
|
});
|
||||||
|
|
||||||
// A simple log2 function that is correct if x is a power of two
|
// A simple log2 function that is correct if x is a power of two
|
||||||
let log2 = |x: usize| usize::BITS - x.leading_zeros() - 1;
|
let log2 = |x: usize| usize::BITS - x.leading_zeros() - 1;
|
||||||
|
|
||||||
@ -96,7 +99,6 @@ impl<T: Clone + Copy + Debug> BucketMap<T> {
|
|||||||
drives,
|
drives,
|
||||||
max_buckets_pow2: log2(config.max_buckets) as u8,
|
max_buckets_pow2: log2(config.max_buckets) as u8,
|
||||||
stats,
|
stats,
|
||||||
max_search,
|
|
||||||
temp_dir,
|
temp_dir,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,92 +114,24 @@ impl<T: Clone + Copy + Debug> BucketMap<T> {
|
|||||||
self.buckets.len()
|
self.buckets.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bucket_len(&self, ix: usize) -> u64 {
|
|
||||||
self.buckets[ix]
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.as_ref()
|
|
||||||
.map(|bucket| bucket.bucket_len())
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the items for bucket `ix` in `range`
|
|
||||||
pub fn items_in_range<R>(&self, ix: usize, range: &Option<&R>) -> Vec<BucketItem<T>>
|
|
||||||
where
|
|
||||||
R: RangeBounds<Pubkey>,
|
|
||||||
{
|
|
||||||
self.buckets[ix]
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.as_ref()
|
|
||||||
.map(|bucket| bucket.items_in_range(range))
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the Pubkeys for bucket `ix`
|
|
||||||
pub fn keys(&self, ix: usize) -> Vec<Pubkey> {
|
|
||||||
self.buckets[ix]
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.as_ref()
|
|
||||||
.map_or_else(Vec::default, |bucket| bucket.keys())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the values for Pubkey `key`
|
/// Get the values for Pubkey `key`
|
||||||
pub fn read_value(&self, key: &Pubkey) -> Option<(Vec<T>, RefCount)> {
|
pub fn read_value(&self, key: &Pubkey) -> Option<(Vec<T>, RefCount)> {
|
||||||
let ix = self.bucket_ix(key);
|
self.get_bucket(key).read_value(key)
|
||||||
self.buckets[ix]
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|bucket| {
|
|
||||||
bucket
|
|
||||||
.read_value(key)
|
|
||||||
.map(|(value, ref_count)| (value.to_vec(), ref_count))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete the Pubkey `key`
|
/// Delete the Pubkey `key`
|
||||||
pub fn delete_key(&self, key: &Pubkey) {
|
pub fn delete_key(&self, key: &Pubkey) {
|
||||||
let ix = self.bucket_ix(key);
|
self.get_bucket(key).delete_key(key);
|
||||||
if let Some(bucket) = self.buckets[ix].write().unwrap().as_mut() {
|
|
||||||
bucket.delete_key(key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update Pubkey `key`'s value with 'value'
|
/// Update Pubkey `key`'s value with 'value'
|
||||||
pub fn insert(&self, ix: usize, key: &Pubkey, value: (&[T], RefCount)) {
|
pub fn insert(&self, key: &Pubkey, value: (&[T], RefCount)) {
|
||||||
let mut bucket = self.get_bucket(ix);
|
self.get_bucket(key).insert(key, value)
|
||||||
bucket.as_mut().unwrap().insert(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_bucket(&self, ix: usize) -> RwLockWriteGuard<Option<Bucket<T>>> {
|
|
||||||
let mut bucket = self.buckets[ix].write().unwrap();
|
|
||||||
if bucket.is_none() {
|
|
||||||
*bucket = Some(Bucket::new(
|
|
||||||
Arc::clone(&self.drives),
|
|
||||||
self.max_search,
|
|
||||||
Arc::clone(&self.stats),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
bucket
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update Pubkey `key`'s value with 'value'
|
/// Update Pubkey `key`'s value with 'value'
|
||||||
pub fn try_insert(
|
pub fn try_insert(&self, key: &Pubkey, value: (&[T], RefCount)) -> Result<(), BucketMapError> {
|
||||||
&self,
|
self.get_bucket(key).try_write(key, value)
|
||||||
ix: usize,
|
|
||||||
key: &Pubkey,
|
|
||||||
value: (&[T], RefCount),
|
|
||||||
) -> Result<(), BucketMapError> {
|
|
||||||
let mut bucket = self.get_bucket(ix);
|
|
||||||
bucket.as_mut().unwrap().try_write(key, value.0, value.1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// if err is a grow error, then grow the appropriate piece
|
|
||||||
pub fn grow(&self, ix: usize, err: BucketMapError) {
|
|
||||||
let mut bucket = self.get_bucket(ix);
|
|
||||||
bucket.as_mut().unwrap().grow(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update Pubkey `key`'s value with function `updatefn`
|
/// Update Pubkey `key`'s value with function `updatefn`
|
||||||
@ -205,9 +139,15 @@ impl<T: Clone + Copy + Debug> BucketMap<T> {
|
|||||||
where
|
where
|
||||||
F: Fn(Option<(&[T], RefCount)>) -> Option<(Vec<T>, RefCount)>,
|
F: Fn(Option<(&[T], RefCount)>) -> Option<(Vec<T>, RefCount)>,
|
||||||
{
|
{
|
||||||
let ix = self.bucket_ix(key);
|
self.get_bucket(key).update(key, updatefn)
|
||||||
let mut bucket = self.get_bucket(ix);
|
}
|
||||||
bucket.as_mut().unwrap().update(key, updatefn)
|
|
||||||
|
pub fn get_bucket(&self, key: &Pubkey) -> &Arc<BucketApi<T>> {
|
||||||
|
self.get_bucket_from_index(self.bucket_ix(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_bucket_from_index(&self, ix: usize) -> &Arc<BucketApi<T>> {
|
||||||
|
&self.buckets[ix]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the bucket index for Pubkey `key`
|
/// Get the bucket index for Pubkey `key`
|
||||||
@ -223,15 +163,15 @@ impl<T: Clone + Copy + Debug> BucketMap<T> {
|
|||||||
/// Increment the refcount for Pubkey `key`
|
/// Increment the refcount for Pubkey `key`
|
||||||
pub fn addref(&self, key: &Pubkey) -> Option<RefCount> {
|
pub fn addref(&self, key: &Pubkey) -> Option<RefCount> {
|
||||||
let ix = self.bucket_ix(key);
|
let ix = self.bucket_ix(key);
|
||||||
let mut bucket = self.buckets[ix].write().unwrap();
|
let bucket = &self.buckets[ix];
|
||||||
bucket.as_mut()?.addref(key)
|
bucket.addref(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrement the refcount for Pubkey `key`
|
/// Decrement the refcount for Pubkey `key`
|
||||||
pub fn unref(&self, key: &Pubkey) -> Option<RefCount> {
|
pub fn unref(&self, key: &Pubkey) -> Option<RefCount> {
|
||||||
let ix = self.bucket_ix(key);
|
let ix = self.bucket_ix(key);
|
||||||
let mut bucket = self.buckets[ix].write().unwrap();
|
let bucket = &self.buckets[ix];
|
||||||
bucket.as_mut()?.unref(key)
|
bucket.unref(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,6 +187,7 @@ mod tests {
|
|||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bucket_map_test_insert() {
|
fn bucket_map_test_insert() {
|
||||||
@ -263,21 +204,21 @@ mod tests {
|
|||||||
let key = Pubkey::new_unique();
|
let key = Pubkey::new_unique();
|
||||||
let config = BucketMapConfig::new(1 << 1);
|
let config = BucketMapConfig::new(1 << 1);
|
||||||
let index = BucketMap::new(config);
|
let index = BucketMap::new(config);
|
||||||
let ix = index.bucket_ix(&key);
|
let bucket = index.get_bucket(&key);
|
||||||
if pass == 0 {
|
if pass == 0 {
|
||||||
index.insert(ix, &key, (&[0], 0));
|
index.insert(&key, (&[0], 0));
|
||||||
} else {
|
} else {
|
||||||
let result = index.try_insert(ix, &key, (&[0], 0));
|
let result = index.try_insert(&key, (&[0], 0));
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert_eq!(index.read_value(&key), None);
|
assert_eq!(index.read_value(&key), None);
|
||||||
if pass == 2 {
|
if pass == 2 {
|
||||||
// another call to try insert again - should still return an error
|
// another call to try insert again - should still return an error
|
||||||
let result = index.try_insert(ix, &key, (&[0], 0));
|
let result = index.try_insert(&key, (&[0], 0));
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert_eq!(index.read_value(&key), None);
|
assert_eq!(index.read_value(&key), None);
|
||||||
}
|
}
|
||||||
index.grow(ix, result.unwrap_err());
|
bucket.grow(result.unwrap_err());
|
||||||
let result = index.try_insert(ix, &key, (&[0], 0));
|
let result = index.try_insert(&key, (&[0], 0));
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
assert_eq!(index.read_value(&key), Some((vec![0], 0)));
|
assert_eq!(index.read_value(&key), Some((vec![0], 0)));
|
||||||
@ -289,9 +230,9 @@ mod tests {
|
|||||||
let key = Pubkey::new_unique();
|
let key = Pubkey::new_unique();
|
||||||
let config = BucketMapConfig::new(1 << 1);
|
let config = BucketMapConfig::new(1 << 1);
|
||||||
let index = BucketMap::new(config);
|
let index = BucketMap::new(config);
|
||||||
index.insert(index.bucket_ix(&key), &key, (&[0], 0));
|
index.insert(&key, (&[0], 0));
|
||||||
assert_eq!(index.read_value(&key), Some((vec![0], 0)));
|
assert_eq!(index.read_value(&key), Some((vec![0], 0)));
|
||||||
index.insert(index.bucket_ix(&key), &key, (&[1], 0));
|
index.insert(&key, (&[1], 0));
|
||||||
assert_eq!(index.read_value(&key), Some((vec![1], 0)));
|
assert_eq!(index.read_value(&key), Some((vec![1], 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,8 +406,8 @@ mod tests {
|
|||||||
let mut r = vec![];
|
let mut r = vec![];
|
||||||
for bin in 0..map.num_buckets() {
|
for bin in 0..map.num_buckets() {
|
||||||
r.append(
|
r.append(
|
||||||
&mut map
|
&mut map.buckets[bin]
|
||||||
.items_in_range(bin, &None::<&std::ops::RangeInclusive<Pubkey>>),
|
.items_in_range(&None::<&std::ops::RangeInclusive<Pubkey>>),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
r
|
r
|
||||||
@ -505,7 +446,7 @@ mod tests {
|
|||||||
let insert = thread_rng().gen_range(0, 2) == 0;
|
let insert = thread_rng().gen_range(0, 2) == 0;
|
||||||
maps.iter().for_each(|map| {
|
maps.iter().for_each(|map| {
|
||||||
if insert {
|
if insert {
|
||||||
map.insert(map.bucket_ix(&k), &k, (&v.0, v.1))
|
map.insert(&k, (&v.0, v.1))
|
||||||
} else {
|
} else {
|
||||||
map.update(&k, |current| {
|
map.update(&k, |current| {
|
||||||
assert!(current.is_none());
|
assert!(current.is_none());
|
||||||
@ -524,7 +465,7 @@ mod tests {
|
|||||||
let insert = thread_rng().gen_range(0, 2) == 0;
|
let insert = thread_rng().gen_range(0, 2) == 0;
|
||||||
maps.iter().for_each(|map| {
|
maps.iter().for_each(|map| {
|
||||||
if insert {
|
if insert {
|
||||||
map.insert(map.bucket_ix(&k), &k, (&v, rc))
|
map.insert(&k, (&v, rc))
|
||||||
} else {
|
} else {
|
||||||
map.update(&k, |current| {
|
map.update(&k, |current| {
|
||||||
assert_eq!(current, v_old.map(|(v, rc)| (&v[..], *rc)), "{}", k);
|
assert_eq!(current, v_old.map(|(v, rc)| (&v[..], *rc)), "{}", k);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![allow(clippy::integer_arithmetic)]
|
#![allow(clippy::integer_arithmetic)]
|
||||||
mod bucket;
|
mod bucket;
|
||||||
|
pub mod bucket_api;
|
||||||
mod bucket_item;
|
mod bucket_item;
|
||||||
pub mod bucket_map;
|
pub mod bucket_map;
|
||||||
mod bucket_stats;
|
mod bucket_stats;
|
||||||
|
@ -6,6 +6,7 @@ use crate::bucket_map_holder::{Age, BucketMapHolder};
|
|||||||
use crate::bucket_map_holder_stats::BucketMapHolderStats;
|
use crate::bucket_map_holder_stats::BucketMapHolderStats;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
use solana_bucket_map::bucket_api::BucketApi;
|
||||||
use solana_measure::measure::Measure;
|
use solana_measure::measure::Measure;
|
||||||
use solana_sdk::{clock::Slot, pubkey::Pubkey};
|
use solana_sdk::{clock::Slot, pubkey::Pubkey};
|
||||||
use std::collections::{hash_map::Entry, HashMap};
|
use std::collections::{hash_map::Entry, HashMap};
|
||||||
@ -28,6 +29,8 @@ pub struct InMemAccountsIndex<T: IndexValue> {
|
|||||||
storage: Arc<BucketMapHolder<T>>,
|
storage: Arc<BucketMapHolder<T>>,
|
||||||
bin: usize,
|
bin: usize,
|
||||||
|
|
||||||
|
bucket: Option<Arc<BucketApi<SlotT<T>>>>,
|
||||||
|
|
||||||
// pubkey ranges that this bin must hold in the cache while the range is present in this vec
|
// pubkey ranges that this bin must hold in the cache while the range is present in this vec
|
||||||
pub(crate) cache_ranges_held: CacheRangesHeld,
|
pub(crate) cache_ranges_held: CacheRangesHeld,
|
||||||
// true while ranges are being manipulated. Used to keep an async flush from removing things while a range is being held.
|
// true while ranges are being manipulated. Used to keep an async flush from removing things while a range is being held.
|
||||||
@ -51,6 +54,11 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
|||||||
map_internal: RwLock::default(),
|
map_internal: RwLock::default(),
|
||||||
storage: Arc::clone(storage),
|
storage: Arc::clone(storage),
|
||||||
bin,
|
bin,
|
||||||
|
bucket: storage
|
||||||
|
.disk
|
||||||
|
.as_ref()
|
||||||
|
.map(|disk| disk.get_bucket_from_index(bin))
|
||||||
|
.map(Arc::clone),
|
||||||
cache_ranges_held: CacheRangesHeld::default(),
|
cache_ranges_held: CacheRangesHeld::default(),
|
||||||
stop_flush: AtomicU64::default(),
|
stop_flush: AtomicU64::default(),
|
||||||
bin_dirty: AtomicBool::default(),
|
bin_dirty: AtomicBool::default(),
|
||||||
@ -111,7 +119,7 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn load_from_disk(&self, pubkey: &Pubkey) -> Option<(SlotList<T>, RefCount)> {
|
fn load_from_disk(&self, pubkey: &Pubkey) -> Option<(SlotList<T>, RefCount)> {
|
||||||
self.storage.disk.as_ref().and_then(|disk| {
|
self.bucket.as_ref().and_then(|disk| {
|
||||||
let m = Measure::start("load_disk_found_count");
|
let m = Measure::start("load_disk_found_count");
|
||||||
let entry_disk = disk.read_value(pubkey);
|
let entry_disk = disk.read_value(pubkey);
|
||||||
match &entry_disk {
|
match &entry_disk {
|
||||||
@ -209,7 +217,7 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn delete_disk_key(&self, pubkey: &Pubkey) {
|
fn delete_disk_key(&self, pubkey: &Pubkey) {
|
||||||
if let Some(disk) = self.storage.disk.as_ref() {
|
if let Some(disk) = self.bucket.as_ref() {
|
||||||
disk.delete_key(pubkey)
|
disk.delete_key(pubkey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -579,8 +587,8 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
|||||||
let m = Measure::start("range");
|
let m = Measure::start("range");
|
||||||
|
|
||||||
// load from disk
|
// load from disk
|
||||||
if let Some(disk) = self.storage.disk.as_ref() {
|
if let Some(disk) = self.bucket.as_ref() {
|
||||||
let items = disk.items_in_range(self.bin, range);
|
let items = disk.items_in_range(range);
|
||||||
let mut map = self.map().write().unwrap();
|
let mut map = self.map().write().unwrap();
|
||||||
let future_age = self.storage.future_age_to_flush();
|
let future_age = self.storage.future_age_to_flush();
|
||||||
for item in items {
|
for item in items {
|
||||||
@ -668,7 +676,7 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
|||||||
loop {
|
loop {
|
||||||
let mut removes = Vec::default();
|
let mut removes = Vec::default();
|
||||||
let mut removes_random = Vec::default();
|
let mut removes_random = Vec::default();
|
||||||
let disk = self.storage.disk.as_ref().unwrap();
|
let disk = self.bucket.as_ref().unwrap();
|
||||||
|
|
||||||
let mut updates = Vec::default();
|
let mut updates = Vec::default();
|
||||||
let m = Measure::start("flush_scan");
|
let m = Measure::start("flush_scan");
|
||||||
@ -706,11 +714,8 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
|||||||
continue; // marked dirty after we grabbed it above, so handle this the next time this bucket is flushed
|
continue; // marked dirty after we grabbed it above, so handle this the next time this bucket is flushed
|
||||||
}
|
}
|
||||||
flush_entries_updated_on_disk += 1;
|
flush_entries_updated_on_disk += 1;
|
||||||
disk_resize = disk.try_insert(
|
disk_resize =
|
||||||
self.bin,
|
disk.try_write(&k, (&v.slot_list.read().unwrap(), v.ref_count()));
|
||||||
&k,
|
|
||||||
(&v.slot_list.read().unwrap(), v.ref_count()),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if disk_resize.is_err() {
|
if disk_resize.is_err() {
|
||||||
// disk needs to resize, so mark all unprocessed items as dirty again so we pick them up after the resize
|
// disk needs to resize, so mark all unprocessed items as dirty again so we pick them up after the resize
|
||||||
@ -745,7 +750,7 @@ impl<T: IndexValue> InMemAccountsIndex<T> {
|
|||||||
Err(err) => {
|
Err(err) => {
|
||||||
// grow the bucket, outside of all in-mem locks.
|
// grow the bucket, outside of all in-mem locks.
|
||||||
// then, loop to try again
|
// then, loop to try again
|
||||||
disk.grow(self.bin, err);
|
disk.grow(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user