Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

PLAN — 完成宗旨

宗旨: 自研 Rust Swift-native FUSE client。 ProxyFS distributed-client/lease + JuiceFS chunk/slice/block+compaction

  • rclone native Swift + GeeseFS async/parallel IO + Mountpoint 高吞吐 data path ⟹ metadata 与 data 分离 / control plane 强一致 / data plane 完全 scale-out / object 永远 immutable / mutation 只发生在 metadata

Phase 1(thin adapter,已交付)达成度 ~45%:native Swift 数据路径 ✓、 scale-out 客户端 ✓、对象级不可变 ✓。缺 chunk 数据模型、强一致控制面、 并行读吞吐。本计划以完成宗旨为唯一目标,不再新增 thin-adapter 功能。

目标架构

                 control plane (强一致)
 ┌───────────────────────────────────────┐
 │ MetadataEngine (trait)                │
 │  ├─ P1: SQLite (单机验证数据模型)     │
 │  └─ P2: etcd + lease (多客户端强一致) │
 │  inode: size/mode/times/version       │
 │  chunk map: chunks → slices → blocks  │
 │  原子 commit: CAS on version          │
 └───────────────┬───────────────────────┘
                 │
 ┌───────────────▼───────────────────────┐
 │ FUSE client (ChunkFs, 每节点一个)     │
 │  lookup/getattr/readdir ← engine      │
 │  read  ← blocks 并行 GET + hole 补零  │
 │  write → slice buffer → blocks PUT    │
 │          → metadata commit (先数据后元数据) │
 │  compaction: 后台合并 slices          │
 │  GC: 孤儿 blocks 异步回收             │
 └───────────────┬───────────────────────┘
                 │ data plane (完全 scale-out, 直连 Swift)
                 ▼
          Swift: 容器 <fsid>-data, 对象 = immutable blocks
          文件 ≠ 对象; 对象永远只写一次

数据模型(JuiceFS 对齐,参数可配)

inode
├─ chunk 0    [0, 64MiB)
│   ├─ slice A   start=0    blocks=[b0,b1]      ← 一次 flush 的产物
│   └─ slice B   start=1MiB blocks=[b2]         ← 随机写 = 新 slice, 不读旧数据
├─ chunk 1    [64, 128MiB)
│   └─ slice C ...
  • CHUNK_SIZE = 64 MiB(逻辑),BLOCK_SIZE = 4 MiB(物理,可配)
  • block 对象名:<inode-hex>/<chunk>/<slice-id>/<block-idx>,全局唯一、永不覆盖
  • slice-id 由 metadata 引擎分配(单调),保证跨客户端不冲突
  • chunk 未覆盖区间 = holes → 读补零
  • 覆盖语义:先 PUT blocks(幂等,孤儿无害),再 metadata CAS commit (崩溃 = 孤儿 blocks,GC 回收;读者永远看不到半成品)

阶段与验收

阶段内容宗旨映射验收
P1 ✅SQLite 元数据引擎 + chunk/slice/block 读写 + 原子 commit + compaction + GCJuiceFS 数据模型、metadata/data 分离、object immutable单机挂载:随机 4K 写 100MB 不重读旧数据;md5 全对;45/45 测试;双挂载并发 CAS 一致
P2 ✅etcd 引擎(同 trait)+ open/close lease + txn CASProxyFS lease、control plane 强一致双节点并发写一致;EBUSY + TTL 崩溃回收实测通过
P3 ✅窗口放大读 + 块缓存 + inflight 去重 + path 缓存 + write-backGeeseFS/Mountpoint 吞吐热读 915MB/s、冷读 80MB/s(Swift 集群上限)、stat 3ms
P4 ✅4 节点部署 + benchmark matrix + 一致性验证整体docs/BENCHMARK.md 全矩阵;多节点并发写/读实测

里程碑(本仓库分支策略)

  • main = 宗旨实现主线;thin adapter 保留为 --mode=raw
  • 每阶段合并前提:单元测试 + 集群集成验证全绿

P1 交付证据(2026-08-07, Contabo lab)

  • 数据面块布局实测:100MB 文件 = chunk0(64MB,17块) + chunk1(36MB,9块); 50MB 处 8KB 随机写 = 1 个新 8KB 块,旧块零接触(chunkfs-data 容器审计)
  • 双挂载(同 meta.db)并发写同一文件:CAS Conflict 重试,两端读到一致结果
  • compaction:30 次重叠写 → 合并为 1 块,读数据前后一致;与并发写竞争时 Conflict 恢复正确(新 slices 优先,JuiceFS 同款语义)
  • hole 补零、跨 chunk 读、truncate(纯 metadata)、dir rename(纯 metadata)全过

已定决策(不再回问)

  1. 元数据引擎先 SQLite(数据模型验证),P2 换 etcd —— trait 隔离,写路径不变
  2. 文件 ≠ Swift 对象(宗旨必然推论,ProxyFS 2.x 同样放弃 bi-modal); 原始对象可见性由 --mode=raw 保留
  3. 一致性 = lease-based close/open(ProxyFS 路线),非 close-to-open
  4. chunk 参数默认 64MiB/4MiB,Swift 实测后按 U 型曲线调