DEV Community

m-yoshimo
m-yoshimo

Posted on

2 2

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 ファイルを削除しておきましょう

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay