
Fletch:在可编程交换机里缓存分布式文件系统元数据
📄 论文解读
Fletch: File-System Metadata Caching in Programmable Switches Qingxiu Liu, Jiazhen Cai, Siyuan Sheng, Yuhui Chen, Lu Tang, Zhirong Shen, Patrick P. C. Lee arXiv:2510.08351v3 · 2026 年 5 月 · 17 页 GitHub: https://github.com/adslabcuhk/fletch
一句话核心
把 HDFS 的元数据缓存从客户端搬到可编程交换机数据平面里,用 P4 写到 Tofino, 直接在网络关键路径上吸收读请求,把一致性维护从 O(N) 降到 O(1),同时用 “in-switch 集中化"天然吸收热点请求、缓解后端负载倾斜。
为什么需要它
- 百度 AI Cloud 文件系统请求 67–96% 是元数据;Yahoo HDFS 3% 文件承担 34–39% 请求——访问严重倾斜。
- 客户端缓存三大痛点:
- 一致性维护 O(N),每次写要通知所有客户端;
- 客户端缓存各自为战,没法协同消除后端负载倾斜;
- 现有方案(IndexFS / InfiniFS)只能缓存目录权限,attribute 仍要回 server。
- 可编程交换机正好相反:处在所有 client↔server 关键路径上,集中化天然解决这两 个问题——一致性 O(1),热点天然吸收。
三大关键技术
| 技术 | 做什么 |
|---|---|
| 路径感知缓存管理 (§IV) | 缓存一条路径必同时缓存它所有祖先;用 CMS 估频 + frequency counter 精确统计;淘汰按"无后代且频率最低"递归淘汰祖先 |
| 多级读-写锁 (§V) | 8 个 lock counter array(按路径层级分配),每个 64K 槽 16-bit;validation flag 1-bit 标记有效;靠 recirculation 让 PHV 沿路径层层处理 |
| 本地哈希冲突解决 (§VI) | 64-bit MD5 + 8-bit token = (hash, token);controller 同步 token 给 switch / server / client;避免每次问 controller |
多级锁 P4 拆解
下面是基于论文 §V 推演的 P4 风格伪代码——P4_16 没有循环/递归,多级路径解析靠 packet recirculation(一次处理一层,再把包送回 ingress)。
数据结构
// === Lock counter arrays: 8 组,每组 64K 槽 × 16-bit ===
register lock_array_1 { Width: 16; Size: 65536; } // level 1: /a
register lock_array_2 { Width: 16; Size: 65536; } // level 2: /a/b
register lock_array_3 { Width: 16; Size: 65536; } // level 3: /a/b/c
// ... 共 7 个;level 8+ 共享 lock_array_8
// === Validation flags: 1 bit / cached path ===
register validation { Width: 1; Size: <hash_space>; }
// === Cache values ===
register cache_value_lo { Width: 32; Size: <num_slots>; }
register cache_value_hi { Width: 32; Size: <num_slots>; }
读路径(单路径读,如 open/stat)
每一层都要:① 加锁 → ② 校验 → ③ 权限检查 → ④ 通过则去下一层 → ⑤ 上一层解锁。 整个流程靠 recirculation 实现:
action process_level() {
lvl = cur_level;
idx = hash_low16_at_level(lvl); // 从 PHV 取预计算的 hash
valid = validation.read(idx);
if (valid == 1) {
lock_val = lock_array_X.read(idx);
lock_array_X.write(idx, lock_val + 1); // 读锁:counter += 1
meta = read_cache(idx);
if (!permission_check(meta)) {
failed = 1; fail_level = lvl;
}
} else {
failed = 1; fail_level = lvl; // 失效或正在写
}
if (lvl > 1) release_lock(lvl - 1); // 释放上一层锁
if (cur_level < depth && !failed) {
cur_level = lvl + 1;
recirculate(); // 处理下一层
} else {
release_lock(lvl); // 释放最后一层
if (!failed) send_response_to_client(); // packet cloning 直返
else forward_to_server();
}
}
写路径(单路径写,如 chmod/chown)
action process_write() {
idx = hash_low16_at_level(depth_of_target);
cur = lock_array_X.read(idx);
if (cur > 0) { recirculate(); return; } // spin-wait 直到归零
validation.write(idx, 0); // 独占
forward_to_server(); // write-through
}
action on_write_response() {
if (success) update_cache(idx, new_metadata);
validation.write(idx, 1); // 不论成败都恢复可读
// 用 sequence number 防 ACK 丢失造成的 lock 双重 decrement
send_ack_to_server();
}
写饥饿隐患(论文自陈): reader-preferring,写等待时新读会持续 ++counter, 写永远等不到锁。这是 Fletch 当前明确遗留的限制。
多路径写(chmod -R)的 top-down 技巧
父路径的 validation 一直 = 0,直到所有 cached 后代都更新完才置 1, 任何中途到来的读都会被拦下、走 server,永远读不到"父已更新、子未更新"的混合状态。
P4 实际写不出来的部分——“循环"怎么落地
| 逻辑 | P4 实现 |
|---|---|
| 解析 level 1..depth | 每个 PHV 包最多 recirculate depth + 1 次 |
| 按层级选不同 lock_array | ingress pipeline 里根据 cur_level 做 if-else |
| 每个 memory block 每次只能访问一次 | 把 lock_array / validation / cache_value 放在不同 pipeline 阶段 |
关键实验数字(128 模拟 server)
| 负载 | Fletch 相对 NoCache | Fletch+(加客户端缓存)相对 CCache |
|---|---|---|
| Alibaba | +11.0% | +14.7% |
| Training | +134.6% | +103.6% |
| Thumb | +181.6% | +139.6% |
| +71.2% | +57.3% |
- 延迟:读密集场景 @ 0.26 MOPS,Fletch 把 NoCache 的 avg / p95 / p99 降 64.6% / 89.8% / 87.7%。
- 交换机资源:SRAM 58.4% / Stage 100% / ALU 98% / PHV 93%——比 NetCache / FarReach 略高,但还能用。
- 恢复时间:server 0.5–2.1 s / controller < 40 ms / switch 14.6–75.5 s(最慢,要 replay admission)。
论文自陈的局限
- 写饥饿未解决(reader-preferring lock,writer 可能永远拿不到锁)
- 写密集负载掉链:chmod > 50% 时吞吐开始跌
- PHV 已用到 93%,再加功能就撞资源墙
- 强假设:root 永久缓存、permissions 不变
- 多交换机拓扑未讨论,目前只跑了一台 Tofino
我的判断
思路对、实现完整、评估扎实。但有两个工程隐患值得注意:
- 写饥饿是真实部署的雷区,生产环境如果 chmod / rename 多就会卡;
- PHV 93% 离天花板很近,未来想加任何 feature 都会撞资源墙。

