りゅうじの学習blog

学習したことをアウトプットしていきます。

2021年9月23日パーフェクト Ruby on Rails

複数のデータベースを扱う

Webアプリケーションの規模が大きくなるとしばしば応答速度の低下が問題になる事があり、ボトルネックになりやすいのはDBアクセスに関する部分という事がよくあります。
そういった問題を解決し、複数のDBを利用するgemパッケージとしてOctopusswitch_pointなどがありましたが、Rails6.0からはRails標準機能として複数DBに対応しました。

複数DB対応で提供している機能

  • 複数のprimaryデータベースと、それぞれに対応する1つのreplica
  • モデルでのコネクション自動切り替え
  • HTTP verbや直近の書き込みに応じたprimaryとreplicaの自動スワップ
  • マルチブルデータベースの作成、削除、マイグレーション、やり取りを行うRailsタスク

これらの機能がユースケースと合致しない場合はOctopusswitch_pointの使用を検討すると良いです。

2つのDBへ接続するには

config/database.ymlを記述する

default: $default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS"){5} %>
  timeout: 5000
development:
  primary:
    <<: *default
    database: db/development.sqlite3
  sub:
    <<: *default
    database: db/development_sub.sqlite3
    migrations_paths: db/sub_migrate
test:
  略
production:
  略

database.yml内のdevelopmentといった環境変数の下の階層として、各種DBの名称(どういう用途のDBかわかりやすい名称にする)を記述し、その中に接続情報を記述します。

そして、片方のDBにだけマイグレーションパスを指定します。これで異なるテーブルの定義を反映させられます。

この段階でdb:createを実行するとそれぞれのデータベースを作成可能です。

またdb:create:subとすると、subの方だけのデータベースを作成する事もできます。
この時点ではモデルを操作しても片方のDBしか参照できないので、特定のモデルと特定のDBを接続させたい場合は各モデルクラスでestablish_connectionを利用します。(establishは設立するという意味です)
役割を明確にするためにestablish_connectionを指定productionちあ抽象クラスを作成し、そのクラスを継承したモデルクラスを利用すると良いです。

# app/models/sub_base.rb
class SubBase < ApplicationRecord
  self.abstract_class = true
  establish_connection :sub
end

# app/models/author.rb
class Author < SubBase
end

これでAuthorモデルを通じてレコードを作成した場合、subデータベースへ書き込みを行うようになりました。

self.abstract_class = trueが何かわからなかったので調べました。

ActiveRecord::Base を継承したクラスをモデルとして作成すると、Rails はそのクラス名に対応したデータベースのテーブルを自動的に探そうとします。対応するデータベースのテーブルを用意しない場合は、self.abstract_class = true を書く必要があります。ActiveRecord::Base を継承したクラスを作成し、さらにそのクラスを継承させたい場合に self.abstract_class = true が書かれるようです。

今回の場合Author.createでデータを入れようとした場合に通常ではエラーが起きてしまうため、self.abstract_class = trueを記述する必要があります。

ひとこと

複数のデータベースを作成することはしたことがないのですが、いずれ使うときのためにひとつひとつ踏みしめて学んでいきます。

参照

[Rails] self.abstract_class = true の意味と挙動