Skip to main content

sh_layer1/
cache_manager.rs

1//! 缓存管理模块
2//!
3//! 高性能缓存,支持 LRU、TTL、TTI 策略。
4
5use moka::future::Cache as MokaCache;
6use serde::{Deserialize, Serialize};
7use std::time::Duration;
8
9/// 缓存配置
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct CacheConfig {
12    /// 最大容量
13    pub max_capacity: u64,
14    /// TTL(秒)- 时间到期后自动过期
15    pub ttl_secs: u64,
16    /// TTI(秒)- 空闲时间到期后自动过期
17    pub tti_secs: Option<u64>,
18    /// 初始容量(预分配)
19    pub initial_capacity: Option<u64>,
20    /// 是否启用统计
21    pub enable_stats: bool,
22}
23
24impl Default for CacheConfig {
25    fn default() -> Self {
26        Self {
27            max_capacity: 10_000,
28            ttl_secs: 300, // 5分钟
29            tti_secs: None,
30            initial_capacity: Some(1_000),
31            enable_stats: false,
32        }
33    }
34}
35
36impl CacheConfig {
37    /// 创建高性能缓存配置
38    pub fn high_performance() -> Self {
39        Self {
40            max_capacity: 100_000,
41            ttl_secs: 600,       // 10分钟
42            tti_secs: Some(300), // 5分钟空闲过期
43            initial_capacity: Some(10_000),
44            enable_stats: true,
45        }
46    }
47
48    /// 创建低内存占用配置
49    pub fn low_memory() -> Self {
50        Self {
51            max_capacity: 1_000,
52            ttl_secs: 60, // 1分钟
53            tti_secs: Some(30),
54            initial_capacity: Some(100),
55            enable_stats: false,
56        }
57    }
58}
59
60/// 缓存管理器
61pub struct CacheManager {
62    cache: MokaCache<String, Vec<u8>>,
63    config: CacheConfig,
64}
65
66impl CacheManager {
67    pub fn new(config: CacheConfig) -> Self {
68        let mut builder = MokaCache::builder()
69            .max_capacity(config.max_capacity)
70            .time_to_live(Duration::from_secs(config.ttl_secs));
71
72        // 设置 TTI(空闲过期)
73        if let Some(tti_secs) = config.tti_secs {
74            builder = builder.time_to_idle(Duration::from_secs(tti_secs));
75        }
76
77        // 设置初始容量
78        if let Some(initial_capacity) = config.initial_capacity {
79            builder = builder.initial_capacity(initial_capacity as usize);
80        }
81
82        let cache = builder.build();
83
84        Self { cache, config }
85    }
86
87    /// 获取缓存
88    pub async fn get(&self, key: &str) -> Option<Vec<u8>> {
89        self.cache.get(key).await
90    }
91
92    /// 设置缓存
93    pub async fn set(&self, key: String, value: Vec<u8>) {
94        self.cache.insert(key, value).await;
95    }
96
97    /// 删除缓存
98    pub async fn remove(&self, key: &str) {
99        self.cache.invalidate(key).await;
100    }
101
102    /// 批量获取
103    pub async fn get_batch(&self, keys: &[String]) -> Vec<Option<Vec<u8>>> {
104        let mut results = Vec::with_capacity(keys.len());
105        for key in keys {
106            results.push(self.cache.get(key).await);
107        }
108        results
109    }
110
111    /// 批量设置
112    pub async fn set_batch(&self, entries: Vec<(String, Vec<u8>)>) {
113        for (key, value) in entries {
114            self.cache.insert(key, value).await;
115        }
116    }
117
118    /// 清空所有缓存
119    pub async fn clear(&self) {
120        self.cache.invalidate_all();
121    }
122
123    /// 获取缓存条目数量
124    pub fn entry_count(&self) -> u64 {
125        self.cache.entry_count()
126    }
127
128    /// 获取配置
129    pub fn config(&self) -> &CacheConfig {
130        &self.config
131    }
132
133    /// 检查缓存是否存在
134    pub async fn contains(&self, key: &str) -> bool {
135        self.cache.get(key).await.is_some()
136    }
137
138    /// 获取并更新(如果不存在则插入)
139    pub async fn get_or_insert<F>(&self, key: String, default: F) -> Vec<u8>
140    where
141        F: FnOnce() -> Vec<u8> + Send,
142    {
143        match self.cache.get(&key).await {
144            Some(value) => value,
145            None => {
146                let value = default();
147                self.cache.insert(key, value.clone()).await;
148                value
149            }
150        }
151    }
152}
153
154impl Default for CacheManager {
155    fn default() -> Self {
156        Self::new(CacheConfig::default())
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163
164    #[tokio::test]
165    async fn test_basic_operations() {
166        let cache = CacheManager::default();
167
168        cache.set("key1".to_string(), b"value1".to_vec()).await;
169        let value = cache.get("key1").await;
170        assert_eq!(value, Some(b"value1".to_vec()));
171
172        cache.remove("key1").await;
173        let value = cache.get("key1").await;
174        assert!(value.is_none());
175    }
176
177    #[tokio::test]
178    async fn test_ttl() {
179        let config = CacheConfig {
180            ttl_secs: 1,
181            ..Default::default()
182        };
183        let cache = CacheManager::new(config);
184
185        cache.set("key1".to_string(), b"value1".to_vec()).await;
186        assert!(cache.get("key1").await.is_some());
187
188        // 等待 TTL 过期
189        tokio::time::sleep(Duration::from_secs(2)).await;
190        assert!(cache.get("key1").await.is_none());
191    }
192
193    #[tokio::test]
194    async fn test_batch_operations() {
195        let cache = CacheManager::default();
196
197        let entries = vec![
198            ("key1".to_string(), b"value1".to_vec()),
199            ("key2".to_string(), b"value2".to_vec()),
200            ("key3".to_string(), b"value3".to_vec()),
201        ];
202        cache.set_batch(entries).await;
203
204        let values = cache
205            .get_batch(&["key1".to_string(), "key2".to_string(), "key3".to_string()])
206            .await;
207        assert_eq!(values.len(), 3);
208        assert!(values[0].is_some());
209        assert!(values[1].is_some());
210        assert!(values[2].is_some());
211    }
212
213    #[tokio::test]
214    async fn test_clear() {
215        let cache = CacheManager::default();
216
217        cache.set("key1".to_string(), b"value1".to_vec()).await;
218        cache.set("key2".to_string(), b"value2".to_vec()).await;
219
220        // moka 可能异步处理,所以等待一下
221        tokio::time::sleep(Duration::from_millis(10)).await;
222
223        cache.clear().await;
224
225        // invalidate_all 后条目应该被清除
226        assert!(cache.get("key1").await.is_none());
227        assert!(cache.get("key2").await.is_none());
228    }
229
230    #[tokio::test]
231    async fn test_contains() {
232        let cache = CacheManager::default();
233
234        cache.set("key1".to_string(), b"value1".to_vec()).await;
235        assert!(cache.contains("key1").await);
236        assert!(!cache.contains("key2").await);
237    }
238
239    #[tokio::test]
240    async fn test_get_or_insert() {
241        let cache = CacheManager::default();
242
243        // 第一次应该插入默认值
244        let value = cache
245            .get_or_insert("key1".to_string(), || b"default".to_vec())
246            .await;
247        assert_eq!(value, b"default".to_vec());
248
249        // 第二次应该返回已存在的值
250        let value = cache
251            .get_or_insert("key1".to_string(), || b"new_default".to_vec())
252            .await;
253        assert_eq!(value, b"default".to_vec());
254    }
255
256    #[test]
257    fn test_config_presets() {
258        let hp_config = CacheConfig::high_performance();
259        assert_eq!(hp_config.max_capacity, 100_000);
260        assert!(hp_config.tti_secs.is_some());
261
262        let lm_config = CacheConfig::low_memory();
263        assert_eq!(lm_config.max_capacity, 1_000);
264        assert!(lm_config.tti_secs.is_some());
265    }
266
267    #[tokio::test]
268    async fn test_max_capacity() {
269        let config = CacheConfig {
270            max_capacity: 5,
271            ..Default::default()
272        };
273        let cache = CacheManager::new(config);
274
275        // 插入超过容量的条目
276        for i in 0..10 {
277            cache
278                .set(format!("key{}", i), format!("value{}", i).into_bytes())
279                .await;
280        }
281
282        // 由于 LRU,部分条目应该被淘汰
283        // 注意:moka 可能不会立即淘汰,所以这里只检查不会崩溃
284        assert!(cache.entry_count() <= 10);
285    }
286}