マイグレーションファイルは慎重に作成し、ミスったらやり直すくらいの覚悟じゃないとHerokuにpushしてheroku run rails db:migrate
したときに沼にハマるよという話。
##発生している問題
ローカル環境ではキチンと動くのに、(SQLite->Postgresへの対応などの基本的な設定を済ませてから)Herokuにデプロイするも、一部のページだけが500(Internal Server Error)となる。フロントでは「We're sorry, but something went wrong.」なので、404の「The page you were looking for doesn't exist.」とは違って内部的なエラーが発生している。
heroku logs --tail
でログを見ても以下の500エラーと書かれただけで何が原因か全然分からないかった。
2020-06-23T09:27:34.223359+00:00 app[web.1]: [8ee3a40d-5258-46f4-8742-48c47f28b01f] Category Load (1.2ms) SELECT "categories".* FROM "categories"
2020-06-23T09:27:34.474847+00:00 heroku[router]: at=info method=GET path="/categories/1/show" host=my-app.herokuapp.com request_id=8ee3a40d-5258-46f4-8742-48c47f28b01f fwd="27.81.17.54" dyno=web.1 connect=1ms service=263ms status=500 bytes=1827 protocol=https
##結論
汚いマイグレーションファイルを残したままpushしてheroku run rails db:migrate
していないか疑え!
以下の記事の【1】と同じだった。
https://qiita.com/kaorioka09jm/items/9167ba77d1edf7addac2
ローカル開発中にDB作りを試行錯誤したことが原因
Herokuにデプロイした全ページが500とかならheroku run rails db:migrate
してないなど他の理由もあるが、一部のページだけ表示されない場合はDBのマイグレーションが上手くいっていない場合が多いようだ。
結局、ローカルのマイグレーションファイルを削除したり、作り直したりしても、一度Herokuにpushしてしまったマイグレーションファイルがあると、データベースをResetしてもDestroyしても、そこからheroku run rails db:migrate
してもデータベースは綺麗にならない。
そのため筆者の場合はHerokuからアプリを削除して再度pushし直す羽目になった。
マイグレーションファイルの注意点
- マイグレーションファイルは一度しか
rails db:migrate
できない - 生成される
schema.rb
を直接編集しちゃダメ(と思われる) - テーブルの削除なども都度マイグレーションファイルを作ってやること
そのため以下のようなマイグレーションファイルの生成は慎重に行う必要がある。もし間違ってカンマを入れたり本来integerのデータ型を間違ってtextに指定してしまったなどだけで、変なマイグレーションファイルが生成される。
// 例
rails g migration CreatePostCategoryRelations post_id:integer category_id:integer
// 間にカンマを入れたり、コロンの位置が違ったり、integerの綴りが違ったりと間違えがち
それを挽回するために別のマイグレーションファイルを作って‥なんてやってローカルでDBをぐちゃぐちゃ構築しても、それをpushした後にheroku run rails db:migrate
を実行すると過去のマイグレーションファイルを順に実行するのでおかしくなる。
今回はPosticoの使用により、DBの中のあるテーブルのcategory_id
カラムがStringになっていたことに気付けたので、Heroku側のDB生成が上手くいってないことに気付けた。
DBがおかしいと気付くには、rails db:migrate
した際のログをめんどうでもちゃんと読んで確認するのも役立つと思う。
- キチンと標準のテーブル+マイグレーションファイルで指定したテーブルが作成されているか?
- 各テーブルのカラムのデータ型は正しいか?
##テーブルの削除
Heroku上に正しいマイグレーションファイルをpushするには、まずローカルのマイグレーションファイルを綺麗にする必要がある。しかし、間違った記述のあるマイグレーションファイルを削除し、正しいマイグレーションファイルを作成してrails db:migrate
しても、以下のように怒られて上書きはできない。
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
SQLite3::SQLException: table "post_category_relations" already exists
そのためテーブルの削除用のマイグレーションファイルを作成してrails db:migrate
し、再度新たなテーブル作成用のマイグレーションファイルを作成してrails db:migrate
する必要がある。
$ rails generate migration RemoveMyTable
class RemoveMyTable < ActiveRecord::Migration[6.0]
def change
drop_table :my_table
end
end
その後、テーブルの削除などに使った余分なマイグレーションファイルを削除する。こうして綺麗なマイグレーションファイルだけが残ったので、これをまっさらなHerokuにpushしてからheroku run rails db:migrate
をしてやると綺麗なデータベースができる。
##考えたこと
###SQLiteとPostgreSQLの互換性の問題ではないか
Herokuでは SQLite が使えないので、production環境だけ PostgreSQL を使用するのはHerokuで最初につまづくポイントだが、これの変更によって使えないメソッドがあるのではと考えた。
が、そんなことはなく、そもそもマイグレーションファイルとはSQLを書かずに、かつ言語の種類(SQLiteかPostgreSQLか等)によらずにDBを操作するためのものである。
今回はControllerの以下のincludes
やwhere
がPostgreSQLでは使えないのでは、といった馬鹿な勘違いをしてしまった‥orz.
def show
@posts = Post.includes(:categories).where(post_category_relations: { category_id: @category })
end
※Gemのpgloaderを使うなどの記事を見つけたが、それはMySQLのデータをPostgreSQLのデータに移行するものであって、メソッドを変換とかではない。
###push後のWarningされてる部分に問題があるのではないか
pushした後に以下のWarningが出るが今回は無関係。またこれらは、どれも特別な使い方をしない限りは、初心者は無視していい警告だ。
remote: ###### WARNING:
remote: You set your `config.assets.compile = true` in production.
remote: You set your `config.active_storage.service` to :local in production.
remote: We detected that some binary dependencies required to
remote: No Procfile detected, using the default web server.
##マイグレーションファイルの理解を深めるのに役立ちそうな記事
そもそもマイグレーションとは何なのか👇
https://qiita.com/sobameshi0901/items/f5823fec20e2dd78d9c4
ロールバックという手法もあるらしい👇
https://qiita.com/params_bird/items/3d503b5c9f8097df147a