自定义计划动作最容易在生产制造「跑两次就多一张发票/多一笔库存」。Odoo 19 里计划动作落在 ir.cron(触发还可看 ir.cron.trigger):写代码前先问——同一业务键执行 N 次,结果是否一样?本文把幂等设计、错峰参数(间隔/批次/休眠)、执行用户最小化、以及每次运行的可观测日志一次说清,并给出可直接贴进验收清单的条目。
幂等:业务键比「希望别并行」更可靠
只靠 cron「单线程」不够:升级、手工「立即运行」、多数据库复制演练都可能重入。推荐模式:待处理集合用稳定 domain 查出 → 对每条用外部唯一键或「已处理」标记 (例如 x_last_cron_key / 关联已存在的 account.move)→ 写库存或会计前先 search_count 是否已有结果单据。禁止「查出所有草稿再无条件确认」这种不可重复脚本。长事务拆批:每批 commit(若架构允许)或缩小 recordset,避免锁表到超时。
错峰参数做成系统参数
不要把批次大小、休眠秒数写死在代码。用 ir.config_parameter 例如:my_module.cron_batch_size、my_module.cron_sleep_ms、my_module.cron_max_per_run。高峰把批次调小,低峰调大,无需发版。ir.cron 上设置合理 interval_number/interval_type,nextcall 错开整点;执行用户用专用机器人账号,只开最小安全组,不要用超级用户跑业务过账(除非法规要求且有审计)。
防重入锁与失败退避
任务开头获取命名锁(PostgreSQL advisory lock 或「运行中」行锁)。拿不到锁就写日志退出,而不是并行双写。调用外部 API 失败用指数退避,并把失败计数落到字段;达到阈值创建 mail.activity 给负责人,停止自动狂重试。财务类过账、支付通知禁止「无限自动重试」。
可观测:每次跑都留下数字
标准日志行建议包含:cron xmlid、开始/结束时间、处理条数、跳过条数、失败条数、最长单条耗时。失败样本写单据 id 与异常摘要。运维日报盯:nextcall 过期数量、单次时长、失败次数。与监控文(队列/邮件/Webhook)打通时,cron 失败率应进入同一告警频道。
落地检查清单
- 在预生产对同一批数据连续点两次「立即运行」,业务单据数量不变。
- 把 batch_size 从 50 改到 10,不改代码即可生效。
- 故意让外部 API 失败,确认有活动且不会每分钟打爆对方。
- 机器人用户权限:不能进设置、不能改银行账号。
验收标准
- 连续执行两次不产生重复的发票、库存移动或付款通知。
- 批次与休眠可在系统参数调节。
- 失败有负责人活动;日志能回答「今天处理了多少、跳过了多少」。
- 白天高峰 CPU 不被该 cron 打满(错峰或限流生效)。
参数变更与灰度
调整 ir.config_parameter 批次大小时,先在预生产用同等数据量跑两轮,
观察锁等待与耗时。生产变更写变更单:旧值、新值、回退值、观察指标(处理条数、失败数、CPU)。
高峰只允许调小批次;调大仅低峰。机器人用户的登录与操作写进审计。若 cron 调用外部 API,
在对方限流策略内设置休眠,并把 HTTP 429 当成可重试错误而非业务成功。
cron 代码评审强制问:并行两次会怎样?失败中途重入会怎样?回答不了不准合并。
并发与重入测试方法
预生产准备可重复数据;终端 A/B 同时点「立即运行」或触发 ir.cron.trigger;
断言结果单据唯一。再测:杀掉进程中断半途,重启后重跑,断言无双份过账。
日志中打印批次参数快照,便于回放。参数调小/调大各跑一轮。机器人用户权限表存档,
季度复核是否膨胀。外部 API 模拟 500/429,确认退避与活动创建。
主题 397 收尾:权限、测试数据清理与 runbook 链接由负责人签字。
预生产勾选表需覆盖文章编号 397 全部验收点后再约生产窗口。
中国Odoo网|对照 Odoo 19 企业版 ir.cron 实操:幂等键优先,错峰参数外置,每次运行留下可审计计数。