【訂正】Railsでconnection数を破綻させずにestablish_connectionで別DBを利用する
前のエントリ(http://d.hatena.ne.jp/raugisu/20120428/1335598633)で、ActiveRecord で別DBに接続するために establish_connection を安易に使うことで発生する地獄について書いた。
んで、その地獄への対策として挙げたもののなかで、一番良いと思われる、「別DBへのコネクションプールを引き受けるクラスを作って継承する」方法を試してみた。
ぶっちゃけ、元ネタ(http://d.hatena.ne.jp/rudeboyjet/20101221/p1 )のほうがキチンとしてるので、そっち見た方が参考になる。
(2012/5/8、コメント欄で「self.abstract_class = true」の設定の指摘をいただいたので訂正しました。ありがとうございました!)
(2012/5/29、訂正部分、打ち消し線で訂正とかしてたんだけど、読みづらいので消しました)
方法は、Railsでモデルを2個(hoge.rb, fuga.rb)作って、適当なコントローラ(foo_controller.rb)のindexアクションで、render :text => (Hoge.count + Fuga.count).to_s
とかやっておしまい。viewまでする必要もなし。
最初は別DBにつながず、/foo/index を何度か実行して、mysqlのshow processlist; でコネクション数を確認する。
mysql> show processlist;
Id | User | Host | db | Command | Time | State | Info |
158 | root | localhost | mysql | Query | 0 | NULL | show processlist |
195 | root | localhost | rider_development | Sleep | 5 | NULL | |
196 | root | localhost | rider_development | Sleep | 5 | NULL |
※DB名はドンマイ。
unicornのワーカー数を2個にしているので、2個(+mysqlコンソールの1個)のコネクションがでる。
まあ、普通。
ここで、もう一個 create database rider_dummy でmysqlにDBを作り、database.ymlにもrider_dummy用の設定を書く「dummy:」で作るといいんじゃないかな。
DBの中身は元のrider_developmentをmysqldumpして書き込めばいいと思うよ。
$ vi database.yml development: adapter: mysql2 encoding: utf8 reconnect: false database: rider_development pool: 5 username: root password: host: localhost dummy: adapter: mysql2 encoding: utf8 reconnect: false database: rider_dummy pool: 5 username: root password: host: localhost
そして、HogeとFugaの中に、establish_connection :dummy って書き込んでやり、再び foo/index を実行して、mysqlのコネクション数を見る。
mysql> show processlist;
Id | User | Host | db | Command | Time | State | Info |
158 | root | localhost | mysql | Query | 0 | NULL | show processlist |
163 | root | localhost | rider_development | Sleep | 22 | NULL | |
164 | root | localhost | rider_dummy | Sleep | 26 | NULL | |
165 | root | localhost | rider_dummy | Sleep | 26 | NULL | |
166 | root | localhost | rider_development | Sleep | 23 | NULL | |
167 | root | localhost | rider_dummy | Sleep | 23 | NULL | |
168 | root | localhost | rider_dummy | Sleep | 23 | NULL |
rider_dummyにつなぎに行くクラスが2個なので、コネクションがunicornワーカーひとつにつき2個できたのがわかる。
rider_developmentへのコネクションは、使ってなくてもできちゃうんだね(でも、pool分のコネクションは作らない模様)
で、ここで、もう一個ActiveRecord::Baseを継承したクラス(dummy_access.rb)をつくる。
$ vi app/models/dummy_access.rb class DummyAccess < ActiveRecord::Base self.abstract_class = true establish_connection :dummy end
DummyAccess に self.abstract_class = true を書き込んでおくと継承用のモデルクラスになるそうで、対応するテーブルなくても大丈夫。
で、establish_connection :dummy と書き込んで、これが別DBに接続しにいくようにする。
さらに、HogeとFugaを DummyAccess を継承するように書き換える。
$ vi hoge.rb class Hoge < DummyAccess end $ vi fuga.rb class Fuga < DummyAccess end
で、ここまでやってから、一度 unicorn を再起動させ、foo/index を何度か実行してから mysql で show processlist; してみる。
mysql> show processlist;
Id | User | Host | db | Command | Time | State | Info |
158 | root | localhost | mysql | Query | 0 | NULL | show processlist |
187 | root | localhost | rider_development | Sleep | 3 | NULL | |
188 | root | localhost | rider_dummy | Sleep | 3 | NULL | |
189 | root | localhost | rider_development | Sleep | 3 | NULL | |
190 | root | localhost | rider_dummy | Sleep | 5 | NULL |
みごと、別DBであるrider_dummyへの接続が1個になった\(^O^)/
これで一安心。
(2012/5/29追記)
当初、「self.abstract_class = true」の設定を知らなくて、DummyAccessでもダミーのテーブルをset_table_nameでセットしたり、それを継承したHogeクラス、Fugaクラスでもわざわざset_table_nameでセットする必要がある、と書いていました。
でもさすがはRuby On Rails、人気のフレームワークだけあって、そんなアホなことはなかったですね!
コメント欄でアドバイスくださった通りすがりの方、マジでありがとう!
すごい助かりました。
いくつかある地獄の一つを解決できました。