背景:Discord 大面积无响应

某天早晨,多个 Discord 频道突然停止响应。日志显示 DiscordMessageListener 处理时间严重超标:

[[[[[[000000222223::::::112451167729]]]]]]39512143..3285..68sseesscceeooccnnooddnnssddss9health-monitor

一个上午触发了 5 次以上 gateway 重启,用户体验完全崩溃。

根因诊断

第一层:sessions.json 文件异常膨胀

ssDeeisssssccTiiroeooorlnnndesg.essrjneeastssmorss/niiiFeooesnnisssh343u821.5760402MB85%

第二层:skillsSnapshot 是罪魁祸首

OpenClaw 在每个 session entry 里存储了一份完整的 skillsSnapshot,包含所有 79 个 workspace skills 的系统提示文本。

skill3se5Sn.nt4ar2pyM.sB8hoMt4B95/9~275%50K0B/entry

为什么要存 skillsSnapshot? 设计上服务三个功能:

  1. 技能变更检测:文件 watcher 监控 SKILL.md 变化,session 检测到版本不匹配时重新构建系统提示
  2. Env 变量覆盖恢复:某些 skill 配置了 API key 等环境变量,tool call 时从 snapshot 恢复
  3. 系统提示缓存:避免每次 turn 重新读取 79 个 SKILL.md 文件

设计缺陷:prompt 全文(~70KB)应该全局共享一份 + 各 session 存 hash 引用,而不是每个 session 存一份完整拷贝。500 entries × 75KB vs 1 × 75KB + 500 × 64B,相差约 37 倍。

第三层:同步 I/O 阻塞 event loop

源码中 loadSessionStore() 在 turn 处理的关键路径使用同步读取:

1
2
3
4
// sessions-D-LKdTsU.js
const raw = fs.readFileSync(storePath, "utf-8");  // 同步读 38MB
const parsed = JSON.parse(raw);                     // CPU 密集解析
return structuredClone(store);                       // 深拷贝 38MB 对象

每条 Discord 消息都触发这个流程。加上 OpenClaw 的 turn 队列是串行的,一个慢 turn 会阻塞所有频道。

第四层:cron session 无限积累

40+ 个 cron job 每次运行都创建新的 isolated session entry,每个携带 75KB 的 skillsSnapshot。427 个 cron entries 贡献了约 31MB。

解决方案

立即修复:清理脚本

编写 cleanup-sessions.py,逻辑与 OpenClaw 原生 maintenance 行为对齐:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 1. 剥离 cron session 的 skillsSnapshot(省空间,不影响功能)
if ":cron:" in key and "skillsSnapshot" in entry:
    del entry["skillsSnapshot"]

# 2. 删除超过 3 天不活跃的 session entry
if age_days > STALE_DAYS:
    # 归档 .jsonl 文件(rename → .deleted.TIMESTAMP)
    archive_session_file(session_file, reason="deleted")
    del data[key]

# 3. 保护主 session 永不删除
# agent:main:main → skip

关键细节:

  • 删除 entry 时同步归档对应的 .jsonl transcript 文件
  • 归档方式:rename → xxx.jsonl.deleted.2026-03-04T…Z(与 OpenClaw 行为一致)
  • OpenClaw 自身会在 30 天后清理 .deleted. 文件

优化效果

指标之前之后
sessions.json39.7 MB2.8 MB(↓93%)
entries 总数511447
归档 Discord thread13 个占着已清除
过期 session51 个已清除

长期维护:每日 Cron

JSMocobhd::eedlsu:elseccs:lliaeouandnseu-3-pc:h-l4ase5ieaksnAusuM-ip4oC-nS5sT.py-apply-days3

Discord Thread 操作与 Session 的关系

操作Discord 历史sessions.json.jsonl 文件
/new✅ 保留✅ 新 sessionId旧 → .reset. 归档
Close thread✅ 保留❌ 不动❌ 不动
Lock thread✅ 保留❌ 不动❌ 不动
Delete thread❌ 删除❌ 不动❌ 不动

/new 是唯一真正重置 AI session 的方式。 Discord 的 close/lock/delete 只影响 Discord 侧,不碰 OpenClaw 的 session 状态。清理已关闭 thread 的正确姿势:先 /new,然后 Lock thread。

关联 Issues

  • #15145(open):skillsSnapshot per-session 存储导致 sessions.json 膨胀 — 已提交详细诊断数据
  • #11950(open):提议 per-session 文件替代单一 sessions.json
  • #12289(closed):cron session 无限积累
  • #9238:Discord 消息队列卡死

后续计划

  • 持续关注 #15145 是否有官方修复
  • 监控 sessions.json 大小,必要时降低 --days 阈值
  • 考虑提 PR:cron :run: entry 不写入 skillsSnapshot(一行代码修复根因)

脚本存档路径:~/.openclaw/workspace/memory/reference/scripts/cleanup-sessions.py