Skip to main content

sz_orm_core/
l2_cache.rs

1//! L2 二级缓存(Level-2 Cache)
2//!
3//! 对应文档 6.8 节改进项 21(L2 二级缓存)。
4//!
5//! # 核心概念
6//!
7//! - **L2Cache**:跨 Session 共享的二级缓存(与 Hibernate L2 Cache / MyBatis 二级缓存对应)
8//! - **CacheKey**:统一缓存键构造(table + pk 或 table + query_hash)
9//! - **L2CacheStats**:命中率统计(hits/misses/evictions/sets)
10//! - **表级失效**:`invalidate_table(table)` 一次失效某表的所有缓存项
11//!
12//! 与 L1 缓存(Session 级别)的区别:
13//! - L1:单次 Session/请求 内有效,事务结束自动清空
14//! - L2:跨 Session 共享,进程级缓存,需显式失效
15//!
16//! # 设计灵感
17//!
18//! - Hibernate L2 Cache(`@Cache` / `@Cacheable`)
19//! - MyBatis 二级缓存(`<cache>` 标签)
20//! - Rails `Rails.cache`
21//! - Django cache framework
22//!
23//! # 使用示例
24//!
25//! ```no_run
26//! use sz_orm_core::l2_cache::{L2Cache, CacheKey};
27//! use sz_orm_core::Value;
28//!
29//! // 1. 创建 L2 缓存
30//! let cache = L2Cache::new();
31//!
32//! // 2. 缓存单行(pk 维度)
33//! let key = CacheKey::by_pk("users", 1);
34//! cache.put(&key, Value::String("Alice".to_string()), None);
35//!
36//! // 3. 读取
37//! let val = cache.get(&key);
38//! assert!(val.is_some());
39//!
40//! // 4. 表级失效(用户表更新后)
41//! cache.invalidate_table("users");
42//! assert!(cache.get(&key).is_none());
43//!
44//! // 5. 命中率统计
45//! let stats = cache.stats();
46//! println!("hit rate: {:.2}%", stats.hit_rate() * 100.0);
47//! ```
48
49use crate::cache::Cache;
50use crate::error::CacheError;
51use crate::value::Value;
52use std::collections::HashMap;
53use std::future::Future;
54use std::pin::Pin;
55use std::sync::{Arc, RwLock};
56use std::time::Duration;
57// #72 修复:使用 tokio::time::Instant 替代 std::time::Instant
58// tokio::time::Instant 支持 tokio::time::pause() 测试辅助,
59// 允许测试在不真实睡眠的情况下控制时间流逝。
60// 在非测试环境(未调用 pause)下,行为与 std::time::Instant 完全一致。
61use tokio::time::Instant;
62
63// ============================================================================
64// InvalidationBus — 缓存失效消息总线(跨实例失效)
65// ============================================================================
66
67/// 缓存失效消息
68#[derive(Debug, Clone)]
69pub enum InvalidationMessage {
70    /// 失效单个 key
71    InvalidateKey(String),
72    /// 失效整张表
73    InvalidateTable(String),
74    /// 失效所有缓存
75    InvalidateAll,
76}
77
78/// 缓存失效总线 trait
79///
80/// 用于跨实例缓存失效:当一个实例失效了某张表的缓存时,
81/// 通过总线通知其他订阅者同步失效。
82pub trait InvalidationBus: Send + Sync {
83    /// 发布失效消息
84    fn publish(&self, message: InvalidationMessage);
85    /// 订阅失效消息(返回一个迭代器,drain 当前缓冲的消息)
86    fn subscribe(&self) -> Box<dyn Iterator<Item = InvalidationMessage> + Send>;
87}
88
89/// 进程内失效总线(单实例用)
90///
91/// 基于 `tokio::sync::broadcast` 实现多订阅者广播。
92/// `subscribe()` 返回的迭代器会 drain 当前已缓冲但未消费的消息。
93pub struct LocalInvalidationBus {
94    tx: tokio::sync::broadcast::Sender<InvalidationMessage>,
95}
96
97impl LocalInvalidationBus {
98    /// 创建进程内失效总线,`capacity` 为广播缓冲区容量
99    pub fn new(capacity: usize) -> Self {
100        let (tx, _rx) = tokio::sync::broadcast::channel(capacity.max(1));
101        Self { tx }
102    }
103}
104
105impl Default for LocalInvalidationBus {
106    fn default() -> Self {
107        Self::new(256)
108    }
109}
110
111impl InvalidationBus for LocalInvalidationBus {
112    fn publish(&self, message: InvalidationMessage) {
113        // 忽略无订阅者的错误
114        let _ = self.tx.send(message);
115    }
116
117    fn subscribe(&self) -> Box<dyn Iterator<Item = InvalidationMessage> + Send> {
118        let mut rx = self.tx.subscribe();
119        Box::new(std::iter::from_fn(move || loop {
120            match rx.try_recv() {
121                Ok(msg) => return Some(msg),
122                // 缓冲区为空或通道已关闭 → 终止迭代
123                Err(tokio::sync::broadcast::error::TryRecvError::Empty)
124                | Err(tokio::sync::broadcast::error::TryRecvError::Closed) => return None,
125                // 滞后(订阅者落后太多)→ 跳过丢失的消息,继续读下一条
126                Err(tokio::sync::broadcast::error::TryRecvError::Lagged(_)) => continue,
127            }
128        }))
129    }
130}
131
132// ============================================================================
133// CacheKey — 统一缓存键
134// ============================================================================
135
136/// 统一缓存键
137///
138/// 通过 `table` + `kind` + `identifier` 三元组唯一标识一个缓存项:
139/// - `table`:表名(用于表级失效)
140/// - `kind`:缓存类型(ByPk / ByQuery / ByRelation)
141/// - `identifier`:具体标识(pk 值 / 查询哈希 / 关联键)
142#[derive(Debug, Clone, PartialEq, Eq, Hash)]
143pub struct CacheKey {
144    /// 表名
145    pub table: String,
146    /// 缓存类型
147    pub kind: CacheKeyKind,
148    /// 具体标识
149    pub identifier: String,
150}
151
152/// 缓存键类型
153#[derive(Debug, Clone, PartialEq, Eq, Hash)]
154pub enum CacheKeyKind {
155    /// 按主键缓存
156    ByPk,
157    /// 按查询条件缓存
158    ByQuery,
159    /// 按关联关系缓存
160    ByRelation,
161}
162
163impl CacheKey {
164    /// 构造主键维度的缓存键
165    pub fn by_pk(table: impl Into<String>, pk: impl std::fmt::Display) -> Self {
166        Self {
167            table: table.into(),
168            kind: CacheKeyKind::ByPk,
169            identifier: pk.to_string(),
170        }
171    }
172
173    /// 构造查询维度的缓存键(identifier 通常是 SQL + params 的哈希)
174    pub fn by_query(table: impl Into<String>, query_hash: impl std::fmt::Display) -> Self {
175        Self {
176            table: table.into(),
177            kind: CacheKeyKind::ByQuery,
178            identifier: query_hash.to_string(),
179        }
180    }
181
182    /// 构造关联维度的缓存键
183    pub fn by_relation(table: impl Into<String>, relation: impl std::fmt::Display) -> Self {
184        Self {
185            table: table.into(),
186            kind: CacheKeyKind::ByRelation,
187            identifier: relation.to_string(),
188        }
189    }
190
191    /// 序列化为字符串(用于底层存储键)
192    pub fn to_string_key(&self) -> String {
193        let kind_str = match self.kind {
194            CacheKeyKind::ByPk => "pk",
195            CacheKeyKind::ByQuery => "q",
196            CacheKeyKind::ByRelation => "rel",
197        };
198        format!("l2:{}:{}:{}", self.table, kind_str, self.identifier)
199    }
200}
201
202impl std::fmt::Display for CacheKey {
203    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204        write!(f, "{}", self.to_string_key())
205    }
206}
207
208// ============================================================================
209// L2CacheStats — 命中率统计
210// ============================================================================
211
212/// L2 缓存命中率统计
213#[derive(Debug, Clone, Default)]
214pub struct L2CacheStats {
215    /// 命中次数
216    pub hits: u64,
217    /// 未命中次数
218    pub misses: u64,
219    /// 设置次数
220    pub sets: u64,
221    /// 失效次数(含单键和表级失效)
222    pub evictions: u64,
223    /// 当前缓存项数量
224    pub size: usize,
225}
226
227/// 按表分桶的命中率统计
228///
229/// 用于细粒度观察每张表的缓存命中情况,
230/// 识别"热点表"与"低命中表",指导缓存策略调整。
231#[derive(Debug, Clone, Default)]
232pub struct PerTableStats {
233    /// 命中次数
234    pub hits: u64,
235    /// 未命中次数
236    pub misses: u64,
237    /// 设置次数
238    pub sets: u64,
239    /// 失效次数
240    pub evictions: u64,
241}
242
243impl PerTableStats {
244    /// 总查询次数(hits + misses)
245    pub fn total_lookups(&self) -> u64 {
246        self.hits + self.misses
247    }
248
249    /// 命中率(0.0 ~ 1.0)
250    pub fn hit_rate(&self) -> f64 {
251        let total = self.total_lookups();
252        if total == 0 {
253            0.0
254        } else {
255            self.hits as f64 / total as f64
256        }
257    }
258}
259
260impl L2CacheStats {
261    /// 总查询次数(hits + misses)
262    pub fn total_lookups(&self) -> u64 {
263        self.hits + self.misses
264    }
265
266    /// 命中率(0.0 ~ 1.0)
267    pub fn hit_rate(&self) -> f64 {
268        let total = self.total_lookups();
269        if total == 0 {
270            0.0
271        } else {
272            self.hits as f64 / total as f64
273        }
274    }
275
276    /// 未命中率(0.0 ~ 1.0)
277    pub fn miss_rate(&self) -> f64 {
278        1.0 - self.hit_rate()
279    }
280
281    /// 合并两个统计(用于多分片汇总)
282    pub fn merge(&mut self, other: &L2CacheStats) {
283        self.hits += other.hits;
284        self.misses += other.misses;
285        self.sets += other.sets;
286        self.evictions += other.evictions;
287        self.size += other.size;
288    }
289}
290
291// ============================================================================
292// CacheEntry — 缓存项
293// ============================================================================
294
295/// 缓存项(值 + 过期时间)
296#[derive(Debug, Clone)]
297struct CacheEntry {
298    /// 缓存值
299    value: Value,
300    /// 过期时间(None 表示永不过期)
301    expires_at: Option<Instant>,
302}
303
304impl CacheEntry {
305    fn new(value: Value, ttl: Option<Duration>) -> Self {
306        // Duration::MAX 会导致 Instant::now() + Duration::MAX 溢出
307        // 将其视为永不过期(expires_at = None),与 None 语义一致
308        let expires_at = ttl.and_then(|d| {
309            if d == Duration::MAX {
310                None
311            } else {
312                Some(Instant::now() + d)
313            }
314        });
315        Self { value, expires_at }
316    }
317
318    fn is_expired(&self) -> bool {
319        self.expires_at
320            .map(|t| t <= Instant::now())
321            .unwrap_or(false)
322    }
323}
324
325// ============================================================================
326// LruOrder — O(1) LRU 顺序跟踪器(arena 双向链表 + HashMap)
327// ============================================================================
328
329/// LRU 顺序跟踪器 — 所有操作 O(1)
330///
331/// 基于 arena(Vec<LruNode>)的双向链表 + HashMap 索引实现:
332/// - `touch(key)`:将 key 移到 MRU 端(已存在则摘链+追加,新 key 直接追加)— O(1)
333/// - `remove(key)`:从链表中摘除并回收节点 — O(1)
334/// - `lru_key()`:返回 LRU 端的 key — O(1)
335/// - `iter_keys()`:从 LRU 到 MRU 遍历 — O(n)
336///
337/// 相比 `Vec<String>` + `retain` 方案(touch/remove 为 O(n)),本实现将高频操作
338/// 降为 O(1),仅遍历(用于查找过期 key)保持 O(n)。
339struct LruOrder {
340    /// 节点池(arena):节点索引即数组下标
341    nodes: Vec<LruNode>,
342    /// 空闲节点列表(复用已删除节点的槽位,避免 Vec 无限增长)
343    free_list: Vec<usize>,
344    /// key → 节点索引
345    index: HashMap<String, usize>,
346    /// 链表头(LRU 端,淘汰时从此处取)
347    head: Option<usize>,
348    /// 链表尾(MRU 端,新访问的加入此处)
349    tail: Option<usize>,
350}
351
352/// 双向链表节点
353struct LruNode {
354    key: String,
355    prev: Option<usize>,
356    next: Option<usize>,
357}
358
359impl LruOrder {
360    fn new() -> Self {
361        Self {
362            nodes: Vec::new(),
363            free_list: Vec::new(),
364            index: HashMap::new(),
365            head: None,
366            tail: None,
367        }
368    }
369
370    /// 触碰 key:已存在则移到尾部,不存在则创建并追加到尾部 — O(1)
371    fn touch(&mut self, key: &str) {
372        if let Some(&idx) = self.index.get(key) {
373            self.unlink(idx);
374            self.link_tail(idx);
375        } else {
376            let idx = self.alloc_node(key.to_string());
377            self.link_tail(idx);
378            self.index.insert(key.to_string(), idx);
379        }
380    }
381
382    /// 移除 key — O(1)
383    fn remove(&mut self, key: &str) {
384        if let Some(idx) = self.index.remove(key) {
385            self.unlink(idx);
386            self.free_node(idx);
387        }
388    }
389
390    /// 返回 LRU 端的 key(最久未访问) — O(1)
391    fn lru_key(&self) -> Option<&str> {
392        self.head.map(|idx| self.nodes[idx].key.as_str())
393    }
394
395    /// 从 LRU 到 MRU 遍历所有 key — O(n)
396    fn iter_keys(&self) -> impl Iterator<Item = &str> {
397        LruIter {
398            nodes: &self.nodes,
399            current: self.head,
400        }
401    }
402
403    /// 清空所有 — O(n)(需释放 Vec/HashMap 内存)
404    fn clear(&mut self) {
405        self.nodes.clear();
406        self.free_list.clear();
407        self.index.clear();
408        self.head = None;
409        self.tail = None;
410    }
411
412    /// 当前元素数量 — O(1)
413    #[allow(dead_code)]
414    fn len(&self) -> usize {
415        self.index.len()
416    }
417
418    /// 分配节点(优先复用空闲槽位)
419    fn alloc_node(&mut self, key: String) -> usize {
420        if let Some(idx) = self.free_list.pop() {
421            self.nodes[idx] = LruNode {
422                key,
423                prev: None,
424                next: None,
425            };
426            idx
427        } else {
428            self.nodes.push(LruNode {
429                key,
430                prev: None,
431                next: None,
432            });
433            self.nodes.len() - 1
434        }
435    }
436
437    /// 回收节点到空闲列表
438    fn free_node(&mut self, idx: usize) {
439        self.free_list.push(idx);
440    }
441
442    /// 从链表中摘除节点(仅修改前后指针,不释放节点)
443    fn unlink(&mut self, idx: usize) {
444        let prev = self.nodes[idx].prev;
445        let next = self.nodes[idx].next;
446        match prev {
447            Some(p) => self.nodes[p].next = next,
448            None => self.head = next,
449        }
450        match next {
451            Some(n) => self.nodes[n].prev = prev,
452            None => self.tail = prev,
453        }
454        self.nodes[idx].prev = None;
455        self.nodes[idx].next = None;
456    }
457
458    /// 将节点链接到链表尾部(MRU 端)
459    fn link_tail(&mut self, idx: usize) {
460        match self.tail {
461            Some(t) => {
462                self.nodes[t].next = Some(idx);
463                self.nodes[idx].prev = Some(t);
464            }
465            None => self.head = Some(idx),
466        }
467        self.nodes[idx].next = None;
468        self.tail = Some(idx);
469    }
470}
471
472/// LRU 链表迭代器(从 LRU 端到 MRU 端)
473struct LruIter<'a> {
474    nodes: &'a [LruNode],
475    current: Option<usize>,
476}
477
478impl<'a> Iterator for LruIter<'a> {
479    type Item = &'a str;
480
481    fn next(&mut self) -> Option<Self::Item> {
482        let idx = self.current?;
483        let node = &self.nodes[idx];
484        self.current = node.next;
485        Some(node.key.as_str())
486    }
487}
488
489// ============================================================================
490// L2Cache — 跨 Session 共享的二级缓存
491// ============================================================================
492
493/// L2 二级缓存 — 跨 Session 共享
494///
495/// 线程安全:内部使用 RwLock,可在多线程环境下共享。
496///
497/// # 示例
498///
499/// ```
500/// use sz_orm_core::l2_cache::{L2Cache, CacheKey};
501/// use sz_orm_core::Value;
502/// use std::time::Duration;
503///
504/// let cache = L2Cache::new();
505///
506/// // 缓存单行
507/// let key = CacheKey::by_pk("users", 1);
508/// cache.put(&key, Value::String("Alice".to_string()), None);
509///
510/// // 读取
511/// assert!(cache.get(&key).is_some());
512///
513/// // 表级失效
514/// cache.invalidate_table("users");
515/// assert!(cache.get(&key).is_none());
516/// ```
517pub struct L2Cache {
518    /// 缓存数据
519    data: RwLock<HashMap<String, CacheEntry>>,
520    /// 表名索引(用于表级失效)— table -> Vec<key_string>(去重)
521    table_index: RwLock<HashMap<String, Vec<String>>>,
522    /// LRU 访问顺序跟踪器(O(1) touch/remove/lru_key,arena 双向链表 + HashMap)
523    ///
524    /// # 锁顺序约定
525    ///
526    /// 跨字段持锁时遵循:`data` → `access_order` → `table_index` → `stats`,
527    /// 避免死锁。本字段不允许在持 `data` 写锁时获取其他写锁。
528    access_order: RwLock<LruOrder>,
529    /// 全局统计信息
530    stats: RwLock<L2CacheStats>,
531    /// 按表分桶的统计信息(table -> PerTableStats)
532    table_stats: RwLock<HashMap<String, PerTableStats>>,
533    /// 默认 TTL(`put` 传 `None` 时使用,要"永不失效"请传 `Some(Duration::MAX)`)
534    default_ttl: Option<Duration>,
535    /// 最大容量(LRU 淘汰)
536    max_size: usize,
537    /// 缓存失效总线(可选,用于跨实例失效通知)
538    invalidation_bus: Option<Arc<dyn InvalidationBus>>,
539}
540
541impl Default for L2Cache {
542    fn default() -> Self {
543        Self::new()
544    }
545}
546
547impl L2Cache {
548    /// 创建 L2 缓存(默认容量 10000,无 TTL)
549    pub fn new() -> Self {
550        Self {
551            data: RwLock::new(HashMap::new()),
552            table_index: RwLock::new(HashMap::new()),
553            access_order: RwLock::new(LruOrder::new()),
554            stats: RwLock::new(L2CacheStats::default()),
555            table_stats: RwLock::new(HashMap::new()),
556            default_ttl: None,
557            max_size: 10_000,
558            invalidation_bus: None,
559        }
560    }
561
562    /// 设置默认 TTL
563    pub fn with_default_ttl(mut self, ttl: Duration) -> Self {
564        self.default_ttl = Some(ttl);
565        self
566    }
567
568    /// 设置最大容量
569    pub fn with_max_size(mut self, max_size: usize) -> Self {
570        self.max_size = max_size;
571        self
572    }
573
574    /// 设置缓存失效总线(用于跨实例失效通知)
575    pub fn with_invalidation_bus(mut self, bus: Arc<dyn InvalidationBus>) -> Self {
576        self.invalidation_bus = Some(bus);
577        self
578    }
579
580    /// 存入缓存项
581    ///
582    /// # TTL 语义
583    ///
584    /// - `ttl = Some(d)`:使用 `d` 作为过期时间
585    /// - `ttl = None`:使用 `default_ttl`(若未设置则永不过期)
586    /// - 要显式表示"永不失效",请传 `Some(Duration::MAX)`
587    pub fn put(&self, key: &CacheKey, value: Value, ttl: Option<Duration>) {
588        let actual_ttl = ttl.or(self.default_ttl);
589        let entry = CacheEntry::new(value, actual_ttl);
590        let key_str = key.to_string_key();
591
592        // 1. 写入数据 + LRU 淘汰
593        {
594            // 锁毒化时跳过写入,优雅降级
595            let mut data = match self.data.write() {
596                Ok(d) => d,
597                Err(_) => return,
598            };
599            let exists = data.contains_key(&key_str);
600            if !exists && data.len() >= self.max_size {
601                // LRU 淘汰:优先淘汰已过期的 key,否则淘汰 LRU 端(access_order 头部)
602                let victim = {
603                    // 不在持 data 写锁时获取 access_order 写锁,先读 access_order
604                    // 锁毒化时降级为 None(不淘汰),保留新插入项
605                    match self.access_order.read() {
606                        Ok(order) => {
607                            // 优先找已过期的 key(O(n) 遍历,仅缓存满时触发)
608                            // 分两步计算,避免闭包捕获 order 导致生命周期问题
609                            let expired = order
610                                .iter_keys()
611                                .find(|k| data.get(*k).map(|e| e.is_expired()).unwrap_or(false))
612                                .map(|s| s.to_string());
613                            let lru = order.lru_key().map(|s| s.to_string());
614                            expired.or(lru)
615                        }
616                        Err(_) => None,
617                    }
618                };
619                if let Some(victim) = victim {
620                    data.remove(&victim);
621                    // 同步清理 access_order(O(1) remove)
622                    // 锁毒化时跳过 LRU 顺序同步(不影响数据正确性)
623                    if let Ok(mut order) = self.access_order.write() {
624                        order.remove(&victim);
625                    }
626                }
627            }
628            data.insert(key_str.clone(), entry);
629        };
630
631        // 2. 更新 LRU 访问顺序(O(1) touch:新 key 追加尾部,已存在 key 移到尾部)
632        // 锁毒化时跳过 LRU 顺序更新(不影响数据正确性)
633        if let Ok(mut order) = self.access_order.write() {
634            order.touch(&key_str);
635        }
636
637        // 3. 更新表索引(去重,避免重复 push 导致 invalidate_table 统计错误)
638        // 锁毒化时跳过索引更新(invalidate_table 会遍历 data,影响仅限于失效精度)
639        if let Ok(mut idx) = self.table_index.write() {
640            let keys = idx.entry(key.table.clone()).or_default();
641            if !keys.contains(&key_str) {
642                keys.push(key_str);
643            }
644        }
645
646        // 4. 更新统计(不在此处读取 data.len(),避免锁顺序敏感)
647        // 锁毒化时跳过统计更新(不影响数据正确性)
648        if let Ok(mut stats) = self.stats.write() {
649            stats.sets += 1;
650        }
651        // 4.1 更新按表分桶统计
652        {
653            if let Ok(mut tbl_stats) = self.table_stats.write() {
654                tbl_stats.entry(key.table.clone()).or_default().sets += 1;
655            }
656        }
657    }
658
659    /// 读取缓存项(不存在或已过期返回 None)
660    ///
661    /// 命中时会更新 LRU 访问顺序(移到尾部)。
662    pub fn get(&self, key: &CacheKey) -> Option<Value> {
663        let key_str = key.to_string_key();
664        let table_name = key.table.clone();
665        let result = {
666            let data = self.data.read().ok()?;
667            if let Some(entry) = data.get(&key_str) {
668                if entry.is_expired() {
669                    None
670                } else {
671                    Some(entry.value.clone())
672                }
673            } else {
674                None
675            }
676        };
677
678        // 命中时更新 LRU 顺序(O(1) touch:移到尾部)
679        // 锁毒化时跳过 LRU 顺序更新(不影响数据正确性)
680        if result.is_some() {
681            if let Ok(mut order) = self.access_order.write() {
682                order.touch(&key_str);
683            }
684        }
685
686        // 更新全局统计
687        if let Ok(mut stats) = self.stats.write() {
688            if result.is_some() {
689                stats.hits += 1;
690            } else {
691                stats.misses += 1;
692            }
693        }
694        // 更新按表分桶统计
695        if let Ok(mut tbl_stats) = self.table_stats.write() {
696            let entry = tbl_stats.entry(table_name).or_default();
697            if result.is_some() {
698                entry.hits += 1;
699            } else {
700                entry.misses += 1;
701            }
702        }
703
704        result
705    }
706
707    /// 失效单个缓存项
708    pub fn invalidate(&self, key: &CacheKey) {
709        let key_str = key.to_string_key();
710        let table_name = key.table.clone();
711        let removed = {
712            // 锁毒化时跳过失效操作(视为未删除)
713            let mut data = match self.data.write() {
714                Ok(d) => d,
715                Err(_) => return,
716            };
717            data.remove(&key_str).is_some()
718        };
719        if removed {
720            // 锁毒化时跳过 LRU 顺序同步(不影响数据正确性)
721            if let Ok(mut order) = self.access_order.write() {
722                order.remove(&key_str);
723            }
724        }
725        if removed {
726            // 锁毒化时跳过统计更新(不影响数据正确性)
727            if let Ok(mut stats) = self.stats.write() {
728                stats.evictions += 1;
729            }
730            if let Ok(mut tbl_stats) = self.table_stats.write() {
731                tbl_stats.entry(table_name).or_default().evictions += 1;
732            }
733        }
734    }
735
736    /// 失效整张表的所有缓存项
737    ///
738    /// 仅统计实际从缓存中删除的 key 数量,避免 evictions 偏大。
739    /// 若设置了失效总线,会同时发布 `InvalidateTable` 消息通知其他实例。
740    pub fn invalidate_table(&self, table: &str) {
741        let keys_to_remove: Vec<String> = {
742            let idx = match self.table_index.read() {
743                Ok(i) => i,
744                Err(_) => return,
745            };
746            idx.get(table).cloned().unwrap_or_default()
747        };
748
749        let mut actually_removed: usize = 0;
750        {
751            // 锁毒化时跳过失效并直接返回(不发布总线通知)
752            let mut data = match self.data.write() {
753                Ok(d) => d,
754                Err(_) => return,
755            };
756            for k in &keys_to_remove {
757                if data.remove(k).is_some() {
758                    actually_removed += 1;
759                }
760            }
761        }
762
763        // O(m) 批量移除(m = keys_to_remove),而非旧实现的 O(n*m) retain
764        // 锁毒化时跳过 LRU 顺序同步(不影响数据正确性)
765        if actually_removed > 0 {
766            if let Ok(mut order) = self.access_order.write() {
767                for k in &keys_to_remove {
768                    order.remove(k);
769                }
770            }
771        }
772
773        if let Ok(mut idx) = self.table_index.write() {
774            idx.remove(table);
775        }
776        if actually_removed > 0 {
777            // 锁毒化时跳过统计更新(不影响数据正确性)
778            if let Ok(mut stats) = self.stats.write() {
779                stats.evictions += actually_removed as u64;
780            }
781            if let Ok(mut tbl_stats) = self.table_stats.write() {
782                tbl_stats.entry(table.to_string()).or_default().evictions +=
783                    actually_removed as u64;
784            }
785        }
786
787        // 发布失效消息到总线(通知其他订阅实例)
788        if let Some(bus) = &self.invalidation_bus {
789            bus.publish(InvalidationMessage::InvalidateTable(table.to_string()));
790        }
791    }
792
793    /// 清空所有缓存
794    pub fn clear(&self) {
795        let removed = {
796            // 锁毒化时跳过清空并直接返回
797            let mut data = match self.data.write() {
798                Ok(d) => d,
799                Err(_) => return,
800            };
801            let n = data.len();
802            data.clear();
803            n
804        };
805        if let Ok(mut order) = self.access_order.write() {
806            order.clear();
807        }
808        if let Ok(mut idx) = self.table_index.write() {
809            idx.clear();
810        }
811        if let Ok(mut tbl_stats) = self.table_stats.write() {
812            tbl_stats.clear();
813        }
814        if removed > 0 {
815            // 锁毒化时跳过统计更新(不影响数据正确性)
816            if let Ok(mut stats) = self.stats.write() {
817                stats.evictions += removed as u64;
818                stats.size = 0;
819            }
820        }
821    }
822
823    /// 获取当前缓存项数量
824    pub fn size(&self) -> usize {
825        self.data.read().map(|d| d.len()).unwrap_or(0)
826    }
827
828    /// 获取统计信息
829    pub fn stats(&self) -> L2CacheStats {
830        let mut s = self.stats.read().map(|s| s.clone()).unwrap_or_default();
831        // 实时同步 size 字段(不写入 stats,避免持锁读 data)
832        s.size = self.size();
833        s
834    }
835
836    /// 重置统计信息(含全局和按表分桶)
837    pub fn reset_stats(&self) {
838        if let Ok(mut stats) = self.stats.write() {
839            *stats = L2CacheStats::default();
840        }
841        if let Ok(mut tbl_stats) = self.table_stats.write() {
842            tbl_stats.clear();
843        }
844    }
845
846    /// TASK-023:查询缓存辅助方法
847    ///
848    /// 根据 SQL + 参数生成缓存键,先查缓存,命中则返回缓存结果;
849    /// 未命中则调用 `loader` 执行查询,将结果缓存后返回。
850    ///
851    /// # 参数
852    ///
853    /// * `table` - 表名(用于表级失效)
854    /// * `sql` - SQL 查询语句
855    /// * `params` - 查询参数(用于生成缓存键)
856    /// * `ttl` - 缓存 TTL
857    /// * `loader` - 异步查询闭包,返回 `Result<Vec<QueryRows>, DbError>`
858    ///
859    /// # 空结果缓存
860    ///
861    /// 空结果也会缓存,TTL 缩短为 `ttl / 10`(至少 1 秒),防止缓存穿透。
862    ///
863    /// # 示例
864    ///
865    /// ```ignore
866    /// use sz_orm_core::l2_cache::L2Cache;
867    /// use std::time::Duration;
868    ///
869    /// let cache = L2Cache::new();
870    /// let rows = cache.get_or_load_query(
871    ///     "users",
872    ///     "SELECT * FROM users WHERE status = ?",
873    ///     &[Value::I64(1)],
874    ///     Duration::from_secs(300),
875    ///     || async { conn.query_with_params(sql, params).await },
876    /// ).await?;
877    /// ```
878    pub async fn get_or_load_query<F, Fut>(
879        &self,
880        table: &str,
881        sql: &str,
882        params: &[crate::value::Value],
883        ttl: Duration,
884        loader: F,
885    ) -> Result<crate::pool::QueryRows, crate::DbError>
886    where
887        F: FnOnce() -> Fut,
888        Fut: std::future::Future<Output = Result<crate::pool::QueryRows, crate::DbError>>,
889    {
890        // 生成缓存键:使用 SQL + 参数的哈希
891        use std::collections::hash_map::DefaultHasher;
892        use std::hash::{Hash, Hasher};
893
894        let mut hasher = DefaultHasher::new();
895        sql.hash(&mut hasher);
896        for param in params {
897            param.to_string().hash(&mut hasher);
898        }
899        let query_hash = hasher.finish();
900        let cache_key = CacheKey::by_query(table, query_hash);
901
902        // 检查缓存
903        if let Some(cached) = self.get(&cache_key) {
904            // 缓存命中:反序列化结果
905            if let Value::Json(json_str) = cached {
906                if let Ok(rows) = serde_json::from_str::<crate::pool::QueryRows>(&json_str) {
907                    return Ok(rows);
908                }
909            }
910        }
911
912        // 缓存未命中:执行查询
913        let rows = loader().await?;
914
915        // 确定缓存 TTL(空结果缩短为 1/10)
916        let cache_ttl = if rows.is_empty() {
917            // 空结果也缓存,TTL 缩短为 1/10(至少 1 秒)
918            std::cmp::max(ttl / 10, Duration::from_secs(1))
919        } else {
920            ttl
921        };
922
923        // 序列化并缓存结果
924        if let Ok(json_str) = serde_json::to_string(&rows) {
925            self.put(&cache_key, Value::Json(json_str), Some(cache_ttl));
926        }
927
928        Ok(rows)
929    }
930
931    /// TASK-023:失效查询缓存
932    ///
933    /// 根据 SQL + 参数失效特定的查询缓存项。
934    pub fn invalidate_query(&self, table: &str, sql: &str, params: &[crate::value::Value]) {
935        use std::collections::hash_map::DefaultHasher;
936        use std::hash::{Hash, Hasher};
937
938        let mut hasher = DefaultHasher::new();
939        sql.hash(&mut hasher);
940        for param in params {
941            param.to_string().hash(&mut hasher);
942        }
943        let query_hash = hasher.finish();
944        let cache_key = CacheKey::by_query(table, query_hash);
945        self.invalidate(&cache_key);
946    }
947
948    /// 获取指定表的命中率统计
949    pub fn table_stats(&self, table: &str) -> Option<PerTableStats> {
950        self.table_stats
951            .read()
952            .ok()
953            .and_then(|s| s.get(table).cloned())
954    }
955
956    /// 获取所有表的命中率统计快照
957    pub fn all_table_stats(&self) -> HashMap<String, PerTableStats> {
958        self.table_stats
959            .read()
960            .map(|s| s.clone())
961            .unwrap_or_default()
962    }
963
964    /// 检查缓存项是否存在(不更新统计与 LRU 顺序)
965    pub fn contains(&self, key: &CacheKey) -> bool {
966        let key_str = key.to_string_key();
967        self.data
968            .read()
969            .map(|d| d.get(&key_str).map(|e| !e.is_expired()).unwrap_or(false))
970            .unwrap_or(false)
971    }
972
973    /// 手动清理所有过期项
974    pub fn evict_expired(&self) -> usize {
975        let expired_keys: Vec<String> = {
976            // 锁毒化时返回空 Vec(无过期项可清理)
977            let data = match self.data.read() {
978                Ok(d) => d,
979                Err(_) => return 0,
980            };
981            data.iter()
982                .filter(|(_, e)| e.is_expired())
983                .map(|(k, _)| k.clone())
984                .collect()
985        };
986
987        // 反向查找 key_str -> table_name,用于按表分桶统计
988        // 锁毒化时返回空 map(按表统计将不更新,不影响数据清理)
989        let key_to_table: HashMap<String, String> = match self.table_index.read() {
990            Ok(idx) => {
991                let mut map = HashMap::new();
992                for (table, keys) in idx.iter() {
993                    for k in keys {
994                        map.insert(k.clone(), table.clone());
995                    }
996                }
997                map
998            }
999            Err(_) => HashMap::new(),
1000        };
1001
1002        let mut removed = 0;
1003        if !expired_keys.is_empty() {
1004            // 锁毒化时跳过清理(视为未删除)
1005            let mut data = match self.data.write() {
1006                Ok(d) => d,
1007                Err(_) => return 0,
1008            };
1009            for k in &expired_keys {
1010                if data.remove(k).is_some() {
1011                    removed += 1;
1012                }
1013            }
1014        }
1015
1016        if removed > 0 {
1017            // O(m) 批量移除(m = expired_keys),而非旧实现的 O(n*m) retain
1018            // 锁毒化时跳过 LRU 顺序同步(不影响数据正确性)
1019            if let Ok(mut order) = self.access_order.write() {
1020                for k in &expired_keys {
1021                    order.remove(k);
1022                }
1023            }
1024            {
1025                // 锁毒化时跳过统计更新(不影响数据正确性)
1026                if let Ok(mut stats) = self.stats.write() {
1027                    stats.evictions += removed as u64;
1028                }
1029            }
1030            // 更新按表分桶统计(单独持锁,避免与 stats 锁同时持有)
1031            if let Ok(mut tbl_stats) = self.table_stats.write() {
1032                for k in &expired_keys {
1033                    if let Some(table) = key_to_table.get(k) {
1034                        tbl_stats.entry(table.clone()).or_default().evictions += 1;
1035                    }
1036                }
1037            }
1038        }
1039        removed
1040    }
1041
1042    /// 更新缓存项的 TTL(若 key 不存在返回 false)
1043    ///
1044    /// 用于 `Cache` trait 的 `expire` 方法实现。
1045    pub fn update_ttl(&self, key: &CacheKey, ttl: Duration) -> bool {
1046        let key_str = key.to_string_key();
1047        let mut data = match self.data.write() {
1048            Ok(d) => d,
1049            Err(_) => return false,
1050        };
1051        if let Some(entry) = data.get_mut(&key_str) {
1052            entry.expires_at = Some(Instant::now() + ttl);
1053            true
1054        } else {
1055            false
1056        }
1057    }
1058
1059    /// 获取缓存项的剩余 TTL
1060    ///
1061    /// 返回值:
1062    /// - `None`:key 不存在或已过期
1063    /// - `Some(None)`:key 存在但无 TTL(永不过期)
1064    /// - `Some(Some(d))`:key 存在且剩余 TTL 为 d
1065    ///
1066    /// 用于 `Cache` trait 的 `ttl` 方法实现。
1067    pub fn remaining_ttl(&self, key: &CacheKey) -> Option<Option<Duration>> {
1068        let key_str = key.to_string_key();
1069        let data = self.data.read().ok()?;
1070        let entry = data.get(&key_str)?;
1071        match entry.expires_at {
1072            Some(expires_at) => {
1073                let now = Instant::now();
1074                if expires_at <= now {
1075                    None
1076                } else {
1077                    Some(Some(expires_at.duration_since(now)))
1078                }
1079            }
1080            None => Some(None),
1081        }
1082    }
1083}
1084
1085// ============================================================================
1086// Cache trait 实现 — 让 L2Cache 可作为通用 Cache 使用
1087// ============================================================================
1088
1089/// 为 L2Cache 实现 `Cache` trait
1090///
1091/// 通过 `CacheKey::by_pk("__cache__", key)` 将字符串 key 映射到 L2Cache 的 CacheKey 体系,
1092/// 所有通过 `Cache` trait 写入的缓存项归入 `__cache__` 表,与业务缓存项隔离。
1093///
1094/// 值以 `Value::Bytes(Vec<u8>)` 存储;若通过 `Cache::get` 读取到的 Value 非 Bytes 类型
1095/// (如直接通过 `L2Cache::put` 写入的其他类型),则回退为 JSON 序列化。
1096impl Cache for L2Cache {
1097    fn get(&self, key: &str) -> Result<Option<Vec<u8>>, CacheError> {
1098        let cache_key = CacheKey::by_pk("__cache__", key);
1099        match L2Cache::get(self, &cache_key) {
1100            Some(Value::Bytes(bytes)) => Ok(Some(bytes)),
1101            Some(other) => {
1102                let json = serde_json::to_vec(&other)
1103                    .map_err(|e| CacheError::SerializationError(e.to_string()))?;
1104                Ok(Some(json))
1105            }
1106            None => Ok(None),
1107        }
1108    }
1109
1110    fn set(&self, key: &str, value: Vec<u8>, ttl: Option<Duration>) -> Result<(), CacheError> {
1111        let cache_key = CacheKey::by_pk("__cache__", key);
1112        self.put(&cache_key, Value::Bytes(value), ttl);
1113        Ok(())
1114    }
1115
1116    fn delete(&self, key: &str) -> Result<(), CacheError> {
1117        let cache_key = CacheKey::by_pk("__cache__", key);
1118        self.invalidate(&cache_key);
1119        Ok(())
1120    }
1121
1122    fn clear(&self) -> Result<(), CacheError> {
1123        // 仅清除通过 Cache trait 写入的原始缓存项(__cache__ 表),
1124        // 不影响通过 CacheKey 直接写入的业务缓存项。
1125        self.invalidate_table("__cache__");
1126        Ok(())
1127    }
1128
1129    fn exists(&self, key: &str) -> Result<bool, CacheError> {
1130        let cache_key = CacheKey::by_pk("__cache__", key);
1131        Ok(self.contains(&cache_key))
1132    }
1133
1134    fn expire(&self, key: &str, ttl: Duration) -> Result<(), CacheError> {
1135        let cache_key = CacheKey::by_pk("__cache__", key);
1136        if self.update_ttl(&cache_key, ttl) {
1137            Ok(())
1138        } else {
1139            Err(CacheError::NotFound(key.to_string()))
1140        }
1141    }
1142
1143    fn ttl(&self, key: &str) -> Result<Option<Duration>, CacheError> {
1144        let cache_key = CacheKey::by_pk("__cache__", key);
1145        match self.remaining_ttl(&cache_key) {
1146            None => Err(CacheError::NotFound(key.to_string())),
1147            Some(None) => Ok(None),
1148            Some(Some(d)) => Ok(Some(d)),
1149        }
1150    }
1151}
1152
1153// ============================================================================
1154// L2CacheBackend — 分布式缓存后端抽象(trait + InMemoryBackend + RedisBackend stub)
1155// ============================================================================
1156
1157/// L2 缓存异步 Future 类型别名
1158///
1159/// 用于简化 `L2CacheBackend` trait 中方法的返回类型签名,
1160/// 避免重复书写复杂的 `Pin<Box<dyn Future<...> + Send + 'a>>`。
1161pub type L2CacheFuture<'a, T> = Pin<Box<dyn Future<Output = Result<T, CacheError>> + Send + 'a>>;
1162
1163/// L2 缓存后端 trait(分布式抽象)
1164///
1165/// 定义跨进程共享的二级缓存后端接口,支持进程内内存、Redis 等实现。
1166/// 手动解糖 async 方法(不使用 `#[async_trait]`),与 `Connection` trait 风格一致。
1167///
1168/// # 设计要点
1169///
1170/// - **键值以 `&[u8]` 传输**:后端无关的序列化格式(由调用方决定 bincode/json 等)
1171/// - **TTL 可选**:`Some(Duration)` 设置过期时间,`None` 表示永不过期
1172/// - **前缀失效**:`invalidate_prefix` 批量失效某前缀的所有键(用于表级失效)
1173///
1174/// # 实现方
1175///
1176/// - [`InMemoryBackend`]:进程内内存后端(默认,单机场景)
1177/// - [`RedisBackend`]:Redis 分布式后端(stub,需启用 `redis` feature 并补充依赖)
1178pub trait L2CacheBackend: Send + Sync {
1179    /// 获取缓存值,不存在或已过期返回 `None`
1180    fn get<'a>(&'a self, key: &'a str) -> L2CacheFuture<'a, Option<Vec<u8>>>;
1181
1182    /// 设置缓存值,`ttl` 为 `None` 表示永不过期
1183    fn set<'a>(
1184        &'a self,
1185        key: &'a str,
1186        value: &'a [u8],
1187        ttl: Option<Duration>,
1188    ) -> L2CacheFuture<'a, ()>;
1189
1190    /// 删除单个缓存键
1191    fn delete<'a>(&'a self, key: &'a str) -> L2CacheFuture<'a, ()>;
1192
1193    /// 按前缀批量失效缓存项(用于表级失效)
1194    fn invalidate_prefix<'a>(&'a self, prefix: &'a str) -> L2CacheFuture<'a, ()>;
1195}
1196
1197/// 进程内内存后端(默认实现)
1198///
1199/// 适用于单机场景,不跨进程共享。内部使用 `RwLock<HashMap>` 存储,
1200/// `invalidate_prefix` 通过遍历键前缀匹配实现(O(n),单机场景可接受)。
1201///
1202/// # 线程安全
1203///
1204/// 所有操作通过 `RwLock` 保护,可在多线程环境下共享。
1205///
1206/// # 注意
1207///
1208/// 由于 `std::sync::RwLock` 的 guard 是 `!Send`,所有同步操作在创建
1209/// `Future` 之前完成,guard 在 block 退出时释放,避免跨 `.await` 持锁。
1210pub struct InMemoryBackend {
1211    /// 缓存数据:key -> (value, expiry_time)
1212    /// 使用类型别名降低类型复杂度(clippy::type_complexity)
1213    data: RwLock<InMemoryCacheData>,
1214}
1215
1216/// 内存缓存条目类型别名
1217type InMemoryCacheData = HashMap<String, (Vec<u8>, Option<Instant>)>;
1218
1219impl Default for InMemoryBackend {
1220    fn default() -> Self {
1221        Self::new()
1222    }
1223}
1224
1225impl InMemoryBackend {
1226    /// 创建空的内存后端
1227    pub fn new() -> Self {
1228        Self {
1229            data: RwLock::new(HashMap::new()),
1230        }
1231    }
1232}
1233
1234impl L2CacheBackend for InMemoryBackend {
1235    fn get<'a>(&'a self, key: &'a str) -> L2CacheFuture<'a, Option<Vec<u8>>> {
1236        // 同步完成读操作,guard 在 block 退出时释放,避免跨 await 持锁
1237        let result = {
1238            let data = match self.data.read() {
1239                Ok(d) => d,
1240                Err(e) => {
1241                    let err = CacheError::from(e);
1242                    return Box::pin(async move { Err(err) });
1243                }
1244            };
1245            match data.get(key) {
1246                Some((value, expiry)) => {
1247                    // 过期检查:expiry 为 None 表示永不过期
1248                    if expiry.map(|t| t <= Instant::now()).unwrap_or(false) {
1249                        Ok(None)
1250                    } else {
1251                        Ok(Some(value.clone()))
1252                    }
1253                }
1254                None => Ok(None),
1255            }
1256        };
1257        Box::pin(async move { result })
1258    }
1259
1260    fn set<'a>(
1261        &'a self,
1262        key: &'a str,
1263        value: &'a [u8],
1264        ttl: Option<Duration>,
1265    ) -> L2CacheFuture<'a, ()> {
1266        let result = {
1267            let mut data = match self.data.write() {
1268                Ok(d) => d,
1269                Err(e) => {
1270                    let err = CacheError::from(e);
1271                    return Box::pin(async move { Err(err) });
1272                }
1273            };
1274            let expiry = ttl.map(|d| Instant::now() + d);
1275            data.insert(key.to_string(), (value.to_vec(), expiry));
1276            Ok(())
1277        };
1278        Box::pin(async move { result })
1279    }
1280
1281    fn delete<'a>(&'a self, key: &'a str) -> L2CacheFuture<'a, ()> {
1282        let result = {
1283            let mut data = match self.data.write() {
1284                Ok(d) => d,
1285                Err(e) => {
1286                    let err = CacheError::from(e);
1287                    return Box::pin(async move { Err(err) });
1288                }
1289            };
1290            data.remove(key);
1291            Ok(())
1292        };
1293        Box::pin(async move { result })
1294    }
1295
1296    fn invalidate_prefix<'a>(&'a self, prefix: &'a str) -> L2CacheFuture<'a, ()> {
1297        let result = {
1298            let mut data = match self.data.write() {
1299                Ok(d) => d,
1300                Err(e) => {
1301                    let err = CacheError::from(e);
1302                    return Box::pin(async move { Err(err) });
1303                }
1304            };
1305            // O(n) 遍历,删除所有以 prefix 开头的键
1306            let keys_to_remove: Vec<String> = data
1307                .keys()
1308                .filter(|k| k.starts_with(prefix))
1309                .cloned()
1310                .collect();
1311            for k in keys_to_remove {
1312                data.remove(&k);
1313            }
1314            Ok(())
1315        };
1316        Box::pin(async move { result })
1317    }
1318}
1319
1320/// Redis 分布式缓存后端
1321///
1322/// 基于 `redis` crate 0.27 + `tokio-comp` 异步 IO + `connection-manager` 自动重连。
1323///
1324/// # 实现要点
1325///
1326/// - **连接管理**:使用 `redis::aio::ConnectionManager`(内部自动重连的连接池)
1327/// - **`get`** → `redis::cmd("GET")` 异步执行
1328/// - **`set`** → `SET key value` + 可选 `EX seconds`(合并为单次 `SET` 命令,原子性保证)
1329/// - **`delete`** → `redis::cmd("DEL")`
1330/// - **`invalidate_prefix`** → `SCAN` + 批量 `DEL`(避免 `KEYS` 阻塞 Redis 主线程)
1331///   - 使用 `COUNT 100` 分批扫描,避免单次 SCAN 拉取过多 key 导致阻塞
1332///   - 多次 DEL 调用合并为单次 pipeline 批量执行,减少 RTT 开销
1333///
1334/// # 错误处理
1335///
1336/// - 连接失败 → `CacheError::Internal`,由调用方决定是否重试
1337/// - Redis 命令错误 → 原始错误字符串包装为 `CacheError::Internal`
1338///
1339/// # 启用方式
1340///
1341/// 在 `Cargo.toml` 中启用 `redis` feature:
1342/// ```toml
1343/// [dependencies]
1344/// sz-orm-core = { version = "1.0", features = ["redis"] }
1345/// ```
1346///
1347/// # 使用示例
1348///
1349/// ```no_run
1350/// # use sz_orm_core::l2_cache::{RedisBackend, L2CacheBackend};
1351/// # use std::time::Duration;
1352/// # #[tokio::main]
1353/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
1354/// let backend = RedisBackend::new("redis://127.0.0.1:6379/0").await?;
1355/// backend.set("user:1", b"alice", Some(Duration::from_secs(60))).await?;
1356/// let val = backend.get("user:1").await?;
1357/// assert_eq!(val, Some(b"alice".to_vec()));
1358/// backend.delete("user:1").await?;
1359/// # Ok(())
1360/// # }
1361/// ```
1362#[cfg(feature = "redis")]
1363pub struct RedisBackend {
1364    /// Redis 异步连接管理器(自动重连)
1365    manager: redis::aio::ConnectionManager,
1366}
1367
1368#[cfg(feature = "redis")]
1369impl RedisBackend {
1370    /// 创建 Redis 后端
1371    ///
1372    /// `url` 格式:`redis://[:password@]host:port[/db]`
1373    /// - `redis://127.0.0.1:6379/0` — 默认 DB 0
1374    /// - `redis://:secret@127.0.0.1:6379/1` — 带密码,DB 1
1375    ///
1376    /// # 错误
1377    ///
1378    /// - 连接失败 → `CacheError::Internal`
1379    pub async fn new(url: impl Into<String>) -> Result<Self, CacheError> {
1380        let url = url.into();
1381        let client = redis::Client::open(url.as_str())
1382            .map_err(|e| CacheError::Internal(format!("Redis client create failed: {}", e)))?;
1383        let manager = redis::aio::ConnectionManager::new(client)
1384            .await
1385            .map_err(|e| CacheError::Internal(format!("Redis connect failed: {}", e)))?;
1386        Ok(Self { manager })
1387    }
1388
1389    /// 使用已有 ConnectionManager 创建后端(用于复用连接池)
1390    pub fn from_manager(manager: redis::aio::ConnectionManager) -> Self {
1391        Self { manager }
1392    }
1393
1394    /// SCAN + 批量 DEL 实现前缀失效
1395    ///
1396    /// 使用 `SCAN cursor MATCH prefix* COUNT 100` 迭代扫描所有匹配的 key,
1397    /// 累计到本地 Vec 后通过 pipeline 批量 DEL,避免:
1398    /// 1. `KEYS pattern` 阻塞 Redis 主线程(O(N) 全表扫描)
1399    /// 2. 单次 DEL 调用过多导致 RTT 累积
1400    ///
1401    /// # 参数
1402    /// - `prefix`:key 前缀(不含通配符,函数内部追加 `*`)
1403    ///
1404    /// # 返回
1405    /// - `Ok(())`:扫描完成,无论是否删除了 key
1406    /// - `Err(_)`:连接错误或命令执行失败
1407    async fn invalidate_prefix_inner(&self, prefix: &str) -> Result<(), CacheError> {
1408        let pattern = format!("{}*", prefix);
1409        let mut cursor: u64 = 0;
1410        loop {
1411            // SCAN 返回 (next_cursor, Vec<key>)
1412            // 注意:必须先 clone 出独立的 conn,避免 &mut temporary 借用问题
1413            let mut conn = self.manager.clone();
1414            let scan_result: redis::RedisResult<(u64, Vec<String>)> = redis::cmd("SCAN")
1415                .arg(cursor)
1416                .arg("MATCH")
1417                .arg(&pattern)
1418                .arg("COUNT")
1419                .arg(100usize)
1420                .query_async(&mut conn)
1421                .await;
1422            let (next_cursor, keys): (u64, Vec<String>) = scan_result
1423                .map_err(|e| CacheError::Internal(format!("Redis SCAN failed: {}", e)))?;
1424
1425            if !keys.is_empty() {
1426                // 批量 DEL:使用 pipeline 减少往返次数
1427                let mut pipe = redis::pipe();
1428                for k in &keys {
1429                    pipe.del(k);
1430                }
1431                // 显式指定 RedisResult<()> 类型,避免 never type fallback 警告
1432                let del_result: redis::RedisResult<()> = pipe.query_async(&mut conn).await;
1433                del_result.map_err(|e| {
1434                    CacheError::Internal(format!("Redis DEL pipeline failed: {}", e))
1435                })?;
1436            }
1437
1438            // cursor == 0 表示扫描完成
1439            if next_cursor == 0 {
1440                break;
1441            }
1442            cursor = next_cursor;
1443        }
1444        Ok(())
1445    }
1446}
1447
1448#[cfg(feature = "redis")]
1449impl L2CacheBackend for RedisBackend {
1450    fn get<'a>(&'a self, key: &'a str) -> L2CacheFuture<'a, Option<Vec<u8>>> {
1451        Box::pin(async move {
1452            use redis::AsyncCommands;
1453            let mut conn = self.manager.clone();
1454            let value: Option<Vec<u8>> = conn
1455                .get(key)
1456                .await
1457                .map_err(|e| CacheError::Internal(format!("Redis GET failed: {}", e)))?;
1458            Ok(value)
1459        })
1460    }
1461
1462    fn set<'a>(
1463        &'a self,
1464        key: &'a str,
1465        value: &'a [u8],
1466        ttl: Option<Duration>,
1467    ) -> L2CacheFuture<'a, ()> {
1468        Box::pin(async move {
1469            use redis::AsyncCommands;
1470            let mut conn = self.manager.clone();
1471            // 合并 SET + EX 为单次原子操作(SET key value EX seconds)
1472            // 避免 SET 后 EXPIRE 之间的窗口期 key 无 TTL
1473            match ttl {
1474                Some(d) => {
1475                    let secs = d.as_secs();
1476                    if secs > 0 {
1477                        let _: () = conn.set_ex(key, value, secs).await.map_err(|e| {
1478                            CacheError::Internal(format!("Redis SET EX failed: {}", e))
1479                        })?;
1480                    } else {
1481                        // TTL < 1s:退化为 SET + PEXPIRE(毫秒精度)
1482                        let _: () = conn.set(key, value).await.map_err(|e| {
1483                            CacheError::Internal(format!("Redis SET failed: {}", e))
1484                        })?;
1485                        // 毫秒数转换为 i64(u128 → i64,实际值不会超过 i64 范围)
1486                        let ms: i64 = d.as_millis().min(i64::MAX as u128) as i64;
1487                        let _: () = conn.pexpire(key, ms).await.map_err(|e| {
1488                            CacheError::Internal(format!("Redis PEXPIRE failed: {}", e))
1489                        })?;
1490                    }
1491                }
1492                None => {
1493                    let _: () = conn
1494                        .set(key, value)
1495                        .await
1496                        .map_err(|e| CacheError::Internal(format!("Redis SET failed: {}", e)))?;
1497                }
1498            }
1499            Ok(())
1500        })
1501    }
1502
1503    fn delete<'a>(&'a self, key: &'a str) -> L2CacheFuture<'a, ()> {
1504        Box::pin(async move {
1505            use redis::AsyncCommands;
1506            let mut conn = self.manager.clone();
1507            let _: () = conn
1508                .del(key)
1509                .await
1510                .map_err(|e| CacheError::Internal(format!("Redis DEL failed: {}", e)))?;
1511            Ok(())
1512        })
1513    }
1514
1515    fn invalidate_prefix<'a>(&'a self, prefix: &'a str) -> L2CacheFuture<'a, ()> {
1516        Box::pin(async move { self.invalidate_prefix_inner(prefix).await })
1517    }
1518}
1519
1520/// Redis 分布式缓存后端(stub,未启用 `redis` feature 时使用)
1521///
1522/// 当未启用 `redis` feature 时,所有操作返回 `CacheError::Internal`,
1523/// 提示用户在 `Cargo.toml` 中启用 `redis` feature。
1524#[cfg(not(feature = "redis"))]
1525pub struct RedisBackend {
1526    /// Redis 连接字符串(保留字段用于错误提示)
1527    url: String,
1528}
1529
1530#[cfg(not(feature = "redis"))]
1531impl RedisBackend {
1532    /// 创建 Redis 后端 stub
1533    ///
1534    /// 返回 stub 实例,所有操作将返回 `CacheError::Internal`。
1535    /// 启用 `redis` feature 后自动切换为真实实现。
1536    pub fn new(_url: impl Into<String>) -> Self {
1537        Self { url: _url.into() }
1538    }
1539}
1540
1541#[cfg(not(feature = "redis"))]
1542impl L2CacheBackend for RedisBackend {
1543    fn get<'a>(&'a self, _key: &'a str) -> L2CacheFuture<'a, Option<Vec<u8>>> {
1544        let url = self.url.clone();
1545        Box::pin(async move {
1546            Err(CacheError::Internal(format!(
1547                "RedisBackend not compiled: enable 'redis' feature in sz-orm-core. URL: {}",
1548                url
1549            )))
1550        })
1551    }
1552
1553    fn set<'a>(
1554        &'a self,
1555        _key: &'a str,
1556        _value: &'a [u8],
1557        _ttl: Option<Duration>,
1558    ) -> L2CacheFuture<'a, ()> {
1559        let url = self.url.clone();
1560        Box::pin(async move {
1561            Err(CacheError::Internal(format!(
1562                "RedisBackend not compiled: enable 'redis' feature in sz-orm-core. URL: {}",
1563                url
1564            )))
1565        })
1566    }
1567
1568    fn delete<'a>(&'a self, _key: &'a str) -> L2CacheFuture<'a, ()> {
1569        let url = self.url.clone();
1570        Box::pin(async move {
1571            Err(CacheError::Internal(format!(
1572                "RedisBackend not compiled: enable 'redis' feature in sz-orm-core. URL: {}",
1573                url
1574            )))
1575        })
1576    }
1577
1578    fn invalidate_prefix<'a>(&'a self, _prefix: &'a str) -> L2CacheFuture<'a, ()> {
1579        let url = self.url.clone();
1580        Box::pin(async move {
1581            Err(CacheError::Internal(format!(
1582                "RedisBackend not compiled: enable 'redis' feature in sz-orm-core. URL: {}",
1583                url
1584            )))
1585        })
1586    }
1587}
1588
1589// ============================================================================
1590// WriteBehind — 异步写回缓存模式(Fix #40)
1591// ============================================================================
1592//
1593// Write-Behind 模式:写操作立即更新缓存,并异步批量刷新到后端存储(如数据库)。
1594// 适用于写吞吐高、可容忍短暂数据不一致的场景。
1595//
1596// # 工作流程
1597//
1598// 1. `write()` / `delete()` → 立即更新 L2CacheBackend,同时将操作入队
1599// 2. 后台任务每 `flush_interval` 触发一次 `flush()`,或显式调用 `flush()`
1600// 3. `flush()` 将队列中的操作批量应用回调 `on_flush`
1601//
1602// # 失败处理
1603//
1604// - 缓存写入失败:立即返回错误给调用方
1605// - 队列写入失败(锁中毒):返回 `CacheError::Internal`
1606// - 后端刷新失败:调用 `on_error` 回调,操作**保留在队列中**等待下次重试
1607//
1608// # 注意
1609//
1610// - 不保证写入顺序与刷新顺序一致(多生产者并发入队)
1611// - 同一 key 的多次写入会按入队顺序刷新(FIFO)
1612// - 调用方需自行处理幂等性(如使用 upsert)
1613
1614/// 写回操作类型
1615#[derive(Debug, Clone)]
1616pub enum WriteOp {
1617    /// SET 操作(key, value, ttl)
1618    Set {
1619        /// 缓存键
1620        key: String,
1621        /// 缓存值(已序列化的字节)
1622        value: Vec<u8>,
1623        /// TTL(与 set 调用一致)
1624        ttl: Option<Duration>,
1625    },
1626    /// DELETE 操作
1627    Delete {
1628        /// 缓存键
1629        key: String,
1630    },
1631}
1632
1633/// 写回刷新回调类型
1634///
1635/// 接收一批待刷新的操作,调用方需将其应用到后端存储(如执行 SQL)。
1636/// 返回 `Err` 表示刷新失败,操作将保留在队列中等待重试。
1637pub type FlushCallback = Arc<
1638    dyn Fn(Vec<WriteOp>) -> Pin<Box<dyn Future<Output = Result<(), CacheError>> + Send>>
1639        + Send
1640        + Sync,
1641>;
1642
1643/// 写回错误回调类型
1644pub type ErrorCallback = Arc<dyn Fn(Vec<WriteOp>, CacheError) + Send + Sync>;
1645
1646/// Write-Behind 写入器
1647///
1648/// 包装一个 `L2CacheBackend`,将写操作同时写入缓存与内存队列,
1649/// 后台任务或显式 `flush()` 触发批量刷新到后端存储。
1650///
1651/// # 线程安全
1652///
1653/// 内部使用 `tokio::sync::Mutex` 保护队列,可被多线程并发调用。
1654///
1655/// # 示例
1656///
1657/// ```no_run
1658/// use sz_orm_core::l2_cache::{WriteBehindWriter, WriteOp, InMemoryBackend};
1659/// use std::sync::Arc;
1660/// use std::time::Duration;
1661///
1662/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1663/// let backend = Arc::new(InMemoryBackend::new());
1664/// let on_flush = Arc::new(|ops: Vec<WriteOp>| {
1665///     Box::pin(async move {
1666///         // 这里将 ops 应用到数据库(如批量 INSERT/UPDATE)
1667///         for op in &ops {
1668///             println!("flushing: {:?}", op);
1669///         }
1670///         Ok(())
1671///     }) as std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), _>> + Send>>
1672///     as _
1673/// });
1674/// let writer = WriteBehindWriter::new(backend.clone(), on_flush);
1675///
1676/// // 立即更新缓存,并异步刷新到数据库
1677/// writer.write(b"key1", b"value1", None).await?;
1678///
1679/// // 显式刷新所有待处理操作
1680/// writer.flush().await?;
1681/// # Ok(())
1682/// # }
1683/// ```
1684pub struct WriteBehindWriter {
1685    /// 被包装的 L2 缓存后端
1686    backend: Arc<dyn L2CacheBackend>,
1687    /// 待刷新操作队列
1688    queue: tokio::sync::Mutex<Vec<WriteOp>>,
1689    /// 刷新回调
1690    on_flush: FlushCallback,
1691    /// 错误回调(可选)
1692    on_error: Option<ErrorCallback>,
1693}
1694
1695impl WriteBehindWriter {
1696    /// 创建 Write-Behind 写入器
1697    ///
1698    /// # 参数
1699    /// - `backend`:被包装的 L2 缓存后端(如 `InMemoryBackend`、`RedisBackend`)
1700    /// - `on_flush`:刷新回调,接收一批操作并应用到后端存储
1701    pub fn new(backend: Arc<dyn L2CacheBackend>, on_flush: FlushCallback) -> Self {
1702        Self {
1703            backend,
1704            queue: tokio::sync::Mutex::new(Vec::new()),
1705            on_flush,
1706            on_error: None,
1707        }
1708    }
1709
1710    /// 设置错误回调
1711    ///
1712    /// 当 `flush()` 失败时调用,传入失败的操作和错误信息。
1713    /// 注意:失败的操作会保留在队列中等待下次重试。
1714    pub fn with_error_callback(mut self, on_error: ErrorCallback) -> Self {
1715        self.on_error = Some(on_error);
1716        self
1717    }
1718
1719    /// 写入缓存(立即更新后端缓存 + 入队待刷新)
1720    ///
1721    /// # 参数
1722    /// - `key`:缓存键
1723    /// - `value`:缓存值(字节切片)
1724    /// - `ttl`:TTL,`None` 表示永不过期
1725    pub async fn write(
1726        &self,
1727        key: &[u8],
1728        value: &[u8],
1729        ttl: Option<Duration>,
1730    ) -> Result<(), CacheError> {
1731        let key_str = String::from_utf8_lossy(key).into_owned();
1732        // 1. 立即更新缓存(同步可见性优先)
1733        self.backend.set(&key_str, value, ttl).await?;
1734        // 2. 入队待刷新
1735        let op = WriteOp::Set {
1736            key: key_str,
1737            value: value.to_vec(),
1738            ttl,
1739        };
1740        self.queue.lock().await.push(op);
1741        Ok(())
1742    }
1743
1744    /// 删除缓存项(立即从后端缓存删除 + 入队待刷新)
1745    pub async fn delete(&self, key: &[u8]) -> Result<(), CacheError> {
1746        let key_str = String::from_utf8_lossy(key).into_owned();
1747        // 1. 立即从缓存删除
1748        self.backend.delete(&key_str).await?;
1749        // 2. 入队待刷新
1750        let op = WriteOp::Delete { key: key_str };
1751        self.queue.lock().await.push(op);
1752        Ok(())
1753    }
1754
1755    /// 刷新所有待处理操作到后端存储
1756    ///
1757    /// 将队列中的操作一次性传给 `on_flush` 回调。
1758    /// 若回调返回错误,操作保留在队列中等待下次重试。
1759    pub async fn flush(&self) -> Result<(), CacheError> {
1760        // 1. 取出所有待刷新操作(drain)
1761        let ops: Vec<WriteOp> = {
1762            let mut guard = self.queue.lock().await;
1763            std::mem::take(&mut *guard)
1764        };
1765        if ops.is_empty() {
1766            return Ok(());
1767        }
1768        // 2. 调用刷新回调
1769        match (self.on_flush)(ops.clone()).await {
1770            Ok(()) => Ok(()),
1771            Err(e) => {
1772                // 刷新失败:将操作放回队列,等待下次重试
1773                let mut guard = self.queue.lock().await;
1774                guard.extend(ops.clone());
1775                // 触发错误回调(如有)
1776                if let Some(ref on_error) = self.on_error {
1777                    on_error(ops, e.clone());
1778                }
1779                Err(e)
1780            }
1781        }
1782    }
1783
1784    /// 当前队列中待刷新的操作数(用于监控)
1785    pub async fn pending_count(&self) -> usize {
1786        self.queue.lock().await.len()
1787    }
1788
1789    /// 启动后台自动刷新任务
1790    ///
1791    /// 每 `interval` 触发一次 `flush()`,直到 `WriteBehindWriter` 被丢弃。
1792    /// 返回 `JoinHandle`,调用方可用于等待任务结束。
1793    ///
1794    /// # 注意
1795    ///
1796    /// 调用方需保证 `WriteBehindWriter` 的生命周期长于后台任务,
1797    /// 否则在 writer 被丢弃后,后台任务会因 Arc 引用计数归零而停止。
1798    pub fn spawn_auto_flush(self: Arc<Self>, interval: Duration) -> tokio::task::JoinHandle<()> {
1799        tokio::spawn(async move {
1800            let mut ticker = tokio::time::interval(interval);
1801            // 跳过首次立即触发(首次 tick 会立即返回)
1802            ticker.tick().await;
1803            loop {
1804                ticker.tick().await;
1805                // 刷新失败时记录日志(不中断循环)
1806                if let Err(e) = self.flush().await {
1807                    eprintln!("[WriteBehind] auto flush failed: {}", e);
1808                }
1809            }
1810        })
1811    }
1812}
1813
1814// ============================================================================
1815// 单元测试
1816// ============================================================================
1817
1818#[cfg(test)]
1819mod tests {
1820    use super::*;
1821    use crate::Value;
1822    use std::thread;
1823    use std::time::Duration;
1824
1825    // ===== CacheKey 测试 =====
1826
1827    #[test]
1828    fn test_cache_key_by_pk() {
1829        let key = CacheKey::by_pk("users", 1);
1830        assert_eq!(key.table, "users");
1831        assert_eq!(key.kind, CacheKeyKind::ByPk);
1832        assert_eq!(key.identifier, "1");
1833        assert_eq!(key.to_string_key(), "l2:users:pk:1");
1834    }
1835
1836    #[test]
1837    fn test_cache_key_by_query() {
1838        let key = CacheKey::by_query("orders", "abc123");
1839        assert_eq!(key.kind, CacheKeyKind::ByQuery);
1840        assert_eq!(key.to_string_key(), "l2:orders:q:abc123");
1841    }
1842
1843    #[test]
1844    fn test_cache_key_by_relation() {
1845        let key = CacheKey::by_relation("users", "posts:1");
1846        assert_eq!(key.kind, CacheKeyKind::ByRelation);
1847        assert_eq!(key.to_string_key(), "l2:users:rel:posts:1");
1848    }
1849
1850    #[test]
1851    fn test_cache_key_equality() {
1852        let k1 = CacheKey::by_pk("users", 1);
1853        let k2 = CacheKey::by_pk("users", 1);
1854        let k3 = CacheKey::by_pk("users", 2);
1855        assert_eq!(k1, k2);
1856        assert_ne!(k1, k3);
1857    }
1858
1859    #[test]
1860    fn test_cache_key_display() {
1861        let key = CacheKey::by_pk("users", 42);
1862        assert_eq!(format!("{}", key), "l2:users:pk:42");
1863    }
1864
1865    // ===== L2CacheStats 测试 =====
1866
1867    #[test]
1868    fn test_stats_hit_rate_empty() {
1869        let stats = L2CacheStats::default();
1870        assert_eq!(stats.hit_rate(), 0.0);
1871        assert_eq!(stats.total_lookups(), 0);
1872    }
1873
1874    #[test]
1875    fn test_stats_hit_rate_calculation() {
1876        let stats = L2CacheStats {
1877            hits: 80,
1878            misses: 20,
1879            ..Default::default()
1880        };
1881        assert_eq!(stats.total_lookups(), 100);
1882        assert!((stats.hit_rate() - 0.8).abs() < 0.001);
1883        assert!((stats.miss_rate() - 0.2).abs() < 0.001);
1884    }
1885
1886    #[test]
1887    fn test_stats_merge() {
1888        let mut s1 = L2CacheStats {
1889            hits: 10,
1890            misses: 5,
1891            sets: 15,
1892            evictions: 2,
1893            size: 100,
1894        };
1895        let s2 = L2CacheStats {
1896            hits: 20,
1897            misses: 10,
1898            sets: 30,
1899            evictions: 5,
1900            size: 200,
1901        };
1902        s1.merge(&s2);
1903        assert_eq!(s1.hits, 30);
1904        assert_eq!(s1.misses, 15);
1905        assert_eq!(s1.sets, 45);
1906        assert_eq!(s1.evictions, 7);
1907        assert_eq!(s1.size, 300);
1908    }
1909
1910    // ===== L2Cache 基本操作 =====
1911
1912    #[test]
1913    fn test_put_and_get() {
1914        let cache = L2Cache::new();
1915        let key = CacheKey::by_pk("users", 1);
1916
1917        cache.put(&key, Value::String("Alice".to_string()), None);
1918        let val = cache.get(&key);
1919        assert_eq!(val, Some(Value::String("Alice".to_string())));
1920    }
1921
1922    #[test]
1923    fn test_get_missing_returns_none() {
1924        let cache = L2Cache::new();
1925        let key = CacheKey::by_pk("users", 999);
1926        assert_eq!(cache.get(&key), None);
1927    }
1928
1929    #[test]
1930    fn test_overwrite_existing_key() {
1931        let cache = L2Cache::new();
1932        let key = CacheKey::by_pk("users", 1);
1933
1934        cache.put(&key, Value::String("Alice".to_string()), None);
1935        cache.put(&key, Value::String("Bob".to_string()), None);
1936        assert_eq!(cache.get(&key), Some(Value::String("Bob".to_string())));
1937    }
1938
1939    #[test]
1940    fn test_invalidate_single_key() {
1941        let cache = L2Cache::new();
1942        let key = CacheKey::by_pk("users", 1);
1943
1944        cache.put(&key, Value::I64(42), None);
1945        assert!(cache.get(&key).is_some());
1946
1947        cache.invalidate(&key);
1948        assert!(cache.get(&key).is_none());
1949    }
1950
1951    // ===== 表级失效 =====
1952
1953    #[test]
1954    fn test_invalidate_table_removes_all_entries_for_table() {
1955        let cache = L2Cache::new();
1956
1957        let k1 = CacheKey::by_pk("users", 1);
1958        let k2 = CacheKey::by_pk("users", 2);
1959        let k3 = CacheKey::by_query("users", "hash1");
1960        let k4 = CacheKey::by_pk("orders", 1); // 不同表
1961
1962        cache.put(&k1, Value::I64(1), None);
1963        cache.put(&k2, Value::I64(2), None);
1964        cache.put(&k3, Value::I64(3), None);
1965        cache.put(&k4, Value::I64(4), None);
1966
1967        cache.invalidate_table("users");
1968
1969        // users 表的所有缓存项应被失效
1970        assert!(cache.get(&k1).is_none());
1971        assert!(cache.get(&k2).is_none());
1972        assert!(cache.get(&k3).is_none());
1973        // orders 表的缓存项应保留
1974        assert!(cache.get(&k4).is_some());
1975    }
1976
1977    #[test]
1978    fn test_invalidate_table_no_op_for_unknown_table() {
1979        let cache = L2Cache::new();
1980        let k1 = CacheKey::by_pk("users", 1);
1981        cache.put(&k1, Value::I64(1), None);
1982
1983        cache.invalidate_table("nonexistent");
1984        assert!(cache.get(&k1).is_some());
1985    }
1986
1987    // ===== TTL 测试 =====
1988
1989    #[test]
1990    fn test_ttl_expiration() {
1991        let cache = L2Cache::new();
1992        let key = CacheKey::by_pk("users", 1);
1993
1994        cache.put(&key, Value::I64(42), Some(Duration::from_millis(50)));
1995        assert!(cache.get(&key).is_some());
1996
1997        // 等待 TTL 过期
1998        thread::sleep(Duration::from_millis(100));
1999        assert!(cache.get(&key).is_none());
2000    }
2001
2002    #[test]
2003    fn test_default_ttl_applied_when_no_explicit_ttl() {
2004        let cache = L2Cache::new().with_default_ttl(Duration::from_millis(50));
2005        let key = CacheKey::by_pk("users", 1);
2006
2007        cache.put(&key, Value::I64(42), None); // 不显式传 TTL
2008        assert!(cache.get(&key).is_some());
2009
2010        thread::sleep(Duration::from_millis(100));
2011        assert!(cache.get(&key).is_none());
2012    }
2013
2014    #[test]
2015    fn test_explicit_ttl_overrides_default() {
2016        // 语义验证:ttl=Some(Duration::MAX) 表示永不失效,覆盖默认 TTL
2017        let cache = L2Cache::new().with_default_ttl(Duration::from_millis(50));
2018        let key = CacheKey::by_pk("users", 1);
2019
2020        // 显式传 Some(Duration::MAX) 覆盖默认 TTL(永不失效)
2021        cache.put(&key, Value::I64(42), Some(Duration::MAX));
2022
2023        // 等待默认 TTL 已过期的时间
2024        thread::sleep(Duration::from_millis(100));
2025        // 由于显式传 Some(Duration::MAX),应仍然有效
2026        assert!(cache.get(&key).is_some());
2027    }
2028
2029    #[test]
2030    fn test_none_ttl_uses_default_ttl() {
2031        // 语义验证:ttl=None 时使用 default_ttl
2032        let cache = L2Cache::new().with_default_ttl(Duration::from_millis(50));
2033        let key = CacheKey::by_pk("users", 1);
2034
2035        cache.put(&key, Value::I64(42), None);
2036        assert!(cache.get(&key).is_some());
2037
2038        thread::sleep(Duration::from_millis(100));
2039        // None 使用了 default_ttl,应已过期
2040        assert!(cache.get(&key).is_none());
2041    }
2042
2043    // ===== 命中率统计 =====
2044
2045    #[test]
2046    fn test_stats_tracks_hits_and_misses() {
2047        let cache = L2Cache::new();
2048
2049        let k1 = CacheKey::by_pk("users", 1);
2050        let k2 = CacheKey::by_pk("users", 2);
2051
2052        cache.put(&k1, Value::I64(1), None);
2053
2054        // 1 次命中
2055        cache.get(&k1);
2056        // 2 次未命中
2057        cache.get(&k2);
2058        cache.get(&k2);
2059
2060        let stats = cache.stats();
2061        assert_eq!(stats.hits, 1);
2062        assert_eq!(stats.misses, 2);
2063        assert_eq!(stats.sets, 1);
2064    }
2065
2066    #[test]
2067    fn test_stats_tracks_evictions() {
2068        let cache = L2Cache::new();
2069        let k1 = CacheKey::by_pk("users", 1);
2070        let k2 = CacheKey::by_pk("users", 2);
2071
2072        cache.put(&k1, Value::I64(1), None);
2073        cache.put(&k2, Value::I64(2), None);
2074
2075        cache.invalidate(&k1); // evictions = 1
2076        cache.invalidate_table("users"); // 仅 k2 实际被删除,evictions = 2
2077
2078        let stats = cache.stats();
2079        // invalidate(k1) 删除 1 项;invalidate_table("users") 仅删除 k2(k1 已不存在)
2080        assert_eq!(stats.evictions, 2);
2081    }
2082
2083    #[test]
2084    fn test_stats_reset() {
2085        let cache = L2Cache::new();
2086        let k1 = CacheKey::by_pk("users", 1);
2087
2088        cache.put(&k1, Value::I64(1), None);
2089        cache.get(&k1);
2090        cache.get(&k1);
2091
2092        let stats_before = cache.stats();
2093        assert!(stats_before.hits > 0);
2094
2095        cache.reset_stats();
2096        let stats_after = cache.stats();
2097        assert_eq!(stats_after.hits, 0);
2098        assert_eq!(stats_after.misses, 0);
2099        assert_eq!(stats_after.sets, 0);
2100    }
2101
2102    // ===== 容量管理 =====
2103
2104    #[test]
2105    fn test_max_size_eviction() {
2106        let cache = L2Cache::new().with_max_size(3);
2107
2108        for i in 0..5 {
2109            let k = CacheKey::by_pk("users", i);
2110            cache.put(&k, Value::I64(i), None);
2111        }
2112
2113        // 真正的 LRU:容量严格不超过 max_size
2114        let size = cache.size();
2115        assert_eq!(
2116            size, 3,
2117            "size should be exactly max_size after LRU eviction, got {}",
2118            size
2119        );
2120    }
2121
2122    #[test]
2123    fn test_lru_eviction_order() {
2124        // 验证 LRU 顺序:访问 k0 后,下次淘汰应跳过 k0 而淘汰 k1
2125        let cache = L2Cache::new().with_max_size(3);
2126
2127        let k0 = CacheKey::by_pk("users", 0);
2128        let k1 = CacheKey::by_pk("users", 1);
2129        let k2 = CacheKey::by_pk("users", 2);
2130        let k3 = CacheKey::by_pk("users", 3);
2131
2132        cache.put(&k0, Value::I64(0), None);
2133        cache.put(&k1, Value::I64(1), None);
2134        cache.put(&k2, Value::I64(2), None);
2135
2136        // 访问 k0,使其成为最近使用
2137        let _ = cache.get(&k0);
2138
2139        // 插入 k3,应淘汰 k1(最久未访问)
2140        cache.put(&k3, Value::I64(3), None);
2141
2142        assert!(
2143            cache.get(&k0).is_some(),
2144            "k0 should survive (recently accessed)"
2145        );
2146        assert!(
2147            cache.get(&k1).is_none(),
2148            "k1 should be evicted (LRU victim)"
2149        );
2150        assert!(cache.get(&k2).is_some(), "k2 should survive");
2151        assert!(
2152            cache.get(&k3).is_some(),
2153            "k3 should survive (just inserted)"
2154        );
2155    }
2156
2157    #[test]
2158    fn test_clear_all() {
2159        let cache = L2Cache::new();
2160        cache.put(&CacheKey::by_pk("users", 1), Value::I64(1), None);
2161        cache.put(&CacheKey::by_pk("users", 2), Value::I64(2), None);
2162        cache.put(&CacheKey::by_pk("orders", 1), Value::I64(3), None);
2163
2164        assert_eq!(cache.size(), 3);
2165        cache.clear();
2166        assert_eq!(cache.size(), 0);
2167    }
2168
2169    // ===== contains(不更新统计)=====
2170
2171    #[test]
2172    fn test_contains_does_not_update_stats() {
2173        let cache = L2Cache::new();
2174        let k1 = CacheKey::by_pk("users", 1);
2175        cache.put(&k1, Value::I64(1), None);
2176
2177        let exists = cache.contains(&k1);
2178        assert!(exists);
2179
2180        let stats = cache.stats();
2181        assert_eq!(stats.hits, 0);
2182        assert_eq!(stats.misses, 0);
2183    }
2184
2185    #[test]
2186    fn test_contains_returns_false_for_missing() {
2187        let cache = L2Cache::new();
2188        let k = CacheKey::by_pk("users", 999);
2189        assert!(!cache.contains(&k));
2190    }
2191
2192    #[test]
2193    fn test_contains_returns_false_for_expired() {
2194        let cache = L2Cache::new();
2195        let k = CacheKey::by_pk("users", 1);
2196        cache.put(&k, Value::I64(1), Some(Duration::from_millis(10)));
2197
2198        thread::sleep(Duration::from_millis(50));
2199        assert!(!cache.contains(&k));
2200    }
2201
2202    // ===== evict_expired 手动清理 =====
2203
2204    #[test]
2205    fn test_evict_expired_removes_only_expired_entries() {
2206        let cache = L2Cache::new();
2207
2208        let k1 = CacheKey::by_pk("users", 1);
2209        let k2 = CacheKey::by_pk("users", 2);
2210
2211        cache.put(&k1, Value::I64(1), Some(Duration::from_millis(10)));
2212        cache.put(&k2, Value::I64(2), None); // 永不过期
2213
2214        thread::sleep(Duration::from_millis(50));
2215        let removed = cache.evict_expired();
2216
2217        assert_eq!(removed, 1);
2218        assert!(cache.get(&k1).is_none());
2219        assert!(cache.get(&k2).is_some());
2220    }
2221
2222    #[test]
2223    fn test_evict_expired_returns_zero_if_no_expired() {
2224        let cache = L2Cache::new();
2225        let k1 = CacheKey::by_pk("users", 1);
2226        cache.put(&k1, Value::I64(1), None);
2227
2228        let removed = cache.evict_expired();
2229        assert_eq!(removed, 0);
2230    }
2231
2232    // ===== 多线程测试 =====
2233
2234    #[test]
2235    fn test_concurrent_access() {
2236        let cache = std::sync::Arc::new(L2Cache::new());
2237        let mut handles = Vec::new();
2238
2239        // 多线程写入
2240        for i in 0..4 {
2241            let c = cache.clone();
2242            handles.push(thread::spawn(move || {
2243                for j in 0..10 {
2244                    let k = CacheKey::by_pk("users", i * 10 + j);
2245                    c.put(&k, Value::I64(i * 10 + j), None);
2246                }
2247            }));
2248        }
2249        for h in handles {
2250            h.join().unwrap();
2251        }
2252
2253        assert_eq!(cache.size(), 40);
2254
2255        // 多线程读取
2256        let mut handles = Vec::new();
2257        for i in 0..4 {
2258            let c = cache.clone();
2259            handles.push(thread::spawn(move || {
2260                for j in 0..10 {
2261                    let k = CacheKey::by_pk("users", i * 10 + j);
2262                    let v = c.get(&k);
2263                    assert!(v.is_some());
2264                }
2265            }));
2266        }
2267        for h in handles {
2268            h.join().unwrap();
2269        }
2270
2271        let stats = cache.stats();
2272        assert_eq!(stats.hits, 40);
2273    }
2274
2275    // ===== Default 测试 =====
2276
2277    #[test]
2278    fn test_default() {
2279        let cache = L2Cache::default();
2280        assert_eq!(cache.size(), 0);
2281    }
2282
2283    // ===== 综合场景 =====
2284
2285    #[test]
2286    fn test_realistic_scenario() {
2287        let cache = L2Cache::new();
2288
2289        // 1. 缓存用户表数据
2290        for i in 1..=5 {
2291            cache.put(
2292                &CacheKey::by_pk("users", i),
2293                Value::String(format!("user_{}", i)),
2294                None,
2295            );
2296        }
2297
2298        // 2. 缓存查询结果
2299        cache.put(
2300            &CacheKey::by_query("users", "active_users_hash"),
2301            Value::I64(5),
2302            None,
2303        );
2304
2305        // 3. 读取(部分命中、部分未命中)
2306        for i in 1..=10 {
2307            let _ = cache.get(&CacheKey::by_pk("users", i));
2308        }
2309
2310        let stats = cache.stats();
2311        assert_eq!(stats.hits, 5); // 1-5 命中
2312        assert_eq!(stats.misses, 5); // 6-10 未命中
2313        assert_eq!(stats.sets, 6); // 5 pk + 1 query
2314
2315        // 4. 用户表更新,失效所有缓存
2316        cache.invalidate_table("users");
2317
2318        // 5. 再次读取应全部未命中
2319        cache.reset_stats();
2320        for i in 1..=5 {
2321            let _ = cache.get(&CacheKey::by_pk("users", i));
2322        }
2323        let stats2 = cache.stats();
2324        assert_eq!(stats2.hits, 0);
2325        assert_eq!(stats2.misses, 5);
2326    }
2327
2328    // ===== WriteBehindWriter 测试(Fix #40) =====
2329
2330    #[tokio::test]
2331    async fn test_write_behind_basic_write_and_flush() {
2332        use std::sync::atomic::{AtomicUsize, Ordering};
2333        // 计数刷新调用次数
2334        let counter = Arc::new(AtomicUsize::new(0));
2335        let counter_clone = counter.clone();
2336        let on_flush: FlushCallback = Arc::new(move |ops: Vec<WriteOp>| {
2337            let c = counter_clone.clone();
2338            Box::pin(async move {
2339                c.fetch_add(ops.len(), Ordering::SeqCst);
2340                Ok(())
2341            })
2342        });
2343        let backend = Arc::new(InMemoryBackend::new());
2344        let writer = WriteBehindWriter::new(backend.clone(), on_flush);
2345
2346        // 写入 3 个键
2347        writer.write(b"k1", b"v1", None).await.unwrap();
2348        writer.write(b"k2", b"v2", None).await.unwrap();
2349        writer.write(b"k3", b"v3", None).await.unwrap();
2350
2351        // 缓存应立即可见
2352        let v1 = backend.get("k1").await.unwrap();
2353        assert_eq!(v1, Some(b"v1".to_vec()));
2354
2355        // 队列应有 3 个待刷新
2356        assert_eq!(writer.pending_count().await, 3);
2357
2358        // 刷新
2359        writer.flush().await.unwrap();
2360        assert_eq!(counter.load(Ordering::SeqCst), 3);
2361        assert_eq!(writer.pending_count().await, 0);
2362    }
2363
2364    #[tokio::test]
2365    async fn test_write_behind_delete() {
2366        let on_flush: FlushCallback =
2367            Arc::new(|_ops: Vec<WriteOp>| Box::pin(async move { Ok(()) }));
2368        let backend = Arc::new(InMemoryBackend::new());
2369        let writer = WriteBehindWriter::new(backend.clone(), on_flush);
2370
2371        // 写入后删除
2372        writer.write(b"k1", b"v1", None).await.unwrap();
2373        assert!(backend.get("k1").await.unwrap().is_some());
2374        writer.delete(b"k1").await.unwrap();
2375        // 删除后缓存中应不存在
2376        assert!(backend.get("k1").await.unwrap().is_none());
2377
2378        // flush 应处理 2 个操作(Set + Delete)
2379        writer.flush().await.unwrap();
2380        assert_eq!(writer.pending_count().await, 0);
2381    }
2382
2383    #[tokio::test]
2384    async fn test_write_behind_flush_failure_retries() {
2385        // 模拟刷新总是失败
2386        let on_flush: FlushCallback = Arc::new(|_ops: Vec<WriteOp>| {
2387            Box::pin(async move { Err(CacheError::Internal("backend down".to_string())) })
2388        });
2389        let backend = Arc::new(InMemoryBackend::new());
2390        let writer = WriteBehindWriter::new(backend.clone(), on_flush);
2391
2392        writer.write(b"k1", b"v1", None).await.unwrap();
2393        // flush 失败,操作应保留在队列中
2394        let result = writer.flush().await;
2395        assert!(result.is_err());
2396        assert_eq!(writer.pending_count().await, 1);
2397    }
2398
2399    #[tokio::test]
2400    async fn test_write_behind_empty_flush_noop() {
2401        let on_flush: FlushCallback =
2402            Arc::new(|_ops: Vec<WriteOp>| Box::pin(async move { Ok(()) }));
2403        let backend = Arc::new(InMemoryBackend::new());
2404        let writer = WriteBehindWriter::new(backend, on_flush);
2405        // 空队列 flush 应立即成功
2406        writer.flush().await.unwrap();
2407        assert_eq!(writer.pending_count().await, 0);
2408    }
2409
2410    #[tokio::test]
2411    async fn test_write_behind_error_callback_invoked() {
2412        use std::sync::atomic::{AtomicUsize, Ordering};
2413        let error_counter = Arc::new(AtomicUsize::new(0));
2414        let ec = error_counter.clone();
2415        let on_error: ErrorCallback = Arc::new(move |_ops, _err| {
2416            ec.fetch_add(1, Ordering::SeqCst);
2417        });
2418        let on_flush: FlushCallback = Arc::new(|_ops: Vec<WriteOp>| {
2419            Box::pin(async move { Err(CacheError::Internal("fail".to_string())) })
2420        });
2421        let backend = Arc::new(InMemoryBackend::new());
2422        let writer = WriteBehindWriter::new(backend, on_flush).with_error_callback(on_error);
2423
2424        writer.write(b"k1", b"v1", None).await.unwrap();
2425        let _ = writer.flush().await;
2426        assert_eq!(error_counter.load(Ordering::SeqCst), 1);
2427    }
2428
2429    // ===== 查询缓存测试(TASK-023) =====
2430
2431    #[tokio::test]
2432    async fn test_query_cache_hit() {
2433        use std::collections::HashMap;
2434
2435        let cache = L2Cache::new();
2436        let mut call_count = 0;
2437
2438        // 第一次查询:缓存未命中,执行查询
2439        let rows1 = cache
2440            .get_or_load_query(
2441                "users",
2442                "SELECT * FROM users WHERE status = ?",
2443                &[crate::value::Value::I64(1)],
2444                std::time::Duration::from_secs(300),
2445                || {
2446                    call_count += 1;
2447                    async {
2448                        let mut row = HashMap::new();
2449                        row.insert("id".to_string(), crate::value::Value::I64(1));
2450                        row.insert(
2451                            "name".to_string(),
2452                            crate::value::Value::String("Alice".to_string()),
2453                        );
2454                        Ok(vec![row])
2455                    }
2456                },
2457            )
2458            .await
2459            .unwrap();
2460
2461        assert_eq!(call_count, 1, "第一次查询应调用 loader");
2462        assert_eq!(rows1.len(), 1, "应返回 1 行");
2463
2464        // 第二次查询:缓存命中,不调用 loader
2465        let rows2 = cache
2466            .get_or_load_query(
2467                "users",
2468                "SELECT * FROM users WHERE status = ?",
2469                &[crate::value::Value::I64(1)],
2470                std::time::Duration::from_secs(300),
2471                || {
2472                    call_count += 1;
2473                    async { Ok(vec![]) }
2474                },
2475            )
2476            .await
2477            .unwrap();
2478
2479        assert_eq!(call_count, 1, "第二次查询不应调用 loader(缓存命中)");
2480        assert_eq!(rows2.len(), 1, "应返回缓存的 1 行");
2481    }
2482
2483    #[tokio::test]
2484    async fn test_query_cache_empty_result_cached() {
2485        let cache = L2Cache::new();
2486        let mut call_count = 0;
2487
2488        // 第一次查询:返回空结果
2489        let rows1 = cache
2490            .get_or_load_query(
2491                "users",
2492                "SELECT * FROM users WHERE status = ?",
2493                &[crate::value::Value::I64(999)],
2494                std::time::Duration::from_secs(300),
2495                || {
2496                    call_count += 1;
2497                    async { Ok(vec![]) }
2498                },
2499            )
2500            .await
2501            .unwrap();
2502
2503        assert_eq!(call_count, 1, "第一次查询应调用 loader");
2504        assert_eq!(rows1.len(), 0, "应返回空结果");
2505
2506        // 第二次查询:应命中空结果缓存
2507        let rows2 = cache
2508            .get_or_load_query(
2509                "users",
2510                "SELECT * FROM users WHERE status = ?",
2511                &[crate::value::Value::I64(999)],
2512                std::time::Duration::from_secs(300),
2513                || {
2514                    call_count += 1;
2515                    async { Ok(vec![]) }
2516                },
2517            )
2518            .await
2519            .unwrap();
2520
2521        assert_eq!(call_count, 1, "第二次查询不应调用 loader(空结果缓存命中)");
2522        assert_eq!(rows2.len(), 0, "应返回缓存的空结果");
2523    }
2524
2525    #[tokio::test]
2526    async fn test_query_cache_different_params() {
2527        use std::collections::HashMap;
2528
2529        let cache = L2Cache::new();
2530        let mut call_count = 0;
2531
2532        // 查询 status = 1
2533        let _ = cache
2534            .get_or_load_query(
2535                "users",
2536                "SELECT * FROM users WHERE status = ?",
2537                &[crate::value::Value::I64(1)],
2538                std::time::Duration::from_secs(300),
2539                || {
2540                    call_count += 1;
2541                    async {
2542                        let mut row = HashMap::new();
2543                        row.insert("id".to_string(), crate::value::Value::I64(1));
2544                        Ok(vec![row])
2545                    }
2546                },
2547            )
2548            .await
2549            .unwrap();
2550
2551        // 查询 status = 2(不同参数,应未命中)
2552        let rows2 = cache
2553            .get_or_load_query(
2554                "users",
2555                "SELECT * FROM users WHERE status = ?",
2556                &[crate::value::Value::I64(2)],
2557                std::time::Duration::from_secs(300),
2558                || {
2559                    call_count += 1;
2560                    async {
2561                        let mut row = HashMap::new();
2562                        row.insert("id".to_string(), crate::value::Value::I64(2));
2563                        row.insert(
2564                            "name".to_string(),
2565                            crate::value::Value::String("Bob".to_string()),
2566                        );
2567                        Ok(vec![row])
2568                    }
2569                },
2570            )
2571            .await
2572            .unwrap();
2573
2574        assert_eq!(call_count, 2, "不同参数应调用 loader 两次");
2575        assert_eq!(rows2.len(), 1, "应返回 1 行");
2576    }
2577
2578    #[tokio::test]
2579    async fn test_query_cache_invalidate() {
2580        use std::collections::HashMap;
2581
2582        let cache = L2Cache::new();
2583        let mut call_count = 0;
2584
2585        // 第一次查询
2586        let _ = cache
2587            .get_or_load_query(
2588                "users",
2589                "SELECT * FROM users WHERE status = ?",
2590                &[crate::value::Value::I64(1)],
2591                std::time::Duration::from_secs(300),
2592                || {
2593                    call_count += 1;
2594                    async {
2595                        let mut row = HashMap::new();
2596                        row.insert("id".to_string(), crate::value::Value::I64(1));
2597                        Ok(vec![row])
2598                    }
2599                },
2600            )
2601            .await
2602            .unwrap();
2603
2604        assert_eq!(call_count, 1, "第一次查询应调用 loader");
2605
2606        // 失效查询缓存
2607        cache.invalidate_query(
2608            "users",
2609            "SELECT * FROM users WHERE status = ?",
2610            &[crate::value::Value::I64(1)],
2611        );
2612
2613        // 再次查询:应重新调用 loader
2614        let _ = cache
2615            .get_or_load_query(
2616                "users",
2617                "SELECT * FROM users WHERE status = ?",
2618                &[crate::value::Value::I64(1)],
2619                std::time::Duration::from_secs(300),
2620                || {
2621                    call_count += 1;
2622                    async { Ok(vec![]) }
2623                },
2624            )
2625            .await
2626            .unwrap();
2627
2628        assert_eq!(call_count, 2, "失效后应重新调用 loader");
2629    }
2630
2631    #[tokio::test]
2632    async fn test_query_cache_hit_rate() {
2633        use std::collections::HashMap;
2634
2635        let cache = L2Cache::new();
2636        let mut call_count = 0;
2637
2638        // 模拟 10 次相同查询
2639        for _ in 0..10 {
2640            let _ = cache
2641                .get_or_load_query(
2642                    "users",
2643                    "SELECT * FROM users WHERE status = ?",
2644                    &[crate::value::Value::I64(1)],
2645                    std::time::Duration::from_secs(300),
2646                    || {
2647                        call_count += 1;
2648                        async {
2649                            let mut row = HashMap::new();
2650                            row.insert("id".to_string(), crate::value::Value::I64(1));
2651                            Ok(vec![row])
2652                        }
2653                    },
2654                )
2655                .await
2656                .unwrap();
2657        }
2658
2659        // 只有第一次调用 loader,后续 9 次命中缓存
2660        assert_eq!(call_count, 1, "10 次查询中只有 1 次调用 loader");
2661
2662        let stats = cache.stats();
2663        assert_eq!(stats.hits, 9, "应命中 9 次");
2664        assert_eq!(stats.misses, 1, "应未命中 1 次");
2665
2666        let hit_rate = stats.hit_rate();
2667        assert!(
2668            hit_rate >= 0.8,
2669            "命中率应 >= 80%,实际: {:.2}%",
2670            hit_rate * 100.0
2671        );
2672    }
2673}