Abortire una migrazione Rails fallito
-
22-08-2019 - |
Domanda
Come si tira indietro una migrazione rotaie fallito? Mi aspetterei che rake db:rollback
sarebbe annullare la migrazione fallita, ma no, rotola di nuovo la migrazione precedente (la migrazione non riuscita meno uno). E rake db:migrate:down VERSION=myfailedmigration
non funziona neanche. Ho incontrato un paio di volte ed è molto frustrante. Ecco un semplice test che ho fatto di riprodurre il problema:
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
Risultati:
== SimpleTest: migrating ===================================================== -- add_column(:assets, :test, :integer) -> 0.0932s -- add_column(:asset, :error) rake aborted! An error has occurred, all later migrations canceled: wrong number of arguments (2 for 3)
Ok, consente di rollback:
$ rake db:rollback == AddLevelsToRoles: reverting =============================================== -- remove_column(:roles, :level) -> 0.0778s == AddLevelsToRoles: reverted (0.0779s) ======================================
eh? che era la mia ultima migrazione prima SimpleTest, non la migrazione non riuscita. (E, oh, sarebbe bello se l'uscita di migrazione incluso il numero di versione).
Quindi, consente di provare a eseguire il basso per la SimpleTest migrazione non riuscita:
$ rake db:migrate:down VERSION=20090326173033 $
Non succede nulla, e nessuna uscita neanche. Ma forse si correva la migrazione in ogni caso? Quindi, consente di correggere l'errore di sintassi nella migrazione SimpleTest, e provare a eseguirlo nuovamente.
$ rake db:migrate:up VERSION=20090326173033 == SimpleTest: migrating ===================================================== -- add_column(:assets, :test, :integer) rake aborted! Mysql::Error: Duplicate column name 'test': ALTER TABLE `assets` ADD `test` int(11)
No. Ovviamente la migrazione: verso il basso non ha funzionato. Non è mancato, non è solo l'esecuzione.
Non c'è modo di sbarazzarsi di quella tabella duplicata altro che andare manualmente nel database e la rimozione, e quindi eseguire il test. Ci deve essere un modo migliore di questo.
Soluzione
Purtroppo, è necessario pulire manualmente migrazioni falliti per MySQL. MySQL non supporta le modifiche transazionali di definizione del database.
Rails 2.2 include le migrazioni transazionali per PostgreSQL. Rails 2.3 include le migrazioni transazionali per SQLite.
Questo in realtà non si aiuta per il vostro problema in questo momento, ma se avete una scelta di database su progetti futuri, mi consiglia di utilizzare uno con il supporto per DDL transazionale perché rende le migrazioni molto più piacevole.
Aggiornamento - questo è ancora vero nel 2017, on Rails 4.2.7 e MySQL 5.7, riportate da Alejandro Babio in un'altra risposta qui
.Altri suggerimenti
Per passare a una versione specificata basta usare:
rake db:migrate VERSION=(the version you want to go to)
Ma se una migrazione non riesce parzialmente, dovrete per ripulirlo prima. Un modo potrebbe essere:
- modificare il metodo
down
della migrazione per annullare solo la parte delup
che ha lavorato - migrare torna allo stato precedente (punto di partenza)
- fissare la migrazione (compresi annullare le modifiche apportate al
down
) - riprovare
OK, gente, ecco come effettivamente farlo. Non so che cosa le risposte di cui sopra stanno parlando.
- Figura quale parte della migrazione verso lavorato. Commento quelli fuori.
- Commento anche fuori / rimuovere la parte della migrazione che ha rotto.
- Esegui nuovamente la migrazione. Ora si completerà le parti non-rotto della migrazione, saltando le parti che sono già state fatte.
- Decommentare i bit della migrazione si come commento al punto 1.
È possibile migrare verso il basso e il backup di nuovo se si desidera verificare che ce l'hai al momento.
Sono d'accordo che si dovrebbe usare PostgreSQL, quando possibile. Tuttavia, quando si è bloccato con MySQL, è possibile evitare la maggior parte di questi problemi, cercando la migrazione sul database di test prima:
rake db:migrate RAILS_ENV=test
È possibile ripristinare lo stato precedente e riprova con
rake db:schema:load RAILS_ENV=test
a 2015 con Rails 4.2.1 e MySQL 5.7, una migrazione fallito non può essere risolto con le azioni di spoglia standard che forniscono Rails, come era il 2009.
MySql non supporta il rollback di statments DDL (a MySQL 5.7 Manuale ). E Rails non possono fare nulla con questo.
Inoltre, siamo in grado di verificare come Rails sta facendo il lavoro: Una migrazione è avvolto in una transazione a seconda di come adattatore di collegamento rispondono al :supports_ddl_transactions?
. Dopo una ricerca di questa azione a rotaie fonte (v 4.2.1), ho scoperto che solo Sqlite3 e PostgreSql supporta le transazioni, e da predefinito non è supportato.
Modifica Così la risposta corrente alla domanda iniziale:. Una migrazione MySQL fallito deve essere fissato manualmente
Il modo più semplice per farlo è quello di avvolgere tutte le azioni in una transazione:
class WhateverMigration < ActiveRecord::Migration
def self.up
ActiveRecord::Base.transaction do
...
end
end
def self.down
ActiveRecord::Base.transaction do
...
end
end
end
Come osservato Luke Francl, "MySQL [ 's tabelle MyISAM non] transazioni di supporto" -. Ed è per questo si potrebbe considerare evitando MySQL in generale o almeno MyISAM in particolare
Se stai usando InnoDB di MySQL, quindi quanto sopra funzionano bene. Eventuali errori in alto o in basso si marcia indietro.
essere consapevoli alcuni tipi di azioni non è possibile ripristinare tramite transazioni. Generalmente, tabella cambia (cadere un tavolo, rimozione o aggiunta di colonne, etc.) non possono essere annullate.
Esegui solo la migrazione verso il basso dalla console:
http: //gilesbowkett.blogspot .com / 2007/07 / how-to-use-migrazioni-da-console.html (clic per raggiungere il suo pastie)
Ho avuto un errore di battitura (in "add_column"):
def self.up
add_column :medias, :title, :text add_colunm :medias, :enctype, :text
end
def self.down
remove_column :medias, :title remove_column :medias, :enctype
end
e quindi il problema (non può annullare la migrazione in parte fallito). dopo un po 'fallito googling mi sono imbattuto in questo modo:
def self.up
remove_column :medias, :title add_column :medias, :title, :text add_column :medias, :enctype, :text
end
def self.down
remove_column :medias, :title remove_column :medias, :enctype
end
come potete vedere ho solo aggiunto la linea di correzione a mano, e poi rimosso di nuovo, prima del check in.
La risposta di Alejandro Babio sopra fornisce la migliore risposta corrente.
Un dettaglio ulteriore voglio aggiungere:
Quando la migrazione myfailedmigration
fallisce, non è considerata come applicata, e questo può essere verificato eseguendo rake db:migrate:status
, che mostrano output simile al seguente:
$ rake db:migrate:status
database: sample_app_dev
Status Migration ID Migration Name
--------------------------------------------------
up 20130206203115 Create users
...
...
down 20150501173156 Test migration
L'effetto residuo di add_column :assets, :test, :integer
in esecuzione sulla migrazione non riuscita dovrà essere invertita a livello di database con una query alter table assets drop column test;
.