Skip to content

fix: make restart lifecycle recoverable, responsive, and fast - #2023

Open
Yuimi-chaya wants to merge 12 commits into
BigPizzaV3:mainfrom
Yuimi-chaya:codex/provider-sync-restart-recovery
Open

fix: make restart lifecycle recoverable, responsive, and fast#2023
Yuimi-chaya wants to merge 12 commits into
BigPizzaV3:mainfrom
Yuimi-chaya:codex/provider-sync-restart-recovery

Conversation

@Yuimi-chaya

Copy link
Copy Markdown
Contributor

上游当前问题与来源

当前 main 的重启问题不是单一端口故障,而是几段历史实现叠加后形成的一条同步启动链:

  1. 887e2bc5 引入的 Manager 重启命令是同步 Tauri command。当前代码仍在同一个调用中执行 Provider Sync 等待、Launcher 停止和 App 停止:commands.rs#L624-L662
  2. 5a3ed5c 为避免强杀正在同步的 Launcher,加入了最长 30 秒的轮询等待;但等待使用 std::thread::sleep,仍发生在上述同步命令中:commands.rs#L5928-L5956。安全意图是正确的,但 UI 会在等待和停止阶段失去响应。
  3. 7bd4a742 把 Provider Sync 放在 helper 和 Codex App 启动之前:launcher.rs#L332-L341。因此同步耗时会直接变成启动/重启耗时。
  4. e94efdb4 的扫描路径会对每个 rollout 执行整文件 fs::read_to_stringprovider_sync.rs#L1275-L1291。大量或超大 JSONL 会造成高峰内存、重复全量扫描和启动延迟。
  5. 5036ff05 让 Manager 按 debug-port 停止 App。当前 Store App 直接启动时可能没有 CDP/debug listener,此时“重启”无法识别并停止正在运行的 App。
  6. c8f08ce1 正确地移除了普通启动时的完整供应商覆盖;之后 7081e847 加入单模型路由。两者组合后,Launcher 可以启动本地协议代理,却不再保证 live config.toml 的活动 transport endpoint 指向该代理。

情况举例

  • Provider Sync 持锁时点击重启,Manager 最长同步等待 30 秒,窗口表现为卡死/未响应。
  • Codex App 从 Windows Store/ChatGPT 入口直接启动且没有 CDP listener 时,点击“重启 Codex++”不能完成语义上的停止并重新接管。
  • App 已退出但旧 Launcher 仍持有 resilient guard 时,第二个 Launcher 收到 WouldBlock 后会进入精简 activation 路径,跳过 Provider Sync、App state sync、插件市场维护、Dream Skin 同步、历史模型后缀清理和协议代理选择。
  • 实机有 766 个 rollout、总计 4.307 GiB。Manager 停止旧进程并生成新 Launcher 只用了约 310 ms,但 helper 在约 23.9 秒后才开始监听;延迟发生在启动前的全量 rollout 扫描。
  • 单模型路由开启时,helper 已在 57321 监听,但活动供应商 base_url 仍保持上游地址,请求绕过本地代理,路由规则不生效。

本 PR 的优化

1. 非阻塞且可恢复的重启事务

  • 将重启 command 改为 async,并通过 spawn_blocking 承载原生进程与文件操作,避免冻结 Manager UI。
  • 加入重启 single-flight;重复点击会立即返回,而不是并发执行两套停止/同步事务。
  • Provider Sync 生命周期锁改为 fail-fast 获取,不再在 UI 操作中轮询 30 秒。
  • 配置同步保留 snapshot/rollback;Launcher/App 停止失败、配置应用失败或 spawn 失败时不会留下半切换状态。
  • 在释放 Provider Sync guard 后才生成新 Launcher,避免新旧生命周期重叠。
  • App 不存在且不要求同步活动供应商时复用启动语义;App 存在时执行真正的停止和重新启动。

2. 跨平台原生 App/Launcher 生命周期

  • Windows 使用现有受支持 App 进程发现,不再要求 CDP/debug listener 才能判断 App 正在运行。
  • macOS 保留精确的 Codex/ChatGPT App 名称,并兼容当前与历史 Launcher 名称。
  • resilient guard 的 stale recovery 同时处理 AddrInUse 和常见的 WouldBlock,确保 App 已退出时重新进入完整 Launcher 生命周期。

3. Provider Sync 有界内存与崩溃恢复

  • rollout 改为流式 JSONL 扫描和单文件处理,不再把整个会话树同时保存在内存中。
  • 事务使用独立 journal、内容哈希、受验证备份和原子替换;启动时先恢复未完成事务,再执行新的同步。
  • Windows 文件 identity、Unix 相对目录操作、reparse/symlink 边界和 downstream SQLite/global-state 回滚都纳入恢复协议。
  • synthetic 48 文件 / 192 MiB 测试约为 17 MiB peak working set。

4. 未变化 rollout 启动快路径

  • 新增独立、非权威的 versioned scan state,绑定扫描规则摘要、rollout root、相对路径、size、mtime、文件 identity、SHA-256 和下游需要的 provider-agnostic summary。
  • 只有证据完整且确认当前目标无需改写的 rollout 才跳过正文读取。
  • 增长、缩小、同路径替换、规则变化、旧/损坏/超限 state、缺少 mtime/identity、需要 provider rewrite 等情况全部回退到原完整事务。
  • state 写入失败不会改变已提交事务结果,只会让下一次执行完整扫描。
  • synthetic 118 文件 / 118 MiB:首次完整扫描 12.327 s,全部未变化的第二次扫描 37.5 ms

5. 协议代理启动不变量

  • 在 helper 启动前结构化解析 live TOML,仅在活动配置确实依赖本地代理时校正 endpoint。
  • 单模型路由、Chat Completions、聚合、OpenAI session identity 和 Official Mix 使用对应的本地代理入口。
  • 普通 Responses 供应商且没有路由时保持直接上游地址,配置文件不会被改写。
  • 不恢复完整 apply_active_relay_profile:不会重写 auth.json、模型/供应商选择、common/context、插件、catalog 或凭据。

自动化验证

  • Provider Sync data lib: 32/32
  • Provider Sync integration: 57/57,另有 2 个 ignored synthetic benchmarks
  • Markdown adapter: 4/4
  • Storage adapter: 26/26
  • Relay config: 138/138
  • Launcher integration: 85/85
  • Launcher binary unit tests: 9/9
  • Manager: 67/67
  • Windows subsystem: 22/22
  • cargo check、focused Rust 2024 rustfmt、git diff --check
  • Windows release build v1.2.55 成功

真人测试

已在 Windows 11、当前 Windows Store Codex/ChatGPT App 上安装本 PR 的 release 构建并完成真人测试:

  • App 正在运行时,“重启 Codex++”可以完成真正的停止、接管和重新启动。
  • App 已退出时,“重启 Codex++”可以按启动语义快速拉起。
  • 大型真实会话目录下的重启不再出现之前的长时间 Launcher 内存增长与约 23 秒全量扫描等待。
  • 单模型路由已确认生效。
  • 普通无路由 Responses 供应商保持直连,不会被强制改写到本地端口。

以上真人测试已通过。

@Yuimi-chaya
Yuimi-chaya force-pushed the codex/provider-sync-restart-recovery branch from 18fa98c to a7ae566 Compare August 27, 2026 07:31
@Yuimi-chaya
Yuimi-chaya force-pushed the codex/provider-sync-restart-recovery branch from a7ae566 to 18ace02 Compare August 27, 2026 20:36
@Yuimi-chaya
Yuimi-chaya force-pushed the codex/provider-sync-restart-recovery branch from 18ace02 to 42298da Compare August 27, 2026 20:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant