Open3

トランザクション内で例外キャッチするとロールバックされない

naon708naon708
class Klass
  ActiveRecord::Base.transaction do
    # ① 例外が発生し得る処理
  rescue StandardError => e
    # ② 例外処理
  end
end

このコードではトランザクション中に例外が発生した場合に、ロールバックされない。
例外発生直後に rescue されてしまうため。

class Klass
  begin
    ActiveRecord::Base.transaction do
      # ① 例外が発生し得る処理
    end
  rescue StandardError => e
    # ② 例外処理
  end
end

このように transaction ブロックの外側でキャッチさせるとロールバックしてくれる。
①で例外が発生 → transaction メソッドがロールバックを行い、直前で発生した例外を返す → begin 式内で例外がキャッチされ、②の例外処理を実行する

naon708naon708

もう少し掘る

https://github.com/rails/rails/blob/5c40239d3104543e70508360d27584a3e4dc5baf/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb#L218-L229

https://github.com/rails/rails/blob/main/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb#L622

begin
  commit_transaction
rescue ActiveRecord::ConnectionFailed
  transaction.invalidate! unless transaction.state.completed?
  raise
rescue Exception
  # ロールバックし、元の例外を返す
  rollback_transaction(transaction) unless transaction.state.completed?
  raise
end
  1. transaction ブロック内で例外を検知
  2. ロールバックして、直前の例外を返す(within_new_transactionメソッド)
  3. begin ブロックで返された例外を検知して rescue される