はじめに
追記
ActiveRecord v6.0から組み込みで複数データベース接続機能が提供されたため、現在ここで紹介してライブラリ側から「ActiveRecord v6.1以降はサポートしません。」と明記されています。よほどの事情がない限り、ここで紹介しているライブラリではなく、ActiveRecord組込の複数DB接続機能を利用しましょう。
https://github.com/eagletmt/switch_point#maintenance-notice
Rails5では、複数DB接続が公式ではサポートされていません(Rails6からは公式サポートされるそうです)。
しかし、読み込み系の処理はリードレプリカに、書き込み系の処理はmasterにって処理を振り分けたいという需要があったので、その時にやったことの備忘録として投稿します。
Modelを介す場合
SwitchPointを使用しました。
Switch Pointの設定
Gemfile
gem 'switch_point'
production:
# 省略
production_slave:
# 省略
SwitchPoint.configure do |config|
config.auto_writable = true
database_config = {
readonly: :"#{Rails.env}_slave",
writable: :"#{Rails.env}"
}
config.define_switch_point :setting, database_config
end
SwitchPoint::writable!(:setting)
SwitchPoint::writable!(:setting)
と書いておくことでデフォルトの接続先がmasterを向きます。
使い方
Slaveに向ける
Hogemodel.with_readonly do
data = Hogemodel.where(id: 1)
end
Hogemodel.with_readonly do
aaa = Hogemodel.where(id: 1)
aaa.title = 'フロントエンドのプロ直伝! CSS余白設定の三原則(+線の引き方)'
Hogemodel. with_writable do
aaa.save!
end
end
Slaveから読み込んでMasterに書き込む
Hogemodel.with_readonly do
aaa = Hogemodel.where(id: 1)
aaa.title = 'フロントエンドのプロ直伝! CSS余白設定の三原則(+線の引き方)'
Hogemodel.with_writable do
aaa.save!
end
end
Slaveに対して生SQLを発行したい場合
ときにはORMでは表現しにくい複雑なクエリをslaveに対して発行したいときもあると思います。
module SlaveDb
extend ActiveSupport::Concern
class << self
# slaveDBとコネクションを確立するための情報を返す
def get_slave_db_connection
db_config = Rails.configuration.database_configuration
db_env = "#{Rails.env}_slave"
ActiveRecord::Base.mysql2_connection(
adapter: db_config[db_env]['adapter'],
host: db_config[db_env]['host'],
username: db_config[db_env]['username'],
password: db_config[db_env]['password'],
database: db_config[db_env]['database']
)
end
end
end
Rails(MySQLに限る)は新しくDBとのコネクションを呼ぶ際に、内部的にmysql2_connectionというメソッドを叩いているため、database.ymlから取得した、slaveへの接続先を渡してこのメソッドを叩きます。
サクッと使いまわせるように、今回はconcernに処理を書きました。
呼び出し側では、
include SlaveDb
begin
# きっと複雑なクエリ
query <<-"EOL"
select * from ...
EOL
con = SlaveDb.get_slave_db_connection
data_list = con.select_all(query).to_hash
ensure
con&.disconnect!
end
こんな感じで使えます。
似たようなメソッドにestablish_connection
というメソッドもあるのですが、こちらは呼ばれると同時に、既存のコネクションを全部切断してしまうので、使用しませんでした。