chore(docs): 文档库并入代码仓(R4 选 a)+ 索引/台账跟进
1) dsh-server-docs/ 从工作区(原 E:\...\aliyun-dsh-server\dsh-server-docs)**整体并入本仓**,
保留目录名 ⇒ 仓库内 dsh-server-docs/... 的相对引用天然继续有效;旧目录(含其 .git)已归档到
工作区 _中间产物_待清理/,未随本提交带入。
2) .gitattributes:新增 `dsh-server-docs/** -text` —— 原文档库是 `* -text` + autocrlf=false,
必须保持纯 LF,否则会被本仓的 CRLF 规则翻掉。
3) 活引用里的绝对路径已全部改到新位置(docs 的 INDEX / README / scripts / skills + 用户级 skills
+ ~/.workbuddy/settings.json 的 hooks);历史档案(04-调整方案/、archive/)按「只增不改」未动。
⚠️ hooks 路径改动需「完全重启会话」才生效(配置是会话启动快照)。
4) 交接单/T08:新增 §16「生产整体切换执行记录」(形态 / 落地动作 / **4 个只有真上线才暴露的真 bug** /
验收证据 / 回滚命令 / 残留项);台账 T08 行 → 已完成并归档;03-路线图 §二 登记 T08 收尾项。
5) 统一称谓:**「本机」只指跑 WorkBuddy 的开发机**,47 / 106 一律写「远程服务器」。
This commit is contained in:
1 parent
c70d5d860e
commit
5ad755116e
173 files changed
+27632
No files matched your search
@@ -0,0 +1,97 @@
|
||||
# 84-实例内存配额口径统一(消灭「388 MiB」误报)
|
||||
|
||||
> 2026-09-13 落地 | 触发:用户问「**实例内存大小是不是调整过了,为什么功能设置中还是显示 388 多**」
|
||||
> 定性:**同一事实被复制成两份、只改了一份** —— 属于本项目反复出现的「两处副本必然漂」类缺陷。
|
||||
|
||||
## 一、现象与实测(先取证,再动手)
|
||||
|
||||
「dsh 设置 → 功能管理」顶部那条内存条显示 **勾选后 388 MiB** 并报 **⚠ 将超出上限**。
|
||||
|
||||
实测真实配额(`systemctl show <scope> -p MemoryMax`,字节换算):
|
||||
|
||||
| 实例 | 装载的重型插件 | 真实 `MemoryMax` | 验算 |
|
||||
|---|---|---|---|
|
||||
| guest(uid 100002) | `dsh-univer-office` | `704643072` = **672 MiB** | 160 + 512 ✓ |
|
||||
| admin(uid 114801) | `dsh-plugin-mcn-suite` | `402653184` = **384 MiB** | clamp(160+128)=384(踩下限)✓ |
|
||||
|
||||
V8 堆两边都 = `min(256, 配额−96)` = **256 MB**。⇒ **配额确实早就改过了,且是真生效的。**
|
||||
|
||||
## 二、根因:两套模型从未对齐
|
||||
|
||||
| 项 | 平台侧(决定真实 `MemoryMax`) | 插件前端(决定界面显示) |
|
||||
|---|---|---|
|
||||
| 位置 | `src/supervisor/orchestrator.ts` | `poc/business-plugins/lib/client.js` |
|
||||
| 基线 | `BASE_MEM_MB = 160` | `MEM_BASE_MIB = **285**` |
|
||||
| `dsh-univer-office` | **512** | **64** |
|
||||
| `dsh-plugin-mcn-suite` | **128** | **39** |
|
||||
| 合成规则 | `clamp(160+Σ, **384**, 1024)` | `285 + Σ`,并把 **384 当硬上限**判超限 |
|
||||
| 「上限」 | 384 只是**下限**,硬顶是 1024 | `MEM_LIMIT_MIB = 384`(档案 58 时代的**写死值副本**) |
|
||||
|
||||
- **388 的来源**:候选池恰好只有这 2 个插件(实调 `/api/plugins/business` 确认),前端表对两者都有值 ⇒ 全勾 = `285+64+39 = 388`。而平台侧同一情形 = `160+512+128 = 800`。
|
||||
- **误报机制**:`388 > 384` 触发「将超出上限」红色警告;但 384 在平台侧**早已只是下限** ⇒ 纯误报。
|
||||
- **为什么插件只能自估**:`/api/dsh/status` **不返回任何内存字段**(只有 running / instance / watchdog / breaker / url)⇒ 插件无从得知真值。
|
||||
- **漂移时点**:档案 81 R1 把「写死 384」改成「按插件集合推导」时,**只改了编排器**;插件那份副本原地未动,此后所有改动(univer 384→512、mcn-suite 128)都没同步。
|
||||
|
||||
## 三、改法(两步,一次做掉)
|
||||
|
||||
### ① 平台侧:把已算好的真值透出来(零新增计算)
|
||||
|
||||
| 文件 | 改动 |
|
||||
|---|---|
|
||||
| `src/supervisor/spawner.ts` | `Spawner` 接口新增可选方法 `quotaInfo?(userId): { memMb; heapMb } \| null`(照 `breakerInfo?` 的模式) |
|
||||
| `src/supervisor/orchestrator.ts` | 实现 `quotaInfo()` —— 调**同一个** `instanceMemMb()` / `heapMbFor()`,读不到返回 `null` |
|
||||
| `src/web/routes/dsh.ts` | `/api/dsh/status` 增加 `quota: app.supervisor.quotaInfo?.(request.user!.id) ?? null` |
|
||||
|
||||
⇒ 因为走的是 `spawn` 时用的同一函数,**不可能再与实际 `MemoryMax` 对不上**。
|
||||
|
||||
### ② 插件侧:改读真值 + 对齐规则 + 修正超限判据
|
||||
|
||||
- 常量与规则**逐项对齐**编排器(`MEM_BASE_MIB 160` / `MEM_MIN_MIB 384` / `MEM_MAX_MIB 1024` / `MEM_HEAP_HEADROOM_MIB 96` / `MEM_TABLE{univer:512, mcn:128}`),并新增 `quotaOf()` / `rawMiB()` / `heapMiBFor()` 与编排器同构。
|
||||
- `load()` 并行取 `/api/dsh/status`(失败静默,只影响内存条),把 `quota.memMb/heapMb` 作为**事实行**直接显示:`实际配额 384 MiB · V8 堆 256 MiB`;读不到才退回估算并如实标注。
|
||||
- **超限判据改为「原始需求 > 硬顶 1024」** —— 旧版拿 clamp 后的值判,既误报、又在语义上不可能触发(clamp 后永远 ≤ MAX)。
|
||||
- 未进成本表的插件不再显示 `≈ 0 MiB` 徽章(平台口径「未识别按 0 计」,显示 0 只会制造噪音)。
|
||||
- 文案:`上限`→`硬顶`、`将超出上限`→`顶到硬顶`,`overWarn` 明确写出「1024 MiB 硬顶」。
|
||||
- 版本 **0.3.6**。
|
||||
|
||||
### ③ 钉死(关键):新增 `scripts/verify-mem-model.mjs`
|
||||
|
||||
**根因是「同一事实两处副本」,所以修法必须包含「让它不可能再漂」。**
|
||||
脚本解析编排器与插件的常量/成本表/clamp 算式/接线点,**逐项交叉断言**,并接进 `npm run verify`:
|
||||
常量不一致、成本表任一键不一致、clamp 算式不同构、`quotaInfo` 接口/实现/路由任一环缺失、插件不再读 status —— **任一命中即构建失败**。
|
||||
|
||||
**反向验证(已做)**:把插件的 `MEM_BASE_MIB` 改回旧值 `285` ⇒ 脚本报
|
||||
`✗ 常量一致 · 基线 BASE -> 编排器 160 / 客户端 285`、退出码 1;还原后全绿。
|
||||
(说明这道闸门真的会拦,而不是摆设。)
|
||||
|
||||
## 四、验证记录
|
||||
|
||||
| 层 | 证据 |
|
||||
|---|---|
|
||||
| 编译 | `npm run build`(tsc)零报错 |
|
||||
| 单测 | `npm run verify` 全绿 —— 含新增的 15 项模型交叉断言 |
|
||||
| 平台实装 | 服务器 `/opt/dshs/lib/supervisor/orchestrator.js`、`lib/web/routes/dsh.js` md5 与本机编译产物一致(差异已核:orchestrator 正好 19 行 = 我的改动;dsh.js 3 行 + 1 行他人旧的注释) |
|
||||
| 接口 | `GET /api/dsh/status` → `quota: {"memMb":384,"heapMb":256}`,与实测 `MemoryMax=402653184` **吻合** |
|
||||
| 插件实装 | admin/guest 两 profile 均 **0.3.6**;`lib/client.js` md5 `86bc8a46…` **与本地产物一致**;旧符号 `MEM_LIMIT_MIB` **0 处** |
|
||||
| 端到端 | 冷启动取下发 bundle(`rev=a8eb3ecf1edb`,4.30 MB):`MEM_BASE_MIB=160`、`MEM_MAX_MIB=1024`、univer 成本 512、读 status **6 处**、旧符号 **0 处** |
|
||||
|
||||
**修复后界面应有的样子**(admin 为例):`当前 384 MiB → 勾选后 800 MiB` + `实际配额 384 MiB · V8 堆 256 MiB`,
|
||||
状态 **余量充足**(800 < 1024)—— **388 与红色误报都不再出现**。
|
||||
|
||||
## 五、事故/踩坑记录
|
||||
|
||||
1. **并发撞车(本次最该记住的一条)**:`poc/business-plugins/lib/client.js` 在我作业期间**被并行会话连续改了两轮**(0.3.4「系统管理改为门户 6 功能页」、0.3.5「插件管理列表高度」,**0.3.5 已部署**)。因为每次 `Edit` 前都**重读了文件**,我的改动是**叠在它们之上**、没有覆盖;但版本号我以为是 0.3.3、实际已是 0.3.5 ⇒ 我第一把 `npm pack` 打出了「内容是别人的 0.3.5 + 我的修改、版本号却写 0.3.5」的**同名假产物**(已删除),改判为 **0.3.6**。
|
||||
⇒ **纪律:动手前先 `git status` + 查最新 tgz 版本号;版本号取「当前值 +1」,不要用记忆里的值。**
|
||||
2. **未提交区已被多方污染**:该仓 `git status` 同时有他人未提交的 0.3.4/0.3.5(已上线但**未 commit**)。
|
||||
⇒ 本次**只提交我自己的文件**,`poc/business-plugins/lib/client.js` 与其 `package.json` 保持未提交、留给他人的批次一起提交(避免把别人在途改动「顺手上锁」)。
|
||||
3. `server 侧 dsh.js` 比仓库**旧一行注释**(`/desktop.html`→`/portal.html`)—— 说明服务器编译产物整体略滞后于仓库;本次顺手带上,无行为影响。
|
||||
|
||||
## 六、回滚
|
||||
|
||||
- 平台:`/opt/dshs/.bak-20260913-memmodel/{orchestrator.js,dsh.js}` 拷回原位 → `systemctl restart dshs.service`
|
||||
- 插件:`/opt/dsh/artifacts/` 保留历代 tgz(0.3.5 仍可取)→ `node /opt/dshs/scripts/ensure-biz-plugins.cjs --all --restart` 指定旧版
|
||||
- 校验脚本:删掉 `package.json` verify 链末尾的 `&& node scripts/verify-mem-model.mjs` 即可(脚本本身独立,不影响运行)
|
||||
|
||||
## 七、后续(未做,非阻塞)
|
||||
|
||||
- 若成本表再加插件:**只改编排器 + 跑 verify 会失败** ⇒ 逼你同步插件侧(这就是设计意图)。若将来希望彻底「只维护一处」,可考虑由 `/api/dsh/status` 或新增只读接口**下发成本表本身**,插件不再内置副本。
|
||||
- `business-plugins` 前端那条内存条现在展示的是**配额**(cgroup 上限)而非**实时占用**;若要显示真实 RSS,需要平台侧新增采样接口(当前未做,文案不声称是实时值)。
|
||||
Reference in new issue
Block a user