Rails(ActiveRecord)でデータベースへのコネクションプーリングをさせなくする

ActiveRecordを使ったRailsアプリは,デフォルトでデータベースへの接続をプールするようになっています.
ActiveRecordユーザとしては待ちわびたぜ!的な機能らしいのですが,設定等でこれをdisableすることが出来ず,LVS+keepalivedを介する場合にはロードバランシングが最初の接続時にしか為されずがなかなか厄介です.

対策として思いついたのは

  • プールした接続を早い周期で捨てる
  • LVS+keepalivedではなく,MySQL Proxyでバランシングする(Proxyへの接続はプール)
  • そもそも接続をプールさせない

くらいでした.

どうするのがセオリーなのかと調べてみると

など4年近く前に議論されていて,"あー,高速道路あるなあ"と,コネクション確立のコストを調べる前に「プールさせない」という選択が出来てしまいました.本来は根っこから調べるべきだとは思いますが,今回はサービスレベルで何が変わるかというのを端的に調べることにしました.

ただ,ActiveRecordのコネクションプーリングについては,実装されることを喜んでいる人はネットで多く見つかるのですが,この機能を潰したいと思う人の声にはなかなか巡り会えませんでした.大きなRailsサイトはいっぱいあると云うのに...そもそもAR使うの?とかMySQLProxy噛まそうよってことなのでしょうか.

最初はdatabase.ymlとかで

pool: 0

とか

wait_timeout: 0

とかやったら出来るんじゃないかと楽をするつもりで試みましたが,エラーだらけになってしまいました.(ちなみにどちらもデフォルト値は5です)

読めよ.コード.

ARでコネクションプーリングを行っている箇所

ActiveRecord-2.3.5だと

activerecord-2.3.5/lib/active_record/connection_adapters/abstract/connection_pool.rb

  • checkout
  • checkin

といったメソッドですね.コメントアウトの説明にもcheckinでpoolにコネクションのオブジェクトconnを入れて,checkoutで取り出しているよと書いてありますね.
安易ではありますが,checkinのときにプールせずにコネクションを捨てて,checkoutで新しくコネクションを確立させればプーリングを機能させなくて済みそうです.

def checkout
    ActiveRecord::Base.send(spec.adapter_method, spec.config.dup) # same def new_connection
end

def checkin(conn)
    conn.disconnect!
end

みたいな感じ.

ちょっと不安だなと思っていたらgithubでも巡り会いました.余計なメソッド潰したりもしていらっしゃいますね.
verify!(activerecord-2.3.5/lib/active_record/connection_adapters/abstract_adapter.rb)で,接続されているかを確認し,そうでなかったら再接続もさせています.

実施

$RAILS_HOME/lib以下にdisable_connection_pooling.rbとでもして上のコードを置いて,$RAILS_HOME/config/environment.rbでrequireさせました.

適用後


同時に存在するMySQLのスレッド数が激減しました.PassengerでRailsアプリを複数のプロセス動かして,それぞれが5プールずつ持っていて,それがまた複数のサーバでDBに接続していた状態からの変化なのでかなりエコですね.
PCサイトだとコネクション確立のコストもそんなに気にするものでもなく,サービスのレスポンスタイムに影響はありませんでした.ただ,モバイルの場合は全体の処理がそんなに多くない為か,アクセスが集中する時間帯などではレスポンスタイムが悪化してしまいました.