cache_kit/backend/
inmemory.rs1use super::CacheBackend;
7use crate::error::Result;
8use dashmap::DashMap;
9use std::sync::Arc;
10use std::time::Duration;
11use std::time::Instant;
12
13struct CacheEntry {
15 data: Vec<u8>,
16 expires_at: Option<Instant>,
17}
18
19impl CacheEntry {
20 fn new(data: Vec<u8>, ttl: Option<Duration>) -> Self {
21 let expires_at = ttl.map(|d| Instant::now() + d);
22 CacheEntry { data, expires_at }
23 }
24
25 fn is_expired(&self) -> bool {
26 self.expires_at.is_some_and(|exp| Instant::now() > exp)
27 }
28}
29
30#[derive(Clone)]
60pub struct InMemoryBackend {
61 store: Arc<DashMap<String, CacheEntry>>,
62}
63
64impl InMemoryBackend {
65 pub fn new() -> Self {
67 InMemoryBackend {
68 store: Arc::new(DashMap::new()),
69 }
70 }
71
72 pub async fn len(&self) -> usize {
74 self.store.len()
75 }
76
77 pub async fn is_empty(&self) -> bool {
79 self.store.is_empty()
80 }
81
82 pub async fn stats(&self) -> CacheStats {
84 let total_bytes: usize = self.store.iter().map(|entry| entry.data.len()).sum();
85 let expired_count = self.store.iter().filter(|entry| entry.is_expired()).count();
86
87 CacheStats {
88 total_entries: self.store.len(),
89 expired_entries: expired_count,
90 total_bytes,
91 }
92 }
93
94 pub async fn log_stats(&self) {
96 let stats = self.stats().await;
97 debug!(
98 "Cache Stats: {} entries ({} expired), {} bytes",
99 stats.total_entries, stats.expired_entries, stats.total_bytes
100 );
101 }
102}
103
104impl Default for InMemoryBackend {
105 fn default() -> Self {
106 Self::new()
107 }
108}
109
110impl CacheBackend for InMemoryBackend {
111 async fn get(&self, key: &str) -> Result<Option<Vec<u8>>> {
112 if let Some(entry) = self.store.get(key) {
114 if !entry.is_expired() {
115 debug!("✓ InMemory GET {} -> HIT", key);
116 return Ok(Some(entry.data.clone()));
117 }
118 }
119
120 self.store.remove(key);
122 debug!("✓ InMemory GET {} -> MISS", key);
123 Ok(None)
124 }
125
126 async fn set(&self, key: &str, value: Vec<u8>, ttl: Option<Duration>) -> Result<()> {
127 let entry = CacheEntry::new(value, ttl);
128 self.store.insert(key.to_string(), entry);
129
130 if let Some(d) = ttl {
131 debug!("✓ InMemory SET {} (TTL: {:?})", key, d);
132 } else {
133 debug!("✓ InMemory SET {}", key);
134 }
135
136 Ok(())
137 }
138
139 async fn delete(&self, key: &str) -> Result<()> {
140 self.store.remove(key);
141 debug!("✓ InMemory DELETE {}", key);
142 Ok(())
143 }
144
145 async fn exists(&self, key: &str) -> Result<bool> {
146 if let Some(entry) = self.store.get(key) {
147 return Ok(!entry.is_expired());
148 }
149
150 Ok(false)
151 }
152
153 async fn mget(&self, keys: &[&str]) -> Result<Vec<Option<Vec<u8>>>> {
154 let results: Vec<Option<Vec<u8>>> = keys
155 .iter()
156 .map(|k| {
157 if let Some(entry) = self.store.get(*k) {
158 if entry.is_expired() {
159 None
160 } else {
161 Some(entry.data.clone())
162 }
163 } else {
164 None
165 }
166 })
167 .collect();
168
169 debug!("✓ InMemory MGET {} keys", keys.len());
170 Ok(results)
171 }
172
173 async fn mdelete(&self, keys: &[&str]) -> Result<()> {
174 for key in keys {
175 self.store.remove(*key);
176 }
177
178 debug!("✓ InMemory MDELETE {} keys", keys.len());
179 Ok(())
180 }
181
182 async fn health_check(&self) -> Result<bool> {
183 Ok(true)
185 }
186
187 async fn clear_all(&self) -> Result<()> {
188 self.store.clear();
189 warn!("⚠ InMemory CLEAR_ALL executed - all cache cleared!");
190 Ok(())
191 }
192}
193
194#[derive(Clone, Debug)]
196pub struct CacheStats {
197 pub total_entries: usize,
198 pub expired_entries: usize,
199 pub total_bytes: usize,
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[tokio::test]
207 async fn test_inmemory_backend_set_get() {
208 let backend = InMemoryBackend::new();
209
210 backend
211 .set("key1", b"value1".to_vec(), None)
212 .await
213 .expect("Failed to set");
214
215 let result = backend.get("key1").await.expect("Failed to get");
216 assert_eq!(result, Some(b"value1".to_vec()));
217 }
218
219 #[tokio::test]
220 async fn test_inmemory_backend_miss() {
221 let backend = InMemoryBackend::new();
222
223 let result = backend.get("nonexistent").await.expect("Failed to get");
224 assert_eq!(result, None);
225 }
226
227 #[tokio::test]
228 async fn test_inmemory_backend_delete() {
229 let backend = InMemoryBackend::new();
230
231 backend
232 .set("key1", b"value1".to_vec(), None)
233 .await
234 .expect("Failed to set");
235 assert!(backend
236 .exists("key1")
237 .await
238 .expect("Failed to check exists"));
239
240 backend.delete("key1").await.expect("Failed to delete");
241 assert!(!backend
242 .exists("key1")
243 .await
244 .expect("Failed to check exists"));
245 }
246
247 #[tokio::test]
248 async fn test_inmemory_backend_ttl_expiration() {
249 let backend = InMemoryBackend::new();
250
251 backend
252 .set("key1", b"value1".to_vec(), Some(Duration::from_millis(100)))
253 .await
254 .expect("Failed to set");
255
256 assert!(backend.get("key1").await.expect("Failed to get").is_some());
258
259 tokio::time::sleep(Duration::from_millis(150)).await;
261
262 assert!(backend.get("key1").await.expect("Failed to get").is_none());
264 }
265
266 #[tokio::test]
267 async fn test_inmemory_backend_mget() {
268 let backend = InMemoryBackend::new();
269
270 backend
271 .set("key1", b"value1".to_vec(), None)
272 .await
273 .expect("Failed to set");
274 backend
275 .set("key2", b"value2".to_vec(), None)
276 .await
277 .expect("Failed to set");
278
279 let results = backend
280 .mget(&["key1", "key2", "key3"])
281 .await
282 .expect("Failed to mget");
283
284 assert_eq!(results.len(), 3);
285 assert_eq!(results[0], Some(b"value1".to_vec()));
286 assert_eq!(results[1], Some(b"value2".to_vec()));
287 assert_eq!(results[2], None);
288 }
289
290 #[tokio::test]
291 async fn test_inmemory_backend_mdelete() {
292 let backend = InMemoryBackend::new();
293
294 backend
295 .set("key1", b"value1".to_vec(), None)
296 .await
297 .expect("Failed to set");
298 backend
299 .set("key2", b"value2".to_vec(), None)
300 .await
301 .expect("Failed to set");
302 backend
303 .set("key3", b"value3".to_vec(), None)
304 .await
305 .expect("Failed to set");
306
307 assert_eq!(backend.len().await, 3);
308
309 backend
310 .mdelete(&["key1", "key2"])
311 .await
312 .expect("Failed to mdelete");
313
314 assert_eq!(backend.len().await, 1);
315 assert!(backend.get("key3").await.expect("Failed to get").is_some());
316 }
317
318 #[tokio::test]
319 async fn test_inmemory_backend_clear_all() {
320 let backend = InMemoryBackend::new();
321
322 backend
323 .set("key1", b"value1".to_vec(), None)
324 .await
325 .expect("Failed to set");
326 backend
327 .set("key2", b"value2".to_vec(), None)
328 .await
329 .expect("Failed to set");
330
331 assert_eq!(backend.len().await, 2);
332
333 backend.clear_all().await.expect("Failed to clear");
334
335 assert_eq!(backend.len().await, 0);
336 }
337
338 #[tokio::test]
339 async fn test_inmemory_backend_stats() {
340 let backend = InMemoryBackend::new();
341
342 backend
343 .set("key1", b"value_with_data".to_vec(), None)
344 .await
345 .expect("Failed to set");
346 backend
347 .set("key2", b"data".to_vec(), None)
348 .await
349 .expect("Failed to set");
350
351 let stats = backend.stats().await;
352 assert_eq!(stats.total_entries, 2);
353 assert_eq!(stats.expired_entries, 0);
354 assert!(stats.total_bytes > 0);
355 }
356
357 #[tokio::test]
358 async fn test_inmemory_backend_clone() {
359 let backend1 = InMemoryBackend::new();
360 backend1
361 .set("key", b"value".to_vec(), None)
362 .await
363 .expect("Failed to set");
364
365 let backend2 = backend1.clone();
366
367 let value = backend2.store.get("key").map(|e| e.data.clone());
369 assert_eq!(value, Some(b"value".to_vec()));
370 }
371
372 #[tokio::test]
373 async fn test_inmemory_backend_thread_safe() {
374 use std::sync::Arc;
375
376 let backend = Arc::new(InMemoryBackend::new());
377 let mut handles = vec![];
378
379 for i in 0..10 {
380 let backend_clone = Arc::clone(&backend);
381 let handle = tokio::spawn(async move {
382 let b = (*backend_clone).clone();
383 let key = format!("key_{}", i);
384 let value = format!("value_{}", i);
385 b.set(&key, value.into_bytes(), None)
386 .await
387 .expect("Failed to set");
388 });
389 handles.push(handle);
390 }
391
392 for handle in handles {
393 handle.await.expect("Task failed");
394 }
395
396 assert!(backend.clone().len().await >= 10);
397 }
398}