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}