use std::{borrow::Cow, fmt::Debug, sync::Arc};
#[cfg(feature = "tracing")]
use foyer_common::tracing::TracingOptions;
use foyer_common::{
code::{DefaultHasher, HashBuilder, StorageKey, StorageValue},
error::Result,
event::EventListener,
metrics::Metrics,
spawn::Spawner,
};
use foyer_memory::{Cache, CacheBuilder, EvictionConfig, Filter, Weighter};
use foyer_storage::{Compression, EngineConfig, IoEngineConfig, RecoverMode, StoreBuilder};
use mixtrics::{metrics::BoxedRegistry, registry::noop::NoopMetricsRegistry};
use crate::hybrid::cache::{
HybridCache, HybridCacheOptions, HybridCachePipe, HybridCachePolicy, HybridCacheProperties,
};
pub struct HybridCacheBuilder<K, V> {
name: Cow<'static, str>,
options: HybridCacheOptions,
event_listener: Option<Arc<dyn EventListener<Key = K, Value = V>>>,
registry: BoxedRegistry,
}
impl<K, V> Default for HybridCacheBuilder<K, V> {
fn default() -> Self {
Self::new()
}
}
impl<K, V> HybridCacheBuilder<K, V> {
pub fn new() -> Self {
Self {
name: "foyer".into(),
options: HybridCacheOptions::default(),
event_listener: None,
registry: Box::new(NoopMetricsRegistry),
}
}
}
impl<K, V> HybridCacheBuilder<K, V> {
pub fn with_name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
self.name = name.into();
self
}
pub fn with_policy(mut self, policy: HybridCachePolicy) -> Self {
self.options.policy = policy;
self
}
pub fn with_event_listener(mut self, event_listener: Arc<dyn EventListener<Key = K, Value = V>>) -> Self {
self.event_listener = Some(event_listener);
self
}
#[cfg(feature = "tracing")]
pub fn with_tracing_options(mut self, tracing_options: TracingOptions) -> Self {
self.options.tracing_options = tracing_options;
self
}
pub fn with_flush_on_close(mut self, flush_on_close: bool) -> Self {
self.options.flush_on_close = flush_on_close;
self
}
pub fn with_metrics_registry(mut self, registry: BoxedRegistry) -> HybridCacheBuilder<K, V> {
self.registry = registry;
self
}
pub fn memory(self, capacity: usize) -> HybridCacheBuilderPhaseMemory<K, V, DefaultHasher>
where
K: StorageKey,
V: StorageValue,
{
let metrics = Arc::new(Metrics::new(self.name.clone(), &self.registry));
let mut builder = CacheBuilder::new(capacity)
.with_name(self.name.clone())
.with_metrics(metrics.clone());
if let Some(event_listener) = self.event_listener {
builder = builder.with_event_listener(event_listener);
}
HybridCacheBuilderPhaseMemory {
name: self.name,
options: self.options,
metrics,
builder,
}
}
}
pub struct HybridCacheBuilderPhaseMemory<K, V, S>
where
K: StorageKey,
V: StorageValue,
S: HashBuilder + Debug,
{
name: Cow<'static, str>,
options: HybridCacheOptions,
metrics: Arc<Metrics>,
builder: CacheBuilder<K, V, S>,
}
impl<K, V, S> HybridCacheBuilderPhaseMemory<K, V, S>
where
K: StorageKey,
V: StorageValue,
S: HashBuilder + Debug,
{
pub fn with_shards(self, shards: usize) -> Self {
let builder = self.builder.with_shards(shards);
HybridCacheBuilderPhaseMemory {
name: self.name,
options: self.options,
metrics: self.metrics,
builder,
}
}
pub fn with_eviction_config(self, eviction_config: impl Into<EvictionConfig>) -> Self {
let builder = self.builder.with_eviction_config(eviction_config.into());
HybridCacheBuilderPhaseMemory {
name: self.name,
options: self.options,
metrics: self.metrics,
builder,
}
}
pub fn with_hash_builder<OS>(self, hash_builder: OS) -> HybridCacheBuilderPhaseMemory<K, V, OS>
where
OS: HashBuilder + Debug,
{
let builder = self.builder.with_hash_builder(hash_builder);
HybridCacheBuilderPhaseMemory {
name: self.name,
options: self.options,
metrics: self.metrics,
builder,
}
}
pub fn with_weighter(self, weighter: impl Weighter<K, V>) -> Self {
let builder = self.builder.with_weighter(weighter);
HybridCacheBuilderPhaseMemory {
name: self.name,
options: self.options,
metrics: self.metrics,
builder,
}
}
pub fn with_filter(self, filter: impl Filter<K, V>) -> Self {
let builder = self.builder.with_filter(filter);
HybridCacheBuilderPhaseMemory {
name: self.name,
options: self.options,
metrics: self.metrics,
builder,
}
}
pub fn storage(self) -> HybridCacheBuilderPhaseStorage<K, V, S> {
let memory = self.builder.build();
HybridCacheBuilderPhaseStorage {
name: self.name.clone(),
options: self.options,
metrics: self.metrics.clone(),
memory: memory.clone(),
builder: StoreBuilder::new(self.name, memory, self.metrics),
}
}
}
pub struct HybridCacheBuilderPhaseStorage<K, V, S>
where
K: StorageKey,
V: StorageValue,
S: HashBuilder + Debug,
{
name: Cow<'static, str>,
options: HybridCacheOptions,
metrics: Arc<Metrics>,
memory: Cache<K, V, S, HybridCacheProperties>,
builder: StoreBuilder<K, V, S, HybridCacheProperties>,
}
impl<K, V, S> HybridCacheBuilderPhaseStorage<K, V, S>
where
K: StorageKey,
V: StorageValue,
S: HashBuilder + Debug,
{
pub fn with_io_engine_config(self, config: impl Into<Box<dyn IoEngineConfig>>) -> Self {
let builder = self.builder.with_io_engine_config(config);
Self {
name: self.name,
options: self.options,
metrics: self.metrics,
memory: self.memory,
builder,
}
}
pub fn with_engine_config(self, config: impl Into<Box<dyn EngineConfig<K, V, HybridCacheProperties>>>) -> Self {
let builder = self.builder.with_engine_config(config);
Self {
name: self.name,
options: self.options,
metrics: self.metrics,
memory: self.memory,
builder,
}
}
pub fn with_recover_mode(self, recover_mode: RecoverMode) -> Self {
let builder = self.builder.with_recover_mode(recover_mode);
Self {
name: self.name,
options: self.options,
metrics: self.metrics,
memory: self.memory,
builder,
}
}
pub fn with_compression(self, compression: Compression) -> Self {
let builder = self.builder.with_compression(compression);
Self {
name: self.name,
options: self.options,
metrics: self.metrics,
memory: self.memory,
builder,
}
}
pub fn with_spawner(self, spawner: Spawner) -> Self {
let builder = self.builder.with_spawner(spawner);
Self {
name: self.name,
options: self.options,
metrics: self.metrics,
memory: self.memory,
builder,
}
}
pub async fn build(self) -> Result<HybridCache<K, V, S>> {
let builder = self.builder;
let piped = match (builder.is_noop(), self.options.policy) {
(true, _) => false,
(false, HybridCachePolicy::WriteOnEviction) => true,
(false, HybridCachePolicy::WriteOnInsertion) => false,
};
let storage = builder.build().await?;
let memory = if piped {
self.memory.with_pipe(Arc::new(HybridCachePipe::new(storage.clone())))
} else {
self.memory
};
Ok(HybridCache::new(self.name, self.options, memory, storage, self.metrics))
}
}