尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

@cloudflare/computer 同步协议深度解析:DO 与容器之间的双向增量文件同步机制

@cloudflare/computer 同步协议深度解析:DO 与容器之间的双向增量文件同步机制 cloudflare/computer 同步协议深度解析DO 与容器之间的双向增量文件同步机制【免费下载链接】computerGive your agent a computer 项目地址: https://gitcode.com/GitHub_Trending/computer1/computer导读本文剖析cloudflare/computer项目中 Durable ObjectDO与容器之间文件系统同步协议的完整设计——从(rev, path)增量游标、按路径合并coalesce的变更流、sha256 内容寻址的分块传输到水印表、交叉侧不变量与冲突语义。读完你将掌握该协议的全套线缆形态wire shape、失败恢复策略与工程取舍并能直接对照 同步驱动源码 与 变更合并实现 进行二次开发与排查。一、核心模型两份文件树一个事实来源工作区在系统中始终维护两份文件系统树的副本通过同步协议保持收敛DO 侧事实来源一个由 SQLite 支撑的 VFS位于 Durable Object 中跨重启持久化是文件系统的权威状态。其表结构见 03. Filesystem Schema。容器侧通过 FUSE 挂载暴露给沙箱的 VFS挂载在配置的工作区根目录。容器侧复用与 DO 侧相同的Database抽象——是否跨容器重启持久化属于部署选择今天的computerd运行在进程生命周期数据库之上容器重启会丢失本地状态下一次来自 DO 的 push 会将其重新基线化re-baseline到最新状态。同步是增量且双向的每一侧都持有一个单调递增计数器因此任何一侧都不需要发送整棵树来追赶进度。计数器语义在源码中以ChangeCursor{ rev, path }与rev水印的形式实现见 水印读写实现。二、同步生命周期一次exec()的六步往返数据沿两个方向、按各自的时钟流动DO → 容器push每次 DO 侧变更都会被盖上一个全新的 revision。当容器需要这些变更时——在exec()之前或显式调用workspace.push()时——DO 会发送容器尚未见过的每一个 revision。容器 → DOpull每次容器侧通过 FUSE 写入都会盖上全新的容器 revision。DO 收集这些 revision——在exec()返回之后或显式调用workspace.pull()时——并将其应用到自己的 SQLite 存储。一次典型的exec()往返包含六步Push推DO 流式发送每个ChangeEntryrev 高于容器已见水位按路径合并成每条一个条目最新状态胜出——两次 exec 之间对同一路径的五次重写在线上只产生一条记录而不是五条。一个硬链接 inode 携带多个名字coalesceChanges会为每个名字发射一条记录确保每个路径都在接收方物化。硬链接身份不会跨线缆保留——每个名字都变成一个内容相同但相互独立的文件而非共享 inode。字节不会内联在条目中条目只携带块哈希chunk hash。发送方DO对引用的哈希调用remote.hasObjects(...)并对接收方缺失的子集跟进调用remote.pushObjects(missing)。接收方应用整批之后DO 才把pushRev推进到刚发送的 rev 之后——仅当期间没有交错的未推送本地写入时因此与应用竞态的本地写入不会被搁浅。这个应用后推进pushRev的机制由提交dc692c0引入正是防止容器自己的 apply 在下一次 pull 时把这些条目原样弹回发送方的关键。Hydrate水合命令可能触及的懒挂载lazy-mountstub 会从其 provider 拉取并包含在同一批 push 中见 06. Mount Interface。Exec执行命令运行。FUSE 写入在发生时就被容器内 VFS 捕获每一条都盖上全新 revision。Fetch拉取DO 调用fetchChanges({ after: fetchCursor })。容器流式返回该(rev, path)游标之后的ChangeEntry记录——每个被触碰路径一条文件条目携带chunks: (hash, size)[]字节不内联。Diff差异DO 从流中读取最多PULL_BATCH_SIZE256条记录对这批记录引用的块哈希做并集探测自己的vfs_blobs看哪些已有然后对缺失子集调用fetchObjects。Apply应用条目 新对象落盘到 DO 的 SQLite。pullOnce的峰值内存被限制为PULL_BATCH_SIZE条记录加上这批记录引用的字节——条目流从不被整体缓冲。每一批都经过applyChanges其中writeFile/mkdir/rm/symlink内部的每次变更各自transactionSync才是真正的持久性边界。驱动随后循环回第 5 步处理下一批。Fetch 游标按已提交批次推进到最后一条流式记录的(rev, path)。coalesceChanges按 rev 升序、再按 path 升序发射条目因此即使一个 rev 包含多个批次这个检查点也是安全的。pull 中途崩溃会从最后一次按批推进处恢复因此重新拉取的工作量被限制在PULL_BATCH_SIZE256条记录以内而非整个流。接收方applyChanges内的alreadyApplied检查仍会把已应用的条目丢弃因此重新应用是幂等且廉价的。exec()之外的writeFile/mkdir/rm遵循同一形态第 1 步变成这一条变更第 3–6 步跳过。workspace.push()按需执行第 1 步workspace.pull()执行第 4–6 步。该流程在 同步驱动 中以pushOnce、pullOnce、tick先 pull 后 push三个函数落地。三、设计取舍为什么协议是终态基而非操作基重命名在本地是 inode 移动但同步线缆上没有 rename 操作码。线缆保持终态基表示——活条目live entries 墓碑tombstones——因此 apply 保持幂等无需按操作顺序重放。代价是一次目录重命名会在一个同步事务内为每个被移动的 inode 盖上一条新 revision并为旧路径记录墓碑。大型目录重命名的本地写入与线缆条目都是 O(子树规模)除调用方自身工作负载外没有额外上限。重命名不会改变父目录的 mtime——这与 POSIXrename(2)不同但把父目录元数据排除在内容同步之外。目录条目携带 mode 和 mtime。新目录以传入的 mtime 创建但已存在目录的幂等只比较 mode。这让匹配目录上的 mtime 漂移不会变成同步流量源码见 apply 实现 中applyDirectoryEntry与alreadyApplied的 mode 比较。当上游的文件、目录或符号链接落在接收方已有不同节点类型的位置时接收方会移除本地节点树并应用上游条目。这是最后写入者胜出last-writer-wins的冲突处理它让树收敛但冲突路径下仅存在于本地的子节点会被丢弃且不产生独立的墓碑。3.1 曾被否决的方案rename 操作码专用的rename条目携带{ fromPath, toPath, inode }能把目录移动折叠成一条线缆记录。它被否决因为它是操作而协议其余部分都是状态基的materialiseChange在 fetch 时把每条记录解析为该路径的当前状态接收方针对自己的活状态做对账alreadyApplied让重放无需有序重放即可幂等。操作码在三个层面破坏这一模型它是相对接收方先前状态的relink(from - to)对一个从未持有from的冷启动对端毫无意义pull 路径刻意与接收方历史无关生产者只回答游标 X 之后的变更不关心某个接收方看过什么因此无法决定何时安全发射操作码而让操作码对终态幂等又需要重新推导出操作码原本想避免的状态对账却仍未解决冷启动。每个子树的成本是维持一套状态基表示即可完成引导、收敛与重放所付出的代价。3.2 曾被否决的方案跨 rev 分块重命名标量 fetch 水印只能在 rev 边界恢复因此一个 rev 上的大型重命名会让崩溃时不得不重放整个 rev。一种不引入路径游标的限界方式是把单次重命名拆到多个 rev让标量水印能在分块边界恢复。这被否决因为它削弱了协议依赖的不变量rev每次变更原子地递增一次见 03. Filesystem Schema因此每个rev值都命名变更日志中的一个已提交点。分块会铸造从未整体提交的中间 rev使大多数rev值描述从未存在过的树状态。(rev, path)fetch 游标避免了这个问题path是一个正交的第二坐标记录同一 rev 内的恢复进度。一次重命名仍在整个子树上恰好盖上一个rev而崩溃可以从中途恢复——可恢复性无需铸造幻影 rev。3.3 游标保证什么(rev, path)游标是一个恢复点resume point不是快照句柄。{rev, path: null}表示所有在 rev 或之前提交的变更都已被提供给接收方非空path表示在 rev 内已提供到path为止。它刻意不是某个时间点的快照读coalesceChanges在流式生成时物化每个路径的当前状态因为存储不保留内容历史。一个在快照打开后被再次重写或删除的路径其活 rev 会越过广告的currentCursor因此该条目会从当前流中丢弃并在更晚的游标下重新投递。因此接收方在游标{5, null}处的树对某个竞态前进的路径而言不必与 rev-5 快照字节一致——但收敛仍然成立因为导致丢弃的那个 rev 大于currentCursor.rev下一次 pull 会重新扫描并投递该路径的当时状态。游标永远不会推进到越过会重新投递被省略路径的那个 rev。这一语义在 coalesce 源码注释 中被显式记录并在 schema 文档 的_vfs_fetch_cursor一节交叉印证。3.4 分块固定 512 KiB sha256 内容寻址文件按固定CHUNK_SIZE512 KiB切分。块边界是确定性的——chunkIdx floor(byteOffset / CHUNK_SIZE)——因此只改动大文件某一区域的编辑只会拉回受影响的块而不是整个文件。每个块以sha256(bytes)内容寻址因此重复内容同一个库 vendor 在两个路径、只重写最后一块的编辑只被传输和存储一次你到底缺哪些字节的探测只是一次 32 字节哈希的集合差运算无需元数据往返。块与文件的关系落在 schema 的vfs_chunksinode → 有序块列表、vfs_blobs/vfs_blob_bytes哈希 → 字节与vfs_manifests每文件有序块列表内容相同则共享 manifest 哈希上详见 03. Filesystem Schema。manifest 当前以 JSON 编码{ version: 1, chunks: [{ hash: hex, size: n }, ...] }见 manifest 编码实现。四、水印Watermarks与交叉侧不变量两侧都持有单调递增的 revision 计数器并在每次 push 与 pull 时交换。线缆词汇统一为rev——一个概念一个名字。水印属主含义pushRevDO最后一个成功推送到容器的 DO 侧rev。fetchCursorDODO 已 fetch 的最后一个容器侧游标path null表示该 rev 之前含的所有变更都已被提供是恢复点不是时间点快照——见上文。currentRevDO盖在 DO 侧变更上的最新rev。currentRev容器盖在容器侧变更上的最新rev。appliedPushCursor容器容器已应用的 DO 侧游标。在每次push与fetchChanges响应中被回显。DO 的水印存于_vfs_watermark表因此能跨 DO 重启存活。容器的水印存在同一个Database抽象中是否跨容器重启存活是部署选择。今天的computerd运行在进程生命周期数据库之上因此容器重启会丢失本地水印下一次来自 DO 的 push 会被当作权威基线senderRev 0分支覆盖外部编排器向全新接收方写入的对称情形。代码层面pushRev与 fetch 游标的读写集中在 水印读写实现并支持按backend隔离使单个工作区可同时托管多个后端、各自维护独立同步游标。4.1 交叉侧不变量每次成功的push与每次fetchChanges之后响应都会携带接收方当前的appliedPushCursor。DO 在继续之前断言该游标覆盖其本地{ rev: pushRev, path: null }。两侧从不共享单一时钟但回显已应用游标让接收方已追平我们的推送这一不变量在线缆上可被检查而不是依赖进程内状态。后置游标推进路径一旦回归会在下一次 push 或 pull 时触发断言而不是悄悄破坏数据。这一不变量在源码中有完整落地pullOnce通过assertAppliedPushCursor(appliedPushCursor, { rev: localPushRev, path: null })守卫当appliedPushCursor.rev localPushRev远程忘记了我们推过的内容典型场景是 WebSocket 存活但 computerd 进程重启或currentCursor after远程日志比我们记忆的更短时驱动会原地重置游标到 0 并重试一次第二次发散才是真正的协议破坏直接抛出。连接建立时还会调用reconcileWatermarks向远端询问其水印凡远端落后于本地的地方都把本地游标归零让下一次 tick 走 rev-0 基线路径增量重发。五、线缆形态Wire ShapeRPC 清单与senderRev语义线缆是对称的push 与 fetch 都搬运ChangeEntry记录都用hasObjects探测都按哈希传输字节。命名沿用 git 的词汇——DO 向容器push条目与对象并把条目与对象fetch回来。DO 与computerd以匹配对部署。协议没有版本协商因此任何请求/响应形态的变更都是硬性线缆断裂需要锁定步调lockstep滚动发布。RPC 契约见 接口定义 中的SyncRPCRPC方向返回说明push({ senderRev, changes })DO → 容器{ rev, appliedPushCursor }通过changesReadableStream流式发送一批合并后的ChangeEntry。发送方随后对引用哈希调用hasObjects并对缺失子集跟进pushObjects。见下方senderRev分支。fetchChanges({ after?, ignore? })容器 → DOPromise{ currentCursor, appliedPushCursor, stream: ReadableStreamChangeEntry }在after之后按 rev、再按 path 排序流式返回每个被触碰路径一条记录。文件携带chunks: (hash, size)[]字节不内联目录携带元数据删除携带墓碑。currentCursor在流打开时是{ rev: currentRev, path: null }拉取方在干净排空后写入它。appliedPushCursor在 pull 路径上承载交叉侧不变量检查。hasObjects(hashes[])发送方探测接收方Uint8Array[]返回接收方已持有的输入子集。git 的have行批量版。fetchObjects(hashes[])容器 → DOReadableStream{ hash, bytes }按哈希流式传输块字节。fetch 路径上的 gitwant/pack 响应。pushObjects(objects)DO → 容器void按哈希流式传输块字节。fetchObjects的 push 方向镜像。5.1senderRev语义push会被两类写入者调用senderRev字段用于区分它们提交dc692c0与c95c74d记录了负载测试依据senderRev 0—— 同步对端DO 调用其容器对端或反之。接收方把整批按upstream应用把自己的 fetch 游标推进到{ rev: senderRev, path: null }在发送方一侧pushRev会推进越过刚发送的 rev受期间无交错的本地写入门控——见第二步。服务端实现见 服务端适配器 中SyncRPCServer.pushisPeer分支把整批包进单个db.transactionSync并在 apply 后推进writeFetchCursor。senderRev 0—— 外部写入者 / 全新接收方供外部编排器使用也是全新接收方尚无水印时的隐式形态。接收方按local应用为每条记录递增自己的currentRev并保持出站水印不动这样下一个同步循环会把新条目继续向上游投递。没有这个分支接收方会在一次外部写入后沉默自己的出站同步——参见c95c74d。多个路径上的相同内容或编辑后未变的块在线缆上恰好出现一次——由哈希寻址保证。线缆的 framing 见 08. Capnweb Interface。六、失败处理从传输丢失到崩溃恢复容器重启或传输丢失Durable Object 检测到 WebSocket 关闭移除并关闭过期后端句柄通过常规 computerd 健康门重新连接。pushOnce与pullOnce在原始逻辑操作内部获得一次重连重试。pushRev与 fetch 游标让这些重试幂等撕裂的 push 重放水印未推进的条目撕裂的 pull 从最后已提交批次恢复。全新的进程生命周期 computerd 数据库会在替换句柄暴露前重置匹配的本地水印因此下一次 push 从 rev-0 基线重建。命令执行期间的容器重启exec 前的 push 必须成功含其重连重试然后才调用shell.exec。本地处置的 stub 可证明从未发出 spawn 请求允许一次重连重试分发后的通用断开是模糊的因此后端使句柄失效并报告命令可能已启动而非重放它。事件流失败遵循同样的不重放规则。命令后的 pull 只对运行该命令的 runtime UUID 安全重试若重连到达替换容器执行结果报告pending sync而不是把空 VFS 当作成功的零条目 pull。持久重试意图保留原始 runtime UUID当定时重试确认该 runtime 已消失时报告同步丢失并清除不可恢复意图使后续命令能在活跃 runtime 上调度自己的 pending pull。容器 apply 中途崩溃从 DO 视角看push在接收方是原子的——服务端把整批包进单个db.transactionSync同步的applyChangesSync辅助函数。Database.transactionSync通过 SQLite SAVEPOINT 可重入因此内部 fs 写入在外层事务内仍保有各自的按次变更原子性。流中段的失败例如组装步骤缺块会回滚该批已应用的所有条目接收方绝不会看到部分 push。pull 路径保持按次变更模型因为流式批次无法跨网络 I/O 持有同步事务。DO pull 中途重启fetch 游标按已提交批次推进到最后一条记录的(rev, path)因此 pull 中途重启会从最后按批检查点恢复——包括单个大 rev 内部。浪费的工作量被限制在PULL_BATCH_SIZE条记录256以内而非整个流。两种路径的终态都是正确的——apply 是幂等的。DO 重启水印已持久化新 DO 实例从旧实例停下的位置继续。容器在间隙期保持computerd存活。并发变更者Workspace.push()与Workspace.pull()走每个 Workspace 的尾承诺 FIFO。两个并发调用者排队——第二个在第一个 resolve 或 reject 之前不能进入pushOnce/pullOnce。命令的 exec 前 push 与流后 pull 各自使用该包装器但FIFO 不会在命令生命周期内一直持有重叠的命令与显式同步调用不是一个事务。拒绝不会传染一次失败的变更向自己的调用者暴露错误而不毒化队列中的下一个。Workspace.fs上的纯读绕过 FIFO——它们直接命中本地 SQLite 存储DO 运行时已通过输入门在内部序列化。七、冲突语义何时可能、何时安全、如何规避当两个写入者都在没有先看到对方变更的情况下修改同一路径时冲突产生。理解cloudflare/computer中冲突能/不能发生在哪里是推理系统保证的前提。7.1 单个 Workspace 实例DO内部单个Workspace实例单个 DO 化身上的所有变更都由 DO 运行时的输入门序列化。同一路径上两次并发的ws.fs.writeFile调用排入同一 SQLite 存储并按序 resolve。单个 DO 实例内不可能有写-写冲突——最后进入输入门的调用胜出那就是权威状态。每个 Workspace 的尾承诺 FIFO 为push()与pull()增加第二层序列化同时触发同步操作的一对并发调用者会在任一进入pushOnce/pullOnce之前在 FIFO 排队。runtime.exec()的 push 与 pull 阶段独立使用该 FIFO运行中的命令不持有它。7.2 共享一个 Workspace 的两个容器之间冲突正是发生在这里。若两个容器两个独立computerd挂载或两个独立WorkspaceBackend实例接驳同一 DO 且都写入同一路径结果完全取决于同步顺序同步协议总是先拉取远端变更再推送本地变更tick() 先 pull 后 push。这防止写入者用陈旧的本地副本覆盖远端状态——它先吸收远端已有内容。然而pull 不会检测某路径自上次同步以来本地和远端都变了。当两个容器并发编辑同一文件时谁的 push 后到达 DO 谁赢。较早写入者的变更会在下次同步时被静默覆盖。没有合并、没有错误、也不会向任一调用者提示发生了冲突。对象级完整性被保留DO 从不持有部分应用的写入。但内容级完整性不被保证——幸存的内容只是最后被应用的那一批。这是同步粒度上的最后写入者胜出与不加锁的共享 NFS 挂载、或不做条件 PUT 的 S3 bucket 语义相同。7.3 何时 LWW 足够安全对常见 agent 模式通常没问题一个 agent、一个容器、一个 DO没有并发写入者。同步实质上是持久化机制而非并发机制。这是当前绝大多数用法。多 agent、路径所有权不相交若 agent A 总写/workspace/a/、agent B 总写/workspace/b/写入永不相交。无论同步顺序如何冲突在结构上不可能。单写入者、多读取者多个消费者从同一 DO 拉取但只有一个推送。构造上无冲突。一次性写入文件配置文件、初始脚手架或只写一次随后只读的生成产物。文件一旦落到 DO 就稳定同步顺序无关紧要。7.4 何时 LWW 不够安全今天可能丢数据的模式多 agent 写入共享文件共享的PLAN.md、共享的state.json、共享日志——任何由两个容器独立更新的文件都会收敛到谁最后同步谁赢的版本静默丢弃另一个。跨容器读-改-写循环若容器 A 读到counter.txt为5、递增到6并写回而容器 B 也读到5、递增到6并写回DO 最终得到其中一个的6而不是7。两次写入都没有报错。无显式同步点的 agent 交接若 agent A 完成工作后 agent B 立即接手同一工作区B 必须在读取前显式调用workspace.pull()以确保看到 A 的最终写入——没有自动栅栏。7.5 当前实操建议在现有保证下最安全的模式每个工作区同一时刻只有一个活跃写入者。若有多 agent在应用层通过显式交接协调例如 agent A 在自己的回合结束时调用workspace.pull()agent B 在开始回合前调用workspace.pull()。划分命名空间。给每个 agent 一个专用子树。共享状态放在 DO 自身通过其 RPC 表面而不是作为共享工作区文件。把工作区文件当作 agent 本地 scratch而非共享可变状态。工作区擅长可靠的按 agent 存储但它尚不是共享 CRDT。针对冲突的一等原语规划见 Future considerations。八、忽略列表Ignore Lists可选的ignore列表会把匹配的路径段从某次同步流中隐藏。过滤是选入制且只在为线缆合并变更时生效文件系统正常保留并暴露这些路径。被忽略的路径因此不会从Workspace.fs删除或隐藏只是从那次特定的 pull 中被省略。这对.next、target、__pycache__、dist这类大型派生文件目录很有用。默认值是[]。调用方提供的列表会替换该次 fetch 的已配置列表。当同步对端应省略某些路径时传入显式列表或传[]以包含所有路径。例如省略node_modules显式配置ignore: [node_modules]。8.1 被过滤条目的行为过滤只影响变更流。文件仍可通过Workspace.fs读写容器侧命令如exec(node ...)可正常使用它们。被过滤的条目仍会推进接收对端的 fetch 游标因此一个同步关系应保持 ignore 配置稳定移除某个模式不会重放已被过滤的变更。实现上忽略规则匹配器 采用整段匹配模式匹配路径中任意位置的同名段但不会匹配更长的段模式是纯字符串而非 glob默认导出DEFAULT_IGNORE []。九、Future considerations 与设计借鉴初始设计推迟的项。若有真实用例依赖某个特定解法请提交 issue。9.1 Bloom/cuckoo filter overvfs_blobs.hash每次 pull 都会做一次hasObjects探测往返。单次 pull 有数万个块时字节量不大但延迟真实存在。一个 DO 侧、从vfs_blobs惰性重建的概率过滤器可让 DO 对自己能证明不存在的块跳过探测只对可能存在的命中回退到hasObjects。无需协议变更纯 DO 侧优化。9.2 Push 背压长时运行的 exec 弄脏容器状态的速度可能超过 DO 的 pull 速度。今天的进程生命周期容器 VFS 通过 OOM 来兜底——这是个糟糕的答案。一旦磁盘支撑的容器镜像落地界会转移到路径数量但同样的问题仍在。可能形态对脏集设软上限例如 256 MiB 待定字节或 10 万路径超过后延迟 FUSE 写回复对写入者形成真实背压或容器机会主义地在带外主动向 DO push而不是等待 exec 后的 pull。9.3 同类先例与选择性复用块存储 每文件 manifest haves/wants 协商模式并不新颖——git、casync、OSTree、restic 与 IPFS unixfs 都解决过同类问题的变体。直接整体复用一个库是把我们可控的实现换成我们不可控的库不匹配。在当前的规模下复用格式与模式、而非复用库是更好的交易。Git pack 协议与我们模型直接对应——trees 目录blobs 文件sha 内容寻址。smart protocol 的 haves/wants 协商正是今天的hasObjects/fetchObjectsisomorphic-git已在GitHubRepo的依赖树中。不适配处git 的分块是逐 blob整文件的子文件去重需要 repack 驱动的 delta 搜索而非由寻址自然得出心智模型是历史——每次 push 都会是合成提交、GC 需要 repack 周期元数据模型差只有可执行位二进制 pack 格式失去 capnweb-text 的可调试性。结论借鉴 haves/wants 模式与命名不借鉴库或线缆格式。casync最接近的契合。.caidx块索引格式是每个文件的(sha256, offset, size)有序列表——我们的vfs_manifests.encoded是同形态的自研实现.castr块存储对应我们的vfs_blobs。Buzhash 内容定义分块解决本文前述的头插入问题。完整 POSIX 元数据符号链接、硬链接、xattrs、mode、mtime内建。障碍在实现casync 是 C唯一像样的移植是 Godesync生产级 TypeScript 实现不存在WASM 构建可行但携带成本大于当前同步实现。OSTree、restic、borg、IPFS unixfs数据形态正确但重心不对——OS 镜像、备份快照或完整 P2P 网络栈。没有哪个给 DO 提供干净的 TypeScript 运行时故事。值得了解不值得引入。复用预算花在哪里——三个具体借鉴无运行时依赖采用 casync 的.caidx格式作为 manifest 编码当前编码已结构相同切换到已发布规范零成本白得可调试性任何容器里的casync mtree、desync index与工作区导出为.caidx.castr的备份/迁移能力。规范借鉴非代码借鉴。hasObjects/fetchObjects已与 git 的 haves/wants 词汇对齐——读过git fetch源码的人一眼就能认出。语义已经相同这纯粹是命名对齐。内容定义分块落地时vendor 一个 FastCDC / buzhash 实现而非自研算法微妙边界稳定性、min/max 界、滚动哈希窗口选择存在良好的 MIT 许可 TS 移植。这是唯一重新发明轮子会受伤的地方。不花预算的地方不把isomorphic-git当作同步引擎历史模型在每次 push 上都与活树模型对抗不把libcasync或 WASM 构建当作运行时依赖我们维护的协议面约 6 个 RPC、几百行逻辑用库不匹配替换它是净损失不采用 IPFS CIDs / multihash单 DO 容器对内部的间接层买不到任何东西。十、结论cloudflare/computer的同步协议用一套状态基、增量、内容寻址的设计把 DO 侧 SQLite VFS 与容器侧 FUSE 挂载树保持收敛(rev, path)双坐标游标提供跨 rev 的断点续传且不铸造幻影 revcoalesceChanges的按路径合并让重写成本恒为一条线缆记录512 KiB 固定分块 sha256 寻址让重复内容只传输一次、缺什么字节退化为集合差senderRev双分支同时服务同步对端与外部写入者交叉侧appliedPushCursor回显把收敛不变量变成可在线缆上断言的检查。理解其水印语义与 LWW 冲突边界是安全编排多容器 agent 工作负载的前提——每个工作区同一时刻保持一个活跃写入者是当前最稳妥的实践。延伸阅读03. Filesystem Schema同步协议底层全部vfs_*表结构、不变量与 GCSyncRPC 接口定义线缆契约的权威 TypeScript 形态同步驱动实现pullOnce/pushOnce/tick/reconcileWatermarks服务端适配器SyncRPCServer的 push 原子性与 hooks变更合并实现 与 apply 实现线缆两端的关键算法06. Mount Interface 与 08. Capnweb Interface懒挂载水合与线缆 framing【免费下载链接】computerGive your agent a computer 项目地址: https://gitcode.com/GitHub_Trending/computer1/computer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表