LoginSignup
4
5

More than 5 years have passed since last update.

Rails + apartment gem + multi-server を試してみた

Last updated at Posted at 2018-05-17

apartment gem にはテナント毎に接続先 DB を切り替える multi-server 機能があります。
テナントが増えてきたら DB 負荷を分散できるように、本機能を試してみました。

環境

Ruby 2.4.4
Rails 5.1.5
MySQL 5.7

database.yml にテナント DB 接続設定を追加する

今回は調査が目的なので、メイン DB とテナント DB は同一としました。

development_tenant_group_1:
  <<: *default
  pool: 1
  username:
  password:
  host: mysql
  socket: /tmp/mysql.sock

apartment の設定を変更する

README の設定例を見ると、テナント名が固定なら Hash で、動的なら lambda で記述する必要があるようです。
作成しているサービスはユーザーの申込みに応じてテナントが動的に増えていくので、lambda で記述する方式としました。

db_config = Rails.configuration.database_configuration["#{Rails.env}_tenant_group_1"]
config.tenant_names = -> { Team.all.each_with_object({}) { |e, hash| hash[e.tenant_name] = db_config } }

config.use_schemas = false
config.with_multi_server_setup = true

Puma から Unicorn に変更

動作確認をしてみると、DB 接続でエラーを吐くようになり正常に動作しませんでした。
Issues を漁ってみると、そもそもスレッドセーフでないため Puma 環境では動作しない、ということがわかりました。
Rails + unicorn に変更して対処しました。

Apartment#db_config_for のモンキーパッチを適用

ログを見ていると、Tenant#switch の度に Team を全件取得する SQL が流れていました。
どうも先程設定した config.tenant_namesswitch の度に実行されている模様。

調査したところ Apartment#db_config_for が呼ばれるために lambda が実行されてしまうようなので、モンキーパッチを適用してしのぎました。

テナント名から DB 接続情報を取得する箇所をモンキーパッチ
module Apartment
  def self.db_config_for(tenant)
    # TODO: 将来的に tenant 毎に DB を切り替える
    Rails.configuration.database_configuration["#{Rails.env}_tenant_group_1"]
  end
end

その他発生した問題

  • Mac 環境で RSpec を流すと途中で DB エラーが発生する (未解決)
    • CircleCI や Docker 上で実行する分には発生しなかった
  • パフォーマンスが落ちた
    • ローカルで開発していると時折遅くなる
    • NewRelic 上のレスポンスタイムの数値で 40-60ms -> 100-120ms くらいまで落ちた
    • チューニングして 60-80ms まで回復
4
5
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
4
5