pi_append_log 0.4.0

Storage-agnostic append-only block log traits, codec, layout, and file backend
# pi_append_log

当前版本提供追加日志公共 trait、默认 V1 块 codec、固定八位文件 Layout 和文件存储实现;网络与对象存储后端尚未实现。文件 segment 使用无后缀的八位编号,最大编号是 active,其余编号是 closed;允许结构编号缺号,rotate 保留旧 segment 并创建下一个编号。archive 使用同一 namespace 内的 rename,暂不支持跨磁盘 copy。

文件后端基于 `pi_async_fs`,公共错误使用 `pi_result`。文件后端采用单 owner:同一物理 root 的多任务必须共享同一个 `Arc<FileAppendLog<L>>`,而不是重复 build。调用方负责排除同 root 的其他 owner、其他进程、旁路 I/O 和 locator 别名;库不检测或拒绝重复 build。`append` 接受任意 `DetachableWriteBuffer`,`append_stream` 支持把异步 chunk 流作为一个连续逻辑 block 写入。首次底层 append 后的错误或取消只使当前 owner 进入 `InvalidState`,保留已写前缀;后续操作需 drop 当前 owner 并按恢复规则重新 build。测试使用 Tokio,库本身不绑定具体异步运行时。

本库只发出 `tracing` 事件;应用可通过 `pi_append_log::observability::init_observability` 初始化 `pi_logger` 日志 subscriber。

接口契约、设计取舍及默认 V1 格式提案见 [追加日志技术设计提案](docs/append-log-design.md)。

## 稳定结构 Key

`AppendLog::Closed` 实现公开的 `ClosedStructure` trait,通过 `structure_key()` 返回借用句柄的 `&str`,用于关联 checkpoint、索引或 manifest 元数据。文件后端统一按 `u64` 结构 ID 生成至少八位十进制 key(例如 `00000001`);自定义 Layout 的路径命名不影响 key,宽于八位的 ID 保留完整数字,不截断。key 不含 root、owner 或 `.archive`,在同一逻辑日志范围内唯一,owner 重建和归档后保持不变。不同 root 或删除重建不保证唯一,外部应使用 `(log_identity, key)`。

`AppendLogVisitor::visit` 的 `BlockVisitContext<'_>` 同时提供 `structure_key: &str` 和物理首尾边界。Forward/Backward 以及 active/closed 使用同一 key 规范;context 中的 key 只保证在本次调用期间有效,需要长期保存时应复制。active 有 key,但不是可归档的 Closed。字符串不能构造 `FileClosed` 或授权归档;archive 仍要求同一 owner 的句柄,重建后应使用本次 build 的 `recovered_closed`,即使旧句柄 key 相同也会被拒绝。

`log_identity` 由调用方持久维护:同一逻辑日志的 owner 重建应沿用它,不同日志或删除后重新创建的日志应使用不同值。它不是 owner token,也不是库自动生成的 root 身份。`ClosedStructure` 的借用绑定句柄生命周期,与 visitor 的单次调用借用不同;需要脱离句柄保存时同样应复制 key。`Debug` 仅用于诊断,不是持久身份或序列化协议。

## 0.1 -> 0.2 迁移说明

0.2 是破坏性升级:

- Layout 的 active/closed 命名替换为 segment_name/archive_name;segment 无后缀,最大 ID 为 active,允许缺号,rotate 不重命名旧 segment。
- AppendLog 的 Block 关联类型已移除;append 改用 DetachableWriteBuffer,并新增 append_stream。公共错误改用 pi_result::Result。
- 文件归档仍是在同一 namespace 内执行 rename,不支持跨磁盘 copy。