マルチDB設定をしたRails6で、普段参照するリードレプリカとも異なる、分析クエリ用リードレプリカからデータを読み出すにはどうすればよいかという話。
Rails6.0のマルチDB設定はリリース後に微妙にインターフェイスが変更されていて今となっては推奨されない情報に行きあたってしまうことがあるので(というか僕自身が行きあたったので)、半分メモとして書き残しておきます。
要するに
- ActiveRecord::Baseを継承した抽象クラスのconnects_toで、writingロールでもreadingロールでもない第三のロールを作る
- 第三のロールは普段は使用されない
- 重いSELECTをするときだけActiveRecord::Base.connected_toのロール指定で第三のロールを使う
詳しく
Rails6.0リリース当初のRailsガイドには、「ActiveRecord::Base.connected_toにdatabaseキーワード引数を渡すと、そのdo~endブロックの中はdatabaseキーワード引数で指定したデータベースを用いる」と書いてありました。 github.com
しかし、これは既に非推奨ならびに削除の未来が決まっていて、英語版のRailsガイドからは既に当該の記述は削除されています。 api.rubyonrails.org github.com
Railsガイドの日本語訳ではまだ残っていたので、該当部分を削除するPRは出しました。
ではActiveRecord::Base.connected_toのdatabaseキーワード引数を使わずに実現するにはどうすればいいかといえば、 話は単純でApplicationRecordでconnects_toにデータベース設定を渡すときにwritingでもreadingでもない第三のロールを同時に設定すればよさそう。
database.ymlが
production: primary: host: プライマリインスタンスのFQDN user: root adapter: mysql replica: host: リードレプリカインスタンスのFQDN user: root_readonly adapter: mysql replica: true analyze: host: 分析用インスタンスのFQDN user: root_readonly adapter: mysql replica: true
となっているとき、
class ApplicationRecord < ActiveRecord::Base self.abstract_class = true connects_to database: { writing: :primary, reading: :replica, analyzing: :analyze } end class Post< ApplicationRecord 中略 end
というようにconnects_to で三つのロールを指定します。
Rails6 のマルチDB機能は特に指定しなければwritingロールを書き込み、readingロールを読み込みに使用するので、analyzingロールは普段使用されません。
そして普段参照するリードレプリカへ発行すると重すぎてサービス自体に影響が出てしまうようなときにだけ、
posts = ActiveRecord::Base.connected_to(role: :analyzing) do Post.all end
などとすればこの時だけ分析用インスタンスへSELECTが発行されるようになります。