LoginSignup
19
16

More than 5 years have passed since last update.

複数のDBにまたがるRailsの運用方法まとめ

Posted at

Railsはデフォルトでは一つのアプリケーションに対して一つのデータベースが対になってますが、サービスを運用している過程で他のサービスで使われているDBに跨った実装をしたくなります(APIを生やせばいいじゃんというツッコミは無しで)。

今回はそんな場合にどうRailsで対応するかについてまとめてみました。

どうやるか

以下ではデフォルトのDBをMainDB、他のサービスで使われているDBをSubDBとし、
database.ymlに定義された設定は下記とします。

default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5

development:
  <<: *default
  host: hoge
  database: hoge
  username: hoge
  password: hoge

development_sub:
  <<: *default
  host: fuga
  database: fuga
  username: fuga
  password: fuga

test:
  <<: *default
  host: foo
  database: foo
  username: foo
  password: foo

test_sub:
  <<: *default
  host: bar
  database: bar
  username: bar
  password: bar

~production, stagingも同様

DBの生成

SubDBからdumpしたsqlテーブル定義ファイルをdbディレクトリの下に置きます。

rails_app
├─ app
├─ db ── migrate
│  ├──── schema.rb
│  ├──── seeds.rb
│  ├──── sub_db.sql

そしてlib/tasksの下にsb_db.sqlを実行するためのrakeタスクを設定します。

namespace :sub do
  task :set_sub_config do
    require 'active_record'
    @config = YAML.load_file(Rails.root.join('config', 'database.yml'))
    @structure_path = Rails.root.join('db', 'sub_db.sql')
    @db = ActiveRecord::Tasks::MySQLDatabaseTasks.new(@config["development_sub"])
  end

  namespace :db do
    task create: :set_sub_config do
      @db.create
    end

    task structure_load: :set_sub_config do
      @db.structure_load(@structure_path, nil)
    end

    task drop: :set_sub_config do
      @db.drop
    end
  end
end

set_sub_configは何をしているかというと、active_recordにdatabse.ymlの中の必要な設定を渡す操作をしています。set_sub_configにdevelopmentかtestといった引数を渡して@config["#{arg}_sub"]といった分岐処理を書いてもいいかもしれません。

active-recordに設定を渡したあとは、dbの名前空間でデータベースの操作をします。
rails sub:db:createでSubDBのデータベースを作成し、
rails sub:db:structure_loadでテーブルを作成し、
rails sub:db:dropでデータベースを削除します。

今回はMySQLが接続先として想定しているためMySQLDatabaseTasksからDB操作メソッドを呼んでいますが、PostgreSQLが接続先の場合は、PostgreSQLDatabaseTasksを指定してください。

modelとの連携

class Sub < ActiveRecord::Base
  self.abstract_class = true
  establish_connection("#{Rails.env}_sub".to_sym)
end
class Sub::Account < Sub
end

modelに接続先DBをSubDBに切り替えたクラスSubを作り、接続先がSubDBのmodelは全てこのSubを継承するようにして作成します。

テスト環境のための設定

主にFactoryGirlとDatabaseCleanerについての設定です。
MainDBにつなぐAccountモデルと、SubDBにつなぐSub::Accountモデルがあるとします。

FactoryGirl.define do
  factory :account do
  end
end
FactoryGirl.define do
  factory :sub_account, class: Sub::Account do
  end
end

通常はクラス名=ファクトリ名なのでaccountモデルに対応するファクトリ名は:accountとなるのですが、上記のようにSubDBにつなぐ側には明示的にclass名を指定してあげてください。

また、SubDB側にマスターデータがある場合、毎テスト毎にマスターデータを再構築するのは効率が悪いので、DatabaseCleanerの操作をスキップする必要があります。

以下のコードははSubDBのaccountsテーブルにマスターデータがあるという想定のもとでDatabaseCleanerの設定をしています。

config.before(:suite) do
  DatabaseCleaner.strategy = :truncation
  DatabaseCleaner[:active_record, connection: :sub_test].strategy =
    :truncation, { except: [:accounts] }
  FactoryGirl.reload
end

config.around(:each) do |example|
  DatabaseCleaner.cleaning do
    example.run
  end
end

まとめ

開発環境で気軽にDBを再構築できるようになったり、E2Eでテスト管理できるようになり、開発上の負担が大変減りました。
また、こちらの方々のように

といった方法も検討して良いかもしれません(自分の場合は他サービスのDBのほんの一部のみ使うことを想定していたため大味すぎると判断し見送りました)。

19
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
16