update WAL segment boundary design

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
dailz
2026-06-11 17:33:39 +08:00
parent 0047d52541
commit f6a2cd979e

View File

@@ -198,6 +198,8 @@ batch 所在 segment 已进入 durable-ready 状态
如果新 segment 进入 durable-ready 之前任一步失败,该 segment 不得成为 active segment也不得承载可确认写入。如果此时尚未分配 sequence可以重试创建或切换到其他 segment如果 sequence 已分配或已有 WAL Batch 依赖该 segment则按 WAL write failure 处理,引擎进入 write-stopped 状态。
新 segment 的 durable-ready 协议只能在当前 active segment 已结束于完整 WAL Batch 边界后启动。实现不得预创建未来 segment也不得让 `segment-N+1.wal``segment-N.wal` 仍可能存在未完成 Batch 尾部时对 recovery 可见。该约束保证:如果 recovery 看到后续 segment 存在,前一个 segment 必须已经 sealed 在完整 Batch 边界;否则前一个 segment 的尾部异常应被视为 WAL 中间损坏。
MANIFEST 不在每次 WAL segment 轮转时更新。MANIFEST 表示 recovery 起点 / checkpoint 状态,只在 MemTable flush、SSTable 与 checkpoint 元数据都持久化后推进;旧 WAL segment 何时可删除由后续文件生命周期规则定义。
#### 设计决策
@@ -309,6 +311,14 @@ Physical Record 的顺序由 WAL 文件的顺序追加和顺序扫描保证,
- Physical Record 的 `length` 必须 `> 0`
- 读取时Block 尾部 padding 必须全为 0如果 padding 区出现非 0 bytes视为 WAL 损坏
##### Segment Rotation 约束
- WAL Batch 不得跨 segment 文件;每个 WAL Batch 的所有 Physical Record 必须位于同一个 WAL segment 内
- Segment rotation 只在 WAL Batch 边界发生:当前 segment 写入完一个完整 WAL Batch 后,如果需要轮转,在下个 Batch 写入前切换到新 segment
- 写入 WAL Batch 前WAL writer 必须根据编码后的 Batch 大小计算 Physical Records 布局;如果当前 segment 剩余空间不足以容纳整个 Batch必须先切换到新 segment再写入该 Batch
- 不允许先写入 Batch 的部分 fragment再因 segment 空间不足切换 segment
- Recovery 的 fragment 收集状态不跨 segment 携带;每个非最后恢复 segment 扫描结束时必须处于 `Idle`
##### WAL Batch
WAL Batch 是物理持久化单元,对应一次 group commit batch。恢复时必须拼出完整 WAL Batch 后才能重放,不能重放半个 batch。WAL Batch 不是事务边界;一个 WAL Batch 可以包含多个独立 autocommit 写入,后续也可以包含一个或多个事务提交记录。
@@ -444,7 +454,11 @@ MANIFEST 是 recovery 起点和 checkpoint 状态的权威源,记录最老仍
6. 在每个 Block 内按 Physical Record 顺序解析
7. 将 Full 或 First/Middle/Last 拼成完整 WAL Batch
8. 校验并重放 WAL Batch 中的 Entries
9. 更新 expectedSequence并继续扫描下一个 batch 或下一个 segment
9. 更新 expectedSequence并继续扫描下一个 batch
10. 当前 segment 所有 Block 扫描完毕后,检查 fragment 状态:
- Idle: 正常结束当前 segment继续下一个 segment
- CollectingFragments 且当前 segment 是最后一个需要恢复的 segment: 丢弃 incomplete batch并截断到最后一个完整 WAL Batch 结束位置
- CollectingFragments 且当前 segment 不是最后一个需要恢复的 segment: 视为 WAL 中间损坏,报错
```
###### Segment 连续性校验
@@ -522,8 +536,11 @@ CollectingFragments
| CollectingFragments | Last | 追加 payload拼出完整 WAL Batch解析重放后回到 Idle |
| CollectingFragments | Full | 非法 fragment 顺序 |
| CollectingFragments | First | 非法 fragment 顺序 |
| Idle | segment 结束 | 正常结束当前 segment |
| CollectingFragments | segment 结束,且是最后恢复 segment | 丢弃 incomplete batch并截断尾部 |
| CollectingFragments | segment 结束,且不是最后恢复 segment | WAL 中间损坏,报错 |
如果扫描到 WAL 尾部时仍处于 `CollectingFragments` 状态,说明最后一个 WAL Batch 未写完整,丢弃该 incomplete batch并截断到最后一个完整 WAL Batch 结束位置。
Segment 边界不是合法的 fragment 边界。`First + Middle* + Last` 必须全部出现在同一个 segment 内。如果扫描到 WAL 尾部时仍处于 `CollectingFragments` 状态,说明最后一个 WAL Batch 未写完整,丢弃该 incomplete batch并截断到最后一个完整 WAL Batch 结束位置。
###### WAL Batch 校验与重放