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