Railsで複数データベースを操作したいならOctopusが便利な件🐙
2020年10月31日
なぜOctopus?
とあるマルチテナント構成のシステムの管理画面の開発にあたり、複数のデータベースに接続して、システム全体で使用するマスタデータの操作や顧客情報の閲覧するという機能要件がありました。
複数DBに接続して操作できる方法として
複数DBに接続して操作できる方法として
- Octopus
- switch_point
- Rails6の複数データベース接続
の3つが選択肢として上がりましたが、マルチテナントが動的に増えていくことや、接続情報をテーブルで管理するなどの要件もあって、Octopusを採用しました。
Octopusをシンプルに使うのであれば非常に簡単です。
Octopusをシンプルに使うのであれば非常に簡単です。
シンプルな使い方
Gemfile
gem 'ar-octopus'
$ bundle install
database.yml
octopus: shards: history_shards: aug2009: adapter: mysql host: localhost database: octopus_shard2 aug2010: adapter: mysql host: localhost database: octopus_shard3 aug2011: adapter: mysql host: localhost database: octopus_shard4 country_shards: canada: adapter: mysql host: localhost database: octopus_shard5 brazil: adapter: mysql host: localhost database: octopus_shard6 russia: adapter: mysql host: localhost database: octopus_shard7
User.using(:canada).create!(:name => 'oi')
などと指定するだけで使うことができます。
たえだ、今回は動的にDB接続先が増えていくので、テーブル情報からDB接続情報を取得する必要がありました。
たえだ、今回は動的にDB接続先が増えていくので、テーブル情報からDB接続情報を取得する必要がありました。
ちょっと複雑な使い方
今回登場するテーブル(イメージ)
# Table name: db_config # # id :bigint(8) not null, primary key # db_name :string # db_host :string # db_user_name :string # db_password :string # Table name: companies # # id :bigint(8) not null, primary key # name :string # mail_address :string # tell :string # Table name: users # # id :bigint(8) not null, primary key # company_id :bigint(8) # name :string # mail_address :string # password :string
config/initializers/octopus.rb
module Octopus class SelectSchema attr_accessor :shards, :master, :companies def initialize @shards = {:master => {}, :company => {}} @master = DbConfig.find_by(db_name: "master") @companies = DbConfig.group(:db_name).where.not(db_name: "master") end def execute begin select_master select_companies Octopus.setup do |config| config.environments = [:production, :development, :test] config.shards = shards end rescue ActiveRecord::StatementInvalid => e puts e end end def select_master return nil unless master.present? shards[:master]['admin'] = { :host => master.try(:db_host).gsub(/:(\d+)\//, ""), :adapter => 'mysql2', :database => master.try(:db_name), :username => master.try(:db_user_name), :password => master.try(:db_password), :port => 3306 } end def select_companies return nil unless companies.present? companies.each do |shard| shards[:company][shard.db_name] = { :host => shard.try(:db_host).gsub(/:(\d+)\//, ""), :adapter => 'mysql2', :database => shard.try(:db_name), :username => shard.try(:db_user_name), :password => shard.try(:db_password), :port => 3306 } end end end end Octopus::SelectSchema.new.execute
この設定後、rails serverを再起動すると、DB接続情報のテーブルを参照して、octopusが使用できます。
なぜ、moduleにして、呼び出しているかというと、Ocotpusはアプリケーションを再起動しないと、DB情報が更新されないため、
システム利用中に動的にDB接続情報が増えた場合に対応できなかったので、ユーザーログイン時やdb_configが増えたタイミングで、このmoduleを再実行する形にしました。
あとは、このような指定方法で使うことはできますが、、、
db_name = DbConfig.find_by(company_id: 1).db_name Octopus.using(:db_name) do User.create(:name => "User") Client.create(:name => "Client") end
この方法だと、使用する都度、DB名を取得しないといけないので、concernに処理を共通化しました。
app/models/concerns/octopus_using.rb
app/models/concerns/octopus_using.rb
module OctopusUsing extend ActiveSupport::Concern def run_using_company(company_id) db_configs = DbConfig.using(:master).find_by(company_id: company_id) unless db_configs.nil? Octopus.using(db_configs.db_name) do yield end end end end
このように共通化して、使いたいときは、modelかcontrollerにincludeしてから
run_using_company(company_id) User.find(1) end
これで、company_idを指定してあげるとそのcompanyのdb情報にアクセスできるようになります。
注意点
長時間接続していないと、DBコネクションが切断される問題があるらしいので、次の記事を参考にして、コネクションの貼り直しを行いました。
https://qiita.com/sachaos/items/fb83c773c3a78a9c7202
ただ、記事の方法だと、ApplicationControllerのbefore_actionですべてのアクション前にコネクションを張り直しています。
記事通りに設定していたところ、Linuxのスレッドが増える問題が発生したので、ログインに成功した場合のみコネクションを張りなすようにしています。
ただ、Rails6になって、複数データベース操作ができるようになったので、今後は、OctopusからRails6の標準機能を使用することが多くなっていくかもしれません。
https://qiita.com/sachaos/items/fb83c773c3a78a9c7202
ただ、記事の方法だと、ApplicationControllerのbefore_actionですべてのアクション前にコネクションを張り直しています。
記事通りに設定していたところ、Linuxのスレッドが増える問題が発生したので、ログインに成功した場合のみコネクションを張りなすようにしています。
ただ、Rails6になって、複数データベース操作ができるようになったので、今後は、OctopusからRails6の標準機能を使用することが多くなっていくかもしれません。
コメント
最新記事
2024年09月12日
JINの夏休み -ミッション達成に向けた種まきの旅- in20242024年09月06日
地方創生のリーディングカンパニーを視察してきた2024年08月30日
共働き夫婦の増加と生活の変化2024年08月22日
未経験エンジニアが語る!~地方支社の魅力と可能性~2024年08月15日
大学生にインターンシップを実施しました!2024年08月08日
地方創生ツアーを実施しました!【後編】2024年08月01日
地方創生ツアーを実施しました!【前編】2024年07月25日
IT企業が新規事業として農業に取り組んで失敗するまで【後編】2024年07月17日
【社員インタビュー】エンジニアの休日の過ごし方って?2024年07月11日
中学生が職場体験に来てくれました! in. 小林支社
過去記事(年月別)
- 2024年09月(2)
- 2024年08月(4)
- 2024年07月(4)
- 2024年06月(4)
- 2024年05月(4)
- 2024年04月(2)
- 2024年03月(4)
- 2024年02月(3)
- 2024年01月(3)
- 2023年12月(3)
- 2023年11月(2)
- 2023年10月(3)
- 2023年09月(1)
- 2023年08月(2)
- 2023年07月(3)
- 2023年06月(5)
- 2023年05月(3)
- 2023年04月(4)
- 2023年03月(5)
- 2023年02月(5)
- 2023年01月(4)
- 2022年12月(3)
- 2022年11月(4)
- 2022年10月(4)
- 2022年09月(6)
- 2022年08月(2)
- 2022年07月(4)
- 2022年06月(4)
- 2022年05月(5)
- 2022年04月(7)
- 2022年03月(7)
- 2022年02月(3)
- 2022年01月(4)
- 2021年12月(2)
- 2021年08月(1)
- 2021年06月(2)
- 2021年04月(1)
- 2021年02月(1)
- 2020年12月(2)
- 2020年11月(3)
- 2020年10月(3)
- 2020年07月(2)
- 2020年02月(3)
- 2020年01月(2)
- 2019年12月(1)
- 2019年09月(1)
- 2018年10月(1)
タグ