Railsのmigrationのバージョン管理がどうやって行われていて、エラーが発生したときにどう対処すればいいか、調べてみたのでまとめていきます。
すでにRailsに関する記事はweb上に山程あって、この記事に目新しい内容は書かれないですが個人の学習記録として残してます。
新しい技術をちゃんとキャッチアップするには、アウトプットが重要なので。
ruby 3.0.6
rails 7.0.6
postgres 15
railsのmigrationファイルは、/app/db/migrate/
に<日時create_model.rb>のような形式でファイルが作られます。bin/rails db:migrate
を実行したときに、どこまでマイグレーションが実行されているかの管理をデータベースのschema_migrationsのほうで記録して、管理しています。
ですので、schema_migrationsに記録されているバージョンと db/migrate/
に存在するmigrationファイルの整合性が合わなくなると、マイグレーションコマンドを実行するときに予期せぬエラーが発生します。
以下のコマンドでモデル(必要ないけど)とマイグレーションファイルを作ります。
bin/rails g model user
そうすると、 20230806103311_
create_
users.rb
が生成されます。
この状態で実行すると、dbのschema_versionsに以下のようにバージョンが記録されます。
version
----------------
20230806103311
この状態でマイグレーションファイルを削除するなりして、 bin/rails db:rollback
を実行すると、
rails aborted!
ActiveRecord::UnknownMigrationVersionError:
No migration with version number 20230806103311.
Tasks: TOP => db:rollback
(See full trace by running task with --trace)
マイグレーションバージョンに記録されたファイルがありませんとエラーが吐かれます。
これは、schema_versionsに記録されたバージョンのマイグレーションファイルがrailsアプリケーションのディレクトリに存在しないため発生します。
わざと、マイグレーションファイルを削除することはないと思いますが、マイグレーション実行して、そのマイグレーションファイルをgit stashした時でも発生するので気をつけましょう。
次は、削除したマイグレーションファイルを復活させて、dbのschema_versionsに記録されたバージョンを削除してみます。
普通はやらないと思うので、あくまでも実験です。
delete from schema_migrations where version='20230806103311';
DELETE 1
そうすると、schema_versionsからは20230806103311のマイグレーションが実行された記録が消えます。
この状態でrollbackやmigrateすると、どうなるでしょう。
bin/rails db:rollback
すると、特にコンソールにログは出力されないです。
じゃあ、usersテーブルは削除されたのか、dbを覗いてみます。
default=# \dt;
List of relations
Schema | Name | Type | Owner
--------+----------------------+-------+----------
public | ar_internal_metadata | table | postgres
public | schema_migrations | table | postgres
public | users | table | postgres
(3 rows)
usersは残っているのでrollbackは正常終了したように見えて、実行されていないのが見て取れます。
では、次にmigrateします。
bin/rails db:migrate
== 20230806103311 CreateUsers: migrating ======================================
-- create_table(:users)
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
PG::DuplicateTable: ERROR: relation "users" already exists
プロジェクト名/db/migrate/20230806103311_create_users.rb:3:in `change'
Caused by:
ActiveRecord::StatementInvalid: PG::DuplicateTable: ERROR: relation "users" already exists
プロジェクト名/db/migrate/20230806103311_create_users.rb:3:in `change'
Caused by:
PG::DuplicateTable: ERROR: relation "users" already exists
プロジェクト名/db/migrate/20230806103311_create_users.rb:3:in `change'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)
すでにusersテーブルはありますと、エラーが出力されます。
この挙動を見る限り、マイグレーションは実際にテーブル名を見ているのではなく、schema_versionsの中身を見て、どこまで実行されているかを判別しているように見えます。
試しにmigrate statusすると、
>>> bin/rails db:migrate:status
database: default
Status Migration ID Migration Name
--------------------------------------------------
down 20230806103311 Create users
usersテーブルはあるのに、ステータスではdownになっています。
ここらへんの挙動を色々実験した結果、エラー発生したときにどこを見て、どう対処したらいいか理解が深まりました。