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 之前任一步失败,该 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 何时可删除由后续文件生命周期规则定义。 MANIFEST 不在每次 WAL segment 轮转时更新。MANIFEST 表示 recovery 起点 / checkpoint 状态,只在 MemTable flush、SSTable 与 checkpoint 元数据都持久化后推进;旧 WAL segment 何时可删除由后续文件生命周期规则定义。
#### 设计决策 #### 设计决策
@@ -309,6 +311,14 @@ Physical Record 的顺序由 WAL 文件的顺序追加和顺序扫描保证,
- Physical Record 的 `length` 必须 `> 0` - Physical Record 的 `length` 必须 `> 0`
- 读取时Block 尾部 padding 必须全为 0如果 padding 区出现非 0 bytes视为 WAL 损坏 - 读取时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
WAL Batch 是物理持久化单元,对应一次 group commit batch。恢复时必须拼出完整 WAL Batch 后才能重放,不能重放半个 batch。WAL Batch 不是事务边界;一个 WAL Batch 可以包含多个独立 autocommit 写入,后续也可以包含一个或多个事务提交记录。 WAL Batch 是物理持久化单元,对应一次 group commit batch。恢复时必须拼出完整 WAL Batch 后才能重放,不能重放半个 batch。WAL Batch 不是事务边界;一个 WAL Batch 可以包含多个独立 autocommit 写入,后续也可以包含一个或多个事务提交记录。
@@ -444,7 +454,11 @@ MANIFEST 是 recovery 起点和 checkpoint 状态的权威源,记录最老仍
6. 在每个 Block 内按 Physical Record 顺序解析 6. 在每个 Block 内按 Physical Record 顺序解析
7. 将 Full 或 First/Middle/Last 拼成完整 WAL Batch 7. 将 Full 或 First/Middle/Last 拼成完整 WAL Batch
8. 校验并重放 WAL Batch 中的 Entries 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 连续性校验 ###### Segment 连续性校验
@@ -522,8 +536,11 @@ CollectingFragments
| CollectingFragments | Last | 追加 payload拼出完整 WAL Batch解析重放后回到 Idle | | CollectingFragments | Last | 追加 payload拼出完整 WAL Batch解析重放后回到 Idle |
| CollectingFragments | Full | 非法 fragment 顺序 | | CollectingFragments | Full | 非法 fragment 顺序 |
| CollectingFragments | First | 非法 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 校验与重放 ###### WAL Batch 校验与重放