環境
Amazon Linux
# rake about
sinatra 1.4.7
ruby 2.3.1p112
Environment staging
Database adapter mysql2
Database schema version 201610xxxxxxxxx
背景
ステージング環境は複数のメンバーが弄ってます。いつの間に、migrationはこんな感じなってしまった。
# RACK_ENV=staging bundle exec rake db:migrate:status
database: xxx_staging
Status Migration ID Migration Name
--------------------------------------------------
up 20160826080803 Create xxx
up 20160929044948 Create xxx
up 20160929045000 Create xxx
up 20160929045041 Create xxx
up 20161111073930 A(add column is_prime)
up 20161116033102 B
up 20161116105100 C
確かに全部migration全部実行されているように見えますが、確認してみたら、[A]は実行されてないようです。以下のエラーが出た。
ActiveModel::UnknownAttributeError - unknown attribute 'is_prime' for table_a
すぐdb/structure.sqlファイルを確認したら、table_aにis_primeはなかった。つまり、中間のmigration(A)を実行されてない
対策
やり直す
もちろん、DBを消して、全てのmigrationをやり直すのは一番速いんですが、データをエクスポートして、インポートしないといけなくて、ちょっと面倒ですし、後発生する可能せが低いですが、もし本番そういうことが起きたら、どう対応すべきかな。
mysqldump -uroot db_name > dump.sql
RACK_ENV=staging bundle exec rake db:drop && RACK_ENV=staging bundle exec rake db:create && RACK_ENV=staging bundle exec rake db:migrate
mysql -uroot db_name < dump.sql
別の方法
そのmigrationだけ実行させてみた
RACK_ENV=staging bundle exec rake db:migrate:redo VERSION=20161111073930
したら、以下のエラーが出た。
Mysql2::Error: Can't drop 'is_prime'; check that column/key exist:
つまり、redoは[down & up]という実装になってます。
ロールバックしてた
RACK_ENV=staging bundle exec rake db:rollback STEP=4
↑と同じエラーが出た。
Mysql2::Error: Can't drop 'is_prime'; check that column/key exist:
一歩づつにやると[A]まではできて、こんな感じになります。
# RACK_ENV=staging bundle exec rake db:migrate:status
database: xxx_staging
Status Migration ID Migration Name
--------------------------------------------------
up 20160826080803 Create xxx
up 20160929044948 Create xxx
up 20160929045000 Create xxx
up 20160929045041 Create xxx
up 20161111073930 A(add column is_prime)
down 20161116033102 B
down 20161116105100 C
まあ、そうだね!そもそも今is_primeは存在してないですから。
migrationファイルを外してみた
migrationはdb/migrationの下のファイルを順番実行するはずだ。だが、二回ロールバックして、20161111073930_xx_xx.rbを外して、migrationを実行して、その後、20161111073930_xx_xx.rbを戻して、実行したら、うまくいけるじゃないと思ったが、何も変わらなかった。
でも、db/migrationのしたのmigrationファイルを実行されたかどうかというの記録きっと別の所にあるということが分かった。ファイル名だけって決めることじゃない。
migrationテーブル修正
mysql> use auction_staging;
Database changed
mysql> show tables;
+---------------------------+
| Tables_in_auction_staging |
+---------------------------+
| ar_internal_metadata |
| xxx_results |
| xxx |
| schema_migrations |
| .... |
+---------------------------+
11 rows in set (0.00 sec)
mysql> select * from schema_migrations;
+----------------+
| version |
+----------------+
| 20160826080803 |
| 20160929044948 |
| 20160929045000 |
| 20160929045041 |
| 20161111073930 |
| 20161116033102 |
| 20161116105100 |
+----------------+
12 rows in set (0.00 sec)
なるほど、20161111073930というrowを消せた、きっとロールバックができるはず!
select * from schema_migrations where version = '20161111073930';
delete from schema_migrations where version = '20161111073930';
二回ロールバックして、migrationしたら、直しました!
RACK_ENV=staging bundle exec rake db:rollback STEP=2
RACK_ENV=staging bundle exec rake db:migration
最後
@jhoshina 色々アドバイスしていただき、ありがとうございます!助かりました!