ZAICOでは、Android・iOS・Rubyエンジニアを絶賛募集中です! 詳しくは、採用ページをご覧ください。
こんにちは、ZAICO開発チームです。
私の地元は結構雪が降る地域で、今年は寒さが厳しかったですが、ようやく過ごしやすくなってきました。春が待ち遠しいです。
さて、今回はRails6.1のいわゆる複数データベース機能について触れたいと思います。
これに関しては様々な記事で紹介されていますが、今回はコネクション切り替え、すなわちprimary/replicaの挙動を試してみます。
前提条件
- Ruby 2.6.6
- Rails 6.1.3
- APIモードを利用
データベース・replicaの設定
他記事では複数データベースも設定されていますが、今回はコネクション切り替えだけを試したく、シンプルに書きます。
database.yml
development: zaicodb: <<: *default database: zaicodb_development zaicodb_replica: <<: *default database: zaicodb_development replica: true
$ bundle exec rails db:create
データベース接続先の設定
app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base self.abstract_class = true connects_to database: { writing: :zaicodb, reading: :zaicodb_replica } # 追記 end
コネクションの自動切り替えを有効にする
config/application.rb
# 追記 config.active_record.database_selector = { delay: 2.seconds } config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
モデルやマイグレーションを用意(検証用)
$ bundle exec rails g model User name:string
$ bundle exec rails db:migrate
コントローラを用意(検証用)
class UsersController < ApplicationController def index render json: User.all end def create user = User.new user.name = params[:name] user.save! render json: user end # ...その他省略 end
primary/replicaどちらを参照しているかログで確認できるようにする
各種記事でも紹介がある、arproxyというgemを利用します。
Gemfile
gem 'arproxy' # 追記
config/initializers/arproxy.rb
class QueryTracer < Arproxy::Base def execute(sql, name=nil) role = ActiveRecord::Base.current_role name = "#{name} [#{role}]" super(sql, name) end end Arproxy.configure do |config| config.adapter = "mysql2" # A DB Apdapter name which is used in your database.yml config.use QueryTracer end Arproxy.enable!
動作確認
$ curl localhost:3000/users/
ちゃんとreadingと出ていて、replicaを読んでいますね。
$ curl -X POST -H ‘Content-Type: application/json’ -d ‘{“name”: “zaico taro”}’ localhost:3000/users
こちらはwriting、つまりprimary(zaicodb)を参照しています。
Railsガイドにはこのように記載されています。
アプリケーションがPOST、PUT、DELETE、PATCHのいずれかのリクエストを受け取ると、自動的にprimaryに書き込みます。書き込み後に指定の時間が経過するまでは、アプリケーションはprimaryから読み出します。アプリケーションがGETリクエストやHEADリクエストを受け取ると、直近の書き込みがなければreplicaから読み出します。
ということなので、これが本当にそうなのか確認してみます。
GETメソッドで呼ばれるアクションで無理やりINSERTを試す
app/controllers/users_controller.rb
# 追記 def save user = User.new user.name = params[:name] user.save! render json: user end
config/routes.rb
Rails.application.routes.draw do resources :users do get :save, on: :collection # 追記 end end
動作確認
$ curl localhost:3000/users/save?name=yahoo
おー!ActiveRecord::ReadOnlyErrorということで、ちゃんと弾かれています。replicaをしっかり読もうとしているんですね。
Rails6.1ということで、水平シャーディング機能なども試したいですが、それは別の機会でやってみます。
以上、モデルでのコネクション自動切り替えを試した内容でした。