
# 缓存

Queuety 包含一个缓存层，可减少队列暂停检查、速率限制计数器、工作流状态查询和锁操作等热点路径上的数据库查询。调用 `Queuety::init()` 时会自动初始化缓存。

## 缓存接口

所有缓存后端都实现 `Queuety\Contracts\Cache`：

| 方法 | 返回值 | 说明 |
|---|---|---|
| `get( string $key )` | `mixed` | 获取一个值。如果未找到或已过期，则返回 `null`。 |
| `set( string $key, mixed $value, int $ttl = 0 )` | `void` | 存储一个值。TTL 为 0 表示永不过期。 |
| `delete( string $key )` | `void` | 移除一个值。 |
| `has( string $key )` | `bool` | 检查键是否存在且尚未过期。 |
| `add( string $key, mixed $value, int $ttl = 0 )` | `bool` | 仅当键不存在时才设置值（原子操作）。设置成功时返回 `true`。 |
| `flush()` | `void` | 移除缓存中的所有项目。`ApcuCache::flush()` 会调用 `apcu_clear_cache()`，清除服务器上的所有 APCu 条目，而不只是 Queuety 的条目。 |

## 内置后端

### MemoryCache（默认）

`Queuety\Cache\MemoryCache` 是基于数组、按进程隔离的缓存。项目存储在 PHP 数组中，并会在进程退出时丢失。APCu 不可用时，这是默认后端。

它无需扩展，适用于单工作进程部署，并使每个工作进程彼此隔离。

### ApcuCache

`Queuety\Cache\ApcuCache` 使用 APCu 扩展，在同一服务器上的所有 PHP 进程间共享内存。所有键都会自动添加前缀 `queuety:`，以避免与其他应用发生冲突。

此后端需要 `php-apcu` 扩展，可在同一服务器的工作进程间共享缓存条目，并通过 `apcu_add()` 实现真正的原子操作 `add()`。

## 自动检测

`Queuety\Cache\CacheFactory` 会自动选择可用的最佳后端：

如果存在 `apcu_store()`，且 `apcu_enabled()` 返回 `true`，Queuety 就会使用 `ApcuCache`。否则会回退到 `MemoryCache`。此操作在 `Queuety::init()` 中进行，因此默认行为无需额外配置。

## 缓存内容

以下多个子系统会在内部使用缓存层：

| 数据 | 用途 |
|---|---|
| 队列暂停状态 | 避免工作进程每次循环迭代都查询数据库 |
| 速率限制计数器 | 在内存中跟踪执行次数，而不是查询数据库 |
| 工作流状态 | 减少多步骤工作流推进期间的读取次数 |
| 锁状态 | 加快唯一任务和互斥锁中间件的锁存在性检查 |

## TTL 配置

默认缓存 TTL 为 5 秒，适用于队列暂停状态和速率限制计数器；工作流状态和状态读取会缓存固定的 2 秒，该时长不会因常量而改变。你可以在 `wp-config.php` 中使用 `QUEUETY_CACHE_TTL` 常量覆盖默认值：

```php
define( 'QUEUETY_CACHE_TTL', 10 ); // 10 seconds
```

较短的 TTL 意味着更频繁地查询数据库，但数据更新鲜。较长的 TTL 会降低数据库负载，但会导致数据更陈旧。对大多数部署而言，默认的 5 秒是一个不错的平衡。

## 自定义缓存后端

要使用自定义缓存后端（Redis、Memcached 或其他后端），请实现 `Contracts\Cache` 接口，并在 `Queuety::init()` 之前调用 `Queuety::set_cache()`：

```php
use Queuety\Contracts\Cache;
use Queuety\Queuety;

class RedisCache implements Cache {
    public function get( string $key ): mixed { /* ... */ }
    public function set( string $key, mixed $value, int $ttl = 0 ): void { /* ... */ }
    public function delete( string $key ): void { /* ... */ }
    public function has( string $key ): bool { /* ... */ }
    public function add( string $key, mixed $value, int $ttl = 0 ): bool { /* ... */ }
    public function flush(): void { /* ... */ }
}

// Set the cache before init() so all subsystems use it.
Queuety::set_cache( new RedisCache() );
Queuety::init( $connection );
```

如果在 `init()` 之前调用 `set_cache()`，则会完全跳过工厂自动检测，并使用你的自定义后端。

## 访问缓存

随时获取当前启用的缓存实例：

```php
$cache = Queuety::cache();

$cache->set( 'my_key', 'my_value', 60 );
$value = $cache->get( 'my_key' );
```

这会返回当前启用的后端（MemoryCache、ApcuCache 或你的自定义实现）。
