PostgreSQL の FATAL 例外を ConnectionFailed に明示的に変換

rails/rails

PostgreSQL が接続を強制終了する FATAL エラーを、Rails が汎用の StatementInvalid ではなく ConnectionFailed として扱えるようにし、接続切断を早期に検知できるようになりました。

背景

FATAL はサーバ側が接続を終了させることを意味し、これを StatementInvalid にマッピングすると、アプリケーションは実際には切断された接続を「失敗したクエリ」として扱い、次回以降の利用で予期しないエラーが発生します。PR #57057 はこの認識のずれを修正し、致命的エラーを正確に ConnectionFailed へ変換することを目的としています。

この変更により、データベースが切断した瞬間に例外が上がり、プールへ返却される前に再接続がトリガーされるため、接続切れが遅延なく検出できるようになります。

技術的な変更

@needs_reconnect フラグを AbstractAdapter に導入し、接続が再接続を要する状態かどうかを明示的に管理します。initialize で初期化し、reconnect!disconnect! の処理でフラグをリセット、verify! でフラグが立っている場合に再接続を実行するロジックを追加しました。

perform_query では従来の exec_params 系メソッドをやめ、send_query_params / send_query_prepared / send_query に置き換えてクエリ送信後に自前で get_result を呼び出すように変更しました。これによりクエリ実行直後に結果オブジェクトを取得でき、FATAL エラーが notice_receiver 経由で届いた場合でも捕捉できます。

新たに導入された get_resultraw_connection.get_result をループで取得し、結果が複数あっても最後のものに対して result.check を呼び出すことで、PGRES_FATAL_ERROR を検出して例外を上げます。さらに connection_terminating_severity? ヘルパーで FATAL または PANIC の重大度を判定し、ErrorResultSnapshot がエラーフィールドやメッセージを保持できるようにしました。

テストコードも拡充され、@needs_reconnect が立った接続でクエリを走らせたときに自動的に再接続されること、kill_connection_from_server によるバックエンド強制終了が ActiveRecord::ConnectionFailed として伝搬することを検証しています。

設計判断

フラグ方式で再接続要件を管理する選択は、既存の接続プールロジックに最小限の侵入で済む点が評価されています。needs_reconnect? は内部的に使用され、外部 API として公開しないことで実装の自由度を保ちつつ、接続状態の明示化を実現しました。

低レベル libpq 関数の直接使用は、exec_params が内部で結果取得を行うため致命的エラーを拾い損ねる問題を回避するための手段です。これによりエラー報告経路を一本化し、ConnectionFailed への正確なマッピングが可能になりましたが、コードパスが増えることでテストカバレッジが必要になるというトレードオフがあります。

エラースナップショットの導入は、PG の診断情報を保持しつつ例外オブジェクトにラップすることで、上位層でのエラーハンドリングをシンプルに保つ設計です。これにより ActiveRecord::ConnectionFailedcause に元の PG::AdminShutdown などが正しく添付され、デバッグ情報が失われません。

まとめ

この PR は PostgreSQL の FATAL エラーを正確に捕捉し ConnectionFailed として扱う仕組みを追加し、接続切断時の例外伝搬を改善しました。@needs_reconnect フラグと低レベルクエリ送信ロジックの組み合わせにより、既存アプリケーションへの影響を最小限に抑えつつ、接続の健全性検知を強化しています。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
3165495e

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

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文・背景・技術的な変更・設計判断・まとめの5セクションが揃っており、総論→各論→結論の流れが明確です。

カスタムMarkdown構文 ⚠ WARNING

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

PR へのリンクが「[PR #57057](URL)」となっており、要求された形式「[#57057](URL)」になっていません。コードハイライトは使用されていないため問題なし。

対象読者への適合性 ✓ PASS

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

Rails/ActiveRecord の内部実装に関する記述が中心で、エンジニア向けの技術レベルに適合しています。

パラグラフ・ライティング ⚠ WARNING

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

いくつかの段落が6文を超えて長くなっており、1段落1トピックの原則に対してやや冗長です。分割すれば可読性が向上します。

Diff内容との照合 ✓ PASS

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

記事の記述は Diff の変更点(@needs_reconnect フラグ、send_query 系、ErrorResultSnapshot など)と一致しており、抜けや誤りは見られません。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

FATAL、StatementInvalid、ConnectionFailed、PG::AdminShutdown などの用語が正しく使われています。

説明の技術的正確性 ✓ PASS

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

技術的な説明は PR の目的や実装内容と合致しており、誤った主張はありません。

事実の突合 ✓ PASS

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

PR タイトル・説明・Diff に裏付けられる事実のみが記載されており、推測や外部情報は含まれていません。

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

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

PR 番号 #57057 だけが数値情報として出てきますが、正確です。

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

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

記事タイトルは PR の趣旨(FATAL 例外を明示的に扱う)を適切に要約しています。

外部知識の正確性 ✓ PASS

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

バージョンサポートやリリース日程など、PR に無い外部知識は記載されていません。

時間表現の正確性 ✓ PASS

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

時間表現のずれはなく、PR の記述と一致しています。