multi_tier_cache/builder.rs
1//! Cache System Builder
2//!
3//! Provides a flexible builder pattern for constructing `CacheSystem` with custom backends.
4//!
5//! # Example: Using Default Backends
6//!
7//! ```rust,no_run
8//! use multi_tier_cache::CacheSystemBuilder;
9//!
10//! #[tokio::main]
11//! async fn main() -> anyhow::Result<()> {
12//! let cache = CacheSystemBuilder::new()
13//! .build()
14//! .await?;
15//! Ok(())
16//! }
17//! ```
18//!
19//! # Example: Custom L1 Backend
20//!
21//! ```rust,ignore
22//! use multi_tier_cache::{CacheSystemBuilder, CacheBackend};
23//! use std::sync::Arc;
24//!
25//! let custom_l1 = Arc::new(MyCustomL1Cache::new());
26//!
27//! let cache = CacheSystemBuilder::new()
28//! .with_l1(custom_l1)
29//! .build()
30//! .await?;
31//! ```
32
33use crate::backends::MokaCacheConfig;
34use crate::traits::{CacheBackend, L2CacheBackend, StreamingBackend};
35use crate::{CacheManager, CacheSystem, CacheTier, L1Cache, L2Cache, TierConfig};
36use anyhow::Result;
37use std::sync::Arc;
38use tracing::info;
39
40/// Builder for constructing `CacheSystem` with custom backends
41///
42/// This builder allows you to configure custom L1 (in-memory) and L2 (distributed)
43/// cache backends, enabling you to swap Moka and Redis with alternative implementations.
44///
45/// # Multi-Tier Support (v0.5.0+)
46///
47/// The builder now supports dynamic multi-tier architectures (L1+L2+L3+L4+...).
48/// Use `.with_tier()` to add custom tiers, or `.with_l3()` / `.with_l4()` for convenience.
49///
50/// # Default Behavior
51///
52/// If no custom backends are provided, the builder uses:
53/// - **L1**: Moka in-memory cache
54/// - **L2**: Redis distributed cache
55///
56/// # Type Safety
57///
58/// The builder accepts any type that implements the required traits:
59/// - L1 backends must implement `CacheBackend`
60/// - L2 backends must implement `L2CacheBackend` (extends `CacheBackend`)
61/// - All tier backends must implement `L2CacheBackend` (for TTL support)
62/// - Streaming backends must implement `StreamingBackend`
63///
64/// # Example - Default 2-Tier
65///
66/// ```rust,no_run
67/// use multi_tier_cache::CacheSystemBuilder;
68///
69/// #[tokio::main]
70/// async fn main() -> anyhow::Result<()> {
71/// // Use default backends (Moka + Redis)
72/// let cache = CacheSystemBuilder::new()
73/// .build()
74/// .await?;
75///
76/// Ok(())
77/// }
78/// ```
79///
80/// # Example - Custom 3-Tier (v0.5.0+)
81///
82/// ```rust,ignore
83/// use multi_tier_cache::{CacheSystemBuilder, TierConfig};
84/// use std::sync::Arc;
85///
86/// let l1 = Arc::new(L1Cache::new().await?);
87/// let l2 = Arc::new(L2Cache::new().await?);
88/// let l3 = Arc::new(RocksDBCache::new("/tmp/cache").await?);
89///
90/// let cache = CacheSystemBuilder::new()
91/// .with_tier(l1, TierConfig::as_l1())
92/// .with_tier(l2, TierConfig::as_l2())
93/// .with_l3(l3) // Convenience method
94/// .build()
95/// .await?;
96/// ```
97pub struct CacheSystemBuilder {
98 // Legacy 2-tier configuration (v0.1.0 - v0.4.x)
99 l1_backend: Option<Arc<dyn CacheBackend>>,
100 l2_backend: Option<Arc<dyn L2CacheBackend>>,
101
102 streaming_backend: Option<Arc<dyn StreamingBackend>>,
103 moka_config: Option<MokaCacheConfig>,
104
105 // Multi-tier configuration (v0.5.0+)
106 tiers: Vec<(Arc<dyn L2CacheBackend>, TierConfig)>,
107}
108
109impl CacheSystemBuilder {
110 /// Create a new builder with no custom backends configured
111 ///
112 /// By default, calling `.build()` will use Moka (L1) and Redis (L2).
113 /// Use `.with_tier()` to configure multi-tier architecture (v0.5.0+).
114 #[must_use]
115 pub fn new() -> Self {
116 Self {
117 l1_backend: None,
118 l2_backend: None,
119
120 streaming_backend: None,
121 moka_config: None,
122 tiers: Vec::new(),
123 }
124 }
125
126 /// Configure a custom L1 (in-memory) cache backend
127 ///
128 /// # Arguments
129 ///
130 /// * `backend` - Any type implementing `CacheBackend` trait
131 ///
132 /// # Example
133 ///
134 /// ```rust,ignore
135 /// use std::sync::Arc;
136 /// use multi_tier_cache::CacheSystemBuilder;
137 ///
138 /// let custom_l1 = Arc::new(MyCustomL1::new());
139 ///
140 /// let cache = CacheSystemBuilder::new()
141 /// .with_l1(custom_l1)
142 /// .build()
143 /// .await?;
144 /// ```
145 #[must_use]
146 pub fn with_l1(mut self, backend: Arc<dyn CacheBackend>) -> Self {
147 self.l1_backend = Some(backend);
148 self
149 }
150
151 /// Configure custom configuration for default L1 (Moka) backend
152 #[must_use]
153 pub fn with_moka_config(mut self, config: MokaCacheConfig) -> Self {
154 self.moka_config = Some(config);
155 self
156 }
157
158 /// Configure a custom L2 (distributed) cache backend
159 ///
160 /// # Arguments
161 ///
162 /// * `backend` - Any type implementing `L2CacheBackend` trait
163 ///
164 /// # Example
165 ///
166 /// ```rust,ignore
167 /// use std::sync::Arc;
168 /// use multi_tier_cache::CacheSystemBuilder;
169 ///
170 /// let custom_l2 = Arc::new(MyMemcachedBackend::new());
171 ///
172 /// let cache = CacheSystemBuilder::new()
173 /// .with_l2(custom_l2)
174 /// .build()
175 /// .await?;
176 /// ```
177 #[must_use]
178 pub fn with_l2(mut self, backend: Arc<dyn L2CacheBackend>) -> Self {
179 self.l2_backend = Some(backend);
180 self
181 }
182
183 /// Configure a custom streaming backend
184 ///
185 /// This is optional. If not provided, streaming functionality will use
186 /// the L2 backend if it implements `StreamingBackend`.
187 ///
188 /// # Arguments
189 ///
190 /// * `backend` - Any type implementing `StreamingBackend` trait
191 ///
192 /// # Example
193 ///
194 /// ```rust,ignore
195 /// use std::sync::Arc;
196 /// use multi_tier_cache::CacheSystemBuilder;
197 ///
198 /// let kafka_backend = Arc::new(MyKafkaBackend::new());
199 ///
200 /// let cache = CacheSystemBuilder::new()
201 /// .with_streams(kafka_backend)
202 /// .build()
203 /// .await?;
204 /// ```
205 #[must_use]
206 pub fn with_streams(mut self, backend: Arc<dyn StreamingBackend>) -> Self {
207 self.streaming_backend = Some(backend);
208 self
209 }
210
211 /// Configure a cache tier with custom settings (v0.5.0+)
212 ///
213 /// Add a cache tier to the multi-tier architecture. Tiers will be sorted
214 /// by `tier_level` during build.
215 ///
216 /// # Arguments
217 ///
218 /// * `backend` - Any type implementing `L2CacheBackend` trait
219 /// * `config` - Tier configuration (level, promotion, TTL scale)
220 ///
221 /// # Example
222 ///
223 /// ```rust,ignore
224 /// use multi_tier_cache::{CacheSystemBuilder, TierConfig, L1Cache, L2Cache};
225 /// use std::sync::Arc;
226 ///
227 /// let l1 = Arc::new(L1Cache::new().await?);
228 /// let l2 = Arc::new(L2Cache::new().await?);
229 /// let l3 = Arc::new(RocksDBCache::new("/tmp").await?);
230 ///
231 /// let cache = CacheSystemBuilder::new()
232 /// .with_tier(l1, TierConfig::as_l1())
233 /// .with_tier(l2, TierConfig::as_l2())
234 /// .with_tier(l3, TierConfig::as_l3())
235 /// .build()
236 /// .await?;
237 /// ```
238 #[must_use]
239 pub fn with_tier(mut self, backend: Arc<dyn L2CacheBackend>, config: TierConfig) -> Self {
240 self.tiers.push((backend, config));
241 self
242 }
243
244 /// Convenience method to add L3 cache tier (v0.5.0+)
245 ///
246 /// Adds a cold storage tier with 2x TTL multiplier.
247 ///
248 /// # Arguments
249 ///
250 /// * `backend` - L3 backend (e.g., `RocksDB`, `LevelDB`)
251 ///
252 /// # Example
253 ///
254 /// ```rust,ignore
255 /// use std::sync::Arc;
256 ///
257 /// let rocksdb = Arc::new(RocksDBCache::new("/tmp/l3cache").await?);
258 ///
259 /// let cache = CacheSystemBuilder::new()
260 /// .with_l3(rocksdb)
261 /// .build()
262 /// .await?;
263 /// ```
264 #[must_use]
265 pub fn with_l3(mut self, backend: Arc<dyn L2CacheBackend>) -> Self {
266 self.tiers.push((backend, TierConfig::as_l3()));
267 self
268 }
269
270 /// Convenience method to add L4 cache tier (v0.5.0+)
271 ///
272 /// Adds an archive storage tier with 8x TTL multiplier.
273 ///
274 /// # Arguments
275 ///
276 /// * `backend` - L4 backend (e.g., S3, file system)
277 ///
278 /// # Example
279 ///
280 /// ```rust,ignore
281 /// use std::sync::Arc;
282 ///
283 /// let s3_cache = Arc::new(S3Cache::new("my-bucket").await?);
284 ///
285 /// let cache = CacheSystemBuilder::new()
286 /// .with_l4(s3_cache)
287 /// .build()
288 /// .await?;
289 /// ```
290 #[must_use]
291 pub fn with_l4(mut self, backend: Arc<dyn L2CacheBackend>) -> Self {
292 self.tiers.push((backend, TierConfig::as_l4()));
293 self
294 }
295
296 /// Build the `CacheSystem` with configured or default backends
297 ///
298 /// If no custom backends were provided via `.with_l1()` or `.with_l2()`,
299 /// this method creates default backends (Moka for L1, Redis for L2).
300 ///
301 /// # Multi-Tier Mode (v0.5.0+)
302 ///
303 /// If tiers were configured via `.with_tier()`, `.with_l3()`, or `.with_l4()`,
304 /// the builder creates a multi-tier `CacheManager` using `new_with_tiers()`.
305 ///
306 /// # Returns
307 ///
308 /// * `Ok(CacheSystem)` - Successfully constructed cache system
309 /// * `Err(e)` - Failed to initialize backends (e.g., Redis connection error)
310 ///
311 /// # Example - Default 2-Tier
312 ///
313 /// ```rust,no_run
314 /// use multi_tier_cache::CacheSystemBuilder;
315 ///
316 /// #[tokio::main]
317 /// async fn main() -> anyhow::Result<()> {
318 /// let cache = CacheSystemBuilder::new()
319 /// .build()
320 /// .await?;
321 ///
322 /// // Use cache_manager for operations
323 /// let manager = cache.cache_manager();
324 ///
325 /// Ok(())
326 /// }
327 /// ```
328 /// # Errors
329 ///
330 /// Returns an error if the default backends cannot be initialized.
331 pub async fn build(self) -> Result<CacheSystem> {
332 info!("Building Multi-Tier Cache System");
333
334 // NEW: Multi-tier mode (v0.5.0+)
335 if !self.tiers.is_empty() {
336 info!(
337 tier_count = self.tiers.len(),
338 "Initializing multi-tier architecture"
339 );
340
341 // Sort tiers by tier_level (ascending: L1 first, L4 last)
342 let mut tiers = self.tiers;
343 tiers.sort_by_key(|(_, config)| config.tier_level);
344
345 // Convert to CacheTier instances
346 let cache_tiers: Vec<CacheTier> = tiers
347 .into_iter()
348 .map(|(backend, config)| {
349 CacheTier::new(
350 backend,
351 config.tier_level,
352 config.promotion_enabled,
353 config.ttl_scale,
354 )
355 })
356 .collect();
357
358 // Create cache manager with multi-tier support
359 let cache_manager = Arc::new(CacheManager::new_with_tiers(
360 cache_tiers,
361 self.streaming_backend,
362 )?);
363
364 info!("Multi-Tier Cache System built successfully");
365 info!("Note: Using multi-tier mode - use cache_manager() for all operations");
366
367 return Ok(CacheSystem {
368 cache_manager,
369 l1_cache: None, // Multi-tier mode doesn't use concrete types
370 l2_cache: None,
371 });
372 }
373
374 // LEGACY: 2-tier mode (v0.1.0 - v0.4.x)
375 // Handle default vs custom backends
376 if self.l1_backend.is_none() && self.l2_backend.is_none() {
377 // Default path: Create concrete types once and reuse them
378 info!("Initializing default backends (Moka + Redis)");
379
380 let l1_cache = Arc::new(L1Cache::new(self.moka_config.unwrap_or_default())?);
381 let l2_cache = Arc::new(L2Cache::new().await?);
382
383 // Use legacy constructor that handles conversion to trait objects
384 let cache_manager =
385 Arc::new(CacheManager::new(l1_cache.clone(), l2_cache.clone()).await?);
386
387 info!("Multi-Tier Cache System built successfully");
388
389 Ok(CacheSystem {
390 cache_manager,
391 l1_cache: Some(l1_cache),
392 l2_cache: Some(l2_cache),
393 })
394 } else {
395 // Custom backend path
396 info!("Building with custom backends");
397
398 let l1_backend: Arc<dyn CacheBackend> = if let Some(backend) = self.l1_backend {
399 info!(backend = %backend.name(), "Using custom L1 backend");
400 backend
401 } else {
402 info!("Using default L1 backend (Moka)");
403 let config = self.moka_config.unwrap_or_default();
404 Arc::new(L1Cache::new(config)?)
405 };
406
407 let l2_backend: Arc<dyn L2CacheBackend> = if let Some(backend) = self.l2_backend {
408 info!(backend = %backend.name(), "Using custom L2 backend");
409 backend
410 } else {
411 info!("Using default L2 backend (Redis)");
412 Arc::new(L2Cache::new().await?)
413 };
414
415 let streaming_backend = self.streaming_backend;
416
417 // Create cache manager with trait objects
418 let cache_manager = Arc::new(CacheManager::new_with_backends(
419 l1_backend,
420 l2_backend,
421 streaming_backend,
422 )?);
423
424 info!("Multi-Tier Cache System built with custom backends");
425 info!("Note: Using custom backends - use cache_manager() for all operations");
426
427 Ok(CacheSystem {
428 cache_manager,
429 l1_cache: None, // Custom backends mode doesn't use concrete types
430 l2_cache: None,
431 })
432 }
433 }
434}
435
436impl Default for CacheSystemBuilder {
437 fn default() -> Self {
438 Self::new()
439 }
440}