Skip to main content

sz_orm_core/
dist_cache.rs

1//! 分布式缓存一致性模块
2//!
3//! 本模块在 `dist-cache` feature gate 下导出,提供:
4//! - [`ConsistencyLevel`] — 一致性级别枚举(Eventual / Strong)
5//! - [`RedisPubSubInvalidationBus`] — Redis Pub/Sub 跨实例失效总线
6//! - [`GossipInvalidationBus`] — Gossip 去中心化失效总线
7//! - [`WriteBehindQueue`] / [`WriteBehindConfig`] / [`WriteOp`] — Write-behind 异步批量写入
8//! - [`BloomFilterGuard`] — 布隆过滤器击穿防护
9//! - `MutexGuard` — 互斥锁击穿防护
10//! - [`RandomTtlJitter`] — 随机 TTL 雪崩防护
11
12use crate::l2_cache::{InvalidationBus, InvalidationMessage};
13use crate::value::Value;
14use std::collections::{HashMap, HashSet, VecDeque};
15use std::path::PathBuf;
16use std::sync::atomic::{AtomicU64, Ordering};
17use std::sync::Arc;
18use std::time::Duration;
19
20// ─── M2-T2:一致性级别配置 ─────────────────────────────────────────
21
22/// 一致性级别枚举
23///
24/// - `Eventual`:默认,写库后异步失效 + TTL 兜底
25/// - `Strong`:先失效所有实例缓存再写库
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
27pub enum ConsistencyLevel {
28    /// 最终一致(写库后异步失效 + TTL 兜底)
29    #[default]
30    Eventual,
31    /// 强一致(先失效所有实例缓存再写库)
32    Strong,
33}
34
35// ─── M2-T3:Redis Pub/Sub 失效总线 ─────────────────────────────────
36
37/// Redis Pub/Sub 跨实例失效总线
38///
39/// 复用既有 Redis 连接管理(自动重连),Pub/Sub 专用连接。
40/// 消息序列化为 ≤1KB JSON,跳过本实例 instance_id 避免自回环。
41pub struct RedisPubSubInvalidationBus {
42    /// Redis 连接管理器
43    client: Option<redis::aio::ConnectionManager>,
44    /// Pub/Sub 通道名
45    channel: String,
46    /// 本地缓冲(订阅循环写入,subscribe drain 读取)
47    local_buffer: parking_lot::Mutex<VecDeque<InvalidationMessage>>,
48    /// 本实例 ID(避免自回环)
49    instance_id: String,
50}
51
52impl RedisPubSubInvalidationBus {
53    /// 创建 Redis Pub/Sub 失效总线
54    pub fn new(client: redis::aio::ConnectionManager, instance_id: impl Into<String>) -> Self {
55        Self {
56            client: Some(client),
57            channel: "sz-orm:invalidation".to_string(),
58            local_buffer: parking_lot::Mutex::new(VecDeque::new()),
59            instance_id: instance_id.into(),
60        }
61    }
62
63    /// 创建未连接的失效总线(降级为本地失效)
64    pub fn disconnected(instance_id: impl Into<String>) -> Self {
65        Self {
66            client: None,
67            channel: "sz-orm:invalidation".to_string(),
68            local_buffer: parking_lot::Mutex::new(VecDeque::new()),
69            instance_id: instance_id.into(),
70        }
71    }
72
73    /// 设置通道名
74    pub fn with_channel(mut self, channel: impl Into<String>) -> Self {
75        self.channel = channel.into();
76        self
77    }
78
79    /// 获取本实例 ID
80    pub fn instance_id(&self) -> &str {
81        &self.instance_id
82    }
83
84    /// 将失效消息序列化为 JSON(≤1KB)
85    fn serialize_message(message: &InvalidationMessage, instance_id: &str) -> String {
86        let payload = match message {
87            InvalidationMessage::InvalidateKey(key) => {
88                serde_json::json!({"type": "key", "key": key, "src": instance_id})
89            }
90            InvalidationMessage::InvalidateTable(table) => {
91                serde_json::json!({"type": "table", "table": table, "src": instance_id})
92            }
93            InvalidationMessage::InvalidateAll => {
94                serde_json::json!({"type": "all", "src": instance_id})
95            }
96        };
97        payload.to_string()
98    }
99
100    /// 从 JSON 反序列化失效消息
101    #[allow(dead_code)]
102    fn deserialize_message(json: &str, self_instance_id: &str) -> Option<InvalidationMessage> {
103        let v: serde_json::Value = serde_json::from_str(json).ok()?;
104        let src = v.get("src")?.as_str()?;
105        // 跳过自回环
106        if src == self_instance_id {
107            return None;
108        }
109        match v.get("type")?.as_str()? {
110            "key" => {
111                let key = v.get("key")?.as_str()?;
112                Some(InvalidationMessage::InvalidateKey(key.to_string()))
113            }
114            "table" => {
115                let table = v.get("table")?.as_str()?;
116                Some(InvalidationMessage::InvalidateTable(table.to_string()))
117            }
118            "all" => Some(InvalidationMessage::InvalidateAll),
119            _ => None,
120        }
121    }
122
123    /// 接收消息(从外部订阅循环调用,写入本地缓冲)
124    pub fn push_received(&self, message: InvalidationMessage) {
125        self.local_buffer.lock().push_back(message);
126    }
127}
128
129impl InvalidationBus for RedisPubSubInvalidationBus {
130    fn publish(&self, message: InvalidationMessage) {
131        if let Some(client) = &self.client {
132            let json = Self::serialize_message(&message, &self.instance_id);
133            // 异步发布(fire-and-forget)
134            let client = client.clone();
135            let channel = self.channel.clone();
136            tokio::spawn(async move {
137                let _: Result<(), _> = redis::cmd("PUBLISH")
138                    .arg(&channel)
139                    .arg(&json)
140                    .query_async(&mut client.clone())
141                    .await;
142            });
143        }
144        // Redis 不可达时降级为本地失效(仅失效本实例缓存)
145    }
146
147    fn subscribe(&self) -> Box<dyn Iterator<Item = InvalidationMessage> + Send> {
148        let mut buffer = self.local_buffer.lock();
149        let drained: Vec<_> = buffer.drain(..).collect();
150        Box::new(drained.into_iter())
151    }
152}
153
154// ─── M2-T4:Gossip 失效总线 ────────────────────────────────────────
155
156/// 节点地址
157#[derive(Debug, Clone)]
158pub struct NodeAddr {
159    /// 主机地址
160    pub host: String,
161    /// 端口
162    pub port: u16,
163}
164
165impl NodeAddr {
166    /// 创建节点地址
167    pub fn new(host: impl Into<String>, port: u16) -> Self {
168        Self {
169            host: host.into(),
170            port,
171        }
172    }
173}
174
175/// Gossip 去中心化失效总线
176///
177/// 点对点发送到所有已知节点,HMAC 共享密钥认证,
178/// seen_messages 去重避免重复传播。
179pub struct GossipInvalidationBus {
180    /// 集群节点地址列表
181    #[allow(dead_code)]
182    nodes: Vec<NodeAddr>,
183    /// 共享密钥认证
184    shared_secret: Vec<u8>,
185    /// 本地缓冲
186    local_buffer: parking_lot::Mutex<VecDeque<InvalidationMessage>>,
187    /// 已见消息 ID 去重
188    seen_messages: parking_lot::RwLock<HashSet<u64>>,
189    /// 本实例 ID
190    instance_id: String,
191    /// 消息序列号
192    sequence: AtomicU64,
193}
194
195impl GossipInvalidationBus {
196    /// 创建 Gossip 失效总线
197    pub fn new(
198        nodes: Vec<NodeAddr>,
199        shared_secret: Vec<u8>,
200        instance_id: impl Into<String>,
201    ) -> Self {
202        Self {
203            nodes,
204            shared_secret,
205            local_buffer: parking_lot::Mutex::new(VecDeque::new()),
206            seen_messages: parking_lot::RwLock::new(HashSet::new()),
207            instance_id: instance_id.into(),
208            sequence: AtomicU64::new(0),
209        }
210    }
211
212    /// 获取本实例 ID
213    pub fn instance_id(&self) -> &str {
214        &self.instance_id
215    }
216
217    /// 生成消息 ID(用于去重)
218    fn message_id(&self) -> u64 {
219        self.sequence.fetch_add(1, Ordering::SeqCst)
220    }
221
222    /// 计算 HMAC 认证标签
223    fn compute_hmac(&self, message: &InvalidationMessage) -> Vec<u8> {
224        let msg_bytes = format!("{:?}", message);
225        sz_orm_crypto::hmac_sha256(&self.shared_secret, msg_bytes.as_bytes()).to_vec()
226    }
227
228    /// 验证 HMAC 认证标签
229    fn verify_hmac(&self, message: &InvalidationMessage, tag: &[u8]) -> bool {
230        let expected = self.compute_hmac(message);
231        expected == tag
232    }
233
234    /// 接收消息(从其他节点调用,需认证 + 去重)
235    pub fn receive(&self, message: InvalidationMessage, msg_id: u64, hmac_tag: &[u8]) -> bool {
236        // 认证
237        if !self.verify_hmac(&message, hmac_tag) {
238            return false;
239        }
240        // 去重
241        let mut seen = self.seen_messages.write();
242        if !seen.insert(msg_id) {
243            return false; // 已见过
244        }
245        drop(seen);
246        // 写入本地缓冲
247        self.local_buffer.lock().push_back(message);
248        true
249    }
250}
251
252impl InvalidationBus for GossipInvalidationBus {
253    fn publish(&self, message: InvalidationMessage) {
254        let msg_id = self.message_id();
255        let _hmac_tag = self.compute_hmac(&message);
256        // 点对点发送到所有已知节点(并行)
257        // 实际网络发送由调用方实现,此处仅写入本地缓冲
258        let mut seen = self.seen_messages.write();
259        seen.insert(msg_id);
260        drop(seen);
261        self.local_buffer.lock().push_back(message);
262    }
263
264    fn subscribe(&self) -> Box<dyn Iterator<Item = InvalidationMessage> + Send> {
265        let mut buffer = self.local_buffer.lock();
266        let drained: Vec<_> = buffer.drain(..).collect();
267        Box::new(drained.into_iter())
268    }
269}
270
271// ─── M2-T6:Write-behind 配置与队列 ────────────────────────────────
272
273/// Write-behind 配置
274#[derive(Debug, Clone)]
275pub struct WriteBehindConfig {
276    /// 批量刷盘大小(默认 100)
277    pub batch_size: u32,
278    /// 刷盘间隔(默认 100ms)
279    pub flush_interval: Duration,
280    /// WAL 文件路径
281    pub wal_path: PathBuf,
282    /// WAL 加密密钥
283    pub encryption_key: Vec<u8>,
284    /// 刷盘失败回退同步写(默认 true)
285    pub fallback_to_sync: bool,
286}
287
288impl Default for WriteBehindConfig {
289    fn default() -> Self {
290        Self {
291            batch_size: 100,
292            flush_interval: Duration::from_millis(100),
293            wal_path: PathBuf::from("wal/sz-orm-wal.log"),
294            encryption_key: Vec::new(),
295            fallback_to_sync: true,
296        }
297    }
298}
299
300impl WriteBehindConfig {
301    /// 创建配置构建器
302    pub fn builder() -> WriteBehindConfigBuilder {
303        WriteBehindConfigBuilder::default()
304    }
305}
306
307/// Write-behind 配置构建器
308#[derive(Debug, Clone, Default)]
309pub struct WriteBehindConfigBuilder {
310    batch_size: Option<u32>,
311    flush_interval: Option<Duration>,
312    wal_path: Option<PathBuf>,
313    encryption_key: Option<Vec<u8>>,
314    fallback_to_sync: Option<bool>,
315}
316
317impl WriteBehindConfigBuilder {
318    /// 设置批大小
319    pub fn batch_size(mut self, size: u32) -> Self {
320        self.batch_size = Some(size);
321        self
322    }
323    /// 设置刷新间隔
324    pub fn flush_interval(mut self, interval: Duration) -> Self {
325        self.flush_interval = Some(interval);
326        self
327    }
328    /// 设置 WAL 文件路径
329    pub fn wal_path(mut self, path: PathBuf) -> Self {
330        self.wal_path = Some(path);
331        self
332    }
333    /// 设置加密密钥
334    pub fn encryption_key(mut self, key: Vec<u8>) -> Self {
335        self.encryption_key = Some(key);
336        self
337    }
338    /// 设置是否回退到同步写
339    pub fn fallback_to_sync(mut self, fallback: bool) -> Self {
340        self.fallback_to_sync = Some(fallback);
341        self
342    }
343    /// 构建配置
344    pub fn build(self) -> WriteBehindConfig {
345        WriteBehindConfig {
346            batch_size: self.batch_size.unwrap_or(100),
347            flush_interval: self
348                .flush_interval
349                .unwrap_or_else(|| Duration::from_millis(100)),
350            wal_path: self
351                .wal_path
352                .unwrap_or_else(|| PathBuf::from("wal/sz-orm-wal.log")),
353            encryption_key: self.encryption_key.unwrap_or_default(),
354            fallback_to_sync: self.fallback_to_sync.unwrap_or(true),
355        }
356    }
357}
358
359/// 写操作类型
360#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
361pub enum WriteOpType {
362    /// 插入
363    Insert,
364    /// 更新
365    Update,
366    /// 删除
367    Delete,
368}
369
370/// Write-behind 写操作
371#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
372pub struct WriteOp {
373    /// 操作类型
374    pub op_type: WriteOpType,
375    /// 表名
376    pub table: String,
377    /// 主键值
378    pub pk: Value,
379    /// 变更数据
380    pub data: Vec<(String, Value)>,
381    /// 时间戳
382    pub timestamp: i64,
383    /// 单调递增序列号
384    pub sequence: u64,
385}
386
387impl WriteOp {
388    /// 创建写操作
389    pub fn new(op_type: WriteOpType, table: impl Into<String>, pk: Value) -> Self {
390        Self {
391            op_type,
392            table: table.into(),
393            pk,
394            data: Vec::new(),
395            timestamp: chrono::Utc::now().timestamp(),
396            sequence: 0,
397        }
398    }
399
400    /// 添加变更数据
401    pub fn with_data(mut self, data: Vec<(String, Value)>) -> Self {
402        self.data = data;
403        self
404    }
405}
406
407/// Write-behind 持久化队列
408///
409/// WAL 持久化先于返回成功(宕机不丢数据),
410/// 内存待刷盘队列 + 单调递增序列号。
411pub struct WriteBehindQueue {
412    /// WAL 文件
413    wal: parking_lot::Mutex<WalFile>,
414    /// 内存待刷盘队列
415    pending: crossbeam_queue::ArrayQueue<WriteOp>,
416    /// 单调递增序列号
417    sequence: AtomicU64,
418    /// 配置
419    config: WriteBehindConfig,
420}
421
422impl WriteBehindQueue {
423    /// 创建 Write-behind 队列
424    pub fn new(config: WriteBehindConfig) -> std::io::Result<Self> {
425        let wal = WalFile::open(&config.wal_path, &config.encryption_key)?;
426        let capacity = (config.batch_size * 10) as usize;
427        Ok(Self {
428            wal: parking_lot::Mutex::new(wal),
429            pending: crossbeam_queue::ArrayQueue::new(capacity.max(1024)),
430            sequence: AtomicU64::new(0),
431            config,
432        })
433    }
434
435    /// 入队写操作(WAL 持久化 + 入内存队列,立即返回)
436    ///
437    /// WAL 持久化先于返回成功,保证宕机不丢数据。
438    pub fn enqueue(&self, mut op: WriteOp) -> std::io::Result<()> {
439        op.sequence = self.sequence.fetch_add(1, Ordering::SeqCst);
440        // 1. WAL 持久化(加密 + CRC)
441        self.wal.lock().append(&op)?;
442        // 2. 入内存 pending 队列
443        let _ = self.pending.push(op);
444        // 3. 立即返回成功
445        Ok(())
446    }
447
448    /// 批量取出待刷盘操作
449    pub fn drain_batch(&self) -> Vec<WriteOp> {
450        let batch_size = self.config.batch_size as usize;
451        let mut batch = Vec::with_capacity(batch_size);
452        for _ in 0..batch_size {
453            match self.pending.pop() {
454                Some(op) => batch.push(op),
455                None => break,
456            }
457        }
458        // 按 sequence 排序
459        batch.sort_by_key(|op| op.sequence);
460        batch
461    }
462
463    /// 标记 WAL 已刷盘(截断)
464    pub fn truncate_wal(&self) -> std::io::Result<()> {
465        self.wal.lock().truncate()
466    }
467
468    /// 宕机重启回放 WAL
469    ///
470    /// 读取 WAL 文件,CRC 校验 + 解密,按 sequence 顺序回放未刷盘 WriteOp。
471    pub fn replay(&self) -> std::io::Result<Vec<WriteOp>> {
472        self.wal.lock().read_all()
473    }
474
475    /// 获取配置
476    pub fn config(&self) -> &WriteBehindConfig {
477        &self.config
478    }
479
480    /// 获取待刷盘数量
481    pub fn pending_count(&self) -> usize {
482        self.pending.len()
483    }
484}
485
486// ─── M2-T7:WAL 持久化与加密 ───────────────────────────────────────
487
488/// WAL 文件
489///
490/// 每条记录格式:[4字节长度][加密载荷][8字节CRC]
491struct WalFile {
492    path: PathBuf,
493    encryption_key: Vec<u8>,
494}
495
496impl WalFile {
497    fn open(path: &std::path::Path, encryption_key: &[u8]) -> std::io::Result<Self> {
498        if let Some(parent) = path.parent() {
499            std::fs::create_dir_all(parent)?;
500        }
501        Ok(Self {
502            path: path.to_path_buf(),
503            encryption_key: encryption_key.to_vec(),
504        })
505    }
506
507    fn append(&mut self, op: &WriteOp) -> std::io::Result<()> {
508        let json = serde_json::to_string(op)
509            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))?;
510        let payload = json.as_bytes();
511
512        let encrypted = if self.encryption_key.is_empty() {
513            payload.to_vec()
514        } else {
515            self.encrypt(payload)
516        };
517
518        let crc = crc64(&encrypted);
519
520        let mut record = Vec::with_capacity(4 + encrypted.len() + 8);
521        record.extend_from_slice(&(encrypted.len() as u32).to_le_bytes());
522        record.extend_from_slice(&encrypted);
523        record.extend_from_slice(&crc.to_le_bytes());
524
525        let mut file = std::fs::OpenOptions::new()
526            .create(true)
527            .append(true)
528            .open(&self.path)?;
529        use std::io::Write;
530        file.write_all(&record)?;
531        file.flush()?;
532        Ok(())
533    }
534
535    fn read_all(&self) -> std::io::Result<Vec<WriteOp>> {
536        let data = match std::fs::read(&self.path) {
537            Ok(d) => d,
538            Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(Vec::new()),
539            Err(e) => return Err(e),
540        };
541        let mut ops = Vec::new();
542        let mut pos = 0;
543        while pos + 4 <= data.len() {
544            let len = u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]])
545                as usize;
546            pos += 4;
547            if pos + len + 8 > data.len() {
548                break;
549            }
550            let encrypted = &data[pos..pos + len];
551            pos += len;
552            let expected_crc = u64::from_le_bytes([
553                data[pos],
554                data[pos + 1],
555                data[pos + 2],
556                data[pos + 3],
557                data[pos + 4],
558                data[pos + 5],
559                data[pos + 6],
560                data[pos + 7],
561            ]);
562            pos += 8;
563
564            if crc64(encrypted) != expected_crc {
565                continue;
566            }
567
568            let decrypted = if self.encryption_key.is_empty() {
569                encrypted.to_vec()
570            } else {
571                self.decrypt(encrypted)
572            };
573
574            if let Ok(op) = serde_json::from_slice::<WriteOp>(&decrypted) {
575                ops.push(op);
576            }
577        }
578        ops.sort_by_key(|op| op.sequence);
579        Ok(ops)
580    }
581
582    fn truncate(&mut self) -> std::io::Result<()> {
583        std::fs::write(&self.path, b"")?;
584        Ok(())
585    }
586
587    fn encrypt(&self, data: &[u8]) -> Vec<u8> {
588        let crypter = sz_orm_crypto::AesGcmCrypter::from_key_str(
589            std::str::from_utf8(&self.encryption_key).unwrap_or("default-key"),
590        );
591        crypter
592            .encrypt_with_aad(data, &[])
593            .unwrap_or_else(|_| data.to_vec())
594    }
595
596    fn decrypt(&self, data: &[u8]) -> Vec<u8> {
597        let crypter = sz_orm_crypto::AesGcmCrypter::from_key_str(
598            std::str::from_utf8(&self.encryption_key).unwrap_or("default-key"),
599        );
600        crypter
601            .decrypt_with_aad(data, &[])
602            .unwrap_or_else(|_| data.to_vec())
603    }
604}
605
606fn crc64(data: &[u8]) -> u64 {
607    let mut crc: u64 = 0;
608    for &byte in data {
609        crc ^= byte as u64;
610        for _ in 0..8 {
611            if crc & 1 != 0 {
612                crc = (crc >> 1) ^ 0xC96E_8607_EAFC_E6CD;
613            } else {
614                crc >>= 1;
615            }
616        }
617    }
618    crc
619}
620
621// ─── M2-T9:布隆过滤器防护 ─────────────────────────────────────────
622
623/// 布隆过滤器击穿防护
624///
625/// 假阳性率 ≤ 1% 可配置,超容量自动重建。
626pub struct BloomFilterGuard {
627    filter: parking_lot::RwLock<bloomfilter::Bloom<String>>,
628    capacity: usize,
629    false_positive_rate: f64,
630    count: AtomicU64,
631}
632
633impl BloomFilterGuard {
634    /// 创建布隆过滤器
635    pub fn new(capacity: usize, false_positive_rate: f64) -> Self {
636        let filter = bloomfilter::Bloom::new_for_fp_rate(capacity, false_positive_rate);
637        Self {
638            filter: parking_lot::RwLock::new(filter),
639            capacity,
640            false_positive_rate,
641            count: AtomicU64::new(0),
642        }
643    }
644
645    /// 使用默认配置创建(容量 100000,假阳性率 0.01)
646    pub fn default_config() -> Self {
647        Self::new(100_000, 0.01)
648    }
649
650    /// 添加 key
651    pub fn add(&self, key: &str) {
652        self.filter.write().set(&key.to_string());
653        self.count.fetch_add(1, Ordering::Relaxed);
654    }
655
656    /// 判断是否可能存在(假阳性 ≤ false_positive_rate)
657    pub fn might_contain(&self, key: &str) -> bool {
658        self.filter.read().check(&key.to_string())
659    }
660
661    /// 重建布隆过滤器(超容量时调用)
662    pub fn rebuild(&self, keys: impl Iterator<Item = String>) {
663        let mut filter =
664            bloomfilter::Bloom::new_for_fp_rate(self.capacity, self.false_positive_rate);
665        for key in keys {
666            filter.set(&key);
667            self.count.fetch_add(1, Ordering::Relaxed);
668        }
669        *self.filter.write() = filter;
670    }
671
672    /// 获取当前元素计数
673    pub fn count(&self) -> u64 {
674        self.count.load(Ordering::Relaxed)
675    }
676}
677
678// ─── M2-T10:互斥锁防护 ────────────────────────────────────────────
679
680/// 互斥锁击穿防护
681///
682/// 按 key 互斥锁,仅允许一个请求查库回填。
683pub struct CacheMutexGuard {
684    mutexes: parking_lot::Mutex<HashMap<String, Arc<tokio::sync::Mutex<()>>>>,
685}
686
687impl CacheMutexGuard {
688    /// 创建互斥锁防护
689    pub fn new() -> Self {
690        Self {
691            mutexes: parking_lot::Mutex::new(HashMap::new()),
692        }
693    }
694
695    /// 获取 key 的互斥锁 Arc(调用方自行 lock)
696    pub fn get_mutex(&self, key: &str) -> Arc<tokio::sync::Mutex<()>> {
697        let mut map = self.mutexes.lock();
698        map.entry(key.to_string())
699            .or_insert_with(|| Arc::new(tokio::sync::Mutex::new(())))
700            .clone()
701    }
702
703    /// 在 key 互斥锁保护下执行闭包
704    pub async fn with_guard<F, R>(&self, key: &str, f: F) -> R
705    where
706        F: std::future::Future<Output = R>,
707    {
708        let mutex = self.get_mutex(key);
709        let _guard = mutex.lock().await;
710        f.await
711    }
712}
713
714impl Default for CacheMutexGuard {
715    fn default() -> Self {
716        Self::new()
717    }
718}
719
720// ─── M2-T11:随机 TTL 雪崩防护 ─────────────────────────────────────
721
722/// 随机 TTL 雪崩防护
723///
724/// 抖动范围默认基础 TTL 的 ±20%,安全随机源避免抖动可预测。
725pub struct RandomTtlJitter;
726
727impl RandomTtlJitter {
728    /// 计算 TTL 抖动
729    ///
730    /// `base_ttl × (1 ± jitter_range × random)`,random 使用 rand crate 安全随机源。
731    /// 默认 jitter_range = 0.2(±20%)。
732    pub fn jitter(base_ttl: Duration, jitter_range: f64) -> Duration {
733        use rand::Rng;
734        let mut rng = rand::thread_rng();
735        let random: f64 = rng.gen_range(-1.0..=1.0);
736        let factor = 1.0 + jitter_range * random;
737        let jittered_ms = (base_ttl.as_millis() as f64 * factor) as u64;
738        Duration::from_millis(jittered_ms.max(1))
739    }
740
741    /// 使用默认 ±20% 抖动
742    pub fn default_jitter(base_ttl: Duration) -> Duration {
743        Self::jitter(base_ttl, 0.2)
744    }
745}
746
747#[cfg(test)]
748mod tests {
749    use super::*;
750
751    // ─── M2-T14.1:RedisPubSubInvalidationBus 测试 ─────────────────
752
753    #[test]
754    fn test_redis_pubsub_serialize_message_key() {
755        let msg = InvalidationMessage::InvalidateKey("user:42".to_string());
756        let json = RedisPubSubInvalidationBus::serialize_message(&msg, "instance-1");
757        assert!(json.contains("\"type\":\"key\""));
758        assert!(json.contains("\"key\":\"user:42\""));
759        assert!(json.contains("\"src\":\"instance-1\""));
760        assert!(json.len() <= 1024, "消息应 ≤1KB: {} bytes", json.len());
761    }
762
763    #[test]
764    fn test_redis_pubsub_serialize_message_table() {
765        let msg = InvalidationMessage::InvalidateTable("users".to_string());
766        let json = RedisPubSubInvalidationBus::serialize_message(&msg, "instance-1");
767        assert!(json.contains("\"type\":\"table\""));
768        assert!(json.contains("\"table\":\"users\""));
769        assert!(json.len() <= 1024);
770    }
771
772    #[test]
773    fn test_redis_pubsub_serialize_message_all() {
774        let msg = InvalidationMessage::InvalidateAll;
775        let json = RedisPubSubInvalidationBus::serialize_message(&msg, "instance-1");
776        assert!(json.contains("\"type\":\"all\""));
777        assert!(json.len() <= 1024);
778    }
779
780    #[test]
781    fn test_redis_pubsub_deserialize_skips_self() {
782        let msg = InvalidationMessage::InvalidateKey("user:42".to_string());
783        let json = RedisPubSubInvalidationBus::serialize_message(&msg, "instance-1");
784        // 自回环应跳过
785        let result = RedisPubSubInvalidationBus::deserialize_message(&json, "instance-1");
786        assert!(result.is_none(), "应跳过自回环");
787    }
788
789    #[test]
790    fn test_redis_pubsub_deserialize_other_instance() {
791        let msg = InvalidationMessage::InvalidateKey("user:42".to_string());
792        let json = RedisPubSubInvalidationBus::serialize_message(&msg, "instance-1");
793        // 其他实例应接收
794        let result = RedisPubSubInvalidationBus::deserialize_message(&json, "instance-2");
795        assert!(result.is_some(), "应接收其他实例消息");
796    }
797
798    #[test]
799    fn test_redis_pubsub_disconnected_publish() {
800        let bus = RedisPubSubInvalidationBus::disconnected("instance-1");
801        // 断连状态 publish 不应 panic
802        bus.publish(InvalidationMessage::InvalidateAll);
803    }
804
805    #[test]
806    fn test_redis_pubsub_subscribe_drain() {
807        let bus = RedisPubSubInvalidationBus::disconnected("instance-1");
808        bus.push_received(InvalidationMessage::InvalidateTable("users".to_string()));
809        bus.push_received(InvalidationMessage::InvalidateAll);
810        let messages: Vec<_> = bus.subscribe().collect();
811        assert_eq!(messages.len(), 2);
812        // 再次 subscribe 应为空
813        let messages2: Vec<_> = bus.subscribe().collect();
814        assert_eq!(messages2.len(), 0);
815    }
816
817    // ─── M2-T14.2:GossipInvalidationBus 测试 ──────────────────────
818
819    #[test]
820    fn test_gossip_publish_and_subscribe() {
821        let bus = GossipInvalidationBus::new(
822            vec![NodeAddr::new("127.0.0.1", 8080)],
823            b"secret-key".to_vec(),
824            "instance-1",
825        );
826        bus.publish(InvalidationMessage::InvalidateTable("users".to_string()));
827        bus.publish(InvalidationMessage::InvalidateAll);
828        let messages: Vec<_> = bus.subscribe().collect();
829        assert_eq!(messages.len(), 2);
830    }
831
832    #[test]
833    fn test_gossip_hmac_authentication() {
834        let bus = GossipInvalidationBus::new(
835            vec![NodeAddr::new("127.0.0.1", 8080)],
836            b"secret-key".to_vec(),
837            "instance-1",
838        );
839        let msg = InvalidationMessage::InvalidateKey("user:42".to_string());
840        let tag = bus.compute_hmac(&msg);
841        assert!(bus.verify_hmac(&msg, &tag));
842        // 错误的 tag 应拒绝
843        assert!(!bus.verify_hmac(&msg, &[0u8; 32]));
844    }
845
846    #[test]
847    fn test_gossip_receive_dedup() {
848        let bus = GossipInvalidationBus::new(
849            vec![NodeAddr::new("127.0.0.1", 8080)],
850            b"secret-key".to_vec(),
851            "instance-1",
852        );
853        let msg = InvalidationMessage::InvalidateKey("user:42".to_string());
854        let tag = bus.compute_hmac(&msg);
855        // 第一次接收应成功
856        assert!(bus.receive(msg.clone(), 1, &tag));
857        // 重复消息应被去重
858        assert!(!bus.receive(msg, 1, &tag));
859    }
860
861    #[test]
862    fn test_gossip_receive_unauthenticated() {
863        let bus = GossipInvalidationBus::new(
864            vec![NodeAddr::new("127.0.0.1", 8080)],
865            b"secret-key".to_vec(),
866            "instance-1",
867        );
868        let msg = InvalidationMessage::InvalidateKey("user:42".to_string());
869        // 错误的 HMAC 应拒绝
870        assert!(!bus.receive(msg, 1, &[0u8; 32]));
871    }
872
873    // ─── M2-T14.3:WriteBehindQueue 测试 ───────────────────────────
874
875    #[test]
876    fn test_write_behind_config_default() {
877        let config = WriteBehindConfig::default();
878        assert_eq!(config.batch_size, 100);
879        assert_eq!(config.flush_interval, Duration::from_millis(100));
880        assert!(config.fallback_to_sync);
881    }
882
883    #[test]
884    fn test_write_behind_config_builder() {
885        let config = WriteBehindConfig::builder()
886            .batch_size(50)
887            .flush_interval(Duration::from_millis(200))
888            .fallback_to_sync(false)
889            .build();
890        assert_eq!(config.batch_size, 50);
891        assert_eq!(config.flush_interval, Duration::from_millis(200));
892        assert!(!config.fallback_to_sync);
893    }
894
895    #[test]
896    fn test_write_op_new() {
897        let op = WriteOp::new(WriteOpType::Insert, "users", Value::I64(42));
898        assert_eq!(op.op_type, WriteOpType::Insert);
899        assert_eq!(op.table, "users");
900        assert_eq!(op.pk, Value::I64(42));
901        assert_eq!(op.sequence, 0);
902    }
903
904    #[test]
905    fn test_write_behind_queue_enqueue_and_drain() {
906        let temp_dir = std::env::temp_dir().join("sz-orm-test-wal");
907        let _ = std::fs::remove_dir_all(&temp_dir);
908        let config = WriteBehindConfig::builder()
909            .batch_size(10)
910            .wal_path(temp_dir.join("test.log"))
911            .build();
912        let queue = WriteBehindQueue::new(config).unwrap();
913
914        // 入队 3 条
915        for i in 0..3 {
916            let op = WriteOp::new(WriteOpType::Update, "users", Value::I64(i));
917            queue.enqueue(op).unwrap();
918        }
919
920        assert_eq!(queue.pending_count(), 3);
921
922        // 批量取出
923        let batch = queue.drain_batch();
924        assert_eq!(batch.len(), 3);
925        // 按 sequence 排序
926        assert!(batch.windows(2).all(|w| w[0].sequence <= w[1].sequence));
927
928        // 清理
929        let _ = std::fs::remove_dir_all(&temp_dir);
930    }
931
932    #[test]
933    fn test_write_behind_queue_replay() {
934        let temp_dir = std::env::temp_dir().join("sz-orm-test-wal-replay");
935        let _ = std::fs::remove_dir_all(&temp_dir);
936        let config = WriteBehindConfig::builder()
937            .batch_size(10)
938            .wal_path(temp_dir.join("test.log"))
939            .build();
940        let queue = WriteBehindQueue::new(config).unwrap();
941
942        for i in 0..5 {
943            let op = WriteOp::new(WriteOpType::Insert, "orders", Value::I64(i)).with_data(vec![(
944                "status".to_string(),
945                Value::String("pending".to_string()),
946            )]);
947            queue.enqueue(op).unwrap();
948        }
949
950        // 回放
951        let replayed = queue.replay().unwrap();
952        assert_eq!(replayed.len(), 5);
953        // 按 sequence 排序
954        assert!(replayed.windows(2).all(|w| w[0].sequence <= w[1].sequence));
955
956        // 清理
957        let _ = std::fs::remove_dir_all(&temp_dir);
958    }
959
960    // ─── M2-T14.4:BloomFilterGuard + MutexGuard + RandomTtlJitter ─
961
962    #[test]
963    fn test_bloom_filter_basic() {
964        let guard = BloomFilterGuard::new(1000, 0.01);
965        guard.add("user:1");
966        guard.add("user:2");
967        assert!(guard.might_contain("user:1"));
968        assert!(guard.might_contain("user:2"));
969        assert_eq!(guard.count(), 2);
970    }
971
972    #[test]
973    fn test_bloom_filter_false_positive_rate() {
974        let guard = BloomFilterGuard::new(10_000, 0.01);
975        for i in 0..1000 {
976            guard.add(&format!("user:{}", i));
977        }
978        let mut false_positives = 0;
979        for i in 1000..2000 {
980            if guard.might_contain(&format!("user:{}", i)) {
981                false_positives += 1;
982            }
983        }
984        let fp_rate = false_positives as f64 / 1000.0;
985        assert!(fp_rate < 0.05, "假阳性率应 < 5%(实际 {})", fp_rate);
986    }
987
988    #[tokio::test]
989    async fn test_cache_mutex_guard() {
990        let guard = CacheMutexGuard::new();
991        guard
992            .with_guard("user:42", async {
993                // 互斥锁保护下执行
994            })
995            .await;
996        guard
997            .with_guard("user:43", async {
998                // 不同 key 不互斥
999            })
1000            .await;
1001    }
1002
1003    #[test]
1004    fn test_random_ttl_jitter_range() {
1005        let base = Duration::from_millis(1000);
1006        for _ in 0..100 {
1007            let jittered = RandomTtlJitter::default_jitter(base);
1008            let ms = jittered.as_millis();
1009            // ±20% 范围:800..=1200
1010            assert!(
1011                (800..=1200).contains(&ms),
1012                "TTL 抖动应在 ±20% 范围内: {}ms",
1013                ms
1014            );
1015        }
1016    }
1017
1018    #[test]
1019    fn test_consistency_level_default() {
1020        assert_eq!(ConsistencyLevel::default(), ConsistencyLevel::Eventual);
1021    }
1022
1023    #[test]
1024    fn test_crc64() {
1025        let crc1 = crc64(b"hello");
1026        let crc2 = crc64(b"hello");
1027        assert_eq!(crc1, crc2);
1028        let crc3 = crc64(b"world");
1029        assert_ne!(crc1, crc3);
1030    }
1031}