use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use crate::model::StarTriple;
#[derive(Debug)]
pub struct StarCache {
triple_cache: Arc<RwLock<HashMap<String, Vec<StarTriple>>>>,
pattern_cache: Arc<RwLock<HashMap<String, Vec<StarTriple>>>>,
config: CacheConfig,
access_frequency: Arc<RwLock<HashMap<String, usize>>>,
stats: Arc<RwLock<CacheStatistics>>,
}
#[derive(Debug, Clone)]
pub struct CacheConfig {
pub max_triple_entries: usize,
pub max_pattern_entries: usize,
pub ttl_seconds: u64,
pub enable_lru: bool,
pub optimization_threshold: f64,
}
impl Default for CacheConfig {
fn default() -> Self {
Self {
max_triple_entries: 10000,
max_pattern_entries: 5000,
ttl_seconds: 300, enable_lru: true,
optimization_threshold: 0.8,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct CacheStatistics {
pub hits: u64,
pub misses: u64,
pub evictions: u64,
pub total_lookups: u64,
}
impl CacheStatistics {
pub fn hit_rate(&self) -> f64 {
if self.total_lookups == 0 {
0.0
} else {
self.hits as f64 / self.total_lookups as f64
}
}
}
impl StarCache {
pub fn new(config: CacheConfig) -> Self {
Self {
triple_cache: Arc::new(RwLock::new(HashMap::new())),
pattern_cache: Arc::new(RwLock::new(HashMap::new())),
config,
access_frequency: Arc::new(RwLock::new(HashMap::new())),
stats: Arc::new(RwLock::new(CacheStatistics::default())),
}
}
pub fn get(&self, key: &str) -> Option<Vec<StarTriple>> {
let mut stats = self.stats.write().unwrap_or_else(|e| e.into_inner());
stats.total_lookups += 1;
if let Some(results) = self
.triple_cache
.read()
.unwrap_or_else(|e| e.into_inner())
.get(key)
{
stats.hits += 1;
let mut freq = self
.access_frequency
.write()
.unwrap_or_else(|e| e.into_inner());
*freq.entry(key.to_string()).or_insert(0) += 1;
return Some(results.clone());
}
if let Some(results) = self
.pattern_cache
.read()
.unwrap_or_else(|e| e.into_inner())
.get(key)
{
stats.hits += 1;
let mut freq = self
.access_frequency
.write()
.unwrap_or_else(|e| e.into_inner());
*freq.entry(key.to_string()).or_insert(0) += 1;
return Some(results.clone());
}
stats.misses += 1;
None
}
pub fn put(&self, key: String, results: Vec<StarTriple>) {
if self.config.enable_lru {
let mut cache = self.triple_cache.write().unwrap_or_else(|e| e.into_inner());
if cache.len() >= self.config.max_triple_entries {
if let Some(lfu_key) = self.find_least_frequent_key() {
cache.remove(&lfu_key);
let mut stats = self.stats.write().unwrap_or_else(|e| e.into_inner());
stats.evictions += 1;
}
}
cache.insert(key, results);
}
}
fn find_least_frequent_key(&self) -> Option<String> {
let freq = self
.access_frequency
.read()
.unwrap_or_else(|e| e.into_inner());
freq.iter()
.min_by_key(|&(_, &count)| count)
.map(|(key, _)| key.clone())
}
pub fn get_statistics(&self) -> CacheStatistics {
self.stats.read().unwrap_or_else(|e| e.into_inner()).clone()
}
pub fn clear(&self) {
self.triple_cache
.write()
.unwrap_or_else(|e| e.into_inner())
.clear();
self.pattern_cache
.write()
.unwrap_or_else(|e| e.into_inner())
.clear();
self.access_frequency
.write()
.unwrap_or_else(|e| e.into_inner())
.clear();
}
}