lru-cache 同一份 dump 第二次 load 后,TTL 条目立刻消失
11.5.3的load会把entry.start改成内部计时坐标,再次加载原数组就可能误判过期。每次从未改动快照复制entry,保留真实剩余TTL,不用当前时间续期。
一份 lru-cache 的 dump,加载到第一个缓存能够命中,再把同一数组加载到第二个缓存,带 TTL 的条目却立即变成 undefined。若使用正式 lru-cache 11.5.3、运行时采用 performance.now 计时,要检查第一次 load 是否已经修改了快照里的 start。
dump 用 Date.now 对应的可移植时间戳记录条目起点;load 为了恢复剩余 TTL,把它换算到缓存使用的计时坐标。但 11.5.3 直接写回传入的 entry.start。第二次再加载同一对象时,旧值已不是原来的墙上时钟时间戳,新的换算会把条目视为早已过期。
复制数组还没有隔离条目
外层数组、键值二元组、entry 对象是不同层级。仅写 [...snapshot] 仍共享每个 entry;重新创建二元组却继续放入原 entry,也没有隔离被修改的 start。
先保留最初 dump 与第一次加载前后的字段,比较每个 entry 的 ttl、start、size。不要为了让第二个缓存命中,直接把 start 改成当前时间;那会给条目续期,改变原本应当到期的数据。
也不要把所有缓存 MISS 都归为这个问题。快照保存时间已经超过 TTL、目标缓存容量更小或使用不同配置,都可能使恢复后无法命中。这里的关键是同一个未过期快照被重复加载,第一次调用改变了调用方仍想复用的元数据。
每次 load 使用独立的 entry 对象
将一份未被 load 改动过的快照作为只读来源,每次创建新的数组、二元组与 entry。下面保留 key 和 value 的原引用,只隔离 load 会修改的条目元数据:
import { LRUCache } from 'lru-cache';
function copyDumpForLoad(snapshot) {
return snapshot.map(([key, entry]) => [key, { ...entry }]);
}
const source = new LRUCache({ max: 10, ttl: 60_000 });
source.set('one', { count: 1 });
const snapshot = source.dump();
const first = new LRUCache({ max: 10, ttl: 60_000 });
const second = new LRUCache({ max: 10, ttl: 60_000 });
first.load(copyDumpForLoad(snapshot));
second.load(copyDumpForLoad(snapshot));
必须从原始快照复制;一份已经被旧 load 改过 start 的对象,再多复制几次也恢复不了原来的时间戳。若没有可信原快照,应从数据源重新生成,而不是猜一个时间让它暂时命中。
这个浅复制不隔离业务 value 的内部修改,也不克隆对象 key。若两个缓存需要独立的数据所有权,应另按应用的数据类型制定复制协议。不要顺手用 JSON stringify/parse 深拷贝:对象键的身份、Date、Map、BigInt 等数据可能无法按原含义恢复。
恢复不能暂停时间
恢复时仍会计算快照保存以来经过的时间。即使每次传入独立 entry,第一次命中、很久之后第二次不命中,也可能只是正常过期。不同进程或机器使用快照,还要核对墙上时钟是否符合应用的同步约定。
load 会清空目标缓存再按快照顺序加载,这不是增量合并接口。需要保留目标已有条目时,应先决定合并规则,不要直接在正服务请求的共享实例上反复 load。
验收时检查原始 snapshot 的元数据在两次加载后完全未变,两个目标在剩余 TTL 内均能按预期读取;随后也应按原期限过期,不能被恢复动作无意续命。再分别核对容量和尺寸配置,确认没有把正常淘汰误当作 TTL 损坏。正式版本更新后,仍应在实际安装实现上保留重复加载与快照不变性检查。


