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