Skip to content

🐯 [存储] 扫描块索引二分依赖 lastKey 是否含墓碑 · iterator.go #295

Description

@github-actions

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

💡 [建议 · 存储] 扫描块索引二分依赖 lastKey 是否含墓碑 storage/iterator.go:39

问题根因newSSTableIteratorFrom 的块索引二分逻辑:找到「first lastKey >= start」的块,从其 BlockOffset 起读。但若目标块恰为墓碑所在块且 lastKey 取自块内最后一条的 key,这在 start 恰好落在该块中间(在 lastKey 与已定位 key 之间且该 key 该块中靠前)时仍正确——因为它是「第一个 lastKey>=start」的块,start 必然落在此块。此处逻辑其实自洽。真正的隐患在别处:块索引的 LastKey 是否与迭代器实际产出的 key 语义完全一致(含墓碑 key 的入索引方式)未在 diff 中封闭验证,若某个块仅含墓碑、lastKey 被落下/未被索引,则二分定位可能漏掉该块中实际存在的 key。

为什么低级解法不够:在二分定位外再补一层范围裁剪的兜底能掩盖不一致,但没有确立「块索引 lastKey 与迭代器产出 key 的互斥契约」,同类错位会在配置变化后复发。

架构级方案:在扫描实现里建立并验证不变量:块索引二分定位后,仍交由 rangeIterator 做 [start,end) 裁剪兜底(diff 已这么做),同时补一条跨越「仅墓碑块」「start 落在块中间」「start 正好等于块 lastKey」的多用例守护,锁定「二分定位 + range 裁剪」组合对任意 start 的正确性——即无论块索引是否完整,正确性由 range 裁剪保证、性能才依赖索引。这个分级保证(correctness by range-clip, perf by index)应在注释和测试中明示。

代价/收益:代价:属加固性工作,需补边界多用例;收益:把「索引优化引入扫描也可能引入正确性回归」的风险锁定为被测试屏障覆盖的既定契约,长期可信。

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions