multi_tier_cache/
lib.rs

1//! Multi-Tier Cache
2//!
3//! A high-performance, production-ready multi-tier caching library for Rust featuring:
4//! - **L1 Cache**: In-memory caching with Moka (sub-millisecond latency)
5//! - **L2 Cache**: Distributed caching with Redis (persistent storage)
6//! - **Cache Stampede Protection**: `DashMap` + Mutex request coalescing
7//! - **Redis Streams**: Built-in support for event streaming
8//! - **Automatic L2-to-L1 Promotion**: Intelligent cache tier promotion
9//! - **Comprehensive Statistics**: Hit rates, promotions, in-flight tracking
10//!
11//! # Quick Start
12//!
13//! ```rust,no_run
14//! use multi_tier_cache::{CacheSystem, CacheStrategy};
15//!
16//! #[tokio::main]
17//! async fn main() -> anyhow::Result<()> {
18//!     // Initialize cache system
19//!     let cache = CacheSystem::new().await?;
20//!
21//!     // Store data with cache strategy
22//!     let data = serde_json::json!({"user": "alice", "score": 100});
23//!     cache.cache_manager()
24//!         .set_with_strategy("user:1", data, CacheStrategy::ShortTerm)
25//!         .await?;
26//!
27//!     // Retrieve data (L1 first, then L2 fallback)
28//!     if let Some(cached) = cache.cache_manager().get("user:1").await? {
29//!         tracing::info!("Cached data: {}", cached);
30//!     }
31//!
32//!     // Get statistics
33//!     let stats = cache.cache_manager().get_stats();
34//!     tracing::info!("Hit rate: {:.2}%", stats.hit_rate);
35//!
36//!     Ok(())
37//! }
38//! ```
39//!
40//! # Features
41//!
42//! - **Multi-Tier Architecture**: Combines fast in-memory (L1) with persistent distributed (L2) caching
43//! - **Cache Stampede Protection**: Prevents duplicate computations during cache misses
44//! - **Redis Streams**: Publish/subscribe with automatic trimming
45//! - **Zero-Config**: Sensible defaults, works out of the box
46//! - **Production-Proven**: Battle-tested at 16,829+ RPS with 5.2ms latency
47//!
48//! # Architecture
49//!
50//! ```text
51//! Request → L1 Cache (Moka) → L2 Cache (Redis) → Compute/Fetch
52//!           ↓ Hit (90%)       ↓ Hit (75%)        ↓ Miss (5%)
53//!           Return            Promote to L1       Store in L1+L2
54//! ```
55
56use anyhow::Result;
57use std::sync::Arc;
58use tracing::{info, warn};
59
60pub mod backends;
61pub mod builder;
62pub mod cache_manager;
63pub mod invalidation;
64pub mod redis_streams;
65pub mod traits;
66
67// Re-export backend types (maintains backward compatibility)
68pub use backends::{
69    DashMapCache, // Additional backends
70    L1Cache,
71    L2Cache, // Type aliases
72    MokaCache,
73    MokaCacheConfig,
74    RedisCache, // Default backends
75};
76
77// Optional backends (feature-gated)
78#[cfg(feature = "backend-memcached")]
79pub use backends::MemcachedCache;
80
81#[cfg(feature = "backend-quickcache")]
82pub use backends::QuickCacheBackend;
83pub use builder::CacheSystemBuilder;
84pub use cache_manager::{
85    CacheManager,
86    CacheManagerStats,
87    CacheStrategy,
88    CacheTier,
89    // Multi-tier support (v0.5.0+)
90    TierConfig,
91    TierStats,
92};
93pub use invalidation::{
94    InvalidationConfig, InvalidationMessage, InvalidationPublisher, InvalidationStats,
95    InvalidationSubscriber,
96};
97pub use redis_streams::RedisStreams;
98pub use traits::{CacheBackend, L2CacheBackend, StreamingBackend};
99
100// Re-export async_trait for user convenience
101pub use async_trait::async_trait;
102
103/// Main entry point for the Multi-Tier Cache system
104///
105/// Provides unified access to L1 (Moka) and L2 (Redis) caches with
106/// automatic failover, promotion, and stampede protection.
107///
108/// # Example
109///
110/// ```rust,no_run
111/// use multi_tier_cache::CacheSystem;
112///
113/// #[tokio::main]
114/// async fn main() -> anyhow::Result<()> {
115///     let cache = CacheSystem::new().await?;
116///
117///     // Use cache_manager for all operations
118///     let manager = cache.cache_manager();
119///
120///     Ok(())
121/// }
122/// ```
123///
124/// # Note on `l1_cache` and `l2_cache` Fields
125///
126/// When using multi-tier mode or custom backends, `l1_cache` and `l2_cache`
127/// may be `None`. Always use `cache_manager()` for cache operations.
128#[derive(Clone)]
129pub struct CacheSystem {
130    /// Unified cache manager (primary interface)
131    pub cache_manager: Arc<CacheManager>,
132    /// L1 Cache (in-memory, Moka) - `None` when using custom backends
133    pub l1_cache: Option<Arc<L1Cache>>,
134    /// L2 Cache (distributed, Redis) - `None` when using custom backends
135    pub l2_cache: Option<Arc<L2Cache>>,
136}
137
138impl CacheSystem {
139    /// Create new cache system with default configuration
140    ///
141    /// # Configuration
142    ///
143    /// Redis connection is configured via `REDIS_URL` environment variable.
144    /// Default: `redis://127.0.0.1:6379`
145    ///
146    /// # Example
147    ///
148    /// ```rust,no_run
149    /// use multi_tier_cache::CacheSystem;
150    ///
151    /// #[tokio::main]
152    /// async fn main() -> anyhow::Result<()> {
153    ///     // Set environment variable (optional)
154    ///     std::env::set_var("REDIS_URL", "redis://localhost:6379");
155    ///
156    ///     let cache = CacheSystem::new().await?;
157    ///     Ok(())
158    /// }
159    /// ```
160    /// # Errors
161    ///
162    /// Returns an error if cache initialization fails.
163    pub async fn new() -> Result<Self> {
164        info!("Initializing Multi-Tier Cache System");
165
166        // Initialize L1 cache (Moka)
167        let l1_cache = Arc::new(L1Cache::new(MokaCacheConfig::default())?);
168
169        // Initialize L2 cache (Redis)
170        let l2_cache = Arc::new(L2Cache::new().await?);
171
172        // Initialize cache manager
173        let cache_manager = Arc::new(CacheManager::new(l1_cache.clone(), l2_cache.clone()).await?);
174
175        info!("Multi-Tier Cache System initialized successfully");
176
177        Ok(Self {
178            cache_manager,
179            l1_cache: Some(l1_cache),
180            l2_cache: Some(l2_cache),
181        })
182    }
183
184    /// Create cache system with custom Redis URL
185    ///
186    /// # Arguments
187    ///
188    /// * `redis_url` - Redis connection string (e.g., `<redis://localhost:6379>`)
189    ///
190    /// # Example
191    ///
192    /// ```rust,no_run
193    /// use multi_tier_cache::CacheSystem;
194    ///
195    /// #[tokio::main]
196    /// async fn main() -> anyhow::Result<()> {
197    ///     let cache = CacheSystem::with_redis_url("redis://custom:6379").await?;
198    ///     Ok(())
199    /// }
200    /// ```
201    /// # Errors
202    ///
203    /// Returns an error if cache initialization fails.
204    pub async fn with_redis_url(redis_url: &str) -> Result<Self> {
205        info!(redis_url = %redis_url, "Initializing Multi-Tier Cache System with custom Redis URL");
206
207        // Initialize L1 cache (Moka)
208        let l1_cache = Arc::new(L1Cache::new(MokaCacheConfig::default())?);
209
210        // Initialize L2 cache (Redis) with custom URL
211        let l2_cache = Arc::new(L2Cache::with_url(redis_url).await?);
212
213        // Initialize cache manager
214        let cache_manager =
215            Arc::new(CacheManager::new(Arc::clone(&l1_cache), Arc::clone(&l2_cache)).await?);
216
217        info!("Multi-Tier Cache System initialized successfully");
218
219        Ok(Self {
220            cache_manager,
221            l1_cache: Some(l1_cache),
222            l2_cache: Some(l2_cache),
223        })
224    }
225
226    /// Perform health check on all cache tiers
227    ///
228    /// Returns `true` if at least L1 is operational.
229    /// L2 failure is tolerated (graceful degradation).
230    ///
231    /// # Example
232    ///
233    /// ```rust,no_run
234    /// use multi_tier_cache::CacheSystem;
235    ///
236    /// #[tokio::main]
237    /// async fn main() -> anyhow::Result<()> {
238    ///     let cache = CacheSystem::new().await?;
239    ///
240    ///     if cache.health_check().await {
241    ///         tracing::info!("Cache system healthy");
242    ///     }
243    ///
244    ///     Ok(())
245    /// }
246    /// ```
247    pub async fn health_check(&self) -> bool {
248        let l1_ok = match &self.l1_cache {
249            Some(cache) => cache.health_check().await,
250            None => true, // If no L1 cache instance, assume OK (using custom backends)
251        };
252
253        let l2_ok = match &self.l2_cache {
254            Some(cache) => cache.health_check().await,
255            None => true, // If no L2 cache instance, assume OK (using custom backends)
256        };
257
258        if l1_ok && l2_ok {
259            info!("Multi-Tier Cache health check passed");
260            true
261        } else {
262            warn!(l1_ok = %l1_ok, l2_ok = %l2_ok, "Multi-Tier Cache health check - partial failure");
263            l1_ok // At minimum, L1 should work
264        }
265    }
266
267    /// Get reference to cache manager (primary interface)
268    ///
269    /// Use this for all cache operations: get, set, streams, etc.
270    #[must_use]
271    pub fn cache_manager(&self) -> &Arc<CacheManager> {
272        &self.cache_manager
273    }
274}