
# 原子性与恢复

`AtomicFilesystem::write_atomic()` 会写入临时文件，刷新并 fsync 该文件句柄，将其重命名并覆盖目标路径，然后尝试对父目录执行 fsync。写入失败时会删除对应的临时文件。

写入中的存储实例会在 `.storh/writers/` 下注册一个进程标记，并在正常关闭时将其移除。打开存储时会检查这些标记：如果所有标记的所属进程都仍在运行（或没有标记），打开操作会在常数时间内完成；如果标记所属进程已退出，则说明写入进程崩溃了，打开操作会扫描整个集合，清理孤立的临时文件并清除过期标记。`repair()` 始终会执行扫描。

DocStore 修复时会将损坏或截断的记录文件隔离到 `.storh/corrupt/` 下，并根据剩余有效记录重建二级索引。DocStore 验证还会将索引文件与完整记录扫描结果进行比较，以便在缺失或过期的索引条目被视为正常之前报告问题。

仅追加引擎会先刷新并 fsync 日志文件句柄，然后才将事件视为已在内存中提交。断电后的持久性仍取决于底层文件系统和挂载选项是否遵循文件与目录同步操作。

分段日志会将每一行写成：

```text
<byte-length>\t<xxh32>\t<json>\n
```

读取时会验证字节长度和校验和。恢复过程会扫描每个分段，并将文件截断到最后一行完整且有效的位置，从而移除末尾未完整写入的内容。验证过程会将当前的派生状态索引与独立的分段重放结果进行比较，以便在修复重建索引之前报告外部更改或过期状态。

队列验证会解码每个持久化队列事件，并将当前的待处理、处理中、已完成、负载和认领顺序状态与独立的日志重放结果进行比较。修复会从 `queue.log` 重建状态，并截断末尾不完整的事件。认领和完成操作会在同步持久化日志时持有队列锁，因此多个工作进程不会认领同一个待处理任务。

压缩会写入新的 `compact-*` 分段文件，然后更新清单。如果进程在切换清单之前退出，重新打开时会删除未被引用的压缩输出，并从旧清单继续。压缩完成后会有意保留旧的封存分段，以确保已从这些分段读取数据的读者仍能正常使用。
