1use std::collections::HashMap;
6use std::hash::Hash;
7use std::sync::Arc;
8use parking_lot::RwLock;
9use serde::{Deserialize, Serialize};
10
11struct CacheEntry<V> {
13 value: V,
14 access_count: u64,
15 last_accessed: std::time::Instant,
16}
17
18pub struct LruCache<K, V>
20where
21 K: Eq + Hash + Clone,
22 V: Clone,
23{
24 cache: Arc<RwLock<HashMap<K, CacheEntry<V>>>>,
25 capacity: usize,
26 hits: Arc<RwLock<u64>>,
27 misses: Arc<RwLock<u64>>,
28}
29
30impl<K, V> LruCache<K, V>
31where
32 K: Eq + Hash + Clone,
33 V: Clone,
34{
35 pub fn new(capacity: usize) -> Self {
37 Self {
38 cache: Arc::new(RwLock::new(HashMap::new())),
39 capacity,
40 hits: Arc::new(RwLock::new(0)),
41 misses: Arc::new(RwLock::new(0)),
42 }
43 }
44
45 pub fn get(&self, key: &K) -> Option<V> {
47 let mut cache = self.cache.write();
48
49 if let Some(entry) = cache.get_mut(key) {
50 entry.access_count += 1;
51 entry.last_accessed = std::time::Instant::now();
52 *self.hits.write() += 1;
53 Some(entry.value.clone())
54 } else {
55 *self.misses.write() += 1;
56 None
57 }
58 }
59
60 pub fn insert(&self, key: K, value: V) {
62 let mut cache = self.cache.write();
63
64 if cache.len() >= self.capacity && !cache.contains_key(&key) {
66 self.evict_lru(&mut cache);
67 }
68
69 cache.insert(
70 key,
71 CacheEntry {
72 value,
73 access_count: 1,
74 last_accessed: std::time::Instant::now(),
75 },
76 );
77 }
78
79 fn evict_lru(&self, cache: &mut HashMap<K, CacheEntry<V>>) {
81 if let Some((key_to_remove, _)) = cache
82 .iter()
83 .min_by_key(|(_, entry)| entry.last_accessed)
84 {
85 let key = key_to_remove.clone();
86 cache.remove(&key);
87 }
88 }
89
90 pub fn remove(&self, key: &K) {
92 let mut cache = self.cache.write();
93 cache.remove(key);
94 }
95
96 pub fn clear(&self) {
98 let mut cache = self.cache.write();
99 cache.clear();
100 *self.hits.write() = 0;
101 *self.misses.write() = 0;
102 }
103
104 pub fn stats(&self) -> CacheStats {
106 let hits = *self.hits.read();
107 let misses = *self.misses.read();
108 let total = hits + misses;
109 let hit_rate = if total > 0 {
110 hits as f64 / total as f64
111 } else {
112 0.0
113 };
114
115 CacheStats {
116 hits,
117 misses,
118 hit_rate,
119 size: self.cache.read().len(),
120 capacity: self.capacity,
121 }
122 }
123
124 pub fn len(&self) -> usize {
126 self.cache.read().len()
127 }
128
129 pub fn is_empty(&self) -> bool {
131 self.cache.read().is_empty()
132 }
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct CacheStats {
138 pub hits: u64,
139 pub misses: u64,
140 pub hit_rate: f64,
141 pub size: usize,
142 pub capacity: usize,
143}
144
145pub struct CacheManager {
147 operation_cache: LruCache<String, Vec<u8>>,
149
150 blob_cache: LruCache<String, Vec<u8>>,
152
153 query_cache: LruCache<String, String>,
155}
156
157impl CacheManager {
158 pub fn new() -> Self {
160 Self {
161 operation_cache: LruCache::new(1000),
162 blob_cache: LruCache::new(100),
163 query_cache: LruCache::new(500),
164 }
165 }
166
167 pub fn with_capacities(op_capacity: usize, blob_capacity: usize, query_capacity: usize) -> Self {
169 Self {
170 operation_cache: LruCache::new(op_capacity),
171 blob_cache: LruCache::new(blob_capacity),
172 query_cache: LruCache::new(query_capacity),
173 }
174 }
175
176 pub fn get_operation(&self, key: &str) -> Option<Vec<u8>> {
178 self.operation_cache.get(&key.to_string())
179 }
180
181 pub fn cache_operation(&self, key: String, data: Vec<u8>) {
183 self.operation_cache.insert(key, data);
184 }
185
186 pub fn get_blob(&self, hash: &str) -> Option<Vec<u8>> {
188 self.blob_cache.get(&hash.to_string())
189 }
190
191 pub fn cache_blob(&self, hash: String, content: Vec<u8>) {
193 self.blob_cache.insert(hash, content);
194 }
195
196 pub fn get_query(&self, query: &str) -> Option<String> {
198 self.query_cache.get(&query.to_string())
199 }
200
201 pub fn cache_query(&self, query: String, result: String) {
203 self.query_cache.insert(query, result);
204 }
205
206 pub fn clear_all(&self) {
208 self.operation_cache.clear();
209 self.blob_cache.clear();
210 self.query_cache.clear();
211 }
212
213 pub fn get_stats(&self) -> CombinedStats {
215 CombinedStats {
216 operation_cache: self.operation_cache.stats(),
217 blob_cache: self.blob_cache.stats(),
218 query_cache: self.query_cache.stats(),
219 }
220 }
221
222 pub fn print_stats(&self) {
224 let stats = self.get_stats();
225
226 println!("\n=== Cache Statistics ===\n");
227
228 println!("Operation Cache:");
229 print_cache_stats(&stats.operation_cache);
230
231 println!("\nBlob Cache:");
232 print_cache_stats(&stats.blob_cache);
233
234 println!("\nQuery Cache:");
235 print_cache_stats(&stats.query_cache);
236
237 let total_hits = stats.operation_cache.hits + stats.blob_cache.hits + stats.query_cache.hits;
238 let total_misses = stats.operation_cache.misses + stats.blob_cache.misses + stats.query_cache.misses;
239 let overall_hit_rate = if total_hits + total_misses > 0 {
240 total_hits as f64 / (total_hits + total_misses) as f64
241 } else {
242 0.0
243 };
244
245 println!("\nOverall Hit Rate: {:.2}%", overall_hit_rate * 100.0);
246 println!();
247 }
248}
249
250impl Default for CacheManager {
251 fn default() -> Self {
252 Self::new()
253 }
254}
255
256fn print_cache_stats(stats: &CacheStats) {
257 println!(" Size: {}/{}", stats.size, stats.capacity);
258 println!(" Hits: {}", stats.hits);
259 println!(" Misses: {}", stats.misses);
260 println!(" Hit Rate: {:.2}%", stats.hit_rate * 100.0);
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize)]
265pub struct CombinedStats {
266 pub operation_cache: CacheStats,
267 pub blob_cache: CacheStats,
268 pub query_cache: CacheStats,
269}
270
271static GLOBAL_CACHE: once_cell::sync::Lazy<CacheManager> =
273 once_cell::sync::Lazy::new(|| CacheManager::new());
274
275pub fn global_cache() -> &'static CacheManager {
277 &GLOBAL_CACHE
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283
284 #[test]
285 fn test_lru_cache() {
286 let cache = LruCache::new(3);
287
288 cache.insert("a", 1);
289 cache.insert("b", 2);
290 cache.insert("c", 3);
291
292 assert_eq!(cache.get(&"a"), Some(1));
293 assert_eq!(cache.get(&"b"), Some(2));
294 assert_eq!(cache.get(&"c"), Some(3));
295
296 cache.insert("d", 4);
298 assert_eq!(cache.len(), 3);
299 }
300
301 #[test]
302 fn test_cache_stats() {
303 let cache = LruCache::new(10);
304
305 cache.insert("a", 1);
306 cache.insert("b", 2);
307
308 let _ = cache.get(&"a"); let _ = cache.get(&"a"); let _ = cache.get(&"c"); let stats = cache.stats();
313 assert_eq!(stats.hits, 2);
314 assert_eq!(stats.misses, 1);
315 assert_eq!(stats.size, 2);
316 }
317
318 #[test]
319 fn test_cache_manager() {
320 let manager = CacheManager::new();
321
322 manager.cache_operation("op1".to_string(), vec![1, 2, 3]);
323 manager.cache_blob("hash1".to_string(), vec![4, 5, 6]);
324 manager.cache_query("query1".to_string(), "result1".to_string());
325
326 assert_eq!(manager.get_operation("op1"), Some(vec![1, 2, 3]));
327 assert_eq!(manager.get_blob("hash1"), Some(vec![4, 5, 6]));
328 assert_eq!(manager.get_query("query1"), Some("result1".to_string()));
329 }
330}