From 6211ee0f4bb478c5e68c010ed6bb4036cea8dedc Mon Sep 17 00:00:00 2001 From: npmrun <1549469775@qq.com> Date: Fri, 22 May 2026 09:10:28 +0800 Subject: [PATCH] docs: add cache system design spec Co-Authored-By: Claude Opus 4.7 --- .../specs/2026-05-22-cache-system-design.md | 175 +++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-22-cache-system-design.md diff --git a/docs/superpowers/specs/2026-05-22-cache-system-design.md b/docs/superpowers/specs/2026-05-22-cache-system-design.md new file mode 100644 index 0000000..b676cf5 --- /dev/null +++ b/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 { + value: T + expiresAt: number | null // Unix ms,null = 永不过期 +} + +export interface CacheDriver { + name: string + get(key: string): Promise + set(key: string, value: T, ttl?: number): Promise + del(key: string): Promise + exists(key: string): Promise + clear(): Promise +} + +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(key: string): Promise + async set(key: string, value: T, ttl?: number): Promise + async del(key: string): Promise + async exists(key: string): Promise + async clear(): Promise + + withPrefix(ns: string): CacheManager +} +``` + +**降级逻辑(get):** 遍历 `drivers`,返回第一个成功结果,全部失败返回 `null`。 + +**一致性逻辑(set/del/exists/clear):** 遍历所有 driver 并发写入,确保降级后各层数据一致。 + +## 5. MemoryDriver + +**文件:** `lib/drivers/memory-driver.ts` + +- 用 `Map` 存储 +- 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" +} +``` + +内存驱动无外部依赖。 \ No newline at end of file