rrag/caching/mod.rs
1//! # Intelligent Caching Layer
2//!
3//! Enterprise-grade multi-level caching system designed specifically for RAG applications
4//! with semantic awareness, intelligent eviction policies, and advanced performance
5//! optimization features.
6//!
7//! This module provides a comprehensive caching solution that understands the unique
8//! characteristics of RAG workloads, including query similarity, embedding reuse,
9//! semantic relationships, and result patterns. It offers multiple cache layers
10//! working together to minimize latency and computational overhead.
11//!
12//! ## Key Features
13//!
14//! - **Multi-Layer Architecture**: Query, embedding, semantic, and result caches
15//! - **Semantic Awareness**: Understanding query similarity and content relationships
16//! - **Intelligent Eviction**: Multiple policies (LRU, LFU, TTL, ARC, Semantic-aware)
17//! - **Persistence Support**: Durable caching across system restarts
18//! - **Performance Monitoring**: Comprehensive metrics and analytics
19//! - **Memory Management**: Automatic cleanup and pressure-based eviction
20//! - **Async Operations**: Non-blocking cache operations for high throughput
21//!
22//! ## Architecture
23//!
24//! The caching system consists of four specialized cache layers:
25//!
26//! 1. **Query Cache**: Caches complete query-response pairs with similarity matching
27//! 2. **Embedding Cache**: Reuses expensive embedding computations
28//! 3. **Semantic Cache**: Groups semantically similar queries for broader cache hits
29//! 4. **Result Cache**: Caches search results for parameter combinations
30//!
31//! ## Examples
32//!
33//! ### Basic Cache Setup
34//! ```rust
35//! use rrag::caching::{CacheService, CacheConfig};
36//!
37//! # async fn example() -> rrag::RragResult<()> {
38//! let cache_config = CacheConfig::default()
39//! .with_query_cache(true)
40//! .with_embedding_cache(true)
41//! .with_semantic_cache(true);
42//!
43//! let mut cache = CacheService::new(cache_config)?;
44//!
45//! // Cache will automatically optimize based on your RAG workload patterns
46//! println!("Cache initialized with {} layers", 4);
47//! # Ok(())
48//! # }
49//! ```
50//!
51//! ### Query Result Caching
52//! ```rust
53//! use rrag::caching::{QueryCacheEntry, CachedSearchResult};
54//!
55//! # async fn example() -> rrag::RragResult<()> {
56//! # let mut cache = rrag::caching::CacheService::new(rrag::caching::CacheConfig::default())?;
57//! let query = "What is machine learning?";
58//!
59//! // Check cache first
60//! if let Some(cached_entry) = cache.get_query_results(query).await {
61//! println!("Cache hit! Retrieved {} results", cached_entry.results.len());
62//! return Ok(());
63//! }
64//!
65//! // If not cached, perform search and cache results
66//! let search_results = vec![
67//! CachedSearchResult {
68//! document_id: "doc_1".to_string(),
69//! content: "Machine learning is a subset of AI...".to_string(),
70//! score: 0.95,
71//! rank: 1,
72//! metadata: std::collections::HashMap::new(),
73//! }
74//! ];
75//!
76//! let cache_entry = QueryCacheEntry {
77//! query: query.to_string(),
78//! embedding_hash: "hash123".to_string(),
79//! results: search_results,
80//! generated_answer: Some("ML is a field of AI...".to_string()),
81//! metadata: rrag::caching::CacheEntryMetadata::new(),
82//! };
83//!
84//! cache.cache_query_results(query.to_string(), cache_entry).await?;
85//! # Ok(())
86//! # }
87//! ```
88//!
89//! ### Embedding Caching for Performance
90//! ```rust
91//! # async fn example() -> rrag::RragResult<()> {
92//! # let mut cache = rrag::caching::CacheService::new(rrag::caching::CacheConfig::default())?;
93//! let text = "Advanced machine learning techniques";
94//! let model = "sentence-transformers/all-MiniLM-L6-v2";
95//!
96//! // Check if embedding is already computed
97//! if let Some(cached_embedding) = cache.get_embedding(text, model).await {
98//! println!("Using cached embedding of dimension {}", cached_embedding.len());
99//! } else {
100//! // Compute embedding (expensive operation)
101//! let embedding = compute_embedding(text, model).await?;
102//!
103//! // Cache for future use
104//! cache.cache_embedding(
105//! text.to_string(),
106//! model.to_string(),
107//! embedding
108//! ).await?;
109//!
110//! println!("Computed and cached new embedding");
111//! }
112//!
113//! # async fn compute_embedding(text: &str, model: &str) -> rrag::RragResult<Vec<f32>> {
114//! # Ok(vec![0.1, 0.2, 0.3]) // Mock embedding
115//! # }
116//! # Ok(())
117//! # }
118//! ```
119//!
120//! ### Semantic Similarity Caching
121//! ```rust
122//! use rrag::caching::{SemanticCacheEntry, SimilarEntry};
123//!
124//! # async fn example() -> rrag::RragResult<()> {
125//! # let mut cache = rrag::caching::CacheService::new(rrag::caching::CacheConfig::default())?;
126//! let similar_queries = [
127//! "What is artificial intelligence?",
128//! "Explain AI concepts",
129//! "Define artificial intelligence",
130//! ];
131//!
132//! // Check for semantically similar cached results
133//! for query in &similar_queries {
134//! if let Some(semantic_entry) = cache.get_semantic_results(query).await {
135//! println!("Found semantic match for: {}", query);
136//! println!("Representative: {}", semantic_entry.representative);
137//! println!("Similar entries: {}", semantic_entry.similar_entries.len());
138//! return Ok(());
139//! }
140//! }
141//!
142//! // Create semantic cache entry for related queries
143//! let semantic_entry = SemanticCacheEntry {
144//! representative: "What is artificial intelligence?".to_string(),
145//! cluster_id: Some(1),
146//! similar_entries: vec![
147//! SimilarEntry {
148//! text: "Explain AI concepts".to_string(),
149//! similarity: 0.92,
150//! added_at: std::time::SystemTime::now(),
151//! },
152//! SimilarEntry {
153//! text: "Define artificial intelligence".to_string(),
154//! similarity: 0.89,
155//! added_at: std::time::SystemTime::now(),
156//! },
157//! ],
158//! results: vec![], // Shared results for all similar queries
159//! metadata: rrag::caching::CacheEntryMetadata::new(),
160//! };
161//!
162//! cache.cache_semantic_results(
163//! "ai_concepts_cluster".to_string(),
164//! semantic_entry
165//! ).await?;
166//! # Ok(())
167//! # }
168//! ```
169//!
170//! ### Advanced Cache Configuration
171//! ```rust
172//! use rrag::caching::{
173//! CacheConfig, QueryCacheConfig, EmbeddingCacheConfig,
174//! EvictionPolicy, PersistenceConfig, PersistenceFormat
175//! };
176//! use std::time::Duration;
177//!
178//! # async fn example() -> rrag::RragResult<()> {
179//! let advanced_config = CacheConfig {
180//! enabled: true,
181//! query_cache: QueryCacheConfig {
182//! enabled: true,
183//! max_size: 5000,
184//! ttl: Duration::from_secs(7200), // 2 hours
185//! eviction_policy: EvictionPolicy::SemanticAware,
186//! similarity_threshold: 0.92,
187//! },
188//! embedding_cache: EmbeddingCacheConfig {
189//! enabled: true,
190//! max_size: 50000,
191//! ttl: Duration::from_secs(86400), // 24 hours
192//! eviction_policy: EvictionPolicy::LFU,
193//! compression_enabled: true,
194//! },
195//! persistence: PersistenceConfig {
196//! enabled: true,
197//! storage_path: "/data/rag_cache".to_string(),
198//! auto_save_interval: Duration::from_secs(300),
199//! format: PersistenceFormat::MessagePack,
200//! },
201//! ..Default::default()
202//! };
203//!
204//! let cache = CacheService::new(advanced_config)?;
205//! println!("Advanced cache configured with persistence and compression");
206//! # Ok(())
207//! # }
208//! ```
209//!
210//! ### Cache Performance Monitoring
211//! ```rust
212//! # async fn example() -> rrag::RragResult<()> {
213//! # let mut cache = rrag::caching::CacheService::new(rrag::caching::CacheConfig::default())?;
214//! // Get comprehensive cache metrics
215//! let metrics = cache.get_metrics();
216//!
217//! println!("📊 Cache Performance Report");
218//! println!("Query Cache: {:.1}% hit rate, {} entries",
219//! metrics.query_cache.hit_rate * 100.0,
220//! metrics.query_cache.total_entries);
221//!
222//! println!("Embedding Cache: {:.1}% hit rate, {:.1}MB memory",
223//! metrics.embedding_cache.hit_rate * 100.0,
224//! metrics.embedding_cache.memory_usage as f32 / 1024.0 / 1024.0);
225//!
226//! println!("Semantic Cache: {:.1}% hit rate, {} evictions",
227//! metrics.semantic_cache.hit_rate * 100.0,
228//! metrics.semantic_cache.evictions);
229//!
230//! println!("Overall Efficiency: {:.1}%, Time Saved: {:.1}ms",
231//! metrics.overall.efficiency_score * 100.0,
232//! metrics.overall.time_saved_ms);
233//!
234//! // Performance optimization based on metrics
235//! if metrics.overall.memory_pressure > 0.8 {
236//! println!("⚠️ High memory pressure detected, triggering cleanup");
237//! cache.maintenance().await?;
238//! }
239//! # Ok(())
240//! # }
241//! ```
242//!
243//! ## Cache Eviction Policies
244//!
245//! ### LRU (Least Recently Used)
246//! Best for: General-purpose caching with temporal locality
247//! - Evicts items that haven't been accessed recently
248//! - Good memory efficiency
249//! - Simple and fast
250//!
251//! ### LFU (Least Frequently Used)
252//! Best for: Embedding caches where popular items should stay
253//! - Evicts items with lowest access frequency
254//! - Excellent for reusable computations
255//! - Handles long-term access patterns
256//!
257//! ### TTL (Time-To-Live)
258//! Best for: Fresh data requirements
259//! - Evicts items after fixed time period
260//! - Ensures data freshness
261//! - Predictable memory usage
262//!
263//! ### Semantic-Aware
264//! Best for: RAG-specific query patterns
265//! - Considers semantic similarity in eviction decisions
266//! - Maintains representative queries from clusters
267//! - Optimizes for query pattern diversity
268//!
269//! ## Performance Optimization Tips
270//!
271//! 1. **Tune Cache Sizes**: Monitor hit rates and adjust max_size accordingly
272//! 2. **Enable Compression**: For embedding caches with large vectors
273//! 3. **Use Persistence**: For frequently reused embeddings across sessions
274//! 4. **Semantic Clustering**: Enable for diverse query workloads
275//! 5. **Async Operations**: Enable for high-throughput applications
276//! 6. **Memory Monitoring**: Set appropriate pressure thresholds
277//!
278//! ## Integration with RAG Systems
279//!
280//! ```rust
281//! use rrag::{RragSystemBuilder, caching::CacheConfig};
282//!
283//! # async fn example() -> rrag::RragResult<()> {
284//! let rag_system = RragSystemBuilder::new()
285//! .with_caching(
286//! CacheConfig::production()
287//! .with_semantic_awareness(true)
288//! .with_persistence(true)
289//! .with_intelligent_eviction(true)
290//! )
291//! .build()
292//! .await?;
293//!
294//! // Cache automatically optimizes based on your query patterns
295//! let results = rag_system.search("machine learning applications", Some(10)).await?;
296//! // Subsequent similar queries will benefit from semantic caching
297//! # Ok(())
298//! # }
299//! ```
300
301pub mod cache_core;
302pub mod embedding_cache;
303pub mod metrics;
304pub mod persistence;
305pub mod policies;
306pub mod query_cache;
307pub mod result_cache;
308pub mod semantic_cache;
309
310use crate::RragResult;
311use serde::{Deserialize, Serialize};
312use std::collections::HashMap;
313use std::hash::{Hash, Hasher};
314use std::time::{Duration, SystemTime};
315
316/// Main caching service orchestrating multiple cache layers
317pub struct CacheService {
318 /// Query result cache
319 query_cache: Box<dyn Cache<String, QueryCacheEntry>>,
320
321 /// Embedding cache for reusing computations
322 embedding_cache: Box<dyn Cache<String, EmbeddingCacheEntry>>,
323
324 /// Semantic similarity cache
325 semantic_cache: Box<dyn Cache<String, SemanticCacheEntry>>,
326
327 /// Document retrieval cache
328 result_cache: Box<dyn Cache<String, ResultCacheEntry>>,
329
330 /// Cache configuration
331 config: CacheConfig,
332
333 /// Performance metrics
334 metrics: CacheMetrics,
335}
336
337/// Global cache configuration
338#[derive(Debug, Clone)]
339pub struct CacheConfig {
340 /// Enable/disable caching globally
341 pub enabled: bool,
342
343 /// Query cache configuration
344 pub query_cache: QueryCacheConfig,
345
346 /// Embedding cache configuration
347 pub embedding_cache: EmbeddingCacheConfig,
348
349 /// Semantic cache configuration
350 pub semantic_cache: SemanticCacheConfig,
351
352 /// Result cache configuration
353 pub result_cache: ResultCacheConfig,
354
355 /// Persistence configuration
356 pub persistence: PersistenceConfig,
357
358 /// Performance tuning
359 pub performance: PerformanceConfig,
360}
361
362/// Query cache configuration
363#[derive(Debug, Clone)]
364pub struct QueryCacheConfig {
365 pub enabled: bool,
366 pub max_size: usize,
367 pub ttl: Duration,
368 pub eviction_policy: EvictionPolicy,
369 pub similarity_threshold: f32,
370}
371
372/// Embedding cache configuration
373#[derive(Debug, Clone)]
374pub struct EmbeddingCacheConfig {
375 pub enabled: bool,
376 pub max_size: usize,
377 pub ttl: Duration,
378 pub eviction_policy: EvictionPolicy,
379 pub compression_enabled: bool,
380}
381
382/// Semantic cache configuration
383#[derive(Debug, Clone)]
384pub struct SemanticCacheConfig {
385 pub enabled: bool,
386 pub max_size: usize,
387 pub ttl: Duration,
388 pub similarity_threshold: f32,
389 pub clustering_enabled: bool,
390 pub max_clusters: usize,
391}
392
393/// Result cache configuration
394#[derive(Debug, Clone)]
395pub struct ResultCacheConfig {
396 pub enabled: bool,
397 pub max_size: usize,
398 pub ttl: Duration,
399 pub eviction_policy: EvictionPolicy,
400 pub compress_large_results: bool,
401}
402
403/// Persistence configuration
404#[derive(Debug, Clone)]
405pub struct PersistenceConfig {
406 pub enabled: bool,
407 pub storage_path: String,
408 pub auto_save_interval: Duration,
409 pub format: PersistenceFormat,
410}
411
412/// Performance configuration
413#[derive(Debug, Clone)]
414pub struct PerformanceConfig {
415 pub async_writes: bool,
416 pub batch_operations: bool,
417 pub background_cleanup: bool,
418 pub memory_pressure_threshold: f32,
419}
420
421/// Cache eviction policies
422#[derive(Debug, Clone, Copy)]
423pub enum EvictionPolicy {
424 /// Least Recently Used
425 LRU,
426 /// Least Frequently Used
427 LFU,
428 /// Time-To-Live based
429 TTL,
430 /// Adaptive Replacement Cache
431 ARC,
432 /// Custom semantic-aware policy
433 SemanticAware,
434}
435
436/// Persistence formats
437#[derive(Debug, Clone)]
438pub enum PersistenceFormat {
439 Binary,
440 Json,
441 MessagePack,
442}
443
444/// Generic cache trait
445pub trait Cache<K, V>: Send + Sync
446where
447 K: Hash + Eq + Clone + Send + Sync + 'static,
448 V: Clone + Send + Sync + 'static,
449{
450 /// Get value from cache
451 fn get(&self, key: &K) -> Option<V>;
452
453 /// Put value into cache
454 fn put(&mut self, key: K, value: V) -> RragResult<()>;
455
456 /// Remove value from cache
457 fn remove(&mut self, key: &K) -> Option<V>;
458
459 /// Check if key exists
460 fn contains(&self, key: &K) -> bool;
461
462 /// Clear all entries
463 fn clear(&mut self);
464
465 /// Get cache size
466 fn size(&self) -> usize;
467
468 /// Get cache statistics
469 fn stats(&self) -> CacheStats;
470}
471
472/// Cache entry metadata
473#[derive(Debug, Clone, Serialize, Deserialize)]
474pub struct CacheEntryMetadata {
475 /// Creation timestamp
476 pub created_at: SystemTime,
477
478 /// Last access timestamp
479 pub last_accessed: SystemTime,
480
481 /// Access count
482 pub access_count: u64,
483
484 /// Entry size in bytes
485 pub size_bytes: usize,
486
487 /// Time-to-live
488 pub ttl: Option<Duration>,
489
490 /// Custom metadata
491 pub custom: HashMap<String, String>,
492}
493
494/// Query cache entry
495#[derive(Debug, Clone, Serialize, Deserialize)]
496pub struct QueryCacheEntry {
497 /// Original query
498 pub query: String,
499
500 /// Query embedding hash
501 pub embedding_hash: String,
502
503 /// Cached results
504 pub results: Vec<CachedSearchResult>,
505
506 /// Generation result if any
507 pub generated_answer: Option<String>,
508
509 /// Metadata
510 pub metadata: CacheEntryMetadata,
511}
512
513/// Embedding cache entry
514#[derive(Debug, Clone, Serialize, Deserialize)]
515pub struct EmbeddingCacheEntry {
516 /// Input text
517 pub text: String,
518
519 /// Text hash for verification
520 pub text_hash: String,
521
522 /// Computed embedding
523 pub embedding: Vec<f32>,
524
525 /// Model used for embedding
526 pub model: String,
527
528 /// Metadata
529 pub metadata: CacheEntryMetadata,
530}
531
532/// Semantic cache entry
533#[derive(Debug, Clone, Serialize, Deserialize)]
534pub struct SemanticCacheEntry {
535 /// Representative query/text
536 pub representative: String,
537
538 /// Cluster ID if clustering enabled
539 pub cluster_id: Option<usize>,
540
541 /// Similar queries/texts
542 pub similar_entries: Vec<SimilarEntry>,
543
544 /// Cached semantic results
545 pub results: Vec<CachedSearchResult>,
546
547 /// Metadata
548 pub metadata: CacheEntryMetadata,
549}
550
551/// Result cache entry
552#[derive(Debug, Clone, Serialize, Deserialize)]
553pub struct ResultCacheEntry {
554 /// Search parameters hash
555 pub params_hash: String,
556
557 /// Cached search results
558 pub results: Vec<CachedSearchResult>,
559
560 /// Result metadata
561 pub result_metadata: HashMap<String, String>,
562
563 /// Metadata
564 pub metadata: CacheEntryMetadata,
565}
566
567/// Similar entry for semantic cache
568#[derive(Debug, Clone, Serialize, Deserialize)]
569pub struct SimilarEntry {
570 /// Similar text
571 pub text: String,
572
573 /// Similarity score
574 pub similarity: f32,
575
576 /// When added
577 pub added_at: SystemTime,
578}
579
580/// Cached search result
581#[derive(Debug, Clone, Serialize, Deserialize)]
582pub struct CachedSearchResult {
583 /// Document ID
584 pub document_id: String,
585
586 /// Document content
587 pub content: String,
588
589 /// Relevance score
590 pub score: f32,
591
592 /// Result rank
593 pub rank: usize,
594
595 /// Additional metadata
596 pub metadata: HashMap<String, String>,
597}
598
599/// Cache statistics
600#[derive(Debug, Clone, Serialize, Deserialize)]
601pub struct CacheStats {
602 /// Total number of entries
603 pub total_entries: usize,
604
605 /// Total cache hits
606 pub hits: u64,
607
608 /// Total cache misses
609 pub misses: u64,
610
611 /// Hit rate percentage
612 pub hit_rate: f32,
613
614 /// Total memory usage in bytes
615 pub memory_usage: usize,
616
617 /// Average access time in microseconds
618 pub avg_access_time_us: f32,
619
620 /// Eviction count
621 pub evictions: u64,
622
623 /// Last cleanup time
624 pub last_cleanup: SystemTime,
625}
626
627/// Cache performance metrics
628#[derive(Debug, Clone, Serialize, Deserialize)]
629pub struct CacheMetrics {
630 /// Query cache metrics
631 pub query_cache: CacheStats,
632
633 /// Embedding cache metrics
634 pub embedding_cache: CacheStats,
635
636 /// Semantic cache metrics
637 pub semantic_cache: CacheStats,
638
639 /// Result cache metrics
640 pub result_cache: CacheStats,
641
642 /// Overall performance metrics
643 pub overall: OverallCacheMetrics,
644}
645
646/// Overall cache performance metrics
647#[derive(Debug, Clone, Serialize, Deserialize)]
648pub struct OverallCacheMetrics {
649 /// Total memory saved (bytes)
650 pub memory_saved: usize,
651
652 /// Total time saved (milliseconds)
653 pub time_saved_ms: f32,
654
655 /// Cache efficiency score
656 pub efficiency_score: f32,
657
658 /// Memory pressure level (0.0 to 1.0)
659 pub memory_pressure: f32,
660
661 /// Total operations per second
662 pub ops_per_second: f32,
663}
664
665impl CacheService {
666 /// Create new cache service
667 pub fn new(config: CacheConfig) -> RragResult<Self> {
668 let query_cache = Box::new(query_cache::QueryCache::new(config.query_cache.clone())?);
669
670 let embedding_cache = Box::new(embedding_cache::EmbeddingCache::new(
671 config.embedding_cache.clone(),
672 )?);
673
674 let semantic_cache = Box::new(semantic_cache::SemanticCache::new(
675 config.semantic_cache.clone(),
676 )?);
677
678 let result_cache = Box::new(result_cache::ResultCache::new(config.result_cache.clone())?);
679
680 Ok(Self {
681 query_cache,
682 embedding_cache,
683 semantic_cache,
684 result_cache,
685 config,
686 metrics: CacheMetrics::default(),
687 })
688 }
689
690 /// Get cached query results
691 pub async fn get_query_results(&self, query: &str) -> Option<QueryCacheEntry> {
692 if !self.config.enabled || !self.config.query_cache.enabled {
693 return None;
694 }
695
696 self.query_cache.get(&query.to_string())
697 }
698
699 /// Cache query results
700 pub async fn cache_query_results(
701 &mut self,
702 query: String,
703 entry: QueryCacheEntry,
704 ) -> RragResult<()> {
705 if !self.config.enabled || !self.config.query_cache.enabled {
706 return Ok(());
707 }
708
709 self.query_cache.put(query, entry)
710 }
711
712 /// Get cached embedding
713 pub async fn get_embedding(&self, text: &str, model: &str) -> Option<Vec<f32>> {
714 if !self.config.enabled || !self.config.embedding_cache.enabled {
715 return None;
716 }
717
718 let key = format!("{}:{}", model, text);
719 self.embedding_cache.get(&key).map(|entry| entry.embedding)
720 }
721
722 /// Cache embedding
723 pub async fn cache_embedding(
724 &mut self,
725 text: String,
726 model: String,
727 embedding: Vec<f32>,
728 ) -> RragResult<()> {
729 if !self.config.enabled || !self.config.embedding_cache.enabled {
730 return Ok(());
731 }
732
733 let key = format!("{}:{}", model, text);
734 let entry = EmbeddingCacheEntry {
735 text: text.clone(),
736 text_hash: Self::hash_string(&text),
737 embedding,
738 model,
739 metadata: CacheEntryMetadata::new(),
740 };
741
742 self.embedding_cache.put(key, entry)
743 }
744
745 /// Get semantically similar cached results
746 pub async fn get_semantic_results(&self, query: &str) -> Option<SemanticCacheEntry> {
747 if !self.config.enabled || !self.config.semantic_cache.enabled {
748 return None;
749 }
750
751 self.semantic_cache.get(&query.to_string())
752 }
753
754 /// Cache semantic results
755 pub async fn cache_semantic_results(
756 &mut self,
757 query: String,
758 entry: SemanticCacheEntry,
759 ) -> RragResult<()> {
760 if !self.config.enabled || !self.config.semantic_cache.enabled {
761 return Ok(());
762 }
763
764 self.semantic_cache.put(query, entry)
765 }
766
767 /// Get cache metrics
768 pub fn get_metrics(&self) -> &CacheMetrics {
769 &self.metrics
770 }
771
772 /// Clear all caches
773 pub fn clear_all(&mut self) {
774 self.query_cache.clear();
775 self.embedding_cache.clear();
776 self.semantic_cache.clear();
777 self.result_cache.clear();
778 }
779
780 /// Perform cache maintenance
781 pub async fn maintenance(&mut self) -> RragResult<()> {
782 // Background cleanup, eviction, persistence, etc.
783 Ok(())
784 }
785
786 /// Hash string for cache keys
787 fn hash_string(s: &str) -> String {
788 use std::collections::hash_map::DefaultHasher;
789 let mut hasher = DefaultHasher::new();
790 s.hash(&mut hasher);
791 format!("{:x}", hasher.finish())
792 }
793}
794
795impl CacheEntryMetadata {
796 /// Create new metadata
797 pub fn new() -> Self {
798 let now = SystemTime::now();
799 Self {
800 created_at: now,
801 last_accessed: now,
802 access_count: 0,
803 size_bytes: 0,
804 ttl: None,
805 custom: HashMap::new(),
806 }
807 }
808
809 /// Update access info
810 pub fn accessed(&mut self) {
811 self.last_accessed = SystemTime::now();
812 self.access_count += 1;
813 }
814
815 /// Check if entry has expired
816 pub fn is_expired(&self) -> bool {
817 if let Some(ttl) = self.ttl {
818 if let Ok(elapsed) = self.created_at.elapsed() {
819 return elapsed > ttl;
820 }
821 }
822 false
823 }
824}
825
826impl Default for CacheConfig {
827 fn default() -> Self {
828 Self {
829 enabled: true,
830 query_cache: QueryCacheConfig::default(),
831 embedding_cache: EmbeddingCacheConfig::default(),
832 semantic_cache: SemanticCacheConfig::default(),
833 result_cache: ResultCacheConfig::default(),
834 persistence: PersistenceConfig::default(),
835 performance: PerformanceConfig::default(),
836 }
837 }
838}
839
840impl Default for QueryCacheConfig {
841 fn default() -> Self {
842 Self {
843 enabled: true,
844 max_size: 1000,
845 ttl: Duration::from_secs(3600), // 1 hour
846 eviction_policy: EvictionPolicy::LRU,
847 similarity_threshold: 0.95,
848 }
849 }
850}
851
852impl Default for EmbeddingCacheConfig {
853 fn default() -> Self {
854 Self {
855 enabled: true,
856 max_size: 10000,
857 ttl: Duration::from_secs(86400), // 24 hours
858 eviction_policy: EvictionPolicy::LFU,
859 compression_enabled: true,
860 }
861 }
862}
863
864impl Default for SemanticCacheConfig {
865 fn default() -> Self {
866 Self {
867 enabled: true,
868 max_size: 5000,
869 ttl: Duration::from_secs(7200), // 2 hours
870 similarity_threshold: 0.85,
871 clustering_enabled: true,
872 max_clusters: 100,
873 }
874 }
875}
876
877impl Default for ResultCacheConfig {
878 fn default() -> Self {
879 Self {
880 enabled: true,
881 max_size: 2000,
882 ttl: Duration::from_secs(1800), // 30 minutes
883 eviction_policy: EvictionPolicy::TTL,
884 compress_large_results: true,
885 }
886 }
887}
888
889impl Default for PersistenceConfig {
890 fn default() -> Self {
891 Self {
892 enabled: false,
893 storage_path: "./cache".to_string(),
894 auto_save_interval: Duration::from_secs(300), // 5 minutes
895 format: PersistenceFormat::Binary,
896 }
897 }
898}
899
900impl Default for PerformanceConfig {
901 fn default() -> Self {
902 Self {
903 async_writes: true,
904 batch_operations: true,
905 background_cleanup: true,
906 memory_pressure_threshold: 0.8,
907 }
908 }
909}
910
911impl Default for CacheStats {
912 fn default() -> Self {
913 Self {
914 total_entries: 0,
915 hits: 0,
916 misses: 0,
917 hit_rate: 0.0,
918 memory_usage: 0,
919 avg_access_time_us: 0.0,
920 evictions: 0,
921 last_cleanup: SystemTime::now(),
922 }
923 }
924}
925
926impl Default for CacheMetrics {
927 fn default() -> Self {
928 Self {
929 query_cache: CacheStats::default(),
930 embedding_cache: CacheStats::default(),
931 semantic_cache: CacheStats::default(),
932 result_cache: CacheStats::default(),
933 overall: OverallCacheMetrics::default(),
934 }
935 }
936}
937
938impl Default for OverallCacheMetrics {
939 fn default() -> Self {
940 Self {
941 memory_saved: 0,
942 time_saved_ms: 0.0,
943 efficiency_score: 0.0,
944 memory_pressure: 0.0,
945 ops_per_second: 0.0,
946 }
947 }
948}
949
950#[cfg(test)]
951mod tests {
952 use super::*;
953
954 #[tokio::test]
955 async fn test_cache_service_creation() {
956 let config = CacheConfig::default();
957 let cache_service = CacheService::new(config).unwrap();
958
959 let metrics = cache_service.get_metrics();
960 assert_eq!(metrics.overall.efficiency_score, 0.0);
961 }
962
963 #[test]
964 fn test_cache_entry_metadata() {
965 let mut metadata = CacheEntryMetadata::new();
966 assert_eq!(metadata.access_count, 0);
967
968 metadata.accessed();
969 assert_eq!(metadata.access_count, 1);
970 }
971
972 #[test]
973 fn test_cache_config_defaults() {
974 let config = CacheConfig::default();
975 assert!(config.enabled);
976 assert!(config.query_cache.enabled);
977 assert!(config.embedding_cache.enabled);
978 }
979}