Files
dsh_ai1net_server/dsh-server-docs/skills/dsh-instance-diagnose/SKILL.md
T
admin d2ef362a98 feat(overlay): 覆盖网络线 序㊾ —— 探针观测面改「两台中继并集」(附 序㊽ 源码/文档补提交)
序㊾(本棒):
- scripts/overlay-probe.cjs:OBS-01 / OBS-08 / OBS-09 的数据源由「只读 47 中继」
  改为「按两台中继取并集」,消除 worker 归属漂移时的假红 / 假 SKIP
  · endpoints 以 network:hostId:port 为键合并,online 取「或」、localPort 取在线那一侧
  · used 按 network/hostId 去重计数(不求和,避免凭空放大在册数)
  · localPort 属中继机回环落点 ⇒ 按归属分机探活(106 侧落点由 106 机上探)
  · derived(OBS-11)保持 47 视角;阈值与判据一律未放宽
  · OBS-16 计数约束:对 47 /status 的读取仍为三次、Δ 只取 47 的 counters;
    对端 106 的采样为独立一次,落在第三次采样之后,不进 (status2, status3] 门窗口
  · 新增 --peer-status-fixture(并集的对端那一半)与「并集不可取证」强制留痕
- 交接单《覆盖网络-序45-低熵块治理-测熵与实现》§16 全节(§8 前前缀逐字未变)
- 参数表 §11.16 补记(§10 现算指纹未变,值格未动)

附(前几棒已完成并已部署、但尚未入仓的源码 / 文档):
- src/net/relay/content/*.ts、src/net/relay/index.ts、main.ts:块级寻址 C 域分离
- src/supervisor/orchestrator.ts、src/worker/agent.ts:日志采集与巡检(方案 C)
- test/overlay-content.test.mjs:随附用例(npm test = 200 pass / 0 fail / 1 skipped,Node 22)
- scripts/dshlog.mjs(跨机日志取证)、scripts/overlay-entropy.cjs(熵探针)
- dsh-server-docs/04-调整方案/129、133;INDEX.md / docs-manifest.json / 交接单 README 登记
2026-09-19 05:35:35 +08:00

252 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: dsh-instance-diagnose
description: DSH 多租户平台(alotbuy.com / 47.77.182.89)单个用户实例的「故障诊断」技能,重点是内存 / OOM / 实例崩溃重启。当出现「实例打不开」「会话突然报错/中断」「跑着跑着断了」「响应慢」「怀疑内存不够」时触发。核心:先做第 0 层「跨机日志取证」(dshlog),再分「服务级 / 实例级 / 会话级」三层定位,再用 cgroup 内存三件套定量归因,最后在隔离 cgroup 里复现——**绝不在生产实例上做压力测试**。
version: 1.1.0
updated_at: 2026-09-18
last_change: 2026-09-15:死亡路径由 2 条扩到 4 条 —— 新增 C(bwrap 挂载点被嵌套 tmpfs 遮蔽(`Can't chdir to <正常路径>`))与 D(迁移复现不了启动参数(`Can't chdir to :` 空路径)),含分诊判据与「中间目录必须统一前置」的修法。
agent_created: true
---
# dsh-instance-diagnose — DSH 实例故障诊断
## 何时用
用户报「实例打不开 / 会话突然报错 / 聊到一半断了 / 很慢 / 内存不够」,或你看到 `crash-restart` 日志。**报障第一步永远是先读 journal 拿真实失败请求**,不要先猜。
## 平台事实(硬编码,勿猜)
| 项 | 值 |
|---|---|
| 服务器 | SSH 别名 `bt-server`(端口 32022,root) |
| 用户数据根 | `/var/lib/dshs/users/<uuid>/`(**不是** `/opt/dsh/users`,那里只有 main) |
| 已知账号 | guest = `4092b965-2f68-4977-9989-68b3966f7df0`(系统 uid **100002**)|admin = `cce6d1cd-b376-4304-80f0-0e1c58c9ffde`(uid 114801) |
| 实例配额 | ⚠️ **2026-09-14 起:基础 MIN、最多浮动到 MAX**(档案 **96**,用户要求:**不受插件开关影响**):cgroup `MemoryHigh = MIN_MEM_MB = 448 MiB`(**软限/基础**,超过即回收·限速)+ `MemoryMax = MAX_MEM_MB = 1024 MiB`(**硬限/上界**,越界 OOM);**不再读 profile 的 bundles**;`heapMbFor()` = 配额 − 96,**cap 256**。其余 `CPUQuota 150%` / `TasksMax 128` 未变。实测(2026-09-14):两 scope 均 `MemoryHigh=448M` + `MemoryMax=1024M`(与各自插件集合无关)。⚠️ **`dsh-univer-office` 这类重插件(gateway ≈390MB + 基座)512 装不下 ⇒ 先抬 MIN**。⚠️ `/etc/dshs.env` 里的 `DSH_INSTANCE_NODE_OPTIONS=--max-old-space-size=160` **已不是生效值**(代码侧 `withHeap()` 会摘掉 `--max-old-space-size` 再按配额补回),查看实际值请直接读 `/proc/<pid>/environ` 与 `systemctl show <scope> -p MemoryMax` |
| 宿主 | 物理内存仅 **1.83 GB**(1915896 kB);swap 1 GB |
| 会话文件 | `<用户根>/home/sessions/--var-lib-...-ws-<工作区>--/<session-id>/session.jsonl.zstd`(**多帧 zstd**,按 magic `28 b5 2f fd` 切分逐帧解压) |
> ⚠️ 会话目录名**不是** `home/sessions/<session>`,中间还有一层带名字的 workspace 转义目录。
## 三层定位(按顺序做,每层都能单独结案)
### 第 0 层 · 跨机日志取证(**先做这一层** —— 2026-09-18 加)
**遇到"线上跑着跑着不对"但说不清哪一层时,先用 `dshlog` 把 47 / 106 的 journald 拉回本机再判**:跨机、跨单元、带毫秒时间线,比逐台 `journalctl` 快一个量级。
```bash
N="E:/ProgramData/.workbuddy/binaries/node/versions/22.22.2-3/node.exe"
$N scripts/dshlog.mjs collect --since 6h # 拉取(之后再用就不带 --since,走 cursor 增量)
$N scripts/dshlog.mjs watch --since 1h # 巡检:8 条规则出判据表,有 FAIL ⇒ rc=2
$N scripts/dshlog.mjs q "/EADDRINUSE|OOM/" --since 24h
$N scripts/dshlog.mjs timeline --from 2026-09-18T20:00 --grep "overlay" --out /tmp/tl.log
```
⚠️ **三条必知**(细节见 `04-调整方案/129-日志采集与巡检-方案C实现.md`):
- 🔴 **`ssh -C` 是硬前提**:47 出方向未压缩实测 ~20 KB/s(1.6 MB 要 84 s),开压缩后 11 s。不加会**看起来像卡死**。
- 🔴 **实例层(`dsh --profile`)的 stdout 不进 journald**(`_SYSTEMD_UNIT=…scope` 与 `_PID=` 均为空,已双重取证)⇒ **`dshlog` 拿不到实例自己的日志**,只有 Worker 转发的部分。要实例层证据仍须按下面第 2 层**上机取 cgroup / proc**。
- 📌 归档在 `E:/dsh-logs/`(本机 E 盘,不在仓库内);`prune --keep N` 管保留。
### 第 1 层 · 服务级
```bash
systemctl status dshs --no-pager | head -20 # 重启过没
journalctl -u dshs --since today --no-pager | grep -iE "crash-restart|instance-restart|instance-stable"
```
- `instance-restart` 带 **`exitCode 137`** ⇒ **内核 SIGKILL(128+9),九成是 OOM**
- `exitCode 1` + `ModuleLoader.import` ⇒ 插件加载崩(如 `duplicate loader entry id`)
- 无 exitCode ⇒ 信号终止,看紧随其后的服务重启
### 第 2 层 · 实例级(内存三件套)
**先找 scope 名**(**每次重启 hash 会变**,别写死):
```bash
ls -d /sys/fs/cgroup/memory/system.slice/dsh-*.scope
```
> ⚠️ **本服务器是 cgroup v1**,路径是 `/sys/fs/cgroup/memory/system.slice/<scope>/`,
> 文件叫 `memory.usage_in_bytes` / `memory.max_usage_in_bytes` / `memory.limit_in_bytes`。
> cgroup v2 才是 `memory.current` / `memory.peak` —— **写 v2 路径会静默读到空**。
```bash
P=/sys/fs/cgroup/memory/system.slice/<scope>
cat $P/memory.usage_in_bytes # 当前(字节!不是 KB)
cat $P/memory.max_usage_in_bytes # 历史峰值 —— 等于 limit 就是「曾精确打满」
cat $P/memory.stat # ★ 关键:分 rss 与 cache
cat /proc/<pid>/smaps_rollup # Anonymous / Pss_Anon
```
**判据**:
- `rss` 占绝对多数、`cache` 很小 ⇒ **几乎全不可回收,内核 OOM killer 无路可退**
- `rss` 接近 `limit` 且 `max_usage == limit` ⇒ 配额**零余量**,任何波动就是终点
- `rss_huge` 大 ⇒ 透明大页放大占用
> ⚠️ **`ps` 的 RSS ≠ cgroup 计费**:实测 `ps` 报 437 MiB 而 cgroup 只用 381 MiB(共享页不计入)。
### 第 3 层 · 会话级(谁在吃内存)
```bash
find <用户根>/home/sessions -name session.jsonl.zstd -printf "%TY-%Tm-%Td %TH:%TM %10s %h\n" | sort -r | head -12
```
拉回本地用 `dsh-server-docs/scripts/sess-analyze.mjs` 解(**注意该脚本同目录的 `sess-list-presets.mjs` 首行曾缺 `/**` 起始符**,如报 `SyntaxError: Unexpected token '*'` 先补)。
**别忘了对照**:多实例时横向比 `dsh-instance-mem.log`(**时间戳是 UTC,+8 才是本地时间**)。无插件的实例峰值 vs 有插件的实例峰值 = 插件的净增量。
## 四条死亡路径(症状不同,别混)
| 路径 | 触发 | 日志指纹 | systemd 结果 |
|---|---|---|---|
| **A · V8 堆限** | 堆冲到 `--max-old-space-size` | `FATAL ERROR: Reached heap limit` / `Ineffective mark-compacts` | `code=dumped/status=ABRT` |
| **B · cgroup 限** | RSS 打满 `MemoryMax` | `kernel: oom-kill:constraint=CONSTRAINT_MEMCG, task=node` | `code=killed/status=9/KILL` → 平台 `exitCode 137` |
| **C · bwrap 挂载点被遮蔽**(2026-09-15 新增) | bwrap 参数里**同时绑多个路径且它们嵌套在同一前缀下**(如"用户根" + "共享技能层")。后挂的 `--tmpfs <共同祖先>` 会把**已绑好的挂载点整个遮掉** | `bwrap: Can't chdir to <userRoot>/ws/xxx: No such file or directory` —— ⚠️ **路径看起来完全正常**,极易误判成"目录没建" | 子进程 `exitCode 1` → 平台按崩溃退避重启 → 5 次后**熔断**(10 min 冷却) |
| **D · cwd 为空**(2026-09-15 新增) | 启动参数里 `folder` 为空/`null`(**跨机迁移**时最容易:复现不了原启动参数) | `bwrap: Can't chdir to : No such file or directory` —— ⚠️ **路径是空的**(一眼可辨) | 同上 |
**A/B 拿不到可读的应用层日志**(这正是用户觉得"莫名其妙就断了"的原因);**C/D 反而有明确日志** ⇒ 见到 `Can't chdir` 直接分诊:
- **路径是空的** ⇒ **D**:查实例记录 / 迁移入参里的 `folder` 是不是 NULL。
📌 集群模式下实例行是 `claimInstance` 建的(local 模式不写库),**它必须把 `folder`/`patch` 一并落库**,否则迁移时无处取得启动参数。
- **路径正常却报不存在** ⇒ **C**:看 `orchestrator.ts` 的 bwrap 参数里,**中间目录是不是"就近创建"的**(例如插在 `--bind root root` 之后)。
✅ 正确做法:**所有挂载点的中间目录统一前置 + 去重 + 由外到内**,禁止插在任何一个 `--bind` 之后。
- 最小复现(**别拿生产实例试**):`bwrap <与平台等价的参数> -- /usr/bin/ls -ld <那个路径>`,再逐条增删参数二分。
⚠️ 顺带记住:需要给挂载点权限时只能用 `--tmpfs`(自带 0755),**不能用 `--perms`** —— 47 上 bwrap 是 0.4.0,不认该选项。
## 隔离复现(**唯一允许的压测方式**)
⛔ **压测走隔离环境**:在**生产实例**上压满配额 = 当场 SIGKILL,会连带把实例带进 crash-loop / 熔断(自找干扰)。⚠️ 注意 **2026-09-13 用户已明确「服务器是开发环境,不用担心中断用户」**(R8 已放宽)—— 但**别因此就去污染实例**:隔离复现能拿到同样的数据而**不留副作用**,仍是首选;只有在需要复现"平台侧联动"(如自动回滚、熔断计数)时才动真实例。
```bash
systemd-run --wait --pipe --collect --unit=memsim-x \
-p MemoryHigh=448M \
-p MemoryMax=1024M \
node /tmp/memsim/probe.mjs
```
- 独立 cgroup ⇒ **撞墙只杀模拟进程**,生产不受任何影响(宿主 available 需 > 400 MiB)
- 跑完 `rm -rf /tmp/memsim`,`systemctl list-units "memsim*"` 确认无残留
**复现路径 B(137)的配方**:必须用**纯堆外分配**才能走到 cgroup 限——
```js
const b = Buffer.allocUnsafe(4 * 1024 * 1024); b.fill(1); bufs.push(b)
```
⚠️ 若改用 JS 对象数组,会**先撞 V8 堆限(路径 A)**,永远复现不出 137。这是本轮踩到的坑。
**复现路径 A 的配方**:正常 push JS 对象即可,同时打印 `process.memoryUsage()` 看崩在哪个 `heapUsed`。
## 踩坑清单(全部实测)
1. **cgroup v1 vs v2 路径不同** —— 写错不报错,静默读到空值。
2. **`memory.*_bytes` 单位是字节**,不是 KB(差 1024 倍)。
3. **`dsh-instance-mem.log` 时间戳是 UTC**,+8 才对得上本地时间。
4. **scope 名带随机 hash**,每次重启都变,必须现查。
5. **`ps` RSS ≠ cgroup usage**(共享页不计入 cgroup)。
6. **会话目录多一层 workspace 转义目录**,别按 `home/sessions/<id>` 找。
7. **`systemd-run` 起服务不继承调用者 env** ⇒ 压测必须 `--setenv=NODE_OPTIONS=...`,否则堆限根本没生效。**自证手段**:脚本里打印 `require('node:v8').getHeapStatistics().heap_size_limit`。
8. **同质字符串会被引擎优化**:`'x'.repeat(N) + i` 走 cons string 惰性拼接(不复制前缀)→ 实测「投喂 23 GB 只涨 140 MB」的假数据。**内存类模拟实验极易骗人,优先用真实数据统计 + smaps 实测**。
9. **`.cjs` 不支持顶层 await** ⇒ 用 `await import()` 的探针脚本要存成 `.mjs`。
## 定量归因的经验值(2026-09-12 guest 实例实测)
| 组成 | 量 | 说明 |
|---|---|---|
| V8 老生代堆 | ≤`--max-old-space-size` | **这是「许可」不是「上限」**,V8 会主动把水位推满以减少 GC |
| RSS / heapUsed 膨胀 | **约 3.4×** | 实测 heapUsed 37 MiB 时 RSS 已 125 MiB(预留未用 + 回收未归还给 OS) |
| `dsh-univer-office` | **+65 MiB** | 磁盘 168 MB,含 29 MB + 10 MB 原生绑定 |
| 页缓存(node_modules mmap) | ~18 MiB | **唯一可回收的部分** |
| V8 不管但计入配额 | ~50 MiB | Buffer / 原生库 / 模块映射 |
### 内存去向实测(2026-09-12 · 空载实例 smaps 拆解)
**空载实例(零会话)已占 285 MiB**:`V8 堆/匿名 155.9` + `[heap] 58.1` + `文件映射 69.1`(其中 **node 本体 59.8**)+ `JIT 2.2`。
⚠️ **`[heap]`(malloc 区)不受 `--max-old-space-size` 约束** ⇒ 压堆限**不会**让总量线性下降。
### 会话数据几乎不占内存(重要反直觉)
实测:**整个会话(2546 事件 / 11 轮)的全部字符串只有 0.9 MB**(最大单串 40820 字符 ⇒ 工具有截断)。
⇒ **「减会话长度 / 少用 web_fetch」对内存几乎无用**;占用主体是**代码与插件加载**。
### 插件内存成本(逐个 `import` 实测)
| 插件 | rss 增量 | 备注 |
|---|---|---|
| `dsh-univer-office` | **+65.1 MiB** | 单文件 bundle 5400 行 / 6 MB,**顶层静态 import** 拉起重型依赖(连 `@puppeteer/browsers` 都在),全文件仅 2 处动态 import |
| `libsql` | +7.8 MiB | 原生绑定 |
| 平台自研 ×3(portal-entry / workspace-scoped-picker / business-plugins) | **0.0 MiB** | 自研插件写法是轻的 |
| `puppeteer-core` | **0.0 MiB** | 只 `import` 主入口、不启动浏览器 ⇒ **懒加载确实有效** |
⇒ **成本由「装了什么」决定,不是「装了几个」** —— 一个重型插件 ≈ 无穷多个轻量插件。
⇒ 治理优先级:**卸重型插件 > 让插件懒加载 > 折腾堆参数**。
⚠️ 插件的 `pnpm remove`(禁用)**必须重启实例**才真释放 —— Node 模块缓存不卸载。
**诊断结论的落地口径**:算「V8 堆上限 + 插件 + 堆外 + 缓存」总和是否 ≥ `MemoryMax`。若 ≥,就是**配额本身零余量**(配置问题,不是 bug)——此时可调项只有:① 压 `--max-old-space-size`(代价:更易撞路径 A)② 提高配额(受宿主物理内存限制)③ 减少单会话负载。
## 已知与本平台不兼容的插件
### `dsh-univer-office`(2026-09-12 定性:**架构级不兼容,配置不可修**)
两条**独立**的阻碍,缺一都打不开:
| 阻碍 | 机理 | 证据 |
|---|---|---|
| **Host → Gateway** | 插件启动 bundled Gateway(默认 `127.0.0.1:9080`),实例内 node 要连它做健康检查;而平台 nft `dsh_egress` 对 `skuid 100000-199999 → 127.0.0.0/8` 是 `reject with tcp reset` ⇒ 永远连不上 | `univer_new` 恒报 `bundled Gateway did not become ready within 10000ms`;实例内 `curl 127.0.0.1:908x` → `000` / Connection refused |
| **Browser → Viewer** | **源码硬编码** `gateway = http://127.0.0.1:${port}`、`viewerUrl = ${gateway}/?file=...`,client 拿它当 **iframe src** ⇒ 浏览器去**用户自己电脑**的 127.0.0.1 找 Viewer,那里没有服务 | `grep -o "viewerUrl: [^,]*" lib/index.js`;`grep -oE "http://127\.0\.0\.1:[^\`\"']*"` |
⇒ 它的 Viewer **假设「浏览器与实例同机」(本地部署场景)**,与托管多租户平台根本不兼容。**解封 loopback 也没用**——第二条拦在浏览器侧。
⇒ 只能改插件(viewerUrl 改走平台代理的相对路径)。**治理结论:候选池应下架 / 用户应禁用**(顺带省 **65 MiB**)。
**替代路径**:让 agent 用 python(openpyxl + python-docx + matplotlib)直接产出 xlsx/docx,已验证可行(2026-09-12)。
⇒ **评估标准已立档**:`04-调整方案/75`(**托管友好性 H1–H4** + **资源成本**;自研插件改造规范 **R-a~R-e**)。今后候选池导入与自研插件验收按该档的检查项走 —— 核心判据一句话:**「插件的一切对外交互,是否都能走平台已有的那一条入口」**(浏览器侧只用相对路径;实例侧不依赖 loopback 网络服务)。
## 注入层 / 浮层的**真机验证配方**(2026-09-13 实测,4 轮才摸清)
**为什么不能用 curl 验**:`src/supervisor/proxy.ts` 的注释写得很明白 —— **「curl 不带 Accept-Encoding,故此前验证是假阳性」**。注入只在「客户端接受 HTML → 代理把上游 `accept-encoding` 改写成 `identity` → 上游回未压缩 HTML」时发生;curl 一不留神就绕过这个判定,于是**"有标记"不代表脚本能在浏览器里跑**,而**"没标记"也可能是 curl 自己造成的**。⇒ **判定注入层是否活着,必须用真浏览器。**
**前置(R4 允许的临时会话,别用真实账号)**:
```bash
# 服务器上:建一个 10 分钟自过期的 guest 会话(ip=127.0.0.1 / ua=poc-curl2)
TOKEN=$(ssh bt-server 'cd /opt/dshs && /usr/local/bin/node mksess-guest.cjs')
# 用完立刻删(否则留下真实可用的会话行)
ssh bt-server "cd /opt/dshs && /usr/local/bin/node -e \"const c=require('crypto');const D=require('/opt/dshs/node_modules/better-sqlite3');const db=new D('/var/lib/dshs/dshs.db');console.log(db.prepare('DELETE FROM sessions WHERE token_hash=?').run(c.createHash('sha256').update('$TOKEN').digest('hex')).changes);db.close();\""
```
**工具**:`playwright-core` + `channel:'chrome'`(装在 `E:\ProgramData\.workbuddy\binaries\node\workspace`;须 `createRequire` 指向该目录,并在该目录内跑)。把 `sid` cookie 写进 context(`domain:'.alotbuy.com'`, `secure:true`, `sameSite:'None'`),再 `goto https://<user>.alotbuy.com/`。
**判定用的哨兵与元素(直接 `page.evaluate` 读)**:
| 判据 | 说明 |
|---|---|
| `window.__dshRecover === 1` | **recovery.js 全文执行完毕的哨兵**(脚本第 23 行设)—— **最强证据**,比找 DOM 元素可靠 |
| `window.fetch.toString()` 不含 `[native code]` | 证明监控层已装载(脚本包装了 `fetch` / `EventSource` / `WebSocket`) |
| `#__dshAssistBar` 存在 | assist.js 跑起来了(它 `mount()` 时建这个容器) |
| `#__dshRecover` + 文案 + `#__dshRetryBtn` | 浮层级 |
| `document.scripts` 里含 `__dsh` 的 `<script>` 数 = 2 | 两个注入脚本都下发了 |
**4 个坑(每个都让我误判过一次)**:
1. **`page.route` 拦不住 WebSocket** ⇒ 想用「拦 `/api/*`」或 `ctx.setOffline(true)` 造断流**逼不出浮层**(dsh 的主链路是 WS/SSE)。`setOffline` 也不会立即掐断已建立的 WS。
2. **`probe()` 有 15 秒节流**(`lastProbe`),而心跳每 25 秒跑一次 ⇒ **心跳会把节流窗口占掉**,你手动 `dispatchEvent(new Event('focus'))` 往往被**静默丢弃**。要么按心跳节奏等(`status` 恒 `running:false` → 连续两次软失败 ≈ 50 s),要么确保距上次探针 ≥15 s。
3. **`recover()` 成功后 0.7 秒内 `location.replace()` / `location.reload()`** ⇒ 浮层只亮 0.7 s。**任何 ≥2 秒的轮询都会全踩空**,然后你会得出"浮层没出现"的**错误结论**。
4. **`#__dshAssistBar` 本身是容器**,里面才是「📁 我的文件」「🧭 能力」两个 button —— **点容器无效**,要用 `page.click('#__dshAssistBar button:nth-child(1)')`。
**✅ 正解(纯客户端、零生产副作用)**:拦 `status` 仿真未运行 + **把 `/api/dsh/enter` 挂住不回**,浮层就会**停住不下跳**,从容观测:
```js
await page.route('**/api/dsh/status*', r => r.fulfill({ status:200, contentType:'application/json', body:'{"running":false}' }))
await page.route('**/api/dsh/enter*', () => { /* 故意永不回包 → 浮层停住 */ })
// 之后每 2 秒采样 #__dshRecover 的可见性与文案即可
```
2026-09-13 用这套验证拿到的实测结果:文案「工作区已休眠,正在唤醒…(**已等待 N 秒**)」**倒计时逐秒递增**、`DSH · AUTO RECOVERY`、AI 核心视觉 + 进度条;助手面板点开后正确列出工作区目录。**全程零页面错误。**
## 相关
- 排查完整案例:`04-调整方案/55`(会话档位)、`04-调整方案/16`(崩溃循环)
- 实例配额与内存优化:`04-调整方案/58`
- 会话档位是「会话创建时播种」的,老会话不跟随平台默认:`04-调整方案/33`