テスト teardown 時のプール Reaper との競合による誤検知を修正

rails/rails

ActiveRecord::TestCase#check_connection_leaksConnectionPool::Reaper のメンテナンス処理と競合し、正常な接続を「リーク」と誤報告していた問題が修正されました。reaper_lock で排他制御することで、teardown がメンテナンス完了を待機してから接続の使用状態を判定するようになります。

背景

check_connection_leaks は teardown 時にプール内の接続をスキャンし、バックグラウンドスレッドが接続を保持したままテストを終えていないかを検査するメソッドです。pool.connections を走査して conn.in_use? が真であれば「リーク」と判断し、所有スレッドを kill します。

一方、ConnectionPool::Reaper は定期的にメンテナンス(タイムアウト接続の回収等)を行う際、reaper_lock を取得した状態で一時的に接続を保持します。teardown のリークチェックがこの期間中に pool.connections を参照した場合、Reaper が保持している接続を in_use? と見なし、AR Pool Reaper スレッドを kill してしまいます。テスト自体は何もリークしていないにもかかわらず、です。

この問題は #57367 の検証中に CI で発生した無関係な失敗として発覚し、報告されたリークオーナーが Reaper スレッドであったことで特定されました。

技術的な変更

activerecord/test/cases/test_case.rbcheck_connection_leaks メソッド内で、プールの走査全体を pool.reaper_lock ブロックで包む変更が加えられました。

変更前:

pool.reap
pool.connections.each do |conn|
  if conn.in_use?
    if conn.owner != Fiber.current && conn.owner != Thread.current
      leaked_conn << [conn.owner, conn.owner.backtrace]
      conn.owner&.kill
    end
    conn.steal!
    pool.checkin(conn)
  end
end

変更後:

# Avoid racing the pool reaper while it is performing maintenance.
pool.reaper_lock do
  pool.reap
  pool.connections.each do |conn|
    if conn.in_use?
      if conn.owner != Fiber.current && conn.owner != Thread.current
        leaked_conn << [conn.owner, conn.owner.backtrace]
        conn.owner&.kill
      end
      conn.steal!
      pool.checkin(conn)
    end
  end
end

pool.reappool.connections.each の両方が reaper_lock ブロック内に移動しています。これにより teardown は Reaper が進行中のメンテナンスを完了させるまでロック取得を待ち、走査のタイミングで Reaper が接続を保持していない状態を保証します。

同時に、リグレッションテストとして activerecord/test/cases/test_case_test.rb が新規追加されました。テストは以下の手順で競合条件を再現します:

  1. Thread.current.name = "AR Pool Reaper" で Reaper スレッドを模倣したスレッドを起動し、reaper_lock を取得して接続を保持する
  2. pool.stub(:reaper_lock, ...) でリークチェックが reaper_lock に到達したことを CountDownLatch で検知する
  3. リークチェック完了後に leaked_conn が空であることを確認する

このテストはパッチ適用前の origin/main では失敗し、適用後に通過することが確認されています。

設計判断

reaper_lock をリークチェック側で取得する という対称的なアプローチが採用されました。

リークチェックの目的は「テストが終了した後に、本来解放されているべき接続が保持されていないか」を検査することです。Reaper によるメンテナンス中の接続保持はその定義から外れますが、既存の実装はこの区別を行っていませんでした。reaper_lock を共有することで、Reaper がロックを保持している間はリークチェックが走らない(または逆に Reaper がロックを待つ)という相互排除が実現されます。

また、修正のスコープがテストインフラ(test/cases/test_case.rb)に限定されており、プロダクションコードへの変更を伴わない点も注目に値します。競合はテストのテアダウン処理固有の問題であり、修正もその層に閉じています。

まとめ

本PRは、reaper_lock という既存の排他制御機構をリークチェックと共有することで、最小限の変更で競合を解消しています。CI における断続的な誤検知を排除するとともに、Reaper スレッドが誤って kill されることでテスト環境が不安定になるリスクも取り除いた修正といえます。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
900cb677

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

ファイル名付きシンタックスハイライト(```ruby:path/to/file.rb)と、PR番号のリンク記法([#123](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

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

「ConnectionPool::Reaper」「teardown」などの専門用語を前提として説明が進められており、対象読者である専門知識を持つエンジニアに適した内容です。

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

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

各セクション、各パラグラフが「総論→各論」の構成になっており、トピックセンテンスが段落の冒頭に配置されているため、非常に高い可読性を実現しています。段落の長さも適切です。

Diff内容との照合 ✓ PASS

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

記事内のコードブロック(変更前・変更後)は、提供されたDiff情報を正確に反映しています。ファイルパスの記載も正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「reaper_lock」「in_use?」「AR Pool Reaper」など、PRの文脈で使われる技術用語を正確に使用しています。

説明の技術的正確性 ✓ PASS

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

「reaper_lock」による排他制御が、なぜReaperとの競合を防ぎ、誤検知を修正するのかという技術的な説明が論理的かつ正確です。

事実の突合 ✓ PASS

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

記事内のすべての主張(問題の背景、解決策、発覚の経緯など)が、提供されたPRのDescriptionによって裏付けられており、ハルシネーションは検出されませんでした。

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

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

Primary SourceのPR番号(#57370)や、言及されている関連PR番号(#57367)が正確に記載されています。

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

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

記事のタイトル「テスト teardown 時のプール Reaper との競合による誤検知を修正」は、元のPRタイトル「Prevent false leaked-connection reports during reaper maintenance」の内容を的確に表現しています。

外部知識の正確性 ✓ PASS

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

記事の内容は提供されたPR情報に完全に準拠しており、バージョンサポート状況やリリース予定といった外部知識の持ち込みはありません。

時間表現の正確性 ✓ PASS

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

記事内に時間表現の歪曲は見られず、PRの事実関係を正確に記述しています。