不具合
def perform
ApplicationRecord::Base.transaction do
unless create_something
raise ActiveRecord::Rollback
end
end
end
def create_something
Hoge.create!(name: 'hoge')
...
return false if error
end
だいたい上記のような、モデルオブジェクトを作ったりするメソッド内でエラーがあった際にロールバックすることを意図した処理があったのですが、create_something内でエラーが起こり画面にエラーメッセージが出ているにもかかわらず、何故かHogeのレコードは作られてしまっているという不具合がありました。
create_somethingがfalseを返しraise ActiveRecord::Rollback
の行を通っているのは確かなのに、ログを見たところなぜかROLLBACKされておらずCOMMITされてしまっていました。
なかなか原因の手掛かりが掴めなかったのですが、しばらくしてこのperformを呼んでいる箇所でもさらにトランザクションがかかっていることに気づきました。
def create
ActiveRecord::Base.transaction do
...
something_service.perform
...
end
end
ネストしたトランザクション内でraise ActiveRecord::RollbackしてもRollbackされない
ググってみたところ、どうやらネストしたトランザクション内でraise ActiveRecord::Rollback
してもロールバックされない仕様のようです。
ネストしたトランザクション内で ActiveRecord::Rollback を raise しても握りつぶされるだけだ
リンク先の例ではネストが1箇所にまとまっていて分かりやすいですが、今回の不具合のようにトランザクションの記述が別のファイルに分かれていてネストに気づけなくても同様の問題が発生してしまいますので、トランザクションをかけるレイヤーは揃えておくべきかと思いました。
なおネストしたトランザクション内でActiveRecord::Rollback
したい場合はドキュメントに記載されているようにrequires_new: true
を指定するといいようです。
https://github.com/rails/rails/blob/v4.2.0/activerecord/lib/active_record/transactions.rb#L142