sh_layer1/
cache_manager.rs1use moka::future::Cache as MokaCache;
6use serde::{Deserialize, Serialize};
7use std::time::Duration;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct CacheConfig {
12 pub max_capacity: u64,
14 pub ttl_secs: u64,
16 pub tti_secs: Option<u64>,
18 pub initial_capacity: Option<u64>,
20 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, tti_secs: None,
30 initial_capacity: Some(1_000),
31 enable_stats: false,
32 }
33 }
34}
35
36impl CacheConfig {
37 pub fn high_performance() -> Self {
39 Self {
40 max_capacity: 100_000,
41 ttl_secs: 600, tti_secs: Some(300), initial_capacity: Some(10_000),
44 enable_stats: true,
45 }
46 }
47
48 pub fn low_memory() -> Self {
50 Self {
51 max_capacity: 1_000,
52 ttl_secs: 60, tti_secs: Some(30),
54 initial_capacity: Some(100),
55 enable_stats: false,
56 }
57 }
58}
59
60pub 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 if let Some(tti_secs) = config.tti_secs {
74 builder = builder.time_to_idle(Duration::from_secs(tti_secs));
75 }
76
77 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 pub async fn get(&self, key: &str) -> Option<Vec<u8>> {
89 self.cache.get(key).await
90 }
91
92 pub async fn set(&self, key: String, value: Vec<u8>) {
94 self.cache.insert(key, value).await;
95 }
96
97 pub async fn remove(&self, key: &str) {
99 self.cache.invalidate(key).await;
100 }
101
102 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 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 pub async fn clear(&self) {
120 self.cache.invalidate_all();
121 }
122
123 pub fn entry_count(&self) -> u64 {
125 self.cache.entry_count()
126 }
127
128 pub fn config(&self) -> &CacheConfig {
130 &self.config
131 }
132
133 pub async fn contains(&self, key: &str) -> bool {
135 self.cache.get(key).await.is_some()
136 }
137
138 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 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 tokio::time::sleep(Duration::from_millis(10)).await;
222
223 cache.clear().await;
224
225 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 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 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 for i in 0..10 {
277 cache
278 .set(format!("key{}", i), format!("value{}", i).into_bytes())
279 .await;
280 }
281
282 assert!(cache.entry_count() <= 10);
285 }
286}