pub mod cache_core;
pub mod embedding_cache;
pub mod metrics;
pub mod persistence;
pub mod policies;
pub mod query_cache;
pub mod result_cache;
pub mod semantic_cache;
use crate::RragResult;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::time::{Duration, SystemTime};
pub struct CacheService {
query_cache: Box<dyn Cache<String, QueryCacheEntry>>,
embedding_cache: Box<dyn Cache<String, EmbeddingCacheEntry>>,
semantic_cache: Box<dyn Cache<String, SemanticCacheEntry>>,
result_cache: Box<dyn Cache<String, ResultCacheEntry>>,
config: CacheConfig,
metrics: CacheMetrics,
}
#[derive(Debug, Clone)]
pub struct CacheConfig {
pub enabled: bool,
pub query_cache: QueryCacheConfig,
pub embedding_cache: EmbeddingCacheConfig,
pub semantic_cache: SemanticCacheConfig,
pub result_cache: ResultCacheConfig,
pub persistence: PersistenceConfig,
pub performance: PerformanceConfig,
}
#[derive(Debug, Clone)]
pub struct QueryCacheConfig {
pub enabled: bool,
pub max_size: usize,
pub ttl: Duration,
pub eviction_policy: EvictionPolicy,
pub similarity_threshold: f32,
}
#[derive(Debug, Clone)]
pub struct EmbeddingCacheConfig {
pub enabled: bool,
pub max_size: usize,
pub ttl: Duration,
pub eviction_policy: EvictionPolicy,
pub compression_enabled: bool,
}
#[derive(Debug, Clone)]
pub struct SemanticCacheConfig {
pub enabled: bool,
pub max_size: usize,
pub ttl: Duration,
pub similarity_threshold: f32,
pub clustering_enabled: bool,
pub max_clusters: usize,
}
#[derive(Debug, Clone)]
pub struct ResultCacheConfig {
pub enabled: bool,
pub max_size: usize,
pub ttl: Duration,
pub eviction_policy: EvictionPolicy,
pub compress_large_results: bool,
}
#[derive(Debug, Clone)]
pub struct PersistenceConfig {
pub enabled: bool,
pub storage_path: String,
pub auto_save_interval: Duration,
pub format: PersistenceFormat,
}
#[derive(Debug, Clone)]
pub struct PerformanceConfig {
pub async_writes: bool,
pub batch_operations: bool,
pub background_cleanup: bool,
pub memory_pressure_threshold: f32,
}
#[derive(Debug, Clone, Copy)]
pub enum EvictionPolicy {
LRU,
LFU,
TTL,
ARC,
SemanticAware,
}
#[derive(Debug, Clone)]
pub enum PersistenceFormat {
Binary,
Json,
MessagePack,
}
pub trait Cache<K, V>: Send + Sync
where
K: Hash + Eq + Clone + Send + Sync + 'static,
V: Clone + Send + Sync + 'static,
{
fn get(&self, key: &K) -> Option<V>;
fn put(&mut self, key: K, value: V) -> RragResult<()>;
fn remove(&mut self, key: &K) -> Option<V>;
fn contains(&self, key: &K) -> bool;
fn clear(&mut self);
fn size(&self) -> usize;
fn stats(&self) -> CacheStats;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CacheEntryMetadata {
pub created_at: SystemTime,
pub last_accessed: SystemTime,
pub access_count: u64,
pub size_bytes: usize,
pub ttl: Option<Duration>,
pub custom: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryCacheEntry {
pub query: String,
pub embedding_hash: String,
pub results: Vec<CachedSearchResult>,
pub generated_answer: Option<String>,
pub metadata: CacheEntryMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmbeddingCacheEntry {
pub text: String,
pub text_hash: String,
pub embedding: Vec<f32>,
pub model: String,
pub metadata: CacheEntryMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SemanticCacheEntry {
pub representative: String,
pub cluster_id: Option<usize>,
pub similar_entries: Vec<SimilarEntry>,
pub results: Vec<CachedSearchResult>,
pub metadata: CacheEntryMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResultCacheEntry {
pub params_hash: String,
pub results: Vec<CachedSearchResult>,
pub result_metadata: HashMap<String, String>,
pub metadata: CacheEntryMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SimilarEntry {
pub text: String,
pub similarity: f32,
pub added_at: SystemTime,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CachedSearchResult {
pub document_id: String,
pub content: String,
pub score: f32,
pub rank: usize,
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CacheStats {
pub total_entries: usize,
pub hits: u64,
pub misses: u64,
pub hit_rate: f32,
pub memory_usage: usize,
pub avg_access_time_us: f32,
pub evictions: u64,
pub last_cleanup: SystemTime,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CacheMetrics {
pub query_cache: CacheStats,
pub embedding_cache: CacheStats,
pub semantic_cache: CacheStats,
pub result_cache: CacheStats,
pub overall: OverallCacheMetrics,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OverallCacheMetrics {
pub memory_saved: usize,
pub time_saved_ms: f32,
pub efficiency_score: f32,
pub memory_pressure: f32,
pub ops_per_second: f32,
}
impl CacheService {
pub fn new(config: CacheConfig) -> RragResult<Self> {
let query_cache = Box::new(query_cache::QueryCache::new(config.query_cache.clone())?);
let embedding_cache = Box::new(embedding_cache::EmbeddingCache::new(
config.embedding_cache.clone(),
)?);
let semantic_cache = Box::new(semantic_cache::SemanticCache::new(
config.semantic_cache.clone(),
)?);
let result_cache = Box::new(result_cache::ResultCache::new(config.result_cache.clone())?);
Ok(Self {
query_cache,
embedding_cache,
semantic_cache,
result_cache,
config,
metrics: CacheMetrics::default(),
})
}
pub async fn get_query_results(&self, query: &str) -> Option<QueryCacheEntry> {
if !self.config.enabled || !self.config.query_cache.enabled {
return None;
}
self.query_cache.get(&query.to_string())
}
pub async fn cache_query_results(
&mut self,
query: String,
entry: QueryCacheEntry,
) -> RragResult<()> {
if !self.config.enabled || !self.config.query_cache.enabled {
return Ok(());
}
self.query_cache.put(query, entry)
}
pub async fn get_embedding(&self, text: &str, model: &str) -> Option<Vec<f32>> {
if !self.config.enabled || !self.config.embedding_cache.enabled {
return None;
}
let key = format!("{}:{}", model, text);
self.embedding_cache.get(&key).map(|entry| entry.embedding)
}
pub async fn cache_embedding(
&mut self,
text: String,
model: String,
embedding: Vec<f32>,
) -> RragResult<()> {
if !self.config.enabled || !self.config.embedding_cache.enabled {
return Ok(());
}
let key = format!("{}:{}", model, text);
let entry = EmbeddingCacheEntry {
text: text.clone(),
text_hash: Self::hash_string(&text),
embedding,
model,
metadata: CacheEntryMetadata::new(),
};
self.embedding_cache.put(key, entry)
}
pub async fn get_semantic_results(&self, query: &str) -> Option<SemanticCacheEntry> {
if !self.config.enabled || !self.config.semantic_cache.enabled {
return None;
}
self.semantic_cache.get(&query.to_string())
}
pub async fn cache_semantic_results(
&mut self,
query: String,
entry: SemanticCacheEntry,
) -> RragResult<()> {
if !self.config.enabled || !self.config.semantic_cache.enabled {
return Ok(());
}
self.semantic_cache.put(query, entry)
}
pub fn get_metrics(&self) -> &CacheMetrics {
&self.metrics
}
pub fn clear_all(&mut self) {
self.query_cache.clear();
self.embedding_cache.clear();
self.semantic_cache.clear();
self.result_cache.clear();
}
pub async fn maintenance(&mut self) -> RragResult<()> {
Ok(())
}
fn hash_string(s: &str) -> String {
use std::collections::hash_map::DefaultHasher;
let mut hasher = DefaultHasher::new();
s.hash(&mut hasher);
format!("{:x}", hasher.finish())
}
}
impl CacheEntryMetadata {
pub fn new() -> Self {
let now = SystemTime::now();
Self {
created_at: now,
last_accessed: now,
access_count: 0,
size_bytes: 0,
ttl: None,
custom: HashMap::new(),
}
}
pub fn accessed(&mut self) {
self.last_accessed = SystemTime::now();
self.access_count += 1;
}
pub fn is_expired(&self) -> bool {
if let Some(ttl) = self.ttl {
if let Ok(elapsed) = self.created_at.elapsed() {
return elapsed > ttl;
}
}
false
}
}
impl Default for CacheConfig {
fn default() -> Self {
Self {
enabled: true,
query_cache: QueryCacheConfig::default(),
embedding_cache: EmbeddingCacheConfig::default(),
semantic_cache: SemanticCacheConfig::default(),
result_cache: ResultCacheConfig::default(),
persistence: PersistenceConfig::default(),
performance: PerformanceConfig::default(),
}
}
}
impl Default for QueryCacheConfig {
fn default() -> Self {
Self {
enabled: true,
max_size: 1000,
ttl: Duration::from_secs(3600), eviction_policy: EvictionPolicy::LRU,
similarity_threshold: 0.95,
}
}
}
impl Default for EmbeddingCacheConfig {
fn default() -> Self {
Self {
enabled: true,
max_size: 10000,
ttl: Duration::from_secs(86400), eviction_policy: EvictionPolicy::LFU,
compression_enabled: true,
}
}
}
impl Default for SemanticCacheConfig {
fn default() -> Self {
Self {
enabled: true,
max_size: 5000,
ttl: Duration::from_secs(7200), similarity_threshold: 0.85,
clustering_enabled: true,
max_clusters: 100,
}
}
}
impl Default for ResultCacheConfig {
fn default() -> Self {
Self {
enabled: true,
max_size: 2000,
ttl: Duration::from_secs(1800), eviction_policy: EvictionPolicy::TTL,
compress_large_results: true,
}
}
}
impl Default for PersistenceConfig {
fn default() -> Self {
Self {
enabled: false,
storage_path: "./cache".to_string(),
auto_save_interval: Duration::from_secs(300), format: PersistenceFormat::Binary,
}
}
}
impl Default for PerformanceConfig {
fn default() -> Self {
Self {
async_writes: true,
batch_operations: true,
background_cleanup: true,
memory_pressure_threshold: 0.8,
}
}
}
impl Default for CacheStats {
fn default() -> Self {
Self {
total_entries: 0,
hits: 0,
misses: 0,
hit_rate: 0.0,
memory_usage: 0,
avg_access_time_us: 0.0,
evictions: 0,
last_cleanup: SystemTime::now(),
}
}
}
impl Default for CacheMetrics {
fn default() -> Self {
Self {
query_cache: CacheStats::default(),
embedding_cache: CacheStats::default(),
semantic_cache: CacheStats::default(),
result_cache: CacheStats::default(),
overall: OverallCacheMetrics::default(),
}
}
}
impl Default for OverallCacheMetrics {
fn default() -> Self {
Self {
memory_saved: 0,
time_saved_ms: 0.0,
efficiency_score: 0.0,
memory_pressure: 0.0,
ops_per_second: 0.0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_cache_service_creation() {
let config = CacheConfig::default();
let cache_service = CacheService::new(config).unwrap();
let metrics = cache_service.get_metrics();
assert_eq!(metrics.overall.efficiency_score, 0.0);
}
#[test]
fn test_cache_entry_metadata() {
let mut metadata = CacheEntryMetadata::new();
assert_eq!(metadata.access_count, 0);
metadata.accessed();
assert_eq!(metadata.access_count, 1);
}
#[test]
fn test_cache_config_defaults() {
let config = CacheConfig::default();
assert!(config.enabled);
assert!(config.query_cache.enabled);
assert!(config.embedding_cache.enabled);
}
}