TransactionRollbackError発生時のROLLBACK文を省略

rails/rails

COMMIT時にTransactionRollbackErrorが発生した場合、ActiveRecordは不要なROLLBACK文の実行を省略するようになりました。これによりPostgreSQLのlibpqから直接出力される「WARNING: there is no transaction in progress」という警告ログを防ぎます。

背景

Amazon Aurora DSQLのような並行トランザクションの競合が頻繁に発生するPostgreSQL互換データベースでは、SerializationFailureTransactionRollbackErrorの一種)が日常的に発生します。この状況は通常のPostgreSQLのSERIALIZABLE分離レベルと同様ですが、既存の実装ではCOMMIT失敗後にROLLBACKを実行しようとしていました。

データベースエンジンはCOMMITの失敗時に既にトランザクションをロールバックしているため、その後のROLLBACK文は「進行中のトランザクションがありません」という警告を生成します。rails/rails内のSERIALIZABLEトランザクションのテストでは、この警告をclient_min_messagesで抑制していましたが、本番環境のワークロードでログ抑制は望ましくありません。

技術的な変更

トランザクション無効化の追加

activerecord/lib/active_record/connection_adapters/abstract/transaction.rbwithin_new_transactionメソッドに、TransactionRollbackErrorを捕捉する処理が追加されました。

変更後:

rescue ActiveRecord::TransactionRollbackError
  unless transaction.state.completed?
    transaction.invalidate!
    rollback_transaction(transaction)
  end
  raise

COMMIT中にTransactionRollbackErrorが発生すると、トランザクションを無効化(invalidate!)してから通常のロールバック処理を実行します。この時点でトランザクションは無効化されているため、RealTransaction#rollback内のrollback_db_transaction呼び出しはスキップされます。

ROLLBACK文の条件付き実行

RealTransaction#rollbackメソッドが、トランザクションの無効化状態を確認するように変更されました。

変更前:

def rollback
  if materialized?
    connection.rollback_db_transaction
    connection.reset_isolation_level if isolation_level
  end
  @state.full_rollback!
end

変更後:

def rollback
  if materialized?
    connection.rollback_db_transaction unless @state.invalidated?
    connection.reset_isolation_level if isolation_level
  end
  @state.full_rollback!
end

@state.invalidated?trueの場合、データベースへのROLLBACK文の送信を省略します。トランザクションの状態管理は通常通りfull_rollback!で完了します。

テストコードの簡素化

警告抑制が不要になったため、activerecord/test/cases/adapters/postgresql/transaction_test.rbtransaction_nested_test.rbからwith_warning_suppressionブロックが削除されました。SERIALIZABLEトランザクションのデッドロックやシリアライゼーション失敗のテストケースは、警告なしで実行できるようになっています。

設計判断

トランザクション無効化のタイミング

既存の無効化ロジックは使用できませんでした。6a8a90eCOMMIT発行前にトランザクションが@stateからポップされるため、current_transactionNullTransactionを返します。この制約により、TransactionRollbackErrorの捕捉時に明示的に無効化する必要がありました。

MySQL互換性の維持

PostgreSQLはセーブポイントのロールバックを許可しますが、MySQLはシリアライゼーション失敗やデッドロック発生後にセーブポイントのロールバックを処理できません。本PRの変更はRealTransaction(トップレベルトランザクション)のみに影響し、MySQLのセーブポイント制約(#30922で導入)には干渉しません。

まとめ

本PRは、データベースエンジンによって既にロールバックされたトランザクションに対する冗長なROLLBACK文を省略する変更です。TransactionRollbackError発生時のトランザクション無効化により、PostgreSQL特有の警告ログを根本的に解決し、本番環境でのログ品質を向上させています。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文(総論)→セクション群(各論)→まとめ(結論)の3部構成が明確に適用されており、読者が内容を理解しやすい構成になっています。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト、コミットIDやPR番号のGitHubリンク記法が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

ActiveRecordのトランザクション管理に関する専門的な内容であり、対象読者であるエンジニアに適した技術レベルと表現です。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成になっており、各段落もトピックセンテンスで始まるなど、パラグラフ・ライティングの原則に準拠しています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内のコードブロックは提供されたDiffの内容を正確に引用しており、ファイルパスも一致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`TransactionRollbackError`, `SerializationFailure`, `libpq`など、技術用語が正確かつ適切に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

コード変更の意図や動作の仕組みに関する説明は、Diffの内容と整合性が取れており、技術的に正確です。

事実の突合 ⚠ WARNING

PR情報による主張の裏付け(ハルシネーション検出)

記事の大部分はPR情報に基づいていますが、「設計判断」セクションの「MySQL互換性の維持」に関する記述は、提供されたPR Descriptionには含まれていません。ただし、内容は技術的に正確で関連性も高いです。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#56717)、コミットID(6a8a90e)、関連PR番号(#30922)がすべて正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトルはPRのタイトル「Skip ROLLBACK statement following TransactionRollbackError」の内容を正確に和訳・要約しています。

外部知識の正確性 ⚠ WARNING

PRに記載のない外部知識(LTS、サポート状況など)の不使用

「設計判断」セクションで言及されている関連PR [#30922] の情報は、元のPR Descriptionには記載のない情報です。内容は正確ですが、厳密には外部知識の追加にあたります。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「〜ようになりました」といった時間表現が、PRの適用による変更を正しく反映しており、PR情報との矛盾はありません。