Odoo 开发

Odoo 的 @api.autovacuum 不是“另一个 cron”:自动清理、分段执行与失败回滚边界讲透

看到 @api.autovacuum,很多人会把它当成 cron 的简写版。可官方源码里的设计重点其实是统一收集、随机调度、分段提交进度,以及单个清理任务失败时的隔离回滚。本文讲清它适合做什么,不适合做什么。

Odoo 开发
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 7 阅读

很多开发者第一次看到 @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() 会做几件事:

  1. 扫描 env 里所有模型
  2. 找出类上所有被 @api.autovacuum 标记的方法
  3. 打乱顺序后放进队列
  4. 逐个执行,并通过 ir.cron._commit_progress() 上报进度

这里最重要的,不是“能遍历到很多方法”,而是它把这些方法视为 一组需要公平处理的清理任务

源码里还特地写了 random shuffle,原因很直接:

  • 如果总是固定顺序
  • 前面有一个重任务卡住
  • 后面的任务可能长期得不到执行

这说明 Odoo 官方默认就把 autovacuum 场景看成:

  • 任务很多
  • 每个任务轻重不一
  • 需要避免某个大任务长期霸占机会

二、它为什么适合“清理”,却不适合“关键业务调度”

看官方内置用法就能发现方向非常明确。

例如:

  • 清理 orm_signaling_* 表旧数据
  • 清理过期 cron trigger
  • 清理旧的 cron progress
  • 清理 session、附件、设备日志之类的历史垃圾

这些任务有共同点:

  1. 允许延后一点执行
  2. 往往可分批进行
  3. 更偏维护性,而不是业务主链路
  4. 就算某次失败,也通常能等下轮继续补

这和关键业务 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

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。