失敗したRails移行のロールバック


82

失敗したレールの移行をどのようにロールバックしますか?rake db:rollback失敗した移行は元に戻されると思いますが、いいえ、前の移行(失敗した移行から1を引いたもの)をロールバックします。そしてrake db:migrate:down VERSION=myfailedmigration、どちらも機能しません。私はこれに数回遭遇しました、そしてそれは非常にイライラします。問題を再現するために私が行った簡単なテストは次のとおりです。

class SimpleTest < ActiveRecord::Migration
  def self.up
    add_column :assets, :test, :integer
    # the following syntax error will cause the migration to fail
    add_column :asset, :test2, :integer
  end

  def self.down
    remove_column :assets, :test
    remove_column :assets, :test2
  end
end

結果:

== SimpleTest:移行============================================= ========
--add_column(:assets、:test、:integer)
   -> 0.0932s
--add_column(:asset、:error)
レーキが中止されました!
エラーが発生し、それ以降の移行はすべてキャンセルされました。

引数の数が間違っています(2対3)

わかりました、ロールバックしましょう:

$ rake db:rollback
== AddLevelsToRoles:元に戻す============================================= ==
--remove_column(:roles、:level)
   -> 0.0778s
== AddLevelsToRoles:元に戻しました(0.0779s)======================================

え?これは、SimpleTestの前の最後の移行であり、失敗した移行ではありません。(そして、移行出力にバージョン番号が含まれていると便利です。)

それでは、失敗した移行SimpleTestのダウンを実行してみましょう。

$ rake db:migrate:down VERSION = 20090326173033
$

何も起こらず、出力もありません。しかし、多分それはとにかく移行を実行しましたか?それでは、SimpleTest移行の構文エラーを修正して、もう一度実行してみましょう。

$ rake db:migrate:up VERSION = 20090326173033
== SimpleTest:移行============================================= ========
--add_column(:assets、:test、:integer)
レーキが中止されました!
Mysql :: Error:Duplicate column name'test ':ALTER TABLEʻassets` ADD `test` int(11)

いいえ。明らかに、migrate:downは機能しませんでした。失敗しているのではなく、実行されていないだけです。

手動でデータベースにアクセスしてデータベースを削除し、テストを実行する以外に、重複するテーブルを削除する方法はありません。それよりも良い方法が必要です。

回答:


79

残念ながら、MySQLの失敗した移行を手動でクリーンアップする必要があります。MySQLは、トランザクションデータベース定義の変更をサポートしていません。

Rails 2.2には、PostgreSQLのトランザクション移行が含まれています。Rails 2.3には、SQLiteのトランザクション移行が含まれています。

これは現在の問題にはあまり役立ちませんが、将来のプロジェクトでデータベースを選択できる場合は、移行がはるかに快適になるため、トランザクションDDLをサポートするデータベースを使用することをお勧めします。

更新-これは2017年もRails4.2.7とMySQL5.7に当てはまり、AlejandroBabioが別の回答で報告しています。


1
素晴らしい、ありがとう。私はPGSQLで新しいプロジェクトを行うので、それがオプションであることを知っておくとよいでしょう。
insane.dreamer 2009年

これはまだ最良の答えなので、これは恵みの私見に値します。
nathanvda 2015年

20

指定されたバージョンに移動するには、次を使用します。

rake db:migrate VERSION=(the version you want to go to)

ただし、移行が途中で失敗した場合は、最初に移行をクリーンアップする必要があります。1つの方法は次のとおりです。

  • 編集downへの移行の方法は、ただの一部取り消しup働いていたことを
  • 以前の状態(開始した場所)に移行します
  • 移行を修正します(への変更を元に戻すことを含むdown
  • 再試行

ありがとう。はい、移行が失敗するまで再移行できることはわかっていますが、移行の履歴が長い場合は、問題が発生することがあります。理想的には、すべて正常に実行されるはずですが、多くの場合、途中で失敗し、さらに大きな混乱が発生します:-)
insane.dreamer 2009年

20

OK、皆さん、これが実際のやり方です。上記の答えが何を話しているのかわかりません。

  1. アップマイグレーションのどの部分が機能したかを把握します。それらをコメントアウトします。
  2. また、移行が失敗した部分をコメントアウト/削除します。
  3. 移行を再度実行します。これで、移行の中断されていない部分が完了し、すでに実行された部分はスキップされます。
  4. 手順1でコメントアウトした移行の一部のコメントを解除します。

すぐに移行できることを確認したい場合は、移行してから再度バックアップすることができます。


2
私は非常によく似たことをしますが、ステップ2を「移行の失敗した部分を修正する」に置き換えます。
ドンカークビー2015年

2
最後のポイントを強調する価値があります-実行しbundle exec rake db:migrate:redoます。一歩後退して一歩前進するので、最新の移行が最後まで実行されていることを確認できます。これは、コードの更新とともに移行をプッシュする必要がある場合はいつでも良い習慣です。
mahemoff 2015年

12

可能であればPostgreSQLを使用することに同意します。ただし、MySQLで立ち往生している場合は、最初にテストデータベースで移行を試すことで、これらの問題のほとんどを回避できます。

rake db:migrate RAILS_ENV=test

前の状態に戻して、で再試行できます

rake db:schema:load RAILS_ENV=test

答えというよりは回避策の方が多いですが、これは私がこれまで思いつかなかった良い考えです。
エミリー

10

Rails4.2.1とMySQL5.7を使用した2015年では、失敗した移行は、2009年のように、Railsが提供する標準のレーキアクションでは修正できません。

MySqlは、DDLステートメントのロールバックをサポートしていません(MySQL 5.7マニュアル)。そして、Railsはそれで何もできません。

また、Railsがどのようにジョブを実行しているかを確認できます。接続アダプターがにどのように応答するかに応じて移行はトランザクションにラップさ:supports_ddl_transactions?ます。Railsソース(v 4.2.1)でこのアクションを検索したところ、トランザクションをサポートしているのはSqlite3PostgreSqlのみであり、デフォルトではサポートされていないことがわかりました。

編集 したがって、元の質問に対する現在の回答:失敗したMySQL移行は手動で修正する必要があります。


私はこの答えを完全には理解していません。バージョン番号を更新することを除いて、元の受け入れられた答えには何も追加されません。
nathanvda 2015年

1
元の質問については、非常に真実です。アンドリュー・グリムが始めた報奨金について:「2009年3月に質問されてから状況が変わったかどうか知りたい」。これは現在の回答であり、将来の変更を確認する方法を提供します。
アレハンドロバビオ2015年

8

これを行う簡単な方法は、すべてのアクションをトランザクションでラップすることです。

class WhateverMigration < ActiveRecord::Migration

 def self.up
    ActiveRecord::Base.transaction do
...
    end
  end

  def self.down
    ActiveRecord::Base.transaction do
...
    end
  end

end

Luke Franclが指摘したように、「MySql [のMyISAMテーブルは]トランザクションをサポートしていません」-これが、MySQL全般、または少なくともMyISAMを回避することを検討する理由です。

MySQLのInnoDBを使用している場合、上記は問題なく機能します。アップまたはダウンのエラーはすべて取り消されます。

注意して行動のいくつかの種類がトランザクションを経由して元に戻すことはできません。通常、テーブルの変更(テーブルの削除、列の削除または追加など)はロールバックできません。


5
MyISAMやInnoDBの問題ではありません。InnoDBはトランザクションをサポートしますが、トランザクションデータベース定義(DDL)の変更はサポートしません。PostgreSQLでは、テーブルを削除してから、その変更をロールバックできます。
ルークフランクル2009

1
ルークは正しいです、mysqlはDDL変更のトランザクションをサポートしていません。テーブルの列の追加や削除など、自分でクリーンアップを検討する必要があります。
Leon Guan


1

(「add_column」に)タイプミスがありました:

def self.up

add_column :medias, :title, :text
add_colunm :medias, :enctype, :text

終わり

def self.down

remove_column :medias, :title
remove_column :medias, :enctype   

終わり

そしてあなたの問題(部分的に失敗した移行を元に戻すことはできません)。グーグルに失敗した後、私はこれを実行しました:

def self.up

remove_column :medias, :title
add_column :medias, :title, :text
add_column :medias, :enctype, :text

終わり

def self.down

remove_column :medias, :title
remove_column :medias, :enctype

終わり

ご覧のとおり、チェックインする前に、修正ラインを手動で追加してから、再度削除しました。


1

上記のAlejandroBabioの回答は、現在の最良の回答を提供します。

追加したいもう1つの詳細:

ときにmyfailedmigration移行が失敗し、それが適用されるものとしてみなされていない、これは実行することで確認することができrake db:migrate:status、次のような出力が表示されるでしょうこれ、:

$  rake db:migrate:status
database: sample_app_dev

 Status   Migration ID    Migration Name
--------------------------------------------------
   up      20130206203115  Create users
   ...
   ...
   down    20150501173156  Test migration

add_column :assets, :test, :integer失敗した移行で実行された残りの影響は、alter table assets drop column test;クエリを使用してデータベースレベルで元に戻す必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.