Redmineプラグインのマイグレーションを適用した環境でdb:setup, db:resetを実行してはいけない


確認したバージョン: Redmine 4.0.4, 3.4.11(たぶん他のバージョンでも起きる)

  1. Redmineプラグインのマイグレーションを適用済みの環境で
  2. rails db:setuprails db:resetを実行してしまうと
  3. rails redmine:plugins:migrateを実行するとTable already existsといったエラーが出て失敗するようになる。1

問題を起こさない方法

  • 新しいDBにスキーマを適用したい場合にはrails db:migrate redmine:plugins:migrateを実行する
  • DBのスキーマを作り直したい場合にはrails db:migrate:reset redmine:plugins:migrateを実行する2

問題が起きたあとの修正方法

  • rails db:migrate:reset redmine:plugins:migrateでDBを作り直す
  • schema_migrationsテーブルをいじってプラグインのマイグレーションが適用されたことにする
  • プラグインのマイグレーションで追加されたテーブルなどを手動で消す

のどれかで直る。だいたい上のほうが簡単なはず。

原因

  • db:setupdb:resetdb/schema.rbをソースにしてスキーマとマイグレーションの適用状況を復元する。
    • db/schema.rbのスキーマ情報にはRedmine本体のほか、プラグインのマイグレーションの結果も保存されているので、プラグインが追加したテーブルなどは正しく復元される。
    • しかしマイグレーションの適用状況はRedmine本体のものしか保存されていないので、プラグインのマイグレーションの適用情報は失われる。
  • そのため、次にredmine:plugins:migrateを実行するとプラグインのマイグレーションが二重に適用されてしまう。
    • マイグレーションの中でテーブルやカラムを追加していた場合、同名のテーブルなどがすでに存在するため、エラーが起きてマイグレーションが失敗する。
  • 一方、db:migrate:resetdb:migrateでマイグレーションをひとつずつ実行していくので、schema_migrationsテーブルとスキーマの不整合が起きない。

用語メモ

  • db/schema.rb: DBのスキーマ情報を保存したファイル3
    • (↓以下2つは確認してないけどたぶん合ってるはず)
    • マイグレーションの実行が完了するたびに更新される。
    • 最後に実行が完了したマイグレーションのバージョンを保持している。
  • schema_migrationsテーブル: どのマイグレーションを適用したかを記録しておくためのテーブル。

コードリーディングメモ

再現コード

再現確認用のリポジトリを作った。pushするたびにGitHub Actionsで

  1. 再現用のプラグインを用意する
  2. Redmineのソースをダウンロードして展開する
  3. プラグインとdatabase.ymlをRedmineのディレクトリに配置する
  4. rails db:create db:migrate redmine:plugins:migrateを実行してからrails db:reset redmine:plugins:migrateを実行してエラーメッセージを確認する

という感じのことをやっていて、不具合が再現したらコミットのステータスが緑になる。


  1. プラグインのすべてのマイグレーションがロールバックせずに何度でも実行できるなら問題は起きないが、そういうケースはごくまれだと思う。 

  2. ActiveRecord::ProtectedEnvironmentErrorが発生したらRails 5に入ったDB破壊系taskの防止処理について | 日々雑記などを参照。 

  3. 設定によってはdb/schema.rbではなくdb/structure.sqlになる。