news 2026/8/30 14:15:08

Cloudflare Computer同步协议30分钟深入:从ChangeEntry到applyChanges全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Cloudflare Computer同步协议30分钟深入:从ChangeEntry到applyChanges全流程

Cloudflare Computer同步协议30分钟深入:从ChangeEntry到applyChanges全流程

【免费下载链接】computerGive your agent a computer 👾项目地址: https://gitcode.com/GitHub_Trending/computer1/computer

Cloudflare Computer 是一个跑在 Cloudflare Durable Object 里的虚拟文件系统,它的**同步协议(Sync Protocol)**负责把云端 SQLite 文件树与沙箱容器里的 FUSE 挂载双向同步。这篇文章带你用 30 分钟走完全流程:从一条ChangeEntry记录如何产生、如何在网络上流转,到最终被applyChanges写进本地存储的每一步,帮你彻底搞懂这套增量双向同步的设计。

同步协议要解决什么问题?

Cloudflare Computer 的架构里,同一个文件系统有两份副本

  • DO 侧(Durable Object):基于 SQLite 的虚拟文件系统,是重启后仍然存在的"事实来源"(source of truth);
  • 容器侧:通过 FUSE 挂载暴露给沙箱的真实文件系统,供computerd守护进程和 Agent 使用。

难点在于:两边都可能随时被修改——你在容器里跑npm install,DO 侧也可能会收到外部写入。如果每次都全量传输文件树,网络成本会爆炸。

所以同步协议是增量的、双向的:每侧都维护一个单调递增的修订号计数器(rev),双方只交换"对方还没见过的那部分变化"。

核心构件:ChangeEntry 同步记录

整套协议的"最小传输单元"就是 ChangeEntry,一条描述"某个路径的最终状态"的记录。它有四种形态:

类型携带的信息说明
file路径、权限、mtime、大小、分块哈希列表文件内容不在记录里,只有每块的sha256哈希
dir路径、权限、mtime目录元数据
symlink路径、链接目标、权限符号链接永不解引用
delete路径、修订号删除操作的"墓碑"记录

两个关键设计值得新手特别注意:

  1. 基于最终状态,而非操作日志。协议里没有rename这类操作指令——目录改名只是把子树里每个 inode 都盖上新的修订号,再为旧路径补一条墓碑。这样接收方不需要按顺序重放操作,重复应用也天然幂等。
  2. 字节从不内联传输。文件按固定 512 KiB 分块,每块用内容寻址(sha256)。同一内容(比如node_modules里被多处引用的库)无论在几个路径出现,网络上只传一次。

一条ChangeEntry是怎么来的?当路径被修改后,materialiseChange 会读取该路径的当前状态——存活的 inode 优先于墓碑,正确处理了"删除后重建"的场景——然后把它打包成线上记录。

Push 流程:DO 如何把变更推给容器

每次执行命令前,DO 会先做一次push,把容器还没见过的变更流过去。流程分三步:

  1. 合并(coalesce)。coalesceChanges 扫描修订号区间内的所有脏路径,同一路径合并成一条(最新状态胜出)——两次执行命令之间重写同一个文件 5 次,网络上只花 1 条记录。条目按rev升序、路径升序输出,为后面的断点续传铺路。
  2. 哈希探测(hasObjects)。发送方先问接收方:"这些块哈希你有哪些?"——相当于 git 的have协商,用一次集合差运算就跳过所有已有字节,没有任何多余的元数据往返。
  3. 补传对象(pushObjects)+ 推条目流(push)。只传输缺失的块,然后流式发送合并后的ChangeEntry批次。

接收方(容器)把整批变更放进单个同步事务里原子应用——半途失败就整批回滚,接收方永远不会看到"半截"的推送。

Pull 全流程:fetchChanges 到 applyChanges

命令执行完后,方向反过来:DO 把容器里 FUSE 捕获的写入拉回来。这是 pullOnce 实现的六步循环,也是全文的精华:

步骤动作一句话解释
① FetchfetchChanges({ after: 游标 })从上次停下的(rev, path)游标处,按修订号排序流出ChangeEntry
② 批处理缓冲最多 256 条(PULL_BATCH_SIZE峰值内存被批大小封顶,而不是整个流
③ Diff探测本地vfs_blobs+fetchObjects拉取缺失块32 字节哈希的集合差,零内容重复传输
④ Apply批次交给applyChanges逐条写入 SQLite,见下方详解
⑤ 检查点游标推进到该批最后一条的(rev, path)崩溃后从最后提交批次恢复,最多重做 256 条
⑥ 循环回到 ② 处理下一批直到流耗尽,最后写入currentCursor

applyChanges 的三条防御线

applyChanges 是变更真正落地的地方,它内建了三道安全机制:

  • 幂等跳过(alreadyApplied)。应用前逐条比对本地状态:文件比 manifest 哈希,目录比权限位,符号链接比目标+权限。已经一致的条目直接丢弃——这让"重复拉取"既便宜又无副作用,是断点恢复能安全工作的根基。
  • 只读挂载守卫。落在只读挂载点下的条目不会被应用,而是收集进skipped返回给调用方,保证挂载点内容不被静默篡改。
  • 结构冲突清理。当远端发来一个文件、而本地该路径是个目录时,接收方会删掉本地子树再应用远端条目——这是**最后写入者胜出(last-writer-wins)**的冲突处理,树永远收敛,但不会做内容合并。

水位线(Watermark):崩溃恢复的秘密

协议双方交换的"进度表"有五个水位线:pushRev(DO 已推到的修订号)、fetchCursor(DO 已拉取到的容器游标)、currentRev(本地最新修订号)、appliedPushCursor(接收方已应用到的推送游标)。

两个亮点:

  1. (rev, path)双坐标游标。一个大的目录改名可能在单个rev内产生成百上千条记录。纯数字游标崩溃后只能重放整个 rev;加入path坐标后,崩溃可以在 rev 内部精确恢复,而不用伪造从未整体提交的"幽灵修订号"。详见 watermarks.ts。
  2. 跨侧不变量。每次 push 和 pull 的响应都会回声接收方的appliedPushCursor,发送方断言它覆盖了自己刚推的修订号。一旦断言失败,说明接收方丢过状态——协议选择大声失败而不是静默腐坏。

重连场景更贴心:reconcileWatermarks 在连接建立时先问对方"你到哪了",发现落后就把本地游标归零,从 rev-0 基线全量重发——由于幂等检查,重复传输几乎零成本。

并发与冲突:什么时候安全?

  • 单个 Workspace 内:所有写入被 DO 运行时串行化,不存在写-写冲突;
  • 两个容器共享一个 Workspace:可能出现真冲突,语义是同步粒度上的 last-writer-wins——最后到达 DO 的推送胜出,先写的变更会被静默覆盖。这和"无锁 NFS 挂载"同语义。

实践建议(来自官方文档):一个 workspace 同时只有一个活跃写者、给多 Agent 划分互不相交的子树、把共享状态放到 DO 的 RPC 面上而不是共享文件里。完整的冲突语义分析见 docs/02_sync_protocol.md。

另一个对新手很实用的机制是ignore 列表:默认排除node_modules,否则一次npm install会把数万个小文件推进同步网络。被忽略的路径在容器内照常工作,只是字节永远不上线。

想继续深挖?三个入口

  • 协议规范与全部设计取舍(为什么不要 rename 操作码、为什么借鉴 git 的 haves/wants):docs/02_sync_protocol.md
  • 线上记录的定义与物化逻辑:packages/dofs/src/sync/changes.ts
  • 批量应用与幂等检查的完整实现:packages/dofs/src/sync/apply.ts
  • 拉取/推送/水位线对账的驱动循环:packages/rpc/src/sync-driver.ts
  • 文件系统表结构与 rev 递增机制:docs/03_filesystem_schema.md

小结:Cloudflare Computer 的同步协议用四个概念解决了"两份文件树如何廉价且安全地收敛"——ChangeEntry状态化记录、内容寻址分块、(rev, path)断点游标、applyChanges幂等应用。它不追求合并冲突,而是用幂等和最终状态保证任何崩溃、重连、重复传输下系统都能收敛到正确状态。这正是分布式文件系统"简单优于聪明"的经典取舍。

【免费下载链接】computerGive your agent a computer 👾项目地址: https://gitcode.com/GitHub_Trending/computer1/computer

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/30 14:09:21

Wi-Fi室内定位实战指南:不依赖UWB/蓝牙的低成本部署方案

简介:本资源是一个面向Android开发者与室内定位技术学习者的Wi-Fi指纹定位系统实战项目,聚焦商业场景下的无GPS室内位置感知需求,适用于智能建筑、商场导航、医疗资产追踪等应用开发。压缩包共2000个文件,总大小28.5MB&#xff0c…

作者头像 李华
网站建设 2026/8/30 14:07:39

全桥峰值电流控制实战:LAT1319 Push-Pull模式斜坡补偿与调试

做电源的人都知道,全桥拓扑在大功率DC-DC里几乎是绕不开的选项。最近我在调试一台输出10kW的样机,控制器选了LAT1319,芯片工作在Push-Pull模式,配合峰值电流控制,整体效果比我之前用纯电压模式明显稳了一个档次。这篇文…

作者头像 李华
网站建设 2026/8/30 14:06:15

一周入门大模型:从本地部署到LoRA微调完整路线

如果你打算从 8 月 12 号开始学大模型,一周时间能学到什么程度?先说结论:能把本地部署、接口调用、提示词工程、RAG 和 LoRA 微调全部跑通一遍,并且能形成自己的第一个可用 Demo。这一周不是让你从数学原理、Transformer 源码一点…

作者头像 李华
网站建设 2026/8/30 14:04:10

AI写代码的完整边界:从工具选型到本地部署实践指南

AI写代码爽三个月,然后呢?先说结论:AI 写代码不是神话,也不是智商税。它最大的价值不是把程序员换掉,而是把“从零开始写”变成“快速验证、再修改、再验证”。但如果你只停留在让它帮你补全函数、生成 DTO、写单元测试…

作者头像 李华
网站建设 2026/8/30 14:03:58

工具调用不能只看演示效果

工具调用不能只看演示效果工具调用演示顺畅,并不代表它已经适合真实用户。演示里通常只有一个明确问题、一个工具和一组规范参数;真实环境中,模型可能选择不该使用的工具、漏掉字段、混淆单位、重复调用,工具本身也可能超时、返回…

作者头像 李华
网站建设 2026/8/30 14:03:24

文献综述怎么按主题分类而不是逐篇罗列:BunnyScholar三版综述生成实测

格子达检测全文AI率过高、逐段修改太慢:BunnyScholar长文档处理流程 对于采用格子达作为官方抽检工具的高校毕业生而言,面对全篇飘红的初检报告往往会感到压力倍增:格子达检测全文AI率过高、逐段修改太慢怎么办?整篇本科毕业设计…

作者头像 李华