Skip to main content

Crate sz_rust_cache_facade

Crate sz_rust_cache_facade 

Source
Expand description

SZ-Rust Cache facade — 对齐 PHP think\facade\Cache

缓存 facade 模块。

§PHP 对齐

§1. 静态 API 风格(对齐 PHP think\facade\Cache::__callStatic

PHP think\facade\Cache 是 facade,所有静态方法通过 __callStatic 转发到 think\Cache(Manager)实例的对应方法。

Rust 端通过全局 OnceLock<Cache> + Cache::default_instance() 提供“伪静态“ API;调用方也可以创建独立 Cache 实例用于测试隔离。

§2. 驱动管理器(对齐 PHP think\Cache extends Manager

PHP think\Cache 继承 think\Manager,通过 $namespace = '\\think\\cache\\driver\\'createDriver(array $config) 创建驱动实例,并缓存到 $this->drivers[]

Rust 端通过 CacheManager + CacheDriver trait 提供等价能力:

  • register_store(name, driver):注册命名驱动
  • store(name):获取命名驱动
  • default_store():获取默认驱动

§3. 序列化策略(对齐 PHP is_numeric 短路)

PHP think\cache\Driver::serialize($data) 第 612 行:

public function serialize($data): string
{
    if (is_numeric($data)) {
        return (string) $data;
    }
    return serialize($data);
}

PHP think\cache\Driver::unserialize($data) 第 623 行:

public function unserialize($data)
{
    if (is_numeric($data)) {
        return $data;  // ⚠️ 返回 string,而非 int(PHP 源码 bug)
    }
    return unserialize($data);
}

PHP 源码 bug 复刻unserializeis_numeric 的值返回 string, 而非还原为 int。本模块通过 CacheValue::Number 标记 + get::<String>() 返回 string 来复刻此行为。

§4. remember 锁机制(对齐 PHP think\cache\Driver::remember

PHP think\cache\Driver::remember 第 287-310 行:

public function remember(string $name, callable $callback, $expire = null)
{
    if (($data = $this->get($name)) !== null) {
        return $data;
    }

    $lockName = $name . '_lock';

    // 抢锁
    if ($this->has($lockName)) {
        // 等待锁释放,200ms 轮询,5 秒超时
        $startTime = microtime(true);
        while ($this->has($lockName) && microtime(true) - $startTime < 5) {
            usleep(200000);
        }
        // 锁释放后再次读取
        if ($this->has($lockName)) {
            // 超时仍未释放,直接调用 callback(防止永久阻塞)
            return $callback();
        }
        $data = $this->get($name);
        if ($data !== null) {
            return $data;
        }
    }

    // 抢到锁(无 TTL,PHP 源码 bug:锁不设过期时间)
    $this->set($lockName, 1);

    try {
        $data = $callback();
        $this->set($name, $data, $expire);
    } finally {
        $this->delete($lockName);
    }

    return $data;
}

PHP 源码 bug 复刻

  1. 锁 key 无 TTL(若进程崩溃,锁永久存在 → 死锁)
  2. has() + get() !== null 双查(先 hasget,存在 TOCTOU)

§5. push 上限 1000 + array_shift + array_unique(对齐 PHP)

PHP think\cache\Driver::push($name, $value) 第 339-358 行:

public function push(string $name, $value, $expire = null)
{
    $data = $this->get($name, []);
    if (!is_array($data)) {
        $data = [];
    }
    $data[] = $value;

    // 上限 1000
    if (count($data) > 1000) {
        array_shift($data);  // 丢弃最旧
    }

    // 去重
    $data = array_unique($data);

    $this->set($name, $data, $expire);
    return $this;
}

PHP 行为复刻

  • 数组上限 1000,超过时丢弃最旧(FIFO)
  • array_unique 去重(保留首次出现的元素)

§6. inc / dec 不经序列化(对齐 PHP Redis 驱动)

PHP think\cache\driver\Redis::inc($name, $step = 1) 第 156 行:

public function inc(string $name, int $step = 1): bool
{
    if ($this->handler->exists($name)) {
        $value = $this->handler->incrby($name, $step);
        // ...
    }
    // 不存在时初始化为 step
    $this->handler->set($name, $step);
    return true;
}

Redis 驱动直接使用 INCRBY / DECRBY 命令,不经过 serialize/unserialize。 File 驱动则会读取 → 加减 → 写回。本 MemoryCacheDriver 采用 File 驱动行为: 读取 → 解析为 i64 → 加减 → 写回(数字字符串形式)。

§架构

SzRustCache (facade)
    ↓
CacheManager (driver manager, like PHP think\Cache extends Manager)
    ↓
CacheDriver trait (like PHP think\cache\Driver abstract)
    ↓
MemoryCacheDriver (PHP think\cache\driver\File analog, in-memory)
    ↓
sz_rust_orm_facade::Cache trait / MemoryCache (底层 KV 存储)

§使用示例

use sz_rust_cache_facade::{Cache, MemoryCacheDriver};
use std::time::Duration;

// 注册默认驱动
let cache = Cache::new();
cache.register_default(MemoryCacheDriver::new());

// 基本 set/get
cache.set("user:1", "Alice", None).unwrap();
assert_eq!(cache.get::<String>("user:1").unwrap(), Some("Alice".to_string()));

// is_numeric 短路
cache.set("count", 42i64, None).unwrap();
// PHP bug 复刻:unserialize 返回 string,而非 int
assert_eq!(cache.get::<String>("count").unwrap(), Some("42".to_string()));

// remember
let val = cache.remember("expensive", None, || 100i64).unwrap();
assert_eq!(val, 100);

Structs§

Cache
Cache facade(对齐 PHP think\facade\Cache
CacheManager
缓存驱动管理器(对齐 PHP think\Cache extends Manager
MemcachedCacheDriver
Memcached 缓存驱动(对齐 PHP think\cache\driver\Memcached
MemcachedConfig
Memcached 配置(对齐 PHP think\cache\driver\Memcached$options
MemoryCacheDriver
内存缓存驱动(对齐 PHP think\cache\driver\File
MockMemcachedBackend
内存 Mock Memcached 后端
MockRedisBackend
Mock Redis 后端(用 HashMap 模拟 Redis 行为)
MultiLevelCacheDriver
多级缓存驱动(对齐 PHP think-cache 多驱动场景)
RedisCacheDriver
Redis 缓存驱动(对齐 PHP think\cache\driver\Redis
RedisConfig
Redis 缓存配置(对齐 PHP think\cache\driver\Redis::$options
TagSet
缓存标签集合(对齐 PHP think\cache\TagSet

Enums§

CacheValue
缓存值(区分 is_numeric 短路与 JSON 序列化)

Traits§

CacheDriver
缓存驱动 trait(对齐 PHP think\cache\Driver 抽象基类)
MemcachedBackend
Memcached 后端 trait(抽象 Memcached 协议命令)
RedisBackend
Redis 后端 trait(抽象 Redis 命令)

Functions§

default_cache
获取全局 Cache facade 实例
init_default_cache
初始化全局 Cache facade 实例(注册默认驱动)
php_is_numeric
PHP is_numeric 简化实现
php_serialize
序列化值(对齐 PHP Driver::serialize($data)
php_unserialize
反序列化值(对齐 PHP Driver::unserialize($data)