Skip to content

🐯 [存储] 回收需在 WAL checkpoint 之后 · engine.go #300

Description

@github-actions

来自 #296 的架构级评审建议。不阻塞合入,仅供参考是否有更好的架构解法。

⚠️ [重要 · 存储] 回收需在 WAL checkpoint 之后 storage/engine.go:522

问题根因:standalone 模式下,回收删除已被投递的 SSTable 文件后,若该文件的记录仍存在于 WAL 中(即尚未被 checkpoint 重写剪枝),一旦 WAL replay 到这些记录,会把这些已回收数据重新写回 memtable 并最终再次 flush 成新 SSTable——「已回收的数据复活」。ReclaimUpTo 只与 compaction 互斥(fileMu),并未与 Checkpoint(cpMu.Lock)协调,也未确认被回收文件的数据是否已在 WAL 中剪枝。若回收前 WAL 中仍残留这些 key 的记录,重启后 replay 会把它们写回——彻底违反『已投递数据不再读回』的回收语义。

为什么低级解法不够:在 ReclaimDelivered 加 cpMu 锁不够,因为 checkpoint 间隔是 2×MaxMemTableSize 次写,无法保证回收时 WAL 已含被回收键的 checkpoint。低修(调用前强制一次 Checkpoint)有效但把回收变成了同步强刷,性能代价高,且未在架构层面确立『回收与 WAL 剪枝的一致性』。

架构级方案:确立不变式:回收任何 SSTable 前,必须保证其数据已从 WAL 中剪枝(即已 checkpoint 到不含这些 key 的活跃快照)。实现:ReclaimDelivered 在 ReclaimUpTo 前调用一次 kv.Checkpoint()(持 cpMu 独占静默),或者更优——把回收纳入 checkpoint 流程:每当投递游标推进使某个文件整体被越过时,先 checkpoint、再回收。若担忧频繁 checkpoint 的性能,可把回收粒度从「每批投递」放宽到「每 N 批」并配齐 WAL 剪枝保证。若不做,回收是无效的(重启后数据复活),保留期语义名存实亡。

代价/收益:收益:standalone 下回收真正成立,崩溃重启后不会复活已回收数据。代价:每批回收需先同步 checkpoint,若保留期开启场景频繁回收会引入 checkpoint 开销;可通过批量回收(每 N 批一次)摊销。这是保留期功能正确性的必要前提,不可省。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions