Rails6.0のmultiple databaseを使ってみた
Railsアプリケーションのデータベースに置いて、冗長化・負荷軽減・高速化などの目的で複数データベース対応を行うことも少なくはありません。
これまで、データベースの切り替え・書き込みや読み込みの違いによる切り替えにはswitch_pointやestablish_connectionを使用していました。
このようなこともあって、先日リリースされたRails6では、複数データベース対応がサポートされました。
そこで、従来の switch_point / establish_connection から Rails6 の multiple database に実装を置き換えてみました。
multiple databaseの設定
まずはdatabase.ymlを2段落から3段落に書き換えます。replicaとして使用する場合には replica: true を付け加えます。また他のデータベースとして、anotherを追加しました。
# database.yml
production:
main:
<<: *default
main_replica:
<<: *default
host: <%= ENV['DB_HOST_REPLICA'] %>
replica: true
another:
<<: *another
migrations_paths: db/another_migrate
another_replica:
<<: *another
host: <%= ENV['ANOTHER_DB_HOST_REPLICA'] %>
replica: true
次に各モデルで上記の設定を反映させます。writingは更新系、readingは参照系を表しています。他のデータベースを読み込む設定は、別途、抽象クラスを作成し、こちらを子モデルに継承させます。
# application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
connects_to database: { writing: :main, reading: :main_replica }
end
# another_base.rb
class AnotherBase < ApplicationRecord
self.abstract_class = true
connects_to database: { writing: :another, reading: :another_replica }
end
# hoge.rb
class Hoge < AnotherBase
end
これだけでは、自動でコネクションの切り替えが行われません。自動切り替えを有効にするには、アプリケーションの設定ファイルに以下の行を追加する必要があります。
# production.rb
config.active_record.database_selector = { delay: 2.seconds } # 2秒以内であれば、自分が書き込んだものを読み取る
config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
これで複数データベース対応は終わりです。
接続状況をログで確認する
複数データベース対応を行ったものの、現在実行中のクエリがどちらのデータベースに接続しているかが分からず、不安になりました。そこでActiveRecordのクエリログに接続状況を表示するようにしました。
ログの出力にはarproxyを使用します。
# config/initializers/arproxy.rb
if Rails.env.development? || Rails.env.test?
require 'multiple_database_connection_logger'
Arproxy.configure do |config|
config.adapter = 'mysql2'
config.use MultipleDatabaseConnectionLogger
end
Arproxy.enable!
end
# lib/multiple_database_connection_logger.rb
class MultipleDatabaseConnectionLogger < Arproxy::Base
def execute(sql, name = nil)
role = ActiveRecord::Base.current_role
name = "#{name} [#{role}]"
super(sql, name)
end
end
これでクエリログに [writing] または [reading] が出力されるようになります。
まとめ
複数データベース対応を switch_point や establish_connection から Rails6 のmultiple database に置き換えを行いました。
またクエリログに接続中のデータベース状況を出力するようにしました。
サードパーティのgemを用いるより、公式の機能を用いたほうが安心できますね。
参考
https://railsguides.jp/active_record_multiple_databases.html
https://tech.raksul.com/2018/08/03/rails_switch_point/
この記事が気に入ったらサポートをしてみませんか?