修复消息渲染并更新发布包
This commit is contained in:
3
.trellis/tasks/09-27-codex-start-lock-retry/check.jsonl
Normal file
3
.trellis/tasks/09-27-codex-start-lock-retry/check.jsonl
Normal file
@@ -0,0 +1,3 @@
|
||||
{"file":".trellis/spec/backend/error-handling.md","reason":"检查启动忙错误、非暂时错误与最终错误信息是否符合后端错误处理规范。"}
|
||||
{"file":".trellis/spec/backend/logging-guidelines.md","reason":"检查锁竞争和退避日志是否脱敏、可追踪且不过度输出。"}
|
||||
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"检查实现和回归测试是否满足后端质量要求。"}
|
||||
@@ -0,0 +1,3 @@
|
||||
{"file":".trellis/spec/backend/error-handling.md","reason":"启动失败、可重试错误与最终错误诊断需要遵循后端错误处理约定。"}
|
||||
{"file":".trellis/spec/backend/logging-guidelines.md","reason":"锁等待、重试与释放需要遵循现有日志脱敏和事件命名约定。"}
|
||||
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"新增跨进程锁与测试需要遵循后端质量和验证要求。"}
|
||||
35
.trellis/tasks/09-27-codex-start-lock-retry/prd.md
Normal file
35
.trellis/tasks/09-27-codex-start-lock-retry/prd.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Codex 启动单实例锁与退避重试
|
||||
|
||||
## 目标
|
||||
|
||||
避免 cc-web 启动 Codex app-server 时与其他 cc-web 实例或残留启动流程并发写入同一 `CODEX_HOME`,导致 SQLite 报 `database is locked` 并直接启动失败。
|
||||
|
||||
## 范围
|
||||
|
||||
- 在 `lib/codex-app-server-client.js` 的 app-server 启动层实现跨进程单实例锁。
|
||||
- 锁文件固定为 `<CODEX_HOME>/.cc-web-codex.lock`;锁目录按解析后的绝对 `CODEX_HOME` 隔离。
|
||||
- 锁持有范围从启动尝试前开始,覆盖 app-server 初始化成功后的整个子进程生命周期;仅在子进程收到 `exit`/`error`、初始化失败清理完成或显式 `stop()` 后释放。
|
||||
- 锁记录使用 JSON,至少包含 `pid`、Linux `/proc/<pid>/stat` 的进程启动时间(无法读取时为 `null`)、随机 `token`、创建时间。释放前必须校验 token,避免误删后来者的锁。
|
||||
- 陈旧锁判定:PID 不存在,或 PID 存在但进程启动时间与记录不一致;记录损坏或不完整时,先按活动锁保护,超过 2 秒宽限期仍未补全才允许清理。
|
||||
- 锁获取默认最多等待 30 秒,轮询间隔 200ms 起步、最多 2 秒并加入少量随机抖动;等待超时返回 `CODEX_HOME_LOCK_BUSY`,不杀死锁持有者。
|
||||
- 对可判定为 SQLite/状态库暂时忙的初始化失败执行最多 4 次启动尝试,延迟为 500ms、1s、2s(上限 5s)并加入少量随机抖动;每次重试都等待旧子进程退出、释放本次锁后重新获取锁。默认总等待不超过 30 秒(不包含一次 30 秒初始化 RPC 超时)。
|
||||
- 可通过环境变量覆盖:`CC_WEB_CODEX_LOCK_WAIT_MS`、`CC_WEB_CODEX_START_RETRY_ATTEMPTS`、`CC_WEB_CODEX_START_RETRY_DELAY_MS`、`CC_WEB_CODEX_START_RETRY_MAX_DELAY_MS`;所有值限制在合理范围内。
|
||||
- 仅当 stderr、启动错误 message 或错误 cause 明确包含以下模式时重试:`database is locked`、`database table is locked`、`SQLITE_BUSY`、`SQLITE_BUSY_TIMEOUT`、`failed to open ... (state|log|database)`、`failed to initialize state runtime`。不因鉴权、命令不存在、参数错误、协议错误或普通网络错误重试。
|
||||
- 保留现有 app-server 客户端 API 与 worker/source 两条启动路径。
|
||||
- 增加可独立运行的 Node 回归测试,覆盖锁互斥、活动锁等待超时、陈旧锁清理、PID 启动时间不匹配保护、暂时性错误重试、非暂时错误不重试、成功/失败/显式停止后的锁释放。
|
||||
|
||||
## 约束
|
||||
|
||||
- 不删除用户数据,不改动用户的 Codex 数据库;只创建/清理 cc-web 自己的锁文件。
|
||||
- 不把外部 Codex 进程误判为可安全杀死的残留进程;锁竞争只等待或报错。
|
||||
- `SIGKILL` 等无法执行清理的场景依赖 PID + 启动时间识别陈旧锁,不保证实时删除锁文件。
|
||||
- 启动失败最终抛出的错误保留最后一次原始 stderr,并附带尝试次数、锁路径和最后一次退避原因;日志不写入 API key。
|
||||
- 重试仅发生在 `initialize` 阶段,不能自动重发 `thread/start`、`turn/start` 或其他有副作用的请求。
|
||||
|
||||
## 完成标准
|
||||
|
||||
1. 同一 `CODEX_HOME` 的两个 cc-web app-server 客户端不能同时持有锁;第二个客户端在锁释放前最多等待配置的锁等待时长,超时得到 `CODEX_HOME_LOCK_BUSY` 和锁持有者摘要。
|
||||
2. 初始化 stderr/message 命中暂时性 SQLite 模式时,按明确的有限退避策略重新启动,成功后对调用方透明;非暂时性错误只尝试一次。
|
||||
3. app-server 正常停止、初始化失败和进程退出都会释放锁;无存活 PID 或 PID 启动时间不匹配的陈旧锁不会永久阻塞启动,活动锁不会被误删。
|
||||
4. 现有 Codex App 协议行为不变,worker/source 两条路径都经过同一锁与重试逻辑;语义判断器等额外客户端若无法获取锁,应收到可诊断的忙错误,不得杀掉已有 app-server。
|
||||
5. 新增测试可稳定复现上述场景,并通过项目现有的 Node 语法检查、单元测试和回归测试。
|
||||
26
.trellis/tasks/09-27-codex-start-lock-retry/task.json
Normal file
26
.trellis/tasks/09-27-codex-start-lock-retry/task.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "codex-start-lock-retry",
|
||||
"name": "codex-start-lock-retry",
|
||||
"title": "Codex 启动单实例锁与退避重试",
|
||||
"description": "",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "shiyue",
|
||||
"assignee": "shiyue",
|
||||
"createdAt": "2026-09-27",
|
||||
"completedAt": "2026-09-28",
|
||||
"branch": null,
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
Reference in New Issue
Block a user