Octopusというライブラリを用いるとActiveRecordでsharding/replicationが実現できる。
DB構成
ユーザデータを持つDB
ホスト: userdb.localhost
データベース名: user
replication: しない (アプリケーションでは読み取りしかしないので最初からslaveのみ参照するようになっている)
アプリケーションのデータを持つDB
ホスト: app.localhost
データベース名: app_{RAILS_ENV}
replication: する
何が問題か
OctopusがActiveRecord::Base#connection
とActiveRecord::Base#connection_pool
を乗っ取って、クエリを監視して適宜、shardもしくはmaster/slaveへ投げるようになる。
Octopusを使ってreplicationをする設定 (config/shards.ymlにreplicated: true
) の場合、masterはconfig/database.yml, slaveはconfig/shards.ymlを見る。
replicationをしたくない場合 (config/shards.ymlにfully_replicated: false
) であっても、Octopusがクエリ監視をするため、モデルで個別の接続先を指定していてもOctopusが上書きしてしまう。
対処法
目には目を、歯には歯を、メタプログラミングにはメタプログラミングを。
alias_method_chain
で上書きしている。 (cf. lib/octopus/model.rb:L80)
alias_method_chain(original_method, feature_name)
で{original_method}_with_{feature_name}
と{original_method}_without_{feature_name}
というメソッドを定義する。
{original_method}_without_{feature_name}
がオリジナルの実装なのでこれをさらにaliasすればよい。
class User < ActiveRecord::Base
if defined?(Octopus::Model::InstanceMethods) && self < Octopus::Model::InstanceMethods
alias_method :connection, :connection_without_octopus
alias_method :connection_pool, :connection_pool_without_octopus
end
establish_connection("user_db_#{Rails.env}")
self.table_name = :user
end