oxirs_vec/
advanced_caching_multilevel.rs1use crate::advanced_caching::{CacheConfig, CacheEntry, CacheKey, CacheStats};
10use crate::advanced_caching_eviction::{MemoryCache, PersistentCache};
11use anyhow::Result;
12use std::collections::HashMap;
13use std::sync::{Arc, RwLock};
14use std::time::Duration;
15
16type TagIndex = Arc<RwLock<HashMap<String, HashMap<String, Vec<CacheKey>>>>>;
18
19pub struct MultiLevelCache {
21 pub(super) memory_cache: Arc<RwLock<MemoryCache>>,
22 pub(super) persistent_cache: Option<Arc<PersistentCache>>,
23 #[allow(dead_code)]
24 pub(super) config: CacheConfig,
25 pub(super) stats: Arc<RwLock<MultiLevelCacheStats>>,
26}
27
28#[derive(Debug, Default, Clone)]
29pub struct MultiLevelCacheStats {
30 pub memory_hits: u64,
31 pub memory_misses: u64,
32 pub persistent_hits: u64,
33 pub persistent_misses: u64,
34 pub total_requests: u64,
35}
36
37impl MultiLevelCache {
38 pub fn new(config: CacheConfig) -> Result<Self> {
39 let memory_cache = Arc::new(RwLock::new(MemoryCache::new(config.clone())));
40
41 let persistent_cache = if config.enable_persistent {
42 Some(Arc::new(PersistentCache::new(config.clone())?))
43 } else {
44 None
45 };
46
47 Ok(Self {
48 memory_cache,
49 persistent_cache,
50 config,
51 stats: Arc::new(RwLock::new(MultiLevelCacheStats::default())),
52 })
53 }
54
55 pub fn insert(&self, key: CacheKey, data: crate::Vector) -> Result<()> {
57 let entry = CacheEntry::new(data);
58
59 {
61 let mut memory = self.memory_cache.write().expect("lock poisoned");
62 memory.insert(key.clone(), entry.clone())?;
63 }
64
65 if let Some(ref persistent) = self.persistent_cache {
67 persistent.store(&key, &entry)?;
68 }
69
70 Ok(())
71 }
72
73 pub fn get(&self, key: &CacheKey) -> Option<crate::Vector> {
75 self.update_stats_total();
76
77 {
79 let mut memory = self.memory_cache.write().expect("lock poisoned");
80 if let Some(data) = memory.get(key) {
81 self.update_stats_memory_hit();
82 return Some(data.clone());
83 }
84 }
85
86 self.update_stats_memory_miss();
87
88 if let Some(ref persistent) = self.persistent_cache {
90 if let Ok(Some(mut entry)) = persistent.load(key) {
91 self.update_stats_persistent_hit();
92
93 let data = entry.data.clone();
95 entry.touch();
96 if let Ok(mut memory) = self.memory_cache.write() {
97 let _ = memory.insert(key.clone(), entry);
98 }
99
100 return Some(data);
101 }
102 }
103
104 self.update_stats_persistent_miss();
105 None
106 }
107
108 pub fn remove(&self, key: &CacheKey) -> Result<()> {
110 {
112 let mut memory = self.memory_cache.write().expect("lock poisoned");
113 memory.remove(key);
114 }
115
116 if let Some(ref persistent) = self.persistent_cache {
118 persistent.remove(key)?;
119 }
120
121 Ok(())
122 }
123
124 pub fn clear(&self) -> Result<()> {
126 {
128 let mut memory = self.memory_cache.write().expect("lock poisoned");
129 memory.clear();
130 }
131
132 if let Some(ref persistent) = self.persistent_cache {
134 persistent.clear()?;
135 }
136
137 {
139 let mut stats = self.stats.write().expect("lock poisoned");
140 *stats = MultiLevelCacheStats::default();
141 }
142
143 Ok(())
144 }
145
146 pub fn get_stats(&self) -> MultiLevelCacheStats {
148 self.stats.read().expect("lock poisoned").clone()
149 }
150
151 pub fn get_memory_stats(&self) -> CacheStats {
153 let memory = self.memory_cache.read().expect("lock poisoned");
154 memory.stats()
155 }
156
157 fn update_stats_total(&self) {
159 let mut stats = self.stats.write().expect("lock poisoned");
160 stats.total_requests += 1;
161 }
162
163 fn update_stats_memory_hit(&self) {
164 let mut stats = self.stats.write().expect("lock poisoned");
165 stats.memory_hits += 1;
166 }
167
168 fn update_stats_memory_miss(&self) {
169 let mut stats = self.stats.write().expect("lock poisoned");
170 stats.memory_misses += 1;
171 }
172
173 fn update_stats_persistent_hit(&self) {
174 let mut stats = self.stats.write().expect("lock poisoned");
175 stats.persistent_hits += 1;
176 }
177
178 fn update_stats_persistent_miss(&self) {
179 let mut stats = self.stats.write().expect("lock poisoned");
180 stats.persistent_misses += 1;
181 }
182}
183
184pub struct CacheInvalidator {
190 cache: Arc<MultiLevelCache>,
191 tag_index: TagIndex, namespace_index: Arc<RwLock<HashMap<String, Vec<CacheKey>>>>, }
194
195impl CacheInvalidator {
196 pub fn new(cache: Arc<MultiLevelCache>) -> Self {
197 Self {
198 cache,
199 tag_index: Arc::new(RwLock::new(HashMap::new())),
200 namespace_index: Arc::new(RwLock::new(HashMap::new())),
201 }
202 }
203
204 pub fn register_entry(&self, key: &CacheKey, tags: &HashMap<String, String>) {
206 {
208 let mut ns_index = self.namespace_index.write().expect("lock poisoned");
209 ns_index
210 .entry(key.namespace.clone())
211 .or_default()
212 .push(key.clone());
213 }
214
215 {
217 let mut tag_idx = self.tag_index.write().expect("lock poisoned");
218 for (tag_key, tag_value) in tags {
219 tag_idx
220 .entry(tag_key.clone())
221 .or_default()
222 .entry(tag_value.clone())
223 .or_default()
224 .push(key.clone());
225 }
226 }
227 }
228
229 pub fn unregister_entry(&self, key: &CacheKey) {
231 {
233 let mut ns_index = self.namespace_index.write().expect("lock poisoned");
234 if let Some(keys) = ns_index.get_mut(&key.namespace) {
235 keys.retain(|k| k != key);
236 if keys.is_empty() {
237 ns_index.remove(&key.namespace);
238 }
239 }
240 }
241
242 {
244 let mut tag_idx = self.tag_index.write().expect("lock poisoned");
245 let mut tags_to_remove = Vec::new();
246
247 for (tag_key, tag_values) in tag_idx.iter_mut() {
248 let mut values_to_remove = Vec::new();
249
250 for (tag_value, keys) in tag_values.iter_mut() {
251 keys.retain(|k| k != key);
252 if keys.is_empty() {
253 values_to_remove.push(tag_value.clone());
254 }
255 }
256
257 for value in values_to_remove {
258 tag_values.remove(&value);
259 }
260
261 if tag_values.is_empty() {
262 tags_to_remove.push(tag_key.clone());
263 }
264 }
265
266 for tag in tags_to_remove {
267 tag_idx.remove(&tag);
268 }
269 }
270 }
271
272 pub fn invalidate_by_tag(&self, tag_key: &str, tag_value: &str) -> Result<usize> {
274 let keys_to_invalidate = {
275 let tag_idx = self.tag_index.read().expect("lock poisoned");
276 tag_idx
277 .get(tag_key)
278 .and_then(|values| values.get(tag_value))
279 .cloned()
280 .unwrap_or_default()
281 };
282
283 let mut invalidated_count = 0;
284 for key in &keys_to_invalidate {
285 if self.cache.remove(key).is_ok() {
286 invalidated_count += 1;
287 }
288 self.unregister_entry(key);
289 }
290
291 Ok(invalidated_count)
292 }
293
294 pub fn invalidate_namespace(&self, namespace: &str) -> Result<usize> {
296 let keys_to_invalidate = {
297 let ns_index = self.namespace_index.read().expect("lock poisoned");
298 ns_index.get(namespace).cloned().unwrap_or_default()
299 };
300
301 let mut invalidated_count = 0;
302 for key in &keys_to_invalidate {
303 if self.cache.remove(key).is_ok() {
304 invalidated_count += 1;
305 }
306 self.unregister_entry(key);
307 }
308
309 Ok(invalidated_count)
310 }
311
312 pub fn invalidate_expired(&self) -> Result<usize> {
314 if let Some(ref persistent) = self.cache.persistent_cache {
317 return self.scan_and_remove_expired_files(persistent);
318 }
319 Ok(0)
320 }
321
322 fn scan_and_remove_expired_files(&self, persistent_cache: &PersistentCache) -> Result<usize> {
324 let cache_dir = &persistent_cache.cache_dir;
325 let mut removed_count = 0;
326
327 if !cache_dir.exists() {
328 return Ok(0);
329 }
330
331 for entry in std::fs::read_dir(cache_dir)? {
333 let entry = entry?;
334 if entry.file_type()?.is_dir() {
335 for sub_entry in std::fs::read_dir(entry.path())? {
337 let sub_entry = sub_entry?;
338 if sub_entry.file_type()?.is_file() {
339 if let Some(file_name) = sub_entry.file_name().to_str() {
340 if file_name.ends_with(".cache") {
341 if let Some(cache_key) =
343 persistent_cache.decode_cache_key_from_filename(file_name)
344 {
345 if let Ok(Some(loaded)) = persistent_cache.load(&cache_key) {
347 if loaded.is_expired() {
348 let _ = std::fs::remove_file(sub_entry.path());
349 removed_count += 1;
350 }
351 } else {
352 let _ = std::fs::remove_file(sub_entry.path());
354 removed_count += 1;
355 }
356 } else {
357 if let Ok(metadata) = std::fs::metadata(sub_entry.path()) {
359 if let Ok(modified) = metadata.modified() {
360 let age = modified
361 .elapsed()
362 .unwrap_or(Duration::from_secs(0));
363 if age > Duration::from_secs(24 * 3600) {
365 let _ = std::fs::remove_file(sub_entry.path());
366 removed_count += 1;
367 }
368 }
369 }
370 }
371 }
372 }
373 }
374 }
375 }
376 }
377
378 Ok(removed_count)
379 }
380
381 pub fn get_stats(&self) -> InvalidationStats {
383 let tag_idx = self.tag_index.read().expect("lock poisoned");
384 let ns_index = self.namespace_index.read().expect("lock poisoned");
385
386 let total_tag_entries = tag_idx
387 .values()
388 .flat_map(|values| values.values())
389 .map(|keys| keys.len())
390 .sum();
391
392 let total_namespace_entries = ns_index.values().map(|keys| keys.len()).sum();
393
394 InvalidationStats {
395 tracked_tags: tag_idx.len(),
396 tracked_namespaces: ns_index.len(),
397 total_tag_entries,
398 total_namespace_entries,
399 }
400 }
401}
402
403#[derive(Debug, Clone)]
405pub struct InvalidationStats {
406 pub tracked_tags: usize,
407 pub tracked_namespaces: usize,
408 pub total_tag_entries: usize,
409 pub total_namespace_entries: usize,
410}