agentic_reality/cache/
lru.rs1use super::metrics::CacheMetrics;
2use std::collections::HashMap;
3use std::hash::Hash;
4use std::time::{Duration, Instant};
5
6struct CacheEntry<V> {
7 value: V,
8 inserted_at: Instant,
9 last_accessed: Instant,
10}
11
12pub struct LruCache<K, V> {
13 store: HashMap<K, CacheEntry<V>>,
14 max_size: usize,
15 ttl: Duration,
16 metrics: CacheMetrics,
17}
18
19impl<K: Eq + Hash + Clone, V: Clone> LruCache<K, V> {
20 pub fn new(max_size: usize, ttl: Duration) -> Self {
21 Self {
22 store: HashMap::with_capacity(max_size),
23 max_size,
24 ttl,
25 metrics: CacheMetrics::new(),
26 }
27 }
28 pub fn get(&mut self, key: &K) -> Option<V> {
29 let now = Instant::now();
30 if let Some(entry) = self.store.get_mut(key) {
31 if now.duration_since(entry.inserted_at) > self.ttl {
32 self.store.remove(key);
33 self.metrics.record_eviction();
34 self.metrics.record_miss();
35 return None;
36 }
37 entry.last_accessed = now;
38 self.metrics.record_hit();
39 return Some(entry.value.clone());
40 }
41 self.metrics.record_miss();
42 None
43 }
44 pub fn insert(&mut self, key: K, value: V) {
45 if self.store.len() >= self.max_size && !self.store.contains_key(&key) {
46 self.evict_lru();
47 }
48 let now = Instant::now();
49 self.store.insert(
50 key,
51 CacheEntry {
52 value,
53 inserted_at: now,
54 last_accessed: now,
55 },
56 );
57 self.metrics.set_size(self.store.len());
58 }
59 pub fn invalidate(&mut self, key: &K) -> bool {
60 let removed = self.store.remove(key).is_some();
61 if removed {
62 self.metrics.record_eviction();
63 self.metrics.set_size(self.store.len());
64 }
65 removed
66 }
67 pub fn clear(&mut self) {
68 self.store.clear();
69 self.metrics.set_size(0);
70 }
71 pub fn contains(&self, key: &K) -> bool {
72 self.store
73 .get(key)
74 .is_some_and(|e| Instant::now().duration_since(e.inserted_at) <= self.ttl)
75 }
76 pub fn len(&self) -> usize {
77 self.store.len()
78 }
79 pub fn is_empty(&self) -> bool {
80 self.store.is_empty()
81 }
82 pub fn metrics(&self) -> &CacheMetrics {
83 &self.metrics
84 }
85 fn evict_lru(&mut self) {
86 if let Some(key) = self
87 .store
88 .iter()
89 .min_by_key(|(_, e)| e.last_accessed)
90 .map(|(k, _)| k.clone())
91 {
92 self.store.remove(&key);
93 self.metrics.record_eviction();
94 }
95 }
96}