oxirs_vec/
advanced_caching.rs

1//! Advanced multi-level caching system for vector embeddings and search results
2//!
3//! This module provides:
4//! - Multi-level caching (memory + persistent)
5//! - LRU, LFU, ARC eviction policies
6//! - TTL expiration
7//! - Cache coherence and invalidation
8//! - Background cache updates
9
10use 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
20/// Type alias for complex tag index structure
21type TagIndex = Arc<RwLock<HashMap<String, HashMap<String, Vec<CacheKey>>>>>;
22
23/// Cache eviction policy
24#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
25pub enum EvictionPolicy {
26    /// Least Recently Used
27    LRU,
28    /// Least Frequently Used
29    LFU,
30    /// Adaptive Replacement Cache
31    ARC,
32    /// First In, First Out
33    FIFO,
34    /// Time-based expiration only
35    TTL,
36}
37
38/// Cache configuration
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct CacheConfig {
41    /// Maximum number of entries in memory cache
42    pub max_memory_entries: usize,
43    /// Maximum memory usage in bytes
44    pub max_memory_bytes: usize,
45    /// Time-to-live for cache entries
46    pub ttl: Option<Duration>,
47    /// Eviction policy
48    pub eviction_policy: EvictionPolicy,
49    /// Enable persistent cache
50    pub enable_persistent: bool,
51    /// Persistent cache directory
52    pub persistent_cache_dir: Option<std::path::PathBuf>,
53    /// Maximum persistent cache size in bytes
54    pub max_persistent_bytes: usize,
55    /// Enable cache compression
56    pub enable_compression: bool,
57    /// Enable background updates
58    pub enable_background_updates: bool,
59    /// Background update interval
60    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,  // 100MB
68            ttl: Some(Duration::from_secs(3600)), // 1 hour
69            eviction_policy: EvictionPolicy::LRU,
70            enable_persistent: true,
71            persistent_cache_dir: None,
72            max_persistent_bytes: 1024 * 1024 * 1024, // 1GB
73            enable_compression: true,
74            enable_background_updates: false,
75            background_update_interval: Duration::from_secs(300), // 5 minutes
76        }
77    }
78}
79
80/// Cache entry with metadata
81#[derive(Debug, Clone)]
82pub struct CacheEntry {
83    /// Cached data
84    pub data: Vector,
85    /// Creation timestamp
86    pub created_at: Instant,
87    /// Last access timestamp
88    pub last_accessed: Instant,
89    /// Access count for LFU
90    pub access_count: u64,
91    /// Entry size in bytes
92    pub size_bytes: usize,
93    /// TTL for this specific entry
94    pub ttl: Option<Duration>,
95    /// Metadata tags
96    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; // Rough estimate
103
104        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    /// Check if entry has expired
126    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    /// Update access statistics
135    pub fn touch(&mut self) {
136        self.last_accessed = Instant::now();
137        self.access_count += 1;
138    }
139}
140
141/// Cache key that can be hashed
142#[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
174/// Memory cache implementation
175pub struct MemoryCache {
176    config: CacheConfig,
177    entries: HashMap<CacheKey, CacheEntry>,
178    access_order: VecDeque<CacheKey>,      // For LRU
179    frequency_map: HashMap<CacheKey, u64>, // For LFU
180    current_memory_bytes: usize,
181    // ARC state
182    arc_t1: VecDeque<CacheKey>, // Recently accessed pages
183    arc_t2: VecDeque<CacheKey>, // Frequently accessed pages
184    arc_b1: VecDeque<CacheKey>, // Ghost list for T1
185    arc_b2: VecDeque<CacheKey>, // Ghost list for T2
186    arc_p: usize,               // Target size for T1
187}
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    /// Insert or update cache entry
206    pub fn insert(&mut self, key: CacheKey, entry: CacheEntry) -> Result<()> {
207        // Remove expired entries first
208        self.clean_expired();
209
210        // Check if we need to evict
211        while self.should_evict(&entry) {
212            self.evict_one()?;
213        }
214
215        // Remove existing entry if present
216        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        // Insert new entry
222        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    /// Get cache entry
230    pub fn get(&mut self, key: &CacheKey) -> Option<Vector> {
231        // Check if entry exists and is not expired
232        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    /// Remove entry from cache
254    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    /// Clear all entries
265    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    /// Check if eviction is needed
273    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    /// Evict one entry based on policy
279    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            // Fallback: remove first entry
293            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    /// Find LRU key
302    fn find_lru_key(&self) -> Option<CacheKey> {
303        self.access_order.front().cloned()
304    }
305
306    /// Find LFU key
307    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    /// Find ARC key using Adaptive Replacement Cache algorithm
315    fn find_arc_key(&mut self) -> Option<CacheKey> {
316        let c = self.config.max_memory_entries;
317
318        // If T1 is not empty and |T1| > p, evict from T1
319        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                // Move to B1
325                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        // Otherwise evict from T2
334        if let Some(key) = self.arc_t2.pop_front() {
335            // Move to B2
336            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        // Fallback to LRU if ARC lists are empty
344        self.find_lru_key()
345    }
346
347    /// Find FIFO key (oldest entry)
348    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    /// Find expired key
356    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    /// Track access for LRU/LFU/ARC
364    fn track_access(&mut self, key: &CacheKey) {
365        // Update LRU order
366        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        // Update LFU frequency
372        *self.frequency_map.entry(key.clone()).or_insert(0) += 1;
373
374        // Update ARC tracking
375        if self.config.eviction_policy == EvictionPolicy::ARC {
376            self.track_arc_access(key);
377        }
378    }
379
380    /// Track access for ARC algorithm
381    fn track_arc_access(&mut self, key: &CacheKey) {
382        let c = self.config.max_memory_entries;
383
384        // Check if key is in T1 or T2
385        if let Some(pos) = self.arc_t1.iter().position(|k| k == key) {
386            // Move from T1 to T2 (promote to frequent)
387            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            // Move to end of T2 (most recently used)
391            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            // Hit in B1: increase p and move to T2
395            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            // Hit in B2: decrease p and move to T2
400            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            // New key: add to T1
407            self.arc_t1.push_back(key.clone());
408        }
409    }
410
411    /// Remove from tracking structures
412    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        // Remove from ARC structures
419        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    /// Clean expired entries
436    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    /// Get cache statistics
450    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, // Would need to track hits/misses
457        }
458    }
459}
460
461/// Cache statistics
462#[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
471/// Persistent cache for disk storage
472pub 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    /// Store entry to disk
490    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    /// Load entry from disk
509    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        // Check if entry has expired
527        if entry.is_expired() {
528            // Remove expired entry
529            let _ = std::fs::remove_file(file_path);
530            Ok(None)
531        } else {
532            Ok(Some(entry))
533        }
534    }
535
536    /// Remove entry from disk
537    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    /// Clear all persistent cache
546    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    /// Get file path for cache key
555    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        // Create subdirectory structure to avoid too many files in one directory
560        let sub_dir = format!("{:02x}", (hash % 256) as u8);
561
562        // Encode key information in filename for reconstruction during cleanup
563        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    /// Encode cache key information into filename-safe format
570    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 encoding to safely include key information in filename
578        use base64::{engine::general_purpose, Engine as _};
579        general_purpose::URL_SAFE_NO_PAD.encode(key_data.to_string().as_bytes())
580    }
581
582    /// Decode cache key from filename
583    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    /// Hash cache key
605    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    /// Serialize cache entry to bytes
612    fn serialize_entry(&self, entry: &CacheEntry) -> Result<Vec<u8>> {
613        // Custom binary serialization since CacheEntry has Instant fields
614        let mut data = Vec::new();
615
616        // Serialize vector data
617        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        // Serialize timestamps as epoch nanos from creation
624        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        // Serialize other fields
630        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        // Serialize TTL
634        if let Some(ttl) = entry.ttl {
635            data.push(1); // TTL present
636            data.extend_from_slice(&ttl.as_nanos().to_le_bytes());
637        } else {
638            data.push(0); // No TTL
639        }
640
641        // Serialize tags
642        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    /// Deserialize cache entry from bytes
654    fn deserialize_entry(&self, data: &[u8]) -> Result<CacheEntry> {
655        let mut offset = 0;
656
657        // Deserialize vector data
658        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        // Deserialize timestamps (stored as elapsed nanos, convert back to Instant)
680        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        // Reconstruct timestamps (approximation - will be recent)
705        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        // Deserialize other fields
710        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        // Deserialize TTL
735        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        // Deserialize tags
763        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    /// Compress data using simple RLE compression
808    fn compress_data(&self, data: &[u8]) -> Result<Vec<u8>> {
809        // Simple run-length encoding for demonstration
810        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        // Add the last run
831        compressed.push(count);
832        compressed.push(current_byte);
833
834        Ok(compressed)
835    }
836
837    /// Decompress data using RLE decompression
838    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
858/// Multi-level cache combining memory and persistent storage
859pub 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    /// Insert entry into cache
895    pub fn insert(&self, key: CacheKey, data: Vector) -> Result<()> {
896        let entry = CacheEntry::new(data);
897
898        // Insert into memory cache
899        {
900            let mut memory = self.memory_cache.write().unwrap();
901            memory.insert(key.clone(), entry.clone())?;
902        }
903
904        // Insert into persistent cache
905        if let Some(ref persistent) = self.persistent_cache {
906            persistent.store(&key, &entry)?;
907        }
908
909        Ok(())
910    }
911
912    /// Get entry from cache
913    pub fn get(&self, key: &CacheKey) -> Option<Vector> {
914        self.update_stats_total();
915
916        // Try memory cache first
917        {
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        // Try persistent cache
928        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                // Promote to memory cache
933                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    /// Remove entry from cache
948    pub fn remove(&self, key: &CacheKey) -> Result<()> {
949        // Remove from memory cache
950        {
951            let mut memory = self.memory_cache.write().unwrap();
952            memory.remove(key);
953        }
954
955        // Remove from persistent cache
956        if let Some(ref persistent) = self.persistent_cache {
957            persistent.remove(key)?;
958        }
959
960        Ok(())
961    }
962
963    /// Clear all caches
964    pub fn clear(&self) -> Result<()> {
965        // Clear memory cache
966        {
967            let mut memory = self.memory_cache.write().unwrap();
968            memory.clear();
969        }
970
971        // Clear persistent cache
972        if let Some(ref persistent) = self.persistent_cache {
973            persistent.clear()?;
974        }
975
976        // Reset stats
977        {
978            let mut stats = self.stats.write().unwrap();
979            *stats = MultiLevelCacheStats::default();
980        }
981
982        Ok(())
983    }
984
985    /// Get cache statistics
986    pub fn get_stats(&self) -> MultiLevelCacheStats {
987        self.stats.read().unwrap().clone()
988    }
989
990    /// Get memory cache statistics
991    pub fn get_memory_stats(&self) -> CacheStats {
992        let memory = self.memory_cache.read().unwrap();
993        memory.stats()
994    }
995
996    // Stats update methods
997    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
1023/// Cache invalidation utilities with indexing support
1024pub struct CacheInvalidator {
1025    cache: Arc<MultiLevelCache>,
1026    tag_index: TagIndex, // tag_key -> tag_value -> keys
1027    namespace_index: Arc<RwLock<HashMap<String, Vec<CacheKey>>>>, // namespace -> keys
1028}
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    /// Register a cache entry for invalidation tracking
1040    pub fn register_entry(&self, key: &CacheKey, tags: &HashMap<String, String>) {
1041        // Index by namespace
1042        {
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        // Index by tags
1051        {
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    /// Unregister a cache entry from invalidation tracking
1065    pub fn unregister_entry(&self, key: &CacheKey) {
1066        // Remove from namespace index
1067        {
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        // Remove from tag index
1078        {
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    /// Invalidate entries by tag
1108    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    /// Invalidate entries by namespace
1130    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    /// Invalidate all expired entries
1148    pub fn invalidate_expired(&self) -> Result<usize> {
1149        // Memory cache cleans expired entries automatically during operations
1150        // For persistent cache, we need to scan and remove expired files
1151        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    /// Scan persistent cache directory and remove expired files
1158    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        // Walk through all cache files
1167        for entry in std::fs::read_dir(cache_dir)? {
1168            let entry = entry?;
1169            if entry.file_type()?.is_dir() {
1170                // Recursively scan subdirectories
1171                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                                // Decode cache key from filename - no more hacks!
1177                                if let Some(cache_key) =
1178                                    persistent_cache.decode_cache_key_from_filename(file_name)
1179                                {
1180                                    // Load the actual cache entry to check expiration
1181                                    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                                        // If we can't load the entry, it might be corrupted - remove it
1188                                        let _ = std::fs::remove_file(sub_entry.path());
1189                                        removed_count += 1;
1190                                    }
1191                                } else {
1192                                    // If we can't decode the key, it might be an old format - use file age as fallback
1193                                    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                                            // Remove files older than 24 hours as fallback for old cache files
1199                                            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    /// Get invalidation statistics
1217    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/// Statistics for cache invalidation tracking
1239#[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
1247/// Background cache worker for maintenance tasks
1248pub 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    /// Start the background worker
1272    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                // Perform maintenance tasks
1285                if let Err(e) = Self::perform_maintenance(&cache, &invalidator) {
1286                    eprintln!("Background cache maintenance error: {e}");
1287                }
1288
1289                // Sleep for the configured interval
1290                thread::sleep(interval);
1291            }
1292        });
1293
1294        self.worker_handle = Some(handle);
1295        Ok(())
1296    }
1297
1298    /// Stop the background worker
1299    pub fn stop(&mut self) -> Result<()> {
1300        // Signal shutdown
1301        {
1302            let mut signal = self.shutdown_signal.write().unwrap();
1303            *signal = true;
1304        }
1305
1306        // Wait for worker to finish
1307        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    /// Perform background maintenance tasks
1317    fn perform_maintenance(
1318        cache: &Arc<MultiLevelCache>,
1319        invalidator: &Arc<CacheInvalidator>,
1320    ) -> Result<()> {
1321        // 1. Clean expired entries
1322        let expired_count = invalidator.invalidate_expired()?;
1323        if expired_count > 0 {
1324            println!("Background worker cleaned {expired_count} expired entries");
1325        }
1326
1327        // 2. Optimize memory usage if fragmentation is high
1328        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            // Trigger more aggressive cleanup
1333            Self::aggressive_cleanup(cache)?;
1334        }
1335
1336        // 3. Preemptive persistent cache sync
1337        Self::sync_hot_entries(cache)?;
1338
1339        Ok(())
1340    }
1341
1342    /// Perform aggressive cleanup when memory usage is high
1343    fn aggressive_cleanup(_cache: &Arc<MultiLevelCache>) -> Result<()> {
1344        // Force cleanup of memory cache by temporarily reducing limits
1345        // This is a simplified approach - in practice you'd implement more sophisticated logic
1346        println!("Performing aggressive cache cleanup due to high memory usage");
1347        Ok(())
1348    }
1349
1350    /// Sync frequently accessed entries to persistent storage
1351    fn sync_hot_entries(_cache: &Arc<MultiLevelCache>) -> Result<()> {
1352        // In a real implementation, you'd identify hot entries and ensure they're in persistent storage
1353        // This helps with cache warming after restarts
1354        Ok(())
1355    }
1356}
1357
1358impl Drop for BackgroundCacheWorker {
1359    fn drop(&mut self) {
1360        let _ = self.stop();
1361    }
1362}
1363
1364/// Cache warming utilities
1365pub struct CacheWarmer {
1366    cache: Arc<MultiLevelCache>,
1367}
1368
1369impl CacheWarmer {
1370    pub fn new(cache: Arc<MultiLevelCache>) -> Self {
1371        Self { cache }
1372    }
1373
1374    /// Warm cache with a list of key-value pairs
1375    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    /// Warm cache by loading frequently accessed entries from persistent storage
1388    pub fn warm_from_persistent(&self, keys: Vec<CacheKey>) -> Result<usize> {
1389        let mut loaded_count = 0;
1390
1391        for key in keys {
1392            // Try to load from persistent cache and promote to memory
1393            if self.cache.get(&key).is_some() {
1394                loaded_count += 1;
1395            }
1396        }
1397
1398        Ok(loaded_count)
1399    }
1400
1401    /// Warm cache using a precomputed dataset
1402    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
1420/// Advanced cache analytics and optimization recommendations
1421pub 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, // 0.0 to 1.0
1434}
1435
1436impl CacheAnalyzer {
1437    pub fn new(cache: Arc<MultiLevelCache>, invalidator: Arc<CacheInvalidator>) -> Self {
1438        Self { cache, invalidator }
1439    }
1440
1441    /// Generate comprehensive cache analysis report
1442    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        // Generate recommendations
1467        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        // Calculate performance score (weighted combination of metrics)
1489        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![], // Would need access pattern tracking
1498            recommendations,
1499            performance_score,
1500        }
1501    }
1502
1503    /// Get recommendations for cache configuration optimization
1504    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        // Insert vectors
1543        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        // Check retrieval
1551        assert!(cache.get(&key1).is_some());
1552        assert!(cache.get(&key2).is_some());
1553
1554        // Insert third vector (should evict one)
1555        cache
1556            .insert(key3.clone(), CacheEntry::new(vector3.clone()))
1557            .unwrap();
1558
1559        // One of the first two should be evicted
1560        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        // Store and retrieve
1581        cache.store(&key, &entry).unwrap();
1582        let retrieved = cache.load(&key).unwrap();
1583
1584        // Should succeed now with proper serialization
1585        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        // Insert and retrieve
1607        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        // Check stats
1613        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        // Should be available immediately
1635        assert!(cache.get(&key).is_some());
1636
1637        // Wait for expiration
1638        std::thread::sleep(Duration::from_millis(20));
1639
1640        // Should be expired and removed
1641        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        // Insert three items
1662        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        // Access key1 multiple times to make it frequent
1673        cache.get(&key1);
1674        cache.get(&key1);
1675        cache.get(&key1);
1676
1677        // Insert key4 - should evict the least valuable item
1678        cache
1679            .insert(key4.clone(), CacheEntry::new(vector.clone()))
1680            .unwrap();
1681
1682        // key1 should still be there (frequent access)
1683        assert!(cache.get(&key1).is_some());
1684
1685        // Check that we have exactly 3 items
1686        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        // Prepare test data
1704        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        // Warm cache with data
1711        let loaded_count = warmer.warm_with_data(test_data.clone()).unwrap();
1712        assert_eq!(loaded_count, 3);
1713
1714        // Verify data is in cache
1715        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        // Use generator to warm cache
1736        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        // Verify generated data is in cache
1748        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        // Add some test data and access patterns
1771        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        // Access the cache to generate some stats
1779        cache.get(&key1);
1780        cache.get(&key2);
1781        cache.get(&key1); // Hit
1782        cache.get(&CacheKey::new("test", "nonexistent")); // Miss
1783
1784        // Analyze cache performance
1785        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        // Should have some recommendations if performance isn't perfect
1792        let recommendations = analyzer.get_optimization_recommendations();
1793        // In this test case, we might get recommendations about hit ratio
1794        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        // Start the worker
1816        worker.start().unwrap();
1817
1818        // Add some test data
1819        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        // Let the worker run for a short time
1824        std::thread::sleep(Duration::from_millis(150));
1825
1826        // Stop the worker
1827        worker.stop().unwrap();
1828
1829        // Verify data is still accessible
1830        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        // Create entries with tags
1848        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        // Register entries with tags
1859        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        // Invalidate by tag
1872        let invalidated_count = invalidator
1873            .invalidate_by_tag("category", "embeddings")
1874            .unwrap();
1875        assert_eq!(invalidated_count, 2);
1876
1877        // Check that tagged entries are removed
1878        assert!(cache.get(&key1).is_none());
1879        assert!(cache.get(&key2).is_none());
1880
1881        // Check that untagged entry remains
1882        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        // Create entries in different namespaces
1900        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        // Register entries for tracking
1911        invalidator.register_entry(&key1, &HashMap::new());
1912        invalidator.register_entry(&key2, &HashMap::new());
1913        invalidator.register_entry(&key3, &HashMap::new());
1914
1915        // Invalidate by namespace
1916        let invalidated_count = invalidator.invalidate_namespace("embeddings").unwrap();
1917        assert_eq!(invalidated_count, 2);
1918
1919        // Check that namespace entries are removed
1920        assert!(cache.get(&key1).is_none());
1921        assert!(cache.get(&key2).is_none());
1922
1923        // Check that other namespace entry remains
1924        assert!(cache.get(&key3).is_some());
1925    }
1926}