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}