先日、Railsのtransactionのrequires_newオプションを知りました。
記事などを見れば理解できるのですが、(非推奨の):joinableと混同して「どっちだっけ?」となるので自分なりに表にまとめて整理しました。
まとめ
| No. | requires_new | 例外の種類 | ロールバックする処理 | |
|---|---|---|---|---|
| 親 | 子 | |||
| 1 | false |
false |
AR:Transaction |
子の処理もロールバックしない |
| 2 | true |
false |
AR:Transaction |
子の処理もロールバックしない |
| 3 | false |
true |
AR:Transaction |
子の処理のみロールバックする |
| 4 | true |
true |
AR:Transaction |
子の処理のみロールバックする | 5 | false |
false |
AR:Transaction以外 |
親の処理までロールバックする ※ requires_newは無関係 |
<補足>
- 検証バージョン:
Rails.8.0 requires_newはfalseがデフォルトですAR::RollbackはActiveRecord::Rollbackの略です- 「親の処理」「子の処理」は以下のイメージです
ActiveRecord::Base.transaction do Car.create! # ←「親の処理」と呼んでいます ActiveRecord::Base.transaction do Bike.create! # ←「子の処理」と呼んでいます # ここで例外が起きるイメージ end end
検証用コード
上記の表No.1~No.5について、検証用のRSpecの結果を書いておきます。
No.1
it do expect do ActiveRecord::Base.transaction do Car.create! ActiveRecord::Base.transaction do Bike.create! raise ActiveRecord::Rollback end end end.to change(Car, :count).by(1) .and change(Bike, :count).by(1) end # ↓結果 # 1 example, 0 failures
No.2
it do expect do ActiveRecord::Base.transaction(requires_new: true) do Car.create! ActiveRecord::Base.transaction do Bike.create! raise ActiveRecord::Rollback end end end.to change(Car, :count).by(1) .and change(Bike, :count).by(1) end # ↓結果 # 1 example, 0 failures
No.3
it do expect do ActiveRecord::Base.transaction do Car.create! ActiveRecord::Base.transaction(requires_new: true) do Bike.create! raise ActiveRecord::Rollback end end end.to change(Car, :count).by(1) .and change(Bike, :count).by(0) end # ↓結果 # 1 example, 0 failures
No.4
it do expect do ActiveRecord::Base.transaction(requires_new: true) do Car.create! ActiveRecord::Base.transaction(requires_new: true) do Bike.create! raise ActiveRecord::Rollback end end end.to change(Car, :count).by(1) .and change(Bike, :count).by(0) end # ↓結果 # 1 example, 0 failures
No.5
it do expect do ActiveRecord::Base.transaction do Car.create! ActiveRecord::Base.transaction do Bike.create! nil.foo # NoMethodError end end end.to raise_error(NoMethodError) .and change(Car, :count).by(0) .and change(Bike, :count).by(0) end # ↓結果 # 1 example, 0 failures