61 lines
4.0 KiB
Markdown
61 lines
4.0 KiB
Markdown
# 25 · 实例崩溃循环事故(重复 loader id)+ not_running 浏览器导航兜底
|
||||
|
|
|
|||
|
|
- 日期:2026-09-11
|
|||
|
|
- 触发:用户刷新后看到 **`{"error":"not_running"}`** 且无自动跳转
|
|||
|
|
- 结论一句话:**这是一次自造事故** —— 插件包内的 `cordis.patch.yml` 与 profile 层**重复 insert 同一个 loader id** → 实例启动失败并**崩溃循环**(被新上线的熔断拦住),用户停在 `not_running` JSON。已修:包内改空补丁(单点插入)+ 浏览器导航永不吐 JSON + 并发进场等待在飞实例。
|
|||
|
|
- 状态:**已修复并上线**(服务已重启;插件 0.1.2 已装到两个 profile)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 一、事故链(journald 实证)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Error: dsh: plugin tree failed to load: failed to apply loader entry include (cordis:include):
|
|||
|
|
duplicate loader entry id: workspace-scoped-picker
|
|||
|
|
→ 实例启动即退出 → 自动重启 ×5(1→2→4→8→16s)
|
|||
|
|
[crash-restart] {"event":"crash-loop-circuit-open","restartsInWindow":5,"maxRestartsInWindow":5,...}
|
|||
|
|
→ 熔断生效、停止自动重启、条目标 failed 并移除
|
|||
|
|
→ 用户刷新子域页面 → 代理判定 not_running → 回落到 reply.code(404).send({error:'not_running'})
|
|||
|
|
→ 浏览器显示裸 JSON
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**根因**:把包内 `cordis.patch.yml` 从"覆盖行"改成 `insert: workspace-scoped-picker` 后,**profile 层也 insert 同一 id** → loader 认为重复 id 是致命错误(`duplicate loader entry id`)→ 插件树加载失败。
|
|||
|
|
|
|||
|
|
> ✅ 值得记录的正面结果:**档案 20 的崩溃熔断按设计工作**(5 次后开断,避免无限重启风暴),并且它把实例标 `failed` 后允许用户重新进入(清空计数、给足新预算)。
|
|||
|
|
|
|||
|
|
## 二、修复
|
|||
|
|
|
|||
|
|
| # | 修复 | 位置 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| **1** | **包内 bundle patch 改为空补丁 `[]`** —— 该行只在 profile 层**单点插入**,避免双写;插件版本 0.1.2 | `poc/workspace-scoped-picker/cordis.patch.yml` |
|
|||
|
|
| **2** | **not_running 浏览器导航兜底(档案 25 主体)**:`GET + accept: text/html` 时**永不**返回 JSON;并发进场(另一请求正在拉起 → `AlreadyRunningError`)改为**等待在飞实例的 launch token(≤20s)** 再 302;仍拿不到则 **302 回门户** | `src/supervisor/proxy.ts` |
|
|||
|
|
| **3** | 插件 v0.1.2:面包屑根节点用友好名(不暴露 `/var/lib/...`)+ **加载标记** `[workspace-scoped-picker] loaded root=…`(用于确定性验证是否生效) | `poc/workspace-scoped-picker/lib/index.js` |
|
|||
|
|
| **4** | (同批)预置默认工作区 `seedDefaultWorkspace`(见档案 24 后续):编辑器要求"必须选工作区"才能开会话 → 平台直接种 `ws` | `src/supervisor/orchestrator.ts` |
|
|||
|
|
|
|||
|
|
**安装要点(踩坑)**:
|
|||
|
|
- `pnpm add file:<tgz>` 必须带 `HOME=<userRoot>/ws`;
|
|||
|
|
- **profile 是 pnpm workspace 根时才需要 `-w`**(guest 需要),否则会报 `--workspace-root may only be used inside a workspace`(admin 不需要);
|
|||
|
|
- 换包版本(0.1.1→0.1.2)以确保 pnpm 不复用缓存。
|
|||
|
|
|
|||
|
|
## 三、验证
|
|||
|
|
|
|||
|
|
| 检查 | 结果 |
|
|||
|
|
|---|---|
|
|||
|
|
| `ci.sh`(typecheck + build + 38 单测) | ✅ |
|
|||
|
|
| 两 profile 安装 0.1.2 | ✅ 软链均指向 `0.1.2.tgz`;包内 `cordis.patch.yml` 确认为 `[]` |
|
|||
|
|
| profile patch 幂等 | ✅ 仅一处 `insert`(另一处为 `name:` 值) |
|
|||
|
|
| 插件自检 | ✅ 26/26 |
|
|||
|
|
| 服务重启 | ✅ active / 0 残留 / 门户 200 |
|
|||
|
|
| 端到端(实例启动无 duplicate 报错 + 加载标记出现) | ⏳ 待用户进入会话后核对(我会在日志里查 `[workspace-scoped-picker] loaded` 与 `[seed-workspace]`) |
|
|||
|
|
|
|||
|
|
## 四、回滚
|
|||
|
|
|
|||
|
|
| 项 | 回滚 |
|
|||
|
|
|---|---|
|
|||
|
|
| 插件包 | 退回 0.1.1(但 0.1.1 含重复 insert,**不要**;应退回空补丁版并保持 0.1.2) |
|
|||
|
|
| 编排器 | `/opt/dsh/backups/orchestrator.ts.bak-<TS>`、`proxy.ts.bak-<TS>`;或 `git revert ce1b3ac` → `npm run build` → 重启 |
|
|||
|
|
| profile patch | `<profile>/cordis.patch.yml.bak-*` |
|
|||
|
|
|
|||
|
|
## 五、红线遵守
|
|||
|
|
|
|||
|
|
只改编排器自身代码与自建插件包;未触碰官方 dsh 主程序与缓存;未读取任何用户数据。
|