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