很多开发者第一次看到 @api.autovacuum,会把它理解成:
哦,就是一个不用手动建
ir.cron的定时任务。
这理解只对了一半。
从官方源码看,@api.autovacuum 的重点从来不只是“定时执行”,而是把各种 数据库清理类工作 收拢到统一清洁器里,并提供:
- 自动收集所有打了装饰器的方法
- 随机化顺序,避免长期饥饿
- 通过
_commit_progress()做分段推进 - 某个方法失败时只回滚该方法,不拖垮整轮清理
所以它更像“系统保洁总线”,不是“你随手塞业务逻辑的 cron 替代品”。
一、源码怎么跑起来的
在 /home/ubuntu/odoo-temp/odoo/addons/base/models/ir_autovacuum.py 里,ir.autovacuum._run_vacuum_cleaner() 会做几件事:
- 扫描 env 里所有模型
- 找出类上所有被
@api.autovacuum标记的方法 - 打乱顺序后放进队列
- 逐个执行,并通过
ir.cron._commit_progress()上报进度
这里最重要的,不是“能遍历到很多方法”,而是它把这些方法视为 一组需要公平处理的清理任务。
源码里还特地写了 random shuffle,原因很直接:
- 如果总是固定顺序
- 前面有一个重任务卡住
- 后面的任务可能长期得不到执行
这说明 Odoo 官方默认就把 autovacuum 场景看成:
- 任务很多
- 每个任务轻重不一
- 需要避免某个大任务长期霸占机会
二、它为什么适合“清理”,却不适合“关键业务调度”
看官方内置用法就能发现方向非常明确。
例如:
- 清理
orm_signaling_*表旧数据 - 清理过期 cron trigger
- 清理旧的 cron progress
- 清理 session、附件、设备日志之类的历史垃圾
这些任务有共同点:
- 允许延后一点执行
- 往往可分批进行
- 更偏维护性,而不是业务主链路
- 就算某次失败,也通常能等下轮继续补
这和关键业务 cron 很不一样。比如“每天 9 点必须开票”“每 5 分钟必须拉单”,这种对时序和确定性要求高的任务,不该用 autovacuum 思路去理解。
三、返回 (done, remaining) 不是花哨写法,而是分段协作协议
在 ir.cron 相关模型里,官方 autovacuum 方法常返回:
return len(records), len(records) == GC_UNLINK_LIMIT
也就是 (func_done, func_remaining)。
在 ir.autovacuum._run_vacuum_cleaner() 里,如果方法返回的是一个二元组,系统会把它理解为:
- 这次清了多少
- 后面还有没有剩余
如果 remaining 为真,就会把该任务重新放回队列前端,继续后续轮次。
这个设计非常实用,因为数据库清理经常不是“一把梭”能安全完成的。比如:
- 历史表太大
- 一次删太多锁太久
- 一次跑太久会拖慢其他任务
所以更稳的方式是:
- 每次只清一小批
- 提交进度
- 如果还有剩余,下轮继续
这就是 autovacuum 的一个核心边界:它鼓励分段、可恢复、可续跑的清理逻辑。
四、失败回滚为什么很关键
源码里每个 autovacuum 方法都包在 try/except 里。某个方法报错时,会:
- 记录异常日志
- 执行
self.env.cr.rollback() - 然后继续后面的任务
这对系统维护任务非常重要。
因为清理任务的现实世界很脏:
- 有脏数据
- 有历史兼容问题
- 有锁冲突
- 有外部模块的扩展 bug
如果一个清理方法出错就把整轮保洁都带停,系统只会越积越脏。官方这里做的是 单任务失败隔离,让其他清理工作还能继续推进。
这也反过来提醒开发者:
- autovacuum 方法最好幂等
- 最好小步执行
- 最好即使中途失败,下次再跑也能接上
五、它和普通 cron 最容易混淆的地方
相同点
- 都是后台自动执行
- 都可能做维护工作
- 都不需要用户手动点击
不同点
- cron 更像“明确的一条调度任务”
- autovacuum 更像“被统一清洁器轮询的一组清理方法”
换句话说:
- cron 是“这件事几点跑”
- autovacuum 是“系统有空清理这些卫生问题”
如果你要的是业务时点确定性,选 cron;如果你要的是框架级保洁钩子,才考虑 autovacuum。
六、实战里最适合放进 autovacuum 的事
比较适合的包括:
- 删除过期临时记录
- 回收旧日志、旧 token、旧缓存映射
- 清理已失效的中间表数据
- 分批修整某些可延后的维护性脏数据
不太适合的包括:
- 关键业务动作
- 需要固定触发时间的任务
- 需要严格顺序的流程编排
- 用户能明显感知时效的任务
七、新手最容易误解的 3 个点
1)“有装饰器就一定会稳定按时跑”
不应该这么理解。它更偏系统维护节奏,不是 SLA 级业务调度。
2)“适合一次处理所有历史数据”
大表清理最忌一次跑到底。源码已经在鼓励你返回 (done, remaining),这就是在提醒你分批。
3)“失败会自动帮我处理一致性”
它只会帮你回滚当前失败方法,不会替你设计业务补偿。可恢复性仍然要你自己负责。
结语
@api.autovacuum 的真正价值,不是“少建一个 cron”,而是把 系统保洁类任务 纳入一个统一、可分段、可隔离失败的执行框架。
如果你把它当业务调度器来用,往往会用偏;如果你把它当维护清理钩子来设计,就会发现官方这套队列、进度和回滚逻辑其实非常合理。
DISCUSSION
评论区