codeprism_storage/
cache.rs1use crate::{CacheStats, CacheStorage};
4use anyhow::Result;
5use async_trait::async_trait;
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::{Arc, Mutex};
9use std::time::{Duration, SystemTime};
10
11#[derive(Debug, Clone)]
13struct CacheEntry {
14 data: Vec<u8>,
15 last_accessed: SystemTime,
16 expires_at: Option<SystemTime>,
17}
18
19pub struct LruCacheStorage {
21 cache: Arc<Mutex<HashMap<String, CacheEntry>>>,
22 max_size_bytes: usize,
23 current_size_bytes: Arc<Mutex<usize>>,
24 stats: Arc<Mutex<CacheStats>>,
25}
26
27impl LruCacheStorage {
28 pub fn new(max_size_bytes: usize) -> Self {
30 Self {
31 cache: Arc::new(Mutex::new(HashMap::new())),
32 max_size_bytes,
33 current_size_bytes: Arc::new(Mutex::new(0)),
34 stats: Arc::new(Mutex::new(CacheStats {
35 total_keys: 0,
36 memory_usage_bytes: 0,
37 hit_count: 0,
38 miss_count: 0,
39 eviction_count: 0,
40 })),
41 }
42 }
43
44 fn evict_expired(&self) -> Result<()> {
46 let now = SystemTime::now();
47 let mut cache = self.cache.lock().unwrap();
48 let mut current_size = self.current_size_bytes.lock().unwrap();
49 let mut stats = self.stats.lock().unwrap();
50
51 let keys_to_remove: Vec<String> = cache
52 .iter()
53 .filter_map(|(key, entry)| {
54 if let Some(expires_at) = entry.expires_at {
55 if now > expires_at {
56 Some(key.clone())
57 } else {
58 None
59 }
60 } else {
61 None
62 }
63 })
64 .collect();
65
66 for key in keys_to_remove {
67 if let Some(entry) = cache.remove(&key) {
68 *current_size -= entry.data.len();
69 stats.eviction_count += 1;
70 }
71 }
72
73 stats.total_keys = cache.len();
74 stats.memory_usage_bytes = *current_size;
75
76 Ok(())
77 }
78
79 fn evict_lru(&self, needed_space: usize) -> Result<()> {
81 let mut cache = self.cache.lock().unwrap();
82 let mut current_size = self.current_size_bytes.lock().unwrap();
83 let mut stats = self.stats.lock().unwrap();
84
85 while *current_size + needed_space > self.max_size_bytes && !cache.is_empty() {
86 let lru_key = cache
88 .iter()
89 .min_by_key(|(_, entry)| entry.last_accessed)
90 .map(|(key, _)| key.clone());
91
92 if let Some(key) = lru_key {
93 if let Some(entry) = cache.remove(&key) {
94 *current_size -= entry.data.len();
95 stats.eviction_count += 1;
96 }
97 } else {
98 break;
99 }
100 }
101
102 stats.total_keys = cache.len();
103 stats.memory_usage_bytes = *current_size;
104
105 Ok(())
106 }
107}
108
109#[async_trait]
110impl CacheStorage for LruCacheStorage {
111 async fn get<T>(&self, key: &str) -> Result<Option<T>>
112 where
113 T: for<'de> Deserialize<'de> + Send,
114 {
115 self.evict_expired()?;
117
118 let mut cache = self.cache.lock().unwrap();
119 let mut stats = self.stats.lock().unwrap();
120
121 if let Some(entry) = cache.get_mut(key) {
122 if let Some(expires_at) = entry.expires_at {
124 if SystemTime::now() > expires_at {
125 cache.remove(key);
126 stats.miss_count += 1;
127 stats.total_keys = cache.len();
128 return Ok(None);
129 }
130 }
131
132 entry.last_accessed = SystemTime::now();
134 stats.hit_count += 1;
135
136 let value: T = bincode::deserialize(&entry.data)?;
138 Ok(Some(value))
139 } else {
140 stats.miss_count += 1;
141 Ok(None)
142 }
143 }
144
145 async fn set<T>(&self, key: &str, value: &T, ttl: Option<Duration>) -> Result<()>
146 where
147 T: Serialize + Send + Sync,
148 {
149 self.evict_expired()?;
151
152 let serialized = bincode::serialize(value)?;
153 let needed_space = serialized.len();
154
155 self.evict_lru(needed_space)?;
157
158 let expires_at = ttl.map(|duration| SystemTime::now() + duration);
159
160 let entry = CacheEntry {
161 data: serialized,
162 last_accessed: SystemTime::now(),
163 expires_at,
164 };
165
166 let mut cache = self.cache.lock().unwrap();
167 let mut current_size = self.current_size_bytes.lock().unwrap();
168 let mut stats = self.stats.lock().unwrap();
169
170 if let Some(old_entry) = cache.remove(key) {
172 *current_size -= old_entry.data.len();
173 }
174
175 *current_size += entry.data.len();
177 cache.insert(key.to_string(), entry);
178
179 stats.total_keys = cache.len();
180 stats.memory_usage_bytes = *current_size;
181
182 Ok(())
183 }
184
185 async fn delete(&self, key: &str) -> Result<()> {
186 let mut cache = self.cache.lock().unwrap();
187 let mut current_size = self.current_size_bytes.lock().unwrap();
188 let mut stats = self.stats.lock().unwrap();
189
190 if let Some(entry) = cache.remove(key) {
191 *current_size -= entry.data.len();
192 }
193
194 stats.total_keys = cache.len();
195 stats.memory_usage_bytes = *current_size;
196
197 Ok(())
198 }
199
200 async fn invalidate_pattern(&self, pattern: &str) -> Result<()> {
201 let mut cache = self.cache.lock().unwrap();
202 let mut current_size = self.current_size_bytes.lock().unwrap();
203 let mut stats = self.stats.lock().unwrap();
204
205 let keys_to_remove: Vec<String> = cache
206 .keys()
207 .filter(|key| key.contains(pattern))
208 .cloned()
209 .collect();
210
211 for key in keys_to_remove {
212 if let Some(entry) = cache.remove(&key) {
213 *current_size -= entry.data.len();
214 }
215 }
216
217 stats.total_keys = cache.len();
218 stats.memory_usage_bytes = *current_size;
219
220 Ok(())
221 }
222
223 async fn get_stats(&self) -> Result<CacheStats> {
224 let stats = self.stats.lock().unwrap();
225 Ok(stats.clone())
226 }
227
228 async fn clear(&self) -> Result<()> {
229 let mut cache = self.cache.lock().unwrap();
230 let mut current_size = self.current_size_bytes.lock().unwrap();
231 let mut stats = self.stats.lock().unwrap();
232
233 cache.clear();
234 *current_size = 0;
235
236 stats.total_keys = 0;
237 stats.memory_usage_bytes = 0;
238
239 Ok(())
240 }
241}