jabberwocky238 d7e4fc73b1 phase2: COW append-only backend (segment diff + in-memory VFS)
- store: data.raw append-only / read-by-offset / fsync
- meta: Segment/VersionDiff/FileView/FileMeta + segment-list update (append/overwrite/truncate/extend) + in-memory directory tree
- fuse_ops: all callbacks route through COW backend; per-write Segment logging (op/phys_off/log_off/size)
- tests/phase2.sh: 5 write scenarios + sqlite + dir ops, 24/24 pass

Metadata in-memory (restart-lost; persistence phase 5); data.raw retains superseded bytes pending GC. cur cache / aggregation / multi-block land in phase 3.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-07 00:52:33 +08:00
2026-07-07 00:18:31 +08:00
2026-07-07 00:07:58 +08:00

etvd — 第二阶段:内存段 DiffCOW append-only 后端)

对应 docs/DESIGN.md 第二阶段。在第一阶段 FUSE 拦截通路基础上,把透传后端替换为 COW append-only 后端:写入只追加到 data.raw 尾部、以 Segment 拼接还原视图,验证五种写入场景并从底层观测每一次 write 的 Segment。

这一步做了什么

  • 第一阶段的 FUSE 拦截层保留,后端由「透传真实目录」改为 COW append-only
    • 物理存储是单个稀疏文件 data.raw,唯一写接口是 Append(顺序追加,绝不原地改字节)。
    • 每次 write 把新字节追加到 data.raw 尾部,得到一个物理段 Segment(phys_off / phys_len / log_off)。
    • 一个虚拟文件的当前内容由一组 Segment 按逻辑顺序拼接;段间 gap 即空洞,读返回零。
    • 读取按当前视图段链表从 data.raw 取字节拼接。
  • 全自管内存 VFS:目录树 + 文件元数据全部内存维护,data.raw 只存文件字节。getattr / readdir / mkdir / unlink / rename 全走内存。
  • 即时写 + 当前视图write 立即 Append + 生成 Segment/VersionDiff + 增量更新当前视图;read 走当前视图;fsync 对 data.raw 落盘并标记版本边界。事务暂存圈定留到第六阶段。
  • 底层观测:每次 write 打印 [etvd] write <path> op=<Append|Overwrite|Truncate|Extend> phys=<off>+<len> off=.. size=.. data=<hex>,满足「从底层观察每一次写入产生的 Segment」。
  • 元数据纯内存,进程重启丢失(第五阶段 KV 持久化);data.raw 残留旧字节待后续 GC。

三层结构

上层程序 (bash / cat / dd / sqlite3)
        │  标准 Linux 文件 API
        ▼
FUSE 拦截层  (src/fuse_ops.cpp)   ── 每次调用先记日志,再走 COW 后端
        │
        ▼
内存元数据层 (src/meta.cpp)       ── 段链表(当前视图)+ 目录树 + historyshared_mutex 保护
        │
        ▼
物理存储层  (src/store.cpp)       ── data.rawAppend-only / 按偏移读

依赖

Linux + /dev/fuselibfuse3-devpkg-configg++C++17)、makesqlite3(测试用)。

sudo apt-get install -y libfuse3-dev pkg-config g++ make sqlite3

构建

make

产出可执行文件 etvd_fuse

运行

./etvd_fuse mnt -o backend=./backend
  • -o backend=DIR:存放 data.raw 的目录(默认 ./backend,不存在会自动创建)。
  • -f 前台运行(已强制开启);-o default_permissions已强制开启)。
  • 日志输出到 stderr。

卸载

fusermount3 -u mnt

验收测试

bash tests/phase2.sh

脚本覆盖五种写入场景 + SQLite + 目录操作,并校验底层日志的 Segment 观测,末尾打印 RESULT: pass=N fail=M

五种写入语义

场景 行为 日志可见
追加写 echo >> write op=Append phys=<尾部off>+<len>
随机覆盖写 dd seek= / SQLite UPDATE 改页 write op=Overwrite phys=<新off>+<len>(旧字节仍在 data.raw,仅视图摘出)
截断缩小 truncate -s N truncate size=N,视图裁剪,不产生新物理字节
截断扩大 / 扩展写 truncate -s M / dd seek=超末尾 write op=Extend,中间空洞读返回零
Fsync 边界 SQLite COMMIT fsync datasync=..,对 data.raw 落盘

实现要点

  • 分层store(物理 Append/Read/Sync+ meta(段链表算法 + 目录树 + history+ fuse_ops(回调)。meta 方法不加锁,由 fuse_opsMeta::Lock()shared_mutex)在外层界定读写锁;write 持写锁跨 Store::Append,保证段落盘与视图更新原子。
  • 段链表更新write 区间 [off,off+len) 把相交旧段拆成前缀/后缀片段、丢弃被覆盖中段、插入新段,保持升序不重叠,gap 即空洞。truncate 缩小裁剪段、扩大只增长 total_size
  • 即时写 + 当前视图:当前视图(FileView)是 cur 的朴素前身;第三阶段才在其上做 LRU 缓存 / 聚合版本 / 多 Block 分块。historyVersionDiff 列表)保留供观测与未来 MVCC,不参与读路径。
  • 全自管内存 VFS:根 /Meta 构造时建立;路径经 Parent/Base 拆分管理父子;rename 目录时遍历子树改 key 前缀。
  • fsync 真落盘fsync 回调 ::fsync(data.raw fd)SQLite 依赖其持久性。
  • lock 回调对 F_GETLK 返回 F_UNLCKF_SETLK 返回 0SQLite fcntl 建议锁单进程正常运行。真实互斥留第十一阶段。
  • default_permissions:getattr 返回内存元数据的 mode/uid/gid,内核据此校验;以当前用户身份运行,文件 0644 / 目录 0755。
  • 重启语义data.raw 的 append 游标从文件大小恢复(继续追加、不覆写旧字节),但内存元数据丢失,旧字节成孤儿待 GC。
  • 未实现 opreadlink / symlink / fallocate / xattr)保持 NULLFUSE 返回 -ENOSYS

第二阶段边界(明确不做)

cur 的 LRU 缓存 / 聚合版本 / 多 Block 分块(第三阶段)、KV 持久化(第五阶段)、事务 TxnBegin/End(第六阶段)、Prefix 策略(第七阶段)、MVCC 恢复(第八阶段)、etcd 协议(第九阶段)、QUIC mTLS Raft(第十阶段)、分布式租约(第十一阶段)、GC 空洞回收、冷数据归档、symlink / 设备文件。

文件

src/store.h/.cpp        物理存储:data.raw Append / Read / Sync
src/meta.h/.cpp         内存元数据:Segment/VersionDiff/FileView/FileMeta + 段链表算法 + 目录树
src/fuse_ops.h/.cpp     FUSE3 回调:日志 + 走 COW 后端 + ops 表
src/main.cpp            参数解析、绝对路径、打开 data.raw、初始化 Meta、挂载入口
src/logger.h            线程安全操作日志 + hex 预览
tests/phase2.sh         五种场景 + SQLite 验收脚本
Makefile                pkg-config fuse3
S
Description
No description provided
Readme 86 KiB
Languages
C++ 87.3%
Shell 11.3%
Makefile 1.4%