Rails6からActiveRecordで複数のデータベースが利用できるようになったので試す
参考:Railsガイド|Active Record で複数のデータベース利用
やったこと
- 複数のデータベースを作成
- commonデータベースとschoolデータベース
- primary/replicaデータベースを利用
- commonデータベースのreplicaと、schoolデータベースのreplica
- GETリクエストはreplicaが呼び出されることを確認
- 異なるデータベースのテーブル間のJOINはできないことを確認
- ruby 2.7.1
- rails 6.0.3.4
$ git clone https://github.com/youichiro/rails-multiple-db-sandbox.git
$ cd rails-multiple-db-sandbox
# config/database.ymlのpasswordやportなどを編集
$ bin/rails db:create
$ bin/rails db:migrate
$ bin/rails db:seed
schoolデータベースにTeacher
モデルを追加する手順の例
$ bin/rails g model teacher name:string --database school
Running via Spring preloader in process 54763
invoke active_record
create db/school_migrate/20201030135726_create_teachers.rb
create app/models/teacher.rb
invoke test_unit
create test/models/teacher_test.rb
create test/fixtures/teachers.yml
--database
でschoolデータベースを指定することでdb/school_migrate
ディレクトリにマイグレーションファイルが作成される
デフォルトではApplicationRecord
を継承しているが、Schoolデータベースを使用したいのでSchoolBaseを継承するように変更する
- class Teacher < ApplicationRecord
+ class Teacher < SchoolBase
end
# 全てのマイグレーションファイルを適用する場合
$ bin/rails db:migrate
# schoolデータベースのマイグレーションファイルのみを適用する場合
$ bin/rails db:migrate:school
replicaを用意することでPOST, PUT, DELETE, PATCHのリクエストはprimaryに書き込み、GET, HEADリクエストはreplicaから読み込むようになる
これを確認するために、arproxyを使用してクエリのログにデータベースの接続状況を表示するようにする
Gemfile
にgem arproxy
を追加してbundle install
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
curlからリクエストを送信してログを見ると、呼び出されたデータベースとwritingかreadingかが表示される
index
$ curl localhost:3000/schools
show
$ curl localhost:3000/schools/1
create
$ curl -X POST -H 'Content-Type: application/json' -d '{"name": "school2"}' localhost:3000/schools
update
$ curl -X PUT -H 'Content-Type: application/json' -d '{"name": "school1(updated)"}' localhost:3000/schools/1
destroy
$ curl -X DELETE http://localhost:3000/schools/3
studentsテーブルをgradeテーブルにJOINする場合
Grade.joins(:students).where(name: 'grade1')
発行されるSQL
SELECT `grades`.*
FROM `grades`
INNER JOIN `students` ON `students`.`grade_id` = `grades`.`id`
WHERE `grades`.`name` = 'grade1
studentsテーブルをusersテーブルにJOINしようとした場合
User.joins(:students).where(name: 'ogawa')
発生するエラー
ActiveRecord::StatementInvalid (Mysql2::Error: Table 'rails_app_common_development.students' doesn't exist)