use std::{
fmt::Debug,
time::{Duration, Instant},
};
use foyer_common::{
code::{DefaultHasher, HashBuilder, StorageKey, StorageValue},
properties::Properties,
};
use foyer_storage::StorageFilterResult;
use crate::{HybridCache, HybridCacheEntry, HybridCacheProperties};
pub struct HybridCacheWriter<K, V, S = DefaultHasher>
where
K: StorageKey,
V: StorageValue,
S: HashBuilder + Debug,
{
hybrid: HybridCache<K, V, S>,
key: K,
}
impl<K, V, S> HybridCacheWriter<K, V, S>
where
K: StorageKey,
V: StorageValue,
S: HashBuilder + Debug,
{
pub(crate) fn new(hybrid: HybridCache<K, V, S>, key: K) -> Self {
Self { hybrid, key }
}
pub fn insert(self, value: V) -> HybridCacheEntry<K, V, S> {
self.hybrid.insert(self.key, value)
}
pub fn insert_with_properties(self, value: V, properties: HybridCacheProperties) -> HybridCacheEntry<K, V, S> {
self.hybrid.insert_with_properties(self.key, value, properties)
}
pub fn storage(self) -> HybridCacheStorageWriter<K, V, S> {
HybridCacheStorageWriter::new(self.hybrid, self.key)
}
}
pub struct HybridCacheStorageWriter<K, V, S = DefaultHasher>
where
K: StorageKey,
V: StorageValue,
S: HashBuilder + Debug,
{
hybrid: HybridCache<K, V, S>,
key: K,
hash: u64,
force: bool,
filter_result: Option<StorageFilterResult>,
pick_duration: Duration,
}
impl<K, V, S> HybridCacheStorageWriter<K, V, S>
where
K: StorageKey,
V: StorageValue,
S: HashBuilder + Debug,
{
pub(crate) fn new(hybrid: HybridCache<K, V, S>, key: K) -> Self {
let hash = hybrid.memory().hash(&key);
Self {
hybrid,
key,
hash,
force: false,
filter_result: None,
pick_duration: Duration::default(),
}
}
pub fn filter(&mut self, estimated_size: usize) -> StorageFilterResult {
let now = Instant::now();
let picked = self.hybrid.storage().filter(self.hash, estimated_size);
self.filter_result = Some(picked);
self.pick_duration = now.elapsed();
picked
}
fn may_pick(&mut self, estimated_size: usize) -> StorageFilterResult {
if let Some(picked) = self.filter_result {
picked
} else {
self.filter(estimated_size)
}
}
pub fn force(mut self) -> Self {
self.force = true;
self
}
fn insert_inner(mut self, value: V, properties: HybridCacheProperties) -> Option<HybridCacheEntry<K, V, S>> {
let now = Instant::now();
if !self.force
&& !self
.may_pick(self.key.estimated_size() + value.estimated_size())
.is_admitted()
{
return None;
}
let entry = self
.hybrid
.insert_with_properties(self.key, value, properties.with_phantom(true));
self.hybrid.metrics().hybrid_insert.increase(1);
self.hybrid
.metrics()
.hybrid_insert_duration
.record((now.elapsed() + self.pick_duration).as_secs_f64());
Some(entry)
}
pub fn insert(self, value: V) -> Option<HybridCacheEntry<K, V, S>> {
self.insert_inner(value, HybridCacheProperties::default())
}
pub fn insert_with_properties(
self,
value: V,
properties: HybridCacheProperties,
) -> Option<HybridCacheEntry<K, V, S>> {
self.insert_inner(value, properties)
}
}