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    /// 获取加密器(v4.8.0 修复 M-9)
588    ///
589    /// 修复前:非 UTF-8 密钥回退到公开字面量 `"default-key"`——该回退密钥
590    /// 是公开已知的,任何知情者可直接解密缓存数据(白帽报告 M-9)。
591    /// 修复后:非 UTF-8 密钥回退为**进程级随机密钥**(启动时生成一次),
592    /// 缓存数据在回退场景下不可被公开密钥解密(代价:旧缓存数据不可恢复,
593    /// 安全优先于可用性)。
594    fn crypter(&self) -> sz_orm_crypto::AesGcmCrypter {
595        match std::str::from_utf8(&self.encryption_key) {
596            Ok(s) => sz_orm_crypto::AesGcmCrypter::from_key_str(s),
597            Err(_) => {
598                static FALLBACK_KEY: std::sync::OnceLock<[u8; 32]> = std::sync::OnceLock::new();
599                let key = FALLBACK_KEY.get_or_init(|| {
600                    use rand::RngCore;
601                    let mut k = [0u8; 32];
602                    rand::rngs::OsRng.fill_bytes(&mut k);
603                    k
604                });
605                sz_orm_crypto::AesGcmCrypter::new(key)
606            }
607        }
608    }
609
610    fn encrypt(&self, data: &[u8]) -> Vec<u8> {
611        self.crypter()
612            .encrypt_with_aad(data, &[])
613            // M-9 修复:加密失败不再回退明文(明文落盘=加密失效),
614            // 返回空(数据按损坏处理,不明文泄露)
615            .unwrap_or_default()
616    }
617
618    fn decrypt(&self, data: &[u8]) -> Vec<u8> {
619        self.crypter()
620            .decrypt_with_aad(data, &[])
621            // 解密失败(密钥变更/数据损坏)返回空 → 上层按缓存 miss 处理
622            .unwrap_or_default()
623    }
624}
625
626fn crc64(data: &[u8]) -> u64 {
627    let mut crc: u64 = 0;
628    for &byte in data {
629        crc ^= byte as u64;
630        for _ in 0..8 {
631            if crc & 1 != 0 {
632                crc = (crc >> 1) ^ 0xC96E_8607_EAFC_E6CD;
633            } else {
634                crc >>= 1;
635            }
636        }
637    }
638    crc
639}
640
641// ─── M2-T9:布隆过滤器防护 ─────────────────────────────────────────
642
643/// 布隆过滤器击穿防护
644///
645/// 假阳性率 ≤ 1% 可配置,超容量自动重建。
646/// v4.7.0 双实现合并:内部使用公共 `crate::bloom::BloomFilter`(原 bloomfilter crate 依赖已移除)。
647pub struct BloomFilterGuard {
648    filter: crate::bloom::BloomFilter,
649    count: AtomicU64,
650}
651
652impl BloomFilterGuard {
653    /// 创建布隆过滤器
654    pub fn new(capacity: usize, false_positive_rate: f64) -> Self {
655        let filter = crate::bloom::BloomFilter::new(capacity, false_positive_rate);
656        Self {
657            filter,
658            count: AtomicU64::new(0),
659        }
660    }
661
662    /// 使用默认配置创建(容量 100000,假阳性率 0.01)
663    pub fn default_config() -> Self {
664        Self::new(100_000, 0.01)
665    }
666
667    /// 添加 key(容量满时拒绝写入——击穿防护场景漏判仅导致多查 DB,安全降级)
668    pub fn add(&self, key: &str) {
669        let _ = self.filter.add(key);
670        self.count.fetch_add(1, Ordering::Relaxed);
671    }
672
673    /// 判断是否可能存在(假阳性 ≤ false_positive_rate)
674    pub fn might_contain(&self, key: &str) -> bool {
675        self.filter.might_contain(key)
676    }
677
678    /// 重建布隆过滤器(超容量时调用)
679    ///
680    /// 公共 `BloomFilter` 内部可变(RwLock),`&self` 即可清空重填。
681    pub fn rebuild(&self, keys: impl Iterator<Item = String>) {
682        self.filter.clear();
683        for key in keys {
684            let _ = self.filter.add(&key);
685            self.count.fetch_add(1, Ordering::Relaxed);
686        }
687    }
688
689    /// 获取当前元素计数
690    pub fn count(&self) -> u64 {
691        self.count.load(Ordering::Relaxed)
692    }
693}
694
695// ─── M2-T10:互斥锁防护 ────────────────────────────────────────────
696
697/// 互斥锁击穿防护
698///
699/// 按 key 互斥锁,仅允许一个请求查库回填。
700pub struct CacheMutexGuard {
701    mutexes: parking_lot::Mutex<HashMap<String, Arc<tokio::sync::Mutex<()>>>>,
702}
703
704impl CacheMutexGuard {
705    /// 创建互斥锁防护
706    pub fn new() -> Self {
707        Self {
708            mutexes: parking_lot::Mutex::new(HashMap::new()),
709        }
710    }
711
712    /// 获取 key 的互斥锁 Arc(调用方自行 lock)
713    pub fn get_mutex(&self, key: &str) -> Arc<tokio::sync::Mutex<()>> {
714        let mut map = self.mutexes.lock();
715        map.entry(key.to_string())
716            .or_insert_with(|| Arc::new(tokio::sync::Mutex::new(())))
717            .clone()
718    }
719
720    /// 在 key 互斥锁保护下执行闭包
721    pub async fn with_guard<F, R>(&self, key: &str, f: F) -> R
722    where
723        F: std::future::Future<Output = R>,
724    {
725        let mutex = self.get_mutex(key);
726        let _guard = mutex.lock().await;
727        f.await
728    }
729}
730
731impl Default for CacheMutexGuard {
732    fn default() -> Self {
733        Self::new()
734    }
735}
736
737// ─── M2-T11:随机 TTL 雪崩防护 ─────────────────────────────────────
738
739/// 随机 TTL 雪崩防护
740///
741/// 抖动范围默认基础 TTL 的 ±20%,安全随机源避免抖动可预测。
742pub struct RandomTtlJitter;
743
744impl RandomTtlJitter {
745    /// 计算 TTL 抖动
746    ///
747    /// `base_ttl × (1 ± jitter_range × random)`,random 使用 rand crate 安全随机源。
748    /// 默认 jitter_range = 0.2(±20%)。
749    pub fn jitter(base_ttl: Duration, jitter_range: f64) -> Duration {
750        use rand::Rng;
751        let mut rng = rand::thread_rng();
752        let random: f64 = rng.gen_range(-1.0..=1.0);
753        let factor = 1.0 + jitter_range * random;
754        let jittered_ms = (base_ttl.as_millis() as f64 * factor) as u64;
755        Duration::from_millis(jittered_ms.max(1))
756    }
757
758    /// 使用默认 ±20% 抖动
759    pub fn default_jitter(base_ttl: Duration) -> Duration {
760        Self::jitter(base_ttl, 0.2)
761    }
762}
763
764#[cfg(test)]
765mod tests {
766    use super::*;
767
768    // ─── M2-T14.1:RedisPubSubInvalidationBus 测试 ─────────────────
769
770    #[test]
771    fn test_redis_pubsub_serialize_message_key() {
772        let msg = InvalidationMessage::InvalidateKey("user:42".to_string());
773        let json = RedisPubSubInvalidationBus::serialize_message(&msg, "instance-1");
774        assert!(json.contains("\"type\":\"key\""));
775        assert!(json.contains("\"key\":\"user:42\""));
776        assert!(json.contains("\"src\":\"instance-1\""));
777        assert!(json.len() <= 1024, "消息应 ≤1KB: {} bytes", json.len());
778    }
779
780    #[test]
781    fn test_redis_pubsub_serialize_message_table() {
782        let msg = InvalidationMessage::InvalidateTable("users".to_string());
783        let json = RedisPubSubInvalidationBus::serialize_message(&msg, "instance-1");
784        assert!(json.contains("\"type\":\"table\""));
785        assert!(json.contains("\"table\":\"users\""));
786        assert!(json.len() <= 1024);
787    }
788
789    #[test]
790    fn test_redis_pubsub_serialize_message_all() {
791        let msg = InvalidationMessage::InvalidateAll;
792        let json = RedisPubSubInvalidationBus::serialize_message(&msg, "instance-1");
793        assert!(json.contains("\"type\":\"all\""));
794        assert!(json.len() <= 1024);
795    }
796
797    #[test]
798    fn test_redis_pubsub_deserialize_skips_self() {
799        let msg = InvalidationMessage::InvalidateKey("user:42".to_string());
800        let json = RedisPubSubInvalidationBus::serialize_message(&msg, "instance-1");
801        // 自回环应跳过
802        let result = RedisPubSubInvalidationBus::deserialize_message(&json, "instance-1");
803        assert!(result.is_none(), "应跳过自回环");
804    }
805
806    #[test]
807    fn test_redis_pubsub_deserialize_other_instance() {
808        let msg = InvalidationMessage::InvalidateKey("user:42".to_string());
809        let json = RedisPubSubInvalidationBus::serialize_message(&msg, "instance-1");
810        // 其他实例应接收
811        let result = RedisPubSubInvalidationBus::deserialize_message(&json, "instance-2");
812        assert!(result.is_some(), "应接收其他实例消息");
813    }
814
815    #[test]
816    fn test_redis_pubsub_disconnected_publish() {
817        let bus = RedisPubSubInvalidationBus::disconnected("instance-1");
818        // 断连状态 publish 不应 panic
819        bus.publish(InvalidationMessage::InvalidateAll);
820    }
821
822    #[test]
823    fn test_redis_pubsub_subscribe_drain() {
824        let bus = RedisPubSubInvalidationBus::disconnected("instance-1");
825        bus.push_received(InvalidationMessage::InvalidateTable("users".to_string()));
826        bus.push_received(InvalidationMessage::InvalidateAll);
827        let messages: Vec<_> = bus.subscribe().collect();
828        assert_eq!(messages.len(), 2);
829        // 再次 subscribe 应为空
830        let messages2: Vec<_> = bus.subscribe().collect();
831        assert_eq!(messages2.len(), 0);
832    }
833
834    // ─── M2-T14.2:GossipInvalidationBus 测试 ──────────────────────
835
836    #[test]
837    fn test_gossip_publish_and_subscribe() {
838        let bus = GossipInvalidationBus::new(
839            vec![NodeAddr::new("127.0.0.1", 8080)],
840            b"secret-key".to_vec(),
841            "instance-1",
842        );
843        bus.publish(InvalidationMessage::InvalidateTable("users".to_string()));
844        bus.publish(InvalidationMessage::InvalidateAll);
845        let messages: Vec<_> = bus.subscribe().collect();
846        assert_eq!(messages.len(), 2);
847    }
848
849    #[test]
850    fn test_gossip_hmac_authentication() {
851        let bus = GossipInvalidationBus::new(
852            vec![NodeAddr::new("127.0.0.1", 8080)],
853            b"secret-key".to_vec(),
854            "instance-1",
855        );
856        let msg = InvalidationMessage::InvalidateKey("user:42".to_string());
857        let tag = bus.compute_hmac(&msg);
858        assert!(bus.verify_hmac(&msg, &tag));
859        // 错误的 tag 应拒绝
860        assert!(!bus.verify_hmac(&msg, &[0u8; 32]));
861    }
862
863    #[test]
864    fn test_gossip_receive_dedup() {
865        let bus = GossipInvalidationBus::new(
866            vec![NodeAddr::new("127.0.0.1", 8080)],
867            b"secret-key".to_vec(),
868            "instance-1",
869        );
870        let msg = InvalidationMessage::InvalidateKey("user:42".to_string());
871        let tag = bus.compute_hmac(&msg);
872        // 第一次接收应成功
873        assert!(bus.receive(msg.clone(), 1, &tag));
874        // 重复消息应被去重
875        assert!(!bus.receive(msg, 1, &tag));
876    }
877
878    #[test]
879    fn test_gossip_receive_unauthenticated() {
880        let bus = GossipInvalidationBus::new(
881            vec![NodeAddr::new("127.0.0.1", 8080)],
882            b"secret-key".to_vec(),
883            "instance-1",
884        );
885        let msg = InvalidationMessage::InvalidateKey("user:42".to_string());
886        // 错误的 HMAC 应拒绝
887        assert!(!bus.receive(msg, 1, &[0u8; 32]));
888    }
889
890    // ─── M2-T14.3:WriteBehindQueue 测试 ───────────────────────────
891
892    #[test]
893    fn test_write_behind_config_default() {
894        let config = WriteBehindConfig::default();
895        assert_eq!(config.batch_size, 100);
896        assert_eq!(config.flush_interval, Duration::from_millis(100));
897        assert!(config.fallback_to_sync);
898    }
899
900    #[test]
901    fn test_write_behind_config_builder() {
902        let config = WriteBehindConfig::builder()
903            .batch_size(50)
904            .flush_interval(Duration::from_millis(200))
905            .fallback_to_sync(false)
906            .build();
907        assert_eq!(config.batch_size, 50);
908        assert_eq!(config.flush_interval, Duration::from_millis(200));
909        assert!(!config.fallback_to_sync);
910    }
911
912    #[test]
913    fn test_write_op_new() {
914        let op = WriteOp::new(WriteOpType::Insert, "users", Value::I64(42));
915        assert_eq!(op.op_type, WriteOpType::Insert);
916        assert_eq!(op.table, "users");
917        assert_eq!(op.pk, Value::I64(42));
918        assert_eq!(op.sequence, 0);
919    }
920
921    #[test]
922    fn test_write_behind_queue_enqueue_and_drain() {
923        let temp_dir = std::env::temp_dir().join("sz-orm-test-wal");
924        let _ = std::fs::remove_dir_all(&temp_dir);
925        let config = WriteBehindConfig::builder()
926            .batch_size(10)
927            .wal_path(temp_dir.join("test.log"))
928            .build();
929        let queue = WriteBehindQueue::new(config).unwrap();
930
931        // 入队 3 条
932        for i in 0..3 {
933            let op = WriteOp::new(WriteOpType::Update, "users", Value::I64(i));
934            queue.enqueue(op).unwrap();
935        }
936
937        assert_eq!(queue.pending_count(), 3);
938
939        // 批量取出
940        let batch = queue.drain_batch();
941        assert_eq!(batch.len(), 3);
942        // 按 sequence 排序
943        assert!(batch.windows(2).all(|w| w[0].sequence <= w[1].sequence));
944
945        // 清理
946        let _ = std::fs::remove_dir_all(&temp_dir);
947    }
948
949    #[test]
950    fn test_write_behind_queue_replay() {
951        let temp_dir = std::env::temp_dir().join("sz-orm-test-wal-replay");
952        let _ = std::fs::remove_dir_all(&temp_dir);
953        let config = WriteBehindConfig::builder()
954            .batch_size(10)
955            .wal_path(temp_dir.join("test.log"))
956            .build();
957        let queue = WriteBehindQueue::new(config).unwrap();
958
959        for i in 0..5 {
960            let op = WriteOp::new(WriteOpType::Insert, "orders", Value::I64(i)).with_data(vec![(
961                "status".to_string(),
962                Value::String("pending".to_string()),
963            )]);
964            queue.enqueue(op).unwrap();
965        }
966
967        // 回放
968        let replayed = queue.replay().unwrap();
969        assert_eq!(replayed.len(), 5);
970        // 按 sequence 排序
971        assert!(replayed.windows(2).all(|w| w[0].sequence <= w[1].sequence));
972
973        // 清理
974        let _ = std::fs::remove_dir_all(&temp_dir);
975    }
976
977    // ─── M2-T14.4:BloomFilterGuard + MutexGuard + RandomTtlJitter ─
978
979    #[test]
980    fn test_bloom_filter_basic() {
981        let guard = BloomFilterGuard::new(1000, 0.01);
982        guard.add("user:1");
983        guard.add("user:2");
984        assert!(guard.might_contain("user:1"));
985        assert!(guard.might_contain("user:2"));
986        assert_eq!(guard.count(), 2);
987    }
988
989    #[test]
990    fn test_bloom_filter_false_positive_rate() {
991        let guard = BloomFilterGuard::new(10_000, 0.01);
992        for i in 0..1000 {
993            guard.add(&format!("user:{}", i));
994        }
995        let mut false_positives = 0;
996        for i in 1000..2000 {
997            if guard.might_contain(&format!("user:{}", i)) {
998                false_positives += 1;
999            }
1000        }
1001        let fp_rate = false_positives as f64 / 1000.0;
1002        assert!(fp_rate < 0.05, "假阳性率应 < 5%(实际 {})", fp_rate);
1003    }
1004
1005    #[tokio::test]
1006    async fn test_cache_mutex_guard() {
1007        let guard = CacheMutexGuard::new();
1008        guard
1009            .with_guard("user:42", async {
1010                // 互斥锁保护下执行
1011            })
1012            .await;
1013        guard
1014            .with_guard("user:43", async {
1015                // 不同 key 不互斥
1016            })
1017            .await;
1018    }
1019
1020    #[test]
1021    fn test_random_ttl_jitter_range() {
1022        let base = Duration::from_millis(1000);
1023        for _ in 0..100 {
1024            let jittered = RandomTtlJitter::default_jitter(base);
1025            let ms = jittered.as_millis();
1026            // ±20% 范围:800..=1200
1027            assert!(
1028                (800..=1200).contains(&ms),
1029                "TTL 抖动应在 ±20% 范围内: {}ms",
1030                ms
1031            );
1032        }
1033    }
1034
1035    #[test]
1036    fn test_consistency_level_default() {
1037        assert_eq!(ConsistencyLevel::default(), ConsistencyLevel::Eventual);
1038    }
1039
1040    #[test]
1041    fn test_crc64() {
1042        let crc1 = crc64(b"hello");
1043        let crc2 = crc64(b"hello");
1044        assert_eq!(crc1, crc2);
1045        let crc3 = crc64(b"world");
1046        assert_ne!(crc1, crc3);
1047    }
1048}