MySQL エラー 1046 を `ConnectionFailed` として分類し、自動リトライを可能に

rails/rails

TCPプロキシによる接続リセット後に発生するMySQL エラー 1046(ER_NO_DB_ERROR: No database selected)が、ConnectionFailed として分類されるようになりました。これにより、with_raw_connection の組み込みリトライループが正常に機能し、接続の自動復旧が期待できます。

背景

mysql2がTCP接続をサイレントに再確立した場合、新しい接続では USE database_name が再発行されません。プロキシによる接続リセットが発生したとき、次のクエリは MySQL エラー 1046 No database selected で失敗しますが、このエラーはこれまで適切に処理されていませんでした。

従来の translate_exception にはエラー 1046 に対応するケースがなく、StatementInvalid にフォールスルーしていました。StatementInvalidretryable_connection_error? に認識されないため、with_raw_connection のリトライループが発火せず、接続の自動復旧が行われないという問題がありました。

この問題は、エラー 2006(CR_SERVER_GONE_ERROR)およびエラー 2013(CR_SERVER_LOST)と同じクラスの問題です。これら2つはすでに ConnectionFailed として分類されており、エラー 1046 も同様に扱われるべきでした。

技術的な変更

abstract_mysql_adapter.rb の2箇所に最小限の変更が加えられました。まずエラー定数の定義に ER_NO_DB_ERROR が追加され、次に translate_exceptionConnectionFailed ブランチにそのエラーが追加されています。

変更前:

# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
ER_DB_CREATE_EXISTS     = 1007
ER_FILSORT_ABORT        = 1028
ER_DUP_ENTRY            = 1062
# ...

when ER_CONNECTION_KILLED, ER_SERVER_SHUTDOWN, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ER_CLIENT_INTERACTION_TIMEOUT
  ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)

変更後:

# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
ER_DB_CREATE_EXISTS     = 1007
ER_FILSORT_ABORT        = 1028
ER_NO_DB_ERROR          = 1046
ER_DUP_ENTRY            = 1062
# ...

when ER_CONNECTION_KILLED, ER_SERVER_SHUTDOWN, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ER_CLIENT_INTERACTION_TIMEOUT, ER_NO_DB_ERROR
  ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)

テストでは、Mysql2::Error のスタブを使って ER_NO_DB_ERRORActiveRecord::ConnectionFailed に変換されることを検証しています。connection_pool 属性が正しく設定されている点も確認されており、エラーハンドリングのチェーンが機能していることを示します。

def test_no_database_selected_error_translates_to_connection_failed
  raw_conn = @conn.raw_connection
  error = assert_raises(ActiveRecord::ConnectionFailed) do
    raw_conn.stub(:query, ->(_sql) { raise Mysql2::Error.new("No database selected", 50700, ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::ER_NO_DB_ERROR) }) {
      @conn.execute("SELECT 1")
    }
  end
  assert_equal @conn.pool, error.connection_pool
end

設計判断

既存の ConnectionFailed 分類の拡張 というアプローチが採用されました。エラー 1046 は一見「データベースが選択されていない」というアプリケーション側の設定ミスのように見えますが、コネクションプールを介した接続リセット後に発生するケースでは、これは本質的に接続状態の喪失を意味します。このため、ステートメントエラーではなく接続エラーとして扱うことが正確です。

ER_NO_DB_ERROR を新たな定数として定義し、when 節に追加する構成は、既存の他のエラー定数(CR_SERVER_GONE_ERRORCR_SERVER_LOST など)と完全に対称的です。新たな抽象化や分岐ロジックを導入せず、確立されたパターンに従うことで変更範囲を最小限に抑えています。

まとめ

この変更は、コネクションプールと中間プロキシを使うMySQLアプリケーションにおいて、サイレントな接続リセット後の障害回復を自動化します。エラー分類のパターンを正しく適用することで、既存のリトライ機構がそのまま機能するようになり、新たなリカバリロジックを追加する必要がありません。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
5995d8ad

この記事は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リンク記法の正確性

ファイル名付きのシンタックスハイライト(```言語:ファイルパス)およびPR番号のリンク記法([PR #57067](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

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

コネクションプーリング、プロキシ、例外クラスなど、専門知識を持つエンジニアを対象とした適切な技術レベルと表現で書かれています。

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

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

各セクションが総論から始まり、各パラグラフがトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られています。可読性が非常に高いです。

Diff内容との照合 ✓ PASS

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

記事内のコードブロックは、提供されたDiff情報と完全に一致しています。変更点やテストコードの引用が正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`ConnectionFailed`, `StatementInvalid`, `ER_NO_DB_ERROR` などの技術用語が、PRの文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

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

エラーが `StatementInvalid` にフォールスルーしていた問題や、他の接続エラー(`CR_SERVER_GONE_ERROR`など)との関連性など、技術的な説明が正確で論理的です。

事実の突合 ✓ PASS

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

記事内のすべての主張(TCPプロキシ起因の問題、リトライが機能しないメカニズムなど)が、提供されたPRのDescriptionによって裏付けられています。ハルシネーションは見られません。

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

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

PR番号(#57067)やMySQLのエラーコード(1046)などの数値・固有名詞はすべて正確に記載されています。

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

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

記事のタイトルはPRの主題「Classify mysql error 1046 ... as ConnectionFailed」を正確に反映し、その影響(自動リトライ)まで含めており、適切です。

外部知識の正確性 ✓ PASS

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

PR情報に記載のない、バージョンサポート状況やリリース日程などの外部知識は含まれておらず、事実に忠実です。

時間表現の正確性 ✓ PASS

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

「これまで」「〜ようになりました」といった時間表現が、PRの文脈(問題の修正)と正しく一致しています。