aether/sandbox/
module_cache.rs

1//! 模块缓存生命周期管理
2//!
3//! 提供显式的模块缓存管理 API,支持 TTL 和容量限制。
4
5use crate::value::Value;
6use std::collections::HashMap;
7use std::sync::RwLock;
8use std::time::{Duration, Instant};
9
10/// 模块缓存条目
11#[derive(Debug, Clone)]
12struct ModuleCacheEntry {
13    /// 导出的符号
14    exports: HashMap<String, Value>,
15    /// 加载时间
16    loaded_at: Instant,
17    /// 访问次数
18    #[allow(dead_code)]
19    access_count: usize,
20}
21
22/// 模块缓存统计信息
23#[derive(Debug, Clone)]
24pub struct ModuleCacheStats {
25    /// 当前缓存的模块数量
26    pub module_count: usize,
27    /// 总加载次数
28    pub total_loads: usize,
29    /// 缓存命中次数
30    pub cache_hits: usize,
31    /// 缓存未命中次数
32    pub cache_misses: usize,
33    /// 命中率
34    pub hit_rate: f64,
35}
36
37/// 模块缓存管理器
38pub struct ModuleCacheManager {
39    /// 缓存存储
40    cache: RwLock<HashMap<String, ModuleCacheEntry>>,
41    /// 最大缓存数量
42    max_size: usize,
43    /// TTL(秒)
44    ttl_secs: u64,
45    /// 统计信息
46    stats: RwLock<ModuleCacheStats>,
47}
48
49impl ModuleCacheManager {
50    /// 创建新的缓存管理器
51    pub fn new(max_size: usize, ttl_secs: u64) -> Self {
52        Self {
53            cache: RwLock::new(HashMap::new()),
54            max_size,
55            ttl_secs,
56            stats: RwLock::new(ModuleCacheStats {
57                module_count: 0,
58                total_loads: 0,
59                cache_hits: 0,
60                cache_misses: 0,
61                hit_rate: 0.0,
62            }),
63        }
64    }
65
66    /// 获取模块导出(如果存在且未过期)
67    pub fn get(&self, module_id: &str) -> Option<HashMap<String, Value>> {
68        let cache = self.cache.read().ok()?;
69        let entry = cache.get(module_id)?;
70
71        // 检查 TTL
72        if self.ttl_secs > 0 {
73            let elapsed = entry.loaded_at.elapsed().as_secs();
74            if elapsed > self.ttl_secs {
75                return None; // 已过期
76            }
77        }
78
79        // 更新统计
80        if let Ok(mut stats) = self.stats.write() {
81            stats.cache_hits += 1;
82            stats.total_loads += 1;
83            if stats.total_loads > 0 {
84                stats.hit_rate = stats.cache_hits as f64 / stats.total_loads as f64;
85            }
86        }
87
88        Some(entry.exports.clone())
89    }
90
91    /// 插入模块导出
92    pub fn insert(&self, module_id: String, exports: HashMap<String, Value>) {
93        // 检查容量限制
94        if self.max_size > 0 {
95            let mut cache = self.cache.write().unwrap();
96            if cache.len() >= self.max_size {
97                // 清理最旧的 10% 条目
98                let to_remove = (self.max_size / 10).max(1);
99                self.evict_oldest(&mut cache, to_remove);
100            }
101
102            cache.insert(
103                module_id.clone(),
104                ModuleCacheEntry {
105                    exports,
106                    loaded_at: Instant::now(),
107                    access_count: 0,
108                },
109            );
110
111            // 更新统计
112            if let Ok(mut stats) = self.stats.write() {
113                stats.cache_misses += 1;
114                stats.total_loads += 1;
115                stats.module_count = cache.len();
116                if stats.total_loads > 0 {
117                    stats.hit_rate = stats.cache_hits as f64 / stats.total_loads as f64;
118                }
119            }
120        }
121    }
122
123    /// 清理过期条目
124    pub fn cleanup_expired(&self) {
125        if self.ttl_secs == 0 {
126            return;
127        }
128
129        let ttl = Duration::from_secs(self.ttl_secs);
130        let mut cache = self.cache.write().unwrap();
131        let now = Instant::now();
132
133        cache.retain(|_, entry| now.duration_since(entry.loaded_at) < ttl);
134
135        if let Ok(mut stats) = self.stats.write() {
136            stats.module_count = cache.len();
137        }
138    }
139
140    /// 清空所有缓存
141    pub fn clear(&self) {
142        let mut cache = self.cache.write().unwrap();
143        cache.clear();
144
145        if let Ok(mut stats) = self.stats.write() {
146            stats.module_count = 0;
147            // 保留命中率统计,不清零
148        }
149    }
150
151    /// 移除特定模块缓存
152    pub fn remove(&self, module_id: &str) -> bool {
153        let mut cache = self.cache.write().unwrap();
154        let removed = cache.remove(module_id).is_some();
155
156        if removed && let Ok(mut stats) = self.stats.write() {
157            stats.module_count = cache.len();
158        }
159
160        removed
161    }
162
163    /// 获取统计信息
164    pub fn stats(&self) -> ModuleCacheStats {
165        self.stats.read().unwrap().clone()
166    }
167
168    /// 获取缓存的模块 ID 列表
169    pub fn cached_modules(&self) -> Vec<String> {
170        self.cache.read().unwrap().keys().cloned().collect()
171    }
172
173    /// 清理最旧的条目(内部方法)
174    fn evict_oldest(&self, cache: &mut HashMap<String, ModuleCacheEntry>, count: usize) {
175        // 收集需要移除的模块 ID
176        let mut entries: Vec<_> = cache.iter().collect();
177        entries.sort_by_key(|(_, entry)| entry.loaded_at);
178
179        let to_remove: Vec<String> = entries
180            .iter()
181            .take(count)
182            .map(|(module_id, _)| (*module_id).clone())
183            .collect();
184
185        // 现在可以安全地移除
186        for module_id in to_remove {
187            cache.remove(&module_id);
188        }
189    }
190}
191
192impl Default for ModuleCacheManager {
193    fn default() -> Self {
194        Self::new(100, 0)
195    }
196}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201
202    #[test]
203    fn test_module_cache_basic() {
204        let manager = ModuleCacheManager::new(10, 0);
205
206        // 插入模块
207        let mut exports = HashMap::new();
208        exports.insert("foo".to_string(), Value::Number(42.0));
209        manager.insert("test_module".to_string(), exports.clone());
210
211        // 获取模块
212        let retrieved = manager.get("test_module");
213        assert!(retrieved.is_some());
214        assert_eq!(retrieved.unwrap().get("foo").unwrap(), &Value::Number(42.0));
215    }
216
217    #[test]
218    fn test_module_cache_miss() {
219        let manager = ModuleCacheManager::new(10, 0);
220
221        // 获取不存在的模块
222        let retrieved = manager.get("nonexistent");
223        assert!(retrieved.is_none());
224
225        // 检查统计
226        let stats = manager.stats();
227        assert_eq!(stats.cache_misses, 0); // 还没有插入
228        assert_eq!(stats.cache_hits, 0);
229    }
230
231    #[test]
232    fn test_module_cache_max_size() {
233        let manager = ModuleCacheManager::new(3, 0); // 最多 3 个
234        let exports = HashMap::new();
235
236        // 插入 5 个模块
237        for i in 0..5 {
238            manager.insert(format!("module{}", i), exports.clone());
239        }
240
241        // 应该只保留 3 个
242        assert_eq!(manager.cached_modules().len(), 3);
243        assert_eq!(manager.stats().module_count, 3);
244    }
245
246    #[test]
247    fn test_module_cache_clear() {
248        let manager = ModuleCacheManager::new(10, 0);
249        let mut exports = HashMap::new();
250        exports.insert("foo".to_string(), Value::Number(42.0));
251
252        manager.insert("test".to_string(), exports);
253        assert_eq!(manager.cached_modules().len(), 1);
254
255        manager.clear();
256        assert_eq!(manager.cached_modules().len(), 0);
257    }
258
259    #[test]
260    fn test_module_cache_remove() {
261        let manager = ModuleCacheManager::new(10, 0);
262        let mut exports = HashMap::new();
263        exports.insert("foo".to_string(), Value::Number(42.0));
264
265        manager.insert("test".to_string(), exports);
266        assert!(manager.remove("test"));
267        assert!(!manager.remove("nonexistent"));
268        assert_eq!(manager.cached_modules().len(), 0);
269    }
270
271    #[test]
272    fn test_module_cache_ttl() {
273        let manager = ModuleCacheManager::new(10, 1); // 1秒 TTL
274        let mut exports = HashMap::new();
275        exports.insert("foo".to_string(), Value::Number(42.0));
276
277        manager.insert("test".to_string(), exports.clone());
278
279        // 立即获取应该成功
280        assert!(manager.get("test").is_some());
281
282        // 等待 2 秒后应该过期
283        std::thread::sleep(Duration::from_secs(2));
284        assert!(manager.get("test").is_none());
285    }
286
287    #[test]
288    fn test_module_cache_stats() {
289        let manager = ModuleCacheManager::new(10, 0);
290        let mut exports = HashMap::new();
291        exports.insert("foo".to_string(), Value::Number(42.0));
292
293        // 插入模块
294        manager.insert("test".to_string(), exports.clone());
295
296        // 命中
297        manager.get("test");
298        manager.get("test");
299
300        // 未命中
301        manager.get("nonexistent");
302
303        let stats = manager.stats();
304        assert_eq!(stats.total_loads, 3);
305        assert_eq!(stats.cache_hits, 2);
306        assert_eq!(stats.cache_misses, 1);
307        assert!((stats.hit_rate - 0.666).abs() < 0.01); // 约 66.6%
308    }
309}