Skip to main content

backbone_cache/
memory.rs

1//! In-memory cache implementation
2
3use std::collections::HashMap;
4use std::sync::Arc;
5use tokio::sync::RwLock;
6use async_trait::async_trait;
7use serde::{Serialize, Deserialize};
8use crate::{CacheResult, CacheError, CacheEntry, CacheStats, Cache};
9use chrono::Utc;
10
11/// In-memory cache implementation
12pub struct MemoryCache {
13    entries: Arc<RwLock<HashMap<String, CacheEntry<Vec<u8>>>>>,
14    stats: Arc<RwLock<CacheStats>>,
15    max_entries: Option<usize>,
16}
17
18impl MemoryCache {
19    /// Create new memory cache
20    pub fn new(max_entries: Option<usize>) -> Self {
21        Self {
22            entries: Arc::new(RwLock::new(HashMap::new())),
23            stats: Arc::new(RwLock::new(CacheStats::default())),
24            max_entries,
25        }
26    }
27
28    /// Remove expired entries
29    async fn cleanup_expired(&self) {
30        let mut entries = self.entries.write().await;
31        let mut expired_count = 0;
32
33        entries.retain(|_, entry| {
34            if entry.is_expired() {
35                expired_count += 1;
36                return false;
37            }
38            true
39        });
40
41        if expired_count > 0 {
42            let mut stats = self.stats.write().await;
43            stats.expired_entries = 0; // Reset since we just cleaned up
44        }
45    }
46
47    /// Ensure we don't exceed max entries (LRU eviction)
48    async fn evict_if_needed(&self) {
49        let Some(max_entries) = self.max_entries else {
50            return;
51        };
52
53        let mut entries = self.entries.write().await;
54        if entries.len() < max_entries {
55            return;
56        }
57
58        // Find least recently used entry. Entries that have never been
59        // accessed fall back to their insertion time, so a freshly inserted
60        // entry is considered older than one that has been touched since.
61        let Some(key_to_remove) = entries
62            .iter()
63            .min_by_key(|(_, entry)| entry.last_accessed.unwrap_or(entry.created_at))
64            .map(|(k, _)| k.clone())
65        else {
66            return;
67        };
68
69        entries.remove(&key_to_remove);
70    }
71}
72
73#[async_trait]
74impl Cache for MemoryCache {
75    async fn set<T>(&self, key: &str, value: &T, ttl_seconds: Option<u64>) -> CacheResult<()>
76    where
77        T: Serialize + Send + Sync,
78    {
79        // Cleanup expired entries periodically
80        self.cleanup_expired().await;
81
82        // Evict if we're at capacity
83        self.evict_if_needed().await;
84
85        // Serialize the value
86        let data = serde_json::to_vec(value)
87            .map_err(|e| CacheError::Serialization(e.to_string()))?;
88
89        // Create cache entry
90        let entry = CacheEntry::new(data, ttl_seconds);
91
92        // Store the entry
93        {
94            let mut entries = self.entries.write().await;
95            entries.insert(key.to_string(), entry);
96        }
97
98        // Update stats
99        {
100            let mut stats = self.stats.write().await;
101            stats.record_set();
102            stats.total_entries = self.entries.read().await.len() as u64;
103        }
104
105        Ok(())
106    }
107
108    async fn get<T>(&self, key: &str) -> CacheResult<Option<T>>
109    where
110        T: for<'de> Deserialize<'de> + Send + Sync,
111    {
112        // Cleanup expired entries periodically
113        self.cleanup_expired().await;
114
115        let entry = {
116            let entries = self.entries.read().await;
117            entries.get(key).cloned()
118        };
119
120        let Some(mut cache_entry) = entry else {
121            // Update stats for miss
122            {
123                let mut stats = self.stats.write().await;
124                stats.record_miss();
125            }
126            return Ok(None);
127        };
128
129        // Check if entry is expired
130        if cache_entry.is_expired() {
131            // Remove expired entry
132            {
133                let mut entries = self.entries.write().await;
134                entries.remove(key);
135            }
136
137            // Update stats for miss
138            {
139                let mut stats = self.stats.write().await;
140                stats.record_miss();
141                stats.total_entries = self.entries.read().await.len() as u64;
142            }
143
144            return Ok(None);
145        }
146
147        // Mark as accessed
148        cache_entry.mark_accessed();
149
150        // Deserialize the value before moving
151        let value = serde_json::from_slice(&cache_entry.data)
152            .map_err(|e| CacheError::Deserialization(e.to_string()))?;
153
154        // Update the entry in the cache
155        {
156            let mut entries = self.entries.write().await;
157            entries.insert(key.to_string(), cache_entry);
158        }
159
160        // Update stats for hit
161        {
162            let mut stats = self.stats.write().await;
163            stats.record_hit();
164        }
165
166        Ok(Some(value))
167    }
168
169    async fn delete(&self, key: &str) -> CacheResult<bool> {
170        let removed = {
171            let mut entries = self.entries.write().await;
172            entries.remove(key).is_some()
173        };
174
175        if removed {
176            // Update stats
177            {
178                let mut stats = self.stats.write().await;
179                stats.record_delete();
180                stats.total_entries = self.entries.read().await.len() as u64;
181            }
182        }
183
184        Ok(removed)
185    }
186
187    async fn exists(&self, key: &str) -> CacheResult<bool> {
188        self.cleanup_expired().await;
189
190        let entries = self.entries.read().await;
191        Ok(entries.get(key).is_some_and(|entry| !entry.is_expired()))
192    }
193
194    async fn expire(&self, key: &str, ttl_seconds: u64) -> CacheResult<bool> {
195        let mut entries = self.entries.write().await;
196
197        let Some(entry) = entries.get_mut(key) else {
198            return Ok(false);
199        };
200
201        entry.expires_at = Some(Utc::now() + chrono::Duration::seconds(ttl_seconds as i64));
202        Ok(true)
203    }
204
205    async fn ttl(&self, key: &str) -> CacheResult<Option<u64>> {
206        self.cleanup_expired().await;
207
208        let entries = self.entries.read().await;
209        let Some(entry) = entries.get(key) else {
210            return Ok(None); // Key doesn't exist
211        };
212
213        let Some(expires_at) = entry.expires_at else {
214            return Ok(None); // No expiration
215        };
216
217        let ttl = (expires_at - Utc::now()).num_seconds();
218        if ttl > 0 {
219            return Ok(Some(ttl as u64));
220        }
221        Ok(Some(0)) // Expired
222    }
223
224    async fn clear(&self) -> CacheResult<()> {
225        {
226            let mut entries = self.entries.write().await;
227            entries.clear();
228        }
229
230        {
231            let mut stats = self.stats.write().await;
232            *stats = CacheStats::default();
233        }
234
235        Ok(())
236    }
237
238    async fn stats(&self) -> CacheResult<CacheStats> {
239        self.cleanup_expired().await;
240
241        let stats = self.stats.read().await;
242        let entries_count = self.entries.read().await.len() as u64;
243
244        Ok(CacheStats {
245            total_entries: entries_count,
246            expired_entries: 0, // We just cleaned up
247            memory_usage: None, // Could calculate based on serialized size
248            hit_rate: stats.hit_rate,
249            hits: stats.hits,
250            misses: stats.misses,
251            sets: stats.sets,
252            deletes: stats.deletes,
253        })
254    }
255
256    async fn mget<T>(&self, keys: Vec<String>) -> CacheResult<Vec<(String, Option<T>)>>
257    where
258        T: for<'de> Deserialize<'de> + Send + Sync,
259    {
260        let mut results = Vec::with_capacity(keys.len());
261
262        for key in keys {
263            let value = self.get::<T>(&key).await?;
264            results.push((key, value));
265        }
266
267        Ok(results)
268    }
269
270    async fn mset<T>(&self, entries: Vec<(String, T, Option<u64>)>) -> CacheResult<()>
271    where
272        T: Serialize + Send + Sync,
273    {
274        for (key, value, ttl) in entries {
275            self.set(&key, &value, ttl).await?;
276        }
277
278        Ok(())
279    }
280
281    async fn mdelete(&self, keys: Vec<String>) -> CacheResult<u64> {
282        let mut deleted_count = 0;
283
284        for key in keys {
285            if self.delete(&key).await? {
286                deleted_count += 1;
287            }
288        }
289
290        Ok(deleted_count)
291    }
292}