use std::collections::{btree_map::Entry, hash_map::RandomState, BTreeMap, BTreeSet};
use linked_hash_map::LinkedHashMap;
use serde::{Deserialize, Serialize};
use crate::common::get_key_range_for_prefix;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct StorageCacheConfig {
pub max_cache_size: usize,
pub max_value_entry_size: usize,
pub max_find_keys_entry_size: usize,
pub max_find_key_values_entry_size: usize,
pub max_cache_entries: usize,
pub max_cache_value_size: usize,
pub max_cache_find_keys_size: usize,
pub max_cache_find_key_values_size: usize,
}
#[derive(Eq, Hash, PartialEq, Debug)]
enum CacheKey {
Value(Vec<u8>),
FindKeys(Vec<u8>),
FindKeyValues(Vec<u8>),
}
enum ValueEntry {
DoesNotExist,
Exists,
Value(Vec<u8>),
}
impl ValueEntry {
fn size(&self) -> usize {
match self {
ValueEntry::Value(vec) => vec.len(),
_ => 0,
}
}
}
struct FindKeysEntry(BTreeSet<Vec<u8>>);
impl FindKeysEntry {
fn size(&self) -> usize {
self.0.iter().map(Vec::len).sum()
}
fn get_keys_by_prefix(&self, key_prefix: Vec<u8>) -> Vec<Vec<u8>> {
let prefix_len = key_prefix.len();
self.0
.range(get_key_range_for_prefix(key_prefix))
.map(|key| key[prefix_len..].to_vec())
.collect()
}
fn contains_key(&self, key: &[u8]) -> bool {
self.0.contains(key)
}
fn update_entry(&mut self, key: &[u8], is_some: bool) {
if is_some {
self.0.insert(key.to_vec());
} else {
self.0.remove(key);
}
}
fn delete_prefix(&mut self, key_prefix: &[u8]) {
let keys = self
.0
.range(get_key_range_for_prefix(key_prefix.to_vec()))
.cloned()
.collect::<Vec<_>>();
for key in keys {
self.0.remove(&key);
}
}
}
struct FindKeyValuesEntry(BTreeMap<Vec<u8>, Vec<u8>>);
impl FindKeyValuesEntry {
fn size(&self) -> usize {
self.0
.iter()
.map(|(key, value)| key.len() + value.len())
.sum()
}
fn get_keys_by_prefix(&self, key_prefix: Vec<u8>) -> Vec<Vec<u8>> {
let prefix_len = key_prefix.len();
self.0
.range(get_key_range_for_prefix(key_prefix))
.map(|(key, _)| key[prefix_len..].to_vec())
.collect()
}
fn get_find_key_values(&self, key_prefix: &[u8]) -> Vec<(Vec<u8>, Vec<u8>)> {
let key_prefix = key_prefix.to_vec();
let prefix_len = key_prefix.len();
self.0
.range(get_key_range_for_prefix(key_prefix))
.map(|(key, value)| (key[prefix_len..].to_vec(), value.to_vec()))
.collect()
}
fn contains_key(&self, key: &[u8]) -> bool {
self.0.contains_key(key)
}
fn get_read_value(&self, key: &[u8]) -> Option<Vec<u8>> {
self.0.get(key).cloned()
}
fn update_entry(&mut self, key: &[u8], new_value: Option<Vec<u8>>) {
match new_value {
None => {
self.0.remove(key);
}
Some(new_value) => {
self.0.insert(key.to_vec(), new_value);
}
}
}
fn delete_prefix(&mut self, key_prefix: &[u8]) {
let keys = self
.0
.range(get_key_range_for_prefix(key_prefix.to_vec()))
.map(|(key, _)| key.clone())
.collect::<Vec<_>>();
for key in keys {
self.0.remove(&key);
}
}
}
pub(crate) struct LruPrefixCache {
value_map: BTreeMap<Vec<u8>, ValueEntry>,
find_keys_map: BTreeMap<Vec<u8>, FindKeysEntry>,
find_key_values_map: BTreeMap<Vec<u8>, FindKeyValuesEntry>,
queue: LinkedHashMap<CacheKey, usize, RandomState>,
config: StorageCacheConfig,
total_size: usize,
total_value_size: usize,
total_find_keys_size: usize,
total_find_key_values_size: usize,
has_exclusive_access: bool,
}
impl LruPrefixCache {
pub(crate) fn new(config: StorageCacheConfig, has_exclusive_access: bool) -> Self {
Self {
value_map: BTreeMap::new(),
find_keys_map: BTreeMap::new(),
find_key_values_map: BTreeMap::new(),
queue: LinkedHashMap::new(),
config,
total_size: 0,
total_value_size: 0,
total_find_keys_size: 0,
total_find_key_values_size: 0,
has_exclusive_access,
}
}
pub(crate) fn has_exclusive_access(&self) -> bool {
self.has_exclusive_access
}
fn move_cache_key_on_top(&mut self, cache_key: CacheKey) {
let size = self
.queue
.remove(&cache_key)
.expect("cache_key should be present");
self.queue.insert(cache_key, size);
}
fn update_sizes(&mut self, cache_key: &CacheKey, old_size: usize, new_size: usize) {
use std::cmp::Ordering;
match new_size.cmp(&old_size) {
Ordering::Greater => {
let increase_size = new_size - old_size;
self.total_size += increase_size;
match cache_key {
CacheKey::Value(_) => {
self.total_value_size += increase_size;
}
CacheKey::FindKeys(_) => {
self.total_find_keys_size += increase_size;
}
CacheKey::FindKeyValues(_) => {
self.total_find_key_values_size += increase_size;
}
}
}
Ordering::Less => {
let decrease_size = old_size - new_size;
self.total_size -= decrease_size;
match cache_key {
CacheKey::Value(_) => {
self.total_value_size -= decrease_size;
}
CacheKey::FindKeys(_) => {
self.total_find_keys_size -= decrease_size;
}
CacheKey::FindKeyValues(_) => {
self.total_find_key_values_size -= decrease_size;
}
}
}
Ordering::Equal => {
}
}
}
fn increase_sizes(&mut self, cache_key: &CacheKey, size: usize) {
self.update_sizes(cache_key, 0, size);
}
fn decrease_sizes(&mut self, cache_key: &CacheKey, size: usize) {
self.update_sizes(cache_key, size, 0);
}
fn remove_cache_key_from_map(&mut self, cache_key: &CacheKey) {
match cache_key {
CacheKey::Value(key) => {
assert!(self.value_map.remove(key).is_some());
}
CacheKey::FindKeys(key) => {
assert!(self.find_keys_map.remove(key).is_some());
}
CacheKey::FindKeyValues(key) => {
assert!(self.find_key_values_map.remove(key).is_some());
}
}
}
fn remove_cache_key(&mut self, cache_key: &CacheKey) {
let size = self
.queue
.remove(cache_key)
.expect("cache_key should be present");
self.decrease_sizes(cache_key, size);
}
fn remove_cache_key_if_exists(&mut self, cache_key: &CacheKey) {
let size = self.queue.remove(cache_key);
if let Some(size) = size {
self.decrease_sizes(cache_key, size);
self.remove_cache_key_from_map(cache_key);
}
}
fn update_cache_key_sizes(&mut self, cache_key: &CacheKey, new_size: usize) {
let size = self
.queue
.get_mut(cache_key)
.expect("cache_key should be present");
let old_size = *size;
*size = new_size;
self.update_sizes(cache_key, old_size, new_size);
}
fn insert_cache_key(&mut self, cache_key: CacheKey, size: usize) {
self.increase_sizes(&cache_key, size);
assert!(self.queue.insert(cache_key, size).is_none());
}
fn get_existing_find_keys_entry(&self, key: &[u8]) -> Option<(&Vec<u8>, &FindKeysEntry)> {
match self.find_keys_map.range(..=key.to_vec()).next_back() {
None => None,
Some((stored_key, value)) => {
if key.starts_with(stored_key) {
Some((stored_key, value))
} else {
None
}
}
}
}
fn get_existing_keys_entry_mut(
&mut self,
key: &[u8],
) -> Option<(&Vec<u8>, &mut FindKeysEntry)> {
match self.find_keys_map.range_mut(..=key.to_vec()).next_back() {
None => None,
Some((stored_key, value)) => {
if key.starts_with(stored_key) {
Some((stored_key, value))
} else {
None
}
}
}
}
fn get_existing_find_key_values_entry(
&self,
key: &[u8],
) -> Option<(&Vec<u8>, &FindKeyValuesEntry)> {
match self.find_key_values_map.range(..=key.to_vec()).next_back() {
None => None,
Some((stored_key, value)) => {
if key.starts_with(stored_key) {
Some((stored_key, value))
} else {
None
}
}
}
}
fn get_existing_find_key_values_entry_mut(
&mut self,
key: &[u8],
) -> Option<(&Vec<u8>, &mut FindKeyValuesEntry)> {
match self
.find_key_values_map
.range_mut(..=key.to_vec())
.next_back()
{
None => None,
Some((stored_key, value)) => {
if key.starts_with(stored_key) {
Some((stored_key, value))
} else {
None
}
}
}
}
fn trim_value_cache(&mut self) {
let mut keys = Vec::new();
let mut control_size = self.total_value_size;
let mut iter = self.queue.iter();
loop {
let value = iter.next();
let Some((cache_key, size)) = value else {
break;
};
if control_size < self.config.max_cache_value_size {
break;
}
if let CacheKey::Value(key) = cache_key {
control_size -= size;
keys.push(key.to_vec());
}
}
for key in keys {
assert!(self.value_map.remove(&key).is_some());
let cache_key = CacheKey::Value(key);
self.remove_cache_key(&cache_key);
}
}
fn trim_find_keys_cache(&mut self) {
let mut prefixes = Vec::new();
let mut control_size = self.total_find_keys_size;
let mut iter = self.queue.iter();
loop {
let value = iter.next();
let Some((cache_key, size)) = value else {
break;
};
if control_size < self.config.max_cache_find_keys_size {
break;
}
if let CacheKey::FindKeys(prefix) = cache_key {
control_size -= size;
prefixes.push(prefix.to_vec());
}
}
for prefix in prefixes {
assert!(self.find_keys_map.remove(&prefix).is_some());
let cache_key = CacheKey::FindKeys(prefix);
self.remove_cache_key(&cache_key);
}
}
fn trim_find_key_values_cache(&mut self) {
let mut prefixes = Vec::new();
let mut control_size = self.total_find_key_values_size;
let mut iter = self.queue.iter();
loop {
let value = iter.next();
let Some((cache_key, size)) = value else {
break;
};
if control_size < self.config.max_cache_find_key_values_size {
break;
}
if let CacheKey::FindKeyValues(prefix) = cache_key {
control_size -= size;
prefixes.push(prefix.to_vec());
}
}
for prefix in prefixes {
assert!(self.find_key_values_map.remove(&prefix).is_some());
let cache_key = CacheKey::FindKeyValues(prefix);
self.remove_cache_key(&cache_key);
}
}
fn trim_cache(&mut self) {
while self.total_size >= self.config.max_cache_size
|| self.queue.len() >= self.config.max_cache_entries
{
let Some((cache_key, size)) = self.queue.pop_front() else {
break;
};
self.decrease_sizes(&cache_key, size);
self.remove_cache_key_from_map(&cache_key);
}
}
fn insert_value(&mut self, key: &[u8], cache_entry: ValueEntry) {
if self.config.max_value_entry_size == 0 {
return;
}
let size = key.len() + cache_entry.size();
if (matches!(cache_entry, ValueEntry::DoesNotExist) && !self.has_exclusive_access)
|| size > self.config.max_value_entry_size
{
if self.value_map.remove(key).is_some() {
let cache_key = CacheKey::Value(key.to_vec());
self.remove_cache_key(&cache_key);
};
return;
}
match self.value_map.entry(key.to_vec()) {
Entry::Occupied(mut entry) => {
entry.insert(cache_entry);
let cache_key = CacheKey::Value(key.to_vec());
self.remove_cache_key(&cache_key);
self.insert_cache_key(cache_key, size);
}
Entry::Vacant(entry) => {
entry.insert(cache_entry);
let cache_key = CacheKey::Value(key.to_vec());
self.insert_cache_key(cache_key, size);
}
}
self.trim_value_cache();
self.trim_cache();
}
pub(crate) fn put_key_value(&mut self, key: &[u8], value: &[u8]) {
if self.has_exclusive_access {
let lower_bound = self.get_existing_keys_entry_mut(key);
if let Some((lower_bound, cache_entry)) = lower_bound {
let reduced_key = &key[lower_bound.len()..];
cache_entry.update_entry(reduced_key, true);
let cache_key = CacheKey::FindKeys(lower_bound.to_vec());
let new_size = lower_bound.len() + cache_entry.size();
self.update_cache_key_sizes(&cache_key, new_size);
}
match self.get_existing_find_key_values_entry_mut(key) {
Some((lower_bound, cache_entry)) => {
let reduced_key = &key[lower_bound.len()..];
cache_entry.update_entry(reduced_key, Some(value.to_vec()));
let cache_key = CacheKey::FindKeyValues(lower_bound.to_vec());
let new_size = lower_bound.len() + cache_entry.size();
self.update_cache_key_sizes(&cache_key, new_size);
let cache_key = CacheKey::Value(key.to_vec());
self.remove_cache_key_if_exists(&cache_key);
}
None => {
let cache_entry = ValueEntry::Value(value.to_vec());
self.insert_value(key, cache_entry);
}
}
} else {
let cache_entry = ValueEntry::Value(value.to_vec());
self.insert_value(key, cache_entry);
}
}
pub(crate) fn delete_key(&mut self, key: &[u8]) {
if self.has_exclusive_access {
let lower_bound = self.get_existing_keys_entry_mut(key);
let mut matching = false; if let Some((lower_bound, cache_entry)) = lower_bound {
let reduced_key = &key[lower_bound.len()..];
cache_entry.update_entry(reduced_key, false);
let cache_key = CacheKey::FindKeys(lower_bound.to_vec());
let new_size = lower_bound.len() + cache_entry.size();
self.update_cache_key_sizes(&cache_key, new_size);
matching = true;
}
let lower_bound = self.get_existing_find_key_values_entry_mut(key);
if let Some((lower_bound, cache_entry)) = lower_bound {
let reduced_key = &key[lower_bound.len()..];
cache_entry.update_entry(reduced_key, None);
let cache_key = CacheKey::FindKeyValues(lower_bound.to_vec());
let new_size = lower_bound.len() + cache_entry.size();
self.update_cache_key_sizes(&cache_key, new_size);
matching = true;
}
if !matching {
let cache_entry = ValueEntry::DoesNotExist;
self.insert_value(key, cache_entry);
} else {
let cache_key = CacheKey::Value(key.to_vec());
self.remove_cache_key_if_exists(&cache_key);
}
} else {
let cache_key = CacheKey::Value(key.to_vec());
self.remove_cache_key_if_exists(&cache_key);
}
}
pub(crate) fn insert_read_value(&mut self, key: &[u8], value: &Option<Vec<u8>>) {
let cache_entry = match value {
None => ValueEntry::DoesNotExist,
Some(vec) => ValueEntry::Value(vec.to_vec()),
};
self.insert_value(key, cache_entry)
}
pub(crate) fn insert_contains_key(&mut self, key: &[u8], result: bool) {
let cache_entry = if result {
ValueEntry::Exists
} else {
ValueEntry::DoesNotExist
};
self.insert_value(key, cache_entry)
}
pub(crate) fn insert_find_keys(&mut self, key_prefix: Vec<u8>, keys: &[Vec<u8>]) {
if self.config.max_find_keys_entry_size == 0 {
return;
}
let size = key_prefix.len() + keys.iter().map(Vec::len).sum::<usize>();
if size > self.config.max_find_keys_entry_size {
return;
}
let find_entry = FindKeysEntry(keys.iter().cloned().collect());
let keys = self
.find_keys_map
.range(get_key_range_for_prefix(key_prefix.clone()))
.map(|(x, _)| x.clone())
.collect::<Vec<_>>();
for key in keys {
assert!(self.find_keys_map.remove(&key).is_some());
let cache_key = CacheKey::FindKeys(key);
self.remove_cache_key(&cache_key);
}
let keys = self
.value_map
.range(get_key_range_for_prefix(key_prefix.clone()))
.filter_map(|(key, value)| match value {
ValueEntry::DoesNotExist => Some(key.to_vec()),
ValueEntry::Exists => Some(key.to_vec()),
ValueEntry::Value(_) => None,
})
.collect::<Vec<_>>();
for key in keys {
assert!(self.value_map.remove(&key).is_some());
let cache_key = CacheKey::Value(key);
self.remove_cache_key(&cache_key);
}
let cache_key = CacheKey::FindKeys(key_prefix.clone());
assert!(self.find_keys_map.insert(key_prefix, find_entry).is_none());
self.insert_cache_key(cache_key, size);
self.trim_find_keys_cache();
self.trim_cache();
}
pub(crate) fn insert_find_key_values(
&mut self,
key_prefix: Vec<u8>,
key_values: &[(Vec<u8>, Vec<u8>)],
) {
if self.config.max_find_key_values_entry_size == 0 {
return;
}
let size = key_prefix.len()
+ key_values
.iter()
.map(|(k, v)| k.len() + v.len())
.sum::<usize>();
if size > self.config.max_find_key_values_entry_size {
return;
}
let find_entry = FindKeyValuesEntry(
key_values
.iter()
.map(|(k, v)| (k.to_vec(), v.to_vec()))
.collect(),
);
let prefixes = self
.find_keys_map
.range(get_key_range_for_prefix(key_prefix.clone()))
.map(|(x, _)| x.clone())
.collect::<Vec<_>>();
for prefix in prefixes {
assert!(self.find_keys_map.remove(&prefix).is_some());
let cache_key = CacheKey::FindKeys(prefix);
self.remove_cache_key(&cache_key);
}
let prefixes = self
.find_key_values_map
.range(get_key_range_for_prefix(key_prefix.clone()))
.map(|(x, _)| x.clone())
.collect::<Vec<_>>();
for prefix in prefixes {
assert!(self.find_key_values_map.remove(&prefix).is_some());
let cache_key = CacheKey::FindKeyValues(prefix);
self.remove_cache_key(&cache_key);
}
let keys = self
.value_map
.range(get_key_range_for_prefix(key_prefix.clone()))
.map(|(x, _)| x.clone())
.collect::<Vec<_>>();
for key in keys {
assert!(self.value_map.remove(&key).is_some());
let cache_key = CacheKey::Value(key);
self.remove_cache_key(&cache_key);
}
let cache_key = CacheKey::FindKeyValues(key_prefix.clone());
assert!(self
.find_key_values_map
.insert(key_prefix, find_entry)
.is_none());
self.insert_cache_key(cache_key, size);
self.trim_find_key_values_cache();
self.trim_cache();
}
pub(crate) fn delete_prefix(&mut self, key_prefix: &[u8]) {
let mut keys = Vec::new();
for (key, _) in self
.value_map
.range(get_key_range_for_prefix(key_prefix.to_vec()))
{
keys.push(key.to_vec());
}
for key in keys {
assert!(self.value_map.remove(&key).is_some());
let cache_key = CacheKey::Value(key);
self.remove_cache_key(&cache_key);
}
if self.has_exclusive_access {
let mut prefixes = Vec::new();
for (prefix, _) in self
.find_keys_map
.range(get_key_range_for_prefix(key_prefix.to_vec()))
{
prefixes.push(prefix.to_vec());
}
for prefix in prefixes {
assert!(self.find_keys_map.remove(&prefix).is_some());
let cache_key = CacheKey::FindKeys(prefix);
self.remove_cache_key(&cache_key);
}
let mut prefixes = Vec::new();
for (prefix, _) in self
.find_key_values_map
.range(get_key_range_for_prefix(key_prefix.to_vec()))
{
prefixes.push(prefix.to_vec());
}
for prefix in prefixes {
assert!(self.find_key_values_map.remove(&prefix).is_some());
let cache_key = CacheKey::FindKeyValues(prefix);
self.remove_cache_key(&cache_key);
}
let lower_bound = self.get_existing_keys_entry_mut(key_prefix);
let result = if let Some((lower_bound, find_entry)) = lower_bound {
let key_prefix_red = &key_prefix[lower_bound.len()..];
find_entry.delete_prefix(key_prefix_red);
let new_cache_size = find_entry.size() + lower_bound.len();
Some((new_cache_size, lower_bound.clone()))
} else {
None
};
if let Some((new_cache_size, lower_bound)) = result {
let cache_key = CacheKey::FindKeys(lower_bound.clone());
self.update_cache_key_sizes(&cache_key, new_cache_size);
}
let lower_bound = self.get_existing_find_key_values_entry_mut(key_prefix);
let result = if let Some((lower_bound, find_entry)) = lower_bound {
let key_prefix_red = &key_prefix[lower_bound.len()..];
find_entry.delete_prefix(key_prefix_red);
let new_cache_size = find_entry.size() + lower_bound.len();
Some((new_cache_size, lower_bound.clone()))
} else {
None
};
if let Some((new_cache_size, lower_bound)) = result {
let cache_key = CacheKey::FindKeyValues(lower_bound.clone());
self.update_cache_key_sizes(&cache_key, new_cache_size);
} else {
let size = key_prefix.len();
let cache_key = CacheKey::FindKeyValues(key_prefix.to_vec());
let find_key_values_entry = FindKeyValuesEntry(BTreeMap::new());
self.find_key_values_map
.insert(key_prefix.to_vec(), find_key_values_entry);
self.insert_cache_key(cache_key, size);
}
}
}
pub(crate) fn query_read_value(&mut self, key: &[u8]) -> Option<Option<Vec<u8>>> {
let result = match self.value_map.get(key) {
None => None,
Some(entry) => match entry {
ValueEntry::DoesNotExist => Some(None),
ValueEntry::Exists => None,
ValueEntry::Value(vec) => Some(Some(vec.clone())),
},
};
if result.is_some() {
let cache_key = CacheKey::Value(key.to_vec());
self.move_cache_key_on_top(cache_key);
return result;
}
if self.has_exclusive_access {
let lower_bound = self.get_existing_find_key_values_entry(key);
let (lower_bound, result) = if let Some((lower_bound, find_entry)) = lower_bound {
let reduced_key = &key[lower_bound.len()..];
(lower_bound, find_entry.get_read_value(reduced_key))
} else {
return None;
};
let cache_key = CacheKey::FindKeyValues(lower_bound.clone());
self.move_cache_key_on_top(cache_key);
Some(result)
} else {
None
}
}
pub(crate) fn query_contains_key(&mut self, key: &[u8]) -> Option<bool> {
let result = self
.value_map
.get(key)
.map(|entry| !matches!(entry, ValueEntry::DoesNotExist));
if result.is_some() {
let cache_key = CacheKey::Value(key.to_vec());
self.move_cache_key_on_top(cache_key);
return result;
}
if self.has_exclusive_access {
let lower_bound = self.get_existing_find_keys_entry(key);
let result = if let Some((lower_bound, find_entry)) = lower_bound {
let reduced_key = &key[lower_bound.len()..];
Some((lower_bound, find_entry.contains_key(reduced_key)))
} else {
None
};
if let Some((lower_bound, result)) = result {
let cache_key = CacheKey::FindKeys(lower_bound.clone());
self.move_cache_key_on_top(cache_key);
return Some(result);
}
let lower_bound = self.get_existing_find_key_values_entry(key);
let (lower_bound, result) = if let Some((lower_bound, find_entry)) = lower_bound {
let reduced_key = &key[lower_bound.len()..];
(lower_bound, find_entry.contains_key(reduced_key))
} else {
return None;
};
let cache_key = CacheKey::FindKeyValues(lower_bound.clone());
self.move_cache_key_on_top(cache_key);
return Some(result);
}
result
}
pub(crate) fn query_find_keys(&mut self, key_prefix: &[u8]) -> Option<Vec<Vec<u8>>> {
let result = match self.get_existing_find_keys_entry(key_prefix) {
None => None,
Some((lower_bound, cache_entry)) => {
let key_prefix_red = key_prefix[lower_bound.len()..].to_vec();
Some((lower_bound, cache_entry.get_keys_by_prefix(key_prefix_red)))
}
};
if let Some((lower_bound, keys)) = result {
let cache_key = CacheKey::FindKeys(lower_bound.clone());
self.move_cache_key_on_top(cache_key);
return Some(keys);
}
let (lower_bound, result) = match self.get_existing_find_key_values_entry(key_prefix) {
None => {
return None;
}
Some((lower_bound, cache_entry)) => {
let key_prefix_red = key_prefix[lower_bound.len()..].to_vec();
(lower_bound, cache_entry.get_keys_by_prefix(key_prefix_red))
}
};
let cache_key = CacheKey::FindKeyValues(lower_bound.clone());
self.move_cache_key_on_top(cache_key);
Some(result)
}
pub(crate) fn query_find_key_values(
&mut self,
key_prefix: &[u8],
) -> Option<Vec<(Vec<u8>, Vec<u8>)>> {
let (lower_bound, result) = match self.get_existing_find_key_values_entry(key_prefix) {
None => {
return None;
}
Some((lower_bound, cache_entry)) => {
let key_prefix_red = &key_prefix[lower_bound.len()..];
(lower_bound, cache_entry.get_find_key_values(key_prefix_red))
}
};
let cache_key = CacheKey::FindKeyValues(lower_bound.to_vec());
self.move_cache_key_on_top(cache_key);
Some(result)
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeSet;
use rand::Rng;
use super::*;
fn check_prefix_free(set: BTreeSet<Vec<u8>>) {
let keys = set.into_iter().collect::<Vec<_>>();
let num_keys = keys.len();
println!("keys={keys:?}");
for i in 0..num_keys {
for j in 0..num_keys {
if i != j {
let key1 = keys[i].clone();
let key2 = keys[j].clone();
println!("i={i} j={j} key1={key1:?} key2={key2:?}");
assert!(!key1.starts_with(&key2));
}
}
}
}
fn check_intersection(keys: Vec<Vec<u8>>, prefixes: &BTreeSet<Vec<u8>>) {
for key in keys {
for prefix in prefixes {
assert!(!key.starts_with(prefix), "key matches a prefix");
}
}
}
impl LruPrefixCache {
fn check_coherence(&self) {
let value_map_set = self
.value_map
.keys()
.cloned()
.collect::<BTreeSet<Vec<u8>>>();
let find_keys_map_set = self
.find_keys_map
.keys()
.cloned()
.collect::<BTreeSet<Vec<u8>>>();
let find_key_values_map_set = self
.find_key_values_map
.keys()
.cloned()
.collect::<BTreeSet<Vec<u8>>>();
check_prefix_free(find_keys_map_set.clone());
check_prefix_free(find_key_values_map_set.clone());
let mut value_queue_set = BTreeSet::new();
let mut find_keys_queue_set = BTreeSet::new();
let mut find_key_values_queue_set = BTreeSet::new();
let mut total_size = 0;
let mut total_value_size = 0;
let mut total_find_keys_size = 0;
let mut total_find_key_values_size = 0;
for (cache_key, size) in &self.queue {
let queue_size = *size;
match cache_key {
CacheKey::Value(key) => {
let value = self.value_map.get(key).unwrap();
let map_size = key.len() + value.size();
assert_eq!(map_size, queue_size, "Incoherence in value size");
value_queue_set.insert(key.clone());
total_value_size += queue_size;
}
CacheKey::FindKeys(key) => {
let value = self.find_keys_map.get(key).unwrap();
let map_size = key.len() + value.size();
assert_eq!(map_size, queue_size, "Incoherence in find-keys size");
find_keys_queue_set.insert(key.clone());
total_find_keys_size += queue_size;
}
CacheKey::FindKeyValues(key) => {
let value = self.find_key_values_map.get(key).unwrap();
let map_size = key.len() + value.size();
assert_eq!(map_size, queue_size, "Incoherence in find-keys size");
find_key_values_queue_set.insert(key.clone());
total_find_key_values_size += queue_size;
}
}
total_size += queue_size;
}
let value_map_vect = self.value_map.keys().cloned().collect::<Vec<_>>();
check_intersection(value_map_vect, &find_key_values_map_set);
let value_existence_map_vect = self
.value_map
.iter()
.filter_map(|(key, value)| match value {
ValueEntry::DoesNotExist => Some(key.to_vec()),
ValueEntry::Exists => Some(key.to_vec()),
ValueEntry::Value(_) => None,
})
.collect::<Vec<_>>();
check_intersection(value_existence_map_vect, &find_keys_map_set);
assert_eq!(
value_queue_set, value_map_set,
"Incoherence in value_map keys"
);
assert_eq!(
find_keys_queue_set, find_keys_map_set,
"Incoherence in find_keys_map keys"
);
assert_eq!(
find_key_values_queue_set, find_key_values_map_set,
"Incoherence in find_key_values_map keys"
);
assert_eq!(total_size, self.total_size, "The total_size are incoherent");
assert_eq!(
total_value_size, self.total_value_size,
"The total_value_size are incoherent"
);
assert_eq!(
total_find_keys_size, self.total_find_keys_size,
"The total_find_keys_size are incoherent"
);
assert_eq!(
total_find_key_values_size, self.total_find_key_values_size,
"The total_find_key_values_size are incoherent"
);
if self.config.max_cache_size > 0 {
assert!(
total_size < self.config.max_cache_size,
"The total_size is too large"
);
} else {
assert!(total_size == 0, "The total_size should be 0");
}
if self.config.max_cache_value_size > 0 {
assert!(
total_value_size < self.config.max_cache_value_size,
"The total_value_size is too large"
);
} else {
assert!(total_value_size == 0, "The total_value_size should be 0");
}
if self.config.max_cache_find_keys_size > 0 {
assert!(
total_find_keys_size < self.config.max_cache_find_keys_size,
"The total_value_size is too large"
);
} else {
assert!(
total_find_keys_size == 0,
"The total_find_keys_size should be 0"
);
}
if self.config.max_cache_find_key_values_size > 0 {
assert!(
total_find_key_values_size < self.config.max_cache_find_key_values_size,
"The total_find_key_values_size is too large"
);
} else {
assert!(
total_find_key_values_size == 0,
"The total_find_key_values_size should be 0"
);
}
}
}
fn create_test_cache(has_exclusive_access: bool) -> LruPrefixCache {
let config = StorageCacheConfig {
max_cache_size: 1000,
max_value_entry_size: 50,
max_find_keys_entry_size: 100,
max_find_key_values_entry_size: 200,
max_cache_entries: 10,
max_cache_value_size: 500,
max_cache_find_keys_size: 500,
max_cache_find_key_values_size: 500,
};
LruPrefixCache::new(config, has_exclusive_access)
}
#[test]
fn test_new_cache_creation() {
let cache = create_test_cache(true);
assert_eq!(cache.total_size, 0);
assert_eq!(cache.total_value_size, 0);
assert_eq!(cache.total_find_keys_size, 0);
assert_eq!(cache.total_find_key_values_size, 0);
assert!(cache.has_exclusive_access);
assert!(cache.value_map.is_empty());
assert!(cache.find_keys_map.is_empty());
assert!(cache.find_key_values_map.is_empty());
assert!(cache.queue.is_empty());
}
#[test]
fn test_insert_and_query_read_value() {
let mut cache = create_test_cache(true);
let key = vec![1, 2, 3];
let value = vec![4, 5, 6];
cache.insert_read_value(&key, &Some(value.clone()));
cache.check_coherence();
let result = cache.query_read_value(&key);
assert_eq!(result, Some(Some(value)));
let result = cache.query_read_value(&[9, 9, 9]);
assert_eq!(result, None);
}
#[test]
fn test_insert_and_query_contains_key() {
let mut cache = create_test_cache(true);
let key = vec![1, 2, 3];
cache.insert_contains_key(&key, true);
cache.check_coherence();
let result = cache.query_contains_key(&key);
assert_eq!(result, Some(true));
let key2 = vec![4, 5, 6];
cache.insert_contains_key(&key2, false);
let result = cache.query_contains_key(&key2);
assert_eq!(result, Some(false));
}
#[test]
fn test_insert_and_query_find_keys() {
let mut cache = create_test_cache(true);
let prefix = vec![1, 2];
let keys = vec![vec![3], vec![4], vec![5]];
cache.insert_find_keys(prefix.clone(), &keys);
cache.check_coherence();
let result = cache.query_find_keys(&prefix);
assert_eq!(result, Some(keys.clone()));
let longer_prefix = vec![1, 2, 3];
let result = cache.query_find_keys(&longer_prefix);
assert_eq!(result, Some(vec![vec![]]));
let result = cache.query_find_keys(&[9, 9]);
assert_eq!(result, None);
}
#[test]
fn test_insert_and_query_find_key_values() {
let mut cache = create_test_cache(true);
let prefix = vec![1, 2];
let key_values = vec![(vec![3], vec![10]), (vec![4], vec![20])];
cache.insert_find_key_values(prefix.clone(), &key_values);
cache.check_coherence();
let result = cache.query_find_key_values(&prefix);
assert_eq!(result, Some(key_values.clone()));
let result = cache.query_find_key_values(&[9, 9]);
assert_eq!(result, None);
}
#[test]
fn test_lru_eviction_by_cache_size() {
let mut cache = create_test_cache(true);
for i in 0..20 {
let key = vec![i];
let value = vec![0; 100]; cache.insert_read_value(&key, &Some(value));
cache.check_coherence();
}
assert!(cache.total_size <= cache.config.max_cache_size);
assert!(cache.queue.len() <= cache.config.max_cache_entries);
}
#[test]
fn test_lru_eviction_by_entry_count() {
let mut cache = create_test_cache(true);
for i in 0..15 {
let key = vec![i];
let value = vec![i]; cache.insert_read_value(&key, &Some(value));
cache.check_coherence();
}
assert!(cache.queue.len() <= cache.config.max_cache_entries);
}
#[test]
fn test_cache_entry_promotion() {
let mut cache = create_test_cache(true);
let key1 = vec![1];
let key2 = vec![2];
let key3 = vec![3];
let value = vec![42];
cache.insert_read_value(&key1, &Some(value.clone()));
cache.check_coherence();
cache.insert_read_value(&key2, &Some(value.clone()));
cache.check_coherence();
assert_eq!(Some(Some(value)), cache.query_read_value(&key1));
assert_eq!(None, cache.query_read_value(&key3));
let queue_keys = cache.queue.keys().collect::<Vec<_>>();
assert_eq!(queue_keys[queue_keys.len() - 1], &CacheKey::Value(key1));
}
#[test]
fn test_update_find_entry_mut() {
let mut cache = create_test_cache(true);
let prefix = vec![1];
let key = vec![1, 2];
let original_keys = vec![vec![2], vec![3]];
cache.insert_find_keys(prefix.clone(), &original_keys);
cache.check_coherence();
cache.put_key_value(&key, &[42]);
cache.check_coherence();
let result = cache.query_find_keys(&prefix);
assert!(result.is_some());
let keys = result.unwrap();
assert!(keys.contains(&vec![2]));
}
#[test]
fn test_delete_prefix_with_exclusive_access() {
let mut cache = create_test_cache(true);
let prefix = vec![1];
let key1 = vec![1, 2];
let key2 = vec![1, 3];
let key3 = vec![2, 4]; let value = vec![42];
cache.insert_read_value(&key1, &Some(value.clone()));
cache.check_coherence();
cache.insert_read_value(&key2, &Some(value.clone()));
cache.check_coherence();
cache.insert_read_value(&key3, &Some(value.clone()));
cache.check_coherence();
cache.delete_prefix(&prefix);
cache.check_coherence();
let result1 = cache.query_read_value(&key1);
assert_eq!(result1, Some(None));
let result2 = cache.query_read_value(&key2);
assert_eq!(result2, Some(None));
let result3 = cache.query_read_value(&key3);
assert_eq!(result3, Some(Some(value)));
}
#[test]
fn test_value_entry_size_limits() {
let mut cache = create_test_cache(true);
let key = vec![1];
let large_value = vec![0; cache.config.max_value_entry_size + 1];
cache.insert_read_value(&key, &Some(large_value));
cache.check_coherence();
let result = cache.query_read_value(&key);
assert_eq!(result, None);
}
#[test]
fn test_findkeys_entry_size_limits() {
let mut cache = create_test_cache(true);
let key_prefix = vec![1];
let mut keys = Vec::new();
for i in 0..cache.config.max_find_keys_entry_size {
keys.push(vec![i as u8]);
}
let size = keys.iter().map(Vec::len).sum::<usize>();
assert_eq!(cache.config.max_find_keys_entry_size, size);
cache.insert_find_keys(key_prefix.clone(), &keys);
cache.check_coherence();
let result = cache.query_find_keys(&key_prefix);
assert_eq!(result, None);
}
#[test]
fn test_findkeyvalues_entry_size_limits() {
let mut cache = create_test_cache(true);
let key_prefix = vec![1];
let mut key_values = Vec::new();
for i in 0..cache.config.max_find_key_values_entry_size / 2 {
key_values.push((vec![i as u8], vec![i as u8]));
}
let size = key_values
.iter()
.map(|(k, v)| k.len() + v.len())
.sum::<usize>();
assert_eq!(cache.config.max_find_key_values_entry_size, size);
cache.insert_find_key_values(key_prefix.clone(), &key_values);
cache.check_coherence();
let result = cache.query_find_key_values(&key_prefix);
assert_eq!(result, None);
}
#[test]
fn test_does_not_exist_entry_without_exclusive_access() {
let mut cache = create_test_cache(false);
let key = vec![1];
cache.insert_read_value(&key, &None);
cache.check_coherence();
let result = cache.query_read_value(&key);
assert_eq!(result, None);
}
#[test]
fn test_find_keys_entry_operations() {
let mut find_entry = FindKeysEntry(BTreeSet::new());
let key1 = vec![1, 2];
let key2 = vec![1, 3];
let key3 = vec![1, 3, 4];
find_entry.update_entry(&key1, true);
find_entry.update_entry(&key2, true);
assert!(find_entry.contains_key(&key1));
assert!(find_entry.contains_key(&key2));
assert!(!find_entry.contains_key(&key3));
assert!(!find_entry.contains_key(&[9, 9]));
let keys = find_entry.get_keys_by_prefix(vec![1]);
assert_eq!(keys.len(), 2);
assert!(keys.contains(&vec![2]));
assert!(keys.contains(&vec![3]));
find_entry.update_entry(&key1, false);
assert!(!find_entry.contains_key(&key1));
assert!(find_entry.contains_key(&key2));
}
#[test]
fn test_find_key_values_entry_operations() {
let mut find_entry = FindKeyValuesEntry(BTreeMap::new());
let key1 = vec![1, 2];
let key2 = vec![1, 3];
let value1 = vec![42];
let value2 = vec![43];
find_entry.update_entry(&key1, Some(value1.clone()));
find_entry.update_entry(&key2, Some(value2.clone()));
assert!(find_entry.contains_key(&key1));
assert!(find_entry.contains_key(&key2));
assert_eq!(find_entry.get_read_value(&key1), Some(value1.clone()));
assert_eq!(find_entry.get_read_value(&key2), Some(value2.clone()));
let key_values = find_entry.get_find_key_values(&[1]);
assert_eq!(key_values.len(), 2);
assert!(key_values.contains(&(vec![2], value1.clone())));
assert!(key_values.contains(&(vec![3], value2.clone())));
find_entry.update_entry(&key1, None);
assert!(!find_entry.contains_key(&key1));
assert_eq!(find_entry.get_read_value(&key1), None);
}
#[test]
fn test_cache_size_tracking() {
let mut cache = create_test_cache(true);
let initial_size = cache.total_size;
let initial_value_size = cache.total_value_size;
let key = vec![1, 2, 3];
let value = vec![4, 5, 6];
cache.insert_read_value(&key, &Some(value.clone()));
cache.check_coherence();
assert!(cache.total_size > initial_size);
assert!(cache.total_value_size > initial_value_size);
let value_size_with_entry = cache.total_value_size;
cache.insert_read_value(&key, &None);
cache.check_coherence();
assert!(cache.total_value_size < value_size_with_entry);
}
#[test]
fn test_trim_value_cache() {
let mut cache = LruPrefixCache::new(
StorageCacheConfig {
max_cache_size: 10000,
max_value_entry_size: 500,
max_find_keys_entry_size: 1000,
max_find_key_values_entry_size: 2000,
max_cache_entries: 100,
max_cache_value_size: 50, max_cache_find_keys_size: 1000,
max_cache_find_key_values_size: 1000,
},
true,
);
for i in 0..10 {
let key = vec![i];
let value = vec![0; 20]; cache.insert_read_value(&key, &Some(value));
cache.check_coherence();
}
assert!(cache.total_value_size <= cache.config.max_cache_value_size);
}
fn has_a_prefix_slow_method(prefixes: &BTreeSet<Vec<u8>>, key: &[u8]) -> bool {
for prefix in prefixes {
if key.starts_with(prefix) {
return true;
}
}
false
}
fn has_a_prefix_fast_method(prefixes: &BTreeSet<Vec<u8>>, key: &[u8]) -> bool {
match prefixes.range(..=key.to_vec()).next_back() {
None => false,
Some(prefix) => key.starts_with(prefix),
}
}
fn has_a_prefix(prefixes: &BTreeSet<Vec<u8>>, key: &[u8]) -> bool {
let test1 = has_a_prefix_slow_method(prefixes, key);
let test2 = has_a_prefix_fast_method(prefixes, key);
assert_eq!(
test1, test2,
"The methods for testing prefix return different results"
);
test1
}
fn get_prefix<R: Rng>(rng: &mut R, max_len: usize, entry_size: usize) -> Vec<u8> {
let len = rng.gen_range(1..=max_len);
let mut prefix = Vec::new();
for _ in 0..len {
let entry = rng.gen_range(0..entry_size);
prefix.push(entry as u8);
}
prefix
}
fn insert_into_prefix_set(prefixes: &mut BTreeSet<Vec<u8>>, new_prefix: Vec<u8>) {
if has_a_prefix(prefixes, &new_prefix) {
return;
}
let removed_keys1 = prefixes
.range(get_key_range_for_prefix(new_prefix.clone()))
.cloned()
.collect::<BTreeSet<Vec<u8>>>();
let mut removed_keys2 = BTreeSet::new();
for prefix in prefixes.clone() {
if prefix.starts_with(&new_prefix) {
removed_keys2.insert(prefix);
}
}
assert_eq!(
removed_keys1, removed_keys2,
"Inconsistent result of the computation of the intervals"
);
for prefix in removed_keys2 {
prefixes.remove(&prefix);
}
prefixes.insert(new_prefix);
}
fn test_prefix_free_set(max_len: usize, num_gen: usize, key_size: usize) {
let mut rng = crate::random::make_deterministic_rng();
let mut prefixes = BTreeSet::new();
for _ in 0..num_gen {
let new_prefix = get_prefix(&mut rng, max_len, key_size);
insert_into_prefix_set(&mut prefixes, new_prefix);
}
}
#[test]
fn test_lower_bounds() {
test_prefix_free_set(10, 500, 2);
test_prefix_free_set(6, 500, 3);
test_prefix_free_set(5, 500, 4);
}
#[test]
fn test_delete_key_operations() {
let mut cache = create_test_cache(true);
let key1 = vec![1, 2, 3];
let key2 = vec![1, 2, 4];
let key3 = vec![2, 3, 4];
let value = vec![42, 43, 44];
cache.put_key_value(&key1, &value);
cache.check_coherence();
cache.put_key_value(&key2, &value);
cache.check_coherence();
cache.put_key_value(&key3, &value);
cache.check_coherence();
assert_eq!(cache.query_read_value(&key1), Some(Some(value.clone())));
assert_eq!(cache.query_read_value(&key2), Some(Some(value.clone())));
assert_eq!(cache.query_read_value(&key3), Some(Some(value.clone())));
cache.delete_key(&key1);
cache.check_coherence();
assert_eq!(cache.query_read_value(&key1), Some(None));
assert_eq!(cache.query_read_value(&key2), Some(Some(value.clone())));
assert_eq!(cache.query_read_value(&key3), Some(Some(value.clone())));
cache.delete_key(&key2);
cache.check_coherence();
assert_eq!(cache.query_read_value(&key2), Some(None));
assert_eq!(cache.query_read_value(&key3), Some(Some(value)));
}
#[test]
fn test_find_key_values_entry_delete_prefix() {
let mut find_entry = FindKeyValuesEntry(BTreeMap::new());
let key1 = vec![1, 2, 3];
let key2 = vec![1, 2, 4];
let key3 = vec![1, 3, 5];
let key4 = vec![2, 4, 6];
let value = vec![42];
find_entry.update_entry(&key1, Some(value.clone()));
find_entry.update_entry(&key2, Some(value.clone()));
find_entry.update_entry(&key3, Some(value.clone()));
find_entry.update_entry(&key4, Some(value.clone()));
assert!(find_entry.contains_key(&key1));
assert!(find_entry.contains_key(&key2));
assert!(find_entry.contains_key(&key3));
assert!(find_entry.contains_key(&key4));
find_entry.delete_prefix(&[1, 2]);
assert!(!find_entry.contains_key(&key1));
assert!(!find_entry.contains_key(&key2));
assert!(find_entry.contains_key(&key3));
assert!(find_entry.contains_key(&key4));
find_entry.delete_prefix(&[1]);
assert!(!find_entry.contains_key(&key3));
assert!(find_entry.contains_key(&key4));
}
#[test]
fn test_trim_value_cache_removes_entries() {
let mut cache = LruPrefixCache::new(
StorageCacheConfig {
max_cache_size: 10000,
max_value_entry_size: 500,
max_find_keys_entry_size: 1000,
max_find_key_values_entry_size: 2000,
max_cache_entries: 100,
max_cache_value_size: 30, max_cache_find_keys_size: 1000,
max_cache_find_key_values_size: 1000,
},
true,
);
let mut inserted_keys = Vec::new();
for i in 0..6 {
let key = vec![i];
let value = vec![0; 10]; cache.insert_read_value(&key, &Some(value));
cache.check_coherence();
inserted_keys.push(key);
}
assert!(cache.total_value_size <= cache.config.max_cache_value_size);
let mut remaining_count = 0;
for key in &inserted_keys {
if cache.query_read_value(key).is_some() {
remaining_count += 1;
}
}
assert!(remaining_count < inserted_keys.len());
let last_key = &inserted_keys[inserted_keys.len() - 1];
assert!(cache.query_read_value(last_key).is_some());
}
#[test]
fn test_max_value_entry_size_zero_early_termination() {
let mut cache = LruPrefixCache::new(
StorageCacheConfig {
max_cache_size: 1000,
max_value_entry_size: 0, max_find_keys_entry_size: 100,
max_find_key_values_entry_size: 200,
max_cache_entries: 10,
max_cache_value_size: 500,
max_cache_find_keys_size: 500,
max_cache_find_key_values_size: 500,
},
true,
);
let key1 = vec![1, 2, 3];
let key2 = vec![4, 5, 6];
let value = vec![42, 43, 44];
cache.insert_read_value(&key1, &Some(value.clone()));
cache.check_coherence();
cache.insert_read_value(&key2, &None);
cache.check_coherence();
assert_eq!(cache.query_read_value(&key1), None);
assert_eq!(cache.query_read_value(&key2), None);
assert_eq!(cache.total_size, 0);
assert_eq!(cache.total_value_size, 0);
assert!(cache.value_map.is_empty());
assert!(cache.queue.is_empty());
cache.put_key_value(&key1, &value);
cache.check_coherence();
assert_eq!(cache.query_read_value(&key1), None);
assert_eq!(cache.total_size, 0);
cache.delete_key(&key1);
cache.check_coherence();
assert_eq!(cache.query_read_value(&key1), None);
assert_eq!(cache.total_size, 0);
}
#[test]
fn test_key_value_replacement_same_length() {
let mut cache = create_test_cache(true);
let key = vec![1, 2, 3];
let value1 = vec![42, 43, 44]; let value2 = vec![99, 98, 97];
cache.put_key_value(&key, &value1);
cache.check_coherence();
assert_eq!(cache.query_read_value(&key), Some(Some(value1.clone())));
let initial_size = cache.total_size;
let initial_value_size = cache.total_value_size;
cache.put_key_value(&key, &value2);
cache.check_coherence();
assert_eq!(cache.query_read_value(&key), Some(Some(value2.clone())));
assert_eq!(cache.total_size, initial_size);
assert_eq!(cache.total_value_size, initial_value_size);
assert_eq!(cache.value_map.len(), 1);
assert_eq!(cache.queue.len(), 1);
let value3 = vec![11, 22, 33]; cache.insert_read_value(&key, &Some(value3.clone()));
cache.check_coherence();
assert_eq!(cache.query_read_value(&key), Some(Some(value3)));
assert_eq!(cache.total_size, initial_size);
assert_eq!(cache.total_value_size, initial_value_size);
}
#[test]
fn test_max_find_keys_entry_size_zero_early_termination() {
let mut cache = LruPrefixCache::new(
StorageCacheConfig {
max_cache_size: 1000,
max_value_entry_size: 100,
max_find_keys_entry_size: 0, max_find_key_values_entry_size: 200,
max_cache_entries: 10,
max_cache_value_size: 500,
max_cache_find_keys_size: 500,
max_cache_find_key_values_size: 500,
},
true,
);
let prefix = vec![1, 2];
let keys = vec![vec![3], vec![4], vec![5]];
cache.insert_find_keys(prefix.clone(), &keys);
cache.check_coherence();
assert_eq!(cache.query_find_keys(&prefix), None);
assert_eq!(cache.total_find_keys_size, 0);
assert!(cache.find_keys_map.is_empty());
for (cache_key, _) in &cache.queue {
assert!(!matches!(cache_key, CacheKey::FindKeys(_)));
}
let prefix2 = vec![9];
let keys2 = vec![vec![1]];
cache.insert_find_keys(prefix2.clone(), &keys2);
cache.check_coherence();
assert_eq!(cache.query_find_keys(&prefix2), None);
assert_eq!(cache.total_find_keys_size, 0);
assert!(cache.find_keys_map.is_empty());
}
#[test]
fn test_max_find_key_values_entry_size_zero_early_termination() {
let mut cache = LruPrefixCache::new(
StorageCacheConfig {
max_cache_size: 1000,
max_value_entry_size: 100,
max_find_keys_entry_size: 100,
max_find_key_values_entry_size: 0, max_cache_entries: 10,
max_cache_value_size: 500,
max_cache_find_keys_size: 500,
max_cache_find_key_values_size: 500,
},
true,
);
let prefix = vec![1, 2];
let key_values = vec![(vec![3], vec![10]), (vec![4], vec![20])];
cache.insert_find_key_values(prefix.clone(), &key_values);
cache.check_coherence();
assert_eq!(cache.query_find_key_values(&prefix), None);
assert_eq!(cache.total_find_key_values_size, 0);
assert!(cache.find_key_values_map.is_empty());
for (cache_key, _) in &cache.queue {
assert!(!matches!(cache_key, CacheKey::FindKeyValues(_)));
}
let prefix2 = vec![9];
let key_values2 = vec![(vec![1], vec![100])];
cache.insert_find_key_values(prefix2.clone(), &key_values2);
cache.check_coherence();
assert_eq!(cache.query_find_key_values(&prefix2), None);
assert_eq!(cache.total_find_key_values_size, 0);
assert!(cache.find_key_values_map.is_empty());
}
#[test]
fn test_value_entry_exists_returns_none() {
let mut cache = create_test_cache(true);
let key = vec![1, 2, 3];
cache.insert_contains_key(&key, true);
cache.check_coherence();
assert_eq!(cache.query_contains_key(&key), Some(true));
assert_eq!(cache.query_read_value(&key), None);
let key2 = vec![4, 5, 6];
cache.insert_contains_key(&key2, false);
cache.check_coherence();
assert_eq!(cache.query_contains_key(&key2), Some(false));
assert_eq!(cache.query_read_value(&key2), Some(None));
assert_eq!(cache.value_map.len(), 2);
assert_eq!(cache.queue.len(), 2);
assert!(matches!(
cache.value_map.get(&key),
Some(ValueEntry::Exists)
));
assert!(matches!(
cache.value_map.get(&key2),
Some(ValueEntry::DoesNotExist)
));
}
#[test]
fn test_find_keys_entry_delete_prefix_function() {
let mut find_entry = FindKeysEntry(BTreeSet::new());
let key1 = vec![1, 2, 3];
let key2 = vec![1, 2, 4];
let key3 = vec![1, 3, 5];
let key4 = vec![2, 4, 6];
find_entry.update_entry(&key1, true);
find_entry.update_entry(&key2, true);
find_entry.update_entry(&key3, true);
find_entry.update_entry(&key4, true);
assert!(find_entry.contains_key(&key1));
assert!(find_entry.contains_key(&key2));
assert!(find_entry.contains_key(&key3));
assert!(find_entry.contains_key(&key4));
assert_eq!(find_entry.0.len(), 4);
find_entry.delete_prefix(&[1, 2]);
assert!(!find_entry.contains_key(&key1));
assert!(!find_entry.contains_key(&key2));
assert!(find_entry.contains_key(&key3));
assert!(find_entry.contains_key(&key4));
assert_eq!(find_entry.0.len(), 2);
find_entry.delete_prefix(&[1]);
assert!(!find_entry.contains_key(&key3));
assert!(find_entry.contains_key(&key4));
assert_eq!(find_entry.0.len(), 1);
find_entry.delete_prefix(&[]);
assert!(!find_entry.contains_key(&key4));
assert_eq!(find_entry.0.len(), 0);
}
#[test]
fn test_find_keys_size_decrease() {
let mut cache = create_test_cache(true);
let prefix = vec![1];
let initial_keys = vec![vec![2, 3, 4], vec![3, 4, 5]];
cache.insert_find_keys(prefix.clone(), &initial_keys);
cache.check_coherence();
let initial_total_size = cache.total_size;
let initial_find_keys_size = cache.total_find_keys_size;
let key_to_delete = vec![1, 2, 3, 4]; cache.delete_key(&key_to_delete);
cache.check_coherence();
assert!(cache.total_size < initial_total_size);
assert!(cache.total_find_keys_size < initial_find_keys_size);
let result = cache.query_find_keys(&prefix);
assert!(result.is_some());
let keys = result.unwrap();
assert!(keys.contains(&vec![3, 4, 5])); assert!(!keys.contains(&vec![2, 3, 4]));
cache.check_coherence();
}
#[test]
fn test_trim_find_keys_cache_lru_eviction() {
let mut cache = LruPrefixCache::new(
StorageCacheConfig {
max_cache_size: 10000,
max_value_entry_size: 500,
max_find_keys_entry_size: 1000,
max_find_key_values_entry_size: 2000,
max_cache_entries: 100,
max_cache_value_size: 5000,
max_cache_find_keys_size: 50, max_cache_find_key_values_size: 5000,
},
true,
);
let prefix1 = vec![1];
let keys1 = vec![vec![2; 10], vec![3; 10]]; cache.insert_find_keys(prefix1.clone(), &keys1);
cache.check_coherence();
let prefix2 = vec![2];
let keys2 = vec![vec![4; 10], vec![5; 10]]; cache.insert_find_keys(prefix2.clone(), &keys2);
cache.check_coherence();
assert!(cache.query_find_keys(&prefix1).is_some());
assert!(cache.query_find_keys(&prefix2).is_some());
let prefix3 = vec![3];
let keys3 = vec![vec![6; 10], vec![7; 10]]; cache.insert_find_keys(prefix3.clone(), &keys3);
cache.check_coherence();
assert!(cache.total_find_keys_size <= cache.config.max_cache_find_keys_size);
assert_eq!(cache.query_find_keys(&prefix1), None);
assert!(cache.query_find_keys(&prefix2).is_some());
assert!(cache.query_find_keys(&prefix3).is_some());
assert!(cache.find_keys_map.len() < 3);
}
#[test]
fn test_find_keys_removal_from_map() {
let mut cache = LruPrefixCache::new(
StorageCacheConfig {
max_cache_size: 100, max_value_entry_size: 50,
max_find_keys_entry_size: 1000,
max_find_key_values_entry_size: 2000,
max_cache_entries: 3, max_cache_value_size: 500,
max_cache_find_keys_size: 500,
max_cache_find_key_values_size: 500,
},
true,
);
let prefix1 = vec![1];
let prefix2 = vec![2];
let prefix3 = vec![3];
let keys = vec![vec![1], vec![2]];
cache.insert_find_keys(prefix1.clone(), &keys);
cache.check_coherence();
cache.insert_find_keys(prefix2.clone(), &keys);
cache.check_coherence();
assert!(cache.find_keys_map.contains_key(&prefix1));
assert!(cache.find_keys_map.contains_key(&prefix2));
cache.insert_find_keys(prefix3.clone(), &keys);
cache.check_coherence();
assert!(!cache.find_keys_map.contains_key(&prefix1));
assert!(cache.find_keys_map.contains_key(&prefix2));
assert!(cache.find_keys_map.contains_key(&prefix3));
assert!(cache.queue.len() <= cache.config.max_cache_entries);
}
#[test]
fn test_find_key_values_removal_from_map() {
let mut cache = LruPrefixCache::new(
StorageCacheConfig {
max_cache_size: 100, max_value_entry_size: 50,
max_find_keys_entry_size: 1000,
max_find_key_values_entry_size: 2000,
max_cache_entries: 3, max_cache_value_size: 500,
max_cache_find_keys_size: 500,
max_cache_find_key_values_size: 500,
},
true,
);
let prefix1 = vec![1];
let prefix2 = vec![2];
let prefix3 = vec![3];
let key_values = vec![(vec![1], vec![10]), (vec![2], vec![20])];
cache.insert_find_key_values(prefix1.clone(), &key_values);
cache.check_coherence();
cache.insert_find_key_values(prefix2.clone(), &key_values);
cache.check_coherence();
assert!(cache.find_key_values_map.contains_key(&prefix1));
assert!(cache.find_key_values_map.contains_key(&prefix2));
cache.insert_find_key_values(prefix3.clone(), &key_values);
cache.check_coherence();
assert!(!cache.find_key_values_map.contains_key(&prefix1));
assert!(cache.find_key_values_map.contains_key(&prefix2));
assert!(cache.find_key_values_map.contains_key(&prefix3));
assert!(cache.queue.len() <= cache.config.max_cache_entries);
}
#[test]
fn test_contains_key_failing_without_exclusive_access() {
let mut cache = create_test_cache(false); let key = vec![1, 2, 3];
let result = cache.query_contains_key(&key);
assert_eq!(result, None);
cache.insert_contains_key(&key, true);
cache.check_coherence();
let result = cache.query_contains_key(&key);
assert_eq!(result, Some(true));
}
#[test]
fn test_contains_key_found_in_find_key_values() {
let mut cache = create_test_cache(true); let prefix = vec![1, 2];
let key = vec![1, 2, 3, 4];
let key_values = vec![(vec![3, 4], vec![100]), (vec![5, 6], vec![200])];
cache.insert_find_key_values(prefix.clone(), &key_values);
cache.check_coherence();
let result = cache.query_contains_key(&key);
assert_eq!(result, Some(true));
let non_existent_key = vec![1, 2, 7, 8];
let result = cache.query_contains_key(&non_existent_key);
assert_eq!(result, Some(false));
let unmatched_key = vec![9, 9, 9];
let result = cache.query_contains_key(&unmatched_key);
assert_eq!(result, None); }
#[test]
fn test_find_keys_removal_when_inserting_broader_find_keys() {
let mut cache = create_test_cache(true);
let specific_prefix = vec![1, 2, 3, 4];
let keys2 = vec![vec![6], vec![7]];
cache.insert_find_keys(specific_prefix.clone(), &keys2);
cache.check_coherence();
let narrow_prefix = vec![1, 2, 3];
let keys1 = vec![vec![4, 6], vec![4, 7]];
cache.insert_find_keys(narrow_prefix.clone(), &keys1);
cache.check_coherence();
}
#[test]
fn test_overlapping_find_key_values_removes_find_keys_and_find_key_values() {
let mut cache = create_test_cache(true);
let find_keys_prefix = vec![1, 2, 3];
let keys = vec![vec![4, 6], vec![4, 7], vec![5]];
cache.insert_find_keys(find_keys_prefix.clone(), &keys);
cache.check_coherence();
let find_key_values_prefix = vec![1, 2, 3, 4];
let key_values1 = vec![(vec![6], vec![10]), (vec![7], vec![20])];
cache.insert_find_key_values(find_key_values_prefix.clone(), &key_values1);
cache.check_coherence();
cache.put_key_value(&[1, 2, 8, 9], &[254]);
cache.check_coherence();
let broad_prefix = vec![1, 2];
let key_values2 = vec![
(vec![3, 4, 6], vec![10]),
(vec![3, 4, 7], vec![20]),
(vec![3, 5], vec![34]),
(vec![8, 9], vec![254]),
];
cache.insert_find_key_values(broad_prefix.clone(), &key_values2);
cache.check_coherence();
assert!(!cache.find_keys_map.contains_key(&find_keys_prefix));
assert!(!cache
.find_key_values_map
.contains_key(&find_key_values_prefix));
assert!(cache.find_key_values_map.contains_key(&broad_prefix));
let key_values = cache.query_find_key_values(&broad_prefix).unwrap();
assert!(key_values.contains(&(vec![3, 4, 6], vec![10])));
assert!(key_values.contains(&(vec![3, 4, 7], vec![20])));
assert!(key_values.contains(&(vec![8, 9], vec![254])));
let query_result = cache.query_contains_key(&[1, 2, 3, 1]);
assert_eq!(query_result, Some(false)); }
#[test]
fn test_delete_prefix_removes_entire_find_keys_entry() {
let mut cache = create_test_cache(true);
let prefix = vec![1, 2, 3];
let keys = vec![vec![4], vec![5], vec![6]];
cache.insert_find_keys(prefix.clone(), &keys);
cache.check_coherence();
assert!(cache.find_keys_map.contains_key(&prefix));
let result = cache.query_find_keys(&prefix);
assert!(result.is_some());
let delete_prefix = vec![1, 2]; cache.delete_prefix(&delete_prefix);
cache.check_coherence();
let result = cache.query_find_keys(&prefix);
assert_eq!(result, Some(vec![]));
for (cache_key, _) in &cache.queue {
assert!(!matches!(cache_key, CacheKey::FindKeys(key) if key == &prefix));
}
}
#[test]
fn test_delete_prefix_removes_keys_within_find_keys_entry() {
let mut cache = create_test_cache(true);
let find_keys_prefix = vec![1];
let keys = vec![vec![2, 3, 4], vec![2, 3, 5], vec![2, 4, 6], vec![3, 7, 8]];
cache.insert_find_keys(find_keys_prefix.clone(), &keys);
cache.check_coherence();
assert!(cache.find_keys_map.contains_key(&find_keys_prefix));
let initial_keys = cache.query_find_keys(&find_keys_prefix).unwrap();
assert!(initial_keys.contains(&vec![2, 3, 4]));
assert!(initial_keys.contains(&vec![2, 3, 5]));
assert!(initial_keys.contains(&vec![2, 4, 6]));
assert!(initial_keys.contains(&vec![3, 7, 8]));
let initial_size = cache.total_find_keys_size;
let delete_prefix = vec![1, 2, 3]; cache.delete_prefix(&delete_prefix);
cache.check_coherence();
assert!(cache.find_keys_map.contains_key(&find_keys_prefix));
let remaining_keys = cache.query_find_keys(&find_keys_prefix).unwrap();
assert!(!remaining_keys.contains(&vec![2, 3, 4])); assert!(!remaining_keys.contains(&vec![2, 3, 5])); assert!(remaining_keys.contains(&vec![2, 4, 6])); assert!(remaining_keys.contains(&vec![3, 7, 8]));
assert!(cache.total_find_keys_size < initial_size);
let delete_prefix2 = vec![1, 2]; cache.delete_prefix(&delete_prefix2);
cache.check_coherence();
let final_keys = cache.query_find_keys(&find_keys_prefix).unwrap();
assert!(!final_keys.contains(&vec![2, 4, 6])); assert!(final_keys.contains(&vec![3, 7, 8])); }
#[test]
fn test_put_key_value_without_find_key_values_match() {
let mut cache = create_test_cache(false); let key = vec![1, 2, 3, 4];
let value = vec![100, 200];
cache.put_key_value(&key, &value);
cache.check_coherence();
assert_eq!(cache.query_read_value(&key), Some(Some(value.clone())));
assert!(cache.value_map.contains_key(&key));
}
#[test]
fn test_delete_key_without_exclusive_access() {
let mut cache = create_test_cache(false); let key = vec![1, 2, 3];
let value = vec![42, 43, 44];
cache.insert_read_value(&key, &Some(value.clone()));
cache.check_coherence();
assert_eq!(cache.query_read_value(&key), Some(Some(value)));
assert!(cache.value_map.contains_key(&key));
cache.delete_key(&key);
cache.check_coherence();
assert!(!cache.value_map.contains_key(&key));
assert_eq!(cache.query_read_value(&key), None);
for (cache_key, _) in &cache.queue {
assert!(!matches!(cache_key, CacheKey::Value(k) if k == &key));
}
let non_existent_key = vec![9, 9, 9];
cache.delete_key(&non_existent_key); cache.check_coherence();
}
#[test]
fn test_line_502_insert_then_delete_without_exclusive_access() {
let mut cache = create_test_cache(false); let key = vec![1, 2, 3, 4];
let value = vec![100, 200, 201];
cache.insert_read_value(&key, &Some(value.clone()));
cache.check_coherence();
assert_eq!(cache.query_read_value(&key), Some(Some(value)));
assert!(cache.value_map.contains_key(&key));
cache.insert_read_value(&key, &None);
cache.check_coherence();
assert!(!cache.value_map.contains_key(&key));
assert_eq!(cache.query_read_value(&key), None);
let queue_contains_key = cache
.queue
.iter()
.any(|(cache_key, _)| matches!(cache_key, CacheKey::Value(k) if k == &key));
assert!(!queue_contains_key);
}
#[test]
fn test_does_not_exist_removal_during_insert_find_keys() {
let mut cache = create_test_cache(true); let prefix = vec![1, 2];
let key1 = vec![1, 2, 3];
let key2 = vec![1, 2, 4];
let key3 = vec![1, 2, 5];
cache.insert_read_value(&key1, &None); cache.insert_read_value(&key2, &None); cache.insert_read_value(&key3, &Some(vec![100])); cache.check_coherence();
assert_eq!(cache.query_read_value(&key1), Some(None));
assert_eq!(cache.query_read_value(&key2), Some(None));
assert_eq!(cache.query_read_value(&key3), Some(Some(vec![100])));
assert!(cache.value_map.contains_key(&key1));
assert!(cache.value_map.contains_key(&key2));
assert!(cache.value_map.contains_key(&key3));
let find_keys_result = vec![key1.clone(), key2.clone(), key3.clone()];
cache.insert_find_keys(prefix.clone(), &find_keys_result);
cache.check_coherence();
assert_eq!(cache.query_read_value(&key1), None); assert_eq!(cache.query_read_value(&key2), None); assert_eq!(cache.query_read_value(&key3), Some(Some(vec![100]))); assert!(!cache.value_map.contains_key(&key1));
assert!(!cache.value_map.contains_key(&key2));
assert!(cache.value_map.contains_key(&key3));
assert_eq!(cache.query_find_keys(&prefix), Some(find_keys_result));
}
#[test]
fn test_trim_value_cache_complete_iteration() {
let mut cache = create_test_cache(true);
let key1 = vec![1, 2, 3];
let key2 = vec![4, 5, 6];
let value1 = vec![100, 200];
let value2 = vec![203, 177];
cache.insert_read_value(&key1, &Some(value1));
cache.insert_read_value(&key2, &Some(value2));
cache.check_coherence();
cache.config.max_cache_value_size = 0;
cache.trim_value_cache();
cache.check_coherence();
assert!(!cache.value_map.contains_key(&key1));
assert!(!cache.value_map.contains_key(&key2));
}
#[test]
fn test_trim_find_keys_cache_complete_iteration() {
let mut cache = create_test_cache(true);
let prefix1 = vec![1, 2];
let prefix2 = vec![3, 4];
let keys1 = vec![vec![1, 2, 3], vec![1, 2, 4]];
let keys2 = vec![vec![3, 4, 5], vec![3, 4, 6]];
cache.insert_find_keys(prefix1, &keys1);
cache.insert_find_keys(prefix2, &keys2);
cache.check_coherence();
cache.config.max_cache_find_keys_size = 0;
cache.trim_find_keys_cache();
cache.check_coherence();
assert!(cache.find_keys_map.is_empty());
}
#[test]
fn test_trim_find_key_values_cache_complete_iteration() {
let mut cache = create_test_cache(true);
let prefix1 = vec![1, 2];
let prefix2 = vec![3, 4];
let key_values1 = vec![(vec![1, 2, 3], vec![100]), (vec![1, 2, 4], vec![200])];
let key_values2 = vec![(vec![3, 4, 5], vec![203]), (vec![3, 4, 6], vec![177])];
cache.insert_find_key_values(prefix1, &key_values1);
cache.insert_find_key_values(prefix2, &key_values2);
cache.check_coherence();
cache.config.max_cache_find_key_values_size = 0;
cache.trim_find_key_values_cache();
cache.check_coherence();
assert!(cache.find_key_values_map.is_empty());
}
#[test]
fn test_trim_cache_breaks_on_empty_queue() {
let mut cache = LruPrefixCache::new(
StorageCacheConfig {
max_cache_size: 10000, max_value_entry_size: 10000, max_find_keys_entry_size: 10000,
max_find_key_values_entry_size: 10000,
max_cache_entries: 10,
max_cache_value_size: 500,
max_cache_find_keys_size: 500,
max_cache_find_key_values_size: 500,
},
true,
);
for i in 0..10 {
let key = vec![1, 2, i];
let value = vec![100 + i];
cache.insert_read_value(&key, &Some(value));
cache.check_coherence();
}
cache.config.max_cache_size = 0;
let key = vec![1, 2];
let value = vec![100];
cache.insert_read_value(&key, &Some(value));
cache.check_coherence();
}
}