Rails 7.2のコネクションプール変更がアプリを遅くする可能性

Rails 7.2にアップグレードした後、アプリが少し遅くなっていることに気づくかもしれません。劇的にではありませんが、測定可能なレベルで—実際のベンチマークで約5-6%です。これはActiveRecordがデータベース接続を管理する方法の変更に起因しています。

変更内容

Rails 7.1では、各スレッドがリクエストの期間中データベース接続を保持していました。

Rails 7.2ではこれが変わりました:接続は各クエリごとにチェックアウトされ、直後に返却されるようになりました。目的は、接続が不足しているマルチスレッド環境での接続共有を改善することでした。

この変更はPR #50793で導入され、ActiveRecord::Base.connectionが一時的なアクセス用のActiveRecord::Base.with_connectionと、より長い保持用のlease_connectionに置き換えられました。

なぜ遅延が発生するのか

チェックイン/チェックアウトのサイクルはタダではありません。接続がプールに戻るたびにcheckinコールバックが実行され、コールバックシステムがオブジェクトを割り当てます。10クエリのリクエストは、ゼロではなく10回のチェックインを行うようになります。

issue #55728のベンチマークは影響を示しています:

Rails 7.1:              98.55 req/sec
Rails 7.2:              92.45 req/sec  (6%遅い)
Rails 7.2 + 回避策:     99.99 req/sec  (正常に戻る)

Unicornのようなシングルスレッドワーカーを実行している場合、ローカルまたは低レイテンシのデータベースがある場合、リクエストごとに多くのクエリを実行している場合に、これを最も感じるでしょう。

解決方法

接続共有が重要でないシングルスレッドワーカーの場合、各リクエストの開始時に接続をリースします:

class ApplicationController < ActionController::Base
  before_action :lease_database_connection

  private

  def lease_database_connection
    ActiveRecord::Base.lease_connection
  end
end

これにより、リクエスト全体で接続が保持され、チェックイン/チェックアウトのサイクルがスキップされます。

バックグラウンドジョブも同様です:

class ApplicationJob < ActiveJob::Base
  before_perform do
    ActiveRecord::Base.lease_connection
  end
end

これをスキップすべき場合

データベース接続より多くのスレッドでマルチスレッドPumaを実行している場合、新しい動作は実際に望ましいものです—スレッド間で接続を共有できます。データベースをあまり使用しない長時間実行リクエストも同様です。必要のない接続を保持すると、他のスレッドが不足します。

内部の仕組み

オーバーヘッドの大部分はrun_callbacks(:checkin)にあります。カスタムコールバックがなくても、Railsは完全なコールバック機構を実行します—オブジェクト割り当て、メソッドディスパッチなど、すべてです。

Jean Boussier(byroot)はRails mainの最適化に取り組んでいますが、これらはバグ修正ではなくパフォーマンス調整のため、7.2にはバックポートされません。

アプリの測定

何かを変更する前に、これが影響しているか確認してください:

# config/initializers/connection_benchmark.rb
if Rails.env.development?
  ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
    event = ActiveSupport::Notifications::Event.new(*args)
    Rails.logger.debug "SQL (#{event.duration.round(2)}ms)"
  end
end

lease_connectionの有無でリクエスト時間を比較してください。違いがなければ、修正する必要はありません。

アップグレード後にレスポンスが遅くなっているUnicornまたはシングルスレッドPumaのデプロイメントには、lease_connectionを試してください。安全で、7.1の動作に戻ります。

進行中の最適化作業を追跡したい場合は、issue #55728をフォローしてください。