自动更新不是没跑,是没跑到点子上
上个月给我的 CPA(CLIProxyAPI,一个自托管的 Claude 代理服务)配了自动更新:每天凌晨 4 点,脚本自动拉上游代码、编译、重启服务。当时觉得这个时间很聪明——半夜,没人用,重启窗口拉长也不打扰。
结果今天早上,更新照常跑,升到了 v7.2.130。然后下午 2 点多,上游又发了 v7.2.131。
就差几个小时,还是得手动推一次。
问题不是自动更新失效了,是定时和上游的发布时段根本不在一个频道上。我决定先把"上游到底什么时候发版"统计清楚,再重新定闹钟。方法也简单:gh 拉最近 60 个 release 的发布时间、最近 80 个 commit 的推送时间,统一换算成北京时间,按小时做频数统计。
按小时频数:两张表看出了作息
表 1:按小时分布(近 60 个 release + 近 80 个 commit,北京时间)
| 小时 | Release 发布数 | 占比 | Commit 推送数 | 占比 |
|---|---|---|---|---|
| 00 | 6 | 10% | 6 | 7.5% |
| 01 | 2 | 3% | 6 | 7.5% |
| 02 | 5 | 8% | 1 | 1% |
| 03 | 5 | 8% | 2 | 2.5% |
| 04 | 2 | 3% | 4 | 5% |
| 05 | 5 | 8% | 5 | 6% |
| 06 | 3 | 5% | 5 | 6% |
| 07 | 3 | 5% | 0 | 0% |
| 08-11 | 0 | 0% | 9 | 11% |
| 12 | 1 | 2% | 0 | 0% |
| 13 | 2 | 3% | 2 | 2.5% |
| 14 | 3 | 5% | 4 | 5% |
| 15 | 1 | 2% | 6 | 7.5% |
| 16 | 6 | 10% | 3 | 4% |
| 17 | 1 | 2% | 3 | 4% |
| 18 | 2 | 3% | 6 | 7.5% |
| 19 | 1 | 2% | 6 | 7.5% |
| 20 | 0 | 0% | 3 | 4% |
| 21 | 2 | 3% | 3 | 4% |
| 22 | 6 | 10% | 4 | 5% |
| 23 | 4 | 7% | 2 | 2.5% |
表 2:窗口聚合——规律一下就出来了
| 窗口 | Release 数 | 占比 | Commit 数 | 占比 |
|---|---|---|---|---|
| 深夜 22-02 | 23 | 38% | 19 | 24% |
| 凌晨 03-07 | 18 | 30% | 16 | 20% |
| 上午-中午 08-12 | 1 | 2% | 9 | 11% |
| 下午 13-19 | 16 | 27% | 30 | 38% |
| 晚 20-21 | 2 | 3% | 6 | 7% |
三个发现:
第一,上游有两个突发窗,上午基本是空的。 下午 13-19 点 commit 最密集(38%),深夜 22 点到凌晨 2 点 commit + release 双高峰。上午 8-12 点几乎零活动。这是很典型的"下午 + 深夜双班"开发节奏。
第二,release 比 commit 晚 2-4 小时。 深夜写下的 commit,要等凌晨 03-07 点才被贴上 release 标签。所以凌晨 4 点定时其实能接住深夜那波——真正漏掉的是下午那波:13 点发的 commit 要躺到第二天凌晨 4 点才被吃到,最长拖 14 小时,这就是"老慢半拍"的根源。
第三,版本几乎天天有,但全是突发。 平均一天 1.7 个版本,峰值日一天 6 个,偶尔有空窗日。某天下午一口气连发 7 个 commit 是常态,不是例外。
为什么"每两小时跑一次"是错误答案
更新不及时,直觉是缩短间隔,比如每 2 小时检查一次。这是错的。
因为上游是突发式推送:同一次突发里 commit 一个接一个。而跳过守卫只判断"有没有新 commit",不区分"突发进行中"和"突发已结束"。每 2 小时跑一次,会在同一次突发里被触发三四次,每次都是一次完整的重建——重启 CPA、重建管理面板容器。下午正干活,被反复打断三次,谁受得了。
高频轮询适合匀速型作者;对突发型作者,它把一次突发拆成了多次重建。
正确定法:卡窗口收尾,每窗一次
两个窗口,一天两次,各卡在窗口尾巴上:
- 21:00 收下午窗(13-20:30 那波):一次重建把整轮突发吃完,延迟大多 1-4 小时,还赶在个人夜间主峰开始前
- 04:00 收深夜窗(22-02:30 那波):顺便躲开凌晨 3 点档其他定时任务的拥挤段
没新版本时,一次检查只要 310 毫秒(git fetch + 比对 commit 一致,直接跳过,零重建)。改完定时我立刻手动触发了一次验证,脚本输出 Already up to date... Skip build+recreate,CPU 310 毫秒,没有任何重启。一天两次的检查成本几乎为零,真来了版本才重建。
顺手的教训:自动更新会悄悄吃磁盘
翻倍频率之前,习惯性看了一眼备份:更新脚本只做备份、从不清理,22 个 backup-before-update-* 镜像堆在 Docker 里,最早的还是 7 月底的。翻倍次数之后只会长得更快。
查了下,CLIProxyAPI 每次更新前会把旧镜像打成 backup-before-update-<时间戳>,但没有对应的清理逻辑。我给这套自动更新补了一条每天凌晨 3:50 的清理定时,只保留最近 3 个备份。每加一次更新频率,先确认它产生的垃圾有没有自动化清理,不然翻倍的是磁盘占用。
通用经验
“自动更新不及时"的正解不是缩短间隔,是先把上游的发布时段摸清楚。突发型作者(一天两次突发、一次连发六七个 commit)适合"窗口收尾"式定时;只有匀速型作者才适合固定整点或高频轮询。
定闹钟之前,先看对方的作息。
