把任务放进 transaction.on_commit 是「事务提交之后才触发」的标准做法。这篇把它的执行顺序、失败行为、savepoint 语义以及在测试里的坑逐条验证。
atomic 里注册 A、B: ['块内最后一行', 'A', 'B'] 没有 atomic 时注册 C: ['C', '注册之后的下一行']
atomic 退出时抛出: 通知服务挂了 已执行的回调: ['first'] (third 没有执行) 订单已提交: True robust=True:atomic 正常退出,已执行的回调: ['first', 'third'] django.db.backends.base 的错误日志: ['Error calling bad in on_commit() during transaction (通知服务挂了).']
回调在提交之后执行,所以它的失败不会让数据回滚。如果任务发不出去就必须保证可靠(不能丢),就需要额外的机制(比如把任务先写进数据库表,再由后台可靠地投递——这是设计上的建议,本文没有实验)。on_commit 本身是「至多一次」:提交成功后、回调执行前进程崩溃,回调就丢了(本文没有模拟崩溃)。
执行了: ['outer', 'inner2(成功)'] 外层整体回滚:执行了 []
[captureOnCommitCallbacks] 捕获到 1 个回调,execute=True 后 ran = ['cb'] [TestCase] atomic 块结束后回调是否执行: [] [TransactionTestCase] atomic 块结束后回调是否执行: ['cb']
用 manage.py test 真实跑出的结果。测试 on_commit 的逻辑用 captureOnCommitCallbacks,或者 TransactionTestCase(更慢)。