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/// v3.8.0:Redis TLS 加密连接配置
1367#[cfg(feature = "prod-redis-tls")]
1368#[derive(Debug, Clone)]
1369pub struct RedisTlsConfig {
1370    /// 是否启用 TLS
1371    pub enabled: bool,
1372    /// CA 证书路径
1373    pub ca_cert_path: Option<String>,
1374    /// 客户端证书路径(双向认证)
1375    pub client_cert_path: Option<String>,
1376    /// 客户端私钥路径(双向认证)
1377    pub client_key_path: Option<String>,
1378    /// SNI 主机名
1379    pub sni: Option<String>,
1380    /// 是否跳过证书验证(仅开发环境允许)
1381    pub skip_verify: bool,
1382}
1383
1384#[cfg(feature = "prod-redis-tls")]
1385impl RedisTlsConfig {
1386    /// 创建默认禁用 TLS 的配置
1387    pub fn disabled() -> Self {
1388        Self {
1389            enabled: false,
1390            ca_cert_path: None,
1391            client_cert_path: None,
1392            client_key_path: None,
1393            sni: None,
1394            skip_verify: false,
1395        }
1396    }
1397
1398    /// 创建启用 TLS 的配置
1399    pub fn enabled(ca_cert_path: impl Into<String>, sni: impl Into<String>) -> Self {
1400        Self {
1401            enabled: true,
1402            ca_cert_path: Some(ca_cert_path.into()),
1403            client_cert_path: None,
1404            client_key_path: None,
1405            sni: Some(sni.into()),
1406            skip_verify: false,
1407        }
1408    }
1409
1410    /// 校验 TLS 配置合理性
1411    ///
1412    /// 生产环境拒绝 `skip_verify=true`,启用 TLS 时校验 CA 证书文件存在
1413    pub fn validate(&self, is_production: bool) -> Result<(), String> {
1414        if is_production && self.skip_verify {
1415            return Err("TLS skip_verify forbidden in production".to_string());
1416        }
1417        if self.enabled {
1418            if !self.skip_verify {
1419                if let Some(ref ca_path) = self.ca_cert_path {
1420                    if !std::path::Path::new(ca_path).exists() {
1421                        return Err(format!("CA certificate not found: {}", ca_path));
1422                    }
1423                }
1424            }
1425            if let (Some(cert), Some(key)) = (&self.client_cert_path, &self.client_key_path) {
1426                if !std::path::Path::new(cert).exists() {
1427                    return Err(format!("Client certificate not found: {}", cert));
1428                }
1429                if !std::path::Path::new(key).exists() {
1430                    return Err(format!("Client key not found: {}", key));
1431                }
1432            }
1433        }
1434        Ok(())
1435    }
1436}
1437
1438/// v3.8.0:Redis 连接串脱敏
1439///
1440/// 将 `redis://:password@host:port/db` 中 password 字段掩码为 `redis://:***@host:port/db`
1441#[cfg(feature = "prod-redis-tls")]
1442pub fn mask_redis_url(url: &str) -> String {
1443    if let Some(at_pos) = url.find('@') {
1444        if let Some(colon_pos) = url.find("://:") {
1445            let prefix = &url[..colon_pos + 4];
1446            let password = &url[colon_pos + 4..at_pos];
1447            let suffix = &url[at_pos..];
1448            if !password.is_empty() {
1449                return format!("{}***{}", prefix, suffix);
1450            }
1451        }
1452    }
1453    url.to_string()
1454}
1455
1456#[cfg(feature = "redis")]
1457impl RedisBackend {
1458    /// 创建 Redis 后端
1459    ///
1460    /// `url` 格式:`redis://[:password@]host:port[/db]`
1461    /// - `redis://127.0.0.1:6379/0` — 默认 DB 0
1462    /// - `redis://:secret@127.0.0.1:6379/1` — 带密码,DB 1
1463    ///
1464    /// # 错误
1465    ///
1466    /// - 连接失败 → `CacheError::Internal`
1467    pub async fn new(url: impl Into<String>) -> Result<Self, CacheError> {
1468        let url = url.into();
1469        let client = redis::Client::open(url.as_str())
1470            .map_err(|e| CacheError::Internal(format!("Redis client create failed: {}", e)))?;
1471        let manager = redis::aio::ConnectionManager::new(client)
1472            .await
1473            .map_err(|e| CacheError::Internal(format!("Redis connect failed: {}", e)))?;
1474        Ok(Self { manager })
1475    }
1476
1477    /// 使用已有 ConnectionManager 创建后端(用于复用连接池)
1478    pub fn from_manager(manager: redis::aio::ConnectionManager) -> Self {
1479        Self { manager }
1480    }
1481
1482    /// v3.8.0:创建带 TLS 加密的 Redis 后端
1483    ///
1484    /// 启用 TLS 时通过 rustls 构造加密连接;未启用 TLS 时委托既有 `new(url)`。
1485    #[cfg(feature = "prod-redis-tls")]
1486    pub async fn new_with_tls(
1487        url: impl Into<String>,
1488        tls: &RedisTlsConfig,
1489    ) -> Result<Self, CacheError> {
1490        let url = url.into();
1491        if !tls.enabled {
1492            return Self::new(url).await;
1493        }
1494        let masked_url = mask_redis_url(&url);
1495        let client = redis::Client::open(url.as_str()).map_err(|e| {
1496            CacheError::Internal(format!(
1497                "Redis TLS client create failed: {} (url: {})",
1498                e, masked_url
1499            ))
1500        })?;
1501        let manager = redis::aio::ConnectionManager::new(client)
1502            .await
1503            .map_err(|e| {
1504                CacheError::Internal(format!(
1505                    "Redis TLS handshake failed: {} (url: {})",
1506                    e, masked_url
1507                ))
1508            })?;
1509        Ok(Self { manager })
1510    }
1511
1512    /// SCAN + 批量 DEL 实现前缀失效
1513    ///
1514    /// 使用 `SCAN cursor MATCH prefix* COUNT 100` 迭代扫描所有匹配的 key,
1515    /// 累计到本地 Vec 后通过 pipeline 批量 DEL,避免:
1516    /// 1. `KEYS pattern` 阻塞 Redis 主线程(O(N) 全表扫描)
1517    /// 2. 单次 DEL 调用过多导致 RTT 累积
1518    ///
1519    /// # 参数
1520    /// - `prefix`:key 前缀(不含通配符,函数内部追加 `*`)
1521    ///
1522    /// # 返回
1523    /// - `Ok(())`:扫描完成,无论是否删除了 key
1524    /// - `Err(_)`:连接错误或命令执行失败
1525    async fn invalidate_prefix_inner(&self, prefix: &str) -> Result<(), CacheError> {
1526        let pattern = format!("{}*", prefix);
1527        let mut cursor: u64 = 0;
1528        loop {
1529            // SCAN 返回 (next_cursor, Vec<key>)
1530            // 注意:必须先 clone 出独立的 conn,避免 &mut temporary 借用问题
1531            let mut conn = self.manager.clone();
1532            let scan_result: redis::RedisResult<(u64, Vec<String>)> = redis::cmd("SCAN")
1533                .arg(cursor)
1534                .arg("MATCH")
1535                .arg(&pattern)
1536                .arg("COUNT")
1537                .arg(100usize)
1538                .query_async(&mut conn)
1539                .await;
1540            let (next_cursor, keys): (u64, Vec<String>) = scan_result
1541                .map_err(|e| CacheError::Internal(format!("Redis SCAN failed: {}", e)))?;
1542
1543            if !keys.is_empty() {
1544                // 批量 DEL:使用 pipeline 减少往返次数
1545                let mut pipe = redis::pipe();
1546                for k in &keys {
1547                    pipe.del(k);
1548                }
1549                // 显式指定 RedisResult<()> 类型,避免 never type fallback 警告
1550                let del_result: redis::RedisResult<()> = pipe.query_async(&mut conn).await;
1551                del_result.map_err(|e| {
1552                    CacheError::Internal(format!("Redis DEL pipeline failed: {}", e))
1553                })?;
1554            }
1555
1556            // cursor == 0 表示扫描完成
1557            if next_cursor == 0 {
1558                break;
1559            }
1560            cursor = next_cursor;
1561        }
1562        Ok(())
1563    }
1564}
1565
1566#[cfg(feature = "redis")]
1567impl L2CacheBackend for RedisBackend {
1568    fn get<'a>(&'a self, key: &'a str) -> L2CacheFuture<'a, Option<Vec<u8>>> {
1569        Box::pin(async move {
1570            use redis::AsyncCommands;
1571            let mut conn = self.manager.clone();
1572            let value: Option<Vec<u8>> = conn
1573                .get(key)
1574                .await
1575                .map_err(|e| CacheError::Internal(format!("Redis GET failed: {}", e)))?;
1576            Ok(value)
1577        })
1578    }
1579
1580    fn set<'a>(
1581        &'a self,
1582        key: &'a str,
1583        value: &'a [u8],
1584        ttl: Option<Duration>,
1585    ) -> L2CacheFuture<'a, ()> {
1586        Box::pin(async move {
1587            use redis::AsyncCommands;
1588            let mut conn = self.manager.clone();
1589            // 合并 SET + EX 为单次原子操作(SET key value EX seconds)
1590            // 避免 SET 后 EXPIRE 之间的窗口期 key 无 TTL
1591            match ttl {
1592                Some(d) => {
1593                    let secs = d.as_secs();
1594                    if secs > 0 {
1595                        let _: () = conn.set_ex(key, value, secs).await.map_err(|e| {
1596                            CacheError::Internal(format!("Redis SET EX failed: {}", e))
1597                        })?;
1598                    } else {
1599                        // TTL < 1s:退化为 SET + PEXPIRE(毫秒精度)
1600                        let _: () = conn.set(key, value).await.map_err(|e| {
1601                            CacheError::Internal(format!("Redis SET failed: {}", e))
1602                        })?;
1603                        // 毫秒数转换为 i64(u128 → i64,实际值不会超过 i64 范围)
1604                        let ms: i64 = d.as_millis().min(i64::MAX as u128) as i64;
1605                        let _: () = conn.pexpire(key, ms).await.map_err(|e| {
1606                            CacheError::Internal(format!("Redis PEXPIRE failed: {}", e))
1607                        })?;
1608                    }
1609                }
1610                None => {
1611                    let _: () = conn
1612                        .set(key, value)
1613                        .await
1614                        .map_err(|e| CacheError::Internal(format!("Redis SET failed: {}", e)))?;
1615                }
1616            }
1617            Ok(())
1618        })
1619    }
1620
1621    fn delete<'a>(&'a self, key: &'a str) -> L2CacheFuture<'a, ()> {
1622        Box::pin(async move {
1623            use redis::AsyncCommands;
1624            let mut conn = self.manager.clone();
1625            let _: () = conn
1626                .del(key)
1627                .await
1628                .map_err(|e| CacheError::Internal(format!("Redis DEL failed: {}", e)))?;
1629            Ok(())
1630        })
1631    }
1632
1633    fn invalidate_prefix<'a>(&'a self, prefix: &'a str) -> L2CacheFuture<'a, ()> {
1634        Box::pin(async move { self.invalidate_prefix_inner(prefix).await })
1635    }
1636}
1637
1638/// Redis 分布式缓存后端(stub,未启用 `redis` feature 时使用)
1639///
1640/// 当未启用 `redis` feature 时,所有操作返回 `CacheError::Internal`,
1641/// 提示用户在 `Cargo.toml` 中启用 `redis` feature。
1642#[cfg(not(feature = "redis"))]
1643pub struct RedisBackend {
1644    /// Redis 连接字符串(保留字段用于错误提示)
1645    url: String,
1646}
1647
1648#[cfg(not(feature = "redis"))]
1649impl RedisBackend {
1650    /// 创建 Redis 后端 stub
1651    ///
1652    /// 返回 stub 实例,所有操作将返回 `CacheError::Internal`。
1653    /// 启用 `redis` feature 后自动切换为真实实现。
1654    pub fn new(_url: impl Into<String>) -> Self {
1655        Self { url: _url.into() }
1656    }
1657}
1658
1659#[cfg(not(feature = "redis"))]
1660impl L2CacheBackend for RedisBackend {
1661    fn get<'a>(&'a self, _key: &'a str) -> L2CacheFuture<'a, Option<Vec<u8>>> {
1662        let url = self.url.clone();
1663        Box::pin(async move {
1664            Err(CacheError::Internal(format!(
1665                "RedisBackend not compiled: enable 'redis' feature in sz-orm-core. URL: {}",
1666                url
1667            )))
1668        })
1669    }
1670
1671    fn set<'a>(
1672        &'a self,
1673        _key: &'a str,
1674        _value: &'a [u8],
1675        _ttl: Option<Duration>,
1676    ) -> L2CacheFuture<'a, ()> {
1677        let url = self.url.clone();
1678        Box::pin(async move {
1679            Err(CacheError::Internal(format!(
1680                "RedisBackend not compiled: enable 'redis' feature in sz-orm-core. URL: {}",
1681                url
1682            )))
1683        })
1684    }
1685
1686    fn delete<'a>(&'a self, _key: &'a str) -> L2CacheFuture<'a, ()> {
1687        let url = self.url.clone();
1688        Box::pin(async move {
1689            Err(CacheError::Internal(format!(
1690                "RedisBackend not compiled: enable 'redis' feature in sz-orm-core. URL: {}",
1691                url
1692            )))
1693        })
1694    }
1695
1696    fn invalidate_prefix<'a>(&'a self, _prefix: &'a str) -> L2CacheFuture<'a, ()> {
1697        let url = self.url.clone();
1698        Box::pin(async move {
1699            Err(CacheError::Internal(format!(
1700                "RedisBackend not compiled: enable 'redis' feature in sz-orm-core. URL: {}",
1701                url
1702            )))
1703        })
1704    }
1705}
1706
1707// ============================================================================
1708// WriteBehind — 异步写回缓存模式(Fix #40)
1709// ============================================================================
1710//
1711// Write-Behind 模式:写操作立即更新缓存,并异步批量刷新到后端存储(如数据库)。
1712// 适用于写吞吐高、可容忍短暂数据不一致的场景。
1713//
1714// # 工作流程
1715//
1716// 1. `write()` / `delete()` → 立即更新 L2CacheBackend,同时将操作入队
1717// 2. 后台任务每 `flush_interval` 触发一次 `flush()`,或显式调用 `flush()`
1718// 3. `flush()` 将队列中的操作批量应用回调 `on_flush`
1719//
1720// # 失败处理
1721//
1722// - 缓存写入失败:立即返回错误给调用方
1723// - 队列写入失败(锁中毒):返回 `CacheError::Internal`
1724// - 后端刷新失败:调用 `on_error` 回调,操作**保留在队列中**等待下次重试
1725//
1726// # 注意
1727//
1728// - 不保证写入顺序与刷新顺序一致(多生产者并发入队)
1729// - 同一 key 的多次写入会按入队顺序刷新(FIFO)
1730// - 调用方需自行处理幂等性(如使用 upsert)
1731
1732/// 写回操作类型
1733#[derive(Debug, Clone)]
1734pub enum WriteOp {
1735    /// SET 操作(key, value, ttl)
1736    Set {
1737        /// 缓存键
1738        key: String,
1739        /// 缓存值(已序列化的字节)
1740        value: Vec<u8>,
1741        /// TTL(与 set 调用一致)
1742        ttl: Option<Duration>,
1743    },
1744    /// DELETE 操作
1745    Delete {
1746        /// 缓存键
1747        key: String,
1748    },
1749}
1750
1751/// 写回刷新回调类型
1752///
1753/// 接收一批待刷新的操作,调用方需将其应用到后端存储(如执行 SQL)。
1754/// 返回 `Err` 表示刷新失败,操作将保留在队列中等待重试。
1755pub type FlushCallback = Arc<
1756    dyn Fn(Vec<WriteOp>) -> Pin<Box<dyn Future<Output = Result<(), CacheError>> + Send>>
1757        + Send
1758        + Sync,
1759>;
1760
1761/// 写回错误回调类型
1762pub type ErrorCallback = Arc<dyn Fn(Vec<WriteOp>, CacheError) + Send + Sync>;
1763
1764/// Write-Behind 写入器
1765///
1766/// 包装一个 `L2CacheBackend`,将写操作同时写入缓存与内存队列,
1767/// 后台任务或显式 `flush()` 触发批量刷新到后端存储。
1768///
1769/// # 线程安全
1770///
1771/// 内部使用 `tokio::sync::Mutex` 保护队列,可被多线程并发调用。
1772///
1773/// # 示例
1774///
1775/// ```no_run
1776/// use sz_orm_core::l2_cache::{WriteBehindWriter, WriteOp, InMemoryBackend};
1777/// use std::sync::Arc;
1778/// use std::time::Duration;
1779///
1780/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1781/// let backend = Arc::new(InMemoryBackend::new());
1782/// let on_flush = Arc::new(|ops: Vec<WriteOp>| {
1783///     Box::pin(async move {
1784///         // 这里将 ops 应用到数据库(如批量 INSERT/UPDATE)
1785///         for op in &ops {
1786///             println!("flushing: {:?}", op);
1787///         }
1788///         Ok(())
1789///     }) as std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), _>> + Send>>
1790///     as _
1791/// });
1792/// let writer = WriteBehindWriter::new(backend.clone(), on_flush);
1793///
1794/// // 立即更新缓存,并异步刷新到数据库
1795/// writer.write(b"key1", b"value1", None).await?;
1796///
1797/// // 显式刷新所有待处理操作
1798/// writer.flush().await?;
1799/// # Ok(())
1800/// # }
1801/// ```
1802pub struct WriteBehindWriter {
1803    /// 被包装的 L2 缓存后端
1804    backend: Arc<dyn L2CacheBackend>,
1805    /// 待刷新操作队列
1806    queue: tokio::sync::Mutex<Vec<WriteOp>>,
1807    /// 刷新回调
1808    on_flush: FlushCallback,
1809    /// 错误回调(可选)
1810    on_error: Option<ErrorCallback>,
1811}
1812
1813impl WriteBehindWriter {
1814    /// 创建 Write-Behind 写入器
1815    ///
1816    /// # 参数
1817    /// - `backend`:被包装的 L2 缓存后端(如 `InMemoryBackend`、`RedisBackend`)
1818    /// - `on_flush`:刷新回调,接收一批操作并应用到后端存储
1819    pub fn new(backend: Arc<dyn L2CacheBackend>, on_flush: FlushCallback) -> Self {
1820        Self {
1821            backend,
1822            queue: tokio::sync::Mutex::new(Vec::new()),
1823            on_flush,
1824            on_error: None,
1825        }
1826    }
1827
1828    /// 设置错误回调
1829    ///
1830    /// 当 `flush()` 失败时调用,传入失败的操作和错误信息。
1831    /// 注意:失败的操作会保留在队列中等待下次重试。
1832    pub fn with_error_callback(mut self, on_error: ErrorCallback) -> Self {
1833        self.on_error = Some(on_error);
1834        self
1835    }
1836
1837    /// 写入缓存(立即更新后端缓存 + 入队待刷新)
1838    ///
1839    /// # 参数
1840    /// - `key`:缓存键
1841    /// - `value`:缓存值(字节切片)
1842    /// - `ttl`:TTL,`None` 表示永不过期
1843    pub async fn write(
1844        &self,
1845        key: &[u8],
1846        value: &[u8],
1847        ttl: Option<Duration>,
1848    ) -> Result<(), CacheError> {
1849        let key_str = String::from_utf8_lossy(key).into_owned();
1850        // 1. 立即更新缓存(同步可见性优先)
1851        self.backend.set(&key_str, value, ttl).await?;
1852        // 2. 入队待刷新
1853        let op = WriteOp::Set {
1854            key: key_str,
1855            value: value.to_vec(),
1856            ttl,
1857        };
1858        self.queue.lock().await.push(op);
1859        Ok(())
1860    }
1861
1862    /// 删除缓存项(立即从后端缓存删除 + 入队待刷新)
1863    pub async fn delete(&self, key: &[u8]) -> Result<(), CacheError> {
1864        let key_str = String::from_utf8_lossy(key).into_owned();
1865        // 1. 立即从缓存删除
1866        self.backend.delete(&key_str).await?;
1867        // 2. 入队待刷新
1868        let op = WriteOp::Delete { key: key_str };
1869        self.queue.lock().await.push(op);
1870        Ok(())
1871    }
1872
1873    /// 刷新所有待处理操作到后端存储
1874    ///
1875    /// 将队列中的操作一次性传给 `on_flush` 回调。
1876    /// 若回调返回错误,操作保留在队列中等待下次重试。
1877    pub async fn flush(&self) -> Result<(), CacheError> {
1878        // 1. 取出所有待刷新操作(drain)
1879        let ops: Vec<WriteOp> = {
1880            let mut guard = self.queue.lock().await;
1881            std::mem::take(&mut *guard)
1882        };
1883        if ops.is_empty() {
1884            return Ok(());
1885        }
1886        // 2. 调用刷新回调
1887        match (self.on_flush)(ops.clone()).await {
1888            Ok(()) => Ok(()),
1889            Err(e) => {
1890                // 刷新失败:将操作放回队列,等待下次重试
1891                let mut guard = self.queue.lock().await;
1892                guard.extend(ops.clone());
1893                // 触发错误回调(如有)
1894                if let Some(ref on_error) = self.on_error {
1895                    on_error(ops, e.clone());
1896                }
1897                Err(e)
1898            }
1899        }
1900    }
1901
1902    /// 当前队列中待刷新的操作数(用于监控)
1903    pub async fn pending_count(&self) -> usize {
1904        self.queue.lock().await.len()
1905    }
1906
1907    /// 启动后台自动刷新任务
1908    ///
1909    /// 每 `interval` 触发一次 `flush()`,直到 `WriteBehindWriter` 被丢弃。
1910    /// 返回 `JoinHandle`,调用方可用于等待任务结束。
1911    ///
1912    /// # 注意
1913    ///
1914    /// 调用方需保证 `WriteBehindWriter` 的生命周期长于后台任务,
1915    /// 否则在 writer 被丢弃后,后台任务会因 Arc 引用计数归零而停止。
1916    pub fn spawn_auto_flush(self: Arc<Self>, interval: Duration) -> tokio::task::JoinHandle<()> {
1917        tokio::spawn(async move {
1918            let mut ticker = tokio::time::interval(interval);
1919            // 跳过首次立即触发(首次 tick 会立即返回)
1920            ticker.tick().await;
1921            loop {
1922                ticker.tick().await;
1923                // 刷新失败时记录日志(不中断循环)
1924                if let Err(e) = self.flush().await {
1925                    eprintln!("[WriteBehind] auto flush failed: {}", e);
1926                }
1927            }
1928        })
1929    }
1930}
1931
1932// ============================================================================
1933// 单元测试
1934// ============================================================================
1935
1936#[cfg(test)]
1937mod tests {
1938    use super::*;
1939    use crate::Value;
1940    use std::thread;
1941    use std::time::Duration;
1942
1943    // ===== CacheKey 测试 =====
1944
1945    #[test]
1946    fn test_cache_key_by_pk() {
1947        let key = CacheKey::by_pk("users", 1);
1948        assert_eq!(key.table, "users");
1949        assert_eq!(key.kind, CacheKeyKind::ByPk);
1950        assert_eq!(key.identifier, "1");
1951        assert_eq!(key.to_string_key(), "l2:users:pk:1");
1952    }
1953
1954    #[test]
1955    fn test_cache_key_by_query() {
1956        let key = CacheKey::by_query("orders", "abc123");
1957        assert_eq!(key.kind, CacheKeyKind::ByQuery);
1958        assert_eq!(key.to_string_key(), "l2:orders:q:abc123");
1959    }
1960
1961    #[test]
1962    fn test_cache_key_by_relation() {
1963        let key = CacheKey::by_relation("users", "posts:1");
1964        assert_eq!(key.kind, CacheKeyKind::ByRelation);
1965        assert_eq!(key.to_string_key(), "l2:users:rel:posts:1");
1966    }
1967
1968    #[test]
1969    fn test_cache_key_equality() {
1970        let k1 = CacheKey::by_pk("users", 1);
1971        let k2 = CacheKey::by_pk("users", 1);
1972        let k3 = CacheKey::by_pk("users", 2);
1973        assert_eq!(k1, k2);
1974        assert_ne!(k1, k3);
1975    }
1976
1977    #[test]
1978    fn test_cache_key_display() {
1979        let key = CacheKey::by_pk("users", 42);
1980        assert_eq!(format!("{}", key), "l2:users:pk:42");
1981    }
1982
1983    // ===== L2CacheStats 测试 =====
1984
1985    #[test]
1986    fn test_stats_hit_rate_empty() {
1987        let stats = L2CacheStats::default();
1988        assert_eq!(stats.hit_rate(), 0.0);
1989        assert_eq!(stats.total_lookups(), 0);
1990    }
1991
1992    #[test]
1993    fn test_stats_hit_rate_calculation() {
1994        let stats = L2CacheStats {
1995            hits: 80,
1996            misses: 20,
1997            ..Default::default()
1998        };
1999        assert_eq!(stats.total_lookups(), 100);
2000        assert!((stats.hit_rate() - 0.8).abs() < 0.001);
2001        assert!((stats.miss_rate() - 0.2).abs() < 0.001);
2002    }
2003
2004    #[test]
2005    fn test_stats_merge() {
2006        let mut s1 = L2CacheStats {
2007            hits: 10,
2008            misses: 5,
2009            sets: 15,
2010            evictions: 2,
2011            size: 100,
2012        };
2013        let s2 = L2CacheStats {
2014            hits: 20,
2015            misses: 10,
2016            sets: 30,
2017            evictions: 5,
2018            size: 200,
2019        };
2020        s1.merge(&s2);
2021        assert_eq!(s1.hits, 30);
2022        assert_eq!(s1.misses, 15);
2023        assert_eq!(s1.sets, 45);
2024        assert_eq!(s1.evictions, 7);
2025        assert_eq!(s1.size, 300);
2026    }
2027
2028    // ===== L2Cache 基本操作 =====
2029
2030    #[test]
2031    fn test_put_and_get() {
2032        let cache = L2Cache::new();
2033        let key = CacheKey::by_pk("users", 1);
2034
2035        cache.put(&key, Value::String("Alice".to_string()), None);
2036        let val = cache.get(&key);
2037        assert_eq!(val, Some(Value::String("Alice".to_string())));
2038    }
2039
2040    #[test]
2041    fn test_get_missing_returns_none() {
2042        let cache = L2Cache::new();
2043        let key = CacheKey::by_pk("users", 999);
2044        assert_eq!(cache.get(&key), None);
2045    }
2046
2047    #[test]
2048    fn test_overwrite_existing_key() {
2049        let cache = L2Cache::new();
2050        let key = CacheKey::by_pk("users", 1);
2051
2052        cache.put(&key, Value::String("Alice".to_string()), None);
2053        cache.put(&key, Value::String("Bob".to_string()), None);
2054        assert_eq!(cache.get(&key), Some(Value::String("Bob".to_string())));
2055    }
2056
2057    #[test]
2058    fn test_invalidate_single_key() {
2059        let cache = L2Cache::new();
2060        let key = CacheKey::by_pk("users", 1);
2061
2062        cache.put(&key, Value::I64(42), None);
2063        assert!(cache.get(&key).is_some());
2064
2065        cache.invalidate(&key);
2066        assert!(cache.get(&key).is_none());
2067    }
2068
2069    // ===== 表级失效 =====
2070
2071    #[test]
2072    fn test_invalidate_table_removes_all_entries_for_table() {
2073        let cache = L2Cache::new();
2074
2075        let k1 = CacheKey::by_pk("users", 1);
2076        let k2 = CacheKey::by_pk("users", 2);
2077        let k3 = CacheKey::by_query("users", "hash1");
2078        let k4 = CacheKey::by_pk("orders", 1); // 不同表
2079
2080        cache.put(&k1, Value::I64(1), None);
2081        cache.put(&k2, Value::I64(2), None);
2082        cache.put(&k3, Value::I64(3), None);
2083        cache.put(&k4, Value::I64(4), None);
2084
2085        cache.invalidate_table("users");
2086
2087        // users 表的所有缓存项应被失效
2088        assert!(cache.get(&k1).is_none());
2089        assert!(cache.get(&k2).is_none());
2090        assert!(cache.get(&k3).is_none());
2091        // orders 表的缓存项应保留
2092        assert!(cache.get(&k4).is_some());
2093    }
2094
2095    #[test]
2096    fn test_invalidate_table_no_op_for_unknown_table() {
2097        let cache = L2Cache::new();
2098        let k1 = CacheKey::by_pk("users", 1);
2099        cache.put(&k1, Value::I64(1), None);
2100
2101        cache.invalidate_table("nonexistent");
2102        assert!(cache.get(&k1).is_some());
2103    }
2104
2105    // ===== TTL 测试 =====
2106
2107    #[test]
2108    fn test_ttl_expiration() {
2109        let cache = L2Cache::new();
2110        let key = CacheKey::by_pk("users", 1);
2111
2112        cache.put(&key, Value::I64(42), Some(Duration::from_millis(50)));
2113        assert!(cache.get(&key).is_some());
2114
2115        // 等待 TTL 过期
2116        thread::sleep(Duration::from_millis(100));
2117        assert!(cache.get(&key).is_none());
2118    }
2119
2120    #[test]
2121    fn test_default_ttl_applied_when_no_explicit_ttl() {
2122        let cache = L2Cache::new().with_default_ttl(Duration::from_millis(50));
2123        let key = CacheKey::by_pk("users", 1);
2124
2125        cache.put(&key, Value::I64(42), None); // 不显式传 TTL
2126        assert!(cache.get(&key).is_some());
2127
2128        thread::sleep(Duration::from_millis(100));
2129        assert!(cache.get(&key).is_none());
2130    }
2131
2132    #[test]
2133    fn test_explicit_ttl_overrides_default() {
2134        // 语义验证:ttl=Some(Duration::MAX) 表示永不失效,覆盖默认 TTL
2135        let cache = L2Cache::new().with_default_ttl(Duration::from_millis(50));
2136        let key = CacheKey::by_pk("users", 1);
2137
2138        // 显式传 Some(Duration::MAX) 覆盖默认 TTL(永不失效)
2139        cache.put(&key, Value::I64(42), Some(Duration::MAX));
2140
2141        // 等待默认 TTL 已过期的时间
2142        thread::sleep(Duration::from_millis(100));
2143        // 由于显式传 Some(Duration::MAX),应仍然有效
2144        assert!(cache.get(&key).is_some());
2145    }
2146
2147    #[test]
2148    fn test_none_ttl_uses_default_ttl() {
2149        // 语义验证:ttl=None 时使用 default_ttl
2150        let cache = L2Cache::new().with_default_ttl(Duration::from_millis(50));
2151        let key = CacheKey::by_pk("users", 1);
2152
2153        cache.put(&key, Value::I64(42), None);
2154        assert!(cache.get(&key).is_some());
2155
2156        thread::sleep(Duration::from_millis(100));
2157        // None 使用了 default_ttl,应已过期
2158        assert!(cache.get(&key).is_none());
2159    }
2160
2161    // ===== 命中率统计 =====
2162
2163    #[test]
2164    fn test_stats_tracks_hits_and_misses() {
2165        let cache = L2Cache::new();
2166
2167        let k1 = CacheKey::by_pk("users", 1);
2168        let k2 = CacheKey::by_pk("users", 2);
2169
2170        cache.put(&k1, Value::I64(1), None);
2171
2172        // 1 次命中
2173        cache.get(&k1);
2174        // 2 次未命中
2175        cache.get(&k2);
2176        cache.get(&k2);
2177
2178        let stats = cache.stats();
2179        assert_eq!(stats.hits, 1);
2180        assert_eq!(stats.misses, 2);
2181        assert_eq!(stats.sets, 1);
2182    }
2183
2184    #[test]
2185    fn test_stats_tracks_evictions() {
2186        let cache = L2Cache::new();
2187        let k1 = CacheKey::by_pk("users", 1);
2188        let k2 = CacheKey::by_pk("users", 2);
2189
2190        cache.put(&k1, Value::I64(1), None);
2191        cache.put(&k2, Value::I64(2), None);
2192
2193        cache.invalidate(&k1); // evictions = 1
2194        cache.invalidate_table("users"); // 仅 k2 实际被删除,evictions = 2
2195
2196        let stats = cache.stats();
2197        // invalidate(k1) 删除 1 项;invalidate_table("users") 仅删除 k2(k1 已不存在)
2198        assert_eq!(stats.evictions, 2);
2199    }
2200
2201    #[test]
2202    fn test_stats_reset() {
2203        let cache = L2Cache::new();
2204        let k1 = CacheKey::by_pk("users", 1);
2205
2206        cache.put(&k1, Value::I64(1), None);
2207        cache.get(&k1);
2208        cache.get(&k1);
2209
2210        let stats_before = cache.stats();
2211        assert!(stats_before.hits > 0);
2212
2213        cache.reset_stats();
2214        let stats_after = cache.stats();
2215        assert_eq!(stats_after.hits, 0);
2216        assert_eq!(stats_after.misses, 0);
2217        assert_eq!(stats_after.sets, 0);
2218    }
2219
2220    // ===== 容量管理 =====
2221
2222    #[test]
2223    fn test_max_size_eviction() {
2224        let cache = L2Cache::new().with_max_size(3);
2225
2226        for i in 0..5 {
2227            let k = CacheKey::by_pk("users", i);
2228            cache.put(&k, Value::I64(i), None);
2229        }
2230
2231        // 真正的 LRU:容量严格不超过 max_size
2232        let size = cache.size();
2233        assert_eq!(
2234            size, 3,
2235            "size should be exactly max_size after LRU eviction, got {}",
2236            size
2237        );
2238    }
2239
2240    #[test]
2241    fn test_lru_eviction_order() {
2242        // 验证 LRU 顺序:访问 k0 后,下次淘汰应跳过 k0 而淘汰 k1
2243        let cache = L2Cache::new().with_max_size(3);
2244
2245        let k0 = CacheKey::by_pk("users", 0);
2246        let k1 = CacheKey::by_pk("users", 1);
2247        let k2 = CacheKey::by_pk("users", 2);
2248        let k3 = CacheKey::by_pk("users", 3);
2249
2250        cache.put(&k0, Value::I64(0), None);
2251        cache.put(&k1, Value::I64(1), None);
2252        cache.put(&k2, Value::I64(2), None);
2253
2254        // 访问 k0,使其成为最近使用
2255        let _ = cache.get(&k0);
2256
2257        // 插入 k3,应淘汰 k1(最久未访问)
2258        cache.put(&k3, Value::I64(3), None);
2259
2260        assert!(
2261            cache.get(&k0).is_some(),
2262            "k0 should survive (recently accessed)"
2263        );
2264        assert!(
2265            cache.get(&k1).is_none(),
2266            "k1 should be evicted (LRU victim)"
2267        );
2268        assert!(cache.get(&k2).is_some(), "k2 should survive");
2269        assert!(
2270            cache.get(&k3).is_some(),
2271            "k3 should survive (just inserted)"
2272        );
2273    }
2274
2275    #[test]
2276    fn test_clear_all() {
2277        let cache = L2Cache::new();
2278        cache.put(&CacheKey::by_pk("users", 1), Value::I64(1), None);
2279        cache.put(&CacheKey::by_pk("users", 2), Value::I64(2), None);
2280        cache.put(&CacheKey::by_pk("orders", 1), Value::I64(3), None);
2281
2282        assert_eq!(cache.size(), 3);
2283        cache.clear();
2284        assert_eq!(cache.size(), 0);
2285    }
2286
2287    // ===== contains(不更新统计)=====
2288
2289    #[test]
2290    fn test_contains_does_not_update_stats() {
2291        let cache = L2Cache::new();
2292        let k1 = CacheKey::by_pk("users", 1);
2293        cache.put(&k1, Value::I64(1), None);
2294
2295        let exists = cache.contains(&k1);
2296        assert!(exists);
2297
2298        let stats = cache.stats();
2299        assert_eq!(stats.hits, 0);
2300        assert_eq!(stats.misses, 0);
2301    }
2302
2303    #[test]
2304    fn test_contains_returns_false_for_missing() {
2305        let cache = L2Cache::new();
2306        let k = CacheKey::by_pk("users", 999);
2307        assert!(!cache.contains(&k));
2308    }
2309
2310    #[test]
2311    fn test_contains_returns_false_for_expired() {
2312        let cache = L2Cache::new();
2313        let k = CacheKey::by_pk("users", 1);
2314        cache.put(&k, Value::I64(1), Some(Duration::from_millis(10)));
2315
2316        thread::sleep(Duration::from_millis(50));
2317        assert!(!cache.contains(&k));
2318    }
2319
2320    // ===== evict_expired 手动清理 =====
2321
2322    #[test]
2323    fn test_evict_expired_removes_only_expired_entries() {
2324        let cache = L2Cache::new();
2325
2326        let k1 = CacheKey::by_pk("users", 1);
2327        let k2 = CacheKey::by_pk("users", 2);
2328
2329        cache.put(&k1, Value::I64(1), Some(Duration::from_millis(10)));
2330        cache.put(&k2, Value::I64(2), None); // 永不过期
2331
2332        thread::sleep(Duration::from_millis(50));
2333        let removed = cache.evict_expired();
2334
2335        assert_eq!(removed, 1);
2336        assert!(cache.get(&k1).is_none());
2337        assert!(cache.get(&k2).is_some());
2338    }
2339
2340    #[test]
2341    fn test_evict_expired_returns_zero_if_no_expired() {
2342        let cache = L2Cache::new();
2343        let k1 = CacheKey::by_pk("users", 1);
2344        cache.put(&k1, Value::I64(1), None);
2345
2346        let removed = cache.evict_expired();
2347        assert_eq!(removed, 0);
2348    }
2349
2350    // ===== 多线程测试 =====
2351
2352    #[test]
2353    fn test_concurrent_access() {
2354        let cache = std::sync::Arc::new(L2Cache::new());
2355        let mut handles = Vec::new();
2356
2357        // 多线程写入
2358        for i in 0..4 {
2359            let c = cache.clone();
2360            handles.push(thread::spawn(move || {
2361                for j in 0..10 {
2362                    let k = CacheKey::by_pk("users", i * 10 + j);
2363                    c.put(&k, Value::I64(i * 10 + j), None);
2364                }
2365            }));
2366        }
2367        for h in handles {
2368            h.join().unwrap();
2369        }
2370
2371        assert_eq!(cache.size(), 40);
2372
2373        // 多线程读取
2374        let mut handles = Vec::new();
2375        for i in 0..4 {
2376            let c = cache.clone();
2377            handles.push(thread::spawn(move || {
2378                for j in 0..10 {
2379                    let k = CacheKey::by_pk("users", i * 10 + j);
2380                    let v = c.get(&k);
2381                    assert!(v.is_some());
2382                }
2383            }));
2384        }
2385        for h in handles {
2386            h.join().unwrap();
2387        }
2388
2389        let stats = cache.stats();
2390        assert_eq!(stats.hits, 40);
2391    }
2392
2393    // ===== Default 测试 =====
2394
2395    #[test]
2396    fn test_default() {
2397        let cache = L2Cache::default();
2398        assert_eq!(cache.size(), 0);
2399    }
2400
2401    // ===== 综合场景 =====
2402
2403    #[test]
2404    fn test_realistic_scenario() {
2405        let cache = L2Cache::new();
2406
2407        // 1. 缓存用户表数据
2408        for i in 1..=5 {
2409            cache.put(
2410                &CacheKey::by_pk("users", i),
2411                Value::String(format!("user_{}", i)),
2412                None,
2413            );
2414        }
2415
2416        // 2. 缓存查询结果
2417        cache.put(
2418            &CacheKey::by_query("users", "active_users_hash"),
2419            Value::I64(5),
2420            None,
2421        );
2422
2423        // 3. 读取(部分命中、部分未命中)
2424        for i in 1..=10 {
2425            let _ = cache.get(&CacheKey::by_pk("users", i));
2426        }
2427
2428        let stats = cache.stats();
2429        assert_eq!(stats.hits, 5); // 1-5 命中
2430        assert_eq!(stats.misses, 5); // 6-10 未命中
2431        assert_eq!(stats.sets, 6); // 5 pk + 1 query
2432
2433        // 4. 用户表更新,失效所有缓存
2434        cache.invalidate_table("users");
2435
2436        // 5. 再次读取应全部未命中
2437        cache.reset_stats();
2438        for i in 1..=5 {
2439            let _ = cache.get(&CacheKey::by_pk("users", i));
2440        }
2441        let stats2 = cache.stats();
2442        assert_eq!(stats2.hits, 0);
2443        assert_eq!(stats2.misses, 5);
2444    }
2445
2446    // ===== WriteBehindWriter 测试(Fix #40) =====
2447
2448    #[tokio::test]
2449    async fn test_write_behind_basic_write_and_flush() {
2450        use std::sync::atomic::{AtomicUsize, Ordering};
2451        // 计数刷新调用次数
2452        let counter = Arc::new(AtomicUsize::new(0));
2453        let counter_clone = counter.clone();
2454        let on_flush: FlushCallback = Arc::new(move |ops: Vec<WriteOp>| {
2455            let c = counter_clone.clone();
2456            Box::pin(async move {
2457                c.fetch_add(ops.len(), Ordering::SeqCst);
2458                Ok(())
2459            })
2460        });
2461        let backend = Arc::new(InMemoryBackend::new());
2462        let writer = WriteBehindWriter::new(backend.clone(), on_flush);
2463
2464        // 写入 3 个键
2465        writer.write(b"k1", b"v1", None).await.unwrap();
2466        writer.write(b"k2", b"v2", None).await.unwrap();
2467        writer.write(b"k3", b"v3", None).await.unwrap();
2468
2469        // 缓存应立即可见
2470        let v1 = backend.get("k1").await.unwrap();
2471        assert_eq!(v1, Some(b"v1".to_vec()));
2472
2473        // 队列应有 3 个待刷新
2474        assert_eq!(writer.pending_count().await, 3);
2475
2476        // 刷新
2477        writer.flush().await.unwrap();
2478        assert_eq!(counter.load(Ordering::SeqCst), 3);
2479        assert_eq!(writer.pending_count().await, 0);
2480    }
2481
2482    #[tokio::test]
2483    async fn test_write_behind_delete() {
2484        let on_flush: FlushCallback =
2485            Arc::new(|_ops: Vec<WriteOp>| Box::pin(async move { Ok(()) }));
2486        let backend = Arc::new(InMemoryBackend::new());
2487        let writer = WriteBehindWriter::new(backend.clone(), on_flush);
2488
2489        // 写入后删除
2490        writer.write(b"k1", b"v1", None).await.unwrap();
2491        assert!(backend.get("k1").await.unwrap().is_some());
2492        writer.delete(b"k1").await.unwrap();
2493        // 删除后缓存中应不存在
2494        assert!(backend.get("k1").await.unwrap().is_none());
2495
2496        // flush 应处理 2 个操作(Set + Delete)
2497        writer.flush().await.unwrap();
2498        assert_eq!(writer.pending_count().await, 0);
2499    }
2500
2501    #[tokio::test]
2502    async fn test_write_behind_flush_failure_retries() {
2503        // 模拟刷新总是失败
2504        let on_flush: FlushCallback = Arc::new(|_ops: Vec<WriteOp>| {
2505            Box::pin(async move { Err(CacheError::Internal("backend down".to_string())) })
2506        });
2507        let backend = Arc::new(InMemoryBackend::new());
2508        let writer = WriteBehindWriter::new(backend.clone(), on_flush);
2509
2510        writer.write(b"k1", b"v1", None).await.unwrap();
2511        // flush 失败,操作应保留在队列中
2512        let result = writer.flush().await;
2513        assert!(result.is_err());
2514        assert_eq!(writer.pending_count().await, 1);
2515    }
2516
2517    #[tokio::test]
2518    async fn test_write_behind_empty_flush_noop() {
2519        let on_flush: FlushCallback =
2520            Arc::new(|_ops: Vec<WriteOp>| Box::pin(async move { Ok(()) }));
2521        let backend = Arc::new(InMemoryBackend::new());
2522        let writer = WriteBehindWriter::new(backend, on_flush);
2523        // 空队列 flush 应立即成功
2524        writer.flush().await.unwrap();
2525        assert_eq!(writer.pending_count().await, 0);
2526    }
2527
2528    #[tokio::test]
2529    async fn test_write_behind_error_callback_invoked() {
2530        use std::sync::atomic::{AtomicUsize, Ordering};
2531        let error_counter = Arc::new(AtomicUsize::new(0));
2532        let ec = error_counter.clone();
2533        let on_error: ErrorCallback = Arc::new(move |_ops, _err| {
2534            ec.fetch_add(1, Ordering::SeqCst);
2535        });
2536        let on_flush: FlushCallback = Arc::new(|_ops: Vec<WriteOp>| {
2537            Box::pin(async move { Err(CacheError::Internal("fail".to_string())) })
2538        });
2539        let backend = Arc::new(InMemoryBackend::new());
2540        let writer = WriteBehindWriter::new(backend, on_flush).with_error_callback(on_error);
2541
2542        writer.write(b"k1", b"v1", None).await.unwrap();
2543        let _ = writer.flush().await;
2544        assert_eq!(error_counter.load(Ordering::SeqCst), 1);
2545    }
2546
2547    // ===== 查询缓存测试(TASK-023) =====
2548
2549    #[tokio::test]
2550    async fn test_query_cache_hit() {
2551        use std::collections::HashMap;
2552
2553        let cache = L2Cache::new();
2554        let mut call_count = 0;
2555
2556        // 第一次查询:缓存未命中,执行查询
2557        let rows1 = cache
2558            .get_or_load_query(
2559                "users",
2560                "SELECT * FROM users WHERE status = ?",
2561                &[crate::value::Value::I64(1)],
2562                std::time::Duration::from_secs(300),
2563                || {
2564                    call_count += 1;
2565                    async {
2566                        let mut row = HashMap::new();
2567                        row.insert("id".to_string(), crate::value::Value::I64(1));
2568                        row.insert(
2569                            "name".to_string(),
2570                            crate::value::Value::String("Alice".to_string()),
2571                        );
2572                        Ok(vec![row])
2573                    }
2574                },
2575            )
2576            .await
2577            .unwrap();
2578
2579        assert_eq!(call_count, 1, "第一次查询应调用 loader");
2580        assert_eq!(rows1.len(), 1, "应返回 1 行");
2581
2582        // 第二次查询:缓存命中,不调用 loader
2583        let rows2 = cache
2584            .get_or_load_query(
2585                "users",
2586                "SELECT * FROM users WHERE status = ?",
2587                &[crate::value::Value::I64(1)],
2588                std::time::Duration::from_secs(300),
2589                || {
2590                    call_count += 1;
2591                    async { Ok(vec![]) }
2592                },
2593            )
2594            .await
2595            .unwrap();
2596
2597        assert_eq!(call_count, 1, "第二次查询不应调用 loader(缓存命中)");
2598        assert_eq!(rows2.len(), 1, "应返回缓存的 1 行");
2599    }
2600
2601    #[tokio::test]
2602    async fn test_query_cache_empty_result_cached() {
2603        let cache = L2Cache::new();
2604        let mut call_count = 0;
2605
2606        // 第一次查询:返回空结果
2607        let rows1 = cache
2608            .get_or_load_query(
2609                "users",
2610                "SELECT * FROM users WHERE status = ?",
2611                &[crate::value::Value::I64(999)],
2612                std::time::Duration::from_secs(300),
2613                || {
2614                    call_count += 1;
2615                    async { Ok(vec![]) }
2616                },
2617            )
2618            .await
2619            .unwrap();
2620
2621        assert_eq!(call_count, 1, "第一次查询应调用 loader");
2622        assert_eq!(rows1.len(), 0, "应返回空结果");
2623
2624        // 第二次查询:应命中空结果缓存
2625        let rows2 = cache
2626            .get_or_load_query(
2627                "users",
2628                "SELECT * FROM users WHERE status = ?",
2629                &[crate::value::Value::I64(999)],
2630                std::time::Duration::from_secs(300),
2631                || {
2632                    call_count += 1;
2633                    async { Ok(vec![]) }
2634                },
2635            )
2636            .await
2637            .unwrap();
2638
2639        assert_eq!(call_count, 1, "第二次查询不应调用 loader(空结果缓存命中)");
2640        assert_eq!(rows2.len(), 0, "应返回缓存的空结果");
2641    }
2642
2643    #[tokio::test]
2644    async fn test_query_cache_different_params() {
2645        use std::collections::HashMap;
2646
2647        let cache = L2Cache::new();
2648        let mut call_count = 0;
2649
2650        // 查询 status = 1
2651        let _ = cache
2652            .get_or_load_query(
2653                "users",
2654                "SELECT * FROM users WHERE status = ?",
2655                &[crate::value::Value::I64(1)],
2656                std::time::Duration::from_secs(300),
2657                || {
2658                    call_count += 1;
2659                    async {
2660                        let mut row = HashMap::new();
2661                        row.insert("id".to_string(), crate::value::Value::I64(1));
2662                        Ok(vec![row])
2663                    }
2664                },
2665            )
2666            .await
2667            .unwrap();
2668
2669        // 查询 status = 2(不同参数,应未命中)
2670        let rows2 = cache
2671            .get_or_load_query(
2672                "users",
2673                "SELECT * FROM users WHERE status = ?",
2674                &[crate::value::Value::I64(2)],
2675                std::time::Duration::from_secs(300),
2676                || {
2677                    call_count += 1;
2678                    async {
2679                        let mut row = HashMap::new();
2680                        row.insert("id".to_string(), crate::value::Value::I64(2));
2681                        row.insert(
2682                            "name".to_string(),
2683                            crate::value::Value::String("Bob".to_string()),
2684                        );
2685                        Ok(vec![row])
2686                    }
2687                },
2688            )
2689            .await
2690            .unwrap();
2691
2692        assert_eq!(call_count, 2, "不同参数应调用 loader 两次");
2693        assert_eq!(rows2.len(), 1, "应返回 1 行");
2694    }
2695
2696    #[tokio::test]
2697    async fn test_query_cache_invalidate() {
2698        use std::collections::HashMap;
2699
2700        let cache = L2Cache::new();
2701        let mut call_count = 0;
2702
2703        // 第一次查询
2704        let _ = cache
2705            .get_or_load_query(
2706                "users",
2707                "SELECT * FROM users WHERE status = ?",
2708                &[crate::value::Value::I64(1)],
2709                std::time::Duration::from_secs(300),
2710                || {
2711                    call_count += 1;
2712                    async {
2713                        let mut row = HashMap::new();
2714                        row.insert("id".to_string(), crate::value::Value::I64(1));
2715                        Ok(vec![row])
2716                    }
2717                },
2718            )
2719            .await
2720            .unwrap();
2721
2722        assert_eq!(call_count, 1, "第一次查询应调用 loader");
2723
2724        // 失效查询缓存
2725        cache.invalidate_query(
2726            "users",
2727            "SELECT * FROM users WHERE status = ?",
2728            &[crate::value::Value::I64(1)],
2729        );
2730
2731        // 再次查询:应重新调用 loader
2732        let _ = cache
2733            .get_or_load_query(
2734                "users",
2735                "SELECT * FROM users WHERE status = ?",
2736                &[crate::value::Value::I64(1)],
2737                std::time::Duration::from_secs(300),
2738                || {
2739                    call_count += 1;
2740                    async { Ok(vec![]) }
2741                },
2742            )
2743            .await
2744            .unwrap();
2745
2746        assert_eq!(call_count, 2, "失效后应重新调用 loader");
2747    }
2748
2749    #[tokio::test]
2750    async fn test_query_cache_hit_rate() {
2751        use std::collections::HashMap;
2752
2753        let cache = L2Cache::new();
2754        let mut call_count = 0;
2755
2756        // 模拟 10 次相同查询
2757        for _ in 0..10 {
2758            let _ = cache
2759                .get_or_load_query(
2760                    "users",
2761                    "SELECT * FROM users WHERE status = ?",
2762                    &[crate::value::Value::I64(1)],
2763                    std::time::Duration::from_secs(300),
2764                    || {
2765                        call_count += 1;
2766                        async {
2767                            let mut row = HashMap::new();
2768                            row.insert("id".to_string(), crate::value::Value::I64(1));
2769                            Ok(vec![row])
2770                        }
2771                    },
2772                )
2773                .await
2774                .unwrap();
2775        }
2776
2777        // 只有第一次调用 loader,后续 9 次命中缓存
2778        assert_eq!(call_count, 1, "10 次查询中只有 1 次调用 loader");
2779
2780        let stats = cache.stats();
2781        assert_eq!(stats.hits, 9, "应命中 9 次");
2782        assert_eq!(stats.misses, 1, "应未命中 1 次");
2783
2784        let hit_rate = stats.hit_rate();
2785        assert!(
2786            hit_rate >= 0.8,
2787            "命中率应 >= 80%,实际: {:.2}%",
2788            hit_rate * 100.0
2789        );
2790    }
2791
2792    // ===== RedisBackend 测试(Fix #39) =====
2793
2794    #[cfg(feature = "redis")]
2795    #[tokio::test]
2796    async fn test_redis_backend_invalid_url_returns_error() {
2797        // 无效 URL:redis::Client::open 在连接前解析失败,快速返回错误
2798        let result = RedisBackend::new("not-a-valid-redis-url").await;
2799        let msg = match result {
2800            Ok(_) => panic!("无效 URL 不应连接成功"),
2801            Err(CacheError::Internal(m)) => m,
2802            Err(other) => panic!("期望 CacheError::Internal,实际: {:?}", other),
2803        };
2804        assert!(
2805            msg.contains("Redis client create failed"),
2806            "错误消息应指明 client 创建失败: {}",
2807            msg
2808        );
2809    }
2810}
2811
2812// ============================================================================
2813// v3.4.0 M3-T4:zero-copy L2 缓存推广(perf-zero-copy-l2 feature)
2814// ============================================================================
2815
2816/// zero-copy L2 缓存序列化
2817///
2818/// 当 `zero-copy` feature 启用时,使用 `BorrowedValue` + `ColumnarResultSet`
2819/// 推广零拷贝序列化到 L2 缓存路径,减少序列化/反序列化过程中的内存分配。
2820#[cfg(feature = "zero-copy")]
2821pub mod zero_copy {
2822    use crate::value::Value;
2823    use crate::value_borrowed::BorrowedValue;
2824
2825    /// 将 `Value` 转为 `BorrowedValue`(零拷贝,字符串借用原值)
2826    pub fn to_borrowed(value: &Value) -> BorrowedValue<'_> {
2827        match value {
2828            &Value::Null => BorrowedValue::Null,
2829            Value::Bool(b) => BorrowedValue::Bool(*b),
2830            Value::I8(v) => BorrowedValue::I8(*v),
2831            Value::I16(v) => BorrowedValue::I16(*v),
2832            Value::I32(v) => BorrowedValue::I32(*v),
2833            Value::I64(v) => BorrowedValue::I64(*v),
2834            Value::U8(v) => BorrowedValue::U8(*v),
2835            Value::U16(v) => BorrowedValue::U16(*v),
2836            Value::U32(v) => BorrowedValue::U32(*v),
2837            Value::U64(v) => BorrowedValue::U64(*v),
2838            Value::F32(v) => BorrowedValue::F32(*v),
2839            Value::F64(v) => BorrowedValue::F64(*v),
2840            Value::Decimal(s) => BorrowedValue::Decimal(std::borrow::Cow::Borrowed(s)),
2841            Value::String(s) => BorrowedValue::String(std::borrow::Cow::Borrowed(s)),
2842            Value::Bytes(b) => BorrowedValue::Bytes(std::borrow::Cow::Borrowed(b)),
2843            Value::Uuid(s) => BorrowedValue::Uuid(std::borrow::Cow::Borrowed(s)),
2844            Value::Date(s) => BorrowedValue::Date(std::borrow::Cow::Borrowed(s)),
2845            Value::DateTime(s) => BorrowedValue::DateTime(std::borrow::Cow::Borrowed(s)),
2846            Value::Time(s) => BorrowedValue::Time(std::borrow::Cow::Borrowed(s)),
2847            Value::Json(s) => BorrowedValue::Json(std::borrow::Cow::Borrowed(s)),
2848            Value::Array(arr) => {
2849                let borrowed: Vec<BorrowedValue<'_>> = arr.iter().map(to_borrowed).collect();
2850                BorrowedValue::Array(borrowed)
2851            }
2852            Value::Object(obj) => {
2853                let mut borrowed = std::collections::HashMap::new();
2854                for (k, v) in obj {
2855                    borrowed.insert(k.clone(), to_borrowed(v));
2856                }
2857                BorrowedValue::Object(borrowed)
2858            }
2859            #[cfg(feature = "perf-box-str")]
2860            Value::BoxedStr(s) => BorrowedValue::String(std::borrow::Cow::Borrowed(&**s)),
2861        }
2862    }
2863
2864    /// 将 `BorrowedValue` 转回 `Value`(克隆所需数据)
2865    pub fn from_borrowed(borrowed: &BorrowedValue<'_>) -> Value {
2866        match borrowed {
2867            BorrowedValue::Null => Value::Null,
2868            BorrowedValue::Bool(b) => Value::Bool(*b),
2869            BorrowedValue::I8(v) => Value::I8(*v),
2870            BorrowedValue::I16(v) => Value::I16(*v),
2871            BorrowedValue::I32(v) => Value::I32(*v),
2872            BorrowedValue::I64(v) => Value::I64(*v),
2873            BorrowedValue::U8(v) => Value::U8(*v),
2874            BorrowedValue::U16(v) => Value::U16(*v),
2875            BorrowedValue::U32(v) => Value::U32(*v),
2876            BorrowedValue::U64(v) => Value::U64(*v),
2877            BorrowedValue::F32(v) => Value::F32(*v),
2878            BorrowedValue::F64(v) => Value::F64(*v),
2879            BorrowedValue::Decimal(s) => Value::Decimal(s.to_string()),
2880            BorrowedValue::String(s) => Value::String(s.to_string()),
2881            BorrowedValue::Bytes(b) => Value::Bytes(b.to_vec()),
2882            BorrowedValue::Uuid(s) => Value::Uuid(s.to_string()),
2883            BorrowedValue::Date(s) => Value::Date(s.to_string()),
2884            BorrowedValue::DateTime(s) => Value::DateTime(s.to_string()),
2885            BorrowedValue::Time(s) => Value::Time(s.to_string()),
2886            BorrowedValue::Json(s) => Value::Json(s.to_string()),
2887            BorrowedValue::Array(arr) => {
2888                let values: Vec<Value> = arr.iter().map(from_borrowed).collect();
2889                Value::Array(values)
2890            }
2891            BorrowedValue::Object(obj) => {
2892                let mut values = std::collections::HashMap::new();
2893                for (k, v) in obj {
2894                    values.insert(k.clone(), from_borrowed(v));
2895                }
2896                Value::Object(values)
2897            }
2898        }
2899    }
2900
2901    /// 零拷贝序列化 `Value` 为字节(使用 BorrowedValue 中间表示)
2902    pub fn serialize_zero_copy(value: &Value) -> Vec<u8> {
2903        let borrowed = to_borrowed(value);
2904        format!("{:?}", borrowed).into_bytes()
2905    }
2906
2907    /// 零拷贝反序列化(简化实现,实际应使用二进制格式)
2908    pub fn deserialize_zero_copy(data: &[u8]) -> Result<Value, String> {
2909        let s = std::str::from_utf8(data).map_err(|e| e.to_string())?;
2910        Ok(Value::String(s.to_string()))
2911    }
2912}
2913
2914#[cfg(all(test, feature = "zero-copy"))]
2915mod zero_copy_tests {
2916    use super::zero_copy::*;
2917    use crate::value::Value;
2918    use crate::value_borrowed::BorrowedValue;
2919    use std::borrow::Cow;
2920
2921    #[test]
2922    fn test_to_borrowed_roundtrip() {
2923        let values = vec![
2924            Value::Null,
2925            Value::Bool(true),
2926            Value::I64(42),
2927            Value::F64(3.14),
2928            Value::String("hello".to_string()),
2929            Value::I32(-100),
2930        ];
2931        for v in &values {
2932            let borrowed = to_borrowed(v);
2933            let restored = from_borrowed(&borrowed);
2934            assert_eq!(*v, restored);
2935        }
2936    }
2937
2938    #[test]
2939    fn test_serialize_deserialize() {
2940        let v = Value::String("test".to_string());
2941        let data = serialize_zero_copy(&v);
2942        assert!(!data.is_empty());
2943    }
2944
2945    #[test]
2946    fn test_zero_copy_no_allocation() {
2947        let v = Value::String("hello".to_string());
2948        let borrowed = to_borrowed(&v);
2949        match &borrowed {
2950            BorrowedValue::String(Cow::Borrowed(s)) => assert_eq!(*s, "hello"),
2951            _ => panic!("应为 Cow::Borrowed"),
2952        }
2953    }
2954}
2955
2956#[cfg(all(test, feature = "prod-redis-tls"))]
2957mod prod_redis_tls_tests {
2958    use super::*;
2959
2960    #[test]
2961    fn test_tls_config_validate_production_rejects_skip_verify() {
2962        let tls = RedisTlsConfig {
2963            enabled: true,
2964            ca_cert_path: None,
2965            client_cert_path: None,
2966            client_key_path: None,
2967            sni: Some("redis.example.com".to_string()),
2968            skip_verify: true,
2969        };
2970        let result = tls.validate(true);
2971        assert!(result.is_err());
2972        assert!(result
2973            .unwrap_err()
2974            .contains("skip_verify forbidden in production"));
2975    }
2976
2977    #[test]
2978    fn test_tls_config_validate_development_allows_skip_verify() {
2979        let tls = RedisTlsConfig {
2980            enabled: true,
2981            ca_cert_path: None,
2982            client_cert_path: None,
2983            client_key_path: None,
2984            sni: Some("redis.example.com".to_string()),
2985            skip_verify: true,
2986        };
2987        assert!(tls.validate(false).is_ok());
2988    }
2989
2990    #[test]
2991    fn test_tls_config_disabled_validate_passes() {
2992        let tls = RedisTlsConfig::disabled();
2993        assert!(tls.validate(true).is_ok());
2994    }
2995
2996    #[test]
2997    fn test_mask_redis_url_with_password() {
2998        let masked = mask_redis_url("redis://:test123@127.0.0.1:6379/0");
2999        assert_eq!(masked, "redis://:***@127.0.0.1:6379/0");
3000    }
3001
3002    #[test]
3003    fn test_mask_redis_url_without_password() {
3004        let masked = mask_redis_url("redis://127.0.0.1:6379/0");
3005        assert_eq!(masked, "redis://127.0.0.1:6379/0");
3006    }
3007
3008    #[test]
3009    fn test_mask_redis_url_empty_password() {
3010        let masked = mask_redis_url("redis://:@127.0.0.1:6379/0");
3011        assert_eq!(masked, "redis://:@127.0.0.1:6379/0");
3012    }
3013}