ネストした`with_connection`がstickyリースを誤ってクリアするバグを修正
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_wasがtrueであるにもかかわらずガード条件によって復元がスキップされ、lease.stickyがfalseのままになります。結果として、外側のwith_connectionブロックが終了した際に接続が誤って解放されます。
この動作が問題になる典型的なケースが、PRの説明にある「アドバイザリロックライブラリ」の例です:ライブラリが接続リースを確保した後、アプリケーションコードがネストしたwith_connection(prevent_permanent_checkout: true)を呼び出すと、ライブラリのリースが消失し、後続の処理でロックを正しく解放できなくなります。
技術的な変更
connection_pool.rbのwith_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_predicateとassert_sameで検証しています。
設計判断
prevent_permanent_checkout: trueの意味を「stickyリースを永続的に確保させない」から「外側のコンテキストのstickyフラグを常に復元する」へと明確化する判断です。
修正前の実装は「sticky_wasがfalseのときだけ復元する」という動作でしたが、これはprevent_permanent_checkoutの本来の目的である「ブロックを抜けたら元の状態に戻す」という契約と矛盾します。sticky_wasがtrueであれば、ブロック終了後もtrueに戻すのが自然な「外側コンテキストの維持」です。修正後はprevent_permanent_checkoutフラグが立っていれば、sticky_wasの値に関わらず無条件に復元が行われるため、挙動が一貫します。
変更はガード条件の削除のみであり、新たなロジックの追加はありません。影響を受けるのは「prevent_permanent_checkout: trueを指定し、かつ呼び出し前にstickyがtrueだった場合」という限定的なケースのみで、他のパスには影響がありません。
まとめ
&& !sticky_wasという小さなガード条件が、「外側コンテキストを常に復元する」というprevent_permanent_checkoutの設計原則を破っていたバグです。2文字相当の削除によって、ネストしたwith_connectionが外側のstickyリース状態を正しく保全するようになり、アドバイザリロックのような接続の同一性に依存するライブラリが安全に動作できる基盤が整います。