Railsで使うデータベースはマイグレーションで組み立てていきますが、状況によってはマイグレーションに失敗して、マイグレーション前でもマイグレーション後でもない闇に落ちていってしまうことがあります。
MySQLの特性
マイグレーションの実行に失敗した場合、Railsは今までにした操作をロールバックして、マイグレーション前の状態に戻ろうとします。これはPostgreSQLやSQLiteの場合なら無事ロールバックできるのですが、MySQLの場合、CREATE TABLE
やALTER TABLE
を実行すると強制的にトランザクション外となってしまうため、成功した操作をロールバックすることもできず、中途半端な状態のDBができあがってしまいます。
失敗例
マイグレーションに失敗する例としては、
- 列名の間違い(すでにある列を追加しようとした、ない列を削除しようとした、ない列にインデックスを張ろうとしたなど)
- すでにあるものと同名のテーブルやインデックスを追加しようとした
- 単純な書き間違い1
などミスが大半ですが、人間はミスをするものである以上、根絶することは難しいです。
予防策
予防策としてはいくつか考えられますが、副作用もあります。
- そもそもMySQLではなく、DDLもロールバックできるDBエンジンを使う(これだけで選ぶほど大きな問題ではない)
- 1つのマイグレーションで1つの書き換えだけ行なって、中途半端な状態を作らせない(マイグレーションが増える)
- 先にテスト環境でマイグレーションを流して、ちゃんと通ってから開発・本番などに持っていく
脱出方法
では、不幸にしてマイグレーションの狭間に落ちてしまった場合は、どうすればいいのでしょうか。この場合、状況としては
- マイグレーションの進捗状況の記録(
schema_migrations
)…前のマイグレーションまで実行 - DBの状態…最新のマイグレーションの途中
ということになっています。手法としては、以下のいくつかが考えられます。
- DBを直接叩いてマイグレーション前の状態に戻して、(誤記は修正して)もう一度
rake db:migrate
- マイグレーションファイルから成功した分以外を消しておいて、
schema_migrations
に手で追加、そして残りは別のマイグレーションに回す - マイグレーションファイルの中で成功した分ををコメントアウトして
rake db:migrate
、終了後にコメントアウトを戻す - 手作業でDBと
schema_migrations
を整え、成功したことにしてしまう
個人的には、(特段の事情がなければ)1がいいかなと考えています。
-
一回、
remove_index
をdrop_index
と書いてしまって動かない、ということがありました。 ↩