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    pub fn batch_size(mut self, size: u32) -> Self {
319        self.batch_size = Some(size);
320        self
321    }
322    pub fn flush_interval(mut self, interval: Duration) -> Self {
323        self.flush_interval = Some(interval);
324        self
325    }
326    pub fn wal_path(mut self, path: PathBuf) -> Self {
327        self.wal_path = Some(path);
328        self
329    }
330    pub fn encryption_key(mut self, key: Vec<u8>) -> Self {
331        self.encryption_key = Some(key);
332        self
333    }
334    pub fn fallback_to_sync(mut self, fallback: bool) -> Self {
335        self.fallback_to_sync = Some(fallback);
336        self
337    }
338    pub fn build(self) -> WriteBehindConfig {
339        WriteBehindConfig {
340            batch_size: self.batch_size.unwrap_or(100),
341            flush_interval: self
342                .flush_interval
343                .unwrap_or_else(|| Duration::from_millis(100)),
344            wal_path: self
345                .wal_path
346                .unwrap_or_else(|| PathBuf::from("wal/sz-orm-wal.log")),
347            encryption_key: self.encryption_key.unwrap_or_default(),
348            fallback_to_sync: self.fallback_to_sync.unwrap_or(true),
349        }
350    }
351}
352
353/// 写操作类型
354#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
355pub enum WriteOpType {
356    Insert,
357    Update,
358    Delete,
359}
360
361/// Write-behind 写操作
362#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
363pub struct WriteOp {
364    /// 操作类型
365    pub op_type: WriteOpType,
366    /// 表名
367    pub table: String,
368    /// 主键值
369    pub pk: Value,
370    /// 变更数据
371    pub data: Vec<(String, Value)>,
372    /// 时间戳
373    pub timestamp: i64,
374    /// 单调递增序列号
375    pub sequence: u64,
376}
377
378impl WriteOp {
379    /// 创建写操作
380    pub fn new(op_type: WriteOpType, table: impl Into<String>, pk: Value) -> Self {
381        Self {
382            op_type,
383            table: table.into(),
384            pk,
385            data: Vec::new(),
386            timestamp: chrono::Utc::now().timestamp(),
387            sequence: 0,
388        }
389    }
390
391    /// 添加变更数据
392    pub fn with_data(mut self, data: Vec<(String, Value)>) -> Self {
393        self.data = data;
394        self
395    }
396}
397
398/// Write-behind 持久化队列
399///
400/// WAL 持久化先于返回成功(宕机不丢数据),
401/// 内存待刷盘队列 + 单调递增序列号。
402pub struct WriteBehindQueue {
403    /// WAL 文件
404    wal: parking_lot::Mutex<WalFile>,
405    /// 内存待刷盘队列
406    pending: crossbeam_queue::ArrayQueue<WriteOp>,
407    /// 单调递增序列号
408    sequence: AtomicU64,
409    /// 配置
410    config: WriteBehindConfig,
411}
412
413impl WriteBehindQueue {
414    /// 创建 Write-behind 队列
415    pub fn new(config: WriteBehindConfig) -> std::io::Result<Self> {
416        let wal = WalFile::open(&config.wal_path, &config.encryption_key)?;
417        let capacity = (config.batch_size * 10) as usize;
418        Ok(Self {
419            wal: parking_lot::Mutex::new(wal),
420            pending: crossbeam_queue::ArrayQueue::new(capacity.max(1024)),
421            sequence: AtomicU64::new(0),
422            config,
423        })
424    }
425
426    /// 入队写操作(WAL 持久化 + 入内存队列,立即返回)
427    ///
428    /// WAL 持久化先于返回成功,保证宕机不丢数据。
429    pub fn enqueue(&self, mut op: WriteOp) -> std::io::Result<()> {
430        op.sequence = self.sequence.fetch_add(1, Ordering::SeqCst);
431        // 1. WAL 持久化(加密 + CRC)
432        self.wal.lock().append(&op)?;
433        // 2. 入内存 pending 队列
434        let _ = self.pending.push(op);
435        // 3. 立即返回成功
436        Ok(())
437    }
438
439    /// 批量取出待刷盘操作
440    pub fn drain_batch(&self) -> Vec<WriteOp> {
441        let batch_size = self.config.batch_size as usize;
442        let mut batch = Vec::with_capacity(batch_size);
443        for _ in 0..batch_size {
444            match self.pending.pop() {
445                Some(op) => batch.push(op),
446                None => break,
447            }
448        }
449        // 按 sequence 排序
450        batch.sort_by_key(|op| op.sequence);
451        batch
452    }
453
454    /// 标记 WAL 已刷盘(截断)
455    pub fn truncate_wal(&self) -> std::io::Result<()> {
456        self.wal.lock().truncate()
457    }
458
459    /// 宕机重启回放 WAL
460    ///
461    /// 读取 WAL 文件,CRC 校验 + 解密,按 sequence 顺序回放未刷盘 WriteOp。
462    pub fn replay(&self) -> std::io::Result<Vec<WriteOp>> {
463        self.wal.lock().read_all()
464    }
465
466    /// 获取配置
467    pub fn config(&self) -> &WriteBehindConfig {
468        &self.config
469    }
470
471    /// 获取待刷盘数量
472    pub fn pending_count(&self) -> usize {
473        self.pending.len()
474    }
475}
476
477// ─── M2-T7:WAL 持久化与加密 ───────────────────────────────────────
478
479/// WAL 文件
480///
481/// 每条记录格式:[4字节长度][加密载荷][8字节CRC]
482struct WalFile {
483    path: PathBuf,
484    encryption_key: Vec<u8>,
485}
486
487impl WalFile {
488    fn open(path: &std::path::Path, encryption_key: &[u8]) -> std::io::Result<Self> {
489        if let Some(parent) = path.parent() {
490            std::fs::create_dir_all(parent)?;
491        }
492        Ok(Self {
493            path: path.to_path_buf(),
494            encryption_key: encryption_key.to_vec(),
495        })
496    }
497
498    fn append(&mut self, op: &WriteOp) -> std::io::Result<()> {
499        let json = serde_json::to_string(op)
500            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))?;
501        let payload = json.as_bytes();
502
503        let encrypted = if self.encryption_key.is_empty() {
504            payload.to_vec()
505        } else {
506            self.encrypt(payload)
507        };
508
509        let crc = crc64(&encrypted);
510
511        let mut record = Vec::with_capacity(4 + encrypted.len() + 8);
512        record.extend_from_slice(&(encrypted.len() as u32).to_le_bytes());
513        record.extend_from_slice(&encrypted);
514        record.extend_from_slice(&crc.to_le_bytes());
515
516        let mut file = std::fs::OpenOptions::new()
517            .create(true)
518            .append(true)
519            .open(&self.path)?;
520        use std::io::Write;
521        file.write_all(&record)?;
522        file.flush()?;
523        Ok(())
524    }
525
526    fn read_all(&self) -> std::io::Result<Vec<WriteOp>> {
527        let data = match std::fs::read(&self.path) {
528            Ok(d) => d,
529            Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(Vec::new()),
530            Err(e) => return Err(e),
531        };
532        let mut ops = Vec::new();
533        let mut pos = 0;
534        while pos + 4 <= data.len() {
535            let len = u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]])
536                as usize;
537            pos += 4;
538            if pos + len + 8 > data.len() {
539                break;
540            }
541            let encrypted = &data[pos..pos + len];
542            pos += len;
543            let expected_crc = u64::from_le_bytes([
544                data[pos],
545                data[pos + 1],
546                data[pos + 2],
547                data[pos + 3],
548                data[pos + 4],
549                data[pos + 5],
550                data[pos + 6],
551                data[pos + 7],
552            ]);
553            pos += 8;
554
555            if crc64(encrypted) != expected_crc {
556                continue;
557            }
558
559            let decrypted = if self.encryption_key.is_empty() {
560                encrypted.to_vec()
561            } else {
562                self.decrypt(encrypted)
563            };
564
565            if let Ok(op) = serde_json::from_slice::<WriteOp>(&decrypted) {
566                ops.push(op);
567            }
568        }
569        ops.sort_by_key(|op| op.sequence);
570        Ok(ops)
571    }
572
573    fn truncate(&mut self) -> std::io::Result<()> {
574        std::fs::write(&self.path, b"")?;
575        Ok(())
576    }
577
578    fn encrypt(&self, data: &[u8]) -> Vec<u8> {
579        let crypter = sz_orm_crypto::AesGcmCrypter::from_key_str(
580            std::str::from_utf8(&self.encryption_key).unwrap_or("default-key"),
581        );
582        crypter
583            .encrypt_with_aad(data, &[])
584            .unwrap_or_else(|_| data.to_vec())
585    }
586
587    fn decrypt(&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            .decrypt_with_aad(data, &[])
593            .unwrap_or_else(|_| data.to_vec())
594    }
595}
596
597fn crc64(data: &[u8]) -> u64 {
598    let mut crc: u64 = 0;
599    for &byte in data {
600        crc ^= byte as u64;
601        for _ in 0..8 {
602            if crc & 1 != 0 {
603                crc = (crc >> 1) ^ 0xC96E_8607_EAFC_E6CD;
604            } else {
605                crc >>= 1;
606            }
607        }
608    }
609    crc
610}
611
612// ─── M2-T9:布隆过滤器防护 ─────────────────────────────────────────
613
614/// 布隆过滤器击穿防护
615///
616/// 假阳性率 ≤ 1% 可配置,超容量自动重建。
617pub struct BloomFilterGuard {
618    filter: parking_lot::RwLock<bloomfilter::Bloom<String>>,
619    capacity: usize,
620    false_positive_rate: f64,
621    count: AtomicU64,
622}
623
624impl BloomFilterGuard {
625    /// 创建布隆过滤器
626    pub fn new(capacity: usize, false_positive_rate: f64) -> Self {
627        let filter = bloomfilter::Bloom::new_for_fp_rate(capacity, false_positive_rate);
628        Self {
629            filter: parking_lot::RwLock::new(filter),
630            capacity,
631            false_positive_rate,
632            count: AtomicU64::new(0),
633        }
634    }
635
636    /// 使用默认配置创建(容量 100000,假阳性率 0.01)
637    pub fn default_config() -> Self {
638        Self::new(100_000, 0.01)
639    }
640
641    /// 添加 key
642    pub fn add(&self, key: &str) {
643        self.filter.write().set(&key.to_string());
644        self.count.fetch_add(1, Ordering::Relaxed);
645    }
646
647    /// 判断是否可能存在(假阳性 ≤ false_positive_rate)
648    pub fn might_contain(&self, key: &str) -> bool {
649        self.filter.read().check(&key.to_string())
650    }
651
652    /// 重建布隆过滤器(超容量时调用)
653    pub fn rebuild(&self, keys: impl Iterator<Item = String>) {
654        let mut filter =
655            bloomfilter::Bloom::new_for_fp_rate(self.capacity, self.false_positive_rate);
656        for key in keys {
657            filter.set(&key);
658            self.count.fetch_add(1, Ordering::Relaxed);
659        }
660        *self.filter.write() = filter;
661    }
662
663    /// 获取当前元素计数
664    pub fn count(&self) -> u64 {
665        self.count.load(Ordering::Relaxed)
666    }
667}
668
669// ─── M2-T10:互斥锁防护 ────────────────────────────────────────────
670
671/// 互斥锁击穿防护
672///
673/// 按 key 互斥锁,仅允许一个请求查库回填。
674pub struct CacheMutexGuard {
675    mutexes: parking_lot::Mutex<HashMap<String, Arc<tokio::sync::Mutex<()>>>>,
676}
677
678impl CacheMutexGuard {
679    /// 创建互斥锁防护
680    pub fn new() -> Self {
681        Self {
682            mutexes: parking_lot::Mutex::new(HashMap::new()),
683        }
684    }
685
686    /// 获取 key 的互斥锁 Arc(调用方自行 lock)
687    pub fn get_mutex(&self, key: &str) -> Arc<tokio::sync::Mutex<()>> {
688        let mut map = self.mutexes.lock();
689        map.entry(key.to_string())
690            .or_insert_with(|| Arc::new(tokio::sync::Mutex::new(())))
691            .clone()
692    }
693
694    /// 在 key 互斥锁保护下执行闭包
695    pub async fn with_guard<F, R>(&self, key: &str, f: F) -> R
696    where
697        F: std::future::Future<Output = R>,
698    {
699        let mutex = self.get_mutex(key);
700        let _guard = mutex.lock().await;
701        f.await
702    }
703}
704
705impl Default for CacheMutexGuard {
706    fn default() -> Self {
707        Self::new()
708    }
709}
710
711// ─── M2-T11:随机 TTL 雪崩防护 ─────────────────────────────────────
712
713/// 随机 TTL 雪崩防护
714///
715/// 抖动范围默认基础 TTL 的 ±20%,安全随机源避免抖动可预测。
716pub struct RandomTtlJitter;
717
718impl RandomTtlJitter {
719    /// 计算 TTL 抖动
720    ///
721    /// `base_ttl × (1 ± jitter_range × random)`,random 使用 rand crate 安全随机源。
722    /// 默认 jitter_range = 0.2(±20%)。
723    pub fn jitter(base_ttl: Duration, jitter_range: f64) -> Duration {
724        use rand::Rng;
725        let mut rng = rand::thread_rng();
726        let random: f64 = rng.gen_range(-1.0..=1.0);
727        let factor = 1.0 + jitter_range * random;
728        let jittered_ms = (base_ttl.as_millis() as f64 * factor) as u64;
729        Duration::from_millis(jittered_ms.max(1))
730    }
731
732    /// 使用默认 ±20% 抖动
733    pub fn default_jitter(base_ttl: Duration) -> Duration {
734        Self::jitter(base_ttl, 0.2)
735    }
736}
737
738#[cfg(test)]
739mod tests {
740    use super::*;
741
742    // ─── M2-T14.1:RedisPubSubInvalidationBus 测试 ─────────────────
743
744    #[test]
745    fn test_redis_pubsub_serialize_message_key() {
746        let msg = InvalidationMessage::InvalidateKey("user:42".to_string());
747        let json = RedisPubSubInvalidationBus::serialize_message(&msg, "instance-1");
748        assert!(json.contains("\"type\":\"key\""));
749        assert!(json.contains("\"key\":\"user:42\""));
750        assert!(json.contains("\"src\":\"instance-1\""));
751        assert!(json.len() <= 1024, "消息应 ≤1KB: {} bytes", json.len());
752    }
753
754    #[test]
755    fn test_redis_pubsub_serialize_message_table() {
756        let msg = InvalidationMessage::InvalidateTable("users".to_string());
757        let json = RedisPubSubInvalidationBus::serialize_message(&msg, "instance-1");
758        assert!(json.contains("\"type\":\"table\""));
759        assert!(json.contains("\"table\":\"users\""));
760        assert!(json.len() <= 1024);
761    }
762
763    #[test]
764    fn test_redis_pubsub_serialize_message_all() {
765        let msg = InvalidationMessage::InvalidateAll;
766        let json = RedisPubSubInvalidationBus::serialize_message(&msg, "instance-1");
767        assert!(json.contains("\"type\":\"all\""));
768        assert!(json.len() <= 1024);
769    }
770
771    #[test]
772    fn test_redis_pubsub_deserialize_skips_self() {
773        let msg = InvalidationMessage::InvalidateKey("user:42".to_string());
774        let json = RedisPubSubInvalidationBus::serialize_message(&msg, "instance-1");
775        // 自回环应跳过
776        let result = RedisPubSubInvalidationBus::deserialize_message(&json, "instance-1");
777        assert!(result.is_none(), "应跳过自回环");
778    }
779
780    #[test]
781    fn test_redis_pubsub_deserialize_other_instance() {
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-2");
786        assert!(result.is_some(), "应接收其他实例消息");
787    }
788
789    #[test]
790    fn test_redis_pubsub_disconnected_publish() {
791        let bus = RedisPubSubInvalidationBus::disconnected("instance-1");
792        // 断连状态 publish 不应 panic
793        bus.publish(InvalidationMessage::InvalidateAll);
794    }
795
796    #[test]
797    fn test_redis_pubsub_subscribe_drain() {
798        let bus = RedisPubSubInvalidationBus::disconnected("instance-1");
799        bus.push_received(InvalidationMessage::InvalidateTable("users".to_string()));
800        bus.push_received(InvalidationMessage::InvalidateAll);
801        let messages: Vec<_> = bus.subscribe().collect();
802        assert_eq!(messages.len(), 2);
803        // 再次 subscribe 应为空
804        let messages2: Vec<_> = bus.subscribe().collect();
805        assert_eq!(messages2.len(), 0);
806    }
807
808    // ─── M2-T14.2:GossipInvalidationBus 测试 ──────────────────────
809
810    #[test]
811    fn test_gossip_publish_and_subscribe() {
812        let bus = GossipInvalidationBus::new(
813            vec![NodeAddr::new("127.0.0.1", 8080)],
814            b"secret-key".to_vec(),
815            "instance-1",
816        );
817        bus.publish(InvalidationMessage::InvalidateTable("users".to_string()));
818        bus.publish(InvalidationMessage::InvalidateAll);
819        let messages: Vec<_> = bus.subscribe().collect();
820        assert_eq!(messages.len(), 2);
821    }
822
823    #[test]
824    fn test_gossip_hmac_authentication() {
825        let bus = GossipInvalidationBus::new(
826            vec![NodeAddr::new("127.0.0.1", 8080)],
827            b"secret-key".to_vec(),
828            "instance-1",
829        );
830        let msg = InvalidationMessage::InvalidateKey("user:42".to_string());
831        let tag = bus.compute_hmac(&msg);
832        assert!(bus.verify_hmac(&msg, &tag));
833        // 错误的 tag 应拒绝
834        assert!(!bus.verify_hmac(&msg, &[0u8; 32]));
835    }
836
837    #[test]
838    fn test_gossip_receive_dedup() {
839        let bus = GossipInvalidationBus::new(
840            vec![NodeAddr::new("127.0.0.1", 8080)],
841            b"secret-key".to_vec(),
842            "instance-1",
843        );
844        let msg = InvalidationMessage::InvalidateKey("user:42".to_string());
845        let tag = bus.compute_hmac(&msg);
846        // 第一次接收应成功
847        assert!(bus.receive(msg.clone(), 1, &tag));
848        // 重复消息应被去重
849        assert!(!bus.receive(msg, 1, &tag));
850    }
851
852    #[test]
853    fn test_gossip_receive_unauthenticated() {
854        let bus = GossipInvalidationBus::new(
855            vec![NodeAddr::new("127.0.0.1", 8080)],
856            b"secret-key".to_vec(),
857            "instance-1",
858        );
859        let msg = InvalidationMessage::InvalidateKey("user:42".to_string());
860        // 错误的 HMAC 应拒绝
861        assert!(!bus.receive(msg, 1, &[0u8; 32]));
862    }
863
864    // ─── M2-T14.3:WriteBehindQueue 测试 ───────────────────────────
865
866    #[test]
867    fn test_write_behind_config_default() {
868        let config = WriteBehindConfig::default();
869        assert_eq!(config.batch_size, 100);
870        assert_eq!(config.flush_interval, Duration::from_millis(100));
871        assert!(config.fallback_to_sync);
872    }
873
874    #[test]
875    fn test_write_behind_config_builder() {
876        let config = WriteBehindConfig::builder()
877            .batch_size(50)
878            .flush_interval(Duration::from_millis(200))
879            .fallback_to_sync(false)
880            .build();
881        assert_eq!(config.batch_size, 50);
882        assert_eq!(config.flush_interval, Duration::from_millis(200));
883        assert!(!config.fallback_to_sync);
884    }
885
886    #[test]
887    fn test_write_op_new() {
888        let op = WriteOp::new(WriteOpType::Insert, "users", Value::I64(42));
889        assert_eq!(op.op_type, WriteOpType::Insert);
890        assert_eq!(op.table, "users");
891        assert_eq!(op.pk, Value::I64(42));
892        assert_eq!(op.sequence, 0);
893    }
894
895    #[test]
896    fn test_write_behind_queue_enqueue_and_drain() {
897        let temp_dir = std::env::temp_dir().join("sz-orm-test-wal");
898        let _ = std::fs::remove_dir_all(&temp_dir);
899        let config = WriteBehindConfig::builder()
900            .batch_size(10)
901            .wal_path(temp_dir.join("test.log"))
902            .build();
903        let queue = WriteBehindQueue::new(config).unwrap();
904
905        // 入队 3 条
906        for i in 0..3 {
907            let op = WriteOp::new(WriteOpType::Update, "users", Value::I64(i));
908            queue.enqueue(op).unwrap();
909        }
910
911        assert_eq!(queue.pending_count(), 3);
912
913        // 批量取出
914        let batch = queue.drain_batch();
915        assert_eq!(batch.len(), 3);
916        // 按 sequence 排序
917        assert!(batch.windows(2).all(|w| w[0].sequence <= w[1].sequence));
918
919        // 清理
920        let _ = std::fs::remove_dir_all(&temp_dir);
921    }
922
923    #[test]
924    fn test_write_behind_queue_replay() {
925        let temp_dir = std::env::temp_dir().join("sz-orm-test-wal-replay");
926        let _ = std::fs::remove_dir_all(&temp_dir);
927        let config = WriteBehindConfig::builder()
928            .batch_size(10)
929            .wal_path(temp_dir.join("test.log"))
930            .build();
931        let queue = WriteBehindQueue::new(config).unwrap();
932
933        for i in 0..5 {
934            let op = WriteOp::new(WriteOpType::Insert, "orders", Value::I64(i)).with_data(vec![(
935                "status".to_string(),
936                Value::String("pending".to_string()),
937            )]);
938            queue.enqueue(op).unwrap();
939        }
940
941        // 回放
942        let replayed = queue.replay().unwrap();
943        assert_eq!(replayed.len(), 5);
944        // 按 sequence 排序
945        assert!(replayed.windows(2).all(|w| w[0].sequence <= w[1].sequence));
946
947        // 清理
948        let _ = std::fs::remove_dir_all(&temp_dir);
949    }
950
951    // ─── M2-T14.4:BloomFilterGuard + MutexGuard + RandomTtlJitter ─
952
953    #[test]
954    fn test_bloom_filter_basic() {
955        let guard = BloomFilterGuard::new(1000, 0.01);
956        guard.add("user:1");
957        guard.add("user:2");
958        assert!(guard.might_contain("user:1"));
959        assert!(guard.might_contain("user:2"));
960        assert_eq!(guard.count(), 2);
961    }
962
963    #[test]
964    fn test_bloom_filter_false_positive_rate() {
965        let guard = BloomFilterGuard::new(10_000, 0.01);
966        for i in 0..1000 {
967            guard.add(&format!("user:{}", i));
968        }
969        let mut false_positives = 0;
970        for i in 1000..2000 {
971            if guard.might_contain(&format!("user:{}", i)) {
972                false_positives += 1;
973            }
974        }
975        let fp_rate = false_positives as f64 / 1000.0;
976        assert!(fp_rate < 0.05, "假阳性率应 < 5%(实际 {})", fp_rate);
977    }
978
979    #[tokio::test]
980    async fn test_cache_mutex_guard() {
981        let guard = CacheMutexGuard::new();
982        guard
983            .with_guard("user:42", async {
984                // 互斥锁保护下执行
985            })
986            .await;
987        guard
988            .with_guard("user:43", async {
989                // 不同 key 不互斥
990            })
991            .await;
992    }
993
994    #[test]
995    fn test_random_ttl_jitter_range() {
996        let base = Duration::from_millis(1000);
997        for _ in 0..100 {
998            let jittered = RandomTtlJitter::default_jitter(base);
999            let ms = jittered.as_millis();
1000            // ±20% 范围:800..=1200
1001            assert!(
1002                (800..=1200).contains(&ms),
1003                "TTL 抖动应在 ±20% 范围内: {}ms",
1004                ms
1005            );
1006        }
1007    }
1008
1009    #[test]
1010    fn test_consistency_level_default() {
1011        assert_eq!(ConsistencyLevel::default(), ConsistencyLevel::Eventual);
1012    }
1013
1014    #[test]
1015    fn test_crc64() {
1016        let crc1 = crc64(b"hello");
1017        let crc2 = crc64(b"hello");
1018        assert_eq!(crc1, crc2);
1019        let crc3 = crc64(b"world");
1020        assert_ne!(crc1, crc3);
1021    }
1022}