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}