Appearance
Harness 哪些数据值得缓存:Cache Aside、TTL、Invalidation 与 Stampede
前置:Database Transaction / Outbox;Idempotent Settlement
核心问题:缓存何时可以直接返回,何时必须失效;大量 Key 同时过期时怎样避免所有请求一起打穿后端?
Harness 不是所有数据都该缓存
适合缓存的典型读路径:
- Model/provider capability metadata;
- MCP
tools/list/ resource list 等可缓存 discovery 结果; - Tool schema catalog 的已版本化快照;
- Retrieval metadata 或可重复计算的 query result;
- 很少变化的 tenant configuration(前提是 permission/version 进入 key)。
不应该把 cache 当 authority 的事实:
text
Run status
Node completion
Checkpoint
Approval decision
Idempotency record
Worker lease epoch这些决定“事情是否真的发生”,必须来自 durable source of truth。
Cache Aside 的职责非常简单
text
read
→ cache hit? return
→ miss: load source of truth
→ populate cache
write
→ commit source of truth
→ invalidate / publish version changeCache Aside 的重点不是某个库,而是明确:应用知道真实数据在哪里,cache 只是一份可丢弃副本。
Cache Key 必须包含“会改变语义的维度”
危险 key:
text
tools:list更合理:
text
tenant + server_identity + auth_scope + protocol_version + catalog_version否则用户 A 发现的 Tool、旧 schema 或高权限结果可能被用户 B/新版本复用。
同理,retrieval cache 至少考虑 index version、query normalization、filters 与权限边界。
TTL 只是兜底,Invalidation 才表达事实变化
TTL 告诉系统“最多允许旧多久”;它不知道事实何时变化。
对于 Tool schema/version、tenant policy 等影响执行安全的配置,发生更新时应主动 invalidation;TTL 只提供丢通知后的最终收敛窗口。
如果某数据必须强一致才能安全执行,就不要靠普通 TTL cache 解决。
Stampede 会把一次 Miss 放大成故障
热门 MCP catalog 或 tenant config 同时过期,100 个 worker 一起 miss:
text
100 cache misses
→ 100 downstream loads
→ timeout
→ retry
→ 更大的洪峰常见控制:
- single-flight/request coalescing;
- TTL jitter;
- stale-while-revalidate(仅适合允许陈旧的值);
- startup/pre-warm;
- downstream concurrency limit。
选择策略前先定义“允许多旧”,而不是默认 stale 一定可接受。
Failure Modes
Cache Key 忽略 Tenant / Permission / Version
后果: 权限串用、旧 schema 调用、错误 capability 判断。
处理: key 中加入所有语义维度;安全敏感信息优先短 TTL + 主动 invalidation。
热点失效触发 Stampede
处理: single-flight + jitter + 下游限流,并监控 miss amplification。
如何验证
| 场景 | 必须成立 |
|---|---|
| Tool schema 从 v4 升 v5 | 新 Run 不再使用 v4 cache entry |
| 两个 tenant scope 不同 | cache 不发生跨租户命中 |
| 100 并发同 key miss | 实际下游 load 次数被合并/限制 |
| Cache 整体清空 | Run/Checkpoint 正确性不受影响 |
自测
- 热门 key 同时过期后 500 个 Worker 一起回源。TTL 本身为什么不能阻止 cache stampede?
- Cache Aside 中 DB 已更新但 cache 仍是旧值。失效顺序和 TTL 应怎样设计,才能把 stale window 控制在可接受范围?