1use ahash::AHashMap;
4use parking_lot::RwLock;
5use serde::{Deserialize, Serialize};
6use std::sync::Arc;
7use std::time::Instant;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct CacheStats {
12 pub entries: usize,
13 pub size_bytes: usize,
14 pub hits: u64,
15 pub misses: u64,
16 pub evictions: u64,
17 pub hit_rate: f64,
18}
19
20#[derive(Debug, Clone)]
22struct CacheEntry {
23 value: serde_json::Value,
24 size: usize,
25 created_at: Instant,
26 last_accessed: Instant,
27 access_count: u64,
28}
29
30pub struct Cache {
32 entries: AHashMap<String, CacheEntry>,
33 max_size: usize,
34 current_size: usize,
35 hits: u64,
36 misses: u64,
37 evictions: u64,
38}
39
40impl Cache {
41 pub fn new() -> Self {
43 Self::with_capacity(100 * 1024 * 1024)
44 }
45
46 pub fn with_capacity(max_size: usize) -> Self {
48 Self {
49 entries: AHashMap::new(),
50 max_size,
51 current_size: 0,
52 hits: 0,
53 misses: 0,
54 evictions: 0,
55 }
56 }
57
58 pub fn get(&mut self, key: &str) -> Option<serde_json::Value> {
60 if let Some(entry) = self.entries.get_mut(key) {
61 entry.last_accessed = Instant::now();
62 entry.access_count += 1;
63 self.hits += 1;
64 Some(entry.value.clone())
65 } else {
66 self.misses += 1;
67 None
68 }
69 }
70
71 pub fn put(&mut self, key: String, value: serde_json::Value) {
73 let size = estimate_size(&value);
74
75 while self.current_size + size > self.max_size && !self.entries.is_empty() {
77 self.evict_lru();
78 }
79
80 let entry = CacheEntry {
81 value,
82 size,
83 created_at: Instant::now(),
84 last_accessed: Instant::now(),
85 access_count: 1,
86 };
87
88 if let Some(old_entry) = self.entries.remove(&key) {
90 self.current_size -= old_entry.size;
91 }
92
93 self.current_size += size;
94 self.entries.insert(key, entry);
95 }
96
97 pub fn clear(&mut self) {
99 self.entries.clear();
100 self.current_size = 0;
101 self.hits = 0;
102 self.misses = 0;
103 self.evictions = 0;
104 }
105
106 pub fn stats(&self) -> CacheStats {
108 let total = self.hits + self.misses;
109 let hit_rate = if total > 0 {
110 self.hits as f64 / total as f64
111 } else {
112 0.0
113 };
114
115 CacheStats {
116 entries: self.entries.len(),
117 size_bytes: self.current_size,
118 hits: self.hits,
119 misses: self.misses,
120 evictions: self.evictions,
121 hit_rate,
122 }
123 }
124
125 fn evict_lru(&mut self) {
127 if let Some((key, _)) = self
128 .entries
129 .iter()
130 .min_by_key(|(_, entry)| entry.last_accessed)
131 .map(|(k, v)| (k.clone(), v.clone()))
132 {
133 if let Some(entry) = self.entries.remove(&key) {
134 self.current_size -= entry.size;
135 self.evictions += 1;
136 }
137 }
138 }
139}
140
141impl Default for Cache {
142 fn default() -> Self {
143 Self::new()
144 }
145}
146
147fn estimate_size(value: &serde_json::Value) -> usize {
149 match value {
150 serde_json::Value::Null => 8,
151 serde_json::Value::Bool(_) => 8,
152 serde_json::Value::Number(_) => 16,
153 serde_json::Value::String(s) => 24 + s.len() * 2,
154 serde_json::Value::Array(arr) => {
155 24 + arr.iter().map(estimate_size).sum::<usize>()
156 }
157 serde_json::Value::Object(map) => {
158 24 + map
159 .iter()
160 .map(|(k, v)| 24 + k.len() * 2 + estimate_size(v))
161 .sum::<usize>()
162 }
163 }
164}
165
166pub struct ThreadSafeCache {
168 inner: Arc<RwLock<Cache>>,
169}
170
171impl ThreadSafeCache {
172 pub fn new() -> Self {
173 Self {
174 inner: Arc::new(RwLock::new(Cache::new())),
175 }
176 }
177
178 pub fn get(&self, key: &str) -> Option<serde_json::Value> {
179 self.inner.write().get(key)
180 }
181
182 pub fn put(&self, key: String, value: serde_json::Value) {
183 self.inner.write().put(key, value);
184 }
185
186 pub fn stats(&self) -> CacheStats {
187 self.inner.read().stats()
188 }
189
190 pub fn clear(&self) {
191 self.inner.write().clear();
192 }
193}
194
195impl Default for ThreadSafeCache {
196 fn default() -> Self {
197 Self::new()
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 fn test_cache_basic_operations() {
207 let mut cache = Cache::with_capacity(1024);
208
209 cache.put("key1".to_string(), serde_json::json!({"test": "value"}));
211 assert!(cache.get("key1").is_some());
212 assert!(cache.get("key2").is_none());
213
214 let stats = cache.stats();
216 assert_eq!(stats.entries, 1);
217 assert_eq!(stats.hits, 1);
218 assert_eq!(stats.misses, 1);
219 }
220
221 #[test]
222 fn test_cache_eviction() {
223 let mut cache = Cache::with_capacity(100); for i in 0..10 {
227 cache.put(
228 format!("key{}", i),
229 serde_json::json!({"data": format!("value{}", i)}),
230 );
231 }
232
233 assert!(cache.entries.len() < 10);
235 assert!(cache.evictions > 0);
236 }
237}