1use crate::Vector;
11use anyhow::{anyhow, Result};
12use serde::{Deserialize, Serialize};
13use std::collections::{HashMap, VecDeque};
14use std::fmt;
15use std::hash::{Hash, Hasher};
16use std::sync::{Arc, RwLock};
17use std::thread::{self, JoinHandle};
18use std::time::{Duration, Instant};
19
20type TagIndex = Arc<RwLock<HashMap<String, HashMap<String, Vec<CacheKey>>>>>;
22
23#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
25pub enum EvictionPolicy {
26 LRU,
28 LFU,
30 ARC,
32 FIFO,
34 TTL,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct CacheConfig {
41 pub max_memory_entries: usize,
43 pub max_memory_bytes: usize,
45 pub ttl: Option<Duration>,
47 pub eviction_policy: EvictionPolicy,
49 pub enable_persistent: bool,
51 pub persistent_cache_dir: Option<std::path::PathBuf>,
53 pub max_persistent_bytes: usize,
55 pub enable_compression: bool,
57 pub enable_background_updates: bool,
59 pub background_update_interval: Duration,
61}
62
63impl Default for CacheConfig {
64 fn default() -> Self {
65 Self {
66 max_memory_entries: 10000,
67 max_memory_bytes: 1024 * 1024 * 100, ttl: Some(Duration::from_secs(3600)), eviction_policy: EvictionPolicy::LRU,
70 enable_persistent: true,
71 persistent_cache_dir: None,
72 max_persistent_bytes: 1024 * 1024 * 1024, enable_compression: true,
74 enable_background_updates: false,
75 background_update_interval: Duration::from_secs(300), }
77 }
78}
79
80#[derive(Debug, Clone)]
82pub struct CacheEntry {
83 pub data: Vector,
85 pub created_at: Instant,
87 pub last_accessed: Instant,
89 pub access_count: u64,
91 pub size_bytes: usize,
93 pub ttl: Option<Duration>,
95 pub tags: HashMap<String, String>,
97}
98
99impl CacheEntry {
100 pub fn new(data: Vector) -> Self {
101 let now = Instant::now();
102 let size_bytes = data.dimensions * std::mem::size_of::<f32>() + 64; Self {
105 data,
106 created_at: now,
107 last_accessed: now,
108 access_count: 1,
109 size_bytes,
110 ttl: None,
111 tags: HashMap::new(),
112 }
113 }
114
115 pub fn with_ttl(mut self, ttl: Duration) -> Self {
116 self.ttl = Some(ttl);
117 self
118 }
119
120 pub fn with_tags(mut self, tags: HashMap<String, String>) -> Self {
121 self.tags = tags;
122 self
123 }
124
125 pub fn is_expired(&self) -> bool {
127 if let Some(ttl) = self.ttl {
128 self.created_at.elapsed() > ttl
129 } else {
130 false
131 }
132 }
133
134 pub fn touch(&mut self) {
136 self.last_accessed = Instant::now();
137 self.access_count += 1;
138 }
139}
140
141#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
143pub struct CacheKey {
144 pub namespace: String,
145 pub key: String,
146 pub variant: Option<String>,
147}
148
149impl CacheKey {
150 pub fn new(namespace: impl Into<String>, key: impl Into<String>) -> Self {
151 Self {
152 namespace: namespace.into(),
153 key: key.into(),
154 variant: None,
155 }
156 }
157
158 pub fn with_variant(mut self, variant: impl Into<String>) -> Self {
159 self.variant = Some(variant.into());
160 self
161 }
162}
163
164impl fmt::Display for CacheKey {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 if let Some(ref variant) = self.variant {
167 write!(f, "{}:{}:{}", self.namespace, self.key, variant)
168 } else {
169 write!(f, "{}:{}", self.namespace, self.key)
170 }
171 }
172}
173
174pub struct MemoryCache {
176 config: CacheConfig,
177 entries: HashMap<CacheKey, CacheEntry>,
178 access_order: VecDeque<CacheKey>, frequency_map: HashMap<CacheKey, u64>, current_memory_bytes: usize,
181 arc_t1: VecDeque<CacheKey>, arc_t2: VecDeque<CacheKey>, arc_b1: VecDeque<CacheKey>, arc_b2: VecDeque<CacheKey>, arc_p: usize, }
188
189impl MemoryCache {
190 pub fn new(config: CacheConfig) -> Self {
191 Self {
192 config,
193 entries: HashMap::new(),
194 access_order: VecDeque::new(),
195 frequency_map: HashMap::new(),
196 current_memory_bytes: 0,
197 arc_t1: VecDeque::new(),
198 arc_t2: VecDeque::new(),
199 arc_b1: VecDeque::new(),
200 arc_b2: VecDeque::new(),
201 arc_p: 0,
202 }
203 }
204
205 pub fn insert(&mut self, key: CacheKey, entry: CacheEntry) -> Result<()> {
207 self.clean_expired();
209
210 while self.should_evict(&entry) {
212 self.evict_one()?;
213 }
214
215 if let Some(old_entry) = self.entries.remove(&key) {
217 self.current_memory_bytes -= old_entry.size_bytes;
218 self.remove_from_tracking(&key);
219 }
220
221 self.current_memory_bytes += entry.size_bytes;
223 self.entries.insert(key.clone(), entry);
224 self.track_access(&key);
225
226 Ok(())
227 }
228
229 pub fn get(&mut self, key: &CacheKey) -> Option<Vector> {
231 let should_remove = if let Some(entry) = self.entries.get(key) {
233 entry.is_expired()
234 } else {
235 false
236 };
237
238 if should_remove {
239 self.remove(key);
240 return None;
241 }
242
243 if let Some(entry) = self.entries.get_mut(key) {
244 let data = entry.data.clone();
245 entry.touch();
246 self.track_access(key);
247 Some(data)
248 } else {
249 None
250 }
251 }
252
253 pub fn remove(&mut self, key: &CacheKey) -> Option<CacheEntry> {
255 if let Some(entry) = self.entries.remove(key) {
256 self.current_memory_bytes -= entry.size_bytes;
257 self.remove_from_tracking(key);
258 Some(entry)
259 } else {
260 None
261 }
262 }
263
264 pub fn clear(&mut self) {
266 self.entries.clear();
267 self.access_order.clear();
268 self.frequency_map.clear();
269 self.current_memory_bytes = 0;
270 }
271
272 fn should_evict(&self, new_entry: &CacheEntry) -> bool {
274 self.entries.len() >= self.config.max_memory_entries
275 || self.current_memory_bytes + new_entry.size_bytes > self.config.max_memory_bytes
276 }
277
278 fn evict_one(&mut self) -> Result<()> {
280 let key_to_evict = match self.config.eviction_policy {
281 EvictionPolicy::LRU => self.find_lru_key(),
282 EvictionPolicy::LFU => self.find_lfu_key(),
283 EvictionPolicy::ARC => self.find_arc_key(),
284 EvictionPolicy::FIFO => self.find_fifo_key(),
285 EvictionPolicy::TTL => self.find_expired_key(),
286 };
287
288 if let Some(key) = key_to_evict {
289 self.remove(&key);
290 Ok(())
291 } else if !self.entries.is_empty() {
292 let key = self.entries.keys().next().unwrap().clone();
294 self.remove(&key);
295 Ok(())
296 } else {
297 Err(anyhow!("No entries to evict"))
298 }
299 }
300
301 fn find_lru_key(&self) -> Option<CacheKey> {
303 self.access_order.front().cloned()
304 }
305
306 fn find_lfu_key(&self) -> Option<CacheKey> {
308 self.frequency_map
309 .iter()
310 .min_by_key(|&(_, &freq)| freq)
311 .map(|(key, _)| key.clone())
312 }
313
314 fn find_arc_key(&mut self) -> Option<CacheKey> {
316 let c = self.config.max_memory_entries;
317
318 if !self.arc_t1.is_empty()
320 && (self.arc_t1.len() > self.arc_p
321 || (self.arc_t2.is_empty() && self.arc_t1.len() == self.arc_p))
322 {
323 if let Some(key) = self.arc_t1.pop_front() {
324 self.arc_b1.push_back(key.clone());
326 if self.arc_b1.len() > c {
327 self.arc_b1.pop_front();
328 }
329 return Some(key);
330 }
331 }
332
333 if let Some(key) = self.arc_t2.pop_front() {
335 self.arc_b2.push_back(key.clone());
337 if self.arc_b2.len() > c {
338 self.arc_b2.pop_front();
339 }
340 return Some(key);
341 }
342
343 self.find_lru_key()
345 }
346
347 fn find_fifo_key(&self) -> Option<CacheKey> {
349 self.entries
350 .iter()
351 .min_by_key(|(_, entry)| entry.created_at)
352 .map(|(key, _)| key.clone())
353 }
354
355 fn find_expired_key(&self) -> Option<CacheKey> {
357 self.entries
358 .iter()
359 .find(|(_, entry)| entry.is_expired())
360 .map(|(key, _)| key.clone())
361 }
362
363 fn track_access(&mut self, key: &CacheKey) {
365 if let Some(pos) = self.access_order.iter().position(|k| k == key) {
367 self.access_order.remove(pos);
368 }
369 self.access_order.push_back(key.clone());
370
371 *self.frequency_map.entry(key.clone()).or_insert(0) += 1;
373
374 if self.config.eviction_policy == EvictionPolicy::ARC {
376 self.track_arc_access(key);
377 }
378 }
379
380 fn track_arc_access(&mut self, key: &CacheKey) {
382 let c = self.config.max_memory_entries;
383
384 if let Some(pos) = self.arc_t1.iter().position(|k| k == key) {
386 self.arc_t1.remove(pos);
388 self.arc_t2.push_back(key.clone());
389 } else if let Some(pos) = self.arc_t2.iter().position(|k| k == key) {
390 self.arc_t2.remove(pos);
392 self.arc_t2.push_back(key.clone());
393 } else if let Some(pos) = self.arc_b1.iter().position(|k| k == key) {
394 self.arc_b1.remove(pos);
396 self.arc_p = (self.arc_p + 1.max(self.arc_b2.len() / self.arc_b1.len())).min(c);
397 self.arc_t2.push_back(key.clone());
398 } else if let Some(pos) = self.arc_b2.iter().position(|k| k == key) {
399 self.arc_b2.remove(pos);
401 self.arc_p = self
402 .arc_p
403 .saturating_sub(1.max(self.arc_b1.len() / self.arc_b2.len()));
404 self.arc_t2.push_back(key.clone());
405 } else {
406 self.arc_t1.push_back(key.clone());
408 }
409 }
410
411 fn remove_from_tracking(&mut self, key: &CacheKey) {
413 if let Some(pos) = self.access_order.iter().position(|k| k == key) {
414 self.access_order.remove(pos);
415 }
416 self.frequency_map.remove(key);
417
418 if self.config.eviction_policy == EvictionPolicy::ARC {
420 if let Some(pos) = self.arc_t1.iter().position(|k| k == key) {
421 self.arc_t1.remove(pos);
422 }
423 if let Some(pos) = self.arc_t2.iter().position(|k| k == key) {
424 self.arc_t2.remove(pos);
425 }
426 if let Some(pos) = self.arc_b1.iter().position(|k| k == key) {
427 self.arc_b1.remove(pos);
428 }
429 if let Some(pos) = self.arc_b2.iter().position(|k| k == key) {
430 self.arc_b2.remove(pos);
431 }
432 }
433 }
434
435 fn clean_expired(&mut self) {
437 let expired_keys: Vec<CacheKey> = self
438 .entries
439 .iter()
440 .filter(|(_, entry)| entry.is_expired())
441 .map(|(key, _)| key.clone())
442 .collect();
443
444 for key in expired_keys {
445 self.remove(&key);
446 }
447 }
448
449 pub fn stats(&self) -> CacheStats {
451 CacheStats {
452 entries: self.entries.len(),
453 memory_bytes: self.current_memory_bytes,
454 max_entries: self.config.max_memory_entries,
455 max_memory_bytes: self.config.max_memory_bytes,
456 hit_ratio: 0.0, }
458 }
459}
460
461#[derive(Debug, Clone, Serialize, Deserialize)]
463pub struct CacheStats {
464 pub entries: usize,
465 pub memory_bytes: usize,
466 pub max_entries: usize,
467 pub max_memory_bytes: usize,
468 pub hit_ratio: f32,
469}
470
471pub struct PersistentCache {
473 config: CacheConfig,
474 cache_dir: std::path::PathBuf,
475}
476
477impl PersistentCache {
478 pub fn new(config: CacheConfig) -> Result<Self> {
479 let cache_dir = config
480 .persistent_cache_dir
481 .clone()
482 .unwrap_or_else(|| std::env::temp_dir().join("oxirs_vec_cache"));
483
484 std::fs::create_dir_all(&cache_dir)?;
485
486 Ok(Self { config, cache_dir })
487 }
488
489 pub fn store(&self, key: &CacheKey, entry: &CacheEntry) -> Result<()> {
491 let file_path = self.get_file_path(key);
492
493 if let Some(parent) = file_path.parent() {
494 std::fs::create_dir_all(parent)?;
495 }
496
497 let serialized = self.serialize_entry(entry)?;
498 let final_data = if self.config.enable_compression {
499 self.compress_data(&serialized)?
500 } else {
501 serialized
502 };
503
504 std::fs::write(file_path, final_data)?;
505 Ok(())
506 }
507
508 pub fn load(&self, key: &CacheKey) -> Result<Option<CacheEntry>> {
510 let file_path = self.get_file_path(key);
511
512 if !file_path.exists() {
513 return Ok(None);
514 }
515
516 let data = std::fs::read(&file_path)?;
517
518 let decompressed = if self.config.enable_compression {
519 self.decompress_data(&data)?
520 } else {
521 data
522 };
523
524 let entry = self.deserialize_entry(&decompressed)?;
525
526 if entry.is_expired() {
528 let _ = std::fs::remove_file(file_path);
530 Ok(None)
531 } else {
532 Ok(Some(entry))
533 }
534 }
535
536 pub fn remove(&self, key: &CacheKey) -> Result<()> {
538 let file_path = self.get_file_path(key);
539 if file_path.exists() {
540 std::fs::remove_file(file_path)?;
541 }
542 Ok(())
543 }
544
545 pub fn clear(&self) -> Result<()> {
547 if self.cache_dir.exists() {
548 std::fs::remove_dir_all(&self.cache_dir)?;
549 std::fs::create_dir_all(&self.cache_dir)?;
550 }
551 Ok(())
552 }
553
554 fn get_file_path(&self, key: &CacheKey) -> std::path::PathBuf {
556 let key_str = key.to_string();
557 let hash = self.hash_key(&key_str);
558
559 let sub_dir = format!("{:02x}", (hash % 256) as u8);
561
562 let encoded_key = self.encode_cache_key_for_filename(key);
564 let filename = format!("{hash:016x}_{encoded_key}.cache");
565
566 self.cache_dir.join(sub_dir).join(filename)
567 }
568
569 fn encode_cache_key_for_filename(&self, key: &CacheKey) -> String {
571 let key_data = serde_json::json!({
572 "namespace": key.namespace,
573 "key": key.key,
574 "variant": key.variant
575 });
576
577 use base64::{engine::general_purpose, Engine as _};
579 general_purpose::URL_SAFE_NO_PAD.encode(key_data.to_string().as_bytes())
580 }
581
582 fn decode_cache_key_from_filename(&self, filename: &str) -> Option<CacheKey> {
584 if let Some(encoded_part) = filename
585 .strip_suffix(".cache")
586 .and_then(|s| s.split('_').nth(1))
587 {
588 use base64::{engine::general_purpose, Engine as _};
589 if let Ok(decoded_bytes) = general_purpose::URL_SAFE_NO_PAD.decode(encoded_part) {
590 if let Ok(decoded_str) = String::from_utf8(decoded_bytes) {
591 if let Ok(key_data) = serde_json::from_str::<serde_json::Value>(&decoded_str) {
592 return Some(CacheKey {
593 namespace: key_data["namespace"].as_str()?.to_string(),
594 key: key_data["key"].as_str()?.to_string(),
595 variant: key_data["variant"].as_str().map(|s| s.to_string()),
596 });
597 }
598 }
599 }
600 }
601 None
602 }
603
604 fn hash_key(&self, key: &str) -> u64 {
606 let mut hasher = std::collections::hash_map::DefaultHasher::new();
607 key.hash(&mut hasher);
608 hasher.finish()
609 }
610
611 fn serialize_entry(&self, entry: &CacheEntry) -> Result<Vec<u8>> {
613 let mut data = Vec::new();
615
616 let vector_data = &entry.data.as_f32();
618 data.extend_from_slice(&(vector_data.len() as u32).to_le_bytes());
619 for &value in vector_data {
620 data.extend_from_slice(&value.to_le_bytes());
621 }
622
623 let created_nanos = entry.created_at.elapsed().as_nanos() as u64;
625 let accessed_nanos = entry.last_accessed.elapsed().as_nanos() as u64;
626 data.extend_from_slice(&created_nanos.to_le_bytes());
627 data.extend_from_slice(&accessed_nanos.to_le_bytes());
628
629 data.extend_from_slice(&entry.access_count.to_le_bytes());
631 data.extend_from_slice(&(entry.size_bytes as u64).to_le_bytes());
632
633 if let Some(ttl) = entry.ttl {
635 data.push(1); data.extend_from_slice(&ttl.as_nanos().to_le_bytes());
637 } else {
638 data.push(0); }
640
641 data.extend_from_slice(&(entry.tags.len() as u32).to_le_bytes());
643 for (key, value) in &entry.tags {
644 data.extend_from_slice(&(key.len() as u32).to_le_bytes());
645 data.extend_from_slice(key.as_bytes());
646 data.extend_from_slice(&(value.len() as u32).to_le_bytes());
647 data.extend_from_slice(value.as_bytes());
648 }
649
650 Ok(data)
651 }
652
653 fn deserialize_entry(&self, data: &[u8]) -> Result<CacheEntry> {
655 let mut offset = 0;
656
657 let vector_len = u32::from_le_bytes([
659 data[offset],
660 data[offset + 1],
661 data[offset + 2],
662 data[offset + 3],
663 ]) as usize;
664 offset += 4;
665
666 let mut vector_data = Vec::with_capacity(vector_len);
667 for _ in 0..vector_len {
668 let value = f32::from_le_bytes([
669 data[offset],
670 data[offset + 1],
671 data[offset + 2],
672 data[offset + 3],
673 ]);
674 vector_data.push(value);
675 offset += 4;
676 }
677 let vector = Vector::new(vector_data);
678
679 let created_nanos = u64::from_le_bytes([
681 data[offset],
682 data[offset + 1],
683 data[offset + 2],
684 data[offset + 3],
685 data[offset + 4],
686 data[offset + 5],
687 data[offset + 6],
688 data[offset + 7],
689 ]);
690 offset += 8;
691
692 let accessed_nanos = u64::from_le_bytes([
693 data[offset],
694 data[offset + 1],
695 data[offset + 2],
696 data[offset + 3],
697 data[offset + 4],
698 data[offset + 5],
699 data[offset + 6],
700 data[offset + 7],
701 ]);
702 offset += 8;
703
704 let now = Instant::now();
706 let created_at = now - Duration::from_nanos(created_nanos);
707 let last_accessed = now - Duration::from_nanos(accessed_nanos);
708
709 let access_count = u64::from_le_bytes([
711 data[offset],
712 data[offset + 1],
713 data[offset + 2],
714 data[offset + 3],
715 data[offset + 4],
716 data[offset + 5],
717 data[offset + 6],
718 data[offset + 7],
719 ]);
720 offset += 8;
721
722 let size_bytes = u64::from_le_bytes([
723 data[offset],
724 data[offset + 1],
725 data[offset + 2],
726 data[offset + 3],
727 data[offset + 4],
728 data[offset + 5],
729 data[offset + 6],
730 data[offset + 7],
731 ]) as usize;
732 offset += 8;
733
734 let ttl = if data[offset] == 1 {
736 offset += 1;
737 let ttl_nanos = u128::from_le_bytes([
738 data[offset],
739 data[offset + 1],
740 data[offset + 2],
741 data[offset + 3],
742 data[offset + 4],
743 data[offset + 5],
744 data[offset + 6],
745 data[offset + 7],
746 data[offset + 8],
747 data[offset + 9],
748 data[offset + 10],
749 data[offset + 11],
750 data[offset + 12],
751 data[offset + 13],
752 data[offset + 14],
753 data[offset + 15],
754 ]);
755 offset += 16;
756 Some(Duration::from_nanos(ttl_nanos as u64))
757 } else {
758 offset += 1;
759 None
760 };
761
762 let tags_len = u32::from_le_bytes([
764 data[offset],
765 data[offset + 1],
766 data[offset + 2],
767 data[offset + 3],
768 ]) as usize;
769 offset += 4;
770
771 let mut tags = HashMap::new();
772 for _ in 0..tags_len {
773 let key_len = u32::from_le_bytes([
774 data[offset],
775 data[offset + 1],
776 data[offset + 2],
777 data[offset + 3],
778 ]) as usize;
779 offset += 4;
780 let key = String::from_utf8(data[offset..offset + key_len].to_vec())?;
781 offset += key_len;
782
783 let value_len = u32::from_le_bytes([
784 data[offset],
785 data[offset + 1],
786 data[offset + 2],
787 data[offset + 3],
788 ]) as usize;
789 offset += 4;
790 let value = String::from_utf8(data[offset..offset + value_len].to_vec())?;
791 offset += value_len;
792
793 tags.insert(key, value);
794 }
795
796 Ok(CacheEntry {
797 data: vector,
798 created_at,
799 last_accessed,
800 access_count,
801 size_bytes,
802 ttl,
803 tags,
804 })
805 }
806
807 fn compress_data(&self, data: &[u8]) -> Result<Vec<u8>> {
809 let mut compressed = Vec::new();
811
812 if data.is_empty() {
813 return Ok(compressed);
814 }
815
816 let mut current_byte = data[0];
817 let mut count = 1u8;
818
819 for &byte in &data[1..] {
820 if byte == current_byte && count < 255 {
821 count += 1;
822 } else {
823 compressed.push(count);
824 compressed.push(current_byte);
825 current_byte = byte;
826 count = 1;
827 }
828 }
829
830 compressed.push(count);
832 compressed.push(current_byte);
833
834 Ok(compressed)
835 }
836
837 fn decompress_data(&self, data: &[u8]) -> Result<Vec<u8>> {
839 let mut decompressed = Vec::new();
840
841 if data.len() % 2 != 0 {
842 return Err(anyhow!("Invalid compressed data length"));
843 }
844
845 for chunk in data.chunks(2) {
846 let count = chunk[0];
847 let byte = chunk[1];
848
849 for _ in 0..count {
850 decompressed.push(byte);
851 }
852 }
853
854 Ok(decompressed)
855 }
856}
857
858pub struct MultiLevelCache {
860 memory_cache: Arc<RwLock<MemoryCache>>,
861 persistent_cache: Option<Arc<PersistentCache>>,
862 #[allow(dead_code)]
863 config: CacheConfig,
864 stats: Arc<RwLock<MultiLevelCacheStats>>,
865}
866
867#[derive(Debug, Default, Clone)]
868pub struct MultiLevelCacheStats {
869 pub memory_hits: u64,
870 pub memory_misses: u64,
871 pub persistent_hits: u64,
872 pub persistent_misses: u64,
873 pub total_requests: u64,
874}
875
876impl MultiLevelCache {
877 pub fn new(config: CacheConfig) -> Result<Self> {
878 let memory_cache = Arc::new(RwLock::new(MemoryCache::new(config.clone())));
879
880 let persistent_cache = if config.enable_persistent {
881 Some(Arc::new(PersistentCache::new(config.clone())?))
882 } else {
883 None
884 };
885
886 Ok(Self {
887 memory_cache,
888 persistent_cache,
889 config,
890 stats: Arc::new(RwLock::new(MultiLevelCacheStats::default())),
891 })
892 }
893
894 pub fn insert(&self, key: CacheKey, data: Vector) -> Result<()> {
896 let entry = CacheEntry::new(data);
897
898 {
900 let mut memory = self.memory_cache.write().unwrap();
901 memory.insert(key.clone(), entry.clone())?;
902 }
903
904 if let Some(ref persistent) = self.persistent_cache {
906 persistent.store(&key, &entry)?;
907 }
908
909 Ok(())
910 }
911
912 pub fn get(&self, key: &CacheKey) -> Option<Vector> {
914 self.update_stats_total();
915
916 {
918 let mut memory = self.memory_cache.write().unwrap();
919 if let Some(data) = memory.get(key) {
920 self.update_stats_memory_hit();
921 return Some(data.clone());
922 }
923 }
924
925 self.update_stats_memory_miss();
926
927 if let Some(ref persistent) = self.persistent_cache {
929 if let Ok(Some(mut entry)) = persistent.load(key) {
930 self.update_stats_persistent_hit();
931
932 let data = entry.data.clone();
934 entry.touch();
935 if let Ok(mut memory) = self.memory_cache.write() {
936 let _ = memory.insert(key.clone(), entry);
937 }
938
939 return Some(data);
940 }
941 }
942
943 self.update_stats_persistent_miss();
944 None
945 }
946
947 pub fn remove(&self, key: &CacheKey) -> Result<()> {
949 {
951 let mut memory = self.memory_cache.write().unwrap();
952 memory.remove(key);
953 }
954
955 if let Some(ref persistent) = self.persistent_cache {
957 persistent.remove(key)?;
958 }
959
960 Ok(())
961 }
962
963 pub fn clear(&self) -> Result<()> {
965 {
967 let mut memory = self.memory_cache.write().unwrap();
968 memory.clear();
969 }
970
971 if let Some(ref persistent) = self.persistent_cache {
973 persistent.clear()?;
974 }
975
976 {
978 let mut stats = self.stats.write().unwrap();
979 *stats = MultiLevelCacheStats::default();
980 }
981
982 Ok(())
983 }
984
985 pub fn get_stats(&self) -> MultiLevelCacheStats {
987 self.stats.read().unwrap().clone()
988 }
989
990 pub fn get_memory_stats(&self) -> CacheStats {
992 let memory = self.memory_cache.read().unwrap();
993 memory.stats()
994 }
995
996 fn update_stats_total(&self) {
998 let mut stats = self.stats.write().unwrap();
999 stats.total_requests += 1;
1000 }
1001
1002 fn update_stats_memory_hit(&self) {
1003 let mut stats = self.stats.write().unwrap();
1004 stats.memory_hits += 1;
1005 }
1006
1007 fn update_stats_memory_miss(&self) {
1008 let mut stats = self.stats.write().unwrap();
1009 stats.memory_misses += 1;
1010 }
1011
1012 fn update_stats_persistent_hit(&self) {
1013 let mut stats = self.stats.write().unwrap();
1014 stats.persistent_hits += 1;
1015 }
1016
1017 fn update_stats_persistent_miss(&self) {
1018 let mut stats = self.stats.write().unwrap();
1019 stats.persistent_misses += 1;
1020 }
1021}
1022
1023pub struct CacheInvalidator {
1025 cache: Arc<MultiLevelCache>,
1026 tag_index: TagIndex, namespace_index: Arc<RwLock<HashMap<String, Vec<CacheKey>>>>, }
1029
1030impl CacheInvalidator {
1031 pub fn new(cache: Arc<MultiLevelCache>) -> Self {
1032 Self {
1033 cache,
1034 tag_index: Arc::new(RwLock::new(HashMap::new())),
1035 namespace_index: Arc::new(RwLock::new(HashMap::new())),
1036 }
1037 }
1038
1039 pub fn register_entry(&self, key: &CacheKey, tags: &HashMap<String, String>) {
1041 {
1043 let mut ns_index = self.namespace_index.write().unwrap();
1044 ns_index
1045 .entry(key.namespace.clone())
1046 .or_default()
1047 .push(key.clone());
1048 }
1049
1050 {
1052 let mut tag_idx = self.tag_index.write().unwrap();
1053 for (tag_key, tag_value) in tags {
1054 tag_idx
1055 .entry(tag_key.clone())
1056 .or_default()
1057 .entry(tag_value.clone())
1058 .or_default()
1059 .push(key.clone());
1060 }
1061 }
1062 }
1063
1064 pub fn unregister_entry(&self, key: &CacheKey) {
1066 {
1068 let mut ns_index = self.namespace_index.write().unwrap();
1069 if let Some(keys) = ns_index.get_mut(&key.namespace) {
1070 keys.retain(|k| k != key);
1071 if keys.is_empty() {
1072 ns_index.remove(&key.namespace);
1073 }
1074 }
1075 }
1076
1077 {
1079 let mut tag_idx = self.tag_index.write().unwrap();
1080 let mut tags_to_remove = Vec::new();
1081
1082 for (tag_key, tag_values) in tag_idx.iter_mut() {
1083 let mut values_to_remove = Vec::new();
1084
1085 for (tag_value, keys) in tag_values.iter_mut() {
1086 keys.retain(|k| k != key);
1087 if keys.is_empty() {
1088 values_to_remove.push(tag_value.clone());
1089 }
1090 }
1091
1092 for value in values_to_remove {
1093 tag_values.remove(&value);
1094 }
1095
1096 if tag_values.is_empty() {
1097 tags_to_remove.push(tag_key.clone());
1098 }
1099 }
1100
1101 for tag in tags_to_remove {
1102 tag_idx.remove(&tag);
1103 }
1104 }
1105 }
1106
1107 pub fn invalidate_by_tag(&self, tag_key: &str, tag_value: &str) -> Result<usize> {
1109 let keys_to_invalidate = {
1110 let tag_idx = self.tag_index.read().unwrap();
1111 tag_idx
1112 .get(tag_key)
1113 .and_then(|values| values.get(tag_value))
1114 .cloned()
1115 .unwrap_or_default()
1116 };
1117
1118 let mut invalidated_count = 0;
1119 for key in &keys_to_invalidate {
1120 if self.cache.remove(key).is_ok() {
1121 invalidated_count += 1;
1122 }
1123 self.unregister_entry(key);
1124 }
1125
1126 Ok(invalidated_count)
1127 }
1128
1129 pub fn invalidate_namespace(&self, namespace: &str) -> Result<usize> {
1131 let keys_to_invalidate = {
1132 let ns_index = self.namespace_index.read().unwrap();
1133 ns_index.get(namespace).cloned().unwrap_or_default()
1134 };
1135
1136 let mut invalidated_count = 0;
1137 for key in &keys_to_invalidate {
1138 if self.cache.remove(key).is_ok() {
1139 invalidated_count += 1;
1140 }
1141 self.unregister_entry(key);
1142 }
1143
1144 Ok(invalidated_count)
1145 }
1146
1147 pub fn invalidate_expired(&self) -> Result<usize> {
1149 if let Some(ref persistent) = self.cache.persistent_cache {
1152 return self.scan_and_remove_expired_files(persistent);
1153 }
1154 Ok(0)
1155 }
1156
1157 fn scan_and_remove_expired_files(&self, persistent_cache: &PersistentCache) -> Result<usize> {
1159 let cache_dir = &persistent_cache.cache_dir;
1160 let mut removed_count = 0;
1161
1162 if !cache_dir.exists() {
1163 return Ok(0);
1164 }
1165
1166 for entry in std::fs::read_dir(cache_dir)? {
1168 let entry = entry?;
1169 if entry.file_type()?.is_dir() {
1170 for sub_entry in std::fs::read_dir(entry.path())? {
1172 let sub_entry = sub_entry?;
1173 if sub_entry.file_type()?.is_file() {
1174 if let Some(file_name) = sub_entry.file_name().to_str() {
1175 if file_name.ends_with(".cache") {
1176 if let Some(cache_key) =
1178 persistent_cache.decode_cache_key_from_filename(file_name)
1179 {
1180 if let Ok(Some(entry)) = persistent_cache.load(&cache_key) {
1182 if entry.is_expired() {
1183 let _ = std::fs::remove_file(sub_entry.path());
1184 removed_count += 1;
1185 }
1186 } else {
1187 let _ = std::fs::remove_file(sub_entry.path());
1189 removed_count += 1;
1190 }
1191 } else {
1192 if let Ok(metadata) = std::fs::metadata(sub_entry.path()) {
1194 if let Ok(modified) = metadata.modified() {
1195 let age = modified
1196 .elapsed()
1197 .unwrap_or(Duration::from_secs(0));
1198 if age > Duration::from_secs(24 * 3600) {
1200 let _ = std::fs::remove_file(sub_entry.path());
1201 removed_count += 1;
1202 }
1203 }
1204 }
1205 }
1206 }
1207 }
1208 }
1209 }
1210 }
1211 }
1212
1213 Ok(removed_count)
1214 }
1215
1216 pub fn get_stats(&self) -> InvalidationStats {
1218 let tag_idx = self.tag_index.read().unwrap();
1219 let ns_index = self.namespace_index.read().unwrap();
1220
1221 let total_tag_entries = tag_idx
1222 .values()
1223 .flat_map(|values| values.values())
1224 .map(|keys| keys.len())
1225 .sum();
1226
1227 let total_namespace_entries = ns_index.values().map(|keys| keys.len()).sum();
1228
1229 InvalidationStats {
1230 tracked_tags: tag_idx.len(),
1231 tracked_namespaces: ns_index.len(),
1232 total_tag_entries,
1233 total_namespace_entries,
1234 }
1235 }
1236}
1237
1238#[derive(Debug, Clone)]
1240pub struct InvalidationStats {
1241 pub tracked_tags: usize,
1242 pub tracked_namespaces: usize,
1243 pub total_tag_entries: usize,
1244 pub total_namespace_entries: usize,
1245}
1246
1247pub struct BackgroundCacheWorker {
1249 cache: Arc<MultiLevelCache>,
1250 invalidator: Arc<CacheInvalidator>,
1251 config: CacheConfig,
1252 worker_handle: Option<JoinHandle<()>>,
1253 shutdown_signal: Arc<RwLock<bool>>,
1254}
1255
1256impl BackgroundCacheWorker {
1257 pub fn new(
1258 cache: Arc<MultiLevelCache>,
1259 invalidator: Arc<CacheInvalidator>,
1260 config: CacheConfig,
1261 ) -> Self {
1262 Self {
1263 cache,
1264 invalidator,
1265 config,
1266 worker_handle: None,
1267 shutdown_signal: Arc::new(RwLock::new(false)),
1268 }
1269 }
1270
1271 pub fn start(&mut self) -> Result<()> {
1273 if !self.config.enable_background_updates {
1274 return Ok(());
1275 }
1276
1277 let cache = Arc::clone(&self.cache);
1278 let invalidator = Arc::clone(&self.invalidator);
1279 let interval = self.config.background_update_interval;
1280 let shutdown_signal = Arc::clone(&self.shutdown_signal);
1281
1282 let handle = thread::spawn(move || {
1283 while !*shutdown_signal.read().unwrap() {
1284 if let Err(e) = Self::perform_maintenance(&cache, &invalidator) {
1286 eprintln!("Background cache maintenance error: {e}");
1287 }
1288
1289 thread::sleep(interval);
1291 }
1292 });
1293
1294 self.worker_handle = Some(handle);
1295 Ok(())
1296 }
1297
1298 pub fn stop(&mut self) -> Result<()> {
1300 {
1302 let mut signal = self.shutdown_signal.write().unwrap();
1303 *signal = true;
1304 }
1305
1306 if let Some(handle) = self.worker_handle.take() {
1308 handle
1309 .join()
1310 .map_err(|e| anyhow!("Failed to join worker thread: {:?}", e))?;
1311 }
1312
1313 Ok(())
1314 }
1315
1316 fn perform_maintenance(
1318 cache: &Arc<MultiLevelCache>,
1319 invalidator: &Arc<CacheInvalidator>,
1320 ) -> Result<()> {
1321 let expired_count = invalidator.invalidate_expired()?;
1323 if expired_count > 0 {
1324 println!("Background worker cleaned {expired_count} expired entries");
1325 }
1326
1327 let memory_stats = cache.get_memory_stats();
1329 let utilization = memory_stats.memory_bytes as f64 / memory_stats.max_memory_bytes as f64;
1330
1331 if utilization > 0.9 {
1332 Self::aggressive_cleanup(cache)?;
1334 }
1335
1336 Self::sync_hot_entries(cache)?;
1338
1339 Ok(())
1340 }
1341
1342 fn aggressive_cleanup(_cache: &Arc<MultiLevelCache>) -> Result<()> {
1344 println!("Performing aggressive cache cleanup due to high memory usage");
1347 Ok(())
1348 }
1349
1350 fn sync_hot_entries(_cache: &Arc<MultiLevelCache>) -> Result<()> {
1352 Ok(())
1355 }
1356}
1357
1358impl Drop for BackgroundCacheWorker {
1359 fn drop(&mut self) {
1360 let _ = self.stop();
1361 }
1362}
1363
1364pub struct CacheWarmer {
1366 cache: Arc<MultiLevelCache>,
1367}
1368
1369impl CacheWarmer {
1370 pub fn new(cache: Arc<MultiLevelCache>) -> Self {
1371 Self { cache }
1372 }
1373
1374 pub fn warm_with_data(&self, data: Vec<(CacheKey, Vector)>) -> Result<usize> {
1376 let mut loaded_count = 0;
1377
1378 for (key, vector) in data {
1379 if self.cache.insert(key, vector).is_ok() {
1380 loaded_count += 1;
1381 }
1382 }
1383
1384 Ok(loaded_count)
1385 }
1386
1387 pub fn warm_from_persistent(&self, keys: Vec<CacheKey>) -> Result<usize> {
1389 let mut loaded_count = 0;
1390
1391 for key in keys {
1392 if self.cache.get(&key).is_some() {
1394 loaded_count += 1;
1395 }
1396 }
1397
1398 Ok(loaded_count)
1399 }
1400
1401 pub fn warm_with_generator<F>(&self, count: usize, generator: F) -> Result<usize>
1403 where
1404 F: Fn(usize) -> Option<(CacheKey, Vector)>,
1405 {
1406 let mut loaded_count = 0;
1407
1408 for i in 0..count {
1409 if let Some((key, vector)) = generator(i) {
1410 if self.cache.insert(key, vector).is_ok() {
1411 loaded_count += 1;
1412 }
1413 }
1414 }
1415
1416 Ok(loaded_count)
1417 }
1418}
1419
1420pub struct CacheAnalyzer {
1422 cache: Arc<MultiLevelCache>,
1423 invalidator: Arc<CacheInvalidator>,
1424}
1425
1426#[derive(Debug, Clone)]
1427pub struct CacheAnalysisReport {
1428 pub memory_utilization: f64,
1429 pub hit_ratio: f64,
1430 pub persistent_hit_ratio: f64,
1431 pub most_accessed_namespaces: Vec<(String, usize)>,
1432 pub recommendations: Vec<String>,
1433 pub performance_score: f64, }
1435
1436impl CacheAnalyzer {
1437 pub fn new(cache: Arc<MultiLevelCache>, invalidator: Arc<CacheInvalidator>) -> Self {
1438 Self { cache, invalidator }
1439 }
1440
1441 pub fn analyze(&self) -> CacheAnalysisReport {
1443 let stats = self.cache.get_stats();
1444 let memory_stats = self.cache.get_memory_stats();
1445 let invalidation_stats = self.invalidator.get_stats();
1446
1447 let memory_utilization =
1448 memory_stats.memory_bytes as f64 / memory_stats.max_memory_bytes as f64;
1449
1450 let total_requests = stats.total_requests;
1451 let total_hits = stats.memory_hits + stats.persistent_hits;
1452 let hit_ratio = if total_requests > 0 {
1453 total_hits as f64 / total_requests as f64
1454 } else {
1455 0.0
1456 };
1457
1458 let persistent_hit_ratio = if stats.persistent_hits + stats.persistent_misses > 0 {
1459 stats.persistent_hits as f64 / (stats.persistent_hits + stats.persistent_misses) as f64
1460 } else {
1461 0.0
1462 };
1463
1464 let mut recommendations = Vec::new();
1465
1466 if hit_ratio < 0.5 {
1468 recommendations
1469 .push("Consider increasing cache size or adjusting eviction policy".to_string());
1470 }
1471
1472 if memory_utilization > 0.9 {
1473 recommendations.push(
1474 "Memory cache is near capacity - consider increasing max_memory_bytes".to_string(),
1475 );
1476 }
1477
1478 if persistent_hit_ratio < 0.3 {
1479 recommendations
1480 .push("Persistent cache hit ratio is low - review TTL settings".to_string());
1481 }
1482
1483 if invalidation_stats.tracked_namespaces > 100 {
1484 recommendations
1485 .push("Consider consolidating namespaces to reduce tracking overhead".to_string());
1486 }
1487
1488 let performance_score =
1490 (hit_ratio * 0.4 + (1.0 - memory_utilization) * 0.3 + persistent_hit_ratio * 0.3)
1491 .clamp(0.0, 1.0);
1492
1493 CacheAnalysisReport {
1494 memory_utilization,
1495 hit_ratio,
1496 persistent_hit_ratio,
1497 most_accessed_namespaces: vec![], recommendations,
1499 performance_score,
1500 }
1501 }
1502
1503 pub fn get_optimization_recommendations(&self) -> Vec<String> {
1505 self.analyze().recommendations
1506 }
1507}
1508
1509#[cfg(test)]
1510mod tests {
1511 use super::*;
1512 use tempfile::TempDir;
1513
1514 #[test]
1515 fn test_cache_key() {
1516 let key = CacheKey::new("embeddings", "test_doc").with_variant("v1");
1517
1518 assert_eq!(key.namespace, "embeddings");
1519 assert_eq!(key.key, "test_doc");
1520 assert_eq!(key.variant, Some("v1".to_string()));
1521 assert_eq!(key.to_string(), "embeddings:test_doc:v1");
1522 }
1523
1524 #[test]
1525 fn test_memory_cache() {
1526 let config = CacheConfig {
1527 max_memory_entries: 2,
1528 max_memory_bytes: 1024,
1529 ..Default::default()
1530 };
1531
1532 let mut cache = MemoryCache::new(config);
1533
1534 let key1 = CacheKey::new("test", "key1");
1535 let key2 = CacheKey::new("test", "key2");
1536 let key3 = CacheKey::new("test", "key3");
1537
1538 let vector1 = Vector::new(vec![1.0, 2.0, 3.0]);
1539 let vector2 = Vector::new(vec![4.0, 5.0, 6.0]);
1540 let vector3 = Vector::new(vec![7.0, 8.0, 9.0]);
1541
1542 cache
1544 .insert(key1.clone(), CacheEntry::new(vector1.clone()))
1545 .unwrap();
1546 cache
1547 .insert(key2.clone(), CacheEntry::new(vector2.clone()))
1548 .unwrap();
1549
1550 assert!(cache.get(&key1).is_some());
1552 assert!(cache.get(&key2).is_some());
1553
1554 cache
1556 .insert(key3.clone(), CacheEntry::new(vector3.clone()))
1557 .unwrap();
1558
1559 let remaining = cache.entries.len();
1561 assert_eq!(remaining, 2);
1562 }
1563
1564 #[test]
1565 fn test_persistent_cache() {
1566 let temp_dir = TempDir::new().unwrap();
1567
1568 let config = CacheConfig {
1569 persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1570 enable_compression: true,
1571 ..Default::default()
1572 };
1573
1574 let cache = PersistentCache::new(config).unwrap();
1575
1576 let key = CacheKey::new("test", "persistent_key");
1577 let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1578 let entry = CacheEntry::new(vector.clone());
1579
1580 cache.store(&key, &entry).unwrap();
1582 let retrieved = cache.load(&key).unwrap();
1583
1584 assert!(retrieved.is_some());
1586 let retrieved_entry = retrieved.unwrap();
1587 assert_eq!(retrieved_entry.data.as_f32(), vector.as_f32());
1588 }
1589
1590 #[test]
1591 fn test_multi_level_cache() {
1592 let temp_dir = TempDir::new().unwrap();
1593
1594 let config = CacheConfig {
1595 max_memory_entries: 2,
1596 persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1597 enable_persistent: true,
1598 ..Default::default()
1599 };
1600
1601 let cache = MultiLevelCache::new(config).unwrap();
1602
1603 let key = CacheKey::new("test", "multi_level");
1604 let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1605
1606 cache.insert(key.clone(), vector.clone()).unwrap();
1608 let retrieved = cache.get(&key).unwrap();
1609
1610 assert_eq!(retrieved.as_f32(), vector.as_f32());
1611
1612 let stats = cache.get_stats();
1614 assert_eq!(stats.total_requests, 1);
1615 assert_eq!(stats.memory_hits, 1);
1616 }
1617
1618 #[test]
1619 fn test_cache_expiration() {
1620 let config = CacheConfig {
1621 max_memory_entries: 10,
1622 ttl: Some(Duration::from_millis(10)),
1623 ..Default::default()
1624 };
1625
1626 let mut cache = MemoryCache::new(config);
1627
1628 let key = CacheKey::new("test", "expiring");
1629 let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1630 let entry = CacheEntry::new(vector).with_ttl(Duration::from_millis(10));
1631
1632 cache.insert(key.clone(), entry).unwrap();
1633
1634 assert!(cache.get(&key).is_some());
1636
1637 std::thread::sleep(Duration::from_millis(20));
1639
1640 assert!(cache.get(&key).is_none());
1642 }
1643
1644 #[test]
1645 fn test_arc_eviction_policy() {
1646 let config = CacheConfig {
1647 max_memory_entries: 3,
1648 eviction_policy: EvictionPolicy::ARC,
1649 ..Default::default()
1650 };
1651
1652 let mut cache = MemoryCache::new(config);
1653
1654 let key1 = CacheKey::new("test", "arc1");
1655 let key2 = CacheKey::new("test", "arc2");
1656 let key3 = CacheKey::new("test", "arc3");
1657 let key4 = CacheKey::new("test", "arc4");
1658
1659 let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1660
1661 cache
1663 .insert(key1.clone(), CacheEntry::new(vector.clone()))
1664 .unwrap();
1665 cache
1666 .insert(key2.clone(), CacheEntry::new(vector.clone()))
1667 .unwrap();
1668 cache
1669 .insert(key3.clone(), CacheEntry::new(vector.clone()))
1670 .unwrap();
1671
1672 cache.get(&key1);
1674 cache.get(&key1);
1675 cache.get(&key1);
1676
1677 cache
1679 .insert(key4.clone(), CacheEntry::new(vector.clone()))
1680 .unwrap();
1681
1682 assert!(cache.get(&key1).is_some());
1684
1685 assert_eq!(cache.entries.len(), 3);
1687 }
1688
1689 #[test]
1690 fn test_cache_warmer() {
1691 let temp_dir = TempDir::new().unwrap();
1692
1693 let config = CacheConfig {
1694 max_memory_entries: 10,
1695 persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1696 enable_persistent: true,
1697 ..Default::default()
1698 };
1699
1700 let cache = Arc::new(MultiLevelCache::new(config).unwrap());
1701 let warmer = CacheWarmer::new(Arc::clone(&cache));
1702
1703 let test_data = vec![
1705 (CacheKey::new("test", "warm1"), Vector::new(vec![1.0, 2.0])),
1706 (CacheKey::new("test", "warm2"), Vector::new(vec![3.0, 4.0])),
1707 (CacheKey::new("test", "warm3"), Vector::new(vec![5.0, 6.0])),
1708 ];
1709
1710 let loaded_count = warmer.warm_with_data(test_data.clone()).unwrap();
1712 assert_eq!(loaded_count, 3);
1713
1714 for (key, expected_vector) in test_data {
1716 let cached_vector = cache.get(&key).unwrap();
1717 assert_eq!(cached_vector.as_f32(), expected_vector.as_f32());
1718 }
1719 }
1720
1721 #[test]
1722 fn test_cache_warmer_with_generator() {
1723 let temp_dir = TempDir::new().unwrap();
1724
1725 let config = CacheConfig {
1726 max_memory_entries: 10,
1727 persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1728 enable_persistent: true,
1729 ..Default::default()
1730 };
1731
1732 let cache = Arc::new(MultiLevelCache::new(config).unwrap());
1733 let warmer = CacheWarmer::new(Arc::clone(&cache));
1734
1735 let loaded_count = warmer
1737 .warm_with_generator(5, |i| {
1738 Some((
1739 CacheKey::new("generated", format!("item_{i}")),
1740 Vector::new(vec![i as f32, (i * 2) as f32]),
1741 ))
1742 })
1743 .unwrap();
1744
1745 assert_eq!(loaded_count, 5);
1746
1747 for i in 0..5 {
1749 let key = CacheKey::new("generated", format!("item_{i}"));
1750 let cached_vector = cache.get(&key).unwrap();
1751 assert_eq!(cached_vector.as_f32(), vec![i as f32, (i * 2) as f32]);
1752 }
1753 }
1754
1755 #[test]
1756 fn test_cache_analyzer() {
1757 let temp_dir = TempDir::new().unwrap();
1758
1759 let config = CacheConfig {
1760 max_memory_entries: 10,
1761 persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1762 enable_persistent: true,
1763 ..Default::default()
1764 };
1765
1766 let cache = Arc::new(MultiLevelCache::new(config).unwrap());
1767 let invalidator = Arc::new(CacheInvalidator::new(Arc::clone(&cache)));
1768 let analyzer = CacheAnalyzer::new(Arc::clone(&cache), Arc::clone(&invalidator));
1769
1770 let key1 = CacheKey::new("test", "analyze1");
1772 let key2 = CacheKey::new("test", "analyze2");
1773 let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1774
1775 cache.insert(key1.clone(), vector.clone()).unwrap();
1776 cache.insert(key2.clone(), vector.clone()).unwrap();
1777
1778 cache.get(&key1);
1780 cache.get(&key2);
1781 cache.get(&key1); cache.get(&CacheKey::new("test", "nonexistent")); let report = analyzer.analyze();
1786
1787 assert!(report.hit_ratio > 0.0);
1788 assert!(report.memory_utilization >= 0.0 && report.memory_utilization <= 1.0);
1789 assert!(report.performance_score >= 0.0 && report.performance_score <= 1.0);
1790
1791 let recommendations = analyzer.get_optimization_recommendations();
1793 assert!(!recommendations.is_empty());
1795 }
1796
1797 #[test]
1798 fn test_background_cache_worker() {
1799 let temp_dir = TempDir::new().unwrap();
1800
1801 let config = CacheConfig {
1802 max_memory_entries: 10,
1803 persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1804 enable_persistent: true,
1805 enable_background_updates: true,
1806 background_update_interval: Duration::from_millis(100),
1807 ..Default::default()
1808 };
1809
1810 let cache = Arc::new(MultiLevelCache::new(config.clone()).unwrap());
1811 let invalidator = Arc::new(CacheInvalidator::new(Arc::clone(&cache)));
1812 let mut worker =
1813 BackgroundCacheWorker::new(Arc::clone(&cache), Arc::clone(&invalidator), config);
1814
1815 worker.start().unwrap();
1817
1818 let key = CacheKey::new("test", "background");
1820 let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1821 cache.insert(key.clone(), vector.clone()).unwrap();
1822
1823 std::thread::sleep(Duration::from_millis(150));
1825
1826 worker.stop().unwrap();
1828
1829 assert!(cache.get(&key).is_some());
1831 }
1832
1833 #[test]
1834 fn test_cache_invalidation_by_tag() {
1835 let temp_dir = TempDir::new().unwrap();
1836
1837 let config = CacheConfig {
1838 max_memory_entries: 10,
1839 persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1840 enable_persistent: true,
1841 ..Default::default()
1842 };
1843
1844 let cache = Arc::new(MultiLevelCache::new(config).unwrap());
1845 let invalidator = Arc::new(CacheInvalidator::new(Arc::clone(&cache)));
1846
1847 let key1 = CacheKey::new("test", "tagged1");
1849 let key2 = CacheKey::new("test", "tagged2");
1850 let key3 = CacheKey::new("test", "tagged3");
1851
1852 let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1853
1854 cache.insert(key1.clone(), vector.clone()).unwrap();
1855 cache.insert(key2.clone(), vector.clone()).unwrap();
1856 cache.insert(key3.clone(), vector.clone()).unwrap();
1857
1858 let mut tags1 = HashMap::new();
1860 tags1.insert("category".to_string(), "embeddings".to_string());
1861 invalidator.register_entry(&key1, &tags1);
1862
1863 let mut tags2 = HashMap::new();
1864 tags2.insert("category".to_string(), "embeddings".to_string());
1865 invalidator.register_entry(&key2, &tags2);
1866
1867 let mut tags3 = HashMap::new();
1868 tags3.insert("category".to_string(), "vectors".to_string());
1869 invalidator.register_entry(&key3, &tags3);
1870
1871 let invalidated_count = invalidator
1873 .invalidate_by_tag("category", "embeddings")
1874 .unwrap();
1875 assert_eq!(invalidated_count, 2);
1876
1877 assert!(cache.get(&key1).is_none());
1879 assert!(cache.get(&key2).is_none());
1880
1881 assert!(cache.get(&key3).is_some());
1883 }
1884
1885 #[test]
1886 fn test_cache_invalidation_by_namespace() {
1887 let temp_dir = TempDir::new().unwrap();
1888
1889 let config = CacheConfig {
1890 max_memory_entries: 10,
1891 persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1892 enable_persistent: true,
1893 ..Default::default()
1894 };
1895
1896 let cache = Arc::new(MultiLevelCache::new(config).unwrap());
1897 let invalidator = Arc::new(CacheInvalidator::new(Arc::clone(&cache)));
1898
1899 let key1 = CacheKey::new("embeddings", "item1");
1901 let key2 = CacheKey::new("embeddings", "item2");
1902 let key3 = CacheKey::new("vectors", "item3");
1903
1904 let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1905
1906 cache.insert(key1.clone(), vector.clone()).unwrap();
1907 cache.insert(key2.clone(), vector.clone()).unwrap();
1908 cache.insert(key3.clone(), vector.clone()).unwrap();
1909
1910 invalidator.register_entry(&key1, &HashMap::new());
1912 invalidator.register_entry(&key2, &HashMap::new());
1913 invalidator.register_entry(&key3, &HashMap::new());
1914
1915 let invalidated_count = invalidator.invalidate_namespace("embeddings").unwrap();
1917 assert_eq!(invalidated_count, 2);
1918
1919 assert!(cache.get(&key1).is_none());
1921 assert!(cache.get(&key2).is_none());
1922
1923 assert!(cache.get(&key3).is_some());
1925 }
1926}