我希望 ActiveRecord 事务故意失败。问题是,当我引发 ActiveRecord::Rollback 异常(或任何其他异常)时,回滚不会发生。在下面的代码中,
perfom
方法导入一个文件。为了简单起见,我们假设它只是更新一条记录:
puts "+++ Before Transaction +++"
ActiveRecord::Base.transaction do
puts "+++ In Transaction +++"
perform
puts "+++ Before Rollback +++"
raise ActiveRecord::Rollback
end
puts "+++ After Transaction +++"
当代码运行时,这是服务器的输出:
+++ Before Transaction +++
+++ In Transaction +++
TRANSACTION (0.1ms) BEGIN
↳ app/models/import.rb:103:in `file_content'
Disk Storage (0.1ms) Downloaded file from key: abcdefg
Car Load (0.2ms) SELECT "cars".* FROM "cars" WHERE "cars"."make" = '207' LIMIT 1
↳ app/models/application_record.rb:82:in `try_update_or_create_report'
Car Exists? (0.3ms) SELECT 1 AS one FROM "rolling_shutters_automatism_prices" WHERE
"cars"."make" = '207' AND "cars"."id" != 5 LIMIT 1
↳ app/models/application_record.rb:106:in `try_update_report'
Car Update (0.2ms) UPDATE "cars" SET "price" = 20.0, "updated_at" = '2023-12-26 15:17:47.978023' WHERE "cars"."id" = 5
↳ app/models/application_record.rb:106:in `try_update_report'
+++ Before Rollback +++
+++ After Transaction +++
我期望的行为是在最后两行之间登录
ROLLBACK
,但没有记录任何内容。这是为什么?
我使用 Rails 7.0.4 和 postgres 15 和 ruby 3.2.2
编辑:我遗漏了一条重要信息。事务发生在 ActiveRecord 回调中。
问题在于事务发生在 ActiveRecord 回调内。由于回调“本身包装在事务中”,因此我尝试执行的事务是“嵌套事务”。因此,引发 ActiveRecord::Rollback 异常会导致外部事务回滚。 根据文档:
为了获得嵌套事务的回滚,您可以通过传递requires_new: true来请求真正的子事务。如果出现任何问题,数据库将回滚到子事务的开头,而不回滚父事务。
因此固定示例如下所示:
puts "+++ Before Transaction +++" ActiveRecord::Base.transaction(requires_new: true) do puts "+++ In Transaction +++" perform puts "+++ Before Rollback +++" raise ActiveRecord::Rollback end puts "+++ After Transaction +++"