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)