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
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-06 19:37:06 +08:00
1 parent 0f656f0f58
commit ad472a1a19
7 files changed
+815 -17

No files matched your search

+4
View File
@@ -132,6 +132,8 @@ const EXPECTED_TABLES = [
"episode_mapping_rule",
// 站点请求失败留样(0077)
"site_failure_sample",
// 缓存命中统计(0086,按天 × region)
"cache_stat",
].sort();
/** 不走 entityTable() 的表:追加型/引擎自有表,没有 uuid id + created_at/updated_at 三件套 */
@@ -167,6 +169,8 @@ const NON_ENTITY_TABLES = [
"site_request_count",
// 失败留样:只追加,只有 created_at
"site_failure_sample",
// 缓存命中统计:(day, region) 主键
"cache_stat",
];
describe.skipIf(skip)("schema migrations (real PG)", () => {