63
34

トランザクション中にrescueするとロールバックしないので注意!

Last updated at Posted at 2021-01-27

トランザクション中のrescueはロールバックを発生させない

動画による説明

[Ruby on Rails] トランザクション中のrescueには気をつけて

トランザクション中のrescue

このようにすると、create!で発生した例外をキャッチして、exec_transactionの返り値としてfalseを返すことができます。

def exec_transaction
  ApplicationRecord.transaction do
    User.create!(name: 'Duplicate')
    User.create!(name: 'Duplicate')
  rescue ActiveRecord::RecordInvalid
    false
  end
end

Userモデルは下記のようにバリデーションが設定してあります。

class User < ApplicationRecord
  validates :name, uniqueness: true
end

このコードを実行すると、二回目のcreate!でエラーが発生しますが、ロールバック処理が実行されません

Rollbackが起きる仕組み

transactionメソッドに与えられたブロックは、最終的にwithin_new_transactionメソッドの中で実行されます。
ここで、与えられたブロックで発生したすべての例外をキャッチして、ロールバックを発生させたあと、例外をもう一度送出します。

activerecord/lib/active_record/connection_adapters/abstract/transaction.rb#L313-L318

この動作によって、transactionで例外が発生すると、ロールバックが暗黙的に実行されて、例外の送出も行われます。

Exceptionをキャッチするとロールバックしない

上述の通り、発生した例外をトリガーにしてロールバックが発生します。したがって、先に示したコードのようにブロックの中で例外をキャッチしてしまうと、ロールバックが起きません。

ロールバックしつつ例外をキャッチしたい場合

方法は二通り。明示的にロールバックするか、トランザクションの外側で例外をキャッチする。

明示的にロールバック

こちらの動画で紹介されている方法と同じ考え方です。
https://www.youtube.com/watch?v=jFBvEQhApKQ

ActiveRecord::Rollbackを送出してロールバックを行い、全て正常終了したかどうかを返り値とします。

def exec_transaction
  success = true
  ApplicationRecord.transaction do
    success &= User.new(name: 'Duplicate').save
    success &= User.new(name: 'Duplicate').save
    unless success do
       raise ActiveRecord::Rollback
    end
  end

  success
end

トランザクションの外側で例外をキャッチする

ロールバックが発生したら例外は再度送出される、という性質を利用します。


def exec_transaction
  ApplicationRecord.transaction do
    User.create!(name: 'Duplicate')
    User.create!(name: 'Duplicate')
  end
rescue ActiveRecord::RecordInvalid
  false
end

そもそもの例外処理の設計

こういう例外処理がある事自体が良くない可能性もあります。もちろん、すべてが悪いということではないです。
エラー処理の設計については、下記の資料で非常に丁寧にまとめられています。

Railsアプリケーションにおけるエラー処理(例外設計)の考え方
プロを目指す人のための例外処理(再)入門 / #rubykansai 2018-01-13

63
34
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
63
34