このエントリは Classi Advent Calendar 2016 8日目の記事です。
そういえばいまだに転職エントリーを書いていませんが7月に転職をしていましてClassiのファースト社員となりました 💪
複数DB on Rails
Classiではセキュリティやら何やらの関係でみんな大好き複数DB on Railsになっています。
R/W Splittingやシャーディングではなく、1つの共通DBとテナント毎のDBがあるマルチテナンシー(?)構成です。
class User < ActiveRecord::Base has_many :entries # (共通DBへの接続情報) end class Entry < ActiveRecord::Base belongs_to :user # (テナントDBへの接続情報) end User.connection # => 共通DBのdatabase名のconnection Entry.connection # => テナントDBのdatabase名のconnection
といった感じで使用するmodelに応じて適切にコネクションが切り替わるようになっています
異DB間association
基本的にはテナント側で閉じる世界観になるのですが、稀によく頻繁につらいことに共通側とテナント側のモデル間でassociationが貼られていたりもします。
ここで問題になるのがPreloadingの処理です。
preload
を使う分にはそれぞれのモデルに対して問い合わせが走るのでコネクションもいい感じに切り替わってくれるのですが、joins
やeager_load
の場合はそうも行きません😱
# OK User.preload(:entries) # NG User.joins(:entries) User.eager_load(:entries) #=> Mysql2::Error: Table 'entries' doesn't exist
そもそもそんなこと出来るの?って思っていたんですが↓の記事を見た感じ、MySQLの場合テーブル名の前にdatabase名を入れることで可能とのこと(もちろん同じDBサーバ内にそれぞれのdatabaseが入ってる必要はあります)
MySQL5:異なるデータベース間のテーブル結合 DB名:db1 TABLE名… - 人力検索はてな
たとえばこんな感じ?
SELECT common.users.*, tenant_1.entries.* FROM common.users JOIN tenant_1.entries ON common.users.id = tenant_1.entries.user_id
Classiの場合はこの条件を満たしていたので、後はRailsが発行するクエリのテーブル名の前に[database名].
を追加するだけで上手くいきそうです。
たとえばtable_name_prefixを使う
困ったときのStack Overflowを頼ったところ以下のようなものが。 Rails 3 - Multiple database with joins condition - Stack Overflow
テーブル名の先頭にprefixを付けるとこで、database名を追加している感じ。 ということで試してました。
class User < ActiveRecord::Base + def self.table_name_prefix + "common." + end end
class Entry < ActiveRecord::Base + def self.table_name_prefix + "tenant." + end end
早速JOINを試してみます
[0] pry(main)> User.joins(:entries).to_sql SELECT `common`.`users`.* FROM `common`.`users` JOIN `tenant`.`entries` ON `common`.`users`.id = `tenant`.`entries`.user_id
ということで上手くいきました。
実際にはbatchなどでtenantを切り替えたりする処理があったりしてtable_name_prefixの更新などが必要になってきますが、
ActiveRecordの reset_table_nameを使えばそれもなんとかなりそうです。
と、ここまで書きましたが深淵なる理由によりこのコードはClassiの内部ではまだ動いていません 😫
本当はこの処理をいい感じにgem化して今日までにリリース!!とかっこいい感じにしたかったのですが、自分が 怪盗業 や プロデューサー業 で多忙だったため進捗ダメでした…
あと1回Advent Calendarが回ってくる予定なのでそれまでに何らかの形にできればなと思ってます
こんな風に複数DBが大好き!!、または日本の教育を変えたい!!というRailsエンジニアの方はぜひClassiに話を聞きに来てください!
明日は @kenjiskywalker の「🍣」です