DiffDaily

Deep & Concise - OSS変更の定点観測

[rails/rails] PostgreSQL schema_search_pathが再接続・リセット後に正しく再適用されない問題の修正

rails/rails

Context

Rails 8.1においてPostgreSQLアダプターで重大なバグが報告されました。database.ymlで設定したschema_search_pathが、reset!またはreconnect!実行後にPostgreSQLのデフォルト値に戻ってしまい、意図したスキーマ検索パスが失われる問題です。

この問題は、マルチテナントアーキテクチャや複数のPostgreSQLスキーマを利用するアプリケーションにおいて、接続の再確立後に誤ったスキーマを参照してしまう可能性があるため、安定版ブランチへのバックポートが必要と判断されました。

Technical Detail

問題の原因

従来の実装では、schema_search_pathは接続確立時に一度だけ設定されていましたが、再接続やリセット時にキャッシュがクリアされないため、PostgreSQLのデフォルト設定に戻ってしまっていました。

実装された修正

今回の修正では、2つの重要な変更が加えられています。

1. clear_cache!メソッドのオーバーライド

新しい接続時にスキーマ検索パスのキャッシュをクリアする処理を追加しました。

def clear_cache!(new_connection: false)
  super
  @schema_search_path = nil if new_connection
end

new_connection: trueの場合、内部変数@schema_search_pathをクリアすることで、次回アクセス時に再設定が行われるようにしています。

2. 接続設定時のキャッシュ初期化

configure_connectionメソッド内で、接続確立直後にschema_search_pathを呼び出すことで、キャッシュを初期化するようにしました。

def configure_connection
  if @config[:encoding]
    @connection.set_client_encoding(@config[:encoding])
  end
  self.client_min_messages = @config[:min_messages] || "warning"
  self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]

  add_pg_encoders
  add_pg_decoders

  schema_search_path # populate cache

  reload_type_map
end

schema_search_pathを呼び出すことで、設定値がキャッシュに正しく保存され、後続の参照で適切に取得できるようになります。

テストケースの追加

修正の妥当性を検証するため、reconnect!reset!の両方のケースに対するテストが追加されています。

def test_schema_search_path_is_reapplied_after_reconnect
  db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")

  connection = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.new(
    db_config.configuration_hash.merge(schema_search_path: "public,foo")
  )

  connection.connect!

  assert_equal "public, foo", connection.select_value("SHOW search_path")

  connection.reconnect!

  assert_equal "public, foo", connection.select_value("SHOW search_path")
ensure
  connection&.disconnect!
end

テストではschema_search_path: "public,foo"を設定した接続を作成し、reconnect!実行後も同じ検索パスが維持されることを検証しています。

影響範囲

この修正はRails 8.1の安定版ブランチ(8-1-stable)へのバックポートであり、以下の条件に該当するアプリケーションに影響します:

  • PostgreSQLアダプターを使用している
  • database.ymlschema_search_pathを明示的に設定している
  • コネクションプールの再接続やリセットが発生する環境(特にマルチスレッド/マルチプロセス環境)

この修正により、接続の再確立後も意図したスキーマ検索パスが維持されるようになり、マルチテナントアプリケーションなどでより安定した動作が期待できます。