本番環境で動いているRailsアプリケーションのDBにおいて、問題に緊急対応するために直接ALTER_TABLE
してしまったようなとき、Railsアプリケーション側ではどんな対応をすればいいか、というお話。
レアケースかもしれないし意外とあるかもしれない。
僕は実際に1年ほど前に開発していたRSSリーダーのときに経験したことがある。ある日の夜中、サービスにアクセスが集中してデータベースのレスポンスがめちゃくちゃ悪くなったとき、僕は普通に寝ていた。いや、それまでの対応で力尽きていたと言っていい。
サーバーがうんともすんとも言わないそんな状況の中、ヘルプで入ってくれている人がスキーマを調べてインデックスが足りない(!)ということに気がついたらしい。翌朝起きてみると「とりあえず直接インデックス追加してなんとかしました」という連絡が来ていた。
インデックス足りないとか!しょぼすぎるミスなのはさておいてひとまず問題は解決した。あれ?でも直接DBいじっちゃったらRails側のマイグレーションと整合性とれないぞ?
そういうときの対処法です。
そもそもマイグレーションとは?
そもそもrake db:migrate
は何をやっているのかというと、db/migrate
以下にあるファイルに書いてあるDBに対する変更処理をファイルの日付順に実施していくというものだ。このとき、DBのschema_migrations
というテーブルにこれまでに適用したマイグレーションの番号(日付の文字列)を持っている。
rake db:migrate
ではこのテーブルを見に行って未適用のマイグレーションを実行するような感じになっている。その都度schema_migrations
は更新される。
具体的な対応方法
Railsアプリケーション側
% rails generate migration AddIndexToHoge
みたいなコマンドで新しいマイグレーションを生成する。そして、手作業で変更してしまった処理に相当する処理をマイグレーションファイルには書いておく。
例えば僕の経験したケースでは、プロダクションのデータベースでインデックスの追加を行った。そういうときはこのような感じのマイグレーションを書く。
class AddIndexToHoge < ActiveRecord::Migration
def change
add_index :hoges, :fuga, name:'idx_hoge_name'
end
end
name
を敢えて指定しているのはプロダクションDBで行った変更で追加したインデックスの名前がRailsのデフォルトとは異なっていたから。
さて、これでRailsアプリケーション側での準備は整った。
プロダクションDB側
先ほど作ったマイグレーションの内容はすでに手動で適用済みであるため、もう一度マイグレーションが走るとエラーになってしまう。そこで先程作ったマイグレーションの番号をschema_migrations
に手動で追加してやる。
> INSERT INTO schema_migrations (version) VALUES(20131031113857);
これであたかもrake db:migrate
でマイグレーションを適用したような結果にできる。
一応これらの対策で、プロダクションDBに対して手動で変更を行ってしまったとしてもRailsアプリケーションの流れに戻せるということになる。