ネストした`with_connection`がstickyリースを誤ってクリアするバグを修正

rails/rails

lease_connectionでstickyリースを取得済みのwith_connectionブロック内で、with_connection(prevent_permanent_checkout: true)をネストして呼び出すと、外側のstickyリースが意図せず解放されるバグが修正されました。

背景

prevent_permanent_checkout: trueオプションを持つwith_connectionは、ブロック終了時に接続のstickyフラグを「ブロック開始前の状態」に戻す設計です。しかし従来の実装では、&& !sticky_wasというガード条件が付いていたため、「ブロック開始前にstickyだった場合(sticky_was == true)」はフラグが復元されませんでした。

この問題が顕在化するのは、以下のネストしたシナリオです:外側のwith_connectionブロック内でlease_connectionを呼び出してstickyリースを確保した後、内側のwith_connection(prevent_permanent_checkout: true)が終了すると、sticky_wastrueであるにもかかわらずガード条件によって復元がスキップされ、lease.stickyfalseのままになります。結果として、外側のwith_connectionブロックが終了した際に接続が誤って解放されます。

この動作が問題になる典型的なケースが、PRの説明にある「アドバイザリロックライブラリ」の例です:ライブラリが接続リースを確保した後、アプリケーションコードがネストしたwith_connection(prevent_permanent_checkout: true)を呼び出すと、ライブラリのリースが消失し、後続の処理でロックを正しく解放できなくなります。

技術的な変更

connection_pool.rbwith_connectionメソッド内にあるensure節で、stickyフラグを復元する条件式から&& !sticky_wasを削除しました。

変更前:

ensure
  lease.sticky = sticky_was if prevent_permanent_checkout && !sticky_was
end

変更後:

ensure
  lease.sticky = sticky_was if prevent_permanent_checkout
end

この修正は、接続が既にリース済みのパス(lease.connectionが存在する場合)と、新規にチェックアウトするパス(lease.connection = checkoutする場合)の両方に適用されています。いずれも同じガード条件を持っていたため、2箇所で同一の修正が行われています。

修正に合わせてconnection_handling_test.rbにテストケースも追加されました。テストでは、外側のwith_connection内でlease_connectionを呼び出した後、内側のwith_connection(prevent_permanent_checkout: true)を実行し、ブロック終了後も接続プールがアクティブな接続を保持し続けることをassert_predicateassert_sameで検証しています。

設計判断

prevent_permanent_checkout: trueの意味を「stickyリースを永続的に確保させない」から「外側のコンテキストのstickyフラグを常に復元する」へと明確化する判断です。

修正前の実装は「sticky_wasfalseのときだけ復元する」という動作でしたが、これはprevent_permanent_checkoutの本来の目的である「ブロックを抜けたら元の状態に戻す」という契約と矛盾します。sticky_wastrueであれば、ブロック終了後もtrueに戻すのが自然な「外側コンテキストの維持」です。修正後はprevent_permanent_checkoutフラグが立っていれば、sticky_wasの値に関わらず無条件に復元が行われるため、挙動が一貫します。

変更はガード条件の削除のみであり、新たなロジックの追加はありません。影響を受けるのは「prevent_permanent_checkout: trueを指定し、かつ呼び出し前にstickytrueだった場合」という限定的なケースのみで、他のパスには影響がありません。

まとめ

&& !sticky_wasという小さなガード条件が、「外側コンテキストを常に復元する」というprevent_permanent_checkoutの設計原則を破っていたバグです。2文字相当の削除によって、ネストしたwith_connectionが外側のstickyリース状態を正しく保全するようになり、アドバイザリロックのような接続の同一性に依存するライブラリが安全に動作できる基盤が整います。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
b58dee60

この記事は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

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

「sticky lease」や「connection pool」などの専門用語を前提として解説しており、専門知識を持つエンジニアという対象読者に適合しています。

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

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

各セクションが総論→各論の構成で書かれ、各段落もトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が遵守されています。非常に読みやすい構成です。

Diff内容との照合 ✓ PASS

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

`activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb`における`&& !sticky_was`の削除というコード変更を、Diffと完全に一致する形で正確に引用しています。テストファイルの追加に関する言及も正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`prevent_permanent_checkout`、`sticky lease`、`lease_connection`など、PRで使われている技術用語を正確かつ適切に使用しています。

説明の技術的正確性 ✓ PASS

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

「`&& !sticky_was`というガード条件が原因で、`sticky_was`が`true`の時にフラグが復元されなかった」というバグの原因と修正内容の説明が、PRの情報と完全に一致しており、技術的に正確です。

事実の突合 ✓ PASS

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

記事内のすべての主張(バグの原因、影響範囲、修正内容)は、提供されたPRのDescription、Title、Diffから裏付けられており、ハルシネーションは見られません。

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

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

PR番号(#57207)がフッターで正しく記載されています。

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

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

記事のタイトルは、PRのタイトル「Fix nested `with_connection(prevent_permanent_checkout: true)` incorrectly clearing sticky lease set by `lease_connection`」の内容を的確に要約・翻訳しています。

外部知識の正確性 ✓ PASS

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

記事内容はすべてPRとDiff情報に基づいており、バージョンサポート情報やリリース日程など、PRに記載のない外部知識の追加はありません。

時間表現の正確性 ✓ PASS

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

「従来の実装では」「修正後は」といった時間表現が適切に使われており、PRの内容と時間的な矛盾はありません。