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
294                .entries
295                .keys()
296                .next()
297                .expect("entries should not be empty when at capacity")
298                .clone();
299            self.remove(&key);
300            Ok(())
301        } else {
302            Err(anyhow!("No entries to evict"))
303        }
304    }
305
306    /// Find LRU key
307    fn find_lru_key(&self) -> Option<CacheKey> {
308        self.access_order.front().cloned()
309    }
310
311    /// Find LFU key
312    fn find_lfu_key(&self) -> Option<CacheKey> {
313        self.frequency_map
314            .iter()
315            .min_by_key(|&(_, &freq)| freq)
316            .map(|(key, _)| key.clone())
317    }
318
319    /// Find ARC key using Adaptive Replacement Cache algorithm
320    fn find_arc_key(&mut self) -> Option<CacheKey> {
321        let c = self.config.max_memory_entries;
322
323        // If T1 is not empty and |T1| > p, evict from T1
324        if !self.arc_t1.is_empty()
325            && (self.arc_t1.len() > self.arc_p
326                || (self.arc_t2.is_empty() && self.arc_t1.len() == self.arc_p))
327        {
328            if let Some(key) = self.arc_t1.pop_front() {
329                // Move to B1
330                self.arc_b1.push_back(key.clone());
331                if self.arc_b1.len() > c {
332                    self.arc_b1.pop_front();
333                }
334                return Some(key);
335            }
336        }
337
338        // Otherwise evict from T2
339        if let Some(key) = self.arc_t2.pop_front() {
340            // Move to B2
341            self.arc_b2.push_back(key.clone());
342            if self.arc_b2.len() > c {
343                self.arc_b2.pop_front();
344            }
345            return Some(key);
346        }
347
348        // Fallback to LRU if ARC lists are empty
349        self.find_lru_key()
350    }
351
352    /// Find FIFO key (oldest entry)
353    fn find_fifo_key(&self) -> Option<CacheKey> {
354        self.entries
355            .iter()
356            .min_by_key(|(_, entry)| entry.created_at)
357            .map(|(key, _)| key.clone())
358    }
359
360    /// Find expired key
361    fn find_expired_key(&self) -> Option<CacheKey> {
362        self.entries
363            .iter()
364            .find(|(_, entry)| entry.is_expired())
365            .map(|(key, _)| key.clone())
366    }
367
368    /// Track access for LRU/LFU/ARC
369    fn track_access(&mut self, key: &CacheKey) {
370        // Update LRU order
371        if let Some(pos) = self.access_order.iter().position(|k| k == key) {
372            self.access_order.remove(pos);
373        }
374        self.access_order.push_back(key.clone());
375
376        // Update LFU frequency
377        *self.frequency_map.entry(key.clone()).or_insert(0) += 1;
378
379        // Update ARC tracking
380        if self.config.eviction_policy == EvictionPolicy::ARC {
381            self.track_arc_access(key);
382        }
383    }
384
385    /// Track access for ARC algorithm
386    fn track_arc_access(&mut self, key: &CacheKey) {
387        let c = self.config.max_memory_entries;
388
389        // Check if key is in T1 or T2
390        if let Some(pos) = self.arc_t1.iter().position(|k| k == key) {
391            // Move from T1 to T2 (promote to frequent)
392            self.arc_t1.remove(pos);
393            self.arc_t2.push_back(key.clone());
394        } else if let Some(pos) = self.arc_t2.iter().position(|k| k == key) {
395            // Move to end of T2 (most recently used)
396            self.arc_t2.remove(pos);
397            self.arc_t2.push_back(key.clone());
398        } else if let Some(pos) = self.arc_b1.iter().position(|k| k == key) {
399            // Hit in B1: increase p and move to T2
400            self.arc_b1.remove(pos);
401            self.arc_p = (self.arc_p + 1.max(self.arc_b2.len() / self.arc_b1.len())).min(c);
402            self.arc_t2.push_back(key.clone());
403        } else if let Some(pos) = self.arc_b2.iter().position(|k| k == key) {
404            // Hit in B2: decrease p and move to T2
405            self.arc_b2.remove(pos);
406            self.arc_p = self
407                .arc_p
408                .saturating_sub(1.max(self.arc_b1.len() / self.arc_b2.len()));
409            self.arc_t2.push_back(key.clone());
410        } else {
411            // New key: add to T1
412            self.arc_t1.push_back(key.clone());
413        }
414    }
415
416    /// Remove from tracking structures
417    fn remove_from_tracking(&mut self, key: &CacheKey) {
418        if let Some(pos) = self.access_order.iter().position(|k| k == key) {
419            self.access_order.remove(pos);
420        }
421        self.frequency_map.remove(key);
422
423        // Remove from ARC structures
424        if self.config.eviction_policy == EvictionPolicy::ARC {
425            if let Some(pos) = self.arc_t1.iter().position(|k| k == key) {
426                self.arc_t1.remove(pos);
427            }
428            if let Some(pos) = self.arc_t2.iter().position(|k| k == key) {
429                self.arc_t2.remove(pos);
430            }
431            if let Some(pos) = self.arc_b1.iter().position(|k| k == key) {
432                self.arc_b1.remove(pos);
433            }
434            if let Some(pos) = self.arc_b2.iter().position(|k| k == key) {
435                self.arc_b2.remove(pos);
436            }
437        }
438    }
439
440    /// Clean expired entries
441    fn clean_expired(&mut self) {
442        let expired_keys: Vec<CacheKey> = self
443            .entries
444            .iter()
445            .filter(|(_, entry)| entry.is_expired())
446            .map(|(key, _)| key.clone())
447            .collect();
448
449        for key in expired_keys {
450            self.remove(&key);
451        }
452    }
453
454    /// Get cache statistics
455    pub fn stats(&self) -> CacheStats {
456        CacheStats {
457            entries: self.entries.len(),
458            memory_bytes: self.current_memory_bytes,
459            max_entries: self.config.max_memory_entries,
460            max_memory_bytes: self.config.max_memory_bytes,
461            hit_ratio: 0.0, // Would need to track hits/misses
462        }
463    }
464}
465
466/// Cache statistics
467#[derive(Debug, Clone, Serialize, Deserialize)]
468pub struct CacheStats {
469    pub entries: usize,
470    pub memory_bytes: usize,
471    pub max_entries: usize,
472    pub max_memory_bytes: usize,
473    pub hit_ratio: f32,
474}
475
476/// Persistent cache for disk storage
477pub struct PersistentCache {
478    config: CacheConfig,
479    cache_dir: std::path::PathBuf,
480}
481
482impl PersistentCache {
483    pub fn new(config: CacheConfig) -> Result<Self> {
484        let cache_dir = config
485            .persistent_cache_dir
486            .clone()
487            .unwrap_or_else(|| std::env::temp_dir().join("oxirs_vec_cache"));
488
489        std::fs::create_dir_all(&cache_dir)?;
490
491        Ok(Self { config, cache_dir })
492    }
493
494    /// Store entry to disk
495    pub fn store(&self, key: &CacheKey, entry: &CacheEntry) -> Result<()> {
496        let file_path = self.get_file_path(key);
497
498        if let Some(parent) = file_path.parent() {
499            std::fs::create_dir_all(parent)?;
500        }
501
502        let serialized = self.serialize_entry(entry)?;
503        let final_data = if self.config.enable_compression {
504            self.compress_data(&serialized)?
505        } else {
506            serialized
507        };
508
509        std::fs::write(file_path, final_data)?;
510        Ok(())
511    }
512
513    /// Load entry from disk
514    pub fn load(&self, key: &CacheKey) -> Result<Option<CacheEntry>> {
515        let file_path = self.get_file_path(key);
516
517        if !file_path.exists() {
518            return Ok(None);
519        }
520
521        let data = std::fs::read(&file_path)?;
522
523        let decompressed = if self.config.enable_compression {
524            self.decompress_data(&data)?
525        } else {
526            data
527        };
528
529        let entry = self.deserialize_entry(&decompressed)?;
530
531        // Check if entry has expired
532        if entry.is_expired() {
533            // Remove expired entry
534            let _ = std::fs::remove_file(file_path);
535            Ok(None)
536        } else {
537            Ok(Some(entry))
538        }
539    }
540
541    /// Remove entry from disk
542    pub fn remove(&self, key: &CacheKey) -> Result<()> {
543        let file_path = self.get_file_path(key);
544        if file_path.exists() {
545            std::fs::remove_file(file_path)?;
546        }
547        Ok(())
548    }
549
550    /// Clear all persistent cache
551    pub fn clear(&self) -> Result<()> {
552        if self.cache_dir.exists() {
553            std::fs::remove_dir_all(&self.cache_dir)?;
554            std::fs::create_dir_all(&self.cache_dir)?;
555        }
556        Ok(())
557    }
558
559    /// Get file path for cache key
560    fn get_file_path(&self, key: &CacheKey) -> std::path::PathBuf {
561        let key_str = key.to_string();
562        let hash = self.hash_key(&key_str);
563
564        // Create subdirectory structure to avoid too many files in one directory
565        let sub_dir = format!("{:02x}", (hash % 256) as u8);
566
567        // Encode key information in filename for reconstruction during cleanup
568        let encoded_key = self.encode_cache_key_for_filename(key);
569        let filename = format!("{hash:016x}_{encoded_key}.cache");
570
571        self.cache_dir.join(sub_dir).join(filename)
572    }
573
574    /// Encode cache key information into filename-safe format
575    fn encode_cache_key_for_filename(&self, key: &CacheKey) -> String {
576        let key_data = serde_json::json!({
577            "namespace": key.namespace,
578            "key": key.key,
579            "variant": key.variant
580        });
581
582        // Use base64 encoding to safely include key information in filename
583        use base64::{engine::general_purpose, Engine as _};
584        general_purpose::URL_SAFE_NO_PAD.encode(key_data.to_string().as_bytes())
585    }
586
587    /// Decode cache key from filename
588    fn decode_cache_key_from_filename(&self, filename: &str) -> Option<CacheKey> {
589        if let Some(encoded_part) = filename
590            .strip_suffix(".cache")
591            .and_then(|s| s.split('_').nth(1))
592        {
593            use base64::{engine::general_purpose, Engine as _};
594            if let Ok(decoded_bytes) = general_purpose::URL_SAFE_NO_PAD.decode(encoded_part) {
595                if let Ok(decoded_str) = String::from_utf8(decoded_bytes) {
596                    if let Ok(key_data) = serde_json::from_str::<serde_json::Value>(&decoded_str) {
597                        return Some(CacheKey {
598                            namespace: key_data["namespace"].as_str()?.to_string(),
599                            key: key_data["key"].as_str()?.to_string(),
600                            variant: key_data["variant"].as_str().map(|s| s.to_string()),
601                        });
602                    }
603                }
604            }
605        }
606        None
607    }
608
609    /// Hash cache key
610    fn hash_key(&self, key: &str) -> u64 {
611        let mut hasher = std::collections::hash_map::DefaultHasher::new();
612        key.hash(&mut hasher);
613        hasher.finish()
614    }
615
616    /// Serialize cache entry to bytes
617    fn serialize_entry(&self, entry: &CacheEntry) -> Result<Vec<u8>> {
618        // Custom binary serialization since CacheEntry has Instant fields
619        let mut data = Vec::new();
620
621        // Serialize vector data
622        let vector_data = &entry.data.as_f32();
623        data.extend_from_slice(&(vector_data.len() as u32).to_le_bytes());
624        for &value in vector_data {
625            data.extend_from_slice(&value.to_le_bytes());
626        }
627
628        // Serialize timestamps as epoch nanos from creation
629        let created_nanos = entry.created_at.elapsed().as_nanos() as u64;
630        let accessed_nanos = entry.last_accessed.elapsed().as_nanos() as u64;
631        data.extend_from_slice(&created_nanos.to_le_bytes());
632        data.extend_from_slice(&accessed_nanos.to_le_bytes());
633
634        // Serialize other fields
635        data.extend_from_slice(&entry.access_count.to_le_bytes());
636        data.extend_from_slice(&(entry.size_bytes as u64).to_le_bytes());
637
638        // Serialize TTL
639        if let Some(ttl) = entry.ttl {
640            data.push(1); // TTL present
641            data.extend_from_slice(&ttl.as_nanos().to_le_bytes());
642        } else {
643            data.push(0); // No TTL
644        }
645
646        // Serialize tags
647        data.extend_from_slice(&(entry.tags.len() as u32).to_le_bytes());
648        for (key, value) in &entry.tags {
649            data.extend_from_slice(&(key.len() as u32).to_le_bytes());
650            data.extend_from_slice(key.as_bytes());
651            data.extend_from_slice(&(value.len() as u32).to_le_bytes());
652            data.extend_from_slice(value.as_bytes());
653        }
654
655        Ok(data)
656    }
657
658    /// Deserialize cache entry from bytes
659    fn deserialize_entry(&self, data: &[u8]) -> Result<CacheEntry> {
660        // Check if data is empty or too small
661        if data.len() < 4 {
662            return Err(anyhow::anyhow!(
663                "Invalid cache entry data: too small (expected at least 4 bytes, got {})",
664                data.len()
665            ));
666        }
667
668        let mut offset = 0;
669
670        // Deserialize vector data
671        let vector_len = u32::from_le_bytes([
672            data[offset],
673            data[offset + 1],
674            data[offset + 2],
675            data[offset + 3],
676        ]) as usize;
677        offset += 4;
678
679        let mut vector_data = Vec::with_capacity(vector_len);
680        for _ in 0..vector_len {
681            let value = f32::from_le_bytes([
682                data[offset],
683                data[offset + 1],
684                data[offset + 2],
685                data[offset + 3],
686            ]);
687            vector_data.push(value);
688            offset += 4;
689        }
690        let vector = Vector::new(vector_data);
691
692        // Deserialize timestamps (stored as elapsed nanos, convert back to Instant)
693        let created_nanos = u64::from_le_bytes([
694            data[offset],
695            data[offset + 1],
696            data[offset + 2],
697            data[offset + 3],
698            data[offset + 4],
699            data[offset + 5],
700            data[offset + 6],
701            data[offset + 7],
702        ]);
703        offset += 8;
704
705        let accessed_nanos = u64::from_le_bytes([
706            data[offset],
707            data[offset + 1],
708            data[offset + 2],
709            data[offset + 3],
710            data[offset + 4],
711            data[offset + 5],
712            data[offset + 6],
713            data[offset + 7],
714        ]);
715        offset += 8;
716
717        // Reconstruct timestamps (approximation - will be recent)
718        let now = Instant::now();
719        let created_at = now - Duration::from_nanos(created_nanos);
720        let last_accessed = now - Duration::from_nanos(accessed_nanos);
721
722        // Deserialize other fields
723        let access_count = u64::from_le_bytes([
724            data[offset],
725            data[offset + 1],
726            data[offset + 2],
727            data[offset + 3],
728            data[offset + 4],
729            data[offset + 5],
730            data[offset + 6],
731            data[offset + 7],
732        ]);
733        offset += 8;
734
735        let size_bytes = u64::from_le_bytes([
736            data[offset],
737            data[offset + 1],
738            data[offset + 2],
739            data[offset + 3],
740            data[offset + 4],
741            data[offset + 5],
742            data[offset + 6],
743            data[offset + 7],
744        ]) as usize;
745        offset += 8;
746
747        // Deserialize TTL
748        let ttl = if data[offset] == 1 {
749            offset += 1;
750            let ttl_nanos = u128::from_le_bytes([
751                data[offset],
752                data[offset + 1],
753                data[offset + 2],
754                data[offset + 3],
755                data[offset + 4],
756                data[offset + 5],
757                data[offset + 6],
758                data[offset + 7],
759                data[offset + 8],
760                data[offset + 9],
761                data[offset + 10],
762                data[offset + 11],
763                data[offset + 12],
764                data[offset + 13],
765                data[offset + 14],
766                data[offset + 15],
767            ]);
768            offset += 16;
769            Some(Duration::from_nanos(ttl_nanos as u64))
770        } else {
771            offset += 1;
772            None
773        };
774
775        // Deserialize tags
776        let tags_len = u32::from_le_bytes([
777            data[offset],
778            data[offset + 1],
779            data[offset + 2],
780            data[offset + 3],
781        ]) as usize;
782        offset += 4;
783
784        let mut tags = HashMap::new();
785        for _ in 0..tags_len {
786            let key_len = u32::from_le_bytes([
787                data[offset],
788                data[offset + 1],
789                data[offset + 2],
790                data[offset + 3],
791            ]) as usize;
792            offset += 4;
793            let key = String::from_utf8(data[offset..offset + key_len].to_vec())?;
794            offset += key_len;
795
796            let value_len = u32::from_le_bytes([
797                data[offset],
798                data[offset + 1],
799                data[offset + 2],
800                data[offset + 3],
801            ]) as usize;
802            offset += 4;
803            let value = String::from_utf8(data[offset..offset + value_len].to_vec())?;
804            offset += value_len;
805
806            tags.insert(key, value);
807        }
808
809        Ok(CacheEntry {
810            data: vector,
811            created_at,
812            last_accessed,
813            access_count,
814            size_bytes,
815            ttl,
816            tags,
817        })
818    }
819
820    /// Compress data using simple RLE compression
821    fn compress_data(&self, data: &[u8]) -> Result<Vec<u8>> {
822        // Simple run-length encoding for demonstration
823        let mut compressed = Vec::new();
824
825        if data.is_empty() {
826            return Ok(compressed);
827        }
828
829        let mut current_byte = data[0];
830        let mut count = 1u8;
831
832        for &byte in &data[1..] {
833            if byte == current_byte && count < 255 {
834                count += 1;
835            } else {
836                compressed.push(count);
837                compressed.push(current_byte);
838                current_byte = byte;
839                count = 1;
840            }
841        }
842
843        // Add the last run
844        compressed.push(count);
845        compressed.push(current_byte);
846
847        Ok(compressed)
848    }
849
850    /// Decompress data using RLE decompression
851    fn decompress_data(&self, data: &[u8]) -> Result<Vec<u8>> {
852        let mut decompressed = Vec::new();
853
854        if data.len() % 2 != 0 {
855            return Err(anyhow!("Invalid compressed data length"));
856        }
857
858        for chunk in data.chunks(2) {
859            let count = chunk[0];
860            let byte = chunk[1];
861
862            for _ in 0..count {
863                decompressed.push(byte);
864            }
865        }
866
867        Ok(decompressed)
868    }
869}
870
871/// Multi-level cache combining memory and persistent storage
872pub struct MultiLevelCache {
873    memory_cache: Arc<RwLock<MemoryCache>>,
874    persistent_cache: Option<Arc<PersistentCache>>,
875    #[allow(dead_code)]
876    config: CacheConfig,
877    stats: Arc<RwLock<MultiLevelCacheStats>>,
878}
879
880#[derive(Debug, Default, Clone)]
881pub struct MultiLevelCacheStats {
882    pub memory_hits: u64,
883    pub memory_misses: u64,
884    pub persistent_hits: u64,
885    pub persistent_misses: u64,
886    pub total_requests: u64,
887}
888
889impl MultiLevelCache {
890    pub fn new(config: CacheConfig) -> Result<Self> {
891        let memory_cache = Arc::new(RwLock::new(MemoryCache::new(config.clone())));
892
893        let persistent_cache = if config.enable_persistent {
894            Some(Arc::new(PersistentCache::new(config.clone())?))
895        } else {
896            None
897        };
898
899        Ok(Self {
900            memory_cache,
901            persistent_cache,
902            config,
903            stats: Arc::new(RwLock::new(MultiLevelCacheStats::default())),
904        })
905    }
906
907    /// Insert entry into cache
908    pub fn insert(&self, key: CacheKey, data: Vector) -> Result<()> {
909        let entry = CacheEntry::new(data);
910
911        // Insert into memory cache
912        {
913            let mut memory = self.memory_cache.write().expect("lock poisoned");
914            memory.insert(key.clone(), entry.clone())?;
915        }
916
917        // Insert into persistent cache
918        if let Some(ref persistent) = self.persistent_cache {
919            persistent.store(&key, &entry)?;
920        }
921
922        Ok(())
923    }
924
925    /// Get entry from cache
926    pub fn get(&self, key: &CacheKey) -> Option<Vector> {
927        self.update_stats_total();
928
929        // Try memory cache first
930        {
931            let mut memory = self.memory_cache.write().expect("lock poisoned");
932            if let Some(data) = memory.get(key) {
933                self.update_stats_memory_hit();
934                return Some(data.clone());
935            }
936        }
937
938        self.update_stats_memory_miss();
939
940        // Try persistent cache
941        if let Some(ref persistent) = self.persistent_cache {
942            if let Ok(Some(mut entry)) = persistent.load(key) {
943                self.update_stats_persistent_hit();
944
945                // Promote to memory cache
946                let data = entry.data.clone();
947                entry.touch();
948                if let Ok(mut memory) = self.memory_cache.write() {
949                    let _ = memory.insert(key.clone(), entry);
950                }
951
952                return Some(data);
953            }
954        }
955
956        self.update_stats_persistent_miss();
957        None
958    }
959
960    /// Remove entry from cache
961    pub fn remove(&self, key: &CacheKey) -> Result<()> {
962        // Remove from memory cache
963        {
964            let mut memory = self.memory_cache.write().expect("lock poisoned");
965            memory.remove(key);
966        }
967
968        // Remove from persistent cache
969        if let Some(ref persistent) = self.persistent_cache {
970            persistent.remove(key)?;
971        }
972
973        Ok(())
974    }
975
976    /// Clear all caches
977    pub fn clear(&self) -> Result<()> {
978        // Clear memory cache
979        {
980            let mut memory = self.memory_cache.write().expect("lock poisoned");
981            memory.clear();
982        }
983
984        // Clear persistent cache
985        if let Some(ref persistent) = self.persistent_cache {
986            persistent.clear()?;
987        }
988
989        // Reset stats
990        {
991            let mut stats = self.stats.write().expect("lock poisoned");
992            *stats = MultiLevelCacheStats::default();
993        }
994
995        Ok(())
996    }
997
998    /// Get cache statistics
999    pub fn get_stats(&self) -> MultiLevelCacheStats {
1000        self.stats.read().expect("lock poisoned").clone()
1001    }
1002
1003    /// Get memory cache statistics
1004    pub fn get_memory_stats(&self) -> CacheStats {
1005        let memory = self.memory_cache.read().expect("lock poisoned");
1006        memory.stats()
1007    }
1008
1009    // Stats update methods
1010    fn update_stats_total(&self) {
1011        let mut stats = self.stats.write().expect("lock poisoned");
1012        stats.total_requests += 1;
1013    }
1014
1015    fn update_stats_memory_hit(&self) {
1016        let mut stats = self.stats.write().expect("lock poisoned");
1017        stats.memory_hits += 1;
1018    }
1019
1020    fn update_stats_memory_miss(&self) {
1021        let mut stats = self.stats.write().expect("lock poisoned");
1022        stats.memory_misses += 1;
1023    }
1024
1025    fn update_stats_persistent_hit(&self) {
1026        let mut stats = self.stats.write().expect("lock poisoned");
1027        stats.persistent_hits += 1;
1028    }
1029
1030    fn update_stats_persistent_miss(&self) {
1031        let mut stats = self.stats.write().expect("lock poisoned");
1032        stats.persistent_misses += 1;
1033    }
1034}
1035
1036/// Cache invalidation utilities with indexing support
1037pub struct CacheInvalidator {
1038    cache: Arc<MultiLevelCache>,
1039    tag_index: TagIndex, // tag_key -> tag_value -> keys
1040    namespace_index: Arc<RwLock<HashMap<String, Vec<CacheKey>>>>, // namespace -> keys
1041}
1042
1043impl CacheInvalidator {
1044    pub fn new(cache: Arc<MultiLevelCache>) -> Self {
1045        Self {
1046            cache,
1047            tag_index: Arc::new(RwLock::new(HashMap::new())),
1048            namespace_index: Arc::new(RwLock::new(HashMap::new())),
1049        }
1050    }
1051
1052    /// Register a cache entry for invalidation tracking
1053    pub fn register_entry(&self, key: &CacheKey, tags: &HashMap<String, String>) {
1054        // Index by namespace
1055        {
1056            let mut ns_index = self.namespace_index.write().expect("lock poisoned");
1057            ns_index
1058                .entry(key.namespace.clone())
1059                .or_default()
1060                .push(key.clone());
1061        }
1062
1063        // Index by tags
1064        {
1065            let mut tag_idx = self.tag_index.write().expect("lock poisoned");
1066            for (tag_key, tag_value) in tags {
1067                tag_idx
1068                    .entry(tag_key.clone())
1069                    .or_default()
1070                    .entry(tag_value.clone())
1071                    .or_default()
1072                    .push(key.clone());
1073            }
1074        }
1075    }
1076
1077    /// Unregister a cache entry from invalidation tracking
1078    pub fn unregister_entry(&self, key: &CacheKey) {
1079        // Remove from namespace index
1080        {
1081            let mut ns_index = self.namespace_index.write().expect("lock poisoned");
1082            if let Some(keys) = ns_index.get_mut(&key.namespace) {
1083                keys.retain(|k| k != key);
1084                if keys.is_empty() {
1085                    ns_index.remove(&key.namespace);
1086                }
1087            }
1088        }
1089
1090        // Remove from tag index
1091        {
1092            let mut tag_idx = self.tag_index.write().expect("lock poisoned");
1093            let mut tags_to_remove = Vec::new();
1094
1095            for (tag_key, tag_values) in tag_idx.iter_mut() {
1096                let mut values_to_remove = Vec::new();
1097
1098                for (tag_value, keys) in tag_values.iter_mut() {
1099                    keys.retain(|k| k != key);
1100                    if keys.is_empty() {
1101                        values_to_remove.push(tag_value.clone());
1102                    }
1103                }
1104
1105                for value in values_to_remove {
1106                    tag_values.remove(&value);
1107                }
1108
1109                if tag_values.is_empty() {
1110                    tags_to_remove.push(tag_key.clone());
1111                }
1112            }
1113
1114            for tag in tags_to_remove {
1115                tag_idx.remove(&tag);
1116            }
1117        }
1118    }
1119
1120    /// Invalidate entries by tag
1121    pub fn invalidate_by_tag(&self, tag_key: &str, tag_value: &str) -> Result<usize> {
1122        let keys_to_invalidate = {
1123            let tag_idx = self.tag_index.read().expect("lock poisoned");
1124            tag_idx
1125                .get(tag_key)
1126                .and_then(|values| values.get(tag_value))
1127                .cloned()
1128                .unwrap_or_default()
1129        };
1130
1131        let mut invalidated_count = 0;
1132        for key in &keys_to_invalidate {
1133            if self.cache.remove(key).is_ok() {
1134                invalidated_count += 1;
1135            }
1136            self.unregister_entry(key);
1137        }
1138
1139        Ok(invalidated_count)
1140    }
1141
1142    /// Invalidate entries by namespace
1143    pub fn invalidate_namespace(&self, namespace: &str) -> Result<usize> {
1144        let keys_to_invalidate = {
1145            let ns_index = self.namespace_index.read().expect("lock poisoned");
1146            ns_index.get(namespace).cloned().unwrap_or_default()
1147        };
1148
1149        let mut invalidated_count = 0;
1150        for key in &keys_to_invalidate {
1151            if self.cache.remove(key).is_ok() {
1152                invalidated_count += 1;
1153            }
1154            self.unregister_entry(key);
1155        }
1156
1157        Ok(invalidated_count)
1158    }
1159
1160    /// Invalidate all expired entries
1161    pub fn invalidate_expired(&self) -> Result<usize> {
1162        // Memory cache cleans expired entries automatically during operations
1163        // For persistent cache, we need to scan and remove expired files
1164        if let Some(ref persistent) = self.cache.persistent_cache {
1165            return self.scan_and_remove_expired_files(persistent);
1166        }
1167        Ok(0)
1168    }
1169
1170    /// Scan persistent cache directory and remove expired files
1171    fn scan_and_remove_expired_files(&self, persistent_cache: &PersistentCache) -> Result<usize> {
1172        let cache_dir = &persistent_cache.cache_dir;
1173        let mut removed_count = 0;
1174
1175        if !cache_dir.exists() {
1176            return Ok(0);
1177        }
1178
1179        // Walk through all cache files
1180        for entry in std::fs::read_dir(cache_dir)? {
1181            let entry = entry?;
1182            if entry.file_type()?.is_dir() {
1183                // Recursively scan subdirectories
1184                for sub_entry in std::fs::read_dir(entry.path())? {
1185                    let sub_entry = sub_entry?;
1186                    if sub_entry.file_type()?.is_file() {
1187                        if let Some(file_name) = sub_entry.file_name().to_str() {
1188                            if file_name.ends_with(".cache") {
1189                                // Decode cache key from filename - no more hacks!
1190                                if let Some(cache_key) =
1191                                    persistent_cache.decode_cache_key_from_filename(file_name)
1192                                {
1193                                    // Load the actual cache entry to check expiration
1194                                    if let Ok(Some(entry)) = persistent_cache.load(&cache_key) {
1195                                        if entry.is_expired() {
1196                                            let _ = std::fs::remove_file(sub_entry.path());
1197                                            removed_count += 1;
1198                                        }
1199                                    } else {
1200                                        // If we can't load the entry, it might be corrupted - remove it
1201                                        let _ = std::fs::remove_file(sub_entry.path());
1202                                        removed_count += 1;
1203                                    }
1204                                } else {
1205                                    // If we can't decode the key, it might be an old format - use file age as fallback
1206                                    if let Ok(metadata) = std::fs::metadata(sub_entry.path()) {
1207                                        if let Ok(modified) = metadata.modified() {
1208                                            let age = modified
1209                                                .elapsed()
1210                                                .unwrap_or(Duration::from_secs(0));
1211                                            // Remove files older than 24 hours as fallback for old cache files
1212                                            if age > Duration::from_secs(24 * 3600) {
1213                                                let _ = std::fs::remove_file(sub_entry.path());
1214                                                removed_count += 1;
1215                                            }
1216                                        }
1217                                    }
1218                                }
1219                            }
1220                        }
1221                    }
1222                }
1223            }
1224        }
1225
1226        Ok(removed_count)
1227    }
1228
1229    /// Get invalidation statistics
1230    pub fn get_stats(&self) -> InvalidationStats {
1231        let tag_idx = self.tag_index.read().expect("lock poisoned");
1232        let ns_index = self.namespace_index.read().expect("lock poisoned");
1233
1234        let total_tag_entries = tag_idx
1235            .values()
1236            .flat_map(|values| values.values())
1237            .map(|keys| keys.len())
1238            .sum();
1239
1240        let total_namespace_entries = ns_index.values().map(|keys| keys.len()).sum();
1241
1242        InvalidationStats {
1243            tracked_tags: tag_idx.len(),
1244            tracked_namespaces: ns_index.len(),
1245            total_tag_entries,
1246            total_namespace_entries,
1247        }
1248    }
1249}
1250
1251/// Statistics for cache invalidation tracking
1252#[derive(Debug, Clone)]
1253pub struct InvalidationStats {
1254    pub tracked_tags: usize,
1255    pub tracked_namespaces: usize,
1256    pub total_tag_entries: usize,
1257    pub total_namespace_entries: usize,
1258}
1259
1260/// Background cache worker for maintenance tasks
1261pub struct BackgroundCacheWorker {
1262    cache: Arc<MultiLevelCache>,
1263    invalidator: Arc<CacheInvalidator>,
1264    config: CacheConfig,
1265    worker_handle: Option<JoinHandle<()>>,
1266    shutdown_signal: Arc<RwLock<bool>>,
1267}
1268
1269impl BackgroundCacheWorker {
1270    pub fn new(
1271        cache: Arc<MultiLevelCache>,
1272        invalidator: Arc<CacheInvalidator>,
1273        config: CacheConfig,
1274    ) -> Self {
1275        Self {
1276            cache,
1277            invalidator,
1278            config,
1279            worker_handle: None,
1280            shutdown_signal: Arc::new(RwLock::new(false)),
1281        }
1282    }
1283
1284    /// Start the background worker
1285    pub fn start(&mut self) -> Result<()> {
1286        if !self.config.enable_background_updates {
1287            return Ok(());
1288        }
1289
1290        let cache = Arc::clone(&self.cache);
1291        let invalidator = Arc::clone(&self.invalidator);
1292        let interval = self.config.background_update_interval;
1293        let shutdown_signal = Arc::clone(&self.shutdown_signal);
1294
1295        let handle = thread::spawn(move || {
1296            while let Ok(shutdown) = shutdown_signal.read() {
1297                if *shutdown {
1298                    break;
1299                }
1300                drop(shutdown); // Release the lock before sleeping
1301
1302                // Perform maintenance tasks
1303                if let Err(e) = Self::perform_maintenance(&cache, &invalidator) {
1304                    // Log error but continue running
1305                    tracing::warn!("Background cache maintenance error: {}", e);
1306                }
1307
1308                // Sleep for the configured interval
1309                thread::sleep(interval);
1310            }
1311        });
1312
1313        self.worker_handle = Some(handle);
1314        Ok(())
1315    }
1316
1317    /// Stop the background worker
1318    pub fn stop(&mut self) -> Result<()> {
1319        // Signal shutdown
1320        {
1321            let mut signal = self.shutdown_signal.write().expect("lock poisoned");
1322            *signal = true;
1323        }
1324
1325        // Wait for worker to finish
1326        if let Some(handle) = self.worker_handle.take() {
1327            handle
1328                .join()
1329                .map_err(|e| anyhow!("Failed to join worker thread: {:?}", e))?;
1330        }
1331
1332        Ok(())
1333    }
1334
1335    /// Perform background maintenance tasks
1336    fn perform_maintenance(
1337        cache: &Arc<MultiLevelCache>,
1338        invalidator: &Arc<CacheInvalidator>,
1339    ) -> Result<()> {
1340        // 1. Clean expired entries
1341        let expired_count = invalidator.invalidate_expired()?;
1342        if expired_count > 0 {
1343            println!("Background worker cleaned {expired_count} expired entries");
1344        }
1345
1346        // 2. Optimize memory usage if fragmentation is high
1347        let memory_stats = cache.get_memory_stats();
1348        let utilization = memory_stats.memory_bytes as f64 / memory_stats.max_memory_bytes as f64;
1349
1350        if utilization > 0.9 {
1351            // Trigger more aggressive cleanup
1352            Self::aggressive_cleanup(cache)?;
1353        }
1354
1355        // 3. Preemptive persistent cache sync
1356        Self::sync_hot_entries(cache)?;
1357
1358        Ok(())
1359    }
1360
1361    /// Perform aggressive cleanup when memory usage is high
1362    fn aggressive_cleanup(_cache: &Arc<MultiLevelCache>) -> Result<()> {
1363        // Force cleanup of memory cache by temporarily reducing limits
1364        // This is a simplified approach - in practice you'd implement more sophisticated logic
1365        println!("Performing aggressive cache cleanup due to high memory usage");
1366        Ok(())
1367    }
1368
1369    /// Sync frequently accessed entries to persistent storage
1370    fn sync_hot_entries(_cache: &Arc<MultiLevelCache>) -> Result<()> {
1371        // In a real implementation, you'd identify hot entries and ensure they're in persistent storage
1372        // This helps with cache warming after restarts
1373        Ok(())
1374    }
1375}
1376
1377impl Drop for BackgroundCacheWorker {
1378    fn drop(&mut self) {
1379        let _ = self.stop();
1380    }
1381}
1382
1383/// Cache warming utilities
1384pub struct CacheWarmer {
1385    cache: Arc<MultiLevelCache>,
1386}
1387
1388impl CacheWarmer {
1389    pub fn new(cache: Arc<MultiLevelCache>) -> Self {
1390        Self { cache }
1391    }
1392
1393    /// Warm cache with a list of key-value pairs
1394    pub fn warm_with_data(&self, data: Vec<(CacheKey, Vector)>) -> Result<usize> {
1395        let mut loaded_count = 0;
1396
1397        for (key, vector) in data {
1398            if self.cache.insert(key, vector).is_ok() {
1399                loaded_count += 1;
1400            }
1401        }
1402
1403        Ok(loaded_count)
1404    }
1405
1406    /// Warm cache by loading frequently accessed entries from persistent storage
1407    pub fn warm_from_persistent(&self, keys: Vec<CacheKey>) -> Result<usize> {
1408        let mut loaded_count = 0;
1409
1410        for key in keys {
1411            // Try to load from persistent cache and promote to memory
1412            if self.cache.get(&key).is_some() {
1413                loaded_count += 1;
1414            }
1415        }
1416
1417        Ok(loaded_count)
1418    }
1419
1420    /// Warm cache using a precomputed dataset
1421    pub fn warm_with_generator<F>(&self, count: usize, generator: F) -> Result<usize>
1422    where
1423        F: Fn(usize) -> Option<(CacheKey, Vector)>,
1424    {
1425        let mut loaded_count = 0;
1426
1427        for i in 0..count {
1428            if let Some((key, vector)) = generator(i) {
1429                if self.cache.insert(key, vector).is_ok() {
1430                    loaded_count += 1;
1431                }
1432            }
1433        }
1434
1435        Ok(loaded_count)
1436    }
1437}
1438
1439/// Advanced cache analytics and optimization recommendations
1440pub struct CacheAnalyzer {
1441    cache: Arc<MultiLevelCache>,
1442    invalidator: Arc<CacheInvalidator>,
1443}
1444
1445#[derive(Debug, Clone)]
1446pub struct CacheAnalysisReport {
1447    pub memory_utilization: f64,
1448    pub hit_ratio: f64,
1449    pub persistent_hit_ratio: f64,
1450    pub most_accessed_namespaces: Vec<(String, usize)>,
1451    pub recommendations: Vec<String>,
1452    pub performance_score: f64, // 0.0 to 1.0
1453}
1454
1455impl CacheAnalyzer {
1456    pub fn new(cache: Arc<MultiLevelCache>, invalidator: Arc<CacheInvalidator>) -> Self {
1457        Self { cache, invalidator }
1458    }
1459
1460    /// Generate comprehensive cache analysis report
1461    pub fn analyze(&self) -> CacheAnalysisReport {
1462        let stats = self.cache.get_stats();
1463        let memory_stats = self.cache.get_memory_stats();
1464        let invalidation_stats = self.invalidator.get_stats();
1465
1466        let memory_utilization =
1467            memory_stats.memory_bytes as f64 / memory_stats.max_memory_bytes as f64;
1468
1469        let total_requests = stats.total_requests;
1470        let total_hits = stats.memory_hits + stats.persistent_hits;
1471        let hit_ratio = if total_requests > 0 {
1472            total_hits as f64 / total_requests as f64
1473        } else {
1474            0.0
1475        };
1476
1477        let persistent_hit_ratio = if stats.persistent_hits + stats.persistent_misses > 0 {
1478            stats.persistent_hits as f64 / (stats.persistent_hits + stats.persistent_misses) as f64
1479        } else {
1480            0.0
1481        };
1482
1483        let mut recommendations = Vec::new();
1484
1485        // Generate recommendations
1486        if hit_ratio < 0.5 {
1487            recommendations
1488                .push("Consider increasing cache size or adjusting eviction policy".to_string());
1489        }
1490
1491        if memory_utilization > 0.9 {
1492            recommendations.push(
1493                "Memory cache is near capacity - consider increasing max_memory_bytes".to_string(),
1494            );
1495        }
1496
1497        if persistent_hit_ratio < 0.3 {
1498            recommendations
1499                .push("Persistent cache hit ratio is low - review TTL settings".to_string());
1500        }
1501
1502        if invalidation_stats.tracked_namespaces > 100 {
1503            recommendations
1504                .push("Consider consolidating namespaces to reduce tracking overhead".to_string());
1505        }
1506
1507        // Calculate performance score (weighted combination of metrics)
1508        let performance_score =
1509            (hit_ratio * 0.4 + (1.0 - memory_utilization) * 0.3 + persistent_hit_ratio * 0.3)
1510                .clamp(0.0, 1.0);
1511
1512        CacheAnalysisReport {
1513            memory_utilization,
1514            hit_ratio,
1515            persistent_hit_ratio,
1516            most_accessed_namespaces: vec![], // Would need access pattern tracking
1517            recommendations,
1518            performance_score,
1519        }
1520    }
1521
1522    /// Get recommendations for cache configuration optimization
1523    pub fn get_optimization_recommendations(&self) -> Vec<String> {
1524        self.analyze().recommendations
1525    }
1526}
1527
1528#[cfg(test)]
1529mod tests {
1530    use super::*;
1531    use tempfile::TempDir;
1532
1533    #[test]
1534    fn test_cache_key() {
1535        let key = CacheKey::new("embeddings", "test_doc").with_variant("v1");
1536
1537        assert_eq!(key.namespace, "embeddings");
1538        assert_eq!(key.key, "test_doc");
1539        assert_eq!(key.variant, Some("v1".to_string()));
1540        assert_eq!(key.to_string(), "embeddings:test_doc:v1");
1541    }
1542
1543    #[test]
1544    fn test_memory_cache() {
1545        let config = CacheConfig {
1546            max_memory_entries: 2,
1547            max_memory_bytes: 1024,
1548            ..Default::default()
1549        };
1550
1551        let mut cache = MemoryCache::new(config);
1552
1553        let key1 = CacheKey::new("test", "key1");
1554        let key2 = CacheKey::new("test", "key2");
1555        let key3 = CacheKey::new("test", "key3");
1556
1557        let vector1 = Vector::new(vec![1.0, 2.0, 3.0]);
1558        let vector2 = Vector::new(vec![4.0, 5.0, 6.0]);
1559        let vector3 = Vector::new(vec![7.0, 8.0, 9.0]);
1560
1561        // Insert vectors
1562        cache
1563            .insert(key1.clone(), CacheEntry::new(vector1.clone()))
1564            .unwrap();
1565        cache
1566            .insert(key2.clone(), CacheEntry::new(vector2.clone()))
1567            .unwrap();
1568
1569        // Check retrieval
1570        assert!(cache.get(&key1).is_some());
1571        assert!(cache.get(&key2).is_some());
1572
1573        // Insert third vector (should evict one)
1574        cache
1575            .insert(key3.clone(), CacheEntry::new(vector3.clone()))
1576            .unwrap();
1577
1578        // One of the first two should be evicted
1579        let remaining = cache.entries.len();
1580        assert_eq!(remaining, 2);
1581    }
1582
1583    #[test]
1584    fn test_persistent_cache() {
1585        let temp_dir = TempDir::new().unwrap();
1586
1587        let config = CacheConfig {
1588            persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1589            enable_compression: true,
1590            ..Default::default()
1591        };
1592
1593        let cache = PersistentCache::new(config).unwrap();
1594
1595        let key = CacheKey::new("test", "persistent_key");
1596        let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1597        let entry = CacheEntry::new(vector.clone());
1598
1599        // Store and retrieve
1600        cache.store(&key, &entry).unwrap();
1601        let retrieved = cache.load(&key).unwrap();
1602
1603        // Should succeed now with proper serialization
1604        assert!(retrieved.is_some());
1605        let retrieved_entry = retrieved.unwrap();
1606        assert_eq!(retrieved_entry.data.as_f32(), vector.as_f32());
1607    }
1608
1609    #[test]
1610    fn test_multi_level_cache() {
1611        let temp_dir = TempDir::new().unwrap();
1612
1613        let config = CacheConfig {
1614            max_memory_entries: 2,
1615            persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1616            enable_persistent: true,
1617            ..Default::default()
1618        };
1619
1620        let cache = MultiLevelCache::new(config).unwrap();
1621
1622        let key = CacheKey::new("test", "multi_level");
1623        let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1624
1625        // Insert and retrieve
1626        cache.insert(key.clone(), vector.clone()).unwrap();
1627        let retrieved = cache.get(&key).unwrap();
1628
1629        assert_eq!(retrieved.as_f32(), vector.as_f32());
1630
1631        // Check stats
1632        let stats = cache.get_stats();
1633        assert_eq!(stats.total_requests, 1);
1634        assert_eq!(stats.memory_hits, 1);
1635    }
1636
1637    #[test]
1638    fn test_cache_expiration() {
1639        let config = CacheConfig {
1640            max_memory_entries: 10,
1641            ttl: Some(Duration::from_millis(10)),
1642            ..Default::default()
1643        };
1644
1645        let mut cache = MemoryCache::new(config);
1646
1647        let key = CacheKey::new("test", "expiring");
1648        let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1649        let entry = CacheEntry::new(vector).with_ttl(Duration::from_millis(10));
1650
1651        cache.insert(key.clone(), entry).unwrap();
1652
1653        // Should be available immediately
1654        assert!(cache.get(&key).is_some());
1655
1656        // Wait for expiration
1657        std::thread::sleep(Duration::from_millis(20));
1658
1659        // Should be expired and removed
1660        assert!(cache.get(&key).is_none());
1661    }
1662
1663    #[test]
1664    fn test_arc_eviction_policy() {
1665        let config = CacheConfig {
1666            max_memory_entries: 3,
1667            eviction_policy: EvictionPolicy::ARC,
1668            ..Default::default()
1669        };
1670
1671        let mut cache = MemoryCache::new(config);
1672
1673        let key1 = CacheKey::new("test", "arc1");
1674        let key2 = CacheKey::new("test", "arc2");
1675        let key3 = CacheKey::new("test", "arc3");
1676        let key4 = CacheKey::new("test", "arc4");
1677
1678        let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1679
1680        // Insert three items
1681        cache
1682            .insert(key1.clone(), CacheEntry::new(vector.clone()))
1683            .unwrap();
1684        cache
1685            .insert(key2.clone(), CacheEntry::new(vector.clone()))
1686            .unwrap();
1687        cache
1688            .insert(key3.clone(), CacheEntry::new(vector.clone()))
1689            .unwrap();
1690
1691        // Access key1 multiple times to make it frequent
1692        cache.get(&key1);
1693        cache.get(&key1);
1694        cache.get(&key1);
1695
1696        // Insert key4 - should evict the least valuable item
1697        cache
1698            .insert(key4.clone(), CacheEntry::new(vector.clone()))
1699            .unwrap();
1700
1701        // key1 should still be there (frequent access)
1702        assert!(cache.get(&key1).is_some());
1703
1704        // Check that we have exactly 3 items
1705        assert_eq!(cache.entries.len(), 3);
1706    }
1707
1708    #[test]
1709    fn test_cache_warmer() {
1710        let temp_dir = TempDir::new().unwrap();
1711
1712        let config = CacheConfig {
1713            max_memory_entries: 10,
1714            persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1715            enable_persistent: true,
1716            ..Default::default()
1717        };
1718
1719        let cache = Arc::new(MultiLevelCache::new(config).unwrap());
1720        let warmer = CacheWarmer::new(Arc::clone(&cache));
1721
1722        // Prepare test data
1723        let test_data = vec![
1724            (CacheKey::new("test", "warm1"), Vector::new(vec![1.0, 2.0])),
1725            (CacheKey::new("test", "warm2"), Vector::new(vec![3.0, 4.0])),
1726            (CacheKey::new("test", "warm3"), Vector::new(vec![5.0, 6.0])),
1727        ];
1728
1729        // Warm cache with data
1730        let loaded_count = warmer.warm_with_data(test_data.clone()).unwrap();
1731        assert_eq!(loaded_count, 3);
1732
1733        // Verify data is in cache
1734        for (key, expected_vector) in test_data {
1735            let cached_vector = cache.get(&key).unwrap();
1736            assert_eq!(cached_vector.as_f32(), expected_vector.as_f32());
1737        }
1738    }
1739
1740    #[test]
1741    fn test_cache_warmer_with_generator() {
1742        let temp_dir = TempDir::new().unwrap();
1743
1744        let config = CacheConfig {
1745            max_memory_entries: 10,
1746            persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1747            enable_persistent: true,
1748            ..Default::default()
1749        };
1750
1751        let cache = Arc::new(MultiLevelCache::new(config).unwrap());
1752        let warmer = CacheWarmer::new(Arc::clone(&cache));
1753
1754        // Use generator to warm cache
1755        let loaded_count = warmer
1756            .warm_with_generator(5, |i| {
1757                Some((
1758                    CacheKey::new("generated", format!("item_{i}")),
1759                    Vector::new(vec![i as f32, (i * 2) as f32]),
1760                ))
1761            })
1762            .unwrap();
1763
1764        assert_eq!(loaded_count, 5);
1765
1766        // Verify generated data is in cache
1767        for i in 0..5 {
1768            let key = CacheKey::new("generated", format!("item_{i}"));
1769            let cached_vector = cache.get(&key).unwrap();
1770            assert_eq!(cached_vector.as_f32(), vec![i as f32, (i * 2) as f32]);
1771        }
1772    }
1773
1774    #[test]
1775    fn test_cache_analyzer() {
1776        let temp_dir = TempDir::new().unwrap();
1777
1778        let config = CacheConfig {
1779            max_memory_entries: 10,
1780            persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1781            enable_persistent: true,
1782            ..Default::default()
1783        };
1784
1785        let cache = Arc::new(MultiLevelCache::new(config).unwrap());
1786        let invalidator = Arc::new(CacheInvalidator::new(Arc::clone(&cache)));
1787        let analyzer = CacheAnalyzer::new(Arc::clone(&cache), Arc::clone(&invalidator));
1788
1789        // Add some test data and access patterns
1790        let key1 = CacheKey::new("test", "analyze1");
1791        let key2 = CacheKey::new("test", "analyze2");
1792        let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1793
1794        cache.insert(key1.clone(), vector.clone()).unwrap();
1795        cache.insert(key2.clone(), vector.clone()).unwrap();
1796
1797        // Access the cache to generate some stats
1798        cache.get(&key1);
1799        cache.get(&key2);
1800        cache.get(&key1); // Hit
1801        cache.get(&CacheKey::new("test", "nonexistent")); // Miss
1802
1803        // Analyze cache performance
1804        let report = analyzer.analyze();
1805
1806        assert!(report.hit_ratio > 0.0);
1807        assert!(report.memory_utilization >= 0.0 && report.memory_utilization <= 1.0);
1808        assert!(report.performance_score >= 0.0 && report.performance_score <= 1.0);
1809
1810        // Should have some recommendations if performance isn't perfect
1811        let recommendations = analyzer.get_optimization_recommendations();
1812        // In this test case, we might get recommendations about hit ratio
1813        assert!(!recommendations.is_empty());
1814    }
1815
1816    #[test]
1817    fn test_background_cache_worker() {
1818        let temp_dir = TempDir::new().unwrap();
1819
1820        let config = CacheConfig {
1821            max_memory_entries: 10,
1822            persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1823            enable_persistent: true,
1824            enable_background_updates: true,
1825            background_update_interval: Duration::from_millis(100),
1826            ..Default::default()
1827        };
1828
1829        let cache = Arc::new(MultiLevelCache::new(config.clone()).unwrap());
1830        let invalidator = Arc::new(CacheInvalidator::new(Arc::clone(&cache)));
1831        let mut worker =
1832            BackgroundCacheWorker::new(Arc::clone(&cache), Arc::clone(&invalidator), config);
1833
1834        // Start the worker
1835        worker.start().unwrap();
1836
1837        // Add some test data
1838        let key = CacheKey::new("test", "background");
1839        let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1840        cache.insert(key.clone(), vector.clone()).unwrap();
1841
1842        // Let the worker run for a short time
1843        std::thread::sleep(Duration::from_millis(150));
1844
1845        // Stop the worker
1846        worker.stop().unwrap();
1847
1848        // Verify data is still accessible
1849        assert!(cache.get(&key).is_some());
1850    }
1851
1852    #[test]
1853    fn test_cache_invalidation_by_tag() {
1854        let temp_dir = TempDir::new().unwrap();
1855
1856        let config = CacheConfig {
1857            max_memory_entries: 10,
1858            persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1859            enable_persistent: true,
1860            ..Default::default()
1861        };
1862
1863        let cache = Arc::new(MultiLevelCache::new(config).unwrap());
1864        let invalidator = Arc::new(CacheInvalidator::new(Arc::clone(&cache)));
1865
1866        // Create entries with tags
1867        let key1 = CacheKey::new("test", "tagged1");
1868        let key2 = CacheKey::new("test", "tagged2");
1869        let key3 = CacheKey::new("test", "tagged3");
1870
1871        let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1872
1873        cache.insert(key1.clone(), vector.clone()).unwrap();
1874        cache.insert(key2.clone(), vector.clone()).unwrap();
1875        cache.insert(key3.clone(), vector.clone()).unwrap();
1876
1877        // Register entries with tags
1878        let mut tags1 = HashMap::new();
1879        tags1.insert("category".to_string(), "embeddings".to_string());
1880        invalidator.register_entry(&key1, &tags1);
1881
1882        let mut tags2 = HashMap::new();
1883        tags2.insert("category".to_string(), "embeddings".to_string());
1884        invalidator.register_entry(&key2, &tags2);
1885
1886        let mut tags3 = HashMap::new();
1887        tags3.insert("category".to_string(), "vectors".to_string());
1888        invalidator.register_entry(&key3, &tags3);
1889
1890        // Invalidate by tag
1891        let invalidated_count = invalidator
1892            .invalidate_by_tag("category", "embeddings")
1893            .unwrap();
1894        assert_eq!(invalidated_count, 2);
1895
1896        // Check that tagged entries are removed
1897        assert!(cache.get(&key1).is_none());
1898        assert!(cache.get(&key2).is_none());
1899
1900        // Check that untagged entry remains
1901        assert!(cache.get(&key3).is_some());
1902    }
1903
1904    #[test]
1905    fn test_cache_invalidation_by_namespace() {
1906        let temp_dir = TempDir::new().unwrap();
1907
1908        let config = CacheConfig {
1909            max_memory_entries: 10,
1910            persistent_cache_dir: Some(temp_dir.path().to_path_buf()),
1911            enable_persistent: true,
1912            ..Default::default()
1913        };
1914
1915        let cache = Arc::new(MultiLevelCache::new(config).unwrap());
1916        let invalidator = Arc::new(CacheInvalidator::new(Arc::clone(&cache)));
1917
1918        // Create entries in different namespaces
1919        let key1 = CacheKey::new("embeddings", "item1");
1920        let key2 = CacheKey::new("embeddings", "item2");
1921        let key3 = CacheKey::new("vectors", "item3");
1922
1923        let vector = Vector::new(vec![1.0, 2.0, 3.0]);
1924
1925        cache.insert(key1.clone(), vector.clone()).unwrap();
1926        cache.insert(key2.clone(), vector.clone()).unwrap();
1927        cache.insert(key3.clone(), vector.clone()).unwrap();
1928
1929        // Register entries for tracking
1930        invalidator.register_entry(&key1, &HashMap::new());
1931        invalidator.register_entry(&key2, &HashMap::new());
1932        invalidator.register_entry(&key3, &HashMap::new());
1933
1934        // Invalidate by namespace
1935        let invalidated_count = invalidator.invalidate_namespace("embeddings").unwrap();
1936        assert_eq!(invalidated_count, 2);
1937
1938        // Check that namespace entries are removed
1939        assert!(cache.get(&key1).is_none());
1940        assert!(cache.get(&key2).is_none());
1941
1942        // Check that other namespace entry remains
1943        assert!(cache.get(&key3).is_some());
1944    }
1945}