Files
rootandClaude Opus 5.5 ad472a1a19 feat(db): 统一元数据缓存层 MetadataCache
- 两级:L1 进程内存(按 region 限条数的 LRU、5 分钟)+ L2 PG kv_cache;读 L1 → L2 → 源,写两级
- 迁移 0086:kv_cache 加 fresh_until(过期先用旧值的宽限期)与 pinned(LRU 淘汰跳过);新表 cache_stat 按天 × region 汇总命中
- stale-while-revalidate:宽限期 = TTL 的 50%(上限 7 天),旧值先返回、同 key 后台只刷一次,刷新失败继续用旧值并记警告
- 同进程同 key 并发只回源一次;回源抛错不缓存;PG 读写失败按未命中 / 不写处理
- PgCache.delete / clear 发 pg_notify(umt_cache_invalidate),listenCacheInvalidation 收到后丢 L1;监听在线才启用 L1,断线停用、重连后整体清空
- PgCache.get 只认新鲜条目,getEntry 带回旧值;renew 只续新鲜条目并可标 pinned

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
2026-10-06 19:37:06 +08:00

42 lines
1.6 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Kysely } from "kysely";
import { dropTables, execDdl } from "../src/migration-helpers.ts";
// 统一元数据缓存(docs/runbook/cache.md):
// - kv_cache.fresh_until:新鲜期截止。过了 fresh_until、还没到 expires_at 的条目是「旧值」:
// MetadataCache 先返回旧值再后台刷新(stale-while-revalidate);PgCache.get 只返回新鲜条目。null = 没有宽限期。
// - kv_cache.pinned:关注中作品的条目,写入时按条数上限淘汰(LRU)跳过它们;到期仍会过期。
// - cache_stat:按天 × region 汇总各进程的命中统计(进程内计数,定期把增量加进来)。
// biome-ignore lint/suspicious/noExplicitAny: 迁移不依赖应用表类型
export async function up(db: Kysely<any>): Promise<void> {
await execDdl(
db,
`
alter table kv_cache add column fresh_until timestamptz;
alter table kv_cache add column pinned boolean not null default false;
create table cache_stat (
day date not null,
region text not null,
l1_hits bigint not null default 0,
l2_hits bigint not null default 0,
stale_hits bigint not null default 0,
misses bigint not null default 0,
failures bigint not null default 0,
refreshes bigint not null default 0,
updated_at timestamptz not null default now(),
primary key (day, region)
);
`,
);
}
// biome-ignore lint/suspicious/noExplicitAny: 同上
export async function down(db: Kysely<any>): Promise<void> {
await execDdl(
db,
`${dropTables(["cache_stat"])}
alter table kv_cache drop column if exists pinned;
alter table kv_cache drop column if exists fresh_until;`,
);
}