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(Value::Json(json_str)) = self.get(&cache_key) {
904            // 缓存命中:反序列化结果
905            if let Ok(rows) = serde_json::from_str::<crate::pool::QueryRows>(&json_str) {
906                return Ok(rows);
907            }
908        }
909
910        // 缓存未命中:执行查询
911        let rows = loader().await?;
912
913        // 确定缓存 TTL(空结果缩短为 1/10)
914        let cache_ttl = if rows.is_empty() {
915            // 空结果也缓存,TTL 缩短为 1/10(至少 1 秒)
916            std::cmp::max(ttl / 10, Duration::from_secs(1))
917        } else {
918            ttl
919        };
920
921        // 序列化并缓存结果
922        if let Ok(json_str) = serde_json::to_string(&rows) {
923            self.put(&cache_key, Value::Json(json_str), Some(cache_ttl));
924        }
925
926        Ok(rows)
927    }
928
929    /// TASK-023:失效查询缓存
930    ///
931    /// 根据 SQL + 参数失效特定的查询缓存项。
932    pub fn invalidate_query(&self, table: &str, sql: &str, params: &[crate::value::Value]) {
933        use std::collections::hash_map::DefaultHasher;
934        use std::hash::{Hash, Hasher};
935
936        let mut hasher = DefaultHasher::new();
937        sql.hash(&mut hasher);
938        for param in params {
939            param.to_string().hash(&mut hasher);
940        }
941        let query_hash = hasher.finish();
942        let cache_key = CacheKey::by_query(table, query_hash);
943        self.invalidate(&cache_key);
944    }
945
946    /// 获取指定表的命中率统计
947    pub fn table_stats(&self, table: &str) -> Option<PerTableStats> {
948        self.table_stats
949            .read()
950            .ok()
951            .and_then(|s| s.get(table).cloned())
952    }
953
954    /// 获取所有表的命中率统计快照
955    pub fn all_table_stats(&self) -> HashMap<String, PerTableStats> {
956        self.table_stats
957            .read()
958            .map(|s| s.clone())
959            .unwrap_or_default()
960    }
961
962    /// 检查缓存项是否存在(不更新统计与 LRU 顺序)
963    pub fn contains(&self, key: &CacheKey) -> bool {
964        let key_str = key.to_string_key();
965        self.data
966            .read()
967            .map(|d| d.get(&key_str).map(|e| !e.is_expired()).unwrap_or(false))
968            .unwrap_or(false)
969    }
970
971    /// 手动清理所有过期项
972    pub fn evict_expired(&self) -> usize {
973        let expired_keys: Vec<String> = {
974            // 锁毒化时返回空 Vec(无过期项可清理)
975            let data = match self.data.read() {
976                Ok(d) => d,
977                Err(_) => return 0,
978            };
979            data.iter()
980                .filter(|(_, e)| e.is_expired())
981                .map(|(k, _)| k.clone())
982                .collect()
983        };
984
985        // 反向查找 key_str -> table_name,用于按表分桶统计
986        // 锁毒化时返回空 map(按表统计将不更新,不影响数据清理)
987        let key_to_table: HashMap<String, String> = match self.table_index.read() {
988            Ok(idx) => {
989                let mut map = HashMap::new();
990                for (table, keys) in idx.iter() {
991                    for k in keys {
992                        map.insert(k.clone(), table.clone());
993                    }
994                }
995                map
996            }
997            Err(_) => HashMap::new(),
998        };
999
1000        let mut removed = 0;
1001        if !expired_keys.is_empty() {
1002            // 锁毒化时跳过清理(视为未删除)
1003            let mut data = match self.data.write() {
1004                Ok(d) => d,
1005                Err(_) => return 0,
1006            };
1007            for k in &expired_keys {
1008                if data.remove(k).is_some() {
1009                    removed += 1;
1010                }
1011            }
1012        }
1013
1014        if removed > 0 {
1015            // O(m) 批量移除(m = expired_keys),而非旧实现的 O(n*m) retain
1016            // 锁毒化时跳过 LRU 顺序同步(不影响数据正确性)
1017            if let Ok(mut order) = self.access_order.write() {
1018                for k in &expired_keys {
1019                    order.remove(k);
1020                }
1021            }
1022            {
1023                // 锁毒化时跳过统计更新(不影响数据正确性)
1024                if let Ok(mut stats) = self.stats.write() {
1025                    stats.evictions += removed as u64;
1026                }
1027            }
1028            // 更新按表分桶统计(单独持锁,避免与 stats 锁同时持有)
1029            if let Ok(mut tbl_stats) = self.table_stats.write() {
1030                for k in &expired_keys {
1031                    if let Some(table) = key_to_table.get(k) {
1032                        tbl_stats.entry(table.clone()).or_default().evictions += 1;
1033                    }
1034                }
1035            }
1036        }
1037        removed
1038    }
1039
1040    /// 更新缓存项的 TTL(若 key 不存在返回 false)
1041    ///
1042    /// 用于 `Cache` trait 的 `expire` 方法实现。
1043    pub fn update_ttl(&self, key: &CacheKey, ttl: Duration) -> bool {
1044        let key_str = key.to_string_key();
1045        let mut data = match self.data.write() {
1046            Ok(d) => d,
1047            Err(_) => return false,
1048        };
1049        if let Some(entry) = data.get_mut(&key_str) {
1050            entry.expires_at = Some(Instant::now() + ttl);
1051            true
1052        } else {
1053            false
1054        }
1055    }
1056
1057    /// 获取缓存项的剩余 TTL
1058    ///
1059    /// 返回值:
1060    /// - `None`:key 不存在或已过期
1061    /// - `Some(None)`:key 存在但无 TTL(永不过期)
1062    /// - `Some(Some(d))`:key 存在且剩余 TTL 为 d
1063    ///
1064    /// 用于 `Cache` trait 的 `ttl` 方法实现。
1065    pub fn remaining_ttl(&self, key: &CacheKey) -> Option<Option<Duration>> {
1066        let key_str = key.to_string_key();
1067        let data = self.data.read().ok()?;
1068        let entry = data.get(&key_str)?;
1069        match entry.expires_at {
1070            Some(expires_at) => {
1071                let now = Instant::now();
1072                if expires_at <= now {
1073                    None
1074                } else {
1075                    Some(Some(expires_at.duration_since(now)))
1076                }
1077            }
1078            None => Some(None),
1079        }
1080    }
1081}
1082
1083// ============================================================================
1084// Cache trait 实现 — 让 L2Cache 可作为通用 Cache 使用
1085// ============================================================================
1086
1087/// 为 L2Cache 实现 `Cache` trait
1088///
1089/// 通过 `CacheKey::by_pk("__cache__", key)` 将字符串 key 映射到 L2Cache 的 CacheKey 体系,
1090/// 所有通过 `Cache` trait 写入的缓存项归入 `__cache__` 表,与业务缓存项隔离。
1091///
1092/// 值以 `Value::Bytes(Vec<u8>)` 存储;若通过 `Cache::get` 读取到的 Value 非 Bytes 类型
1093/// (如直接通过 `L2Cache::put` 写入的其他类型),则回退为 JSON 序列化。
1094impl Cache for L2Cache {
1095    fn get(&self, key: &str) -> Result<Option<Vec<u8>>, CacheError> {
1096        let cache_key = CacheKey::by_pk("__cache__", key);
1097        match L2Cache::get(self, &cache_key) {
1098            Some(Value::Bytes(bytes)) => Ok(Some(bytes)),
1099            Some(other) => {
1100                let json = serde_json::to_vec(&other)
1101                    .map_err(|e| CacheError::SerializationError(e.to_string()))?;
1102                Ok(Some(json))
1103            }
1104            None => Ok(None),
1105        }
1106    }
1107
1108    fn set(&self, key: &str, value: Vec<u8>, ttl: Option<Duration>) -> Result<(), CacheError> {
1109        let cache_key = CacheKey::by_pk("__cache__", key);
1110        self.put(&cache_key, Value::Bytes(value), ttl);
1111        Ok(())
1112    }
1113
1114    fn delete(&self, key: &str) -> Result<(), CacheError> {
1115        let cache_key = CacheKey::by_pk("__cache__", key);
1116        self.invalidate(&cache_key);
1117        Ok(())
1118    }
1119
1120    fn clear(&self) -> Result<(), CacheError> {
1121        // 仅清除通过 Cache trait 写入的原始缓存项(__cache__ 表),
1122        // 不影响通过 CacheKey 直接写入的业务缓存项。
1123        self.invalidate_table("__cache__");
1124        Ok(())
1125    }
1126
1127    fn exists(&self, key: &str) -> Result<bool, CacheError> {
1128        let cache_key = CacheKey::by_pk("__cache__", key);
1129        Ok(self.contains(&cache_key))
1130    }
1131
1132    fn expire(&self, key: &str, ttl: Duration) -> Result<(), CacheError> {
1133        let cache_key = CacheKey::by_pk("__cache__", key);
1134        if self.update_ttl(&cache_key, ttl) {
1135            Ok(())
1136        } else {
1137            Err(CacheError::NotFound(key.to_string()))
1138        }
1139    }
1140
1141    fn ttl(&self, key: &str) -> Result<Option<Duration>, CacheError> {
1142        let cache_key = CacheKey::by_pk("__cache__", key);
1143        match self.remaining_ttl(&cache_key) {
1144            None => Err(CacheError::NotFound(key.to_string())),
1145            Some(None) => Ok(None),
1146            Some(Some(d)) => Ok(Some(d)),
1147        }
1148    }
1149}
1150
1151// ============================================================================
1152// L2CacheBackend — 分布式缓存后端抽象(trait + InMemoryBackend + RedisBackend stub)
1153// ============================================================================
1154
1155/// L2 缓存异步 Future 类型别名
1156///
1157/// 用于简化 `L2CacheBackend` trait 中方法的返回类型签名,
1158/// 避免重复书写复杂的 `Pin<Box<dyn Future<...> + Send + 'a>>`。
1159pub type L2CacheFuture<'a, T> = Pin<Box<dyn Future<Output = Result<T, CacheError>> + Send + 'a>>;
1160
1161/// L2 缓存后端 trait(分布式抽象)
1162///
1163/// 定义跨进程共享的二级缓存后端接口,支持进程内内存、Redis 等实现。
1164/// 手动解糖 async 方法(不使用 `#[async_trait]`),与 `Connection` trait 风格一致。
1165///
1166/// # 设计要点
1167///
1168/// - **键值以 `&[u8]` 传输**:后端无关的序列化格式(由调用方决定 bincode/json 等)
1169/// - **TTL 可选**:`Some(Duration)` 设置过期时间,`None` 表示永不过期
1170/// - **前缀失效**:`invalidate_prefix` 批量失效某前缀的所有键(用于表级失效)
1171///
1172/// # 实现方
1173///
1174/// - [`InMemoryBackend`]:进程内内存后端(默认,单机场景)
1175/// - [`RedisBackend`]:Redis 分布式后端(stub,需启用 `redis` feature 并补充依赖)
1176pub trait L2CacheBackend: Send + Sync {
1177    /// 获取缓存值,不存在或已过期返回 `None`
1178    fn get<'a>(&'a self, key: &'a str) -> L2CacheFuture<'a, Option<Vec<u8>>>;
1179
1180    /// 设置缓存值,`ttl` 为 `None` 表示永不过期
1181    fn set<'a>(
1182        &'a self,
1183        key: &'a str,
1184        value: &'a [u8],
1185        ttl: Option<Duration>,
1186    ) -> L2CacheFuture<'a, ()>;
1187
1188    /// 删除单个缓存键
1189    fn delete<'a>(&'a self, key: &'a str) -> L2CacheFuture<'a, ()>;
1190
1191    /// 按前缀批量失效缓存项(用于表级失效)
1192    fn invalidate_prefix<'a>(&'a self, prefix: &'a str) -> L2CacheFuture<'a, ()>;
1193}
1194
1195/// 进程内内存后端(默认实现)
1196///
1197/// 适用于单机场景,不跨进程共享。内部使用 `RwLock<HashMap>` 存储,
1198/// `invalidate_prefix` 通过遍历键前缀匹配实现(O(n),单机场景可接受)。
1199///
1200/// # 线程安全
1201///
1202/// 所有操作通过 `RwLock` 保护,可在多线程环境下共享。
1203///
1204/// # 注意
1205///
1206/// 由于 `std::sync::RwLock` 的 guard 是 `!Send`,所有同步操作在创建
1207/// `Future` 之前完成,guard 在 block 退出时释放,避免跨 `.await` 持锁。
1208pub struct InMemoryBackend {
1209    /// 缓存数据:key -> (value, expiry_time)
1210    /// 使用类型别名降低类型复杂度(clippy::type_complexity)
1211    data: RwLock<InMemoryCacheData>,
1212}
1213
1214/// 内存缓存条目类型别名
1215type InMemoryCacheData = HashMap<String, (Vec<u8>, Option<Instant>)>;
1216
1217impl Default for InMemoryBackend {
1218    fn default() -> Self {
1219        Self::new()
1220    }
1221}
1222
1223impl InMemoryBackend {
1224    /// 创建空的内存后端
1225    pub fn new() -> Self {
1226        Self {
1227            data: RwLock::new(HashMap::new()),
1228        }
1229    }
1230}
1231
1232impl L2CacheBackend for InMemoryBackend {
1233    fn get<'a>(&'a self, key: &'a str) -> L2CacheFuture<'a, Option<Vec<u8>>> {
1234        // 同步完成读操作,guard 在 block 退出时释放,避免跨 await 持锁
1235        let result = {
1236            let data = match self.data.read() {
1237                Ok(d) => d,
1238                Err(e) => {
1239                    let err = CacheError::from(e);
1240                    return Box::pin(async move { Err(err) });
1241                }
1242            };
1243            match data.get(key) {
1244                Some((value, expiry)) => {
1245                    // 过期检查:expiry 为 None 表示永不过期
1246                    if expiry.map(|t| t <= Instant::now()).unwrap_or(false) {
1247                        Ok(None)
1248                    } else {
1249                        Ok(Some(value.clone()))
1250                    }
1251                }
1252                None => Ok(None),
1253            }
1254        };
1255        Box::pin(async move { result })
1256    }
1257
1258    fn set<'a>(
1259        &'a self,
1260        key: &'a str,
1261        value: &'a [u8],
1262        ttl: Option<Duration>,
1263    ) -> L2CacheFuture<'a, ()> {
1264        let result = {
1265            let mut data = match self.data.write() {
1266                Ok(d) => d,
1267                Err(e) => {
1268                    let err = CacheError::from(e);
1269                    return Box::pin(async move { Err(err) });
1270                }
1271            };
1272            let expiry = ttl.map(|d| Instant::now() + d);
1273            data.insert(key.to_string(), (value.to_vec(), expiry));
1274            Ok(())
1275        };
1276        Box::pin(async move { result })
1277    }
1278
1279    fn delete<'a>(&'a self, key: &'a str) -> L2CacheFuture<'a, ()> {
1280        let result = {
1281            let mut data = match self.data.write() {
1282                Ok(d) => d,
1283                Err(e) => {
1284                    let err = CacheError::from(e);
1285                    return Box::pin(async move { Err(err) });
1286                }
1287            };
1288            data.remove(key);
1289            Ok(())
1290        };
1291        Box::pin(async move { result })
1292    }
1293
1294    fn invalidate_prefix<'a>(&'a self, prefix: &'a str) -> L2CacheFuture<'a, ()> {
1295        let result = {
1296            let mut data = match self.data.write() {
1297                Ok(d) => d,
1298                Err(e) => {
1299                    let err = CacheError::from(e);
1300                    return Box::pin(async move { Err(err) });
1301                }
1302            };
1303            // O(n) 遍历,删除所有以 prefix 开头的键
1304            let keys_to_remove: Vec<String> = data
1305                .keys()
1306                .filter(|k| k.starts_with(prefix))
1307                .cloned()
1308                .collect();
1309            for k in keys_to_remove {
1310                data.remove(&k);
1311            }
1312            Ok(())
1313        };
1314        Box::pin(async move { result })
1315    }
1316}
1317
1318/// Redis 分布式缓存后端
1319///
1320/// 基于 `redis` crate 0.27 + `tokio-comp` 异步 IO + `connection-manager` 自动重连。
1321///
1322/// # 实现要点
1323///
1324/// - **连接管理**:使用 `redis::aio::ConnectionManager`(内部自动重连的连接池)
1325/// - **`get`** → `redis::cmd("GET")` 异步执行
1326/// - **`set`** → `SET key value` + 可选 `EX seconds`(合并为单次 `SET` 命令,原子性保证)
1327/// - **`delete`** → `redis::cmd("DEL")`
1328/// - **`invalidate_prefix`** → `SCAN` + 批量 `DEL`(避免 `KEYS` 阻塞 Redis 主线程)
1329///   - 使用 `COUNT 100` 分批扫描,避免单次 SCAN 拉取过多 key 导致阻塞
1330///   - 多次 DEL 调用合并为单次 pipeline 批量执行,减少 RTT 开销
1331///
1332/// # 错误处理
1333///
1334/// - 连接失败 → `CacheError::Internal`,由调用方决定是否重试
1335/// - Redis 命令错误 → 原始错误字符串包装为 `CacheError::Internal`
1336///
1337/// # 启用方式
1338///
1339/// 在 `Cargo.toml` 中启用 `redis` feature:
1340/// ```toml
1341/// [dependencies]
1342/// sz-orm-core = { version = "1.0", features = ["redis"] }
1343/// ```
1344///
1345/// # 使用示例
1346///
1347/// ```no_run
1348/// # use sz_orm_core::l2_cache::{RedisBackend, L2CacheBackend};
1349/// # use std::time::Duration;
1350/// # #[tokio::main]
1351/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
1352/// let backend = RedisBackend::new("redis://127.0.0.1:6379/0").await?;
1353/// backend.set("user:1", b"alice", Some(Duration::from_secs(60))).await?;
1354/// let val = backend.get("user:1").await?;
1355/// assert_eq!(val, Some(b"alice".to_vec()));
1356/// backend.delete("user:1").await?;
1357/// # Ok(())
1358/// # }
1359/// ```
1360#[cfg(feature = "redis")]
1361pub struct RedisBackend {
1362    /// Redis 异步连接管理器(自动重连)
1363    manager: redis::aio::ConnectionManager,
1364}
1365
1366#[cfg(feature = "redis")]
1367impl RedisBackend {
1368    /// 创建 Redis 后端
1369    ///
1370    /// `url` 格式:`redis://[:password@]host:port[/db]`
1371    /// - `redis://127.0.0.1:6379/0` — 默认 DB 0
1372    /// - `redis://:secret@127.0.0.1:6379/1` — 带密码,DB 1
1373    ///
1374    /// # 错误
1375    ///
1376    /// - 连接失败 → `CacheError::Internal`
1377    pub async fn new(url: impl Into<String>) -> Result<Self, CacheError> {
1378        let url = url.into();
1379        let client = redis::Client::open(url.as_str())
1380            .map_err(|e| CacheError::Internal(format!("Redis client create failed: {}", e)))?;
1381        let manager = redis::aio::ConnectionManager::new(client)
1382            .await
1383            .map_err(|e| CacheError::Internal(format!("Redis connect failed: {}", e)))?;
1384        Ok(Self { manager })
1385    }
1386
1387    /// 使用已有 ConnectionManager 创建后端(用于复用连接池)
1388    pub fn from_manager(manager: redis::aio::ConnectionManager) -> Self {
1389        Self { manager }
1390    }
1391
1392    /// SCAN + 批量 DEL 实现前缀失效
1393    ///
1394    /// 使用 `SCAN cursor MATCH prefix* COUNT 100` 迭代扫描所有匹配的 key,
1395    /// 累计到本地 Vec 后通过 pipeline 批量 DEL,避免:
1396    /// 1. `KEYS pattern` 阻塞 Redis 主线程(O(N) 全表扫描)
1397    /// 2. 单次 DEL 调用过多导致 RTT 累积
1398    ///
1399    /// # 参数
1400    /// - `prefix`:key 前缀(不含通配符,函数内部追加 `*`)
1401    ///
1402    /// # 返回
1403    /// - `Ok(())`:扫描完成,无论是否删除了 key
1404    /// - `Err(_)`:连接错误或命令执行失败
1405    async fn invalidate_prefix_inner(&self, prefix: &str) -> Result<(), CacheError> {
1406        let pattern = format!("{}*", prefix);
1407        let mut cursor: u64 = 0;
1408        loop {
1409            // SCAN 返回 (next_cursor, Vec<key>)
1410            // 注意:必须先 clone 出独立的 conn,避免 &mut temporary 借用问题
1411            let mut conn = self.manager.clone();
1412            let scan_result: redis::RedisResult<(u64, Vec<String>)> = redis::cmd("SCAN")
1413                .arg(cursor)
1414                .arg("MATCH")
1415                .arg(&pattern)
1416                .arg("COUNT")
1417                .arg(100usize)
1418                .query_async(&mut conn)
1419                .await;
1420            let (next_cursor, keys): (u64, Vec<String>) = scan_result
1421                .map_err(|e| CacheError::Internal(format!("Redis SCAN failed: {}", e)))?;
1422
1423            if !keys.is_empty() {
1424                // 批量 DEL:使用 pipeline 减少往返次数
1425                let mut pipe = redis::pipe();
1426                for k in &keys {
1427                    pipe.del(k);
1428                }
1429                // 显式指定 RedisResult<()> 类型,避免 never type fallback 警告
1430                let del_result: redis::RedisResult<()> = pipe.query_async(&mut conn).await;
1431                del_result.map_err(|e| {
1432                    CacheError::Internal(format!("Redis DEL pipeline failed: {}", e))
1433                })?;
1434            }
1435
1436            // cursor == 0 表示扫描完成
1437            if next_cursor == 0 {
1438                break;
1439            }
1440            cursor = next_cursor;
1441        }
1442        Ok(())
1443    }
1444}
1445
1446#[cfg(feature = "redis")]
1447impl L2CacheBackend for RedisBackend {
1448    fn get<'a>(&'a self, key: &'a str) -> L2CacheFuture<'a, Option<Vec<u8>>> {
1449        Box::pin(async move {
1450            use redis::AsyncCommands;
1451            let mut conn = self.manager.clone();
1452            let value: Option<Vec<u8>> = conn
1453                .get(key)
1454                .await
1455                .map_err(|e| CacheError::Internal(format!("Redis GET failed: {}", e)))?;
1456            Ok(value)
1457        })
1458    }
1459
1460    fn set<'a>(
1461        &'a self,
1462        key: &'a str,
1463        value: &'a [u8],
1464        ttl: Option<Duration>,
1465    ) -> L2CacheFuture<'a, ()> {
1466        Box::pin(async move {
1467            use redis::AsyncCommands;
1468            let mut conn = self.manager.clone();
1469            // 合并 SET + EX 为单次原子操作(SET key value EX seconds)
1470            // 避免 SET 后 EXPIRE 之间的窗口期 key 无 TTL
1471            match ttl {
1472                Some(d) => {
1473                    let secs = d.as_secs();
1474                    if secs > 0 {
1475                        let _: () = conn.set_ex(key, value, secs).await.map_err(|e| {
1476                            CacheError::Internal(format!("Redis SET EX failed: {}", e))
1477                        })?;
1478                    } else {
1479                        // TTL < 1s:退化为 SET + PEXPIRE(毫秒精度)
1480                        let _: () = conn.set(key, value).await.map_err(|e| {
1481                            CacheError::Internal(format!("Redis SET failed: {}", e))
1482                        })?;
1483                        // 毫秒数转换为 i64(u128 → i64,实际值不会超过 i64 范围)
1484                        let ms: i64 = d.as_millis().min(i64::MAX as u128) as i64;
1485                        let _: () = conn.pexpire(key, ms).await.map_err(|e| {
1486                            CacheError::Internal(format!("Redis PEXPIRE failed: {}", e))
1487                        })?;
1488                    }
1489                }
1490                None => {
1491                    let _: () = conn
1492                        .set(key, value)
1493                        .await
1494                        .map_err(|e| CacheError::Internal(format!("Redis SET failed: {}", e)))?;
1495                }
1496            }
1497            Ok(())
1498        })
1499    }
1500
1501    fn delete<'a>(&'a self, key: &'a str) -> L2CacheFuture<'a, ()> {
1502        Box::pin(async move {
1503            use redis::AsyncCommands;
1504            let mut conn = self.manager.clone();
1505            let _: () = conn
1506                .del(key)
1507                .await
1508                .map_err(|e| CacheError::Internal(format!("Redis DEL failed: {}", e)))?;
1509            Ok(())
1510        })
1511    }
1512
1513    fn invalidate_prefix<'a>(&'a self, prefix: &'a str) -> L2CacheFuture<'a, ()> {
1514        Box::pin(async move { self.invalidate_prefix_inner(prefix).await })
1515    }
1516}
1517
1518/// Redis 分布式缓存后端(stub,未启用 `redis` feature 时使用)
1519///
1520/// 当未启用 `redis` feature 时,所有操作返回 `CacheError::Internal`,
1521/// 提示用户在 `Cargo.toml` 中启用 `redis` feature。
1522#[cfg(not(feature = "redis"))]
1523pub struct RedisBackend {
1524    /// Redis 连接字符串(保留字段用于错误提示)
1525    url: String,
1526}
1527
1528#[cfg(not(feature = "redis"))]
1529impl RedisBackend {
1530    /// 创建 Redis 后端 stub
1531    ///
1532    /// 返回 stub 实例,所有操作将返回 `CacheError::Internal`。
1533    /// 启用 `redis` feature 后自动切换为真实实现。
1534    pub fn new(_url: impl Into<String>) -> Self {
1535        Self { url: _url.into() }
1536    }
1537}
1538
1539#[cfg(not(feature = "redis"))]
1540impl L2CacheBackend for RedisBackend {
1541    fn get<'a>(&'a self, _key: &'a str) -> L2CacheFuture<'a, Option<Vec<u8>>> {
1542        let url = self.url.clone();
1543        Box::pin(async move {
1544            Err(CacheError::Internal(format!(
1545                "RedisBackend not compiled: enable 'redis' feature in sz-orm-core. URL: {}",
1546                url
1547            )))
1548        })
1549    }
1550
1551    fn set<'a>(
1552        &'a self,
1553        _key: &'a str,
1554        _value: &'a [u8],
1555        _ttl: Option<Duration>,
1556    ) -> L2CacheFuture<'a, ()> {
1557        let url = self.url.clone();
1558        Box::pin(async move {
1559            Err(CacheError::Internal(format!(
1560                "RedisBackend not compiled: enable 'redis' feature in sz-orm-core. URL: {}",
1561                url
1562            )))
1563        })
1564    }
1565
1566    fn delete<'a>(&'a self, _key: &'a str) -> L2CacheFuture<'a, ()> {
1567        let url = self.url.clone();
1568        Box::pin(async move {
1569            Err(CacheError::Internal(format!(
1570                "RedisBackend not compiled: enable 'redis' feature in sz-orm-core. URL: {}",
1571                url
1572            )))
1573        })
1574    }
1575
1576    fn invalidate_prefix<'a>(&'a self, _prefix: &'a str) -> L2CacheFuture<'a, ()> {
1577        let url = self.url.clone();
1578        Box::pin(async move {
1579            Err(CacheError::Internal(format!(
1580                "RedisBackend not compiled: enable 'redis' feature in sz-orm-core. URL: {}",
1581                url
1582            )))
1583        })
1584    }
1585}
1586
1587// ============================================================================
1588// WriteBehind — 异步写回缓存模式(Fix #40)
1589// ============================================================================
1590//
1591// Write-Behind 模式:写操作立即更新缓存,并异步批量刷新到后端存储(如数据库)。
1592// 适用于写吞吐高、可容忍短暂数据不一致的场景。
1593//
1594// # 工作流程
1595//
1596// 1. `write()` / `delete()` → 立即更新 L2CacheBackend,同时将操作入队
1597// 2. 后台任务每 `flush_interval` 触发一次 `flush()`,或显式调用 `flush()`
1598// 3. `flush()` 将队列中的操作批量应用回调 `on_flush`
1599//
1600// # 失败处理
1601//
1602// - 缓存写入失败:立即返回错误给调用方
1603// - 队列写入失败(锁中毒):返回 `CacheError::Internal`
1604// - 后端刷新失败:调用 `on_error` 回调,操作**保留在队列中**等待下次重试
1605//
1606// # 注意
1607//
1608// - 不保证写入顺序与刷新顺序一致(多生产者并发入队)
1609// - 同一 key 的多次写入会按入队顺序刷新(FIFO)
1610// - 调用方需自行处理幂等性(如使用 upsert)
1611
1612/// 写回操作类型
1613#[derive(Debug, Clone)]
1614pub enum WriteOp {
1615    /// SET 操作(key, value, ttl)
1616    Set {
1617        /// 缓存键
1618        key: String,
1619        /// 缓存值(已序列化的字节)
1620        value: Vec<u8>,
1621        /// TTL(与 set 调用一致)
1622        ttl: Option<Duration>,
1623    },
1624    /// DELETE 操作
1625    Delete {
1626        /// 缓存键
1627        key: String,
1628    },
1629}
1630
1631/// 写回刷新回调类型
1632///
1633/// 接收一批待刷新的操作,调用方需将其应用到后端存储(如执行 SQL)。
1634/// 返回 `Err` 表示刷新失败,操作将保留在队列中等待重试。
1635pub type FlushCallback = Arc<
1636    dyn Fn(Vec<WriteOp>) -> Pin<Box<dyn Future<Output = Result<(), CacheError>> + Send>>
1637        + Send
1638        + Sync,
1639>;
1640
1641/// 写回错误回调类型
1642pub type ErrorCallback = Arc<dyn Fn(Vec<WriteOp>, CacheError) + Send + Sync>;
1643
1644/// Write-Behind 写入器
1645///
1646/// 包装一个 `L2CacheBackend`,将写操作同时写入缓存与内存队列,
1647/// 后台任务或显式 `flush()` 触发批量刷新到后端存储。
1648///
1649/// # 线程安全
1650///
1651/// 内部使用 `tokio::sync::Mutex` 保护队列,可被多线程并发调用。
1652///
1653/// # 示例
1654///
1655/// ```no_run
1656/// use sz_orm_core::l2_cache::{WriteBehindWriter, WriteOp, InMemoryBackend};
1657/// use std::sync::Arc;
1658/// use std::time::Duration;
1659///
1660/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1661/// let backend = Arc::new(InMemoryBackend::new());
1662/// let on_flush = Arc::new(|ops: Vec<WriteOp>| {
1663///     Box::pin(async move {
1664///         // 这里将 ops 应用到数据库(如批量 INSERT/UPDATE)
1665///         for op in &ops {
1666///             println!("flushing: {:?}", op);
1667///         }
1668///         Ok(())
1669///     }) as std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), _>> + Send>>
1670///     as _
1671/// });
1672/// let writer = WriteBehindWriter::new(backend.clone(), on_flush);
1673///
1674/// // 立即更新缓存,并异步刷新到数据库
1675/// writer.write(b"key1", b"value1", None).await?;
1676///
1677/// // 显式刷新所有待处理操作
1678/// writer.flush().await?;
1679/// # Ok(())
1680/// # }
1681/// ```
1682pub struct WriteBehindWriter {
1683    /// 被包装的 L2 缓存后端
1684    backend: Arc<dyn L2CacheBackend>,
1685    /// 待刷新操作队列
1686    queue: tokio::sync::Mutex<Vec<WriteOp>>,
1687    /// 刷新回调
1688    on_flush: FlushCallback,
1689    /// 错误回调(可选)
1690    on_error: Option<ErrorCallback>,
1691}
1692
1693impl WriteBehindWriter {
1694    /// 创建 Write-Behind 写入器
1695    ///
1696    /// # 参数
1697    /// - `backend`:被包装的 L2 缓存后端(如 `InMemoryBackend`、`RedisBackend`)
1698    /// - `on_flush`:刷新回调,接收一批操作并应用到后端存储
1699    pub fn new(backend: Arc<dyn L2CacheBackend>, on_flush: FlushCallback) -> Self {
1700        Self {
1701            backend,
1702            queue: tokio::sync::Mutex::new(Vec::new()),
1703            on_flush,
1704            on_error: None,
1705        }
1706    }
1707
1708    /// 设置错误回调
1709    ///
1710    /// 当 `flush()` 失败时调用,传入失败的操作和错误信息。
1711    /// 注意:失败的操作会保留在队列中等待下次重试。
1712    pub fn with_error_callback(mut self, on_error: ErrorCallback) -> Self {
1713        self.on_error = Some(on_error);
1714        self
1715    }
1716
1717    /// 写入缓存(立即更新后端缓存 + 入队待刷新)
1718    ///
1719    /// # 参数
1720    /// - `key`:缓存键
1721    /// - `value`:缓存值(字节切片)
1722    /// - `ttl`:TTL,`None` 表示永不过期
1723    pub async fn write(
1724        &self,
1725        key: &[u8],
1726        value: &[u8],
1727        ttl: Option<Duration>,
1728    ) -> Result<(), CacheError> {
1729        let key_str = String::from_utf8_lossy(key).into_owned();
1730        // 1. 立即更新缓存(同步可见性优先)
1731        self.backend.set(&key_str, value, ttl).await?;
1732        // 2. 入队待刷新
1733        let op = WriteOp::Set {
1734            key: key_str,
1735            value: value.to_vec(),
1736            ttl,
1737        };
1738        self.queue.lock().await.push(op);
1739        Ok(())
1740    }
1741
1742    /// 删除缓存项(立即从后端缓存删除 + 入队待刷新)
1743    pub async fn delete(&self, key: &[u8]) -> Result<(), CacheError> {
1744        let key_str = String::from_utf8_lossy(key).into_owned();
1745        // 1. 立即从缓存删除
1746        self.backend.delete(&key_str).await?;
1747        // 2. 入队待刷新
1748        let op = WriteOp::Delete { key: key_str };
1749        self.queue.lock().await.push(op);
1750        Ok(())
1751    }
1752
1753    /// 刷新所有待处理操作到后端存储
1754    ///
1755    /// 将队列中的操作一次性传给 `on_flush` 回调。
1756    /// 若回调返回错误,操作保留在队列中等待下次重试。
1757    pub async fn flush(&self) -> Result<(), CacheError> {
1758        // 1. 取出所有待刷新操作(drain)
1759        let ops: Vec<WriteOp> = {
1760            let mut guard = self.queue.lock().await;
1761            std::mem::take(&mut *guard)
1762        };
1763        if ops.is_empty() {
1764            return Ok(());
1765        }
1766        // 2. 调用刷新回调
1767        match (self.on_flush)(ops.clone()).await {
1768            Ok(()) => Ok(()),
1769            Err(e) => {
1770                // 刷新失败:将操作放回队列,等待下次重试
1771                let mut guard = self.queue.lock().await;
1772                guard.extend(ops.clone());
1773                // 触发错误回调(如有)
1774                if let Some(ref on_error) = self.on_error {
1775                    on_error(ops, e.clone());
1776                }
1777                Err(e)
1778            }
1779        }
1780    }
1781
1782    /// 当前队列中待刷新的操作数(用于监控)
1783    pub async fn pending_count(&self) -> usize {
1784        self.queue.lock().await.len()
1785    }
1786
1787    /// 启动后台自动刷新任务
1788    ///
1789    /// 每 `interval` 触发一次 `flush()`,直到 `WriteBehindWriter` 被丢弃。
1790    /// 返回 `JoinHandle`,调用方可用于等待任务结束。
1791    ///
1792    /// # 注意
1793    ///
1794    /// 调用方需保证 `WriteBehindWriter` 的生命周期长于后台任务,
1795    /// 否则在 writer 被丢弃后,后台任务会因 Arc 引用计数归零而停止。
1796    pub fn spawn_auto_flush(self: Arc<Self>, interval: Duration) -> tokio::task::JoinHandle<()> {
1797        tokio::spawn(async move {
1798            let mut ticker = tokio::time::interval(interval);
1799            // 跳过首次立即触发(首次 tick 会立即返回)
1800            ticker.tick().await;
1801            loop {
1802                ticker.tick().await;
1803                // 刷新失败时记录日志(不中断循环)
1804                if let Err(e) = self.flush().await {
1805                    eprintln!("[WriteBehind] auto flush failed: {}", e);
1806                }
1807            }
1808        })
1809    }
1810}
1811
1812// ============================================================================
1813// 单元测试
1814// ============================================================================
1815
1816#[cfg(test)]
1817mod tests {
1818    use super::*;
1819    use crate::Value;
1820    use std::thread;
1821    use std::time::Duration;
1822
1823    // ===== CacheKey 测试 =====
1824
1825    #[test]
1826    fn test_cache_key_by_pk() {
1827        let key = CacheKey::by_pk("users", 1);
1828        assert_eq!(key.table, "users");
1829        assert_eq!(key.kind, CacheKeyKind::ByPk);
1830        assert_eq!(key.identifier, "1");
1831        assert_eq!(key.to_string_key(), "l2:users:pk:1");
1832    }
1833
1834    #[test]
1835    fn test_cache_key_by_query() {
1836        let key = CacheKey::by_query("orders", "abc123");
1837        assert_eq!(key.kind, CacheKeyKind::ByQuery);
1838        assert_eq!(key.to_string_key(), "l2:orders:q:abc123");
1839    }
1840
1841    #[test]
1842    fn test_cache_key_by_relation() {
1843        let key = CacheKey::by_relation("users", "posts:1");
1844        assert_eq!(key.kind, CacheKeyKind::ByRelation);
1845        assert_eq!(key.to_string_key(), "l2:users:rel:posts:1");
1846    }
1847
1848    #[test]
1849    fn test_cache_key_equality() {
1850        let k1 = CacheKey::by_pk("users", 1);
1851        let k2 = CacheKey::by_pk("users", 1);
1852        let k3 = CacheKey::by_pk("users", 2);
1853        assert_eq!(k1, k2);
1854        assert_ne!(k1, k3);
1855    }
1856
1857    #[test]
1858    fn test_cache_key_display() {
1859        let key = CacheKey::by_pk("users", 42);
1860        assert_eq!(format!("{}", key), "l2:users:pk:42");
1861    }
1862
1863    // ===== L2CacheStats 测试 =====
1864
1865    #[test]
1866    fn test_stats_hit_rate_empty() {
1867        let stats = L2CacheStats::default();
1868        assert_eq!(stats.hit_rate(), 0.0);
1869        assert_eq!(stats.total_lookups(), 0);
1870    }
1871
1872    #[test]
1873    fn test_stats_hit_rate_calculation() {
1874        let stats = L2CacheStats {
1875            hits: 80,
1876            misses: 20,
1877            ..Default::default()
1878        };
1879        assert_eq!(stats.total_lookups(), 100);
1880        assert!((stats.hit_rate() - 0.8).abs() < 0.001);
1881        assert!((stats.miss_rate() - 0.2).abs() < 0.001);
1882    }
1883
1884    #[test]
1885    fn test_stats_merge() {
1886        let mut s1 = L2CacheStats {
1887            hits: 10,
1888            misses: 5,
1889            sets: 15,
1890            evictions: 2,
1891            size: 100,
1892        };
1893        let s2 = L2CacheStats {
1894            hits: 20,
1895            misses: 10,
1896            sets: 30,
1897            evictions: 5,
1898            size: 200,
1899        };
1900        s1.merge(&s2);
1901        assert_eq!(s1.hits, 30);
1902        assert_eq!(s1.misses, 15);
1903        assert_eq!(s1.sets, 45);
1904        assert_eq!(s1.evictions, 7);
1905        assert_eq!(s1.size, 300);
1906    }
1907
1908    // ===== L2Cache 基本操作 =====
1909
1910    #[test]
1911    fn test_put_and_get() {
1912        let cache = L2Cache::new();
1913        let key = CacheKey::by_pk("users", 1);
1914
1915        cache.put(&key, Value::String("Alice".to_string()), None);
1916        let val = cache.get(&key);
1917        assert_eq!(val, Some(Value::String("Alice".to_string())));
1918    }
1919
1920    #[test]
1921    fn test_get_missing_returns_none() {
1922        let cache = L2Cache::new();
1923        let key = CacheKey::by_pk("users", 999);
1924        assert_eq!(cache.get(&key), None);
1925    }
1926
1927    #[test]
1928    fn test_overwrite_existing_key() {
1929        let cache = L2Cache::new();
1930        let key = CacheKey::by_pk("users", 1);
1931
1932        cache.put(&key, Value::String("Alice".to_string()), None);
1933        cache.put(&key, Value::String("Bob".to_string()), None);
1934        assert_eq!(cache.get(&key), Some(Value::String("Bob".to_string())));
1935    }
1936
1937    #[test]
1938    fn test_invalidate_single_key() {
1939        let cache = L2Cache::new();
1940        let key = CacheKey::by_pk("users", 1);
1941
1942        cache.put(&key, Value::I64(42), None);
1943        assert!(cache.get(&key).is_some());
1944
1945        cache.invalidate(&key);
1946        assert!(cache.get(&key).is_none());
1947    }
1948
1949    // ===== 表级失效 =====
1950
1951    #[test]
1952    fn test_invalidate_table_removes_all_entries_for_table() {
1953        let cache = L2Cache::new();
1954
1955        let k1 = CacheKey::by_pk("users", 1);
1956        let k2 = CacheKey::by_pk("users", 2);
1957        let k3 = CacheKey::by_query("users", "hash1");
1958        let k4 = CacheKey::by_pk("orders", 1); // 不同表
1959
1960        cache.put(&k1, Value::I64(1), None);
1961        cache.put(&k2, Value::I64(2), None);
1962        cache.put(&k3, Value::I64(3), None);
1963        cache.put(&k4, Value::I64(4), None);
1964
1965        cache.invalidate_table("users");
1966
1967        // users 表的所有缓存项应被失效
1968        assert!(cache.get(&k1).is_none());
1969        assert!(cache.get(&k2).is_none());
1970        assert!(cache.get(&k3).is_none());
1971        // orders 表的缓存项应保留
1972        assert!(cache.get(&k4).is_some());
1973    }
1974
1975    #[test]
1976    fn test_invalidate_table_no_op_for_unknown_table() {
1977        let cache = L2Cache::new();
1978        let k1 = CacheKey::by_pk("users", 1);
1979        cache.put(&k1, Value::I64(1), None);
1980
1981        cache.invalidate_table("nonexistent");
1982        assert!(cache.get(&k1).is_some());
1983    }
1984
1985    // ===== TTL 测试 =====
1986
1987    #[test]
1988    fn test_ttl_expiration() {
1989        let cache = L2Cache::new();
1990        let key = CacheKey::by_pk("users", 1);
1991
1992        cache.put(&key, Value::I64(42), Some(Duration::from_millis(50)));
1993        assert!(cache.get(&key).is_some());
1994
1995        // 等待 TTL 过期
1996        thread::sleep(Duration::from_millis(100));
1997        assert!(cache.get(&key).is_none());
1998    }
1999
2000    #[test]
2001    fn test_default_ttl_applied_when_no_explicit_ttl() {
2002        let cache = L2Cache::new().with_default_ttl(Duration::from_millis(50));
2003        let key = CacheKey::by_pk("users", 1);
2004
2005        cache.put(&key, Value::I64(42), None); // 不显式传 TTL
2006        assert!(cache.get(&key).is_some());
2007
2008        thread::sleep(Duration::from_millis(100));
2009        assert!(cache.get(&key).is_none());
2010    }
2011
2012    #[test]
2013    fn test_explicit_ttl_overrides_default() {
2014        // 语义验证:ttl=Some(Duration::MAX) 表示永不失效,覆盖默认 TTL
2015        let cache = L2Cache::new().with_default_ttl(Duration::from_millis(50));
2016        let key = CacheKey::by_pk("users", 1);
2017
2018        // 显式传 Some(Duration::MAX) 覆盖默认 TTL(永不失效)
2019        cache.put(&key, Value::I64(42), Some(Duration::MAX));
2020
2021        // 等待默认 TTL 已过期的时间
2022        thread::sleep(Duration::from_millis(100));
2023        // 由于显式传 Some(Duration::MAX),应仍然有效
2024        assert!(cache.get(&key).is_some());
2025    }
2026
2027    #[test]
2028    fn test_none_ttl_uses_default_ttl() {
2029        // 语义验证:ttl=None 时使用 default_ttl
2030        let cache = L2Cache::new().with_default_ttl(Duration::from_millis(50));
2031        let key = CacheKey::by_pk("users", 1);
2032
2033        cache.put(&key, Value::I64(42), None);
2034        assert!(cache.get(&key).is_some());
2035
2036        thread::sleep(Duration::from_millis(100));
2037        // None 使用了 default_ttl,应已过期
2038        assert!(cache.get(&key).is_none());
2039    }
2040
2041    // ===== 命中率统计 =====
2042
2043    #[test]
2044    fn test_stats_tracks_hits_and_misses() {
2045        let cache = L2Cache::new();
2046
2047        let k1 = CacheKey::by_pk("users", 1);
2048        let k2 = CacheKey::by_pk("users", 2);
2049
2050        cache.put(&k1, Value::I64(1), None);
2051
2052        // 1 次命中
2053        cache.get(&k1);
2054        // 2 次未命中
2055        cache.get(&k2);
2056        cache.get(&k2);
2057
2058        let stats = cache.stats();
2059        assert_eq!(stats.hits, 1);
2060        assert_eq!(stats.misses, 2);
2061        assert_eq!(stats.sets, 1);
2062    }
2063
2064    #[test]
2065    fn test_stats_tracks_evictions() {
2066        let cache = L2Cache::new();
2067        let k1 = CacheKey::by_pk("users", 1);
2068        let k2 = CacheKey::by_pk("users", 2);
2069
2070        cache.put(&k1, Value::I64(1), None);
2071        cache.put(&k2, Value::I64(2), None);
2072
2073        cache.invalidate(&k1); // evictions = 1
2074        cache.invalidate_table("users"); // 仅 k2 实际被删除,evictions = 2
2075
2076        let stats = cache.stats();
2077        // invalidate(k1) 删除 1 项;invalidate_table("users") 仅删除 k2(k1 已不存在)
2078        assert_eq!(stats.evictions, 2);
2079    }
2080
2081    #[test]
2082    fn test_stats_reset() {
2083        let cache = L2Cache::new();
2084        let k1 = CacheKey::by_pk("users", 1);
2085
2086        cache.put(&k1, Value::I64(1), None);
2087        cache.get(&k1);
2088        cache.get(&k1);
2089
2090        let stats_before = cache.stats();
2091        assert!(stats_before.hits > 0);
2092
2093        cache.reset_stats();
2094        let stats_after = cache.stats();
2095        assert_eq!(stats_after.hits, 0);
2096        assert_eq!(stats_after.misses, 0);
2097        assert_eq!(stats_after.sets, 0);
2098    }
2099
2100    // ===== 容量管理 =====
2101
2102    #[test]
2103    fn test_max_size_eviction() {
2104        let cache = L2Cache::new().with_max_size(3);
2105
2106        for i in 0..5 {
2107            let k = CacheKey::by_pk("users", i);
2108            cache.put(&k, Value::I64(i), None);
2109        }
2110
2111        // 真正的 LRU:容量严格不超过 max_size
2112        let size = cache.size();
2113        assert_eq!(
2114            size, 3,
2115            "size should be exactly max_size after LRU eviction, got {}",
2116            size
2117        );
2118    }
2119
2120    #[test]
2121    fn test_lru_eviction_order() {
2122        // 验证 LRU 顺序:访问 k0 后,下次淘汰应跳过 k0 而淘汰 k1
2123        let cache = L2Cache::new().with_max_size(3);
2124
2125        let k0 = CacheKey::by_pk("users", 0);
2126        let k1 = CacheKey::by_pk("users", 1);
2127        let k2 = CacheKey::by_pk("users", 2);
2128        let k3 = CacheKey::by_pk("users", 3);
2129
2130        cache.put(&k0, Value::I64(0), None);
2131        cache.put(&k1, Value::I64(1), None);
2132        cache.put(&k2, Value::I64(2), None);
2133
2134        // 访问 k0,使其成为最近使用
2135        let _ = cache.get(&k0);
2136
2137        // 插入 k3,应淘汰 k1(最久未访问)
2138        cache.put(&k3, Value::I64(3), None);
2139
2140        assert!(
2141            cache.get(&k0).is_some(),
2142            "k0 should survive (recently accessed)"
2143        );
2144        assert!(
2145            cache.get(&k1).is_none(),
2146            "k1 should be evicted (LRU victim)"
2147        );
2148        assert!(cache.get(&k2).is_some(), "k2 should survive");
2149        assert!(
2150            cache.get(&k3).is_some(),
2151            "k3 should survive (just inserted)"
2152        );
2153    }
2154
2155    #[test]
2156    fn test_clear_all() {
2157        let cache = L2Cache::new();
2158        cache.put(&CacheKey::by_pk("users", 1), Value::I64(1), None);
2159        cache.put(&CacheKey::by_pk("users", 2), Value::I64(2), None);
2160        cache.put(&CacheKey::by_pk("orders", 1), Value::I64(3), None);
2161
2162        assert_eq!(cache.size(), 3);
2163        cache.clear();
2164        assert_eq!(cache.size(), 0);
2165    }
2166
2167    // ===== contains(不更新统计)=====
2168
2169    #[test]
2170    fn test_contains_does_not_update_stats() {
2171        let cache = L2Cache::new();
2172        let k1 = CacheKey::by_pk("users", 1);
2173        cache.put(&k1, Value::I64(1), None);
2174
2175        let exists = cache.contains(&k1);
2176        assert!(exists);
2177
2178        let stats = cache.stats();
2179        assert_eq!(stats.hits, 0);
2180        assert_eq!(stats.misses, 0);
2181    }
2182
2183    #[test]
2184    fn test_contains_returns_false_for_missing() {
2185        let cache = L2Cache::new();
2186        let k = CacheKey::by_pk("users", 999);
2187        assert!(!cache.contains(&k));
2188    }
2189
2190    #[test]
2191    fn test_contains_returns_false_for_expired() {
2192        let cache = L2Cache::new();
2193        let k = CacheKey::by_pk("users", 1);
2194        cache.put(&k, Value::I64(1), Some(Duration::from_millis(10)));
2195
2196        thread::sleep(Duration::from_millis(50));
2197        assert!(!cache.contains(&k));
2198    }
2199
2200    // ===== evict_expired 手动清理 =====
2201
2202    #[test]
2203    fn test_evict_expired_removes_only_expired_entries() {
2204        let cache = L2Cache::new();
2205
2206        let k1 = CacheKey::by_pk("users", 1);
2207        let k2 = CacheKey::by_pk("users", 2);
2208
2209        cache.put(&k1, Value::I64(1), Some(Duration::from_millis(10)));
2210        cache.put(&k2, Value::I64(2), None); // 永不过期
2211
2212        thread::sleep(Duration::from_millis(50));
2213        let removed = cache.evict_expired();
2214
2215        assert_eq!(removed, 1);
2216        assert!(cache.get(&k1).is_none());
2217        assert!(cache.get(&k2).is_some());
2218    }
2219
2220    #[test]
2221    fn test_evict_expired_returns_zero_if_no_expired() {
2222        let cache = L2Cache::new();
2223        let k1 = CacheKey::by_pk("users", 1);
2224        cache.put(&k1, Value::I64(1), None);
2225
2226        let removed = cache.evict_expired();
2227        assert_eq!(removed, 0);
2228    }
2229
2230    // ===== 多线程测试 =====
2231
2232    #[test]
2233    fn test_concurrent_access() {
2234        let cache = std::sync::Arc::new(L2Cache::new());
2235        let mut handles = Vec::new();
2236
2237        // 多线程写入
2238        for i in 0..4 {
2239            let c = cache.clone();
2240            handles.push(thread::spawn(move || {
2241                for j in 0..10 {
2242                    let k = CacheKey::by_pk("users", i * 10 + j);
2243                    c.put(&k, Value::I64(i * 10 + j), None);
2244                }
2245            }));
2246        }
2247        for h in handles {
2248            h.join().unwrap();
2249        }
2250
2251        assert_eq!(cache.size(), 40);
2252
2253        // 多线程读取
2254        let mut handles = Vec::new();
2255        for i in 0..4 {
2256            let c = cache.clone();
2257            handles.push(thread::spawn(move || {
2258                for j in 0..10 {
2259                    let k = CacheKey::by_pk("users", i * 10 + j);
2260                    let v = c.get(&k);
2261                    assert!(v.is_some());
2262                }
2263            }));
2264        }
2265        for h in handles {
2266            h.join().unwrap();
2267        }
2268
2269        let stats = cache.stats();
2270        assert_eq!(stats.hits, 40);
2271    }
2272
2273    // ===== Default 测试 =====
2274
2275    #[test]
2276    fn test_default() {
2277        let cache = L2Cache::default();
2278        assert_eq!(cache.size(), 0);
2279    }
2280
2281    // ===== 综合场景 =====
2282
2283    #[test]
2284    fn test_realistic_scenario() {
2285        let cache = L2Cache::new();
2286
2287        // 1. 缓存用户表数据
2288        for i in 1..=5 {
2289            cache.put(
2290                &CacheKey::by_pk("users", i),
2291                Value::String(format!("user_{}", i)),
2292                None,
2293            );
2294        }
2295
2296        // 2. 缓存查询结果
2297        cache.put(
2298            &CacheKey::by_query("users", "active_users_hash"),
2299            Value::I64(5),
2300            None,
2301        );
2302
2303        // 3. 读取(部分命中、部分未命中)
2304        for i in 1..=10 {
2305            let _ = cache.get(&CacheKey::by_pk("users", i));
2306        }
2307
2308        let stats = cache.stats();
2309        assert_eq!(stats.hits, 5); // 1-5 命中
2310        assert_eq!(stats.misses, 5); // 6-10 未命中
2311        assert_eq!(stats.sets, 6); // 5 pk + 1 query
2312
2313        // 4. 用户表更新,失效所有缓存
2314        cache.invalidate_table("users");
2315
2316        // 5. 再次读取应全部未命中
2317        cache.reset_stats();
2318        for i in 1..=5 {
2319            let _ = cache.get(&CacheKey::by_pk("users", i));
2320        }
2321        let stats2 = cache.stats();
2322        assert_eq!(stats2.hits, 0);
2323        assert_eq!(stats2.misses, 5);
2324    }
2325
2326    // ===== WriteBehindWriter 测试(Fix #40) =====
2327
2328    #[tokio::test]
2329    async fn test_write_behind_basic_write_and_flush() {
2330        use std::sync::atomic::{AtomicUsize, Ordering};
2331        // 计数刷新调用次数
2332        let counter = Arc::new(AtomicUsize::new(0));
2333        let counter_clone = counter.clone();
2334        let on_flush: FlushCallback = Arc::new(move |ops: Vec<WriteOp>| {
2335            let c = counter_clone.clone();
2336            Box::pin(async move {
2337                c.fetch_add(ops.len(), Ordering::SeqCst);
2338                Ok(())
2339            })
2340        });
2341        let backend = Arc::new(InMemoryBackend::new());
2342        let writer = WriteBehindWriter::new(backend.clone(), on_flush);
2343
2344        // 写入 3 个键
2345        writer.write(b"k1", b"v1", None).await.unwrap();
2346        writer.write(b"k2", b"v2", None).await.unwrap();
2347        writer.write(b"k3", b"v3", None).await.unwrap();
2348
2349        // 缓存应立即可见
2350        let v1 = backend.get("k1").await.unwrap();
2351        assert_eq!(v1, Some(b"v1".to_vec()));
2352
2353        // 队列应有 3 个待刷新
2354        assert_eq!(writer.pending_count().await, 3);
2355
2356        // 刷新
2357        writer.flush().await.unwrap();
2358        assert_eq!(counter.load(Ordering::SeqCst), 3);
2359        assert_eq!(writer.pending_count().await, 0);
2360    }
2361
2362    #[tokio::test]
2363    async fn test_write_behind_delete() {
2364        let on_flush: FlushCallback =
2365            Arc::new(|_ops: Vec<WriteOp>| Box::pin(async move { Ok(()) }));
2366        let backend = Arc::new(InMemoryBackend::new());
2367        let writer = WriteBehindWriter::new(backend.clone(), on_flush);
2368
2369        // 写入后删除
2370        writer.write(b"k1", b"v1", None).await.unwrap();
2371        assert!(backend.get("k1").await.unwrap().is_some());
2372        writer.delete(b"k1").await.unwrap();
2373        // 删除后缓存中应不存在
2374        assert!(backend.get("k1").await.unwrap().is_none());
2375
2376        // flush 应处理 2 个操作(Set + Delete)
2377        writer.flush().await.unwrap();
2378        assert_eq!(writer.pending_count().await, 0);
2379    }
2380
2381    #[tokio::test]
2382    async fn test_write_behind_flush_failure_retries() {
2383        // 模拟刷新总是失败
2384        let on_flush: FlushCallback = Arc::new(|_ops: Vec<WriteOp>| {
2385            Box::pin(async move { Err(CacheError::Internal("backend down".to_string())) })
2386        });
2387        let backend = Arc::new(InMemoryBackend::new());
2388        let writer = WriteBehindWriter::new(backend.clone(), on_flush);
2389
2390        writer.write(b"k1", b"v1", None).await.unwrap();
2391        // flush 失败,操作应保留在队列中
2392        let result = writer.flush().await;
2393        assert!(result.is_err());
2394        assert_eq!(writer.pending_count().await, 1);
2395    }
2396
2397    #[tokio::test]
2398    async fn test_write_behind_empty_flush_noop() {
2399        let on_flush: FlushCallback =
2400            Arc::new(|_ops: Vec<WriteOp>| Box::pin(async move { Ok(()) }));
2401        let backend = Arc::new(InMemoryBackend::new());
2402        let writer = WriteBehindWriter::new(backend, on_flush);
2403        // 空队列 flush 应立即成功
2404        writer.flush().await.unwrap();
2405        assert_eq!(writer.pending_count().await, 0);
2406    }
2407
2408    #[tokio::test]
2409    async fn test_write_behind_error_callback_invoked() {
2410        use std::sync::atomic::{AtomicUsize, Ordering};
2411        let error_counter = Arc::new(AtomicUsize::new(0));
2412        let ec = error_counter.clone();
2413        let on_error: ErrorCallback = Arc::new(move |_ops, _err| {
2414            ec.fetch_add(1, Ordering::SeqCst);
2415        });
2416        let on_flush: FlushCallback = Arc::new(|_ops: Vec<WriteOp>| {
2417            Box::pin(async move { Err(CacheError::Internal("fail".to_string())) })
2418        });
2419        let backend = Arc::new(InMemoryBackend::new());
2420        let writer = WriteBehindWriter::new(backend, on_flush).with_error_callback(on_error);
2421
2422        writer.write(b"k1", b"v1", None).await.unwrap();
2423        let _ = writer.flush().await;
2424        assert_eq!(error_counter.load(Ordering::SeqCst), 1);
2425    }
2426
2427    // ===== 查询缓存测试(TASK-023) =====
2428
2429    #[tokio::test]
2430    async fn test_query_cache_hit() {
2431        use std::collections::HashMap;
2432
2433        let cache = L2Cache::new();
2434        let mut call_count = 0;
2435
2436        // 第一次查询:缓存未命中,执行查询
2437        let rows1 = cache
2438            .get_or_load_query(
2439                "users",
2440                "SELECT * FROM users WHERE status = ?",
2441                &[crate::value::Value::I64(1)],
2442                std::time::Duration::from_secs(300),
2443                || {
2444                    call_count += 1;
2445                    async {
2446                        let mut row = HashMap::new();
2447                        row.insert("id".to_string(), crate::value::Value::I64(1));
2448                        row.insert(
2449                            "name".to_string(),
2450                            crate::value::Value::String("Alice".to_string()),
2451                        );
2452                        Ok(vec![row])
2453                    }
2454                },
2455            )
2456            .await
2457            .unwrap();
2458
2459        assert_eq!(call_count, 1, "第一次查询应调用 loader");
2460        assert_eq!(rows1.len(), 1, "应返回 1 行");
2461
2462        // 第二次查询:缓存命中,不调用 loader
2463        let rows2 = cache
2464            .get_or_load_query(
2465                "users",
2466                "SELECT * FROM users WHERE status = ?",
2467                &[crate::value::Value::I64(1)],
2468                std::time::Duration::from_secs(300),
2469                || {
2470                    call_count += 1;
2471                    async { Ok(vec![]) }
2472                },
2473            )
2474            .await
2475            .unwrap();
2476
2477        assert_eq!(call_count, 1, "第二次查询不应调用 loader(缓存命中)");
2478        assert_eq!(rows2.len(), 1, "应返回缓存的 1 行");
2479    }
2480
2481    #[tokio::test]
2482    async fn test_query_cache_empty_result_cached() {
2483        let cache = L2Cache::new();
2484        let mut call_count = 0;
2485
2486        // 第一次查询:返回空结果
2487        let rows1 = cache
2488            .get_or_load_query(
2489                "users",
2490                "SELECT * FROM users WHERE status = ?",
2491                &[crate::value::Value::I64(999)],
2492                std::time::Duration::from_secs(300),
2493                || {
2494                    call_count += 1;
2495                    async { Ok(vec![]) }
2496                },
2497            )
2498            .await
2499            .unwrap();
2500
2501        assert_eq!(call_count, 1, "第一次查询应调用 loader");
2502        assert_eq!(rows1.len(), 0, "应返回空结果");
2503
2504        // 第二次查询:应命中空结果缓存
2505        let rows2 = cache
2506            .get_or_load_query(
2507                "users",
2508                "SELECT * FROM users WHERE status = ?",
2509                &[crate::value::Value::I64(999)],
2510                std::time::Duration::from_secs(300),
2511                || {
2512                    call_count += 1;
2513                    async { Ok(vec![]) }
2514                },
2515            )
2516            .await
2517            .unwrap();
2518
2519        assert_eq!(call_count, 1, "第二次查询不应调用 loader(空结果缓存命中)");
2520        assert_eq!(rows2.len(), 0, "应返回缓存的空结果");
2521    }
2522
2523    #[tokio::test]
2524    async fn test_query_cache_different_params() {
2525        use std::collections::HashMap;
2526
2527        let cache = L2Cache::new();
2528        let mut call_count = 0;
2529
2530        // 查询 status = 1
2531        let _ = cache
2532            .get_or_load_query(
2533                "users",
2534                "SELECT * FROM users WHERE status = ?",
2535                &[crate::value::Value::I64(1)],
2536                std::time::Duration::from_secs(300),
2537                || {
2538                    call_count += 1;
2539                    async {
2540                        let mut row = HashMap::new();
2541                        row.insert("id".to_string(), crate::value::Value::I64(1));
2542                        Ok(vec![row])
2543                    }
2544                },
2545            )
2546            .await
2547            .unwrap();
2548
2549        // 查询 status = 2(不同参数,应未命中)
2550        let rows2 = cache
2551            .get_or_load_query(
2552                "users",
2553                "SELECT * FROM users WHERE status = ?",
2554                &[crate::value::Value::I64(2)],
2555                std::time::Duration::from_secs(300),
2556                || {
2557                    call_count += 1;
2558                    async {
2559                        let mut row = HashMap::new();
2560                        row.insert("id".to_string(), crate::value::Value::I64(2));
2561                        row.insert(
2562                            "name".to_string(),
2563                            crate::value::Value::String("Bob".to_string()),
2564                        );
2565                        Ok(vec![row])
2566                    }
2567                },
2568            )
2569            .await
2570            .unwrap();
2571
2572        assert_eq!(call_count, 2, "不同参数应调用 loader 两次");
2573        assert_eq!(rows2.len(), 1, "应返回 1 行");
2574    }
2575
2576    #[tokio::test]
2577    async fn test_query_cache_invalidate() {
2578        use std::collections::HashMap;
2579
2580        let cache = L2Cache::new();
2581        let mut call_count = 0;
2582
2583        // 第一次查询
2584        let _ = cache
2585            .get_or_load_query(
2586                "users",
2587                "SELECT * FROM users WHERE status = ?",
2588                &[crate::value::Value::I64(1)],
2589                std::time::Duration::from_secs(300),
2590                || {
2591                    call_count += 1;
2592                    async {
2593                        let mut row = HashMap::new();
2594                        row.insert("id".to_string(), crate::value::Value::I64(1));
2595                        Ok(vec![row])
2596                    }
2597                },
2598            )
2599            .await
2600            .unwrap();
2601
2602        assert_eq!(call_count, 1, "第一次查询应调用 loader");
2603
2604        // 失效查询缓存
2605        cache.invalidate_query(
2606            "users",
2607            "SELECT * FROM users WHERE status = ?",
2608            &[crate::value::Value::I64(1)],
2609        );
2610
2611        // 再次查询:应重新调用 loader
2612        let _ = cache
2613            .get_or_load_query(
2614                "users",
2615                "SELECT * FROM users WHERE status = ?",
2616                &[crate::value::Value::I64(1)],
2617                std::time::Duration::from_secs(300),
2618                || {
2619                    call_count += 1;
2620                    async { Ok(vec![]) }
2621                },
2622            )
2623            .await
2624            .unwrap();
2625
2626        assert_eq!(call_count, 2, "失效后应重新调用 loader");
2627    }
2628
2629    #[tokio::test]
2630    async fn test_query_cache_hit_rate() {
2631        use std::collections::HashMap;
2632
2633        let cache = L2Cache::new();
2634        let mut call_count = 0;
2635
2636        // 模拟 10 次相同查询
2637        for _ in 0..10 {
2638            let _ = cache
2639                .get_or_load_query(
2640                    "users",
2641                    "SELECT * FROM users WHERE status = ?",
2642                    &[crate::value::Value::I64(1)],
2643                    std::time::Duration::from_secs(300),
2644                    || {
2645                        call_count += 1;
2646                        async {
2647                            let mut row = HashMap::new();
2648                            row.insert("id".to_string(), crate::value::Value::I64(1));
2649                            Ok(vec![row])
2650                        }
2651                    },
2652                )
2653                .await
2654                .unwrap();
2655        }
2656
2657        // 只有第一次调用 loader,后续 9 次命中缓存
2658        assert_eq!(call_count, 1, "10 次查询中只有 1 次调用 loader");
2659
2660        let stats = cache.stats();
2661        assert_eq!(stats.hits, 9, "应命中 9 次");
2662        assert_eq!(stats.misses, 1, "应未命中 1 次");
2663
2664        let hit_rate = stats.hit_rate();
2665        assert!(
2666            hit_rate >= 0.8,
2667            "命中率应 >= 80%,实际: {:.2}%",
2668            hit_rate * 100.0
2669        );
2670    }
2671
2672    // ===== RedisBackend 测试(Fix #39) =====
2673
2674    #[cfg(feature = "redis")]
2675    #[tokio::test]
2676    async fn test_redis_backend_invalid_url_returns_error() {
2677        // 无效 URL:redis::Client::open 在连接前解析失败,快速返回错误
2678        let result = RedisBackend::new("not-a-valid-redis-url").await;
2679        let msg = match result {
2680            Ok(_) => panic!("无效 URL 不应连接成功"),
2681            Err(CacheError::Internal(m)) => m,
2682            Err(other) => panic!("期望 CacheError::Internal,实际: {:?}", other),
2683        };
2684        assert!(
2685            msg.contains("Redis client create failed"),
2686            "错误消息应指明 client 创建失败: {}",
2687            msg
2688        );
2689    }
2690}