ネストしたトランザクション内でActiveRecord::RollbackしてもRollbackされない

不具合

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

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.