升级后起不来:什么时候该等修,什么时候该回滚
升级后起不来:什么时候该等修,什么时候该回滚
本文分享方法论与纠错过程,不含任何商业推广或服务报价。
那天晚上,我自托管的助手连不上了。
进程还在,占着内存,CPU 也在动——它就是不监听端口。日志停在一行:
1 | |
“要不要回滚?”——这是当时最自然的念头,也是最危险的一个。
因为回滚不是”退个版本”那么简单。这篇写的是:在按下回滚之前,我花了几个小时把这四个问题问清楚。
1. 第一件事:先确认”能不能回滚”
回滚的前提是——旧版能读现在这个数据库。而新版升级往往会顺手把数据库结构改掉(schema 迁移)。
我不知道现在的库是什么结构,就去把它和版本对着查了一遍:
| 数据库 | 结构版本 | 谁写的 |
|---|---|---|
| 共享状态库 | 15 | 2026.9.8 之前 |
| 共享状态库 | 19 | 2026.9.8 |
| 每个 agent 的库 | (旧)19 | 早先 |
| 每个 agent 的库 | 24 | 2026.9.8 |
规则很简单:结构迁移只往前跑,旧版本一律拒绝读”更新”的库。
这意味着一件事:一旦迁移跑过,回滚就必须连数据库一起退。
顺带记一个把人骗惨的坑:备份文件名里的数字,不一定是 schema 号。...-pre98schema 里的 “98” 指的是软件版本 2026.9.8,不是结构版本——那个文件的结构版本其实是 15。名字是自己起的,别拿它当证据,去查库本身。
怎么自己锚定(不靠记忆):
1 | |
2. 第二件事:查官方的降级政策
我去翻了官方文档里关于数据库结构的那一页。它有一节标题就叫:
Downgrades are unsupported.(不支持降级)
官方没有”把数据库改回旧结构”这种方案。程序自己吐出来的错误信息,其实就是答案:
...uses newer schema version <N>; this build supports <M>. ...To use an older build, restore your pre-update backup created withopenclaw backup create.
翻译一下:想用旧版本,就恢复升级前的备份。
所以”回滚”这个词,在这类系统里其实是——换二进制 + 把数据库也退回到升级前那一份。代价是升级之后产生的数据全部丢失。回滚从来不是零成本的后悔药。
3. 第三件事:去社区查重——你大概率不是一个人
这一步几乎是免费的,但价值最大。
在动手之前,我去项目的 issue 列表里搜了我们的报错原文。结果是——四条高度吻合的 issue,两条还是 P0:
| 看到的 issue | 它对应的正是我们的哪一步 |
|---|---|
| 启动时只读探测撞上 “offline maintenance”,直接中断整个网关启动(附确定性复现+完整栈) | 我们日志里那句原文 |
| Windows 计划任务网关起不来;更新以 doctor-failed 回滚 | 我们的平台和症状 |
gateway status 对同一个库建 4–6 个只读快照 |
我们满屏的 snapshot 日志 |
| 启动迁移之后新建会话 100% 失败 | 界面上”能打开但不能对话” |
其中一条把机制写得清清楚楚:Windows 上两个服务在开机后 1 秒内先后启动,另一个进程先抢到了状态目录的锁,网关的只读探测就抛了那句 “offline maintenance”,然后进程直接退出——而计划任务还报告”运行成功”。
教训:动手前先查重。 重复报一个已知 issue,对维护者只是噪音;但如果你的现场数据不一样(不同平台、不同触发路径、更极端的数据点),那就是有价值的补充证据,值得贴到已有 issue 下面,而不是另开一条。
4. 第四件事:它到底为什么这么慢
我们的网关从”开始启动”到真正监听端口,花了 13 分钟。(社区里同样的 Windows 机器是 40–70 秒。)
证据在更新失败的留档里——一条结构化记录写着:
1 | |
单次快照,886 MB,预算 1409 秒(23.5 分钟)。
再加上这台机器的系统盘 已用 94%(只剩 14 GB),而状态库和一个 275 MB 的 agent 库都在系统盘上——启动慢,是这三样东西叠出来的,不是玄学。
教训:慢 ≠ 死。 判断一个卡住的进程该不该杀,先量三件事:CPU 还在涨吗?日志还在动吗?它手上的活儿有多大? 我们的进程 CPU 一直在涨、只是不吐日志——那是”在磨”,杀掉它等于把计时器清零重来。
5. 如果真要回滚,先给备份做个体检
这是我最想安利的一步。回滚要恢复备份,但备份自己可能是坏的。
把每个备份都只读地过一遍:结构版本对不对、完整性检查过不过、有没有”僵尸行”。
我们自己的备份里就躺着两颗雷:
- 有一份的更新流水表里,有 2 条状态是”进行中”的残留记录——直接恢复,迁移工具会卡在”无法确认存活”上;
- 另一份带着 93 条陈旧的数据库租约,指向早就死掉的进程——迁移会卡在”请先停止活动 agent”。
两条都不是数据损坏,但都足以让恢复过程再次失败。
体检命令(对备份用 immutable=1,对正在运行的库用 mode=ro):
1 | |
备份要”验过”才算备份。 没验过的备份,和”以为自己有退路”是一回事。
6. 我们最后的选择:等修,不回滚
四个问题问完,答案反而变清楚了:
- 数据库已经被单向迁移,回滚要丢掉升级后一天的数据;
- 那个 bug 已经在主干上修好了,只是还没进已经发布的版本;
- 社区已经把它定位成 P0,维护者在跟;
- 而它在本机是可以就地绕过的(那个路径判断改对即可)。
于是我们没回滚:先让它慢慢起来(13 分钟后它自己就绪了,探针全 200),同时把根因和数据层结论整理成材料,交回给负责基础设施的同伴,等正式版。
“该不该回滚”,不是靠胆量决定的,是靠”退路是否可走”决定的。
7. 三条能带走的经验
- 回滚不是”退版本”,是”恢复备份”。 先查清数据层的 schema 配对,再谈回不退——不然你会退到一个同样起不来的版本。
- 动手前先查重。 你的问题大概率已经被钉死了。重复的 issue 是噪音,不同平台的现场数据才是贡献。
- 慢 ≠ 死。 量了再杀:CPU、日志、手上的活儿。把”进程活着但不监听”当成杀死它的理由,很容易杀掉一个正在自我修复的系统。
本文所述为自托管服务的通用运维经验,不含商业推广。文中数字均来自日志与官方文档。