Browse Source

docs: add cache system design spec

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
beauty-auth
npmrun 4 weeks ago
parent
commit
6211ee0f4b
  1. 175
      docs/superpowers/specs/2026-05-22-cache-system-design.md

175
docs/superpowers/specs/2026-05-22-cache-system-design.md

@ -0,0 +1,175 @@
# 可降级缓存系统设计
**日期:** 2026-05-22
**状态:** approved
## 1. 概述
设计一个 Server-only 的通用 K-V 缓存系统 `packages/cache`,支持 Redis 主存储 + Memory 内存兜底。当 Redis 不可用时,自动降级到内存缓存,对调用方透明。
## 2. 架构
```
packages/cache/
├── package.json
├── index.ts ← 导出工厂函数和类型
└── lib/
├── types.ts ← 核心类型定义
├── managers/
│ └── cache-manager.ts ← CacheManager 实现
└── drivers/
├── base.ts ← Driver 基类 / 接口
├── memory-driver.ts ← 内存驱动
└── redis-driver.ts ← Redis 驱动(ioredis)
```
### 设计原则
- 分层适配器:调用方只跟 `CacheManager` 交互,不感知底层存储
- 手动显式降级:按 `drivers` 数组顺序尝试,返回第一个成功结果
- 扩展性:新增驱动(如 `cloudflare-kv-driver`)只需实现 `CacheDriver` 接口
## 3. 核心类型
```typescript
// lib/types.ts
export interface CacheOptions {
ttl?: number // 默认 TTL(秒),0 = 不过期
namespace?: string // key 前缀隔离(未来扩展)
}
export interface CacheEntry<T = unknown> {
value: T
expiresAt: number | null // Unix ms,null = 永不过期
}
export interface CacheDriver {
name: string
get<T = unknown>(key: string): Promise<T | null>
set<T = unknown>(key: string, value: T, ttl?: number): Promise<void>
del(key: string): Promise<void>
exists(key: string): Promise<boolean>
clear(): Promise<void>
}
export interface CacheManagerOptions {
drivers: CacheDriver[]
defaultTtl?: number
}
```
## 4. CacheManager
**文件:** `lib/managers/cache-manager.ts`
```typescript
export class CacheManager {
private drivers: CacheDriver[]
private defaultTtl: number
constructor(options: CacheManagerOptions)
async get<T = unknown>(key: string): Promise<T | null>
async set<T = unknown>(key: string, value: T, ttl?: number): Promise<void>
async del(key: string): Promise<void>
async exists(key: string): Promise<boolean>
async clear(): Promise<void>
withPrefix(ns: string): CacheManager
}
```
**降级逻辑(get):** 遍历 `drivers`,返回第一个成功结果,全部失败返回 `null`
**一致性逻辑(set/del/exists/clear):** 遍历所有 driver 并发写入,确保降级后各层数据一致。
## 5. MemoryDriver
**文件:** `lib/drivers/memory-driver.ts`
- 用 `Map<string, { value, expiresAt }>` 存储
- TTL = 0 不设置过期时间
- 过期检查在 `get` 时懒清理(检查 `expiresAt` 是否已到)
- `clear()` 清空整个 Map
## 6. RedisDriver
**文件:** `lib/drivers/redis-driver.ts`
- 使用 `ioredis` 客户端
- value 序列化为 `JSON.stringify` 存储
- TTL = 0 时使用 `persist`(不设过期)
- `clear()` 使用 `FLUSHDB` 清空当前数据库(假设 prod 使用独立 db)
- 需支持 disconnect 关闭连接
**RedisOptions:**
```typescript
interface RedisOptions {
host: string
port: number
password?: string
db?: number
}
```
## 7. 工厂函数
**文件:** `index.ts`
```typescript
interface CacheFactoryOptions {
redis?: {
host: string
port: number
password?: string
db?: number
}
memory?: boolean // 是否启用内存兜底,默认 true
defaultTtl?: number // 默认 TTL(秒)
}
function createCache(options: CacheFactoryOptions): CacheManager
```
**行为:**
- 如果指定了 `redis`,则将其作为主驱动加入 drivers 数组首位
- 如果 `memory !== false`,则将 MemoryDriver 加入 drivers 数组末位
- 如果既没有 `redis` 也没有内存(`memory: false`),抛出错误
## 8. 使用方式
```typescript
// 初始化(在 Nitro 插件或 server/utils 中
const cache = createCache({
redis: { host: '127.0.0.1', port: 6379 },
defaultTtl: 300,
})
// API 路由中使用
export default defineEventHandler(async (event) => {
const key = 'users:list'
const cached = await cache.get(key)
if (cached) return cached
const data = await fetchUsers()
await cache.set(key, data)
return data
})
```
## 9. 错误处理
- `get` 操作:单个 driver 失败时 warn 并尝试下一个;全部失败返回 `null`
- `set/del/exists/clear` 操作:单个 driver 失败时 warn 但不中断(因为是并发写)
- Redis 连接失败(如网络不可达):触发降级,不影响服务启动
## 10. 依赖
```json
{
"ioredis": "^5.x"
}
```
内存驱动无外部依赖。
Loading…
Cancel
Save