DEV Community

m-yoshimo
m-yoshimo

Posted on

Rails migration から ridgepole に移行した

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)