为什么 Codex Provider Sync 同步这么快?揭秘原地字节更新与流式替换两大落盘策略
【免费下载链接】codex-provider-syncSynchronize Codex session provider metadata across rollout files and SQLite state.项目地址: https://gitcode.com/gh_mirrors/co/codex-provider-sync
Codex Provider Sync 是一个本地工具,负责把 Codex 会话的 Provider 元数据在 rollout 会话文件与 SQLite 状态之间保持一致——当你的当前配置切换到新 Provider 后,它会把历史会话文件里的 Provider 信息对齐过来,让旧会话可以继续用。很多用户关心一个问题:会话文件动辄几十 MiB,全量重写一遍不是慢死了?其实它靠的是原地字节更新和流式替换两大落盘策略,核心原则只有一句:只动该动的字节,正文一个字节都不碰。
痛点:为什么"完整重写"是最慢的做法
一次普通的 Provider 同步,真正要改的只有session_meta里的model_provider这一个 JSON 字段,但 rollout 文件里还装着你全部的聊天记录。如果像传统做法那样把整个文件读出来、改一个字段、再完整写回:
- 几十 MiB 的正文要完整读取一遍
- 生成一份同样大小的临时副本
- 再做一次文件系统替换
读、写、替换三个动作的成本,全都花在了一个几乎没变的字段上。Codex Provider Sync 的架构决策文档明确否定了这条路线:ADR-0015 把"等长原地更新"定为性能基线,后续的 ADR-0016 进一步把它固定为 Node Core 的默认行为。
策略一:原地字节更新(PIO-2),只覆写 Provider 那几十字节
这是快同步的核心。当新旧两个 Provider ID 满足条件时,同步器不复制任何文件,而是用文件句柄直接定位到model_provider字面量的字节偏移,只覆写那一段:
- 新旧 ID 都匹配
[A-Za-z0-9._-]+,且JSON.stringify后的UTF-8 字节长度相同(注意是字节,不是字符数) - 首行中
model_provider位置唯一、原始字面量与预期一致 - 本次不需要历史模型重写、无多个硬链接目标
这些资格检查由 getInPlaceProviderMutation 完成,成功落盘后,结果里计入inPlaceSessionFiles。效果非常直接:
文件身份不变、文件大小不变、首行以外的正文哈希不变,实际写入的字节数就等于 Provider JSON 字面量的长度。
也就是说,改一个 Provider 的 I/O 量,从"整个文件"降到了"几个字段字节"。Windows 上通过内置的 PowerShell worker 和 windows-provider-bytes.cs 执行同一套句柄协议,POSIX 使用已校验的同一句柄定位写入,两端行为一致。
策略二:流式替换(PIO-3),不等长时正文仍逐字节保持
新旧 ID 长度不同时(比如从openai切到更长的自定义 ID),原地覆写会让后面所有字节错位,此时自动切换到第二条路径:
- 在同目录创建临时文件
- 只重写首行(更新 Provider 字段,保留 LF/CRLF 分隔符)
- 剩余正文以有界流式方式逐块复制,不解析、不重序列化
- 校验后原子替换原文件
这条路径允许文件身份变化,结果计入rewrittenSessionFiles,但正文保证逐字节一致——你看到的聊天记录与之前完全相同。关键点是:流式复制只负责"搬砖",绝不做业务解析,所以即使文件再大,也不会因为解析 JSON 而产生额外内存与时间开销。
💡 两种策略的选择完全由 Core 自动完成,你不需要把 Provider 改成固定长度,也没有隐藏的--fast开关——它就是默认行为。
为什么同步器只需要看"第一行"?
两大落盘策略能成立,还有一个前提:业务扫描边界只在首行。普通 Sync 始终取config.toml根级model_provider作为目标(缺失时默认openai),扫描时只解析每个 rollout 的第一行session_meta(见 PIO-1 说明),绝不会为了查模型、cwd 或历史索引去翻几十 MiB 的正文。加密内容只诊断、不修改,其他字段(历史 model、title、SQLiteupdated_at等)一律不碰。
提速不减安全:落盘顺序有严格约束
速度快不代表可以牺牲可靠性。每次写入都遵循固定顺序(PIO-5):
- 消费一次性 Plan,Home 锁内复核快照与占用状态
- 无变更目标时直接返回,不创建备份
- 先做 UndoBackup(默认保留 2 份),再执行首次写入——首次 mutation 前失败意味着零业务写入
- 中途失败会返回带
backupId与阶段信息的partial结果,重新执行即可收敛,也可手动 Restore
一个容易误解的细节:原地写失败时,同步器不会偷偷降级成整文件替换来绕过失败,而是明确报错。宁可失败,不可绕过。
如何确认这次同步真的走了快速路径
同步结果与操作日志会如实报告两种策略各自的实际数量。当一次操作实际重写的会话文件达到 100 个以上时,桌面端结果窗口才会出现默认折叠的"查看提速建议"提示(展示规则见 ADR-0036,它只控制提示展示,不是性能门禁)。你也可以展开概览页的"如何加快同步"说明,了解当前 Provider 是否适合原地更新:
需要手动压测时,可以运行 benchmark-provider-io.mjs 生成参考数据;但项目明确不以 wall-clock 作为门禁——真正的验收由 provider-sync-lite.test.js 这类测试保证:写入字节数精确等于字面量长度、正文尾哈希不变、文件身份与大小不变、每个 rollout 正文至多扫描一次。
快速参考
| 想深入了解 | 看这里 |
|---|---|
| 原地字节更新的原始决策 | docs/adr/0015-provider-byte-updates-and-fast-sync.md |
| 当前落盘策略与 PIO-1~PIO-6 不变量 | docs/architecture/NODE_CORE_ARCHITECTURE_ZH.md |
| 原地写与流式替换实现 | src/session-files.js |
| Windows 端句柄协议 helper | src/windows-provider-bytes.cs |
| 存储层四端口设计 | packages/core/src/infrastructure/codex-storage.js |
| 提速提示与日志布局 | docs/adr/0036-sync-performance-guidance-and-log-split-view.md |
一句话总结:Codex Provider Sync 的快,来自"少干活"而不是"干得快"——能原地覆写几个字节,就绝不复制整个文件;必须复制时,正文也只流过一遍、一个字节都不改。
【免费下载链接】codex-provider-syncSynchronize Codex session provider metadata across rollout files and SQLite state.项目地址: https://gitcode.com/gh_mirrors/co/codex-provider-sync
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考