2009-03-26 23 views
77

¿Cómo revierte una migración fallida de rieles? Esperaría que rake db:rollback deshaga la migración fallida, pero no, revierte la migración anterior (la migración fallida menos uno). Y rake db:migrate:down VERSION=myfailedmigration tampoco funciona. Me he topado con esto algunas veces y es muy frustrante. Aquí está una prueba simple que hice para duplicar el problema:Recuperación de una migración fallida de Rails

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 

resultado:

 
== 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) 

bien, deja rollo de nuevo:

 
$ rake db:rollback 
== AddLevelsToRoles: reverting =============================================== 
-- remove_column(:roles, :level) 
    -> 0.0778s 
== AddLevelsToRoles: reverted (0.0779s) ====================================== 

eh? esa fue mi última migración antes de SimpleTest, no la migración fallida. (Y, oh, sería bueno si la salida de la migración incluido el número de versión.)

Así que vamos a tratar de ejecutar el SimpleTest abajo para la migración ha fallado:

 
$ rake db:migrate:down VERSION=20090326173033 
$ 

No pasa nada, y no hay salida tampoco. Pero tal vez se ejecutó la migración de todos modos? Entonces, solucionemos el error de sintaxis en la migración SimpleTest e intentemos ejecutarlo nuevamente.

 
$ 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) 

Nope. Obviamente, la migración: hacia abajo no funcionó. No está fallando, simplemente no se está ejecutando.

No hay manera de deshacerse de esa tabla duplicada que no sea acceder manualmente a la base de datos y eliminarla, y luego ejecutar la prueba. Tiene que haber una mejor manera que eso.

Respuesta

73

Desafortunadamente, usted debe limpiar manualmente las migraciones fallidas para MySQL. MySQL no admite cambios en la definición de la base de datos transaccional.

Rails 2.2 incluye migraciones transaccionales para PostgreSQL. Rails 2.3 incluye migraciones transaccionales para SQLite.

Esto realmente no le ayuda para su problema en este momento, pero si tiene una opción de base de datos en proyectos futuros, recomiendo usar uno con soporte para DDL transaccional porque hace que las migraciones sean mucho más agradables.

Actualización: esto sigue siendo cierto en 2017, en Rails 4.2.7 y MySQL 5.7, informado por Alejandro Babio en otra respuesta aquí.

+1

Excelente, gracias. Haré nuevos proyectos con PGSQL, así que es bueno saber que es una opción. –

+0

Esta sigue siendo la mejor respuesta, por lo que esta merece la recompensa. – nathanvda

20

Para ir a una versión especificada sólo tiene que utilizar:

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

Pero si un error en la migración forma parte, tendrá que limpiar primero. Una forma sería:

  • editar el método de la migración down que acaba de deshacer la parte de la up que trabajaron
  • migran de nuevo al estado anterior (punto de partida)
  • fijar la migración (incluida deshacer los cambios en el down)
  • intentarlo de nuevo
+0

Gracias. Sí, sé que podría volver a migrar hasta la migración fallida, pero en los casos en los que tengo un largo historial de migraciones, esto a veces puede ser problemático. Lo ideal es que ejecuten todo muy bien, pero la mayoría de las veces les he fallado parcialmente, y luego hay un desastre más grande :-) –

9

La manera más fácil de hacer esto es envolver todas sus acciones en una transacción:

class WhateverMigration < ActiveRecord::Migration 

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

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

end 

Como se señaló Lucas Francl, "MySql [ 's tablas MyISAM no] transacciones de apoyo" - por eso es posible que considere evitar MySQL en general o al menos MyISAM en particular.

Si está utilizando InnoDB de MySQL, lo anterior funcionará bien. Cualquier error en arriba o abajo retrocederá.

ATENCIÓN ALGUNA algunos tipos de acciones no se pueden revertir a través de transacciones. En general, los cambios en la tabla (descartar una tabla, eliminar o agregar columnas, etc.) no se pueden revertir.

+5

No es una cuestión de MyISAM o InnoDB. InnoDB admite transacciones, pero no admite cambios en la definición de la base de datos transaccional (DDL). ¡En PostgreSQL puede soltar una tabla y luego deshacer ese cambio! –

+1

Luke es correcto, mysql no admite la transacción en los cambios de DDL. Tengo que considerar la limpieza por mí mismo, como agregar y eliminar una columna de las tablas. –

1

que tenía un error tipográfico (en "add_column"): self.up

def

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

final

def self.down

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

final

y entonces su problema (no se puede deshacer la migración en parte fallado). después de algunos no buscando en Google me encontré esto:

def self.up

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

final

def self.down

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

final

como usted puedo ver yo acaba de agregar la línea de corrección a mano, y luego la quité de nuevo, antes de registrarla.

12

Estoy de acuerdo en que debe usar PostgreSQL cuando sea posible. Sin embargo, cuando le pegan con MySQL, puede evitar la mayoría de estos problemas al tratar la migración de su base de datos de prueba primero:

rake db:migrate RAILS_ENV=test 

Se puede volver al estado anterior e intentarlo de nuevo con

rake db:schema:load RAILS_ENV=test 
+0

Más de una solución que una respuesta, pero esta es una buena idea que no se me había ocurrido antes. – Emily

18

OK, amigos, así es cómo lo hacen. No sé de qué están hablando las respuestas anteriores.

  1. Averigüe qué parte de la migración ascendente funcionó. Comenta ésos.
  2. También comente/elimine la parte de la migración que se rompió.
  3. Ejecute la migración nuevamente. Ahora completará las partes no rotas de la migración, omitiendo las partes que ya se han realizado.
  4. Descomente los bits de la migración, comentadas en el paso 1.

Puede migrar hacia abajo y una copia de seguridad de nuevo si desea verificar que lo tienes en este momento.

+2

Hago algo muy similar, pero reemplazo el paso 2 con "corrija la parte de la migración que se rompió". –

+1

Vale la pena enfatizar el último punto: ejecute 'bundle exec rake db: migrate: redo'. Dará un paso atrás y un paso adelante, para que pueda verificar que su última migración se ejecute completamente. Esta es una buena práctica cada vez que tiene que enviar una migración junto con algunas actualizaciones de código. – mahemoff

8

En 2015, con rieles 4.2.1 y MySQL 5.7, una migración fallida no se puede solucionar con acciones rastrillo estándar que proporcionan los carriles, como lo fue en 2009.

MySQL no soporta la reversión de statments DDL (en MySQL 5.7 Manual). Y Rails no puede hacer nada con eso.

También, podemos verificar cómo Rails está haciendo el trabajo: Una migración es wrapped in a transaction dependiendo de cómo responda el adaptador de conexión al :supports_ddl_transactions?. Después de buscar esta acción en rails source (v 4.2.1), encontré que solo Sqlite3 y PostgreSql admiten transacciones, y por default no es compatible.

Editar Por lo tanto la respuesta actual a la pregunta original: Una migración de MySQL no debe fijarse manualmente.

+0

No entiendo muy bien esta respuesta: excepto por la actualización de los números de versión, no agrega nada a la respuesta original aceptada. – nathanvda

+1

Muy cierto, para la pregunta original. Para la recompensa comenzó para Andrew Grimm: "Quiero saber si la situación ha cambiado desde que se hizo la pregunta en marzo de 2009". Es una respuesta actual y proporciona un método para verificar cualquier cambio en el futuro. –

1

La respuesta anterior de Alejandro Babio proporciona la mejor respuesta actual.

Un detalle adicional Quiero añadir:

Cuando la migración myfailedmigration falla, no se considera en su aplicación, y esto se puede verificar mediante la ejecución de rake db:migrate:status, lo que mostraría una salida similar a la siguiente:

$ rake db:migrate:status 
database: sample_app_dev 

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

El efecto residual de add_column :assets, :test, :integer que se está ejecutando en la migración fallida tendrá que revertirse a nivel de base de datos con una consulta alter table assets drop column test;.

Cuestiones relacionadas