Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails: 複数DBでridgepoleコマンドを実行するときは -s オプションを指定しよう

Active Record で複数のデータベース利用 - Railsガイドにある通り、複数DBを使うときは database.yml をこんな風に3層にしますよね。

default_primary: &default_primary
  adapter: mysql2
  encoding: utf8mb4
  collation: utf8mb4_bin
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV['PRIMARY_DATABASE_USER'] || 'root' %>
  password: <%= ENV['PRIMARY_DATABASE_PASSWORD'] %>
  host: <%= ENV['PRIMARY_DATABASE_HOST'] || 'localhost' %>

default_animal: &default_animal
  <<: *default_primary
  host: <%= ENV['ANIMAL_DATABASE_HOST'] || 'localhost' %>
  username: <%= ENV['ANIMAL_DATABASE_USER'] || 'root' %>
  password: <%= ENV['ANIMAL_DATABASE_PASSWORD'] %>

development:
  primary:
    <<: *default_primary
    database: <%= ENV['PRIMARY_DATABASE_NAME'] || 'project_development' %>
  animal:
    <<: *default_animal
    database: <%= ENV['ANIMAL_DATABASE_NAME'] || 'project_animal_development' %>

...

例えば、development環境の primary に、 db/Schemafile をapplyしたいとします。
ここで単純に以下のようにするとエラーになります。

$ bundle exec ridgepole -c config/database.yml -E development -f db/Schemafile --apply
[ERROR] database configuration does not specify adapter
        /usr/local/bundle/gems/activerecord-6.1.7.2/lib/active_record/connection_adapters/abstract/connection_pool.rb:1198:in `resolve_pool_config'

この結果はある意味当然で、 primaryanimal のどちらにapplyすればいいか指定していないため、ridgepoleが database.ymldevelopment キー直下を直接読みに行って、adapterが見つからずエラーになっているわけですね。
どうすればDBを指定できるのかしばらく調べていたのですが、ridgepoleのbinコマンドのソースコードを眺めていたら、 spec_name というそれっぽい変数がありました。

# https://github.com/ridgepole/ridgepole/blob/1.2/bin/ridgepole

ARGV.options do |opt|
  # ...
  opt.on('-s', '--spec-name SPEC_NAME') { |v| spec_name = v }

  # ...

  client = Ridgepole::Client.new(Ridgepole::Config.load(config, env, spec_name), options) if config

これじゃない?

# https://github.com/ridgepole/ridgepole/blob/1.2/lib/ridgepole/cli/config.rb

module Ridgepole
  class Config
    class << self
      def load(config, env = 'development', spec_name = '')
        # ...

        if parsed_config.key?(spec_name.to_s)
          parsed_config.fetch(spec_name.to_s)
        else
          parsed_config
        end
      end

      # ...

これじゃん。

ということで、 spec_name を指定すればいいようです。
-s オプションの指定でOKです。

$ bundle exec ridgepole -c config/database.yml -E development -s primary -f db/Schemafile --apply

このPRで入ったオプションのようですね。
今になって思うと、リポジトリ内を「multiple database」で検索すれば情報が見つかる話でした。


この -s オプションですが、 ridgepole --help を見ても説明は薄めです。

    -s, --spec-name SPEC_NAME

説明がほとんどないということは、複数DBを使うときに spec_name を指定することが自明であり、 spec という単語にymlのキーやデータベース名という意味がある(そして自分がそれを理解していなかった)、という話なのかと思ったのですが、調べてみてもそういう意味にはたどり着けませんでした。
ということで、自分のようにオプションが分からない人に向けてこの記事を書いています。

命名経緯についてもう少し調べようと、 -s オプションが追加されたPRを眺めてみると、当時はRails内部で spec_name という変数を使ってデータベース名を管理していたことが分かりました。
おそらくこの名前を引っ張ってきたのでしょう。

# https://github.com/rails/rails/blob/1c24f01596d8d50bf10cb9d205b5d2c08ea9d6c6/activerecord/lib/active_record/database_configurations.rb#L33-L35

    # * <tt>spec_name:</tt> The specification name (i.e. primary, animals, etc.). Defaults
    #   to +nil+. If no +env_name+ is specified the config for the default env and the
    #   passed +spec_name+ will be returned.

ちなみに、Railsの方の変数名は、2023年2月現在では単なる name になっています。

# https://github.com/rails/rails/blob/0614aa6e0aeb11cc19e1ede13da8b602191c00ea/activerecord/lib/active_record/database_configurations.rb#L34-L36

    # * <tt>name:</tt> The db config name (i.e. primary, animals, etc.). Defaults
    #   to +nil+. If no +env_name+ is specified the config for the default env and the
    #   passed +name+ will be returned.

歴史を感じる一幕でした。


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。