Files
zvfs/plan.md
2026-03-02 07:27:48 +00:00

10 KiB
Raw Blame History

ZVFS -> RocksDB LD_PRELOAD 可重入改造计划(给 Codex 执行)

目标:这是一份“可中断、可恢复、可分段提交”的实施计划。
原则:每阶段都能独立编译、独立验证、独立回滚,不要求一次性改完。


0. 执行约束(必须遵守)

  1. 一次只做一个阶段,每阶段结束后必须通过该阶段验收再进入下一阶段。
  2. 不跨阶段混改:例如阶段 1 不改 hook 覆盖,阶段 3 不重构线程模型。
  3. 每阶段必须留下恢复锚点
    • 文档状态更新(本文件“阶段状态”区)
    • 代码中保留临时兼容开关(若有)
    • 能独立提交 commit建议
  4. 失败可回退:阶段内失败仅回退本阶段改动,不影响已完成阶段。
  5. 默认保持旧行为可用,新增能力通过新路径启用,逐步替换旧路径。

1. 阶段状态(可重入)

  • Phase 1: IO 请求结构体落地(单线程兼容模式)
  • Phase 2: Worker + 多线程执行模型落地
  • Phase 3: 偏移型 IO 完整化pread/pwrite 语义)
  • Phase 4: RocksDB 核心 hook 补齐P0
  • Phase 5: 目录/锁/截断接口补齐P1
  • Phase 6: 元数据一致性与崩溃恢复增强
  • Phase 7: 性能增强与观测

重入方式:中断后先看此状态区 + 对应阶段“完成定义”,从未完成阶段继续。


2. 基线与分支策略

2.1 基线要求

  • 当前 libzvfs.so 可正常构建。
  • 当前 func_test 可运行。
  • 已确认 /zvfs 路径仍由现有 hook 接管。

2.2 分支与提交建议

  • 每阶段 1 个分支或 1 个 commit
    • phase1-req-struct
    • phase2-worker-thread
    • phase3-offset-io
    • ...
  • commit message 模板:
    • phaseX: <what> + <compat mode/DoD>

3. 分阶段实施

Phase 1: IO 请求结构体落地(不改线程模型)

目标

先把“每次系统调用复用 file 临时字段”的问题解开;引入可扩展请求对象,为后续多线程做准备。
本阶段仍允许单线程执行路径(内部可继续使用现有 waiter

改动范围

  • zvfs/zvfs.h
  • zvfs/zvfs.c
  • 尽量不改 zvfs/zvfs_hook.c 的接口覆盖面(只做必要适配)

任务清单

  1. 新增 zvfs_req_t(最小字段):
    • op, file, buf, count, offset, ret, op_errno, done
    • 可选:need_copy_back, actual_io_count
  2. 给现有读写流程加“请求入参”版本:
    • 新增 zvfs_read_req(req) / zvfs_write_req(req) 内部函数
    • 旧 API zvfs_read/zvfs_write 变成薄包装(构造 req 后调用)
  3. 去除对 file->io_count/write_staging_buf/finished 的强依赖:
    • 保留字段可兼容,但主流程改为优先使用 req 字段
  4. 错误返回统一:
    • 内部负 errno -> 外部返回值 + errno 映射规则固定

完成定义DoD

  • 编译通过。
  • 现有 func_test 全通过。
  • 无并发改造前提下,行为与旧版一致(回归结果一致)。

重入点

  • 代码里出现 zvfs_req_tzvfs_read/zvfs_write 已封装请求对象。
  • 若中断,优先检查旧路径是否仍可用,再继续替换剩余 call path。

Phase 2: Worker + 多线程执行模型

目标

将“调用线程主动 poll SPDK”改为“专用 worker 线程 poll + 请求队列”。
建立线程安全基础,不追求本阶段 hook 覆盖齐全。

改动范围

  • zvfs/zvfs.h
  • zvfs/zvfs.c
  • zvfs/zvfs_hook.c(仅初始化、提交流程相关)

任务清单

  1. 新增 zvfs_worker_t
    • pthread_t tid
    • 请求队列(先用 mutex + list + cond后续可换 MPSC
    • running/stopping 状态
  2. 新增生命周期函数:
    • zvfs_worker_start()
    • zvfs_worker_stop()
    • 在 mount/init 时启动,在 atexit/unmount 时停止
  3. 新增提交流程:
    • zvfs_submit_and_wait(req):调用线程阻塞等待完成
    • worker 线程执行 SPDK 异步链并回填 req
  4. 替换 waiter() 主路径:
    • 保留 waiter 仅作为 fallback编译开关控制
  5. 锁与并发保护(最小闭环):
    • 全局状态锁:g_fs/g_mounted/fd_map
    • 文件级锁:offset 更新、close 与 io 竞争

完成定义DoD

  • db_bench --threads=4 不崩溃、不死锁(先小规模)。
  • 旧单线程 case 仍通过。
  • 调用线程不再直接 spdk_thread_poll

重入点

  • worker 生命周期已接管主流程(可在日志中看到 worker 启动/停止)。
  • 若中断,优先确保 stop 路径可达,避免进程退出挂死。

Phase 3: 偏移型 IO 完整化pread/pwrite 语义)

目标

补齐 RocksDB 高频调用语义:pread/pwrite 不改文件偏移。
read/write 变成 pread/pwrite + offset 管理 的包装。

改动范围

  • zvfs/zvfs.h
  • zvfs/zvfs.c
  • zvfs/zvfs_hook.c

任务清单

  1. 新增核心接口:
    • zvfs_pread(file, buf, count, offset)
    • zvfs_pwrite(file, buf, count, offset)
  2. 改造 read/write
    • read:在 file lock 下读取并推进 current_offset
    • write:在 file lock 下决定 offset含 O_APPEND
  3. 明确并发语义:
    • 同一 fd 并发 pread/pwrite 允许并行(不同 offset
    • 同一 fd 的 read/write 偏移操作受 file lock 串行

完成定义DoD

  • pread/pwrite 回归用例通过。
  • 同一 fd 的并发 pread 不互相污染 offset。

重入点

  • hook 层可区分 read/writepread/pwrite 路径。
  • 若中断,先保证 read/write 仍有正确 fallback。

Phase 4: RocksDB 核心 hook 补齐P0

目标

先覆盖 RocksDB “能跑起来”最关键接口。

改动范围

  • zvfs/zvfs_hook.c
  • 必要时 zvfs/zvfs.h 新增声明

任务清单(按优先级)

  1. 打开类:
    • open/open64/openat/openat64/__open_2/__open64_2
  2. IO 类:
    • pread/pread64/pwrite/pwrite64
  3. 元数据类:
    • fstat/stat/lstat/fstatat/access
  4. 持久化类:
    • fsync/fdatasync
  5. 文件变更类:
    • rename/renameat/renameat2/unlink/unlinkat

实现要求

  • /zvfs 路径必须透传 real libc。
  • 所有失败分支必须设置 errno。
  • 统一 dlsym 初始化:pthread_once,避免递归。

完成定义DoD

  • RocksDB 基础 workload单线程可跑通
    • fillseq, fillrandom, readrandom
  • strace -f 观察 /zvfs 关键 syscall 已被接管。

重入点

  • 每新增一组 hook 独立提交,失败时可只回退该组。

Phase 5: 目录/锁/截断接口补齐P1

目标

补齐 RocksDB 稳定运行需要的辅助接口。

改动范围

  • zvfs/zvfs_hook.c
  • zvfs/zvfs.c(必要后端支撑)

任务清单

  1. 截断:
    • ftruncate/truncate
  2. 锁:
    • fcntl 最少支持 F_SETLK/F_SETLKW/F_UNLCK/F_GETLK
  3. 目录:
    • mkdir/rmdir/opendir/readdir/closedir
  4. 容量提示(可选但建议):
    • fallocate/posix_fallocate(不支持时明确 EOPNOTSUPP

完成定义DoD

  • RocksDB 多线程基础 workload 可稳定运行(--threads=4/8)。
  • LOCK 文件语义满足单进程多线程正确性。

重入点

  • fcntl 与目录接口可拆成两个子阶段提交。

Phase 6: 元数据一致性与崩溃恢复增强

目标

解决 zvfs_meta.txt 全量覆盖、崩溃窗口、恢复不确定问题。

改动范围

  • zvfs/zvfs_hook.cmeta load/save
  • zvfs/zvfs.c(关键操作强制 flush 时机)
  • 文档与测试脚本

任务清单

  1. 元数据写入策略:
    • 从“每次全量覆盖”改为“批量 + 关键点强制刷盘”
  2. 元数据文件格式增强:
    • 增加版本号 + CRC
  3. 恢复路径:
    • 加载失败时回退到最近完整版本(若实现双文件/快照)
  4. 关键操作一致性点:
    • rename/unlink/truncate/fsync 后策略明确

完成定义DoD

  • 人工崩溃注入后kill -9可重新打开 DB。
  • 元数据损坏能被检测并报错,不默默产生脏状态。

重入点

  • 先做“格式版本化 + 校验”,再做“批量策略”。

Phase 7: 性能增强与观测

目标

在正确性稳定后提性能,确保收益可量化。

改动范围

  • zvfs/zvfs.c
  • 性能脚本与统计输出

任务清单

  1. 请求对象池 / DMA buffer 复用
  2. 减少全局锁竞争(热点路径细化锁)
  3. 元数据 debounce 与批量刷盘参数化
  4. 增加指标:
    • op 次数、失败率、平均/尾延迟、队列深度

完成定义DoD

  • 与旧版本对比,多线程 db_bench 吞吐显著提升(目标 >1.5x,按实测调整)。
  • 无新增数据一致性回归。

重入点

  • 每个优化项独立开关,可单独启停做 A/B。

4. 阶段间依赖关系(严格)

  1. 必须先完成 Phase 1 再做 Phase 2。
  2. 必须先完成 Phase 2 再做 Phase 3/4。
  3. Phase 4核心 hook完成后再做 Phase 5辅助 hook
  4. Phase 6/7 必须基于 Phase 5 稳定分支。

5. 验收矩阵(执行时打勾)

Phase 1

  • 编译通过
  • func_test 通过
  • 请求对象已接管 read/write 内部主流程

Phase 2

  • worker 模型接管
  • 多线程 smoke 测试通过
  • 无死锁/退出挂死

Phase 3

  • pread/pwrite 语义正确
  • offset 并发污染问题消失

Phase 4

  • P0 hook 集合已实现
  • RocksDB 单线程 workload 跑通

Phase 5

  • P1 hook 集合已实现
  • RocksDB 多线程 workload 稳定

Phase 6

  • 元数据校验与恢复策略生效
  • 崩溃恢复测试通过

Phase 7

  • 关键指标可观测
  • 性能目标达成或给出瓶颈结论

6. 风险与应对(执行版)

  1. 接口补齐不完整
    • 应对:每阶段跑 strace -f,统计 /zvfs 相关未接管 syscall。
  2. 并发死锁
    • 应对固定锁顺序debug 模式打印锁获取日志。
  3. 行为回归
    • 应对保留兼容开关old path直到该阶段验收通过。
  4. 性能退化
    • 应对:优化项独立开关,逐项 A/B不一次性合并。

7. 给 Codex 的执行模板(每阶段复用)

  1. 阅读本阶段“目标/任务/DoD/重入点”
  2. 只改本阶段文件与最小依赖
  3. 本地编译 + 对应测试
  4. 更新“阶段状态”勾选
  5. 输出:
    • 改了什么
    • 测了什么
    • 还剩什么
  6. 等待用户确认再进入下一阶段