Coda 1990 — 笔记本拔网线照样写文件,重连后自动合并
是什么
Coda 是 CMU 在 AFS 基础上做的下一代分布式文件系统。同一个团队(Satyanarayanan 牵头),同一个 /afs 风格的全局命名空间,但解决了 AFS 解决不了的两件事:
- 服务器挂了一台,客户端不要跟着停——多副本服务器,写入随便选一台活的
- 笔记本拔网线带去咖啡馆,照样能改文件——离线本地操作,重连后回放并合并冲突
日常类比:AFS 像一家连锁书店,你借书要去店里(要联网);Coda 像图书馆给你办了”自助借阅 + 离线携带”——你在飞机上把书改满批注,落地连上网,图书馆系统读你的批注日志一条条回放,跟别人的修改自动合并;真合不上的就标记”冲突,你来选”。
技术上 Coda 把”高可用”拆成两根支柱:
- 服务器复制(server replication):一个 volume 在多台服务器上有副本,乐观写入,事后协调
- 断连操作(disconnected operation):客户端把”网络消失”当成一种正常状态,本地把所有修改记成日志,重连后批量回放
为什么重要
- 移动计算的开山论文之一——1990 年笔记本刚开始普及,作者直接预言”网络间歇性可用是常态”,比 iPhone 早 17 年
- 把”乐观复制 + 事后合并”这套思路第一次完整落地。Bayou 1995、Git、Dropbox、iCloud Drive 全在这条路上
- AFS 强调”扩展性”(一台服务器服务上千客户端),Coda 强调”可用性”(任何一端故障都不让用户停手)。这是两种不同的设计取向,值得对照看
- “冲突不可避免,必须显式处理”——这一观念让后来所有协作工具(包括版本控制)有了理论根基
- 直到 2000 年代,CMU 学生笔记本仍在跑 Coda;OpenAFS 至今活着是 AFS 路线,Coda 路线则被 Dropbox 之类商业产品继承
核心要点
1. 两根支柱解一个问题
可用性 = “任何一端故障都不让用户停”。Coda 把故障拆成两类:
- 服务器侧故障 → 服务器复制:一个 volume 在多台服务器上有副本(一个 VSG,Volume Storage Group),客户端只要能联系到其中一台(AVSG,Available VSG)就能读写
- 客户端侧网络故障 → 断连操作:网线一拔,客户端自己单干,重连后再同步
两根支柱独立设计,但用同一套乐观协议:先写,后协调。
2. 客户端三状态
Coda 客户端(叫 Venus,AFS 时代叫 Virtue)随时处于三种状态之一:
- Hoarding(囤货态):网络在线。Venus 一边正常服务,一边按用户预声明的”hoard 数据库”在后台预取——把你”明天可能要用”的文件先拖到本地。
- Emulation(仿真态):检测到网络断了。Venus 完全靠本地缓存服务文件操作;所有修改记入 CML(Client Modify Log),不发给服务器。
- Reintegration(重连态):网络回来。Venus 把 CML 一条一条回放给服务器,服务器尝试合并;冲突的部分标记成”待修”。
这三态是论文最被引用的图。后来所有”离线优先”应用(Notion 离线编辑、移动端笔记 app)都能映射到这套状态机。
3. 乐观复制(optimistic replication)
传统分布式系统怕分歧——所以加锁、加 quorum、加共识。Coda 反过来:默认允许分歧,假设大多数情况下不会真冲突。
- 写入:写到 AVSG 里的所有可达副本,不等所有副本,不全局锁
- 读取:从 AVSG 任一副本读,附带版本向量(version vector)
- 检测冲突:版本向量发现两边都改过同一份且互不可见 → 标记冲突
- 修复:目录冲突(A 加文件、B 删文件)能自动合并;文件内容冲突由用户用
repair工具人工选
这是与 Paxos / 两阶段提交完全不同的世界观。代价是程序员要接受”读到的可能是一个分支”。
4. Hoarding:把”我明天要用什么”声明出来
笔记本断网前,Venus 怎么知道哪些文件要预取?答案:用户提前在 ~/.hoard 里写一份hoard 数据库——
add /coda/usr/satya/papers/coda.tex 1000add /coda/usr/satya/src/venus 800add /coda/system/lib 100数字是优先级。Venus 后台不停按这个表更新本地缓存,并且在网络看上去要断时(比如电池切换、关盖前的指令)做一次”hoard walk”把缺的文件全拉下来。
这个设计很反直觉——它让用户参与缓存策略。但实测下来,黑盒 LRU 在断网场景就是不如用户的显式声明,因为 LRU 看不到”明天要去开会,需要这份 PPT”这种意图。
实践案例
案例 1:在线写一个文件
[网络在线,AVSG = {server1, server2, server3}]client: write /coda/usr/satya/draft.tex → Venus 把改动写到本地缓存 → close 时:并发推给 AVSG 三台服务器 → 三台都 ack 后视为成功 → 任何一台不可达?也算成功,事后协调对比 AFS:AFS 是单服务器,那台挂了就停。Coda 接受”少一台也能继续”。
案例 2:拔网线,回家改论文,重连合并
[T0 在办公室] Venus 在线,hoard 数据库已确保 draft.tex 在本地[T1 拔网] 检测断网 → 进入 Emulation 状态[T2 在咖啡馆] 编辑 draft.tex 三次 → CML 记三条 store[T3 在咖啡馆] 新建 figures/diagram.png → CML 记一条 create[T4 回家联网] 检测网络 → 进入 Reintegration 状态 → CML 按时序回放给服务器 → 期间没人动过这些文件 → 全部成功 → CML 清空,回到 Hoarding 状态整个过程用户没做任何同步操作。
案例 3:冲突与修复
[T0] Alice 和 Bob 都缓存了 shared.tex[T1] Alice 拔网,本地改 shared.tex 第 100 行[T2] Bob 在线,改 shared.tex 第 100 行(同一行不同改法)并 commit 到服务器[T3] Alice 重连 → Reintegration 时检测到版本冲突[T4] Coda 把文件标记为 in-conflict → Alice 任何普通访问都报错[T5] Alice 跑 cfs repair → 看到两个版本 → 手动选择 / 合并[T6] 修复完成,文件恢复正常这个流程几乎就是 Git merge conflict 的雏形——只是 Coda 把它放在文件系统层,用户在编辑器里自然碰到。
踩过的坑
- Hoard 数据库要人工维护——用户嫌麻烦,往往断网才发现要的东西没在缓存里。后来研究尝试自动学习 hoard 表,但用户行为预测一直没真正解决。
- 冲突修复体验糟——文件级的二选一对纯文本还好,对二进制文件(图片、PPT)几乎没法合,只能选一边丢一边。这是”乐观复制”无法回避的固有代价。
- 服务器副本一致性靠版本向量——服务器之间要后台跑协调进程;副本数越多,向量越长,存储和带宽开销线性增长,所以 Coda 推荐副本数 3-5 不要更多。
- Reintegration 期间不能再断网——回放过程中又掉网会让事务半途,Venus 要回滚到 emulation 重来,可能丢上次部分进度。论文有讨论但工程上仍然脆弱。
- 认证token过期问题——离线几天后 Kerberos ticket 失效,重连第一件事是续 ticket,否则所有回放都失败。这类”离线 + 安全”的边角,工业落地才会暴露。
适用 vs 不适用场景
适用:
- 移动 / 间歇联网设备做共享存储(90 年代笔记本,今天的协作笔记 app)
- 跨机房多副本但不要求强一致——能容忍秒级到分钟级的延迟同步
- 文档 / 代码 / 小媒体文件——用户能在冲突时看懂并选择
不适用:
- 数据库或事务系统——必须强一致,用 Paxos / Raft 不要乐观
- 二进制大文件协作(视频剪辑工程)——冲突无法人工合
- 安全敏感场景需 close-to-open 强一致——Coda 弱于 AFS
- 副本数量超过 5 台——版本向量与协调开销不划算
历史小故事(可跳过)
- 1987:Satya 在 AFS-2 里第一次注意到”客户端笔记本”会成趋势——当时 IBM ThinkPad 雏形刚出。
- 1990 (这篇):Coda 第一篇论文发表,正式提出 disconnected operation 概念,三状态机被画进所有后续教材。
- 1992-1996:Coda 在 CMU 校园部署,几百用户每日使用;论文持续产出(hoarding profile、reintegration 算法、副本协议)。
- 1998:Coda 进入 Linux 内核 mainline(早于很多今天熟悉的文件系统)。
- 2000 年代:商业云存储兴起(Dropbox 2008),用同样的乐观复制 + 客户端日志思路,但目标用户是普通消费者;Coda 学术血统强、运维复杂,没走出校园。
- 影响传承:Bayou(1995,PARC)继承乐观复制思想做更激进的多 master 数据库;Git 的离线 commit + push 合并是文件系统层之外的同构方案;现代 CRDT 研究是这条路的数学化版本。
学到什么
- 网络故障不是异常,是常态——这一观念领先时代 20 年。今天的边缘计算、车载、物联网仍在补这个课。
- 乐观复制是工程权衡——选了它就得接受”会有冲突”,并且必须给用户暴露冲突。把冲突藏起来反而是灾难。
- 缓存策略可以让用户共同决定——hoard 数据库违反”自动化优于配置”的直觉,但在”断网时手里没东西”这种长尾灾难面前,让用户声明意图反而更稳。
- 状态机是分布式系统第一图——hoarding / emulation / reintegration 三态是论文最值得学的抽象方法:复杂系统先用 3-5 状态画出来,再补转移条件。
- 学术和工业的距离有时候不是技术,是用户——Coda 技术比 Dropbox 更深,但 Dropbox 把”文件夹拖一下就同步”做成普通用户能学会的产品。技术下游永远不是终点。
延伸阅读
- 论文 PDF:Satyanarayanan et al. 1990(约 14 页正文)
- Coda 项目主页(CMU 维护,仍能下载 Linux 内核模块)
- 教学视频:MIT 6.824 分布式系统课的”乐观复制”章节,Coda 是经典案例
- afs-1988 —— 直接前身,必须先读,否则术语对不上
- bayou-1995 —— 把乐观复制做到数据库,比 Coda 走得更远
- nfs-1985 —— 反面例子,全无状态、强一致弱可用,对比鲜明
关联
- afs-1988 —— 同团队前作;Coda 在 AFS 基础上加复制 + 离线两根支柱
- bayou-1995 —— 后辈思想继承者;把乐观复制扩到任意数据库
- nfs-1985 —— 同年代异路;NFS 走无状态强一致,Coda 走有状态弱一致
- lamport-tla-1994 —— 形式化分布式协议工具;Coda 这类乐观系统适合用 TLA+ 验证
反向链接
- afs-1988 —— AFS 1988 — 客户端缓存 + 回调失效让分布式文件系统真正能扩展
- bayou-1995 —— Bayou — 离线先改本地,再回来和别人合并
- lamport-tla-1994 —— TLA — 把状态机和时序逻辑捏成一个公式
- locus-1980 —— LOCUS 1980 — 让一群机器看起来像同一台机器
- nfs-1985 —— NFS 1985 — 让远程磁盘看起来像本地磁盘
- sprite-1988 —— Sprite 1988 — 把一屋子工作站伪装成一台大主机