use super::{ChainCache, ChainLink, ChainReadStrategy};
use crate::backend::BackendScore;
use crate::backend::CacheBackend;
use crate::core::EventPublisher;
#[cfg(feature = "invalidation")]
use crate::features::invalidation::{InvalidatingBackend, InvalidationBus};
use std::sync::Arc;
use std::sync::OnceLock;
use std::time::Duration;
#[derive(Default)]
pub struct ChainCacheBuilder {
links: Vec<ChainLink>,
backfill_enabled: bool,
read_strategy: ChainReadStrategy,
default_ttl: Option<Duration>,
event_publisher: Option<Arc<dyn EventPublisher>>,
#[cfg(feature = "invalidation")]
invalidation_bus: Option<Arc<InvalidationBus>>,
}
impl ChainCacheBuilder {
pub fn link(mut self, link: ChainLink) -> Self {
self.links.push(link);
self
}
pub fn links(mut self, mut links: Vec<ChainLink>) -> Self {
self.links.append(&mut links);
self
}
pub fn backend<B>(self, backend: B) -> Self
where
B: CacheBackend + BackendScore + 'static,
{
self.link(ChainLink::from_backend(backend))
}
pub fn default_time_to_live(mut self, ttl: Duration) -> Self {
self.default_ttl = Some(ttl);
self
}
pub fn enable_backfill(mut self) -> Self {
self.backfill_enabled = true;
self
}
pub fn disable_backfill(mut self) -> Self {
self.backfill_enabled = false;
self
}
pub fn enable_race_read(mut self) -> Self {
self.read_strategy = ChainReadStrategy::Race;
self
}
pub fn disable_race_read(mut self) -> Self {
self.read_strategy = ChainReadStrategy::Sequential;
self
}
pub fn read_strategy(mut self, strategy: ChainReadStrategy) -> Self {
self.read_strategy = strategy;
self
}
pub fn event_publisher(mut self, publisher: Arc<dyn EventPublisher>) -> Self {
self.event_publisher = Some(publisher);
self
}
#[cfg(feature = "invalidation")]
pub fn with_invalidation(mut self, bus: Arc<InvalidationBus>) -> Self {
self.invalidation_bus = Some(bus);
self
}
pub fn build(self) -> ChainCache {
let mut links = self.links;
links.sort_by_key(|link| std::cmp::Reverse(link.score()));
assert!(
!links.is_empty(),
"ChainCacheBuilder::build requires at least one link; \
add one via .link(...) or .backend(...), or use ChainCache::new \
for an intentionally empty chain"
);
#[cfg(feature = "invalidation")]
if let Some(bus) = self.invalidation_bus {
assert!(
links.iter().any(|link| link.is_persistent()),
"ChainCacheBuilder::with_invalidation requires at least one \
persistent link (mark it via ChainLink::new(.., true, ..) or \
L2Builder::persistent(true)); without one the bus would never \
broadcast and writes would silently skip invalidation"
);
links = links
.into_iter()
.map(|link| {
if link.is_persistent() {
let wrapped = Arc::new(InvalidatingBackend::new(
link.backend().clone(),
bus.clone(),
));
ChainLink::from_arc(
wrapped,
link.score(),
link.is_persistent(),
link.name(),
)
} else {
link
}
})
.collect();
}
ChainCache {
links,
backfill_enabled: self.backfill_enabled,
read_strategy: self.read_strategy,
default_ttl: self.default_ttl,
sync_backends: OnceLock::new(),
event_publisher: self.event_publisher,
}
}
}