use std::{
fmt::Debug,
time::{Duration, Instant},
};
use ahash::RandomState;
use foyer_common::code::{HashBuilder, StorageKey, StorageValue};
use foyer_memory::CacheContext;
use crate::{HybridCache, HybridCacheEntry};
pub struct HybridCacheWriter<K, V, S = RandomState>
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_context(self, value: V, context: CacheContext) -> HybridCacheEntry<K, V, S> {
self.hybrid.insert_with_context(self.key, value, context)
}
pub fn storage(self) -> HybridCacheStorageWriter<K, V, S> {
HybridCacheStorageWriter::new(self.hybrid, self.key)
}
}
pub struct HybridCacheStorageWriter<K, V, S = RandomState>
where
K: StorageKey,
V: StorageValue,
S: HashBuilder + Debug,
{
hybrid: HybridCache<K, V, S>,
key: K,
picked: Option<bool>,
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 {
Self {
hybrid,
key,
picked: None,
pick_duration: Duration::default(),
}
}
pub fn pick(&mut self) -> bool {
if let Some(picked) = self.picked {
return picked;
}
let now = Instant::now();
let picked = self.hybrid.storage().pick(&self.key);
self.picked = Some(picked);
self.pick_duration = now.elapsed();
picked
}
pub fn force(mut self) -> Self {
self.picked = Some(true);
self
}
fn insert_inner(mut self, value: V, context: Option<CacheContext>) -> Option<HybridCacheEntry<K, V, S>> {
let now = Instant::now();
if !self.pick() {
return None;
}
let entry = match context {
Some(context) => self.hybrid.memory().deposit_with_context(self.key, value, context),
None => self.hybrid.memory().deposit(self.key, value),
};
self.hybrid.storage().enqueue(entry.clone(), true);
self.hybrid.metrics().hybrid_insert.increment(1);
self.hybrid
.metrics()
.hybrid_insert_duration
.record(now.elapsed() + self.pick_duration);
Some(entry)
}
pub fn insert(self, value: V) -> Option<HybridCacheEntry<K, V, S>> {
self.insert_inner(value, None)
}
pub fn insert_with_context(self, value: V, context: CacheContext) -> Option<HybridCacheEntry<K, V, S>> {
self.insert_inner(value, Some(context))
}
}