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    RedisCache, // Default backends
74};
75
76// Optional backends (feature-gated)
77#[cfg(feature = "backend-memcached")]
78pub use backends::MemcachedCache;
79
80#[cfg(feature = "backend-quickcache")]
81pub use backends::QuickCacheBackend;
82pub use builder::CacheSystemBuilder;
83pub use cache_manager::{
84    CacheManager,
85    CacheManagerStats,
86    CacheStrategy,
87    CacheTier,
88    // Multi-tier support (v0.5.0+)
89    TierConfig,
90    TierStats,
91};
92pub use invalidation::{
93    InvalidationConfig, InvalidationMessage, InvalidationPublisher, InvalidationStats,
94    InvalidationSubscriber,
95};
96pub use redis_streams::RedisStreams;
97pub use traits::{CacheBackend, L2CacheBackend, StreamingBackend};
98
99// Re-export async_trait for user convenience
100pub use async_trait::async_trait;
101
102/// Main entry point for the Multi-Tier Cache system
103///
104/// Provides unified access to L1 (Moka) and L2 (Redis) caches with
105/// automatic failover, promotion, and stampede protection.
106///
107/// # Example
108///
109/// ```rust,no_run
110/// use multi_tier_cache::CacheSystem;
111///
112/// #[tokio::main]
113/// async fn main() -> anyhow::Result<()> {
114///     let cache = CacheSystem::new().await?;
115///
116///     // Use cache_manager for all operations
117///     let manager = cache.cache_manager();
118///
119///     Ok(())
120/// }
121/// ```
122///
123/// # Note on `l1_cache` and `l2_cache` Fields
124///
125/// When using multi-tier mode or custom backends, `l1_cache` and `l2_cache`
126/// may be `None`. Always use `cache_manager()` for cache operations.
127#[derive(Clone)]
128pub struct CacheSystem {
129    /// Unified cache manager (primary interface)
130    pub cache_manager: Arc<CacheManager>,
131    /// L1 Cache (in-memory, Moka) - `None` when using custom backends
132    pub l1_cache: Option<Arc<L1Cache>>,
133    /// L2 Cache (distributed, Redis) - `None` when using custom backends
134    pub l2_cache: Option<Arc<L2Cache>>,
135}
136
137impl CacheSystem {
138    /// Create new cache system with default configuration
139    ///
140    /// # Configuration
141    ///
142    /// Redis connection is configured via `REDIS_URL` environment variable.
143    /// Default: `redis://127.0.0.1:6379`
144    ///
145    /// # Example
146    ///
147    /// ```rust,no_run
148    /// use multi_tier_cache::CacheSystem;
149    ///
150    /// #[tokio::main]
151    /// async fn main() -> anyhow::Result<()> {
152    ///     // Set environment variable (optional)
153    ///     std::env::set_var("REDIS_URL", "redis://localhost:6379");
154    ///
155    ///     let cache = CacheSystem::new().await?;
156    ///     Ok(())
157    /// }
158    /// ```
159    /// # Errors
160    ///
161    /// Returns an error if cache initialization fails.
162    pub async fn new() -> Result<Self> {
163        info!("Initializing Multi-Tier Cache System");
164
165        // Initialize L1 cache (Moka)
166        let l1_cache = Arc::new(L1Cache::new()?);
167
168        // Initialize L2 cache (Redis)
169        let l2_cache = Arc::new(L2Cache::new().await?);
170
171        // Initialize cache manager
172        let cache_manager = Arc::new(CacheManager::new(l1_cache.clone(), l2_cache.clone()).await?);
173
174        info!("Multi-Tier Cache System initialized successfully");
175
176        Ok(Self {
177            cache_manager,
178            l1_cache: Some(l1_cache),
179            l2_cache: Some(l2_cache),
180        })
181    }
182
183    /// Create cache system with custom Redis URL
184    ///
185    /// # Arguments
186    ///
187    /// * `redis_url` - Redis connection string (e.g., `<redis://localhost:6379>`)
188    ///
189    /// # Example
190    ///
191    /// ```rust,no_run
192    /// use multi_tier_cache::CacheSystem;
193    ///
194    /// #[tokio::main]
195    /// async fn main() -> anyhow::Result<()> {
196    ///     let cache = CacheSystem::with_redis_url("redis://custom:6379").await?;
197    ///     Ok(())
198    /// }
199    /// ```
200    /// # Errors
201    ///
202    /// Returns an error if cache initialization fails.
203    pub async fn with_redis_url(redis_url: &str) -> Result<Self> {
204        info!(redis_url = %redis_url, "Initializing Multi-Tier Cache System with custom Redis URL");
205
206        // Initialize L1 cache (Moka)
207        let l1_cache = Arc::new(L1Cache::new()?);
208
209        // Initialize L2 cache (Redis) with custom URL
210        let l2_cache = Arc::new(L2Cache::with_url(redis_url).await?);
211
212        // Initialize cache manager
213        let cache_manager =
214            Arc::new(CacheManager::new(Arc::clone(&l1_cache), Arc::clone(&l2_cache)).await?);
215
216        info!("Multi-Tier Cache System initialized successfully");
217
218        Ok(Self {
219            cache_manager,
220            l1_cache: Some(l1_cache),
221            l2_cache: Some(l2_cache),
222        })
223    }
224
225    /// Perform health check on all cache tiers
226    ///
227    /// Returns `true` if at least L1 is operational.
228    /// L2 failure is tolerated (graceful degradation).
229    ///
230    /// # Example
231    ///
232    /// ```rust,no_run
233    /// use multi_tier_cache::CacheSystem;
234    ///
235    /// #[tokio::main]
236    /// async fn main() -> anyhow::Result<()> {
237    ///     let cache = CacheSystem::new().await?;
238    ///
239    ///     if cache.health_check().await {
240    ///         tracing::info!("Cache system healthy");
241    ///     }
242    ///
243    ///     Ok(())
244    /// }
245    /// ```
246    pub async fn health_check(&self) -> bool {
247        let l1_ok = match &self.l1_cache {
248            Some(cache) => cache.health_check().await,
249            None => true, // If no L1 cache instance, assume OK (using custom backends)
250        };
251
252        let l2_ok = match &self.l2_cache {
253            Some(cache) => cache.health_check().await,
254            None => true, // If no L2 cache instance, assume OK (using custom backends)
255        };
256
257        if l1_ok && l2_ok {
258            info!("Multi-Tier Cache health check passed");
259            true
260        } else {
261            warn!(l1_ok = %l1_ok, l2_ok = %l2_ok, "Multi-Tier Cache health check - partial failure");
262            l1_ok // At minimum, L1 should work
263        }
264    }
265
266    /// Get reference to cache manager (primary interface)
267    ///
268    /// Use this for all cache operations: get, set, streams, etc.
269    #[must_use]
270    pub fn cache_manager(&self) -> &Arc<CacheManager> {
271        &self.cache_manager
272    }
273}