docs(architecture): 同步缓存基础设施 + classes 标杆迁移 + 缓存策略规则
- 004: 补全 shared/lib/cache 9 文件说明(types/memory-store/redis-store/store-factory/cache-fn/invalidation-map/client-invalidation-map/invalidate/index)+ shared/lib/redis-client + shared/lib/query-keys - 005: shared.lib.exports.functions 补 5 个节点(MemoryCacheStore / RedisCacheStore / CLIENT_INVALIDATION_MAP / getRedisClient / queryKeys) - known-issues.md 新增「二十四、缓存策略规则」章节,含 14 条规则表与涉及文件清单
This commit is contained in:
@@ -4366,12 +4366,16 @@ formatNumber(v: number | null | undefined, digits?: number): string
|
||||
// shared/lib/search-params.ts (re-export from utils.ts)
|
||||
getParam(params: SearchParams, key: string): string | undefined // = getSearchParam
|
||||
|
||||
// shared/lib/cache/ (服务端缓存基础设施,P1 重构新增)
|
||||
// shared/lib/cache/ (服务端缓存基础设施 + 客户端失效映射,P1 重构新增)
|
||||
// types.ts —— CacheFnOptions / CacheStore / InvalidationRule 接口定义(纯类型,无运行时依赖)
|
||||
// memory-store.ts —— MemoryCacheStore 实现(Map + LRU + tag 反查索引,单实例 dev/test 用)
|
||||
// redis-store.ts —— RedisCacheStore 实现(多实例共享,故障降级回退 producer)
|
||||
// store-factory.ts —— getCacheStore() 单例工厂:默认 MemoryCacheStore,CACHE_DRIVER=redis 时懒加载 RedisCacheStore
|
||||
// memory-store.ts / redis-store.ts —— CacheStore 两种实现
|
||||
// cache-fn.ts —— cacheFn 双层包装器:外层 react.cache 请求级 memoization,内层 cacheStore.getOrSet 跨请求缓存(按 tag 失效,TTL 可选)
|
||||
// invalidation-map.ts —— INVALIDATION_MAP 集中式失效映射表 + fillTemplate 模板填充
|
||||
// invalidation-map.ts —— INVALIDATION_MAP 集中式失效映射表 + fillTemplate 模板填充(含服务端 tags/queryKeys/paths 三段)
|
||||
// client-invalidation-map.ts —— CLIENT_INVALIDATION_MAP 客户端可见子集(仅 queryKeys 字段,避免客户端 bundle 拉入 revalidateTag 等 server-only 依赖)
|
||||
// invalidate.ts —— invalidateFor 三步编排函数(Server Action 写后调用)
|
||||
// index.ts —— barrel re-export:cacheFn / invalidateFor / INVALIDATION_MAP / fillTemplate / CLIENT_INVALIDATION_MAP + 类型
|
||||
getCacheStore(): Promise<CacheStore>
|
||||
cacheFn<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => Promise<TResult>, options: CacheFnOptions): (...args: TArgs) => Promise<TResult>
|
||||
fillTemplate(template: string, params: Record<string, string>): string
|
||||
@@ -4379,6 +4383,17 @@ INVALIDATION_MAP // 集中式失效映射表常量
|
||||
invalidateFor(actionId: string, params?: Record<string, string>): Promise<void>
|
||||
// 三步编排:1) CacheStore.invalidateTags 2) revalidateTag × N 3) revalidatePath × N
|
||||
// 未知 actionId 抛错 "[cache] Unknown actionId: <id>. Update INVALIDATION_MAP."
|
||||
CLIENT_INVALIDATION_MAP // 客户端可见子集:仅 queryKeys 字段,由 useActionMutation 在 onSuccess 中查询
|
||||
|
||||
// shared/lib/redis-client.ts (P1 重构新增:共享 Redis 客户端单例)
|
||||
// rate-limit + cache 共用,复用 env.UPSTASH_REDIS_REST_URL/TOKEN
|
||||
// 动态 import + webpackIgnore,包未安装/未配置返回 null(fail-open 降级)
|
||||
getRedisClient(): Promise<unknown | null>
|
||||
|
||||
// shared/lib/query-keys.ts (P1 重构新增:客户端 queryKey 工厂)
|
||||
// 命名约定:[module, resource, ...args]
|
||||
// 失效时使用前缀匹配:invalidateQueries({ queryKey: ["classes", "detail"] }) → 失效所有 ["classes", "detail", *]
|
||||
queryKeys // 工厂对象,含 classes.{all,lists,list,details,detail,students,schedule,stats,invitations}
|
||||
```
|
||||
|
||||
### 业务模块核心 Actions
|
||||
|
||||
@@ -1420,6 +1420,33 @@
|
||||
"api/notifications/stream"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MemoryCacheStore",
|
||||
"file": "lib/cache/memory-store.ts",
|
||||
"signature": "class MemoryCacheStore implements CacheStore { getOrSet<T>(key, producer, options): Promise<T>; invalidateTags(tags): Promise<void> }",
|
||||
"purpose": "CacheStore 内存实现:Map + LRU 淘汰(maxEntries 默认 500)+ tag 反查索引 Map<tag, Set<key>>,TTL 惰性删除。适用于单实例 dev/test",
|
||||
"deps": [
|
||||
"server-only",
|
||||
"./types"
|
||||
],
|
||||
"usedBy": [
|
||||
"shared/lib/cache/store-factory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "RedisCacheStore",
|
||||
"file": "lib/cache/redis-store.ts",
|
||||
"signature": "class RedisCacheStore implements CacheStore { getOrSet<T>(key, producer, options): Promise<T>; invalidateTags(tags): Promise<void> }",
|
||||
"purpose": "CacheStore Redis 实现:key 格式 next-edu:cache:{key},value JSON.stringify,tag 反查索引用 Redis Set,TTL 通过 expire 原子设置。Redis 不可用时故障降级透传 producer",
|
||||
"deps": [
|
||||
"server-only",
|
||||
"@/shared/lib/redis-client",
|
||||
"./types"
|
||||
],
|
||||
"usedBy": [
|
||||
"shared/lib/cache/store-factory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "getCacheStore",
|
||||
"file": "lib/cache/store-factory.ts",
|
||||
@@ -1462,7 +1489,7 @@
|
||||
"name": "INVALIDATION_MAP",
|
||||
"file": "lib/cache/invalidation-map.ts",
|
||||
"signature": "INVALIDATION_MAP: Record<string, InvalidationRule>",
|
||||
"purpose": "集中式失效映射表:每个 mutation actionId 声明其副作用 tags/queryKeys/paths",
|
||||
"purpose": "集中式失效映射表:每个 mutation actionId 声明其副作用 tags/queryKeys/paths(服务端三段)",
|
||||
"deps": [
|
||||
"./types"
|
||||
],
|
||||
@@ -1470,6 +1497,16 @@
|
||||
"shared/lib/cache/invalidate"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "CLIENT_INVALIDATION_MAP",
|
||||
"file": "lib/cache/client-invalidation-map.ts",
|
||||
"signature": "CLIENT_INVALIDATION_MAP: Record<string, { queryKeys: readonly (readonly string[])[] }>",
|
||||
"purpose": "客户端可见的失效映射子集(仅 queryKeys 字段),从 invalidation-map 提取以避免客户端 bundle 拉入 revalidateTag 等 server-only 依赖。由 useActionMutation 在 onSuccess 中查询以自动 invalidateQueries",
|
||||
"deps": [],
|
||||
"usedBy": [
|
||||
"shared/hooks/use-action-mutation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "invalidateFor",
|
||||
"file": "lib/cache/invalidate.ts",
|
||||
@@ -1483,6 +1520,33 @@
|
||||
"usedBy": [
|
||||
"modules/* Server Actions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "getRedisClient",
|
||||
"file": "lib/redis-client.ts",
|
||||
"signature": "getRedisClient(): Promise<unknown | null>",
|
||||
"purpose": "共享 Redis 客户端单例(rate-limit + cache 共用)。复用 env.UPSTASH_REDIS_REST_URL/TOKEN,动态 import + webpackIgnore。包未安装或 env 未配置时返回 null(fail-open 降级)",
|
||||
"deps": [
|
||||
"server-only",
|
||||
"@/env.mjs",
|
||||
"@upstash/redis (optional, webpackIgnore)"
|
||||
],
|
||||
"usedBy": [
|
||||
"shared/lib/cache/redis-store",
|
||||
"shared/lib/rate-limit/redis-limiter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "queryKeys",
|
||||
"file": "lib/query-keys.ts",
|
||||
"signature": "queryKeys: { classes: { all, lists, list(filters), details, detail(id), students(classId), schedule(classId), stats(classId), invitations(classId) } }",
|
||||
"purpose": "客户端 queryKey 工厂对象。命名约定 [module, resource, ...args],失效时使用前缀匹配 invalidateQueries({ queryKey: ['classes', 'detail'] }) 失效所有 ['classes', 'detail', *]。后续模块按此模式扩展",
|
||||
"deps": [],
|
||||
"usedBy": [
|
||||
"modules/classes/components/*",
|
||||
"shared/hooks/use-action-query",
|
||||
"shared/hooks/use-action-mutation"
|
||||
]
|
||||
}
|
||||
],
|
||||
"hooks": [
|
||||
|
||||
Reference in New Issue
Block a user