Skip to main content

ck_search/mcp/
cache.rs

1use std::collections::HashMap;
2use std::path::PathBuf;
3use std::sync::Arc;
4use std::time::{Duration, SystemTime};
5use tokio::sync::RwLock;
6
7// EmbedderCache removed - embedder caching would require deeper integration with ck-engine.
8// Currently, search functions create embedders internally via ck_embed::create_embedder().
9// Future optimization: expose search APIs that accept pre-created embedders for true caching.
10
11/// Cache for index statistics with TTL. Stores the full `ck_index::IndexStats`
12/// so cache hits report the same fields as fresh reads (chunk counts, embedded
13/// chunk counts, on-disk index size).
14pub use ck_index::IndexStats;
15
16#[derive(Clone)]
17pub struct StatsCache {
18    stats: Arc<RwLock<HashMap<PathBuf, (IndexStats, SystemTime)>>>,
19    ttl: Duration,
20}
21
22impl StatsCache {
23    pub fn new(ttl: Duration) -> Self {
24        Self {
25            stats: Arc::new(RwLock::new(HashMap::new())),
26            ttl,
27        }
28    }
29
30    pub async fn get(&self, path: &PathBuf) -> Option<IndexStats> {
31        let stats = self.stats.read().await;
32        if let Some((stats, cached_at)) = stats.get(path)
33            && cached_at.elapsed().unwrap_or_default() < self.ttl
34        {
35            return Some(stats.clone());
36        }
37        None
38    }
39
40    pub async fn update(&self, path: PathBuf, stats: IndexStats) {
41        let mut cache = self.stats.write().await;
42        cache.insert(path, (stats, SystemTime::now()));
43    }
44
45    pub async fn invalidate(&self, path: &PathBuf) {
46        let mut cache = self.stats.write().await;
47        cache.remove(path);
48    }
49}
50
51impl Default for StatsCache {
52    /// Create a stats cache with default 30-second TTL optimized for MCP server usage
53    fn default() -> Self {
54        Self::new(Duration::from_secs(30))
55    }
56}