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 if data.len() < 4 {
657 return Err(anyhow::anyhow!(
658 "Invalid cache entry data: too small (expected at least 4 bytes, got {})",
659 data.len()
660 ));
661 }
662
663 let mut offset = 0;
664
665 let vector_len = u32::from_le_bytes([
667 data[offset],
668 data[offset + 1],
669 data[offset + 2],
670 data[offset + 3],
671 ]) as usize;
672 offset += 4;
673
674 let mut vector_data = Vec::with_capacity(vector_len);
675 for _ in 0..vector_len {
676 let value = f32::from_le_bytes([
677 data[offset],
678 data[offset + 1],
679 data[offset + 2],
680 data[offset + 3],
681 ]);
682 vector_data.push(value);
683 offset += 4;
684 }
685 let vector = Vector::new(vector_data);
686
687 let created_nanos = u64::from_le_bytes([
689 data[offset],
690 data[offset + 1],
691 data[offset + 2],
692 data[offset + 3],
693 data[offset + 4],
694 data[offset + 5],
695 data[offset + 6],
696 data[offset + 7],
697 ]);
698 offset += 8;
699
700 let accessed_nanos = u64::from_le_bytes([
701 data[offset],
702 data[offset + 1],
703 data[offset + 2],
704 data[offset + 3],
705 data[offset + 4],
706 data[offset + 5],
707 data[offset + 6],
708 data[offset + 7],
709 ]);
710 offset += 8;
711
712 let now = Instant::now();
714 let created_at = now - Duration::from_nanos(created_nanos);
715 let last_accessed = now - Duration::from_nanos(accessed_nanos);
716
717 let access_count = u64::from_le_bytes([
719 data[offset],
720 data[offset + 1],
721 data[offset + 2],
722 data[offset + 3],
723 data[offset + 4],
724 data[offset + 5],
725 data[offset + 6],
726 data[offset + 7],
727 ]);
728 offset += 8;
729
730 let size_bytes = u64::from_le_bytes([
731 data[offset],
732 data[offset + 1],
733 data[offset + 2],
734 data[offset + 3],
735 data[offset + 4],
736 data[offset + 5],
737 data[offset + 6],
738 data[offset + 7],
739 ]) as usize;
740 offset += 8;
741
742 let ttl = if data[offset] == 1 {
744 offset += 1;
745 let ttl_nanos = u128::from_le_bytes([
746 data[offset],
747 data[offset + 1],
748 data[offset + 2],
749 data[offset + 3],
750 data[offset + 4],
751 data[offset + 5],
752 data[offset + 6],
753 data[offset + 7],
754 data[offset + 8],
755 data[offset + 9],
756 data[offset + 10],
757 data[offset + 11],
758 data[offset + 12],
759 data[offset + 13],
760 data[offset + 14],
761 data[offset + 15],
762 ]);
763 offset += 16;
764 Some(Duration::from_nanos(ttl_nanos as u64))
765 } else {
766 offset += 1;
767 None
768 };
769
770 let tags_len = u32::from_le_bytes([
772 data[offset],
773 data[offset + 1],
774 data[offset + 2],
775 data[offset + 3],
776 ]) as usize;
777 offset += 4;
778
779 let mut tags = HashMap::new();
780 for _ in 0..tags_len {
781 let key_len = u32::from_le_bytes([
782 data[offset],
783 data[offset + 1],
784 data[offset + 2],
785 data[offset + 3],
786 ]) as usize;
787 offset += 4;
788 let key = String::from_utf8(data[offset..offset + key_len].to_vec())?;
789 offset += key_len;
790
791 let value_len = u32::from_le_bytes([
792 data[offset],
793 data[offset + 1],
794 data[offset + 2],
795 data[offset + 3],
796 ]) as usize;
797 offset += 4;
798 let value = String::from_utf8(data[offset..offset + value_len].to_vec())?;
799 offset += value_len;
800
801 tags.insert(key, value);
802 }
803
804 Ok(CacheEntry {
805 data: vector,
806 created_at,
807 last_accessed,
808 access_count,
809 size_bytes,
810 ttl,
811 tags,
812 })
813 }
814
815 fn compress_data(&self, data: &[u8]) -> Result<Vec<u8>> {
817 let mut compressed = Vec::new();
819
820 if data.is_empty() {
821 return Ok(compressed);
822 }
823
824 let mut current_byte = data[0];
825 let mut count = 1u8;
826
827 for &byte in &data[1..] {
828 if byte == current_byte && count < 255 {
829 count += 1;
830 } else {
831 compressed.push(count);
832 compressed.push(current_byte);
833 current_byte = byte;
834 count = 1;
835 }
836 }
837
838 compressed.push(count);
840 compressed.push(current_byte);
841
842 Ok(compressed)
843 }
844
845 fn decompress_data(&self, data: &[u8]) -> Result<Vec<u8>> {
847 let mut decompressed = Vec::new();
848
849 if data.len() % 2 != 0 {
850 return Err(anyhow!("Invalid compressed data length"));
851 }
852
853 for chunk in data.chunks(2) {
854 let count = chunk[0];
855 let byte = chunk[1];
856
857 for _ in 0..count {
858 decompressed.push(byte);
859 }
860 }
861
862 Ok(decompressed)
863 }
864}
865
866pub struct MultiLevelCache {
868 memory_cache: Arc<RwLock<MemoryCache>>,
869 persistent_cache: Option<Arc<PersistentCache>>,
870 #[allow(dead_code)]
871 config: CacheConfig,
872 stats: Arc<RwLock<MultiLevelCacheStats>>,
873}
874
875#[derive(Debug, Default, Clone)]
876pub struct MultiLevelCacheStats {
877 pub memory_hits: u64,
878 pub memory_misses: u64,
879 pub persistent_hits: u64,
880 pub persistent_misses: u64,
881 pub total_requests: u64,
882}
883
884impl MultiLevelCache {
885 pub fn new(config: CacheConfig) -> Result<Self> {
886 let memory_cache = Arc::new(RwLock::new(MemoryCache::new(config.clone())));
887
888 let persistent_cache = if config.enable_persistent {
889 Some(Arc::new(PersistentCache::new(config.clone())?))
890 } else {
891 None
892 };
893
894 Ok(Self {
895 memory_cache,
896 persistent_cache,
897 config,
898 stats: Arc::new(RwLock::new(MultiLevelCacheStats::default())),
899 })
900 }
901
902 pub fn insert(&self, key: CacheKey, data: Vector) -> Result<()> {
904 let entry = CacheEntry::new(data);
905
906 {
908 let mut memory = self.memory_cache.write().unwrap();
909 memory.insert(key.clone(), entry.clone())?;
910 }
911
912 if let Some(ref persistent) = self.persistent_cache {
914 persistent.store(&key, &entry)?;
915 }
916
917 Ok(())
918 }
919
920 pub fn get(&self, key: &CacheKey) -> Option<Vector> {
922 self.update_stats_total();
923
924 {
926 let mut memory = self.memory_cache.write().unwrap();
927 if let Some(data) = memory.get(key) {
928 self.update_stats_memory_hit();
929 return Some(data.clone());
930 }
931 }
932
933 self.update_stats_memory_miss();
934
935 if let Some(ref persistent) = self.persistent_cache {
937 if let Ok(Some(mut entry)) = persistent.load(key) {
938 self.update_stats_persistent_hit();
939
940 let data = entry.data.clone();
942 entry.touch();
943 if let Ok(mut memory) = self.memory_cache.write() {
944 let _ = memory.insert(key.clone(), entry);
945 }
946
947 return Some(data);
948 }
949 }
950
951 self.update_stats_persistent_miss();
952 None
953 }
954
955 pub fn remove(&self, key: &CacheKey) -> Result<()> {
957 {
959 let mut memory = self.memory_cache.write().unwrap();
960 memory.remove(key);
961 }
962
963 if let Some(ref persistent) = self.persistent_cache {
965 persistent.remove(key)?;
966 }
967
968 Ok(())
969 }
970
971 pub fn clear(&self) -> Result<()> {
973 {
975 let mut memory = self.memory_cache.write().unwrap();
976 memory.clear();
977 }
978
979 if let Some(ref persistent) = self.persistent_cache {
981 persistent.clear()?;
982 }
983
984 {
986 let mut stats = self.stats.write().unwrap();
987 *stats = MultiLevelCacheStats::default();
988 }
989
990 Ok(())
991 }
992
993 pub fn get_stats(&self) -> MultiLevelCacheStats {
995 self.stats.read().unwrap().clone()
996 }
997
998 pub fn get_memory_stats(&self) -> CacheStats {
1000 let memory = self.memory_cache.read().unwrap();
1001 memory.stats()
1002 }
1003
1004 fn update_stats_total(&self) {
1006 let mut stats = self.stats.write().unwrap();
1007 stats.total_requests += 1;
1008 }
1009
1010 fn update_stats_memory_hit(&self) {
1011 let mut stats = self.stats.write().unwrap();
1012 stats.memory_hits += 1;
1013 }
1014
1015 fn update_stats_memory_miss(&self) {
1016 let mut stats = self.stats.write().unwrap();
1017 stats.memory_misses += 1;
1018 }
1019
1020 fn update_stats_persistent_hit(&self) {
1021 let mut stats = self.stats.write().unwrap();
1022 stats.persistent_hits += 1;
1023 }
1024
1025 fn update_stats_persistent_miss(&self) {
1026 let mut stats = self.stats.write().unwrap();
1027 stats.persistent_misses += 1;
1028 }
1029}
1030
1031pub struct CacheInvalidator {
1033 cache: Arc<MultiLevelCache>,
1034 tag_index: TagIndex, namespace_index: Arc<RwLock<HashMap<String, Vec<CacheKey>>>>, }
1037
1038impl CacheInvalidator {
1039 pub fn new(cache: Arc<MultiLevelCache>) -> Self {
1040 Self {
1041 cache,
1042 tag_index: Arc::new(RwLock::new(HashMap::new())),
1043 namespace_index: Arc::new(RwLock::new(HashMap::new())),
1044 }
1045 }
1046
1047 pub fn register_entry(&self, key: &CacheKey, tags: &HashMap<String, String>) {
1049 {
1051 let mut ns_index = self.namespace_index.write().unwrap();
1052 ns_index
1053 .entry(key.namespace.clone())
1054 .or_default()
1055 .push(key.clone());
1056 }
1057
1058 {
1060 let mut tag_idx = self.tag_index.write().unwrap();
1061 for (tag_key, tag_value) in tags {
1062 tag_idx
1063 .entry(tag_key.clone())
1064 .or_default()
1065 .entry(tag_value.clone())
1066 .or_default()
1067 .push(key.clone());
1068 }
1069 }
1070 }
1071
1072 pub fn unregister_entry(&self, key: &CacheKey) {
1074 {
1076 let mut ns_index = self.namespace_index.write().unwrap();
1077 if let Some(keys) = ns_index.get_mut(&key.namespace) {
1078 keys.retain(|k| k != key);
1079 if keys.is_empty() {
1080 ns_index.remove(&key.namespace);
1081 }
1082 }
1083 }
1084
1085 {
1087 let mut tag_idx = self.tag_index.write().unwrap();
1088 let mut tags_to_remove = Vec::new();
1089
1090 for (tag_key, tag_values) in tag_idx.iter_mut() {
1091 let mut values_to_remove = Vec::new();
1092
1093 for (tag_value, keys) in tag_values.iter_mut() {
1094 keys.retain(|k| k != key);
1095 if keys.is_empty() {
1096 values_to_remove.push(tag_value.clone());
1097 }
1098 }
1099
1100 for value in values_to_remove {
1101 tag_values.remove(&value);
1102 }
1103
1104 if tag_values.is_empty() {
1105 tags_to_remove.push(tag_key.clone());
1106 }
1107 }
1108
1109 for tag in tags_to_remove {
1110 tag_idx.remove(&tag);
1111 }
1112 }
1113 }
1114
1115 pub fn invalidate_by_tag(&self, tag_key: &str, tag_value: &str) -> Result<usize> {
1117 let keys_to_invalidate = {
1118 let tag_idx = self.tag_index.read().unwrap();
1119 tag_idx
1120 .get(tag_key)
1121 .and_then(|values| values.get(tag_value))
1122 .cloned()
1123 .unwrap_or_default()
1124 };
1125
1126 let mut invalidated_count = 0;
1127 for key in &keys_to_invalidate {
1128 if self.cache.remove(key).is_ok() {
1129 invalidated_count += 1;
1130 }
1131 self.unregister_entry(key);
1132 }
1133
1134 Ok(invalidated_count)
1135 }
1136
1137 pub fn invalidate_namespace(&self, namespace: &str) -> Result<usize> {
1139 let keys_to_invalidate = {
1140 let ns_index = self.namespace_index.read().unwrap();
1141 ns_index.get(namespace).cloned().unwrap_or_default()
1142 };
1143
1144 let mut invalidated_count = 0;
1145 for key in &keys_to_invalidate {
1146 if self.cache.remove(key).is_ok() {
1147 invalidated_count += 1;
1148 }
1149 self.unregister_entry(key);
1150 }
1151
1152 Ok(invalidated_count)
1153 }
1154
1155 pub fn invalidate_expired(&self) -> Result<usize> {
1157 if let Some(ref persistent) = self.cache.persistent_cache {
1160 return self.scan_and_remove_expired_files(persistent);
1161 }
1162 Ok(0)
1163 }
1164
1165 fn scan_and_remove_expired_files(&self, persistent_cache: &PersistentCache) -> Result<usize> {
1167 let cache_dir = &persistent_cache.cache_dir;
1168 let mut removed_count = 0;
1169
1170 if !cache_dir.exists() {
1171 return Ok(0);
1172 }
1173
1174 for entry in std::fs::read_dir(cache_dir)? {
1176 let entry = entry?;
1177 if entry.file_type()?.is_dir() {
1178 for sub_entry in std::fs::read_dir(entry.path())? {
1180 let sub_entry = sub_entry?;
1181 if sub_entry.file_type()?.is_file() {
1182 if let Some(file_name) = sub_entry.file_name().to_str() {
1183 if file_name.ends_with(".cache") {
1184 if let Some(cache_key) =
1186 persistent_cache.decode_cache_key_from_filename(file_name)
1187 {
1188 if let Ok(Some(entry)) = persistent_cache.load(&cache_key) {
1190 if entry.is_expired() {
1191 let _ = std::fs::remove_file(sub_entry.path());
1192 removed_count += 1;
1193 }
1194 } else {
1195 let _ = std::fs::remove_file(sub_entry.path());
1197 removed_count += 1;
1198 }
1199 } else {
1200 if let Ok(metadata) = std::fs::metadata(sub_entry.path()) {
1202 if let Ok(modified) = metadata.modified() {
1203 let age = modified
1204 .elapsed()
1205 .unwrap_or(Duration::from_secs(0));
1206 if age > Duration::from_secs(24 * 3600) {
1208 let _ = std::fs::remove_file(sub_entry.path());
1209 removed_count += 1;
1210 }
1211 }
1212 }
1213 }
1214 }
1215 }
1216 }
1217 }
1218 }
1219 }
1220
1221 Ok(removed_count)
1222 }
1223
1224 pub fn get_stats(&self) -> InvalidationStats {
1226 let tag_idx = self.tag_index.read().unwrap();
1227 let ns_index = self.namespace_index.read().unwrap();
1228
1229 let total_tag_entries = tag_idx
1230 .values()
1231 .flat_map(|values| values.values())
1232 .map(|keys| keys.len())
1233 .sum();
1234
1235 let total_namespace_entries = ns_index.values().map(|keys| keys.len()).sum();
1236
1237 InvalidationStats {
1238 tracked_tags: tag_idx.len(),
1239 tracked_namespaces: ns_index.len(),
1240 total_tag_entries,
1241 total_namespace_entries,
1242 }
1243 }
1244}
1245
1246#[derive(Debug, Clone)]
1248pub struct InvalidationStats {
1249 pub tracked_tags: usize,
1250 pub tracked_namespaces: usize,
1251 pub total_tag_entries: usize,
1252 pub total_namespace_entries: usize,
1253}
1254
1255pub struct BackgroundCacheWorker {
1257 cache: Arc<MultiLevelCache>,
1258 invalidator: Arc<CacheInvalidator>,
1259 config: CacheConfig,
1260 worker_handle: Option<JoinHandle<()>>,
1261 shutdown_signal: Arc<RwLock<bool>>,
1262}
1263
1264impl BackgroundCacheWorker {
1265 pub fn new(
1266 cache: Arc<MultiLevelCache>,
1267 invalidator: Arc<CacheInvalidator>,
1268 config: CacheConfig,
1269 ) -> Self {
1270 Self {
1271 cache,
1272 invalidator,
1273 config,
1274 worker_handle: None,
1275 shutdown_signal: Arc::new(RwLock::new(false)),
1276 }
1277 }
1278
1279 pub fn start(&mut self) -> Result<()> {
1281 if !self.config.enable_background_updates {
1282 return Ok(());
1283 }
1284
1285 let cache = Arc::clone(&self.cache);
1286 let invalidator = Arc::clone(&self.invalidator);
1287 let interval = self.config.background_update_interval;
1288 let shutdown_signal = Arc::clone(&self.shutdown_signal);
1289
1290 let handle = thread::spawn(move || {
1291 while let Ok(shutdown) = shutdown_signal.read() {
1292 if *shutdown {
1293 break;
1294 }
1295 drop(shutdown); if let Err(e) = Self::perform_maintenance(&cache, &invalidator) {
1299 tracing::warn!("Background cache maintenance error: {}", e);
1301 }
1302
1303 thread::sleep(interval);
1305 }
1306 });
1307
1308 self.worker_handle = Some(handle);
1309 Ok(())
1310 }
1311
1312 pub fn stop(&mut self) -> Result<()> {
1314 {
1316 let mut signal = self.shutdown_signal.write().unwrap();
1317 *signal = true;
1318 }
1319
1320 if let Some(handle) = self.worker_handle.take() {
1322 handle
1323 .join()
1324 .map_err(|e| anyhow!("Failed to join worker thread: {:?}", e))?;
1325 }
1326
1327 Ok(())
1328 }
1329
1330 fn perform_maintenance(
1332 cache: &Arc<MultiLevelCache>,
1333 invalidator: &Arc<CacheInvalidator>,
1334 ) -> Result<()> {
1335 let expired_count = invalidator.invalidate_expired()?;
1337 if expired_count > 0 {
1338 println!("Background worker cleaned {expired_count} expired entries");
1339 }
1340
1341 let memory_stats = cache.get_memory_stats();
1343 let utilization = memory_stats.memory_bytes as f64 / memory_stats.max_memory_bytes as f64;
1344
1345 if utilization > 0.9 {
1346 Self::aggressive_cleanup(cache)?;
1348 }
1349
1350 Self::sync_hot_entries(cache)?;
1352
1353 Ok(())
1354 }
1355
1356 fn aggressive_cleanup(_cache: &Arc<MultiLevelCache>) -> Result<()> {
1358 println!("Performing aggressive cache cleanup due to high memory usage");
1361 Ok(())
1362 }
1363
1364 fn sync_hot_entries(_cache: &Arc<MultiLevelCache>) -> Result<()> {
1366 Ok(())
1369 }
1370}
1371
1372impl Drop for BackgroundCacheWorker {
1373 fn drop(&mut self) {
1374 let _ = self.stop();
1375 }
1376}
1377
1378pub struct CacheWarmer {
1380 cache: Arc<MultiLevelCache>,
1381}
1382
1383impl CacheWarmer {
1384 pub fn new(cache: Arc<MultiLevelCache>) -> Self {
1385 Self { cache }
1386 }
1387
1388 pub fn warm_with_data(&self, data: Vec<(CacheKey, Vector)>) -> Result<usize> {
1390 let mut loaded_count = 0;
1391
1392 for (key, vector) in data {
1393 if self.cache.insert(key, vector).is_ok() {
1394 loaded_count += 1;
1395 }
1396 }
1397
1398 Ok(loaded_count)
1399 }
1400
1401 pub fn warm_from_persistent(&self, keys: Vec<CacheKey>) -> Result<usize> {
1403 let mut loaded_count = 0;
1404
1405 for key in keys {
1406 if self.cache.get(&key).is_some() {
1408 loaded_count += 1;
1409 }
1410 }
1411
1412 Ok(loaded_count)
1413 }
1414
1415 pub fn warm_with_generator<F>(&self, count: usize, generator: F) -> Result<usize>
1417 where
1418 F: Fn(usize) -> Option<(CacheKey, Vector)>,
1419 {
1420 let mut loaded_count = 0;
1421
1422 for i in 0..count {
1423 if let Some((key, vector)) = generator(i) {
1424 if self.cache.insert(key, vector).is_ok() {
1425 loaded_count += 1;
1426 }
1427 }
1428 }
1429
1430 Ok(loaded_count)
1431 }
1432}
1433
1434pub struct CacheAnalyzer {
1436 cache: Arc<MultiLevelCache>,
1437 invalidator: Arc<CacheInvalidator>,
1438}
1439
1440#[derive(Debug, Clone)]
1441pub struct CacheAnalysisReport {
1442 pub memory_utilization: f64,
1443 pub hit_ratio: f64,
1444 pub persistent_hit_ratio: f64,
1445 pub most_accessed_namespaces: Vec<(String, usize)>,
1446 pub recommendations: Vec<String>,
1447 pub performance_score: f64, }
1449
1450impl CacheAnalyzer {
1451 pub fn new(cache: Arc<MultiLevelCache>, invalidator: Arc<CacheInvalidator>) -> Self {
1452 Self { cache, invalidator }
1453 }
1454
1455 pub fn analyze(&self) -> CacheAnalysisReport {
1457 let stats = self.cache.get_stats();
1458 let memory_stats = self.cache.get_memory_stats();
1459 let invalidation_stats = self.invalidator.get_stats();
1460
1461 let memory_utilization =
1462 memory_stats.memory_bytes as f64 / memory_stats.max_memory_bytes as f64;
1463
1464 let total_requests = stats.total_requests;
1465 let total_hits = stats.memory_hits + stats.persistent_hits;
1466 let hit_ratio = if total_requests > 0 {
1467 total_hits as f64 / total_requests as f64
1468 } else {
1469 0.0
1470 };
1471
1472 let persistent_hit_ratio = if stats.persistent_hits + stats.persistent_misses > 0 {
1473 stats.persistent_hits as f64 / (stats.persistent_hits + stats.persistent_misses) as f64
1474 } else {
1475 0.0
1476 };
1477
1478 let mut recommendations = Vec::new();
1479
1480 if hit_ratio < 0.5 {
1482 recommendations
1483 .push("Consider increasing cache size or adjusting eviction policy".to_string());
1484 }
1485
1486 if memory_utilization > 0.9 {
1487 recommendations.push(
1488 "Memory cache is near capacity - consider increasing max_memory_bytes".to_string(),
1489 );
1490 }
1491
1492 if persistent_hit_ratio < 0.3 {
1493 recommendations
1494 .push("Persistent cache hit ratio is low - review TTL settings".to_string());
1495 }
1496
1497 if invalidation_stats.tracked_namespaces > 100 {
1498 recommendations
1499 .push("Consider consolidating namespaces to reduce tracking overhead".to_string());
1500 }
1501
1502 let performance_score =
1504 (hit_ratio * 0.4 + (1.0 - memory_utilization) * 0.3 + persistent_hit_ratio * 0.3)
1505 .clamp(0.0, 1.0);
1506
1507 CacheAnalysisReport {
1508 memory_utilization,
1509 hit_ratio,
1510 persistent_hit_ratio,
1511 most_accessed_namespaces: vec![], recommendations,
1513 performance_score,
1514 }
1515 }
1516
1517 pub fn get_optimization_recommendations(&self) -> Vec<String> {
1519 self.analyze().recommendations
1520 }
1521}
1522
1523#[cfg(test)]
1524mod tests {
1525 use super::*;
1526 use tempfile::TempDir;
1527
1528 #[test]
1529 fn test_cache_key() {
1530 let key = CacheKey::new("embeddings", "test_doc").with_variant("v1");
1531
1532 assert_eq!(key.namespace, "embeddings");
1533 assert_eq!(key.key, "test_doc");
1534 assert_eq!(key.variant, Some("v1".to_string()));
1535 assert_eq!(key.to_string(), "embeddings:test_doc:v1");
1536 }
1537
1538 #[test]
1539 fn test_memory_cache() {
1540 let config = CacheConfig {
1541 max_memory_entries: 2,
1542 max_memory_bytes: 1024,
1543 ..Default::default()
1544 };
1545
1546 let mut cache = MemoryCache::new(config);
1547
1548 let key1 = CacheKey::new("test", "key1");
1549 let key2 = CacheKey::new("test", "key2");
1550 let key3 = CacheKey::new("test", "key3");
1551
1552 let vector1 = Vector::new(vec![1.0, 2.0, 3.0]);
1553 let vector2 = Vector::new(vec![4.0, 5.0, 6.0]);
1554 let vector3 = Vector::new(vec![7.0, 8.0, 9.0]);
1555
1556 cache
1558 .insert(key1.clone(), CacheEntry::new(vector1.clone()))
1559 .unwrap();
1560 cache
1561 .insert(key2.clone(), CacheEntry::new(vector2.clone()))
1562 .unwrap();
1563
1564 assert!(cache.get(&key1).is_some());
1566 assert!(cache.get(&key2).is_some());
1567
1568 cache
1570 .insert(key3.clone(), CacheEntry::new(vector3.clone()))
1571 .unwrap();
1572
1573 let remaining = cache.entries.len();
1575 assert_eq!(remaining, 2);
1576 }
1577
1578 #[test]
1579 fn test_persistent_cache() {
1580 let temp_dir = TempDir::new().unwrap();
1581
1582 let config = CacheConfig {
1583 persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1584 enable_compression: true,
1585 ..Default::default()
1586 };
1587
1588 let cache = PersistentCache::new(config).unwrap();
1589
1590 let key = CacheKey::new("test", "persistent_key");
1591 let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1592 let entry = CacheEntry::new(vector.clone());
1593
1594 cache.store(&key, &entry).unwrap();
1596 let retrieved = cache.load(&key).unwrap();
1597
1598 assert!(retrieved.is_some());
1600 let retrieved_entry = retrieved.unwrap();
1601 assert_eq!(retrieved_entry.data.as_f32(), vector.as_f32());
1602 }
1603
1604 #[test]
1605 fn test_multi_level_cache() {
1606 let temp_dir = TempDir::new().unwrap();
1607
1608 let config = CacheConfig {
1609 max_memory_entries: 2,
1610 persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1611 enable_persistent: true,
1612 ..Default::default()
1613 };
1614
1615 let cache = MultiLevelCache::new(config).unwrap();
1616
1617 let key = CacheKey::new("test", "multi_level");
1618 let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1619
1620 cache.insert(key.clone(), vector.clone()).unwrap();
1622 let retrieved = cache.get(&key).unwrap();
1623
1624 assert_eq!(retrieved.as_f32(), vector.as_f32());
1625
1626 let stats = cache.get_stats();
1628 assert_eq!(stats.total_requests, 1);
1629 assert_eq!(stats.memory_hits, 1);
1630 }
1631
1632 #[test]
1633 fn test_cache_expiration() {
1634 let config = CacheConfig {
1635 max_memory_entries: 10,
1636 ttl: Some(Duration::from_millis(10)),
1637 ..Default::default()
1638 };
1639
1640 let mut cache = MemoryCache::new(config);
1641
1642 let key = CacheKey::new("test", "expiring");
1643 let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1644 let entry = CacheEntry::new(vector).with_ttl(Duration::from_millis(10));
1645
1646 cache.insert(key.clone(), entry).unwrap();
1647
1648 assert!(cache.get(&key).is_some());
1650
1651 std::thread::sleep(Duration::from_millis(20));
1653
1654 assert!(cache.get(&key).is_none());
1656 }
1657
1658 #[test]
1659 fn test_arc_eviction_policy() {
1660 let config = CacheConfig {
1661 max_memory_entries: 3,
1662 eviction_policy: EvictionPolicy::ARC,
1663 ..Default::default()
1664 };
1665
1666 let mut cache = MemoryCache::new(config);
1667
1668 let key1 = CacheKey::new("test", "arc1");
1669 let key2 = CacheKey::new("test", "arc2");
1670 let key3 = CacheKey::new("test", "arc3");
1671 let key4 = CacheKey::new("test", "arc4");
1672
1673 let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1674
1675 cache
1677 .insert(key1.clone(), CacheEntry::new(vector.clone()))
1678 .unwrap();
1679 cache
1680 .insert(key2.clone(), CacheEntry::new(vector.clone()))
1681 .unwrap();
1682 cache
1683 .insert(key3.clone(), CacheEntry::new(vector.clone()))
1684 .unwrap();
1685
1686 cache.get(&key1);
1688 cache.get(&key1);
1689 cache.get(&key1);
1690
1691 cache
1693 .insert(key4.clone(), CacheEntry::new(vector.clone()))
1694 .unwrap();
1695
1696 assert!(cache.get(&key1).is_some());
1698
1699 assert_eq!(cache.entries.len(), 3);
1701 }
1702
1703 #[test]
1704 fn test_cache_warmer() {
1705 let temp_dir = TempDir::new().unwrap();
1706
1707 let config = CacheConfig {
1708 max_memory_entries: 10,
1709 persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1710 enable_persistent: true,
1711 ..Default::default()
1712 };
1713
1714 let cache = Arc::new(MultiLevelCache::new(config).unwrap());
1715 let warmer = CacheWarmer::new(Arc::clone(&cache));
1716
1717 let test_data = vec![
1719 (CacheKey::new("test", "warm1"), Vector::new(vec![1.0, 2.0])),
1720 (CacheKey::new("test", "warm2"), Vector::new(vec![3.0, 4.0])),
1721 (CacheKey::new("test", "warm3"), Vector::new(vec![5.0, 6.0])),
1722 ];
1723
1724 let loaded_count = warmer.warm_with_data(test_data.clone()).unwrap();
1726 assert_eq!(loaded_count, 3);
1727
1728 for (key, expected_vector) in test_data {
1730 let cached_vector = cache.get(&key).unwrap();
1731 assert_eq!(cached_vector.as_f32(), expected_vector.as_f32());
1732 }
1733 }
1734
1735 #[test]
1736 fn test_cache_warmer_with_generator() {
1737 let temp_dir = TempDir::new().unwrap();
1738
1739 let config = CacheConfig {
1740 max_memory_entries: 10,
1741 persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1742 enable_persistent: true,
1743 ..Default::default()
1744 };
1745
1746 let cache = Arc::new(MultiLevelCache::new(config).unwrap());
1747 let warmer = CacheWarmer::new(Arc::clone(&cache));
1748
1749 let loaded_count = warmer
1751 .warm_with_generator(5, |i| {
1752 Some((
1753 CacheKey::new("generated", format!("item_{i}")),
1754 Vector::new(vec![i as f32, (i * 2) as f32]),
1755 ))
1756 })
1757 .unwrap();
1758
1759 assert_eq!(loaded_count, 5);
1760
1761 for i in 0..5 {
1763 let key = CacheKey::new("generated", format!("item_{i}"));
1764 let cached_vector = cache.get(&key).unwrap();
1765 assert_eq!(cached_vector.as_f32(), vec![i as f32, (i * 2) as f32]);
1766 }
1767 }
1768
1769 #[test]
1770 fn test_cache_analyzer() {
1771 let temp_dir = TempDir::new().unwrap();
1772
1773 let config = CacheConfig {
1774 max_memory_entries: 10,
1775 persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1776 enable_persistent: true,
1777 ..Default::default()
1778 };
1779
1780 let cache = Arc::new(MultiLevelCache::new(config).unwrap());
1781 let invalidator = Arc::new(CacheInvalidator::new(Arc::clone(&cache)));
1782 let analyzer = CacheAnalyzer::new(Arc::clone(&cache), Arc::clone(&invalidator));
1783
1784 let key1 = CacheKey::new("test", "analyze1");
1786 let key2 = CacheKey::new("test", "analyze2");
1787 let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1788
1789 cache.insert(key1.clone(), vector.clone()).unwrap();
1790 cache.insert(key2.clone(), vector.clone()).unwrap();
1791
1792 cache.get(&key1);
1794 cache.get(&key2);
1795 cache.get(&key1); cache.get(&CacheKey::new("test", "nonexistent")); let report = analyzer.analyze();
1800
1801 assert!(report.hit_ratio > 0.0);
1802 assert!(report.memory_utilization >= 0.0 && report.memory_utilization <= 1.0);
1803 assert!(report.performance_score >= 0.0 && report.performance_score <= 1.0);
1804
1805 let recommendations = analyzer.get_optimization_recommendations();
1807 assert!(!recommendations.is_empty());
1809 }
1810
1811 #[test]
1812 fn test_background_cache_worker() {
1813 let temp_dir = TempDir::new().unwrap();
1814
1815 let config = CacheConfig {
1816 max_memory_entries: 10,
1817 persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1818 enable_persistent: true,
1819 enable_background_updates: true,
1820 background_update_interval: Duration::from_millis(100),
1821 ..Default::default()
1822 };
1823
1824 let cache = Arc::new(MultiLevelCache::new(config.clone()).unwrap());
1825 let invalidator = Arc::new(CacheInvalidator::new(Arc::clone(&cache)));
1826 let mut worker =
1827 BackgroundCacheWorker::new(Arc::clone(&cache), Arc::clone(&invalidator), config);
1828
1829 worker.start().unwrap();
1831
1832 let key = CacheKey::new("test", "background");
1834 let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1835 cache.insert(key.clone(), vector.clone()).unwrap();
1836
1837 std::thread::sleep(Duration::from_millis(150));
1839
1840 worker.stop().unwrap();
1842
1843 assert!(cache.get(&key).is_some());
1845 }
1846
1847 #[test]
1848 fn test_cache_invalidation_by_tag() {
1849 let temp_dir = TempDir::new().unwrap();
1850
1851 let config = CacheConfig {
1852 max_memory_entries: 10,
1853 persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1854 enable_persistent: true,
1855 ..Default::default()
1856 };
1857
1858 let cache = Arc::new(MultiLevelCache::new(config).unwrap());
1859 let invalidator = Arc::new(CacheInvalidator::new(Arc::clone(&cache)));
1860
1861 let key1 = CacheKey::new("test", "tagged1");
1863 let key2 = CacheKey::new("test", "tagged2");
1864 let key3 = CacheKey::new("test", "tagged3");
1865
1866 let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1867
1868 cache.insert(key1.clone(), vector.clone()).unwrap();
1869 cache.insert(key2.clone(), vector.clone()).unwrap();
1870 cache.insert(key3.clone(), vector.clone()).unwrap();
1871
1872 let mut tags1 = HashMap::new();
1874 tags1.insert("category".to_string(), "embeddings".to_string());
1875 invalidator.register_entry(&key1, &tags1);
1876
1877 let mut tags2 = HashMap::new();
1878 tags2.insert("category".to_string(), "embeddings".to_string());
1879 invalidator.register_entry(&key2, &tags2);
1880
1881 let mut tags3 = HashMap::new();
1882 tags3.insert("category".to_string(), "vectors".to_string());
1883 invalidator.register_entry(&key3, &tags3);
1884
1885 let invalidated_count = invalidator
1887 .invalidate_by_tag("category", "embeddings")
1888 .unwrap();
1889 assert_eq!(invalidated_count, 2);
1890
1891 assert!(cache.get(&key1).is_none());
1893 assert!(cache.get(&key2).is_none());
1894
1895 assert!(cache.get(&key3).is_some());
1897 }
1898
1899 #[test]
1900 fn test_cache_invalidation_by_namespace() {
1901 let temp_dir = TempDir::new().unwrap();
1902
1903 let config = CacheConfig {
1904 max_memory_entries: 10,
1905 persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1906 enable_persistent: true,
1907 ..Default::default()
1908 };
1909
1910 let cache = Arc::new(MultiLevelCache::new(config).unwrap());
1911 let invalidator = Arc::new(CacheInvalidator::new(Arc::clone(&cache)));
1912
1913 let key1 = CacheKey::new("embeddings", "item1");
1915 let key2 = CacheKey::new("embeddings", "item2");
1916 let key3 = CacheKey::new("vectors", "item3");
1917
1918 let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1919
1920 cache.insert(key1.clone(), vector.clone()).unwrap();
1921 cache.insert(key2.clone(), vector.clone()).unwrap();
1922 cache.insert(key3.clone(), vector.clone()).unwrap();
1923
1924 invalidator.register_entry(&key1, &HashMap::new());
1926 invalidator.register_entry(&key2, &HashMap::new());
1927 invalidator.register_entry(&key3, &HashMap::new());
1928
1929 let invalidated_count = invalidator.invalidate_namespace("embeddings").unwrap();
1931 assert_eq!(invalidated_count, 2);
1932
1933 assert!(cache.get(&key1).is_none());
1935 assert!(cache.get(&key2).is_none());
1936
1937 assert!(cache.get(&key3).is_some());
1939 }
1940}