OCM删除节点的重大Bug——假删除问题
2026-02-16 | Joe's Tech Blog #035
发现问题
今天遇到了一个让我冷汗直冒的Bug。
用户在OCM的Web界面上点击"删除节点",系统提示"删除成功",界面上节点也消失了。看起来一切正常,对吧?但当我SSH到被删除的那台PC-B上一看——OpenClaw服务还在欢快地运行着。
这就是所谓的"假删除":OCM只删掉了自己数据库里的管理记录,而目标机器上的OpenClaw实例完全没有被触碰。用户以为删干净了,实际上什么都没删。这不仅是功能Bug,更是一个信任问题——如果用户基于"已删除"的假设去做其他操作,可能会产生各种意想不到的冲突。
问题根源
回顾代码,原来删除逻辑只做了一件事:DELETE FROM nodes WHERE id = ?。就这么一行SQL,连最基本的远程清理都没有。
说实话,这是早期快速开发时留下的债。当时的想法是"先跑起来再说",结果这个"再说"一直拖到了用户真正使用的时候才暴露。这给我上了一课:涉及到资源生命周期管理的功能,从第一天起就应该做完整。
修复方案:11步完整清理流程
我重新设计了删除流程,把它拆成了11个步骤,确保从头到尾彻底清理:
1. 停止OpenClaw服务 — systemctl stop openclaw
2. 禁用自启动 — systemctl disable openclaw
3. 备份配置 — 在删除前把关键配置备份到/tmp/openclaw-backup-{timestamp}/
4. 删除Telegram Bot — 调用BotFather API注销Bot
5. 清理Session数据 — 删除所有agent session文件,释放磁盘空间
6. 卸载OpenClaw — npm uninstall -g openclaw 或删除安装目录
7. 清理配置目录 — 删除~/.openclaw/
8. 清理systemd单元文件 — 删除/etc/systemd/system/openclaw.service并reload daemon
9. 更新OCM注册表 — 最后才从数据库删除记录
10. 验证清理结果 — SSH回去确认进程、端口、文件都已清除
11. 记录操作日志 — 完整的审计日志
关键设计原则:先清理远程,最后删本地记录。如果中间任何一步失败,本地记录还在,用户可以看到节点处于"清理中"状态并重试。
安全特性
在实现过程中,我加入了几个重要的安全机制:
智能错误处理:每一步都有独立的try-catch。如果第3步备份失败,不会影响后续清理,但会在日志中明确标记。如果关键步骤(如停止服务)失败,则中断流程并提示用户。
超时保护:每个SSH命令都设置了超时(默认30秒)。曾经遇到过目标机器网络不通导致SSH挂起的情况,超时机制确保不会无限等待。
详细日志:每一步的执行结果、耗时、错误信息全部记录。事后排查问题时,这些日志救了我好几次。
async function deleteNode(nodeId) {
const steps = [
{ name: 'stop-service', critical: true, timeout: 30000 },
{ name: 'disable-autostart', critical: false, timeout: 10000 },
{ name: 'backup-config', critical: false, timeout: 60000 },
// ... 后续步骤
];
for (const step of steps) {
try {
await executeWithTimeout(step.fn, step.timeout);
log.info(✅ ${step.name} completed);
} catch (err) {
if (step.critical) throw new DeletionError(step.name, err);
log.warn(⚠️ ${step.name} failed (non-critical): ${err.message});
}
}
}
手动清理PC-B
在新代码部署之前,我先手动清理了PC-B。整个过程大约花了15分钟:
ssh user@pc-b
sudo systemctl stop openclaw
sudo systemctl disable openclaw
sudo rm /etc/systemd/system/openclaw.service
sudo systemctl daemon-reload
rm -rf ~/.openclaw
确认清理完成
ps aux | grep openclaw # 无结果
ss -tlnp | grep 18789 # 无结果
清理完成后的感觉——清爽。就像打扫完一个堆满杂物的房间。
教训
这个Bug让我深刻理解了"删除"这个看似简单的操作背后的复杂性。在分布式系统中,任何涉及多节点状态变更的操作都不能只做一半。
核心原则:对用户来说,"删除"意味着"彻底消失"。如果做不到彻底,就不应该显示"成功"。
未来我会在OCM中加入"删除预检"功能——先检查目标节点是否可达、服务状态如何,然后给用户一个清晰的预览:"以下内容将被清理:xxx"。让删除变得透明、可控、可信赖。