Browse Source

docs: add cache system implementation plan

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
beauty-auth
npmrun 1 week ago
parent
commit
7ec761220f
  1. 489
      docs/superpowers/plans/2026-05-22-cache-system-implementation-plan.md

489
docs/superpowers/plans/2026-05-22-cache-system-implementation-plan.md

@ -0,0 +1,489 @@
# 可降级缓存系统实现计划
> **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**
```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**
```typescript
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 骨架**
```typescript
export * from './lib/types'
```
Run: `cat packages/cache/index.ts`
Expected: `export * from './lib/types'`
- [ ] **Step 4: 提交**
```bash
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**
```typescript
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**
```typescript
export * from './lib/types'
export { MemoryDriver } from './lib/drivers/memory-driver'
```
Run: `cat packages/cache/index.ts`
Expected: 包含 MemoryDriver 导出
- [ ] **Step 3: 提交**
```bash
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**
```typescript
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**
```typescript
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: 提交**
```bash
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**
```typescript
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**
```typescript
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: 提交**
```bash
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 工厂函数**
```typescript
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: 提交**
```bash
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: 提交**
```bash
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 方法签名在各驱动一致 ✓
Loading…
Cancel
Save