use moka::future::Cache;
use crate::types::retrieval::RetrieveResult;
pub struct RagMemoryCache {
cache: Cache<String, RetrieveResult>,
}
impl RagMemoryCache {
pub fn new(max_entries: usize, ttl_seconds: u64) -> Self {
let cache = Cache::builder()
.max_capacity(max_entries as u64)
.time_to_live(std::time::Duration::from_secs(ttl_seconds))
.build();
Self { cache }
}
pub async fn get(&self, key: &str) -> Option<RetrieveResult> {
self.cache.get(key).await
}
pub async fn set(&self, key: &str, value: RetrieveResult) {
self.cache.insert(key.to_string(), value).await;
}
pub fn invalidate(&self, _namespace: &str) {
self.cache.invalidate_all();
}
}
#[cfg(all(test, feature = "caching"))]
mod tests {
use super::*;
fn make_result() -> RetrieveResult {
RetrieveResult {
hits: vec![],
channel_report: std::collections::HashMap::new(),
latency_ms: 42,
effective_query: "test query".to_string(),
}
}
#[tokio::test]
async fn invalidate_evicts_entries() {
let cache = RagMemoryCache::new(100, 3600);
cache.set("key1", make_result()).await;
assert!(cache.get("key1").await.is_some());
cache.invalidate("test_namespace");
assert!(cache.get("key1").await.is_none());
}
#[tokio::test]
async fn invalidate_evicts_all_entries() {
let cache = RagMemoryCache::new(100, 3600);
cache.set("key1", make_result()).await;
cache.set("key2", make_result()).await;
assert!(cache.get("key1").await.is_some());
assert!(cache.get("key2").await.is_some());
cache.invalidate("test_namespace");
assert!(cache.get("key1").await.is_none());
assert!(cache.get("key2").await.is_none());
}
#[tokio::test]
async fn set_after_invalidate_still_works() {
let cache = RagMemoryCache::new(100, 3600);
cache.set("key1", make_result()).await;
cache.invalidate("test_namespace");
cache.set("key1", make_result()).await;
assert!(cache.get("key1").await.is_some());
}
}