rails migration から ridgepole に移行したので移行時のメモ
Ridgepole 導入の理由
rails migration では migration の度に migration ファイルの作成して、実行後は db/schema.rb が自動更新される。
システムが未成熟なこともあり、ガンガン migration を行うので、下記のような問題が出てきた。
- migration ファイルが大量に出来てしまって管理が大変
- db/schema.rb を github に上げ忘れること多発
- db/schema.rb でコンフリクト多発
- db:rollback 使うことがほとんどなかった
無駄な作業の煩わしさから解放されるために、ridgepole という gem が色々な記事でオススメされていたので、導入してみようと思います。
Ridgepole インストール
Gemfile に以下を追加する
gem 'ridgepole'
bunlder でインストール
$ bundle install --path vendor/bundle
導入手順
https://github.com/winebarrel/ridgepole#usage
ここに書いてある通りなので簡単に移行できるが、今後を考えてテーブル毎に Schema ファイルを分割して運用することを念頭に移行を進めてみる。
Schemafile の作成
まず、現状のデータベースから ridgepole 用の Schemafile を作るのだが、今後の管理を考えてテーブル毎にファイルを分割して作成する。
試験用に既存データベースとして、users と posts というテーブルを用意した。
$ bundle exec ridgepole -c config/database.yml --export --split -o db/Schemafile
Export Schema
write `db/posts.schema`
write `db/users.schema`
write `db/Schemafile`
中身を見てみるとこんな感じ。
# -*- mode: ruby -*-
# vi: set ft=ruby :
require 'posts.schema'
require 'users.schema'
create_table "posts", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPRESSED", force: :cascade do |t|
t.string "name"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["name"], name: "index_posts_on_name"
end
# -*- mode: ruby -*-
# vi: set ft=ruby :
create_table "users", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPRESSED", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["name"], name: "index_users_on_name"
end
add_foreign_key "posts", "users", column: "user_id"
注意したいのは、外部制約キー (add_foreign_key) が users にあること。
どうも、こちらの記事によると、アルファベット順で一番最後のファイルに集約される模様。
ここは好みによると思いますが、add_foreign_key は db/posts.schema に移しました。
- schema ファイルを git で管理するので、export することは稀
- posts のスキーマを確認する際に、posts.schema に書かれていないと存在を忘れる & 再定義してしまい、ミスに繋がる
適用 & 確認
Schemafile を適用してみて変更がないことを確認する。
ローカルの開発環境でテストをするので、-E development を付ける。
$ bundle exec ridgepole -c config/database.yml -E development -f db/Schemafile --apply
Apply `db/Schemafile` (dry-run)
No change
rake task 作成
毎回、上記のようなコマンドを実行するのは大変なので rake task で簡単にします。
こちらの記事を参考に作成しました。
namespace :ridgepole do
desc "Apply ridgepole schemafile"
task apply: :environment do
ridgepole('--apply')
end
desc "Export ridgepole schemafile"
task export: :environment do
ridgepole('--export')
end
private
def config_file
if Rails.env.development?
'config/database.yml'
elsif Rails.env.staging?
'config/database.staging.yml'
elsif Rails.env.production?
'config/database.production.yml'
else
raise 'no configuration specified'
end
end
def ridgepole(*options)
command = ['bundle exec ridgepole --file db/schemas/Schemafile', "-c #{config_file}", "-E #{Rails.env}"]
system (command + options).join(' ')
end
end
これで環境に関わらず、下記のコマンドで Schemafile を適用できます。
$ bundle exec rails ridgepole:apply
db:seed の代用 rake task
ridgepole でスキーマを更新した場合、db:seed による rake task を実行しても、下記のように pending migration による警告が発生して実行できません。
$ bundle exec rails db:seed
You have 2 pending migrations:
20190703015353 CreateUsers
20190704105400 CreatePosts
Run `rails db:migrate` to update your database then try again.
そのため、ridgepole:apply と同様に rake task を定義しておくと良いと思います。
db/seeds.rb は ruby スクリプトで定義していることが多いので、そのまま移植するだけで済むことが多いと思います。
production 環境への適用
手動で production 環境に migration していた場合は下記のコマンドを実行する。
$ bundle exec rails ridgepole:apply RAILS_ENV=production
CI/CD 連携している場合は、スクリプト等でこれまで
bundle exec rails db:migrate
をしていた箇所を
bundle exec rails ridgepole:apply
に差し替えれば良いと思います。
ゴミ掃除
db/schema.rb や db/migrations 以下の migration ファイルを削除しておきましょう
Top comments (0)