はじめに
ridgepole はCookpad社が使用しているスキーマ管理ツールです。
スキーマ情報は通常のRailsの schema.rb
で使用されているDSLで書かれた Schemafile
ファイルで管理され、ここで書かれた定義とDBのスキーマの定義と差分があった場合、DBに差分が適用されるという感じです。
これを使用することによりDBのスキーマを直接変えてからスキーマファイルを生成したり、 rails g migration
を実行してできたマイグレーションファイルの増加をなくすことができたり、DBのスキーマ変更をgitのログなどで見ることができたりします。
今回はこの ridgepole
を本番に適用するときに役立ちそうなメモを残したいと思います。
Schemafile
の作成
まず既存のDBから Schemafile
をファイルを生成します。 Schemafile
を作成するために以下のコマンドを叩きます。
$ bundle exec ridgepole -c config/database.yml --export --split --output db/schemas/Schemafile
-c
でデータベースのコンフィグファイルを指定し、 --export
で Schemafile
ファイルを出力するようにします。また、 --split
を使用することで各テーブルごとに *.schemas
ファイルを作成し、 Schemafile
で require
することもできます。
ただ --split
をすると、外部キーが一番最後に出力されるファイルにまとまってしまうのが謎です。Railsでmigrationファイルを作ってmigrationする時出力される schema.rb
も同じなのでそのあたりの仕様に従っているのかもしれません。
このコマンドを叩くときは何も本番環境で実行必要はなく、開発環境で実行すればよいです(開発環境と本番環境でスキーマに差分はないはず・・・)。
外部キーの問題
実施に Schemafile
を作成したらまずはデータの入っているステージング環境でテストします(まぁ当たり前の話です)。
ここで多くの人が躓くのは外部キーに名前をつけていない問題です。railsでマイグレーションファイルを作るとき、外部キー制約を記述するのに references
を使うと思うのですが、これだと明示的にキーの名前をつけることができず、 出力した Schemafile
を使って ridgepole
を実行するときエラーになってしまいます。
[ERROR] Foreign key name in `テーブル名` is undefined
Railsでは references
で自動的につけた外部キーの名前は fk_rails_ランダムな文字列
になっています。普通に ridgepole
を用いて Schemafile
を出力すると外部キーの名前は 出力されません 。出力させるためには --dump-with-default-fk-name
オプションを付ける必要があります。このオプションを付けると fk_rails_ランダムな文字列
が付与されて外部キーの定義が出力されます。
ところがここで出力した Schemafile
を使っていざ適用すると、
[ERROR] Foreign key name in `テーブル名` is undefined
がまた出てしまいます。
ソースを見てみた所、 Schemafile
と実際のDBのスキーマの差分を取るところでエラーになっているらしいです。
実際に差分を取っているところは以下の場所です。
ここで expected_definition
は読み込んだ Schemafile
なので外部キーの定義は書いてあります。問題は current_definition
で、これが今のDBに定義されているスキーマを ActiveRecord::SchemaDumper.dump
で取得して比較対象を作っています。ここでダンプしてくるときにどうやら外部キーの定義が抜けてしまうようです。
これをなんとかするためにはいったん外部キーを張り直すことで対処しました。
remove_foreign_key :from, :to
add_foreign_key :from, :to, name: 'fk_rails_from_to'
こういうファイルを愚直に作り、外部キーに明示的に名前をつけていきます。
schema.rb
に、
add_foreign_key "from", "to", name: "fk_rails_from_to"
のように name
が追加されれば大丈夫です。
愚直につけていくしか方法がないように自分は思ったのですが、もし他になにかいいやり方がアレばそちらを採用したいぐらいだるい作業です。
外部キーを張り替えてから再度 ridgepole
を実行し、問題なく完了すればOKです。
デプロイ
実際にコマンドで適用できることを確認したら実際のデプロイタスクに組み込みます。
capistrano
を使用していたらpluginでよしなにやってくれることを期待してしまいがちですが、あまり良いのがないのでrakeタスクを作ってそれを実行するようにデプロイスクリプトに組み込むのが良いと思います。
rakeタスクを作ってしまえば heroku
でデプロイする時に buildpack-ruby-rake-deploy-tasks で実行すれば自動化できて大変良いです。
herokuにDeployしたタイミングでdb:migrateを実行したい
ステージングで確認しているとは言え capistrano
を実行して適用するのは緊張感のある作業です。
やってみてどうだったか
増殖するマイグレーションファイルは一掃できましたし、スキーマの変更も楽にできるのが非常に良い感じです。 raild g migration
のような便利タスクはないので自分でファイルを作る必要はありますが、それもgeneratorを作ってしまえば解決できる問題だと思います。
ちょっと気になるところとしては、Cookpad社ではDBで直接 ALTER TABLE
してから ridgepole
でスキーマを出力し、プルリクするというフローでやっているのに対し、自分たちは *.schemas
ファイルを追加したり編集したりするフローにしているところで、あまり意図通りの使い方をしていないんじゃないかというところです。
今のところ特に問題は起きていないので大丈夫そうですが、少し気をつけたほうがいいかもしれません。