html_translation_lib/storage/
smart_cache.rs

1//! 智能缓存系统
2//!
3//! 实现多层缓存策略,包括LRU、频率权重和预测性缓存
4
5use crate::config::TranslationConfig;
6use crate::error::{TranslationError, TranslationResult};
7use std::collections::{HashMap, BTreeSet};
8use std::hash::{Hash, Hasher};
9use std::time::{Duration, SystemTime, UNIX_EPOCH};
10use std::sync::atomic::{AtomicU64, Ordering};
11
12/// 智能缓存管理器
13pub struct SmartCacheManager {
14    /// L1缓存:最近使用的翻译(快速访问)
15    l1_cache: LRUCache<u64, CachedTranslation>,
16    
17    /// L2缓存:频率权重缓存(中等频率访问)
18    l2_cache: FrequencyWeightedCache<u64, CachedTranslation>,
19    
20    /// 缓存统计
21    stats: CacheStats,
22    
23    /// 配置
24    config: CacheConfig,
25    
26    /// 预测器
27    predictor: CachePredictor,
28}
29
30/// 缓存配置
31#[derive(Debug, Clone)]
32pub struct CacheConfig {
33    /// L1缓存大小
34    pub l1_size: usize,
35    /// L2缓存大小
36    pub l2_size: usize,
37    /// TTL时间
38    pub ttl: Duration,
39    /// 启用预测性缓存
40    pub enable_prediction: bool,
41    /// 最大内存使用(字节)
42    pub max_memory_bytes: usize,
43}
44
45impl Default for CacheConfig {
46    fn default() -> Self {
47        Self {
48            l1_size: 1000,
49            l2_size: 5000,
50            ttl: Duration::from_secs(3600),
51            enable_prediction: true,
52            max_memory_bytes: 100 * 1024 * 1024, // 100MB
53        }
54    }
55}
56
57/// 缓存的翻译项
58#[derive(Debug, Clone)]
59pub struct CachedTranslation {
60    /// 翻译结果
61    pub translation: String,
62    /// 创建时间
63    pub created_at: SystemTime,
64    /// 访问次数
65    pub access_count: u32,
66    /// 最后访问时间
67    pub last_accessed: SystemTime,
68    /// 内容哈希(用于验证)
69    pub content_hash: u64,
70    /// 预估内存使用
71    pub memory_size: usize,
72}
73
74impl CachedTranslation {
75    /// 创建新的缓存翻译
76    pub fn new(translation: String, original_text: &str) -> Self {
77        let now = SystemTime::now();
78        let memory_size = translation.len() * 2 + 64; // 估算内存使用
79        
80        let mut hasher = std::collections::hash_map::DefaultHasher::new();
81        original_text.hash(&mut hasher);
82        let content_hash = hasher.finish();
83        
84        Self {
85            translation,
86            created_at: now,
87            access_count: 1,
88            last_accessed: now,
89            content_hash,
90            memory_size,
91        }
92    }
93    
94    /// 是否过期
95    pub fn is_expired(&self, ttl: Duration) -> bool {
96        self.created_at.elapsed().unwrap_or(Duration::ZERO) > ttl
97    }
98    
99    /// 记录访问
100    pub fn mark_accessed(&mut self) {
101        self.access_count += 1;
102        self.last_accessed = SystemTime::now();
103    }
104    
105    /// 获取访问频率(每分钟)
106    pub fn access_frequency(&self) -> f64 {
107        let elapsed = self.created_at.elapsed().unwrap_or(Duration::from_secs(1));
108        let minutes = elapsed.as_secs_f64() / 60.0;
109        self.access_count as f64 / minutes.max(1.0)
110    }
111}
112
113/// LRU缓存实现
114pub struct LRUCache<K, V> {
115    map: HashMap<K, V>,
116    order: BTreeSet<(SystemTime, K)>,
117    capacity: usize,
118    memory_usage: AtomicU64,
119}
120
121impl<K: Hash + Eq + Clone + Ord, V: Clone> LRUCache<K, V> {
122    pub fn new(capacity: usize) -> Self {
123        Self {
124            map: HashMap::with_capacity(capacity),
125            order: BTreeSet::new(),
126            capacity,
127            memory_usage: AtomicU64::new(0),
128        }
129    }
130    
131    pub fn get(&mut self, key: &K) -> Option<&mut V> {
132        if let Some(value) = self.map.get_mut(key) {
133            // 更新访问顺序
134            let now = SystemTime::now();
135            self.order.insert((now, key.clone()));
136            Some(value)
137        } else {
138            None
139        }
140    }
141    
142    pub fn insert(&mut self, key: K, value: V) -> Option<V> 
143    where
144        V: AsRef<CachedTranslation>,
145    {
146        let now = SystemTime::now();
147        
148        // 如果缓存已满,移除最旧的项
149        if self.map.len() >= self.capacity {
150            if let Some(&(_, ref oldest_key)) = self.order.iter().next() {
151                let oldest_key = oldest_key.clone();
152                self.remove(&oldest_key);
153            }
154        }
155        
156        self.order.insert((now, key.clone()));
157        let old_value = self.map.insert(key, value);
158        
159        old_value
160    }
161    
162    pub fn remove(&mut self, key: &K) -> Option<V> {
163        if let Some(value) = self.map.remove(key) {
164            // 清理order中的条目(简化实现,在实际使用中可能需要更精确的清理)
165            self.order.retain(|(_, k)| k != key);
166            Some(value)
167        } else {
168            None
169        }
170    }
171    
172    pub fn len(&self) -> usize {
173        self.map.len()
174    }
175    
176    pub fn is_empty(&self) -> bool {
177        self.map.is_empty()
178    }
179}
180
181/// 频率权重缓存
182pub struct FrequencyWeightedCache<K, V> {
183    map: HashMap<K, V>,
184    frequency_scores: HashMap<K, f64>,
185    capacity: usize,
186}
187
188impl<K: Hash + Eq + Clone, V: Clone> FrequencyWeightedCache<K, V> {
189    pub fn new(capacity: usize) -> Self {
190        Self {
191            map: HashMap::with_capacity(capacity),
192            frequency_scores: HashMap::with_capacity(capacity),
193            capacity,
194        }
195    }
196    
197    pub fn get(&mut self, key: &K) -> Option<&mut V> 
198    where
199        V: AsMut<CachedTranslation>,
200    {
201        if let Some(value) = self.map.get_mut(key) {
202            // 更新频率分数
203            let score = self.frequency_scores.entry(key.clone()).or_insert(1.0);
204            *score *= 1.1; // 增加权重
205            
206            // 标记为已访问
207            value.as_mut().mark_accessed();
208            
209            Some(value)
210        } else {
211            None
212        }
213    }
214    
215    pub fn insert(&mut self, key: K, value: V) -> Option<V> 
216    where
217        V: AsRef<CachedTranslation>,
218    {
219        // 如果缓存已满,移除频率最低的项
220        if self.map.len() >= self.capacity {
221            if let Some((lowest_key, _)) = self.frequency_scores
222                .iter()
223                .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
224                .map(|(k, v)| (k.clone(), *v))
225            {
226                self.remove(&lowest_key);
227            }
228        }
229        
230        self.frequency_scores.insert(key.clone(), 1.0);
231        self.map.insert(key, value)
232    }
233    
234    pub fn remove(&mut self, key: &K) -> Option<V> {
235        self.frequency_scores.remove(key);
236        self.map.remove(key)
237    }
238    
239    pub fn len(&self) -> usize {
240        self.map.len()
241    }
242}
243
244/// 缓存预测器
245pub struct CachePredictor {
246    /// 访问模式历史
247    access_patterns: HashMap<String, Vec<SystemTime>>,
248    /// 共现模式(经常一起出现的翻译对)
249    co_occurrence: HashMap<String, HashMap<String, u32>>,
250}
251
252impl CachePredictor {
253    pub fn new() -> Self {
254        Self {
255            access_patterns: HashMap::new(),
256            co_occurrence: HashMap::new(),
257        }
258    }
259    
260    /// 记录访问模式
261    pub fn record_access(&mut self, text: &str) {
262        let pattern = self.access_patterns.entry(text.to_string()).or_default();
263        pattern.push(SystemTime::now());
264        
265        // 只保留最近的访问记录(避免内存无限增长)
266        if pattern.len() > 50 {
267            pattern.drain(0..pattern.len() - 50);
268        }
269    }
270    
271    /// 记录共现模式
272    pub fn record_co_occurrence(&mut self, texts: &[String]) {
273        for (i, text1) in texts.iter().enumerate() {
274            for text2 in texts.iter().skip(i + 1) {
275                let entry = self.co_occurrence
276                    .entry(text1.clone())
277                    .or_default()
278                    .entry(text2.clone())
279                    .or_insert(0);
280                *entry += 1;
281            }
282        }
283    }
284    
285    /// 预测接下来可能需要的翻译
286    pub fn predict_next(&self, recent_texts: &[String]) -> Vec<String> {
287        let mut predictions = HashMap::new();
288        
289        for text in recent_texts {
290            if let Some(co_occurs) = self.co_occurrence.get(text) {
291                for (co_text, &count) in co_occurs {
292                    *predictions.entry(co_text.clone()).or_insert(0) += count;
293                }
294            }
295        }
296        
297        let mut sorted: Vec<_> = predictions.into_iter().collect();
298        sorted.sort_by_key(|(_, count)| *count);
299        sorted.reverse();
300        
301        sorted.into_iter()
302            .take(10) // 最多预测10个
303            .map(|(text, _)| text)
304            .collect()
305    }
306}
307
308impl SmartCacheManager {
309    /// 创建新的智能缓存管理器
310    pub async fn new(translation_config: &TranslationConfig) -> TranslationResult<Self> {
311        let config = CacheConfig {
312            l1_size: 1000,
313            l2_size: translation_config.max_cache_entries.saturating_sub(1000),
314            ttl: translation_config.cache_ttl,
315            enable_prediction: true,
316            max_memory_bytes: 100 * 1024 * 1024,
317        };
318        
319        Ok(Self {
320            l1_cache: LRUCache::new(config.l1_size),
321            l2_cache: FrequencyWeightedCache::new(config.l2_size),
322            stats: CacheStats::default(),
323            predictor: CachePredictor::new(),
324            config,
325        })
326    }
327    
328    /// 获取翻译(智能缓存查找)
329    pub async fn get(&mut self, text: &str) -> TranslationResult<Option<String>> {
330        let key = self.hash_text(text);
331        
332        // 记录访问模式
333        if self.config.enable_prediction {
334            self.predictor.record_access(text);
335        }
336        
337        // L1缓存查找
338        if let Some(cached) = self.l1_cache.get(&key) {
339            if !cached.is_expired(self.config.ttl) {
340                cached.mark_accessed();
341                self.stats.l1_hits += 1;
342                return Ok(Some(cached.translation.clone()));
343            } else {
344                // 过期,移除
345                self.l1_cache.remove(&key);
346            }
347        }
348        
349        // L2缓存查找
350        if let Some(cached) = self.l2_cache.get(&key) {
351            if !cached.as_ref().is_expired(self.config.ttl) {
352                self.stats.l2_hits += 1;
353                // 将热点数据提升到L1
354                let translation = cached.as_ref().translation.clone();
355                if cached.as_ref().access_frequency() > 1.0 { // 每分钟访问超过1次
356                    let cached_clone = cached.as_ref().clone();
357                    self.l1_cache.insert(key, cached_clone);
358                }
359                return Ok(Some(translation));
360            } else {
361                // 过期,移除
362                self.l2_cache.remove(&key);
363            }
364        }
365        
366        self.stats.misses += 1;
367        Ok(None)
368    }
369    
370    /// 设置翻译到缓存
371    pub async fn set(&mut self, text: &str, translation: String) -> TranslationResult<()> {
372        let key = self.hash_text(text);
373        let cached = CachedTranslation::new(translation, text);
374        
375        // 检查内存使用
376        self.check_memory_usage().await?;
377        
378        // 智能选择缓存层级
379        if self.should_cache_in_l1(text) {
380            self.l1_cache.insert(key, cached);
381        } else {
382            self.l2_cache.insert(key, cached);
383        }
384        
385        Ok(())
386    }
387    
388    /// 批量预热缓存
389    pub async fn preload(&mut self, translations: Vec<(String, String)>) -> TranslationResult<()> {
390        for (text, translation) in translations {
391            self.set(&text, translation).await?;
392        }
393        Ok(())
394    }
395    
396    /// 获取缓存统计
397    pub fn stats(&self) -> &CacheStats {
398        &self.stats
399    }
400    
401    /// 清理过期缓存
402    pub async fn cleanup_expired(&mut self) -> TranslationResult<()> {
403        let now = SystemTime::now();
404        let ttl = self.config.ttl;
405        
406        // 这里需要一个更复杂的实现来清理过期项
407        // 为了简化,我们重置统计
408        self.stats.cleanups_performed += 1;
409        
410        Ok(())
411    }
412    
413    /// 计算文本哈希
414    fn hash_text(&self, text: &str) -> u64 {
415        use std::collections::hash_map::DefaultHasher;
416        let mut hasher = DefaultHasher::new();
417        text.hash(&mut hasher);
418        hasher.finish()
419    }
420    
421    /// 判断是否应该缓存到L1
422    fn should_cache_in_l1(&self, text: &str) -> bool {
423        // 短文本更可能被重复访问
424        text.len() < 100 || 
425        // 常见模式(可以扩展更复杂的启发式规则)
426        text.contains("button") || text.contains("click") || text.contains("submit")
427    }
428    
429    /// 检查内存使用
430    async fn check_memory_usage(&mut self) -> TranslationResult<()> {
431        // 简化的内存检查(实际实现需要更精确的内存统计)
432        let estimated_memory = (self.l1_cache.len() + self.l2_cache.len()) * 100; // 粗略估计
433        
434        if estimated_memory > self.config.max_memory_bytes {
435            // 触发清理
436            self.cleanup_expired().await?;
437        }
438        
439        Ok(())
440    }
441}
442
443/// 缓存统计信息
444#[derive(Debug, Default, Clone)]
445pub struct CacheStats {
446    /// L1缓存命中
447    pub l1_hits: u64,
448    /// L2缓存命中  
449    pub l2_hits: u64,
450    /// 缓存未命中
451    pub misses: u64,
452    /// 执行的清理次数
453    pub cleanups_performed: u64,
454    /// 预测成功次数
455    pub predictions_successful: u64,
456}
457
458impl CacheStats {
459    /// 计算总命中率
460    pub fn hit_rate(&self) -> f64 {
461        let total_requests = self.l1_hits + self.l2_hits + self.misses;
462        if total_requests > 0 {
463            (self.l1_hits + self.l2_hits) as f64 / total_requests as f64
464        } else {
465            0.0
466        }
467    }
468    
469    /// 计算L1命中率
470    pub fn l1_hit_rate(&self) -> f64 {
471        let total_requests = self.l1_hits + self.l2_hits + self.misses;
472        if total_requests > 0 {
473            self.l1_hits as f64 / total_requests as f64
474        } else {
475            0.0
476        }
477    }
478}
479
480// 为CachedTranslation实现必要的traits
481impl AsRef<CachedTranslation> for CachedTranslation {
482    fn as_ref(&self) -> &CachedTranslation {
483        self
484    }
485}
486
487impl AsMut<CachedTranslation> for CachedTranslation {
488    fn as_mut(&mut self) -> &mut CachedTranslation {
489        self
490    }
491}
492
493impl Default for CachePredictor {
494    fn default() -> Self {
495        Self::new()
496    }
497}
498
499#[cfg(test)]
500mod tests {
501    use super::*;
502    
503    #[test]
504    fn test_lru_cache() {
505        let mut cache = LRUCache::new(2);
506        let item1 = CachedTranslation::new("translation1".to_string(), "text1");
507        let item2 = CachedTranslation::new("translation2".to_string(), "text2");
508        let item3 = CachedTranslation::new("translation3".to_string(), "text3");
509        
510        cache.insert("key1", item1);
511        cache.insert("key2", item2);
512        
513        assert_eq!(cache.len(), 2);
514        
515        // 添加第三个项应该移除最旧的
516        cache.insert("key3", item3);
517        assert_eq!(cache.len(), 2);
518        assert!(cache.get(&"key1").is_none()); // 应该被移除
519    }
520    
521    #[test]
522    fn test_cached_translation() {
523        let cached = CachedTranslation::new("hello".to_string(), "原文");
524        assert!(!cached.is_expired(Duration::from_secs(3600)));
525        assert!(cached.is_expired(Duration::from_nanos(1)));
526    }
527    
528    #[tokio::test]
529    async fn test_smart_cache_manager() {
530        let config = TranslationConfig::default();
531        let mut cache = SmartCacheManager::new(&config).await.unwrap();
532        
533        // 测试缓存未命中
534        let result = cache.get("test").await.unwrap();
535        assert!(result.is_none());
536        
537        // 设置缓存
538        cache.set("test", "测试".to_string()).await.unwrap();
539        
540        // 测试缓存命中
541        let result = cache.get("test").await.unwrap();
542        assert_eq!(result.unwrap(), "测试");
543        
544        // 验证统计
545        assert_eq!(cache.stats().misses, 1);
546        assert_eq!(cache.stats().l1_hits, 1);
547    }
548}