Skip to main content

sz_rust_cache_facade/
memcached.rs

1//! Memcached 缓存驱动 — 对齐 PHP `think\cache\driver\Memcached`
2//!
3//! ## PHP 对齐
4//!
5//! PHP `think\cache\driver\Memcached` 通过 `Memcached` 扩展连接 Memcached 服务器,
6//! 提供 KV 缓存操作。本模块通过 `MemcachedBackend` trait 抽象 Memcached 协议命令,
7//! 允许应用层注入真实 backend(如 `memcache` crate 包装)。
8//!
9//! ## 与 Redis 驱动的差异
10//!
11//! | 特性 | Redis | Memcached |
12//! |------|-------|-----------|
13//! | 数据结构 | KV + Set + Hash 等 | 仅 KV |
14//! | TTL | 可选 | 必填(最大 30 天) |
15//! | 持久化 | 支持 RDB/AOF | 不支持 |
16//! | key 前缀 | 服务端无 | 客户端实现 |
17//! | 标签 | 通过 SADD/SMEMBERS | 通过模拟 Set |
18//! | 批量删除 | DEL key1 key2 | 逐个 delete |
19//!
20//! ## 标签模拟
21//!
22//! Memcached 不支持 Set 数据结构,本驱动通过 JSON 序列化模拟标签集合:
23//! - `tag_append`:读取标签 Set JSON → 追加 → 写回
24//! - `tag_items`:读取标签 Set JSON
25//! - `tag_clear`:逐个删除标签内记录的 key
26
27use std::collections::HashMap;
28use std::collections::HashSet;
29use std::time::Duration;
30use std::time::Instant;
31
32use parking_lot::RwLock;
33
34use crate::compute_md5;
35use crate::CacheDriver;
36use crate::CacheError;
37
38/// Memcached value 最大大小(1MB,对齐 Memcached 协议)
39const MEMCACHED_MAX_VALUE_SIZE: usize = 1024 * 1024;
40
41/// Memcached key 最大长度(250 字节,对齐 Memcached 协议)
42const MEMCACHED_MAX_KEY_LEN: usize = 250;
43
44/// Memcached 最大 TTL(30 天,2592000 秒)
45const MEMCACHED_MAX_TTL_SECS: u64 = 30 * 24 * 60 * 60;
46
47/// Memcached 配置(对齐 PHP `think\cache\driver\Memcached` 的 `$options`)
48///
49/// PHP 配置示例:
50/// ```php
51/// 'memcached' => [
52///     'host' => '127.0.0.1',
53///     'port' => 11211,
54///     'expire' => 3600,
55///     'prefix' => '',
56///     'timeout' => 1000,
57///     'weight' => 0,
58/// ],
59/// ```
60#[derive(Debug, Clone)]
61pub struct MemcachedConfig {
62    /// Memcached 主机地址(对齐 PHP `host`)
63    pub host: String,
64    /// Memcached 端口(对齐 PHP `port`,默认 11211)
65    pub port: u16,
66    /// 默认过期时间(对齐 PHP `expire`,`None` 表示使用最大 TTL 30 天)
67    pub expire: Option<Duration>,
68    /// key 前缀(对齐 PHP `prefix`,客户端实现)
69    pub prefix: String,
70    /// tag 前缀(对齐 PHP `tag_prefix`)
71    pub tag_prefix: String,
72    /// 连接超时(对齐 PHP `timeout`,毫秒;`Duration::ZERO` 表示无超时)
73    pub timeout: Duration,
74    /// 服务器权重(对齐 PHP `weight`,多服务器场景使用)
75    pub weight: u32,
76}
77
78impl Default for MemcachedConfig {
79    fn default() -> Self {
80        Self {
81            host: "127.0.0.1".to_string(),
82            port: 11211,
83            expire: None,
84            prefix: String::new(),
85            tag_prefix: "tag:".to_string(),
86            timeout: Duration::ZERO,
87            weight: 0,
88        }
89    }
90}
91
92impl MemcachedConfig {
93    /// 创建带前缀的配置(便捷方法)
94    pub fn with_prefix(prefix: impl Into<String>) -> Self {
95        Self {
96            prefix: prefix.into(),
97            ..Default::default()
98        }
99    }
100
101    /// 创建带默认 TTL 的配置(便捷方法)
102    pub fn with_expire(expire: Duration) -> Self {
103        Self {
104            expire: Some(expire),
105            ..Default::default()
106        }
107    }
108}
109
110/// Memcached 后端 trait(抽象 Memcached 协议命令)
111///
112/// 由于 Rust 端不强制依赖 `memcache`/`memcached` crate,本 trait 抽象 Memcached 命令,
113/// 允许应用层注入真实 backend。
114///
115/// ## 命令映射
116///
117/// | Trait 方法 | Memcached 命令 | PHP Memcached 调用 |
118/// |------------|---------------|-------------------|
119/// | `get` | GET | `$mc->get($key)` |
120/// | `set` | SET(带 TTL) | `$mc->set($key, $value, $ttl)` |
121/// | `delete` | DELETE | `$mc->delete($key)` |
122/// | `increment` | INCR | `$mc->increment($key, $offset)` |
123/// | `decrement` | DECR | `$mc->decrement($key, $offset)` |
124/// | `flush` | FLUSH_ALL | `$mc->flush()` |
125/// | `touch` | TOUCH(设置 TTL) | `$mc->touch($key, $ttl)` |
126pub trait MemcachedBackend: Send + Sync {
127    /// GET 命令 — 读取缓存
128    fn get(&self, key: &str) -> Result<Option<Vec<u8>>, CacheError>;
129
130    /// SET 命令 — 写入缓存(带 TTL)
131    ///
132    /// Memcached 的 SET 必须指定 TTL,若 TTL 为 0 或超过 30 天,
133    /// Memcached 会将其视为 Unix 时间戳。
134    fn set(&self, key: &str, value: Vec<u8>, ttl: Duration) -> Result<(), CacheError>;
135
136    /// DELETE 命令 — 删除缓存
137    ///
138    /// 返回是否删除成功(key 存在且删除成功返回 true)
139    fn delete(&self, key: &str) -> Result<bool, CacheError>;
140
141    /// INCR 命令 — 自增
142    ///
143    /// key 不存在时 Memcached 返回错误(与 Redis 不同)。
144    /// 本 trait 约定:key 不存在时初始化为 0 再 INCR。
145    fn increment(&self, key: &str, step: u64) -> Result<i64, CacheError>;
146
147    /// DECR 命令 — 自减
148    ///
149    /// Memcached 的 DECR 不会变为负数(最小为 0)。
150    fn decrement(&self, key: &str, step: u64) -> Result<i64, CacheError>;
151
152    /// FLUSH_ALL 命令 — 清空所有缓存
153    fn flush(&self) -> Result<(), CacheError>;
154
155    /// TOUCH 命令 — 更新 key 的 TTL(Memcached 1.4.8+)
156    fn touch(&self, key: &str, ttl: Duration) -> Result<bool, CacheError>;
157}
158
159/// Mock Memcached 后端(用 HashMap 模拟 Memcached 行为)
160///
161/// 用于测试和开发环境,不需要真实 Memcached 服务器。
162///
163/// ## 模拟行为
164///
165/// - KV 存储:`HashMap<String, (Vec<u8>, Option<Instant>)>` — value + expires_at
166/// - TTL 过期:`get`/`increment`/`decrement`/`touch` 时检查过期
167/// - INCR:key 不存在时初始化为 0,非数字时返回错误
168/// - DECR:最小为 0(对齐 Memcached 行为)
169type MemcachedKvEntry = (Vec<u8>, Option<Instant>);
170
171/// KV 存储映射:key → (value, expires_at)
172type MemcachedKvMap = HashMap<String, MemcachedKvEntry>;
173
174/// 内存 Mock Memcached 后端
175///
176/// 用于测试和开发环境,不需要真实 Memcached 服务器。
177pub struct MockMemcachedBackend {
178    /// KV 存储:key → (value, expires_at)
179    kv: RwLock<MemcachedKvMap>,
180}
181
182impl Default for MockMemcachedBackend {
183    fn default() -> Self {
184        Self::new()
185    }
186}
187
188impl MockMemcachedBackend {
189    /// 创建空的 Mock Memcached 后端
190    pub fn new() -> Self {
191        Self {
192            kv: RwLock::new(HashMap::new()),
193        }
194    }
195
196    /// 检查 key 是否已过期
197    fn is_expired(expires_at: Option<Instant>) -> bool {
198        match expires_at {
199            Some(exp) => Instant::now() >= exp,
200            None => false,
201        }
202    }
203
204    /// 清理已过期的 key(惰性清理)
205    fn cleanup_expired(kv: &mut HashMap<String, (Vec<u8>, Option<Instant>)>, key: &str) {
206        if let Some((_, Some(exp))) = kv.get(key) {
207            if Self::is_expired(Some(*exp)) {
208                kv.remove(key);
209            }
210        }
211    }
212}
213
214impl MemcachedBackend for MockMemcachedBackend {
215    fn get(&self, key: &str) -> Result<Option<Vec<u8>>, CacheError> {
216        let mut kv = self.kv.write();
217        Self::cleanup_expired(&mut kv, key);
218        Ok(kv.get(key).map(|(v, _)| v.clone()))
219    }
220
221    fn set(&self, key: &str, value: Vec<u8>, ttl: Duration) -> Result<(), CacheError> {
222        if value.len() > MEMCACHED_MAX_VALUE_SIZE {
223            return Err(CacheError::SerializationError(format!(
224                "Memcached value size {} exceeds max {} bytes",
225                value.len(),
226                MEMCACHED_MAX_VALUE_SIZE
227            )));
228        }
229
230        let expires_at = if ttl == Duration::ZERO {
231            None
232        } else {
233            Some(Instant::now() + ttl)
234        };
235
236        let mut kv = self.kv.write();
237        kv.insert(key.to_string(), (value, expires_at));
238        Ok(())
239    }
240
241    fn delete(&self, key: &str) -> Result<bool, CacheError> {
242        let mut kv = self.kv.write();
243        Ok(kv.remove(key).is_some())
244    }
245
246    fn increment(&self, key: &str, step: u64) -> Result<i64, CacheError> {
247        let mut kv = self.kv.write();
248        Self::cleanup_expired(&mut kv, key);
249
250        let current = match kv.get(key) {
251            Some((bytes, exp)) => {
252                if Self::is_expired(*exp) {
253                    kv.remove(key);
254                    0
255                } else {
256                    let s = std::str::from_utf8(bytes).map_err(|e| {
257                        CacheError::DeserializationError(format!(
258                            "increment: value is not valid UTF-8: {}",
259                            e
260                        ))
261                    })?;
262                    s.parse::<i64>().map_err(|e| {
263                        CacheError::DeserializationError(format!(
264                            "increment: value '{}' is not numeric: {}",
265                            s, e
266                        ))
267                    })?
268                }
269            }
270            None => 0,
271        };
272
273        let new_value = current + step as i64;
274        let expires_at = kv
275            .get(key)
276            .and_then(|(_, exp)| *exp)
277            .or_else(|| Some(Instant::now() + Duration::from_secs(MEMCACHED_MAX_TTL_SECS)));
278
279        kv.insert(
280            key.to_string(),
281            (new_value.to_string().into_bytes(), expires_at),
282        );
283        Ok(new_value)
284    }
285
286    fn decrement(&self, key: &str, step: u64) -> Result<i64, CacheError> {
287        let mut kv = self.kv.write();
288        Self::cleanup_expired(&mut kv, key);
289
290        let current = match kv.get(key) {
291            Some((bytes, exp)) => {
292                if Self::is_expired(*exp) {
293                    kv.remove(key);
294                    0
295                } else {
296                    let s = std::str::from_utf8(bytes).map_err(|e| {
297                        CacheError::DeserializationError(format!(
298                            "decrement: value is not valid UTF-8: {}",
299                            e
300                        ))
301                    })?;
302                    s.parse::<i64>().map_err(|e| {
303                        CacheError::DeserializationError(format!(
304                            "decrement: value '{}' is not numeric: {}",
305                            s, e
306                        ))
307                    })?
308                }
309            }
310            None => 0,
311        };
312
313        // Memcached DECR 不会变为负数(最小为 0)
314        let new_value = (current - step as i64).max(0);
315        let expires_at = kv
316            .get(key)
317            .and_then(|(_, exp)| *exp)
318            .or_else(|| Some(Instant::now() + Duration::from_secs(MEMCACHED_MAX_TTL_SECS)));
319
320        kv.insert(
321            key.to_string(),
322            (new_value.to_string().into_bytes(), expires_at),
323        );
324        Ok(new_value)
325    }
326
327    fn flush(&self) -> Result<(), CacheError> {
328        let mut kv = self.kv.write();
329        kv.clear();
330        Ok(())
331    }
332
333    fn touch(&self, key: &str, ttl: Duration) -> Result<bool, CacheError> {
334        let mut kv = self.kv.write();
335        Self::cleanup_expired(&mut kv, key);
336
337        if let Some((_, ref mut exp)) = kv.get_mut(key) {
338            *exp = if ttl == Duration::ZERO {
339                None
340            } else {
341                Some(Instant::now() + ttl)
342            };
343            Ok(true)
344        } else {
345            Ok(false)
346        }
347    }
348}
349
350/// Memcached 缓存驱动(对齐 PHP `think\cache\driver\Memcached`)
351///
352/// 实现 `CacheDriver` trait,通过 `MemcachedBackend` 抽象层操作 Memcached。
353///
354/// ## 标签模拟
355///
356/// 由于 Memcached 不支持 Set 数据结构,标签功能通过 JSON 序列化模拟:
357///
358/// - `tag_append(name, value)`:读取 `tag:<md5(tag)>` 的 JSON 数组 → 追加 → 写回
359/// - `tag_items(tag)`:读取 `tag:<md5(tag)>` 的 JSON 数组
360/// - `tag_clear(keys)`:逐个删除 key
361///
362/// ## 用法
363///
364/// ```ignore
365/// use sz_rust_cache_facade::{Cache, MemcachedCacheDriver, MemcachedConfig, MockMemcachedBackend};
366///
367/// let cache = Cache::new();
368/// cache.register_default(
369///     MemcachedCacheDriver::with_backend(
370///         MemcachedConfig::default(),
371///         Box::new(MockMemcachedBackend::new()),
372///     )
373/// );
374///
375/// cache.set("key", "value", None).unwrap();
376/// assert_eq!(cache.get::<String>("key").unwrap(), Some("value".to_string()));
377/// ```
378pub struct MemcachedCacheDriver {
379    backend: Box<dyn MemcachedBackend>,
380    config: MemcachedConfig,
381}
382
383impl MemcachedCacheDriver {
384    /// 创建 Memcached 缓存驱动(用 Mock backend)
385    ///
386    /// 对齐 PHP `new \think\cache\driver\Memcached($options)`。
387    pub fn new(config: MemcachedConfig) -> Self {
388        Self::with_backend(config, Box::new(MockMemcachedBackend::new()))
389    }
390
391    /// 创建 Memcached 缓存驱动(自定义 backend)
392    ///
393    /// 应用层可注入真实 Memcached backend(如 `memcache::Client` 包装)。
394    pub fn with_backend(config: MemcachedConfig, backend: Box<dyn MemcachedBackend>) -> Self {
395        Self { backend, config }
396    }
397
398    /// 获取配置引用
399    pub fn config(&self) -> &MemcachedConfig {
400        &self.config
401    }
402
403    /// 校验 key 合法性(对齐 Memcached 协议限制)
404    ///
405    /// Memcached key 规则:
406    /// - 长度 ≤ 250 字节
407    /// - 不包含空格和控制字符
408    /// - 不为空
409    fn validate_key(key: &str) -> Result<(), CacheError> {
410        if key.is_empty() {
411            return Err(CacheError::Internal(
412                "Memcached key cannot be empty".to_string(),
413            ));
414        }
415        if key.len() > MEMCACHED_MAX_KEY_LEN {
416            return Err(CacheError::Internal(format!(
417                "Memcached key length {} exceeds max {} bytes",
418                key.len(),
419                MEMCACHED_MAX_KEY_LEN
420            )));
421        }
422        if key.chars().any(|c| c.is_control() || c == ' ') {
423            return Err(CacheError::Internal(format!(
424                "Memcached key contains invalid characters (space or control): {}",
425                key
426            )));
427        }
428        Ok(())
429    }
430
431    /// 将 TTL 转换为 Memcached 兼容的 TTL
432    ///
433    /// Memcached TTL 规则:
434    /// - TTL = 0:永不过期
435    /// - TTL ≤ 60*60*24*30(30天):相对秒数
436    /// - TTL > 60*60*24*30:Unix 时间戳
437    ///
438    /// 本实现始终使用相对秒数(≤ 30 天),超过 30 天的 TTL 截断为 30 天。
439    fn normalize_ttl(ttl: Duration) -> Duration {
440        let max_ttl = Duration::from_secs(MEMCACHED_MAX_TTL_SECS);
441        if ttl > max_ttl {
442            max_ttl
443        } else {
444            ttl
445        }
446    }
447
448    /// 追加 TagSet 数据(模拟 PHP `Driver::append`)
449    ///
450    /// Memcached 不支持 Set,通过 JSON 序列化模拟:
451    /// 1. 读取 `tag:<md5(tag)>` 的 JSON 数组(若不存在则为空数组)
452    /// 2. 追加新 value(去重)
453    /// 3. 写回
454    pub fn append(&self, name: &str, value: &str) -> Result<(), CacheError> {
455        let tag_key = self.get_tag_key(name);
456        let cache_key = self.get_cache_key(&tag_key);
457
458        let mut items: HashSet<String> = self.read_tag_set(&cache_key)?;
459        items.insert(value.to_string());
460
461        self.write_tag_set(&cache_key, &items)
462    }
463
464    /// 获取标签包含的缓存标识(模拟 PHP `getTagItems`)
465    pub fn get_tag_items(&self, tag: &str) -> Result<Vec<String>, CacheError> {
466        let name = self.get_tag_key(tag);
467        let cache_key = self.get_cache_key(&name);
468        let items = self.read_tag_set(&cache_key)?;
469        Ok(items.into_iter().collect())
470    }
471
472    /// 删除缓存标签(模拟 PHP `clearTag`)
473    ///
474    /// Memcached 不支持批量 DEL,逐个删除。
475    pub fn clear_tag(&self, keys: &[&str]) -> Result<(), CacheError> {
476        for key in keys {
477            self.backend.delete(key)?;
478        }
479        Ok(())
480    }
481
482    /// 读取标签集合(JSON 反序列化)
483    fn read_tag_set(&self, cache_key: &str) -> Result<HashSet<String>, CacheError> {
484        match self.backend.get(cache_key)? {
485            Some(bytes) => {
486                if bytes.is_empty() {
487                    return Ok(HashSet::new());
488                }
489                let json_str = std::str::from_utf8(&bytes).map_err(|e| {
490                    CacheError::DeserializationError(format!("tag set is not valid UTF-8: {}", e))
491                })?;
492                let items: Vec<String> = serde_json::from_str(json_str).map_err(|e| {
493                    CacheError::DeserializationError(format!(
494                        "tag set JSON deserialization failed: {}",
495                        e
496                    ))
497                })?;
498                Ok(items.into_iter().collect())
499            }
500            None => Ok(HashSet::new()),
501        }
502    }
503
504    /// 写入标签集合(JSON 序列化)
505    fn write_tag_set(&self, cache_key: &str, items: &HashSet<String>) -> Result<(), CacheError> {
506        let mut vec: Vec<String> = items.iter().cloned().collect();
507        vec.sort(); // 排序确保幂等
508        let json = serde_json::to_string(&vec).map_err(|e| {
509            CacheError::SerializationError(format!("tag set JSON serialization failed: {}", e))
510        })?;
511        let ttl = Duration::from_secs(MEMCACHED_MAX_TTL_SECS);
512        self.backend.set(cache_key, json.into_bytes(), ttl)?;
513        Ok(())
514    }
515}
516
517impl CacheDriver for MemcachedCacheDriver {
518    fn get_raw(&self, key: &str) -> Result<Option<Vec<u8>>, CacheError> {
519        Self::validate_key(key)?;
520        let cache_key = self.get_cache_key(key);
521        self.backend.get(&cache_key)
522    }
523
524    fn set_raw(&self, key: &str, value: Vec<u8>, ttl: Option<Duration>) -> Result<(), CacheError> {
525        Self::validate_key(key)?;
526        let cache_key = self.get_cache_key(key);
527
528        // 对齐 PHP: $expire = is_null($ttl) ? $this->options['expire'] : $ttl
529        let effective_ttl = ttl.or(self.config.expire);
530        // 对齐 Memcached: 必须有 TTL,None → 最大 TTL(30天)
531        let normalized_ttl = effective_ttl
532            .map(Self::normalize_ttl)
533            .unwrap_or_else(|| Duration::from_secs(MEMCACHED_MAX_TTL_SECS));
534
535        self.backend.set(&cache_key, value, normalized_ttl)
536    }
537
538    fn delete(&self, key: &str) -> Result<(), CacheError> {
539        Self::validate_key(key)?;
540        let cache_key = self.get_cache_key(key);
541        self.backend.delete(&cache_key)?;
542        Ok(())
543    }
544
545    fn has(&self, key: &str) -> Result<bool, CacheError> {
546        Self::validate_key(key)?;
547        let cache_key = self.get_cache_key(key);
548        Ok(self.backend.get(&cache_key)?.is_some())
549    }
550
551    fn inc(&self, key: &str, step: i64) -> Result<i64, CacheError> {
552        Self::validate_key(key)?;
553        let cache_key = self.get_cache_key(key);
554
555        if step >= 0 {
556            self.backend.increment(&cache_key, step as u64)
557        } else {
558            // 负数 step 使用 decrement
559            self.backend.decrement(&cache_key, (-step) as u64)
560        }
561    }
562
563    fn dec(&self, key: &str, step: i64) -> Result<i64, CacheError> {
564        self.inc(key, -step)
565    }
566
567    fn clear(&self) -> Result<(), CacheError> {
568        self.backend.flush()
569    }
570
571    fn get_cache_key(&self, name: &str) -> String {
572        if self.config.prefix.is_empty() {
573            name.to_string()
574        } else {
575            format!("{}{}", self.config.prefix, name)
576        }
577    }
578
579    fn get_tag_key(&self, tag: &str) -> String {
580        format!("{}{}", self.config.tag_prefix, compute_md5(tag))
581    }
582}
583
584#[cfg(test)]
585mod tests {
586    use super::*;
587
588    // ========================================================================
589    // 测试组 1: MemcachedConfig
590    // ========================================================================
591
592    #[test]
593    fn test_memcached_config_default() {
594        let config = MemcachedConfig::default();
595        assert_eq!(config.host, "127.0.0.1");
596        assert_eq!(config.port, 11211);
597        assert!(config.expire.is_none());
598        assert!(config.prefix.is_empty());
599        assert_eq!(config.tag_prefix, "tag:");
600    }
601
602    #[test]
603    fn test_memcached_config_with_prefix() {
604        let config = MemcachedConfig::with_prefix("myapp:");
605        assert_eq!(config.prefix, "myapp:");
606        assert_eq!(config.port, 11211);
607    }
608
609    #[test]
610    fn test_memcached_config_with_expire() {
611        let config = MemcachedConfig::with_expire(Duration::from_secs(3600));
612        assert_eq!(config.expire, Some(Duration::from_secs(3600)));
613    }
614
615    // ========================================================================
616    // 测试组 2: MockMemcachedBackend 基本 KV 操作
617    // ========================================================================
618
619    #[test]
620    fn test_mock_backend_set_get() {
621        let backend = MockMemcachedBackend::new();
622        backend
623            .set("key1", b"value1".to_vec(), Duration::from_secs(60))
624            .unwrap();
625        let result = backend.get("key1").unwrap();
626        assert_eq!(result, Some(b"value1".to_vec()));
627    }
628
629    #[test]
630    fn test_mock_backend_get_nonexistent() {
631        let backend = MockMemcachedBackend::new();
632        let result = backend.get("nonexistent").unwrap();
633        assert_eq!(result, None);
634    }
635
636    #[test]
637    fn test_mock_backend_delete() {
638        let backend = MockMemcachedBackend::new();
639        backend
640            .set("key1", b"value1".to_vec(), Duration::from_secs(60))
641            .unwrap();
642        assert!(backend.delete("key1").unwrap());
643        assert!(backend.get("key1").unwrap().is_none());
644    }
645
646    #[test]
647    fn test_mock_backend_delete_nonexistent() {
648        let backend = MockMemcachedBackend::new();
649        assert!(!backend.delete("nonexistent").unwrap());
650    }
651
652    #[test]
653    fn test_mock_backend_flush() {
654        let backend = MockMemcachedBackend::new();
655        backend
656            .set("key1", b"value1".to_vec(), Duration::from_secs(60))
657            .unwrap();
658        backend
659            .set("key2", b"value2".to_vec(), Duration::from_secs(60))
660            .unwrap();
661        backend.flush().unwrap();
662        assert!(backend.get("key1").unwrap().is_none());
663        assert!(backend.get("key2").unwrap().is_none());
664    }
665
666    // ========================================================================
667    // 测试组 3: MockMemcachedBackend TTL 过期
668    // ========================================================================
669
670    #[test]
671    fn test_mock_backend_ttl_expiration() {
672        let backend = MockMemcachedBackend::new();
673        backend
674            .set("key1", b"value1".to_vec(), Duration::from_millis(10))
675            .unwrap();
676        std::thread::sleep(Duration::from_millis(20));
677        assert!(backend.get("key1").unwrap().is_none());
678    }
679
680    #[test]
681    fn test_mock_backend_no_ttl_never_expires() {
682        let backend = MockMemcachedBackend::new();
683        backend
684            .set("key1", b"value1".to_vec(), Duration::ZERO)
685            .unwrap();
686        assert!(backend.get("key1").unwrap().is_some());
687    }
688
689    // ========================================================================
690    // 测试组 4: MockMemcachedBackend increment/decrement
691    // ========================================================================
692
693    #[test]
694    fn test_mock_backend_increment_new_key() {
695        let backend = MockMemcachedBackend::new();
696        let result = backend.increment("counter", 5).unwrap();
697        assert_eq!(result, 5);
698    }
699
700    #[test]
701    fn test_mock_backend_increment_existing_key() {
702        let backend = MockMemcachedBackend::new();
703        backend.increment("counter", 5).unwrap();
704        let result = backend.increment("counter", 3).unwrap();
705        assert_eq!(result, 8);
706    }
707
708    #[test]
709    fn test_mock_backend_decrement() {
710        let backend = MockMemcachedBackend::new();
711        backend.increment("counter", 10).unwrap();
712        let result = backend.decrement("counter", 3).unwrap();
713        assert_eq!(result, 7);
714    }
715
716    #[test]
717    fn test_mock_backend_decrement_not_below_zero() {
718        let backend = MockMemcachedBackend::new();
719        backend.increment("counter", 5).unwrap();
720        let result = backend.decrement("counter", 10).unwrap();
721        assert_eq!(result, 0);
722    }
723
724    #[test]
725    fn test_mock_backend_increment_non_numeric_value() {
726        let backend = MockMemcachedBackend::new();
727        backend
728            .set("text", b"hello".to_vec(), Duration::from_secs(60))
729            .unwrap();
730        let result = backend.increment("text", 1);
731        assert!(result.is_err());
732    }
733
734    // ========================================================================
735    // 测试组 5: MockMemcachedBackend touch
736    // ========================================================================
737
738    #[test]
739    fn test_mock_backend_touch_existing_key() {
740        let backend = MockMemcachedBackend::new();
741        backend
742            .set("key1", b"value1".to_vec(), Duration::from_secs(60))
743            .unwrap();
744        assert!(backend.touch("key1", Duration::from_millis(10)).unwrap());
745        std::thread::sleep(Duration::from_millis(20));
746        assert!(backend.get("key1").unwrap().is_none());
747    }
748
749    #[test]
750    fn test_mock_backend_touch_nonexistent_key() {
751        let backend = MockMemcachedBackend::new();
752        assert!(!backend
753            .touch("nonexistent", Duration::from_secs(60))
754            .unwrap());
755    }
756
757    // ========================================================================
758    // 测试组 6: MockMemcachedBackend value 大小限制
759    // ========================================================================
760
761    #[test]
762    fn test_mock_backend_set_oversized_value() {
763        let backend = MockMemcachedBackend::new();
764        let large_value = vec![0u8; MEMCACHED_MAX_VALUE_SIZE + 1];
765        let result = backend.set("key1", large_value, Duration::from_secs(60));
766        assert!(result.is_err());
767    }
768
769    // ========================================================================
770    // 测试组 7: MemcachedCacheDriver 基本 KV 操作
771    // ========================================================================
772
773    #[test]
774    fn test_driver_set_get_raw() {
775        let driver = MemcachedCacheDriver::new(MemcachedConfig::default());
776        driver
777            .set_raw("key1", b"value1".to_vec(), Some(Duration::from_secs(60)))
778            .unwrap();
779        let result = driver.get_raw("key1").unwrap();
780        assert_eq!(result, Some(b"value1".to_vec()));
781    }
782
783    #[test]
784    fn test_driver_get_nonexistent() {
785        let driver = MemcachedCacheDriver::new(MemcachedConfig::default());
786        let result = driver.get_raw("nonexistent").unwrap();
787        assert_eq!(result, None);
788    }
789
790    #[test]
791    fn test_driver_delete() {
792        let driver = MemcachedCacheDriver::new(MemcachedConfig::default());
793        driver
794            .set_raw("key1", b"value1".to_vec(), Some(Duration::from_secs(60)))
795            .unwrap();
796        driver.delete("key1").unwrap();
797        assert!(driver.get_raw("key1").unwrap().is_none());
798    }
799
800    #[test]
801    fn test_driver_has() {
802        let driver = MemcachedCacheDriver::new(MemcachedConfig::default());
803        assert!(!driver.has("key1").unwrap());
804        driver
805            .set_raw("key1", b"value1".to_vec(), Some(Duration::from_secs(60)))
806            .unwrap();
807        assert!(driver.has("key1").unwrap());
808    }
809
810    #[test]
811    fn test_driver_clear() {
812        let driver = MemcachedCacheDriver::new(MemcachedConfig::default());
813        driver
814            .set_raw("key1", b"value1".to_vec(), Some(Duration::from_secs(60)))
815            .unwrap();
816        driver
817            .set_raw("key2", b"value2".to_vec(), Some(Duration::from_secs(60)))
818            .unwrap();
819        driver.clear().unwrap();
820        assert!(driver.get_raw("key1").unwrap().is_none());
821        assert!(driver.get_raw("key2").unwrap().is_none());
822    }
823
824    // ========================================================================
825    // 测试组 8: MemcachedCacheDriver inc/dec
826    // ========================================================================
827
828    #[test]
829    fn test_driver_inc_new_key() {
830        let driver = MemcachedCacheDriver::new(MemcachedConfig::default());
831        let result = driver.inc("counter", 5).unwrap();
832        assert_eq!(result, 5);
833    }
834
835    #[test]
836    fn test_driver_inc_existing_key() {
837        let driver = MemcachedCacheDriver::new(MemcachedConfig::default());
838        driver.inc("counter", 5).unwrap();
839        let result = driver.inc("counter", 3).unwrap();
840        assert_eq!(result, 8);
841    }
842
843    #[test]
844    fn test_driver_dec() {
845        let driver = MemcachedCacheDriver::new(MemcachedConfig::default());
846        driver.inc("counter", 10).unwrap();
847        let result = driver.dec("counter", 3).unwrap();
848        assert_eq!(result, 7);
849    }
850
851    #[test]
852    fn test_driver_dec_not_below_zero() {
853        let driver = MemcachedCacheDriver::new(MemcachedConfig::default());
854        driver.inc("counter", 5).unwrap();
855        let result = driver.dec("counter", 10).unwrap();
856        assert_eq!(result, 0);
857    }
858
859    // ========================================================================
860    // 测试组 9: MemcachedCacheDriver key 前缀
861    // ========================================================================
862
863    #[test]
864    fn test_driver_get_cache_key_no_prefix() {
865        let driver = MemcachedCacheDriver::new(MemcachedConfig::default());
866        assert_eq!(driver.get_cache_key("mykey"), "mykey");
867    }
868
869    #[test]
870    fn test_driver_get_cache_key_with_prefix() {
871        let config = MemcachedConfig::with_prefix("myapp:");
872        let driver = MemcachedCacheDriver::new(config);
873        assert_eq!(driver.get_cache_key("mykey"), "myapp:mykey");
874    }
875
876    #[test]
877    fn test_driver_set_get_with_prefix() {
878        let config = MemcachedConfig::with_prefix("myapp:");
879        let driver = MemcachedCacheDriver::new(config);
880        driver
881            .set_raw("key1", b"value1".to_vec(), Some(Duration::from_secs(60)))
882            .unwrap();
883        let result = driver.get_raw("key1").unwrap();
884        assert_eq!(result, Some(b"value1".to_vec()));
885    }
886
887    // ========================================================================
888    // 测试组 10: MemcachedCacheDriver key 校验
889    // ========================================================================
890
891    #[test]
892    fn test_driver_validate_key_empty() {
893        assert!(MemcachedCacheDriver::validate_key("").is_err());
894    }
895
896    #[test]
897    fn test_driver_validate_key_with_space() {
898        assert!(MemcachedCacheDriver::validate_key("key with space").is_err());
899    }
900
901    #[test]
902    fn test_driver_validate_key_too_long() {
903        let long_key = "a".repeat(MEMCACHED_MAX_KEY_LEN + 1);
904        assert!(MemcachedCacheDriver::validate_key(&long_key).is_err());
905    }
906
907    #[test]
908    fn test_driver_validate_key_valid() {
909        assert!(MemcachedCacheDriver::validate_key("valid_key_123").is_ok());
910        assert!(MemcachedCacheDriver::validate_key("user:123:session").is_ok());
911    }
912
913    // ========================================================================
914    // 测试组 11: MemcachedCacheDriver TTL 规范化
915    // ========================================================================
916
917    #[test]
918    fn test_driver_normalize_ttl_within_limit() {
919        let ttl = Duration::from_secs(3600);
920        assert_eq!(MemcachedCacheDriver::normalize_ttl(ttl), ttl);
921    }
922
923    #[test]
924    fn test_driver_normalize_ttl_exceeds_limit() {
925        let ttl = Duration::from_secs(MEMCACHED_MAX_TTL_SECS + 100);
926        let normalized = MemcachedCacheDriver::normalize_ttl(ttl);
927        assert_eq!(normalized, Duration::from_secs(MEMCACHED_MAX_TTL_SECS));
928    }
929
930    #[test]
931    fn test_driver_set_with_default_expire() {
932        let config = MemcachedConfig::with_expire(Duration::from_secs(60));
933        let driver = MemcachedCacheDriver::new(config);
934        driver.set_raw("key1", b"value1".to_vec(), None).unwrap();
935        assert!(driver.get_raw("key1").unwrap().is_some());
936    }
937
938    // ========================================================================
939    // 测试组 12: MemcachedCacheDriver 标签模拟
940    // ========================================================================
941
942    #[test]
943    fn test_driver_tag_append_and_items() {
944        let driver = MemcachedCacheDriver::new(MemcachedConfig::default());
945
946        driver.append("mytag", "key1").unwrap();
947        driver.append("mytag", "key2").unwrap();
948        driver.append("mytag", "key3").unwrap();
949
950        let items = driver.get_tag_items("mytag").unwrap();
951        assert_eq!(items.len(), 3);
952        assert!(items.contains(&"key1".to_string()));
953        assert!(items.contains(&"key2".to_string()));
954        assert!(items.contains(&"key3".to_string()));
955    }
956
957    #[test]
958    fn test_driver_tag_append_deduplication() {
959        let driver = MemcachedCacheDriver::new(MemcachedConfig::default());
960
961        driver.append("mytag", "key1").unwrap();
962        driver.append("mytag", "key1").unwrap();
963        driver.append("mytag", "key1").unwrap();
964
965        let items = driver.get_tag_items("mytag").unwrap();
966        assert_eq!(items.len(), 1);
967    }
968
969    #[test]
970    fn test_driver_tag_items_empty() {
971        let driver = MemcachedCacheDriver::new(MemcachedConfig::default());
972        let items = driver.get_tag_items("nonexistent").unwrap();
973        assert!(items.is_empty());
974    }
975
976    #[test]
977    fn test_driver_tag_clear() {
978        let driver = MemcachedCacheDriver::new(MemcachedConfig::default());
979
980        driver
981            .set_raw("key1", b"value1".to_vec(), Some(Duration::from_secs(60)))
982            .unwrap();
983        driver
984            .set_raw("key2", b"value2".to_vec(), Some(Duration::from_secs(60)))
985            .unwrap();
986
987        driver.append("mytag", "key1").unwrap();
988        driver.append("mytag", "key2").unwrap();
989
990        let items = driver.get_tag_items("mytag").unwrap();
991        let item_refs: Vec<&str> = items.iter().map(|s| s.as_str()).collect();
992        driver.clear_tag(&item_refs).unwrap();
993
994        assert!(driver.get_raw("key1").unwrap().is_none());
995        assert!(driver.get_raw("key2").unwrap().is_none());
996    }
997
998    #[test]
999    fn test_driver_tag_clear_empty() {
1000        let driver = MemcachedCacheDriver::new(MemcachedConfig::default());
1001        driver.clear_tag(&[]).unwrap();
1002    }
1003
1004    // ========================================================================
1005    // 测试组 13: MemcachedCacheDriver tag key 生成
1006    // ========================================================================
1007
1008    #[test]
1009    fn test_driver_get_tag_key_uses_md5() {
1010        let driver = MemcachedCacheDriver::new(MemcachedConfig::default());
1011        let tag_key = driver.get_tag_key("mytag");
1012        assert!(tag_key.starts_with("tag:"));
1013        let md5_part = &tag_key["tag:".len()..];
1014        assert_eq!(md5_part.len(), 32);
1015    }
1016
1017    #[test]
1018    fn test_driver_get_tag_key_with_custom_prefix() {
1019        let config = MemcachedConfig {
1020            tag_prefix: "tagset:".to_string(),
1021            ..MemcachedConfig::default()
1022        };
1023        let driver = MemcachedCacheDriver::new(config);
1024        let tag_key = driver.get_tag_key("mytag");
1025        assert!(tag_key.starts_with("tagset:"));
1026    }
1027}