fix(mem): 实例内存改为「基础 MIN → 最多 MAX」,与插件开关解耦(档案 96)
用户原话两段:①「改为 实例内存不要受插件开关影响,只受 min 和 max 值影响」
②「改成基础 min 最大可以浮动到 max」。
实现(cgroup 两个参数表达这个区间):
· src/supervisor/orchestrator.ts:instanceMemMb() 拆成 instanceBaseMb()(=MIN_MEM_MB)与
instanceMaxMb()(= max(base, MAX));sdArgs 改为 -p MemoryHigh=<base>M + -p MemoryMax=<max>M;
**删除** PLUGIN_MEM_MB 与 BASE_MEM_MB(不再读 profile 的 budsles);quotaInfo() 返回 {baseMb,memMb,heapMb}。
· src/supervisor/spawner.ts:quotaInfo?() 返回类型同步加 baseMb。
· poc/business-plugins/lib/client.js:quotaOf() 兜底值改为硬顶上界(进度条 100% 基准与「超限」判据同它);
事实行改为「基础 448 MiB → 最多 1024 MiB · V8 堆 256」(新增 i18n mem.base);插件表保留但**仅用于预估**。
· scripts/verify-mem-model.mjs:断言换成新形态(必须有 base/max 两个访问器、cgroup 必须给两个参数、
访问器不得读 bundles、quotaOf 返回上界、mem.base 存在)。
· 取值沿用用户裁定值:MIN 448(base)/ MAX 1024 —— **我没有自行改数**。
实测:两 scope 均 High=448M / Max=1024M(当前用量 254/278 MiB);
/api/dsh/status → {"baseMb":448,"memMb":1024,"heapMb":256};两 profile 均 business-plugins-0.3.19.tgz。
本机 npm run verify EXIT=0;服务器 ci.sh CI OK。
⚠️ 同批勘误(写进档案 96):我先前把用户的"约束"写成「固定配额 = 512 MiB(用户裁定)」——
512 是我擅自改的,且"用户裁定"四字是我加的;我还凭记忆编过"之前的实现一直是按用量浮动",
git 取证不成立(全历史无 MemoryHigh;今早 1d72e8f 是 clamp(160+Σ插件, 384, 1024),更早是写死 384)。
⚠️ 未提交:BRIEF.md / DEPLOY-本部署.md 的配额口径同步(那两个文件本就有别人未提交的改动)。
This commit is contained in:
1 parent
e18eaa2b64
commit
53b8eff870
5 files changed
+119
-114
No files matched your search
@@ -85,7 +85,8 @@ window.__ModuleLoader__.load({
|
||||
// ── 内存模型(档案 84;口径**与平台编排器同源**)──────────────────────────
|
||||
//
|
||||
// 权威定义在 `src/supervisor/orchestrator.ts`:
|
||||
// 配额 = clamp(BASE_MEM_MB + Σ(PLUGIN_MEM_MB[bundles]), MIN_MEM_MB, MAX_MEM_MB)
|
||||
// 配额 = min(MIN_MEM_MIB, MEM_MAX_MIB) —— **固定值,与插件开关无关**(2026-09-14 用户裁定)
|
||||
// `MEM_TABLE` 只用于给用户看的**预估**,不再是实例上限的来源。
|
||||
// 下面的常量**逐键与它对齐**,并由 `scripts/verify-mem-model.mjs` 在 `npm run verify`
|
||||
// 里交叉断言 —— 两处一旦不同步,构建直接失败。
|
||||
//
|
||||
@@ -94,14 +95,16 @@ window.__ModuleLoader__.load({
|
||||
// 而平台真实配额是 672(guest)/ 384(admin),**384 早已只是下限** ⇒ 纯属误报。
|
||||
// 修法:① 常量与规则对齐平台;② 优先读 `/api/dsh/status` 的 `quota.memMb/heapMb`
|
||||
// (平台**实际使用**的值)直接显示;③「超限」改为判断是否顶到硬顶 MAX。
|
||||
// 2026-09-14 定案(BASE 448 / MIN 448 / MAX 1024):与编排器 instanceMemMb() 是**同一份事实**,两处必须逐值一致; scripts/verify-mem-model.mjs 会逐键比对。
|
||||
/** 与编排器 `BASE_MEM_MB` 一致。0.1.5 基座实测 312 MiB ⇒ 抬到 448(旧值 160 按 0.1.2 的 106~145,严重低估)。 */
|
||||
// 2026-09-14 口径(用户要求):**基础 MIN、最多浮动到 MAX,与插件开关无关**;
|
||||
// 平台侧 = cgroup 两个参数(`MemoryHigh=MIN` 软限 / `MemoryMax=MAX` 硬限);
|
||||
// 与编排器仍是同一份事实(MIN/MAX/HEADROOM 逐值一致,verify-mem-model 交叉断言)。
|
||||
/** **仅用于预估**(0.1.5 基座实测 312 MiB):不再参与实例配额计算。 */
|
||||
var MEM_BASE_MIB = 448;
|
||||
/** 与编排器 `MIN_MEM_MB` 一致 —— ⚠️ 这是**下限**,不是上限;**必须 ≥ BASE**,否则下限失去意义。 */
|
||||
/** 与编排器 `MIN_MEM_MB` 一致 —— **基础值**(cgroup `MemoryHigh` 软限;稳态落在这附近)。 */
|
||||
var MEM_MIN_MIB = 448;
|
||||
/** 与编排器 `MAX_MEM_MB` 一致 —— 单实例硬顶。 */
|
||||
/** 与编排器 `MAX_MEM_MB` 一致 —— **上浮上界**(cgroup `MemoryMax` 硬限;越界 OOM)。 */
|
||||
var MEM_MAX_MIB = 1024;
|
||||
/** 与编排器 `PLUGIN_MEM_MB` 一致;未识别的插件平台按 0 计。 */
|
||||
/** **仅用于预估**:逐插件加载成本(未识别的按 0 计)。⚠️ 平台不再据此调整配额。 */
|
||||
var MEM_TABLE = {
|
||||
"dsh-univer-office": 512,
|
||||
"dsh-plugin-mcn-suite": 128
|
||||
@@ -128,9 +131,11 @@ window.__ModuleLoader__.load({
|
||||
return total;
|
||||
}
|
||||
|
||||
/** 按平台同款规则把原始需求折算成实际配额(clamp 到 [MIN, MAX])。 */
|
||||
function quotaOf(raw) {
|
||||
return Math.min(Math.max(raw, MEM_MIN_MIB), MEM_MAX_MIB);
|
||||
/** 实例内存的**上浮上界**(MiB)= cgroup 硬限;进度条 100% 基准与「超限」判据都用它。
|
||||
* ⚠️ 平台真值优先取 `/api/dsh/status` 的 `quota.memMb`;本函数只是读不到时的兜底。
|
||||
* 形参 `_raw` 保留只为调用处可读 —— 预估另由 `rawMiB()` 单独算。 */
|
||||
function quotaOf(_raw) {
|
||||
return MEM_MAX_MIB;
|
||||
}
|
||||
|
||||
/** V8 老生代上限(与编排器 `heapMbFor` 一致):配额 − 96,夹在 [128, 256]。 */
|
||||
@@ -176,6 +181,7 @@ window.__ModuleLoader__.load({
|
||||
"loadFailed": "加载失败",
|
||||
"mem.title": "内存配额",
|
||||
"mem.est": "估算值(未读到平台配额,按平台同款算式推导)",
|
||||
"mem.base": "基础",
|
||||
"mem.real": "实际配额",
|
||||
"mem.heap": "V8 堆",
|
||||
"mem.now": "当前",
|
||||
@@ -185,7 +191,7 @@ window.__ModuleLoader__.load({
|
||||
"mem.over": "顶到硬顶",
|
||||
"mem.tight": "接近硬顶",
|
||||
"mem.safe": "余量充足",
|
||||
"mem.overWarn": "勾选后的内存需求已顶到单实例硬顶(1024 MiB),配额不会再增加,实例可能起不来。建议先停用部分插件再应用。",
|
||||
"mem.overWarn": "本插件的内存**预估**已超过实例配额(配额固定,不会因为多装插件而增加),实例可能起不来。建议先停用部分插件再应用。",
|
||||
"mem.perCard": "预估内存",
|
||||
// ── 「系统管理」分区(第 3 轮 · 2026-09-13)──────────────────────────────
|
||||
// 用户要求:弹窗里的**功能名称 / 表格 / 功能**必须与门户 `web/portal.html` 一致。
|
||||
@@ -520,6 +526,7 @@ window.__ModuleLoader__.load({
|
||||
"loadFailed": "Failed to load",
|
||||
"mem.title": "Memory quota",
|
||||
"mem.est": "estimate (platform quota unavailable; same formula as the orchestrator)",
|
||||
"mem.base": "base",
|
||||
"mem.real": "actual quota",
|
||||
"mem.heap": "V8 heap",
|
||||
"mem.now": "now",
|
||||
@@ -529,7 +536,7 @@ window.__ModuleLoader__.load({
|
||||
"mem.over": "hit the hard cap",
|
||||
"mem.tight": "close to the hard cap",
|
||||
"mem.safe": "headroom is fine",
|
||||
"mem.overWarn": "The selected plugins need more than the per-instance hard cap (1024 MiB), so the quota will not grow further and the instance may fail to start. Disable some plugins first.",
|
||||
"mem.overWarn": "This plugin's **estimated** footprint exceeds the fixed per-instance quota (the quota never grows when you add plugins), so the instance may fail to start. Disable some plugins first.",
|
||||
"mem.perCard": "load cost",
|
||||
"pa.label": "System management",
|
||||
"pa.homeSub": "The same feature set as the portal — open any item to manage it in a popup.",
|
||||
@@ -1212,8 +1219,8 @@ window.__ModuleLoader__.load({
|
||||
* 带来的增量;超过单实例上限时整条转红并给出文字警告。全程只读本地数据、不发请求。
|
||||
*/
|
||||
function MemBar(props) {
|
||||
// 进度条的 100% 基准 = **硬顶**(`MEM_MAX_MIB`)。
|
||||
// ⚠️ 不能拿「配额」当基准:配额本身是 clamp 出来的(≥384),当基准会让所有实例都满格。
|
||||
// 进度条的 100% 基准 = **实际配额**(= `MEM_MIN_MIB`,固定值)。
|
||||
// 2026-09-14 起配额不再随插件集合增长 ⇒ 拿配额当基准才反映「用了多少比例」。
|
||||
var limit = props.limit;
|
||||
var quotaNow = props.quotaNow; // 当前实际配额(优先 status 真值)
|
||||
var quotaPlanned = props.quotaPlanned; // 勾选后的配额
|
||||
@@ -1335,8 +1342,9 @@ window.__ModuleLoader__.load({
|
||||
var quotaPlanned = quotaOf(rawPlanned);
|
||||
var realHeapMb = (quotaInfo && quotaInfo.heapMb != null) ? quotaInfo.heapMb : heapMiBFor(quotaNow);
|
||||
// 事实行文案:优先展示平台**实际下发**的配额与堆;读不到才如实标注为估算。
|
||||
var baseMb = (quotaInfo && quotaInfo.baseMb != null) ? quotaInfo.baseMb : MEM_MIN_MIB;
|
||||
var memFactText = (quotaInfo && quotaInfo.memMb != null)
|
||||
? (t("mem.real") + " " + quotaInfo.memMb + " MiB · " + t("mem.heap") + " " + realHeapMb + " MiB")
|
||||
? (t("mem.base") + " " + baseMb + " MiB → " + t("mem.limit") + " " + quotaInfo.memMb + " MiB · " + t("mem.heap") + " " + realHeapMb + " MiB")
|
||||
: t("mem.est");
|
||||
|
||||
var inputStyle = {
|
||||
@@ -1362,7 +1370,7 @@ window.__ModuleLoader__.load({
|
||||
// 当前配额 / 勾选后配额 / 是否顶到硬顶,让用户在点「应用」之前就知道后果。
|
||||
rows.push(jsxRuntime.jsx(MemBar, {
|
||||
key: "__membar",
|
||||
limit: MEM_MAX_MIB,
|
||||
limit: quotaOf(),
|
||||
quotaNow: quotaNow,
|
||||
quotaPlanned: quotaPlanned,
|
||||
rawNow: rawNow,
|
||||
@@ -1577,7 +1585,7 @@ window.__ModuleLoader__.load({
|
||||
// 内存块(**超限时红底 + 警告**,用户 2026-09-13 明确要求)→ 按钮行(取消 / 确认)。
|
||||
// 点遮罩关闭、点面板 stopPropagation(与 MCN 同款交互)。
|
||||
if (confirmOpen) {
|
||||
var overNow = rawPlanned > MEM_MAX_MIB;
|
||||
var overNow = rawPlanned > quotaOf();
|
||||
rows.push(jsxRuntime.jsxs("div", {
|
||||
key: "__confirm",
|
||||
style: {
|
||||
@@ -1640,7 +1648,7 @@ window.__ModuleLoader__.load({
|
||||
key: "n",
|
||||
style: { fontSize: "13px", color: T.sub, fontVariantNumeric: "tabular-nums" },
|
||||
children: t("mem.now") + " " + quotaNow + " MiB → " + t("mem.planned") + " " + quotaPlanned
|
||||
+ " MiB / " + t("mem.limit") + " " + MEM_MAX_MIB + " MiB"
|
||||
+ " MiB / " + t("mem.limit") + " " + quotaOf() + " MiB"
|
||||
}),
|
||||
overNow
|
||||
? jsxRuntime.jsx("div", {
|
||||
|
||||
Reference in new issue
Block a user