Zurücksetzen einer fehlgeschlagenen Rails-Migration

82

Wie können Sie eine fehlgeschlagene Rails-Migration zurücksetzen? Ich würde erwarten, dass rake db:rollbackdies die fehlgeschlagene Migration rückgängig macht, aber nein, es wird die vorherige Migration zurückgesetzt (die fehlgeschlagene Migration minus eins). Und rake db:migrate:down VERSION=myfailedmigrationfunktioniert auch nicht. Ich bin ein paar Mal darauf gestoßen und es ist sehr frustrierend. Hier ist ein einfacher Test, den ich durchgeführt habe, um das Problem zu duplizieren:

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

Ergebnis:

== SimpleTest: Migration ================================================ ========
- add_column (: Assets ,: Test ,: Ganzzahl)
   -> 0,0932s
- add_column (: Asset ,: Fehler)
Rechen abgebrochen!
Ein Fehler ist aufgetreten, alle späteren Migrationen wurden abgebrochen:

falsche Anzahl von Argumenten (2 für 3)

ok, lass es uns zurückrollen:

$ rake db: Rollback
== AddLevelsToRoles: Zurücksetzen ============================================= ==
- remove_column (: Rollen ,: Ebene)
   -> 0,0778 s
== AddLevelsToRoles: zurückgesetzt (0.0779s) ======================================

huh? Das war meine letzte Migration vor SimpleTest, nicht die fehlgeschlagene Migration. (Und oh, es wäre schön, wenn die Migrationsausgabe die Versionsnummer enthalten würde.)

Versuchen wir also, den Down für die fehlgeschlagene Migration SimpleTest auszuführen:

$ rake db: migrate: down VERSION = 20090326173033
$

Es passiert nichts und auch keine Ausgabe. Aber vielleicht lief die Migration trotzdem? Beheben wir also den Syntaxfehler in der SimpleTest-Migration und versuchen Sie, ihn erneut auszuführen.

$ rake db: migrate: up VERSION = 20090326173033
== SimpleTest: Migration ================================================ ========
- add_column (: Assets ,: Test ,: Ganzzahl)
Rechen abgebrochen!
MySQL :: Fehler: Doppelter Spaltenname 'Test': ALTER TABLE` Assets` ADD` Test` Int (11)

Nee. Offensichtlich hat die Migration: down nicht funktioniert. Es scheitert nicht, es wird einfach nicht ausgeführt.

Es gibt keine andere Möglichkeit, diese doppelte Tabelle zu entfernen, als manuell in die Datenbank zu gehen und sie zu entfernen und dann den Test auszuführen. Es muss einen besseren Weg geben.

wahnsinniger Träumer
quelle

Antworten:

79

Leider müssen Sie fehlgeschlagene Migrationen für MySQL manuell bereinigen. MySQL unterstützt keine Änderungen der Transaktionsdatenbankdefinition.

Rails 2.2 enthält Transaktionsmigrationen für PostgreSQL. Rails 2.3 enthält Transaktionsmigrationen für SQLite.

Dies hilft Ihnen derzeit nicht wirklich bei Ihrem Problem. Wenn Sie jedoch eine Datenbank für zukünftige Projekte zur Auswahl haben, empfehle ich die Verwendung einer Datenbank mit Unterstützung für Transaktions-DDL, da dadurch Migrationen viel angenehmer werden.

Update - Dies gilt auch 2017 für Rails 4.2.7 und MySQL 5.7, die Alejandro Babio in einer anderen Antwort hier gemeldet hat.

Luke Francl
quelle
1
Großartig, danke. Ich werde neue Projekte mit PGSQL durchführen, daher ist es gut zu wissen, dass dies eine Option ist.
Insane.dreamer
Dies ist immer noch die beste Antwort, daher verdient dies die Prämie imho.
Nathanvda
20

Um zu einer bestimmten Version zu gelangen, verwenden Sie einfach:

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

Wenn eine Migration jedoch teilweise fehlschlägt, müssen Sie sie zuerst bereinigen. Ein Weg wäre:

  • Bearbeiten Sie die downMethode der Migration, um nur den Teil rückgängig zu machen, der upfunktioniert hat
  • Zurück zum vorherigen Status migrieren (wo Sie begonnen haben)
  • Korrigieren Sie die Migration (einschließlich des Rückgängigmachens Ihrer Änderungen an der down)
  • Versuch es noch einmal
MarkusQ
quelle
Vielen Dank. Ja, ich weiß, dass ich bis zur fehlgeschlagenen Migration erneut migrieren kann, aber in Fällen, in denen ich eine lange Migrationsgeschichte habe, kann dies manchmal problematisch sein. Idealerweise sollten sie alle gut ausführen, aber meistens habe ich sie teilweise versagen lassen, und dann gibt es ein größeres Durcheinander :-)
Insane.Dreamer
20

OK, Leute, hier ist, wie du es tatsächlich machst. Ich weiß nicht, worüber die obigen Antworten sprechen.

  1. Finden Sie heraus, welcher Teil der Aufwärtsmigration funktioniert hat. Kommentieren Sie diese aus.
  2. Kommentieren / entfernen Sie auch den Teil der Migration, der unterbrochen wurde.
  3. Führen Sie die Migration erneut aus. Jetzt werden die nicht beschädigten Teile der Migration abgeschlossen, wobei die bereits durchgeführten Teile übersprungen werden.
  4. Kommentieren Sie die Teile der Migration aus, die Sie in Schritt 1 auskommentiert haben.

Sie können nach unten und wieder nach oben migrieren, wenn Sie überprüfen möchten, ob Sie es jetzt haben.

Simon Woodside
quelle
2
Ich mache etwas sehr Ähnliches, aber ich ersetze Schritt 2 durch "Beheben Sie den Teil der Migration, der unterbrochen wurde".
Don Kirkby
2
Hervorzuheben ist der letzte Punkt - Lauf bundle exec rake db:migrate:redo. Es wird einen Schritt zurück und einen Schritt vorwärts gehen, sodass Sie überprüfen können, ob Ihre letzte Migration vollständig ausgeführt wird. Dies ist immer dann eine gute Vorgehensweise, wenn Sie eine Migration zusammen mit einigen Code-Updates vorantreiben müssen.
Mahemoff
12

Ich bin damit einverstanden, dass Sie PostgreSQL verwenden, wenn dies möglich ist. Wenn Sie jedoch mit MySQL nicht weiterkommen, können Sie die meisten dieser Probleme vermeiden, indem Sie zuerst Ihre Migration in Ihrer Testdatenbank versuchen:

rake db:migrate RAILS_ENV=test

Sie können zum vorherigen Status zurückkehren und es erneut versuchen

rake db:schema:load RAILS_ENV=test
StefanH
quelle
Eher eine Problemumgehung als eine Antwort, aber das ist eine gute Idee, die mir vorher noch nicht in den Sinn gekommen war.
Emily
10

2015 mit Rails 4.2.1 und MySQL 5.7 kann eine fehlgeschlagene Migration nicht mit Standard-Rake-Aktionen behoben werden, die Rails bereitstellt, wie dies 2009 der Fall war.

MySQL unterstützt kein Rollback von DDL-Anweisungen (im MySQL 5.7-Handbuch ). Und damit kann Rails nichts anfangen.

Außerdem können wir überprüfen, wie Rails den Job ausführt: Eine Migration wird in eine Transaktion eingeschlossen, je nachdem, wie der Verbindungsadapter reagiert :supports_ddl_transactions?. Nach einer Suche nach dieser Aktion an der Rails-Quelle (Version 4.2.1) stellte ich fest, dass nur Sqlite3 und PostgreSql Transaktionen unterstützen und standardmäßig nicht unterstützt werden.

Bearbeiten Somit die aktuelle Antwort auf die ursprüngliche Frage: Eine fehlgeschlagene MySQL-Migration muss manuell behoben werden.

Alejandro Babio
quelle
Ich verstehe diese Antwort nicht ganz: Außer durch die Aktualisierung der Versionsnummern wird der ursprünglich akzeptierten Antwort nichts hinzugefügt.
Nathanvda
1
Sehr wahr, für die ursprüngliche Frage. Für die Prämie begann für Andrew Grimm: "Möchten Sie wissen, ob sich die Situation geändert hat, seit die Frage im März 2009 gestellt wurde." Es ist eine aktuelle Antwort und bietet eine Methode, um zukünftige Änderungen zu überprüfen.
Alejandro Babio
8

Der einfache Weg, dies zu tun, besteht darin, alle Ihre Aktionen in eine Transaktion einzubinden:

class WhateverMigration < ActiveRecord::Migration

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

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

end

Wie Luke Francl bemerkte, "unterstützen die MyISAM-Tabellen von MySql keine Transaktionen" - weshalb Sie MySQL im Allgemeinen oder zumindest MyISAM im Besonderen vermeiden sollten.

Wenn Sie die InnoDB von MySQL verwenden, funktioniert das oben Genannte einwandfrei. Alle Fehler in oben oder unten werden zurückgesetzt.

ACHTUNG Einige Arten von Aktionen können nicht über Transaktionen zurückgesetzt werden. Im Allgemeinen können Tabellenänderungen (Löschen einer Tabelle, Entfernen oder Hinzufügen von Spalten usw.) nicht rückgängig gemacht werden.

BryanH
quelle
5
Es geht nicht um MyISAM oder InnoDB. InnoDB unterstützt Transaktionen, jedoch keine DDL-Änderungen (Transactional Database Definition). In PostgreSQL können Sie eine Tabelle löschen und diese Änderung dann rückgängig machen!
Luke Francl
1
Luke ist richtig, MySQL unterstützt keine Transaktion für DDL-Änderungen. Ich muss die Bereinigung selbst in Betracht ziehen, z. B. das Hinzufügen und Entfernen einer Spalte aus Tabellen.
Leon Guan
1

Ich hatte einen Tippfehler (in "add_column"):

def self.up

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

Ende

def self.down

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

Ende

und dann Ihr Problem (kann teilweise fehlgeschlagene Migration nicht rückgängig machen). Nach einigem fehlgeschlagenen googeln habe ich folgendes ausgeführt:

def self.up

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

Ende

def self.down

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

Ende

Wie Sie sehen, habe ich die Korrekturzeile einfach von Hand hinzugefügt und dann wieder entfernt, bevor ich sie eingecheckt habe.

Poster
quelle
1

Die obige Antwort von Alejandro Babio liefert die beste aktuelle Antwort.

Ein weiteres Detail möchte ich hinzufügen:

Wenn die myfailedmigrationMigration fehlschlägt, wird sie nicht als angewendet betrachtet. Dies kann durch Ausführen überprüft werden. Dies rake db:migrate:statuswürde eine Ausgabe ähnlich der folgenden anzeigen:

$  rake db:migrate:status
database: sample_app_dev

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

Der verbleibende Effekt der add_column :assets, :test, :integerAusführung auf die fehlgeschlagene Migration muss auf Datenbankebene mit einer alter table assets drop column test;Abfrage rückgängig gemacht werden.

Prakash Murthy
quelle