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,no_run
22//! # use std::sync::Arc;
23//! # use futures_util::future::BoxFuture;
24//! # use bytes::Bytes;
25//! # use std::time::Duration;
26//! # use multi_tier_cache::error::CacheResult;
27//! # struct MyCustomL1Cache;
28//! # impl multi_tier_cache::CacheBackend for MyCustomL1Cache {
29//! # fn get<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, Option<Bytes>> { Box::pin(async { None }) }
30//! # fn set_with_ttl<'a>(&'a self, _key: &'a str, _value: Bytes, _ttl: Duration) -> BoxFuture<'a, CacheResult<()>> { Box::pin(async { Ok(()) }) }
31//! # fn remove<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, CacheResult<()>> { Box::pin(async { Ok(()) }) }
32//! # fn health_check(&self) -> BoxFuture<'_, bool> { Box::pin(async { true }) }
33//! # fn name(&self) -> &'static str { "MyCustomL1" }
34//! # }
35//! # #[tokio::main]
36//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
37//! use multi_tier_cache::{CacheSystemBuilder, CacheBackend};
38//! use std::sync::Arc;
39//!
40//! let custom_l1 = Arc::new(MyCustomL1Cache);
41//!
42//! let cache = CacheSystemBuilder::new()
43//! .with_l1(custom_l1)
44//! .build()
45//! .await?;
46//! # Ok(())
47//! # }
48//! ```
49
50#[cfg(feature = "moka")]
51#[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
52use crate::backends::MokaCacheConfig;
53use crate::traits::{CacheBackend, L2CacheBackend, StreamingBackend};
54use crate::{CacheManager, CacheSystem, CacheTier, TierConfig};
55
56#[cfg(feature = "moka")]
57#[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
58use crate::L1Cache;
59#[cfg(feature = "redis")]
60#[cfg_attr(docsrs, doc(cfg(feature = "redis")))]
61use crate::L2Cache;
62use crate::error::CacheResult;
63use std::sync::Arc;
64use tracing::info;
65
66/// Builder for constructing `CacheSystem` with custom backends
67///
68/// This builder allows you to configure custom L1 (in-memory) and L2 (distributed)
69/// cache backends, enabling you to swap Moka and Redis with alternative implementations.
70///
71/// # Multi-Tier Support (v0.5.0+)
72///
73/// The builder now supports dynamic multi-tier architectures (L1+L2+L3+L4+...).
74/// Use `.with_tier()` to add custom tiers, or `.with_l3()` / `.with_l4()` for convenience.
75///
76/// # Default Behavior
77///
78/// If no custom backends are provided, the builder uses:
79/// - **L1**: Moka in-memory cache
80/// - **L2**: Redis distributed cache
81///
82/// # Type Safety
83///
84/// The builder accepts any type that implements the required traits:
85/// - L1 backends must implement `CacheBackend`
86/// - L2 backends must implement `L2CacheBackend` (extends `CacheBackend`)
87/// - All tier backends must implement `L2CacheBackend` (for TTL support)
88/// - Streaming backends must implement `StreamingBackend`
89///
90/// # Example - Default 2-Tier
91///
92/// ```rust,no_run
93/// use multi_tier_cache::CacheSystemBuilder;
94///
95/// #[tokio::main]
96/// async fn main() -> anyhow::Result<()> {
97/// // Use default backends (Moka + Redis)
98/// let cache = CacheSystemBuilder::new()
99/// .build()
100/// .await?;
101///
102/// Ok(())
103/// }
104/// ```
105///
106/// # Example - Custom 3-Tier (v0.5.0+)
107///
108/// ```rust,no_run
109/// # use std::sync::Arc;
110/// # use futures_util::future::BoxFuture;
111/// # use bytes::Bytes;
112/// # use std::time::Duration;
113/// # use multi_tier_cache::error::CacheResult;
114/// # use multi_tier_cache::{CacheBackend, L2CacheBackend};
115/// # struct RocksDBCache;
116/// # impl CacheBackend for RocksDBCache {
117/// # fn get<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, Option<Bytes>> { Box::pin(async { None }) }
118/// # fn set_with_ttl<'a>(&'a self, _key: &'a str, _value: Bytes, _ttl: Duration) -> BoxFuture<'a, CacheResult<()>> { Box::pin(async { Ok(()) }) }
119/// # fn remove<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, CacheResult<()>> { Box::pin(async { Ok(()) }) }
120/// # fn health_check(&self) -> BoxFuture<'_, bool> { Box::pin(async { true }) }
121/// # fn name(&self) -> &'static str { "RocksDB" }
122/// # }
123/// # impl L2CacheBackend for RocksDBCache {
124/// # fn get_with_ttl<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, Option<(Bytes, Option<Duration>)>> { Box::pin(async { None }) }
125/// # }
126/// # #[tokio::main]
127/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
128/// use multi_tier_cache::{CacheSystemBuilder, TierConfig, L1Cache, L2Cache, MokaCacheConfig};
129/// use std::sync::Arc;
130///
131/// let l1 = Arc::new(L1Cache::new(MokaCacheConfig::default())?);
132/// let l2 = Arc::new(L2Cache::new().await?);
133/// let l3 = Arc::new(RocksDBCache);
134///
135/// let cache = CacheSystemBuilder::new()
136/// .with_tier(l1, TierConfig::as_l1())
137/// .with_tier(l2, TierConfig::as_l2())
138/// .with_l3(l3) // Convenience method
139/// .build()
140/// .await?;
141/// # Ok(())
142/// # }
143/// ```
144pub struct CacheSystemBuilder {
145 // Legacy 2-tier configuration (v0.1.0 - v0.4.x)
146 l1_backend: Option<Arc<dyn CacheBackend>>,
147 l2_backend: Option<Arc<dyn L2CacheBackend>>,
148
149 streaming_backend: Option<Arc<dyn StreamingBackend>>,
150 #[cfg(feature = "moka")]
151 #[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
152 moka_config: Option<MokaCacheConfig>,
153
154 // Multi-tier configuration (v0.5.0+)
155 tiers: Vec<(Arc<dyn L2CacheBackend>, TierConfig)>,
156}
157
158impl CacheSystemBuilder {
159 /// Create a new builder with no custom backends configured
160 ///
161 /// By default, calling `.build()` will use Moka (L1) and Redis (L2).
162 /// Use `.with_tier()` to configure multi-tier architecture (v0.5.0+).
163 #[must_use]
164 pub fn new() -> Self {
165 Self {
166 l1_backend: None,
167 l2_backend: None,
168
169 streaming_backend: None,
170 #[cfg(feature = "moka")]
171 #[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
172 moka_config: None,
173 tiers: Vec::new(),
174 }
175 }
176
177 /// Configure a custom L1 (in-memory) cache backend
178 ///
179 /// # Arguments
180 ///
181 /// * `backend` - Any type implementing `CacheBackend` trait
182 ///
183 /// # Example
184 ///
185 /// ```rust,no_run
186 /// # use std::sync::Arc;
187 /// # use futures_util::future::BoxFuture;
188 /// # use bytes::Bytes;
189 /// # use std::time::Duration;
190 /// # use multi_tier_cache::error::CacheResult;
191 /// # struct MyCustomL1;
192 /// # impl multi_tier_cache::CacheBackend for MyCustomL1 {
193 /// # fn get<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, Option<Bytes>> { Box::pin(async { None }) }
194 /// # fn set_with_ttl<'a>(&'a self, _key: &'a str, _value: Bytes, _ttl: Duration) -> BoxFuture<'a, CacheResult<()>> { Box::pin(async { Ok(()) }) }
195 /// # fn remove<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, CacheResult<()>> { Box::pin(async { Ok(()) }) }
196 /// # fn health_check(&self) -> BoxFuture<'_, bool> { Box::pin(async { true }) }
197 /// # fn name(&self) -> &'static str { "MyCustomL1" }
198 /// # }
199 /// # #[tokio::main]
200 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
201 /// use std::sync::Arc;
202 /// use multi_tier_cache::CacheSystemBuilder;
203 ///
204 /// let custom_l1 = Arc::new(MyCustomL1);
205 ///
206 /// let cache = CacheSystemBuilder::new()
207 /// .with_l1(custom_l1)
208 /// .build()
209 /// .await?;
210 /// # Ok(())
211 /// # }
212 /// ```
213 #[must_use]
214 pub fn with_l1(mut self, backend: Arc<dyn CacheBackend>) -> Self {
215 self.l1_backend = Some(backend);
216 self
217 }
218
219 /// Configure custom configuration for default L1 (Moka) backend
220 #[must_use]
221 #[cfg(feature = "moka")]
222 #[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
223 pub fn with_moka_config(mut self, config: MokaCacheConfig) -> Self {
224 self.moka_config = Some(config);
225 self
226 }
227
228 /// Configure a custom L2 (distributed) cache backend
229 ///
230 /// # Arguments
231 ///
232 /// * `backend` - Any type implementing `L2CacheBackend` trait
233 ///
234 /// # Example
235 ///
236 /// ```rust,no_run
237 /// # use std::sync::Arc;
238 /// # use futures_util::future::BoxFuture;
239 /// # use bytes::Bytes;
240 /// # use std::time::Duration;
241 /// # use multi_tier_cache::error::CacheResult;
242 /// # use multi_tier_cache::{CacheBackend, L2CacheBackend};
243 /// # struct MyMemcachedBackend;
244 /// # impl CacheBackend for MyMemcachedBackend {
245 /// # fn get<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, Option<Bytes>> { Box::pin(async { None }) }
246 /// # fn set_with_ttl<'a>(&'a self, _key: &'a str, _value: Bytes, _ttl: Duration) -> BoxFuture<'a, CacheResult<()>> { Box::pin(async { Ok(()) }) }
247 /// # fn remove<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, CacheResult<()>> { Box::pin(async { Ok(()) }) }
248 /// # fn health_check(&self) -> BoxFuture<'_, bool> { Box::pin(async { true }) }
249 /// # fn name(&self) -> &'static str { "MyMemcached" }
250 /// # }
251 /// # impl L2CacheBackend for MyMemcachedBackend {
252 /// # fn get_with_ttl<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, Option<(Bytes, Option<Duration>)>> { Box::pin(async { None }) }
253 /// # }
254 /// # #[tokio::main]
255 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
256 /// use std::sync::Arc;
257 /// use multi_tier_cache::CacheSystemBuilder;
258 ///
259 /// let custom_l2 = Arc::new(MyMemcachedBackend);
260 ///
261 /// let cache = CacheSystemBuilder::new()
262 /// .with_l2(custom_l2)
263 /// .build()
264 /// .await?;
265 /// # Ok(())
266 /// # }
267 /// ```
268 #[must_use]
269 pub fn with_l2(mut self, backend: Arc<dyn L2CacheBackend>) -> Self {
270 self.l2_backend = Some(backend);
271 self
272 }
273
274 /// Configure a custom streaming backend
275 ///
276 /// This is optional. If not provided, streaming functionality will use
277 /// the L2 backend if it implements `StreamingBackend`.
278 ///
279 /// # Arguments
280 ///
281 /// * `backend` - Any type implementing `StreamingBackend` trait
282 ///
283 /// # Example
284 ///
285 /// ```rust,no_run
286 /// # use std::sync::Arc;
287 /// # use futures_util::future::BoxFuture;
288 /// # use multi_tier_cache::error::CacheResult;
289 /// # use multi_tier_cache::StreamingBackend;
290 /// # use multi_tier_cache::traits::StreamEntry;
291 /// # struct MyKafkaBackend;
292 /// # impl StreamingBackend for MyKafkaBackend {
293 /// # fn stream_add<'a>(&'a self, _s: &'a str, _f: Vec<(String, String)>, _m: Option<usize>) -> BoxFuture<'a, CacheResult<String>> { Box::pin(async { Ok("".to_string()) }) }
294 /// # fn stream_read_latest<'a>(&'a self, _s: &'a str, _c: usize) -> BoxFuture<'a, CacheResult<Vec<StreamEntry>>> { Box::pin(async { Ok(vec![]) }) }
295 /// # fn stream_read<'a>(&'a self, _s: &'a str, _l: &'a str, _c: usize, _b: Option<usize>) -> BoxFuture<'a, CacheResult<Vec<StreamEntry>>> { Box::pin(async { Ok(vec![]) }) }
296 /// # fn stream_create_group<'a>(&'a self, _s: &'a str, _g: &'a str, _i: &'a str) -> BoxFuture<'a, CacheResult<()>> { Box::pin(async { Ok(()) }) }
297 /// # fn stream_read_group<'a>(&'a self, _s: &'a str, _g: &'a str, _c: &'a str, _co: usize, _b: Option<usize>) -> BoxFuture<'a, CacheResult<Vec<StreamEntry>>> { Box::pin(async { Ok(vec![]) }) }
298 /// # fn stream_ack<'a>(&'a self, _s: &'a str, _g: &'a str, _i: &'a [String]) -> BoxFuture<'a, CacheResult<()>> { Box::pin(async { Ok(()) }) }
299 /// # }
300 /// # #[tokio::main]
301 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
302 /// use std::sync::Arc;
303 /// use multi_tier_cache::CacheSystemBuilder;
304 ///
305 /// let kafka_backend = Arc::new(MyKafkaBackend);
306 ///
307 /// let cache = CacheSystemBuilder::new()
308 /// .with_streams(kafka_backend)
309 /// .build()
310 /// .await?;
311 /// # Ok(())
312 /// # }
313 /// ```
314 #[must_use]
315 pub fn with_streams(mut self, backend: Arc<dyn StreamingBackend>) -> Self {
316 self.streaming_backend = Some(backend);
317 self
318 }
319
320 /// Configure a cache tier with custom settings (v0.5.0+)
321 ///
322 /// Add a cache tier to the multi-tier architecture. Tiers will be sorted
323 /// by `tier_level` during build.
324 ///
325 /// # Arguments
326 ///
327 /// * `backend` - Any type implementing `L2CacheBackend` trait
328 /// * `config` - Tier configuration (level, promotion, TTL scale)
329 ///
330 /// # Example
331 ///
332 /// ```rust,no_run
333 /// # use std::sync::Arc;
334 /// # use futures_util::future::BoxFuture;
335 /// # use bytes::Bytes;
336 /// # use std::time::Duration;
337 /// # use multi_tier_cache::error::CacheResult;
338 /// # use multi_tier_cache::{CacheBackend, L2CacheBackend};
339 /// # struct RocksDBCache;
340 /// # impl CacheBackend for RocksDBCache {
341 /// # fn get<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, Option<Bytes>> { Box::pin(async { None }) }
342 /// # fn set_with_ttl<'a>(&'a self, _key: &'a str, _value: Bytes, _ttl: Duration) -> BoxFuture<'a, CacheResult<()>> { Box::pin(async { Ok(()) }) }
343 /// # fn remove<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, CacheResult<()>> { Box::pin(async { Ok(()) }) }
344 /// # fn health_check(&self) -> BoxFuture<'_, bool> { Box::pin(async { true }) }
345 /// # fn name(&self) -> &'static str { "RocksDB" }
346 /// # }
347 /// # impl L2CacheBackend for RocksDBCache {
348 /// # fn get_with_ttl<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, Option<(Bytes, Option<Duration>)>> { Box::pin(async { None }) }
349 /// # }
350 /// # #[tokio::main]
351 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
352 /// use multi_tier_cache::{CacheSystemBuilder, TierConfig, L1Cache, L2Cache, MokaCacheConfig};
353 /// use std::sync::Arc;
354 ///
355 /// let l1 = Arc::new(L1Cache::new(MokaCacheConfig::default())?);
356 /// let l2 = Arc::new(L2Cache::new().await?);
357 /// let l3 = Arc::new(RocksDBCache);
358 ///
359 /// let cache = CacheSystemBuilder::new()
360 /// .with_tier(l1, TierConfig::as_l1())
361 /// .with_tier(l2, TierConfig::as_l2())
362 /// .with_tier(l3, TierConfig::as_l3())
363 /// .build()
364 /// .await?;
365 /// # Ok(())
366 /// # }
367 /// ```
368 #[must_use]
369 pub fn with_tier(mut self, backend: Arc<dyn L2CacheBackend>, config: TierConfig) -> Self {
370 self.tiers.push((backend, config));
371 self
372 }
373
374 /// Convenience method to add L3 cache tier (v0.5.0+)
375 ///
376 /// Adds a cold storage tier with 2x TTL multiplier.
377 ///
378 /// # Arguments
379 ///
380 /// * `backend` - L3 backend (e.g., `RocksDB`, `LevelDB`)
381 ///
382 /// # Example
383 ///
384 /// ```rust,no_run
385 /// # use std::sync::Arc;
386 /// # use futures_util::future::BoxFuture;
387 /// # use bytes::Bytes;
388 /// # use std::time::Duration;
389 /// # use multi_tier_cache::error::CacheResult;
390 /// # use multi_tier_cache::{CacheBackend, L2CacheBackend};
391 /// # struct RocksDBCache;
392 /// # impl CacheBackend for RocksDBCache {
393 /// # fn get<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, Option<Bytes>> { Box::pin(async { None }) }
394 /// # fn set_with_ttl<'a>(&'a self, _key: &'a str, _value: Bytes, _ttl: Duration) -> BoxFuture<'a, CacheResult<()>> { Box::pin(async { Ok(()) }) }
395 /// # fn remove<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, CacheResult<()>> { Box::pin(async { Ok(()) }) }
396 /// # fn health_check(&self) -> BoxFuture<'_, bool> { Box::pin(async { true }) }
397 /// # fn name(&self) -> &'static str { "RocksDB" }
398 /// # }
399 /// # impl L2CacheBackend for RocksDBCache {
400 /// # fn get_with_ttl<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, Option<(Bytes, Option<Duration>)>> { Box::pin(async { None }) }
401 /// # }
402 /// # #[tokio::main]
403 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
404 /// use std::sync::Arc;
405 /// use multi_tier_cache::CacheSystemBuilder;
406 ///
407 /// let rocksdb = Arc::new(RocksDBCache);
408 ///
409 /// let cache = CacheSystemBuilder::new()
410 /// .with_l3(rocksdb)
411 /// .build()
412 /// .await?;
413 /// # Ok(())
414 /// # }
415 /// ```
416 #[must_use]
417 pub fn with_l3(mut self, backend: Arc<dyn L2CacheBackend>) -> Self {
418 self.tiers.push((backend, TierConfig::as_l3()));
419 self
420 }
421
422 /// Convenience method to add L4 cache tier (v0.5.0+)
423 ///
424 /// Adds an archive storage tier with 8x TTL multiplier.
425 ///
426 /// # Arguments
427 ///
428 /// * `backend` - L4 backend (e.g., S3, file system)
429 ///
430 /// # Example
431 ///
432 /// ```rust,no_run
433 /// # use std::sync::Arc;
434 /// # use futures_util::future::BoxFuture;
435 /// # use bytes::Bytes;
436 /// # use std::time::Duration;
437 /// # use multi_tier_cache::error::CacheResult;
438 /// # use multi_tier_cache::{CacheBackend, L2CacheBackend};
439 /// # struct S3Cache;
440 /// # impl CacheBackend for S3Cache {
441 /// # fn get<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, Option<Bytes>> { Box::pin(async { None }) }
442 /// # fn set_with_ttl<'a>(&'a self, _key: &'a str, _value: Bytes, _ttl: Duration) -> BoxFuture<'a, CacheResult<()>> { Box::pin(async { Ok(()) }) }
443 /// # fn remove<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, CacheResult<()>> { Box::pin(async { Ok(()) }) }
444 /// # fn health_check(&self) -> BoxFuture<'_, bool> { Box::pin(async { true }) }
445 /// # fn name(&self) -> &'static str { "S3" }
446 /// # }
447 /// # impl L2CacheBackend for S3Cache {
448 /// # fn get_with_ttl<'a>(&'a self, _key: &'a str) -> BoxFuture<'a, Option<(Bytes, Option<Duration>)>> { Box::pin(async { None }) }
449 /// # }
450 /// # #[tokio::main]
451 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
452 /// use std::sync::Arc;
453 /// use multi_tier_cache::CacheSystemBuilder;
454 ///
455 /// let s3_cache = Arc::new(S3Cache);
456 ///
457 /// let cache = CacheSystemBuilder::new()
458 /// .with_l4(s3_cache)
459 /// .build()
460 /// .await?;
461 /// # Ok(())
462 /// # }
463 /// ```
464 #[must_use]
465 pub fn with_l4(mut self, backend: Arc<dyn L2CacheBackend>) -> Self {
466 self.tiers.push((backend, TierConfig::as_l4()));
467 self
468 }
469
470 /// Build the `CacheSystem` with configured or default backends
471 ///
472 /// If no custom backends were provided via `.with_l1()` or `.with_l2()`,
473 /// this method creates default backends (Moka for L1, Redis for L2).
474 ///
475 /// # Multi-Tier Mode (v0.5.0+)
476 ///
477 /// If tiers were configured via `.with_tier()`, `.with_l3()`, or `.with_l4()`,
478 /// the builder creates a multi-tier `CacheManager` using `new_with_tiers()`.
479 ///
480 /// # Returns
481 ///
482 /// * `Ok(CacheSystem)` - Successfully constructed cache system
483 /// * `Err(e)` - Failed to initialize backends (e.g., Redis connection error)
484 ///
485 /// # Example - Default 2-Tier
486 ///
487 /// ```rust,no_run
488 /// use multi_tier_cache::CacheSystemBuilder;
489 ///
490 /// #[tokio::main]
491 /// async fn main() -> anyhow::Result<()> {
492 /// let cache = CacheSystemBuilder::new()
493 /// .build()
494 /// .await?;
495 ///
496 /// // Use cache_manager for operations
497 /// let manager = cache.cache_manager();
498 ///
499 /// Ok(())
500 /// }
501 /// ```
502 /// # Errors
503 ///
504 /// Returns an error if the default backends cannot be initialized.
505 pub async fn build(self) -> CacheResult<CacheSystem> {
506 info!("Building Multi-Tier Cache System");
507
508 if !self.tiers.is_empty() {
509 self.build_multi_tier()
510 } else if self.l1_backend.is_none() && self.l2_backend.is_none() {
511 self.build_default_2_tier().await
512 } else {
513 self.build_custom_2_tier().await
514 }
515 }
516
517 /// Internal helper for multi-tier mode (v0.5.0+)
518 fn build_multi_tier(self) -> CacheResult<CacheSystem> {
519 info!(
520 tier_count = self.tiers.len(),
521 "Initializing multi-tier architecture"
522 );
523
524 // Sort tiers by tier_level (ascending: L1 first, L4 last)
525 let mut tiers = self.tiers;
526 tiers.sort_by_key(|(_, config)| config.tier_level);
527
528 // Convert to CacheTier instances
529 let cache_tiers: Vec<CacheTier> = tiers
530 .into_iter()
531 .map(|(backend, config)| {
532 CacheTier::new(
533 backend,
534 config.tier_level,
535 config.promotion_enabled,
536 config.promotion_frequency,
537 config.ttl_scale,
538 )
539 })
540 .collect();
541
542 // Create cache manager with multi-tier support
543 let cache_manager = Arc::new(CacheManager::new_with_tiers(
544 cache_tiers,
545 self.streaming_backend,
546 )?);
547
548 info!("Multi-Tier Cache System built successfully");
549 info!("Note: Using multi-tier mode - use cache_manager() for all operations");
550
551 Ok(CacheSystem {
552 cache_manager,
553 #[cfg(feature = "moka")]
554 #[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
555 l1_cache: None,
556 #[cfg(feature = "redis")]
557 #[cfg_attr(docsrs, doc(cfg(feature = "redis")))]
558 l2_cache: None,
559 })
560 }
561
562 /// Internal helper for legacy default 2-tier mode
563 async fn build_default_2_tier(self) -> CacheResult<CacheSystem> {
564 info!("Initializing default backends (Moka + Redis)");
565
566 #[cfg(all(feature = "moka", feature = "redis"))]
567 #[cfg_attr(docsrs, doc(cfg(all(feature = "moka", feature = "redis"))))]
568 {
569 let l1_cache = Arc::new(crate::L1Cache::new(self.moka_config.unwrap_or_default())?);
570 let l2_cache: Arc<crate::L2Cache> = Arc::new(crate::L2Cache::new().await?);
571
572 // Use legacy constructor that handles conversion to trait objects
573 let cache_manager =
574 Arc::new(CacheManager::new(l1_cache.clone(), l2_cache.clone()).await?);
575
576 info!("Multi-Tier Cache System built successfully");
577
578 Ok(CacheSystem {
579 cache_manager,
580 #[cfg(feature = "moka")]
581 #[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
582 l1_cache: Some(l1_cache),
583 #[cfg(feature = "redis")]
584 #[cfg_attr(docsrs, doc(cfg(feature = "redis")))]
585 l2_cache: Some(l2_cache),
586 })
587 }
588 #[cfg(not(all(feature = "moka", feature = "redis")))]
589 {
590 Err(CacheError::ConfigError(
591 "Default backends (Moka/Redis) are not enabled. Provide custom backends or enable 'moka' and 'redis' features.".to_string()
592 ))
593 }
594 }
595
596 /// Internal helper for legacy custom 2-tier mode
597 async fn build_custom_2_tier(self) -> CacheResult<CacheSystem> {
598 info!("Building with custom backends");
599
600 let l1_backend: Arc<dyn CacheBackend> = if let Some(backend) = self.l1_backend {
601 backend
602 } else {
603 #[cfg(feature = "moka")]
604 #[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
605 {
606 Arc::new(L1Cache::new(self.moka_config.unwrap_or_default())?)
607 }
608 #[cfg(not(feature = "moka"))]
609 {
610 return Err(CacheError::ConfigError(
611 "Moka feature not enabled. Provide a custom L1 backend.".to_string(),
612 ));
613 }
614 };
615
616 let l2_backend: Arc<dyn L2CacheBackend> = if let Some(backend) = self.l2_backend {
617 backend
618 } else {
619 #[cfg(feature = "redis")]
620 #[cfg_attr(docsrs, doc(cfg(feature = "redis")))]
621 {
622 Arc::new(L2Cache::new().await?)
623 }
624 #[cfg(not(feature = "redis"))]
625 {
626 return Err(CacheError::ConfigError(
627 "Redis feature not enabled. Provide a custom L2 backend.".to_string(),
628 ));
629 }
630 };
631
632 let streaming_backend = self.streaming_backend;
633
634 // Create cache manager with trait objects
635 let cache_manager = Arc::new(CacheManager::new_with_backends(
636 l1_backend,
637 l2_backend,
638 streaming_backend,
639 )?);
640
641 info!("Multi-Tier Cache System built with custom backends");
642 info!("Note: Using custom backends - use cache_manager() for all operations");
643
644 Ok(CacheSystem {
645 cache_manager,
646 #[cfg(feature = "moka")]
647 #[cfg_attr(docsrs, doc(cfg(feature = "moka")))]
648 l1_cache: None,
649 #[cfg(feature = "redis")]
650 #[cfg_attr(docsrs, doc(cfg(feature = "redis")))]
651 l2_cache: None,
652 })
653 }
654}
655
656impl Default for CacheSystemBuilder {
657 fn default() -> Self {
658 Self::new()
659 }
660}