10 KiB
10 KiB
ZVFS -> RocksDB LD_PRELOAD 可重入改造计划(给 Codex 执行)
目标:这是一份“可中断、可恢复、可分段提交”的实施计划。
原则:每阶段都能独立编译、独立验证、独立回滚,不要求一次性改完。
0. 执行约束(必须遵守)
- 一次只做一个阶段,每阶段结束后必须通过该阶段验收再进入下一阶段。
- 不跨阶段混改:例如阶段 1 不改 hook 覆盖,阶段 3 不重构线程模型。
- 每阶段必须留下恢复锚点:
- 文档状态更新(本文件“阶段状态”区)
- 代码中保留临时兼容开关(若有)
- 能独立提交 commit(建议)
- 失败可回退:阶段内失败仅回退本阶段改动,不影响已完成阶段。
- 默认保持旧行为可用,新增能力通过新路径启用,逐步替换旧路径。
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-structphase2-worker-threadphase3-offset-io- ...
- commit message 模板:
phaseX: <what> + <compat mode/DoD>
3. 分阶段实施
Phase 1: IO 请求结构体落地(不改线程模型)
目标
先把“每次系统调用复用 file 临时字段”的问题解开;引入可扩展请求对象,为后续多线程做准备。
本阶段仍允许单线程执行路径(内部可继续使用现有 waiter)。
改动范围
zvfs/zvfs.hzvfs/zvfs.c- 尽量不改
zvfs/zvfs_hook.c的接口覆盖面(只做必要适配)
任务清单
- 新增
zvfs_req_t(最小字段):op,file,buf,count,offset,ret,op_errno,done- 可选:
need_copy_back,actual_io_count
- 给现有读写流程加“请求入参”版本:
- 新增
zvfs_read_req(req)/zvfs_write_req(req)内部函数 - 旧 API
zvfs_read/zvfs_write变成薄包装(构造 req 后调用)
- 新增
- 去除对
file->io_count/write_staging_buf/finished的强依赖:- 保留字段可兼容,但主流程改为优先使用 req 字段
- 错误返回统一:
- 内部负 errno -> 外部返回值 + errno 映射规则固定
完成定义(DoD)
- 编译通过。
- 现有
func_test全通过。 - 无并发改造前提下,行为与旧版一致(回归结果一致)。
重入点
- 代码里出现
zvfs_req_t且zvfs_read/zvfs_write已封装请求对象。 - 若中断,优先检查旧路径是否仍可用,再继续替换剩余 call path。
Phase 2: Worker + 多线程执行模型
目标
将“调用线程主动 poll SPDK”改为“专用 worker 线程 poll + 请求队列”。
建立线程安全基础,不追求本阶段 hook 覆盖齐全。
改动范围
zvfs/zvfs.hzvfs/zvfs.czvfs/zvfs_hook.c(仅初始化、提交流程相关)
任务清单
- 新增
zvfs_worker_t:pthread_t tid- 请求队列(先用 mutex + list + cond,后续可换 MPSC)
running/stopping状态
- 新增生命周期函数:
zvfs_worker_start()zvfs_worker_stop()- 在 mount/init 时启动,在 atexit/unmount 时停止
- 新增提交流程:
zvfs_submit_and_wait(req):调用线程阻塞等待完成- worker 线程执行 SPDK 异步链并回填 req
- 替换
waiter()主路径:- 保留 waiter 仅作为 fallback(编译开关控制)
- 锁与并发保护(最小闭环):
- 全局状态锁:
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.hzvfs/zvfs.czvfs/zvfs_hook.c
任务清单
- 新增核心接口:
zvfs_pread(file, buf, count, offset)zvfs_pwrite(file, buf, count, offset)
- 改造 read/write:
read:在 file lock 下读取并推进current_offsetwrite:在 file lock 下决定 offset(含 O_APPEND)
- 明确并发语义:
- 同一 fd 并发 pread/pwrite 允许并行(不同 offset)
- 同一 fd 的 read/write 偏移操作受 file lock 串行
完成定义(DoD)
pread/pwrite回归用例通过。- 同一 fd 的并发
pread不互相污染 offset。
重入点
- hook 层可区分
read/write与pread/pwrite路径。 - 若中断,先保证
read/write仍有正确 fallback。
Phase 4: RocksDB 核心 hook 补齐(P0)
目标
先覆盖 RocksDB “能跑起来”最关键接口。
改动范围
zvfs/zvfs_hook.c- 必要时
zvfs/zvfs.h新增声明
任务清单(按优先级)
- 打开类:
open/open64/openat/openat64/__open_2/__open64_2
- IO 类:
pread/pread64/pwrite/pwrite64
- 元数据类:
fstat/stat/lstat/fstatat/access
- 持久化类:
fsync/fdatasync
- 文件变更类:
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.czvfs/zvfs.c(必要后端支撑)
任务清单
- 截断:
ftruncate/truncate
- 锁:
fcntl最少支持F_SETLK/F_SETLKW/F_UNLCK/F_GETLK
- 目录:
mkdir/rmdir/opendir/readdir/closedir
- 容量提示(可选但建议):
fallocate/posix_fallocate(不支持时明确EOPNOTSUPP)
完成定义(DoD)
- RocksDB 多线程基础 workload 可稳定运行(
--threads=4/8)。 - LOCK 文件语义满足单进程多线程正确性。
重入点
fcntl与目录接口可拆成两个子阶段提交。
Phase 6: 元数据一致性与崩溃恢复增强
目标
解决 zvfs_meta.txt 全量覆盖、崩溃窗口、恢复不确定问题。
改动范围
zvfs/zvfs_hook.c(meta load/save)zvfs/zvfs.c(关键操作强制 flush 时机)- 文档与测试脚本
任务清单
- 元数据写入策略:
- 从“每次全量覆盖”改为“批量 + 关键点强制刷盘”
- 元数据文件格式增强:
- 增加版本号 + CRC
- 恢复路径:
- 加载失败时回退到最近完整版本(若实现双文件/快照)
- 关键操作一致性点:
rename/unlink/truncate/fsync后策略明确
完成定义(DoD)
- 人工崩溃注入后(kill -9)可重新打开 DB。
- 元数据损坏能被检测并报错,不默默产生脏状态。
重入点
- 先做“格式版本化 + 校验”,再做“批量策略”。
Phase 7: 性能增强与观测
目标
在正确性稳定后提性能,确保收益可量化。
改动范围
zvfs/zvfs.c- 性能脚本与统计输出
任务清单
- 请求对象池 / DMA buffer 复用
- 减少全局锁竞争(热点路径细化锁)
- 元数据 debounce 与批量刷盘参数化
- 增加指标:
- op 次数、失败率、平均/尾延迟、队列深度
完成定义(DoD)
- 与旧版本对比,多线程
db_bench吞吐显著提升(目标 >1.5x,按实测调整)。 - 无新增数据一致性回归。
重入点
- 每个优化项独立开关,可单独启停做 A/B。
4. 阶段间依赖关系(严格)
- 必须先完成 Phase 1 再做 Phase 2。
- 必须先完成 Phase 2 再做 Phase 3/4。
- Phase 4(核心 hook)完成后再做 Phase 5(辅助 hook)。
- 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. 风险与应对(执行版)
- 接口补齐不完整
- 应对:每阶段跑
strace -f,统计/zvfs相关未接管 syscall。
- 应对:每阶段跑
- 并发死锁
- 应对:固定锁顺序;debug 模式打印锁获取日志。
- 行为回归
- 应对:保留兼容开关(old path)直到该阶段验收通过。
- 性能退化
- 应对:优化项独立开关,逐项 A/B,不一次性合并。
7. 给 Codex 的执行模板(每阶段复用)
- 阅读本阶段“目标/任务/DoD/重入点”
- 只改本阶段文件与最小依赖
- 本地编译 + 对应测试
- 更新“阶段状态”勾选
- 输出:
- 改了什么
- 测了什么
- 还剩什么
- 等待用户确认再进入下一阶段