Rails 6のコネクションプールの無効化

Posted by Shugo Maeda on July 29, 2021 · 1 min read

NaClの前田です。

諸般の事情でRailsのコネクションプールを無効化したかったのですが、Rails 6では既存のgemやモンキーパッチでは動作しなかったため、古いモンキーパッチをベースにRails 6用のモンキーパッチを作成しました。

コネクションプールとは

RailsではDB接続・切断をリクエストを処理する時に毎回行う代りに、リクエストの処理が終わった際にDB接続オブジェクトをコネクションプールに保持し、以後のリクエスト処理ではコネクションプールからDB接続オブジェクトを取得して使い回す仕組みになっています。

プールに保持するDB接続オブジェクト数の上限はconfing/database.ymlで以下のように指定します。

default: &default
  # ...  
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

環境変数RAILS_MAX_THREADSはアプリケーションサーバのスレッド数です。上限を超えたDB接続オブジェクトの取得を行おうとすると、他のスレッドがDB接続オブジェクトをコネクションプールに返却するまで処理が待たされてしまうため、アプリケーションによって必要となる接続数に合わせて設定しておく必要があります。

コネクションプールの無効化

接続コストを収えることができるため、通常のRailsアプリケーションではコネクションプールの仕組みは有用ですが、アプリケーションの特性によってはDB接続数が極端に増えてしまったりする問題があります。

Railsの標準機能ではコネクションプールの無効化はできないので、gemを作成されたりしているようですが、Rails 6での事例をなかなか見かけません。最近はAmazon RDS Proxyを利用されていたりするのでしょうか。

今回は古いモンキーパッチをベースにRails 6用のモンキーパッチを用意して期待通り動作することを確認しました。

require "active_record"

module ActiveRecord::ConnectionAdapters::NoConnectionPool
  def checkout(_checkout_timeout = nil)
    # プールからのチェックアウト時に毎回接続を行う
    c = new_connection
    begin
      c.pool = self
      c.verify!
      c
    rescue Exception
      # 例外発生時のリークを避けるため例外を捕捉して切断する
      c.disconnect!
      raise
    end
  end

  def checkin(conn)
    # プールへのチェックイン時に毎回切断を行う
    conn.disconnect!
  end
end

class ActiveRecord::ConnectionAdapters::ConnectionPool
  prepend ActiveRecord::ConnectionAdapters::NoConnectionPool
end

まとめ

Rails 6でコネクションプールを無効にする方法が見つからなかったため、Rails 6用のモンキーパッチを作成しました。

Rails 6ではコネクションプールの無効化に何か問題があったり、他によりよい方法があるようでしたら教えていただけるとありがたいです。