[Rails] Active Record Transactions の requires_new

transactionをネストしたときに、子のtransaction内でActiveRecord::Rollbackを投げてもロールバックされない。

irb(main):059:0> User.transaction do
irb(main):060:1*   User.create(username: 'Kotori')
irb(main):061:1>   User.transaction do
irb(main):062:2*     User.create(username: 'Nemu')
irb(main):063:2>     raise ActiveRecord::Rollback
irb(main):064:2>   end
irb(main):065:1> end
   (0.1ms)  begin transaction
  User Create (0.7ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Kotori"], ["created_at", "2019-07-18 14:31:50.104487"], ["updated_at", "2019-07-18 14:31:50.104487"]]
  User Create (0.1ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Nemu"], ["created_at", "2019-07-18 14:31:50.106418"], ["updated_at", "2019-07-18 14:31:50.106418"]]
   (1.7ms)  commit transaction
=> nil

irb(main):066:0> User.all.pluck(:username)
   (0.1ms)  SELECT "users"."username" FROM "users"
=> ["Kotori", "Nemu"]

requires_new: trueとすると子のtransaction内だけロールバックする。

irb(main):073:0> User.transaction do
irb(main):074:1*   User.create(username: 'Kotori')
irb(main):075:1>   User.transaction(requires_new: true) do
irb(main):076:2*     User.create(username: 'Nemu')
irb(main):077:2>     raise ActiveRecord::Rollback
irb(main):078:2>   end
irb(main):079:1> end
   (0.1ms)  begin transaction
  User Create (0.3ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Kotori"], ["created_at", "2019-07-18 14:33:44.891135"], ["updated_at", "2019-07-18 14:33:44.891135"]]
   (0.0ms)  SAVEPOINT active_record_1
  User Create (0.1ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Nemu"], ["created_at", "2019-07-18 14:33:44.892205"], ["updated_at", "2019-07-18 14:33:44.892205"]]
   (0.0ms)  ROLLBACK TO SAVEPOINT active_record_1
   (1.5ms)  commit transaction
=> nil

irb(main):080:0> User.all.pluck(:username)
   (0.1ms)  SELECT "users"."username" FROM "users"
=> ["Kotori"]

ちなみに、普通に例外を投げると親のtransactionもロールバックする。普通。

irb(main):087:0> User.transaction do
irb(main):088:1*   User.create(username: 'Kotori')
irb(main):089:1>   User.transaction do
irb(main):090:2*     User.create(username: 'Nemu')
irb(main):091:2>     raise
irb(main):092:2>   end
irb(main):093:1> end
   (0.1ms)  begin transaction
  User Create (0.8ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Kotori"], ["created_at", "2019-07-18 14:36:22.828138"], ["updated_at", "2019-07-18 14:36:22.828138"]]
  User Create (0.2ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Nemu"], ["created_at", "2019-07-18 14:36:22.830153"], ["updated_at", "2019-07-18 14:36:22.830153"]]
   (1.4ms)  rollback transaction
Traceback (most recent call last):
        3: from (irb):87
        2: from (irb):89:in `block in irb_binding'
        1: from (irb):91:in `block (2 levels) in irb_binding'
RuntimeError ()

irb(main):094:0> User.all.pluck(:username)
   (0.1ms)  SELECT "users"."username" FROM "users"
=> []

参考