Ridgepoleのverboseオプションについて

Ridgepoleを使っていると、たまによく分からない差分が出てくることがあります。

そういうときにverboseオプションをつけて実行すると、内部でどのような比較を行っているのかが出力されるので、デバッグの助けになります。

例えば次のようなSchemafileがあったとして

create_table "users", force: :cascade do |t|
  t.string "name", null: false
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
  t.index "lower(name)"
end

一回適用したのに、もう一度適用しようとすると差分(とエラー)が出てくる。

$ ridgepole -a
Apply `Schemafile`
-- create_table("users", {})
   -> 0.0109s
-- add_index("users", "lower(name)", {})
   -> 0.0054s

$ ridgepole -a --dry-run
Apply `Schemafile` (dry-run)
remove_index("users", {:name=>"index_users_on_lower_name"})
add_index("users", "lower(name)", {})

[ERROR] Index name 'index_users_on_lower_name' on table 'users' already exists
  1: remove_index("users", {:name=>"index_users_on_lower_name"})
* 2: add_index("users", "lower(name)", {})
    /Users/sugawara/.rbenv/versions/2.4.2/lib/ruby/gems/2.4.0/gems/activerecord-5.2.0/lib/active_record/connection_adapters/abstract/schema_statements.rb:1169:in `add_index_options'

なので--verboseをつけて実行してみます。

$ ridgepole -a --dry-run --verbose
Apply `Schemafile` (dry-run)
# Parse DSL
# Load tables
#   users
# Compare definitions
#   users
   {"created_at"=>{:options=>{:null=>false}, :type=>:datetime},
    "name"=>{:options=>{:null=>false}, :type=>:string},
    "updated_at"=>{:options=>{:null=>false}, :type=>:datetime}},
- :indices=>
-  {"index_users_on_lower_name"=>
-    {:column_name=>"lower((name)::text)",
-     :options=>{:name=>"index_users_on_lower_name"}}},
+ :indices=>{"lower(name)"=>{:column_name=>"lower(name)", :options=>{}}},
  :options=>{}}
remove_index("users", {:name=>"index_users_on_lower_name"})
add_index("users", "lower(name)", {})

[ERROR] Index name 'index_users_on_lower_name' on table 'users' already exists
  1: remove_index("users", {:name=>"index_users_on_lower_name"})
* 2: add_index("users", "lower(name)", {})
    /Users/sugawara/.rbenv/versions/2.4.2/lib/ruby/gems/2.4.0/gems/activerecord-5.2.0/lib/active_record/connection_adapters/abstract/schema_statements.rb:1169:in `add_index_options'

紫の部分が変更前、つまり実際のデータベースからエクスポートした情報、水色の部分が変更後、つまりSchemafileをパースした情報です。

差異を見てみると

  • lower(name)lower((name)::text)として解釈・適用されている
  • 自動的にインデックス名index_users_on_lower_nameが付けられている

ということが分かります。

なのでSchemafileを以下のように修正すると差分は出なくなります。

create_table "users", force: :cascade do |t|
  t.string "name", null: false
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
  t.index "lower((name)::text)", name: "index_users_on_lower_name"
end
$ ridgepole -a --dry-run
Apply `Schemafile` (dry-run)
No change

ちなみに、このようなデータベースの暗黙的な変更(例えばMySQLだと外部キーに自動的にインデックスが貼られる件など)をRidgepole側で吸収するのはなかなか難しいので、できるだけデーターベース様の意向に沿ってください…というのが今のところのポリシーです。