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