12 KiB
可降级缓存系统实现计划
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 构建 packages/cache 包,实现 Redis 主存储 + Memory 内存降级的通用 K-V 缓存系统
Architecture: 分层适配器架构:CacheDriver 接口定义 get/set/del/exists/clear,MemoryDriver 和 RedisDriver 分别实现, CacheManager 按 drivers 数组顺序尝试调用方透明降级
Tech Stack: TypeScript, ioredis, Node.js
文件结构
packages/cache/
├── package.json
├── index.ts
└── lib/
├── types.ts ← CacheDriver 接口、CacheManagerOptions、CacheEntry
├── managers/
│ └── cache-manager.ts ← CacheManager 实现(降级逻辑)
└── drivers/
├── base.ts ← 抽象基类 AbstractCacheDriver
├── memory-driver.ts ← Map 实现
└── redis-driver.ts ← ioredis 实现
Task 1: 项目脚手架
Files:
-
Create:
packages/cache/package.json -
Create:
packages/cache/index.ts -
Create:
packages/cache/tsconfig.json -
Create:
packages/cache/lib/types.ts -
Step 1: 创建 package.json
{
"name": "cache",
"version": "0.1.0",
"type": "module",
"exports": {
".": "./index.ts"
},
"dependencies": {
"ioredis": "^5.4.1"
},
"devDependencies": {
"@types/node": "^20.0.0"
}
}
Run: ls packages/cache/package.json
Expected: 文件存在
- Step 2: 创建 types.ts
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
}
Run: cat packages/cache/lib/types.ts
Expected: 文件存在,CacheDriver、CacheManagerOptions、CacheEntry 导出
- Step 3: 创建 index.ts 骨架
export * from './lib/types'
Run: cat packages/cache/index.ts
Expected: export * from './lib/types'
- Step 4: 提交
git add packages/cache/package.json packages/cache/index.ts packages/cache/tsconfig.json packages/cache/lib/types.ts
git commit -m "feat(cache): scaffold package and define core types
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>"
Task 2: MemoryDriver
Files:
-
Create:
packages/cache/lib/drivers/memory-driver.ts -
Step 1: 实现 MemoryDriver
import type { CacheDriver } from '../types'
interface MemoryEntry {
value: unknown
expiresAt: number | null
}
export class MemoryDriver implements CacheDriver {
name = 'memory'
private store = new Map<string, MemoryEntry>()
async get<T>(key: string): Promise<T | null> {
const entry = this.store.get(key)
if (!entry) return null
if (entry.expiresAt !== null && entry.expiresAt < Date.now()) {
this.store.delete(key)
return null
}
return entry.value as T
}
async set<T>(key: string, value: T, ttl = 0): Promise<void> {
const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : null
this.store.set(key, { value, expiresAt })
}
async del(key: string): Promise<void> {
this.store.delete(key)
}
async exists(key: string): Promise<boolean> {
const entry = this.store.get(key)
if (!entry) return false
if (entry.expiresAt !== null && entry.expiresAt < Date.now()) {
this.store.delete(key)
return false
}
return true
}
async clear(): Promise<void> {
this.store.clear()
}
}
Run: cat packages/cache/lib/drivers/memory-driver.ts
Expected: 文件存在,类导出
- Step 2: 更新 index.ts 导出 MemoryDriver
export * from './lib/types'
export { MemoryDriver } from './lib/drivers/memory-driver'
Run: cat packages/cache/index.ts
Expected: 包含 MemoryDriver 导出
- Step 3: 提交
git add packages/cache/lib/drivers/memory-driver.ts packages/cache/index.ts
git commit -m "feat(cache): implement MemoryDriver
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>"
Task 3: RedisDriver
Files:
-
Create:
packages/cache/lib/drivers/redis-driver.ts -
Step 1: 实现 RedisDriver
import Redis from 'ioredis'
import type { CacheDriver } from '../types'
export interface RedisDriverOptions {
host: string
port: number
password?: string
db?: number
}
export class RedisDriver implements CacheDriver {
name = 'redis'
private redis: Redis
constructor(options: RedisDriverOptions) {
this.redis = new Redis({
host: options.host,
port: options.port,
password: options.password,
db: options.db ?? 0,
lazyConnect: true,
})
}
private serialize<T>(value: T): string {
return JSON.stringify(value)
}
private deserialize<T>(data: string): T {
return JSON.parse(data) as T
}
async get<T>(key: string): Promise<T | null> {
const data = await this.redis.get(key)
if (!data) return null
return this.deserialize<T>(data)
}
async set<T>(key: string, value: T, ttl = 0): Promise<void> {
const serialized = this.serialize(value)
if (ttl > 0) {
await this.redis.set(key, serialized, 'EX', ttl)
} else {
await this.redis.set(key, serialized)
}
}
async del(key: string): Promise<void> {
await this.redis.del(key)
}
async exists(key: string): Promise<boolean> {
const result = await this.redis.exists(key)
return result === 1
}
async clear(): Promise<void> {
await this.redis.flushdb()
}
async disconnect(): Promise<void> {
await this.redis.quit()
}
}
Run: cat packages/cache/lib/drivers/redis-driver.ts
Expected: 文件存在,RedisDriver 导出,connect lazy
- Step 2: 更新 index.ts 导出 RedisDriver
export * from './lib/types'
export { MemoryDriver } from './lib/drivers/memory-driver'
export { RedisDriver } from './lib/drivers/redis-driver'
export type { RedisDriverOptions } from './lib/drivers/redis-driver'
Run: cat packages/cache/index.ts
Expected: 包含 RedisDriver 导出
- Step 3: 提交
git add packages/cache/lib/drivers/redis-driver.ts packages/cache/index.ts
git commit -m "feat(cache): implement RedisDriver with ioredis
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>"
Task 4: CacheManager
Files:
-
Create:
packages/cache/lib/managers/cache-manager.ts -
Step 1: 实现 CacheManager
import type { CacheDriver, CacheManagerOptions } from '../types'
export class CacheManager {
private drivers: CacheDriver[]
private defaultTtl: number
constructor(options: CacheManagerOptions) {
if (!options.drivers || options.drivers.length === 0) {
throw new Error('[cache] at least one driver is required')
}
this.drivers = options.drivers
this.defaultTtl = options.defaultTtl ?? 0
}
async get<T>(key: string): Promise<T | null> {
for (const driver of this.drivers) {
try {
const value = await driver.get<T>(key)
return value
} catch (err) {
console.warn(`[cache] ${driver.name} get failed:`, err)
continue
}
}
return null
}
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
const effectiveTtl = ttl ?? this.defaultTtl
await Promise.all(
this.drivers.map(async (driver) => {
try {
await driver.set(key, value, effectiveTtl)
} catch (err) {
console.warn(`[cache] ${driver.name} set failed:`, err)
}
})
)
}
async del(key: string): Promise<void> {
await Promise.all(
this.drivers.map(async (driver) => {
try {
await driver.del(key)
} catch (err) {
console.warn(`[cache] ${driver.name} del failed:`, err)
}
})
)
}
async exists(key: string): Promise<boolean> {
for (const driver of this.drivers) {
try {
const exists = await driver.exists(key)
if (exists) return true
} catch (err) {
console.warn(`[cache] ${driver.name} exists failed:`, err)
continue
}
}
return false
}
async clear(): Promise<void> {
await Promise.all(
this.drivers.map(async (driver) => {
try {
await driver.clear()
} catch (err) {
console.warn(`[cache] ${driver.name} clear failed:`, err)
}
})
)
}
}
Run: cat packages/cache/lib/managers/cache-manager.ts
Expected: 文件存在,CacheManager 导出
- Step 2: 更新 index.ts 导出 CacheManager
export * from './lib/types'
export { MemoryDriver } from './lib/drivers/memory-driver'
export { RedisDriver } from './lib/drivers/redis-driver'
export { CacheManager } from './lib/managers/cache-manager'
export type { RedisDriverOptions } from './lib/drivers/redis-driver'
Run: cat packages/cache/index.ts
Expected: 包含 CacheManager 导出
- Step 3: 提交
git add packages/cache/lib/managers/cache-manager.ts packages/cache/index.ts
git commit -m "feat(cache): implement CacheManager with fallback logic
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>"
Task 5: createCache 工厂函数
Files:
-
Modify:
packages/cache/index.ts -
Step 1: 实现 createCache 工厂函数
import { CacheManager } from './lib/managers/cache-manager'
import { MemoryDriver } from './lib/drivers/memory-driver'
import { RedisDriver } from './lib/drivers/redis-driver'
import type { CacheDriver, CacheManagerOptions } from './lib/types'
import type { RedisDriverOptions } from './lib/drivers/redis-driver'
export * from './lib/types'
export { MemoryDriver } from './lib/drivers/memory-driver'
export { RedisDriver } from './lib/drivers/redis-driver'
export { CacheManager } from './lib/managers/cache-manager'
export type { RedisDriverOptions } from './lib/drivers/redis-driver'
export interface CacheFactoryOptions {
redis?: RedisDriverOptions
memory?: boolean
defaultTtl?: number
}
export function createCache(options: CacheFactoryOptions): CacheManager {
const drivers: CacheDriver[] = []
if (options.redis) {
drivers.push(new RedisDriver(options.redis))
}
if (options.memory !== false) {
drivers.push(new MemoryDriver())
}
if (drivers.length === 0) {
throw new Error('[cache] at least one driver (redis or memory) is required')
}
return new CacheManager({ drivers, defaultTtl: options.defaultTtl })
}
Run: cat packages/cache/index.ts
Expected: createCache 工厂函数存在
- Step 2: 提交
git add packages/cache/index.ts
git commit -m "feat(cache): add createCache factory function
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>"
Task 6: 验证构建
- Step 1: 安装依赖
Run: cd /home/dash/coding/nuxt-app && bun add -w cache
Expected: ioredis 被添加到 packages/cache/package.json
- Step 2: 类型检查
Run: cd packages/cache && npx tsc --noEmit
Expected: 无错误输出
- Step 3: 提交
git add -A
git commit -m "chore(cache): add dependencies and verify build
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>"
自检清单
- spec 覆盖:CacheDriver 接口 ✓ / MemoryDriver ✓ / RedisDriver ✓ / CacheManager ✓ / createCache ✓ / 降级逻辑 ✓
- 无 placeholder:无 TBD/TODO/类似占位符
- 类型一致性:get/set/del/exists/clear 方法签名在各驱动一致 ✓