Skip to main content

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