disconnect後もピン留めされたコネクションをプールに保持
disconnect! 実行後もピン留めされたコネクションが @connections 配列に残るようになります。これにより、コネクション状態の不整合が解消され、クエリキャッシュが正常に機能するようになります。
背景
QueryCacheTest#test_cache_is_available_when_connection_is_connected が特定の実行順序で失敗する問題が #56796 で報告されました。PostgresqlByteaWithDecoderTest の teardown で disconnect! が呼ばれた後、次のテストでクエリキャッシュが機能しなくなっていました。
原因は disconnect! がすべてのコネクションを切断する際に @connections を空配列で初期化 していたことです。unpin_connection! で @available に戻されたピン留めコネクションを checkout_and_verify が取得しても、@connections に追加されない状態が発生していました。その結果、connected? が false を返し、クエリキャッシュが無効化されてしまう不整合が生じていました。
技術的な変更
activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb の disconnect メソッドが修正され、@connections の初期化時にピン留めコネクションを保持するようになりました。
変更前:
@connections = []
変更後:
@connections = @pinned_connection ? [@pinned_connection] : []
この変更により、以下の不変条件が維持されます:
-
pin_connection!でピン留めされたコネクションは@connectionsに含まれる -
checkoutでチェックアウトされたコネクションは@connectionsに含まれる -
checkout_and_verifyで取得されたコネクションも@connectionsに含まれる
disconnect! 後の状態でもこの不変条件が保たれるようになりました。
設計判断
ピン留めコネクションを特別扱いする方式 が採用されました。
@pinned_connection の存在確認を三項演算子で行い、存在する場合のみ配列に含める実装です。これは disconnect! がすべてのコネクションを切断する処理の中で、唯一ピン留めコネクションだけは状態を保持する必要があるという設計判断を反映しています。#56799 では checkout_and_verify 側での対処も検討されましたが、根本原因である disconnect! での状態管理を修正する現行のアプローチが採用されました。
空配列への初期化は一見シンプルですが、ピン留めという例外的な状態を考慮していませんでした。条件分岐を加えることで、コネクションプールの状態管理の整合性を保つトレードオフです。
まとめ
本PRは、disconnect! 実行時の状態管理を修正することで、コネクションプールの不変条件を維持した変更です。1行の条件分岐追加により、ピン留めコネクションが存在する場合の connected? 判定が正常化し、クエリキャッシュの動作が安定します。