Откат неудачной миграции рельсов
как откатить неудачную миграцию rails? Я бы ожидал, что rake db:rollback
отменит неудачную миграцию, но нет, она откатывает предыдущую миграцию (неудачная миграция минус один). И 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: 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)
хорошо, давайте откатим его назад:
$ rake db:rollback == AddLevelsToRoles: reverting =============================================== -- remove_column(:roles, :level) -> 0.0778s == AddLevelsToRoles: reverted (0.0779s) ======================================
да? это была моя последняя миграция до этого Самый простой, а не неудачная миграция. (И о, Было бы неплохо, если бы вывод миграции включал номер версии.)
Итак, давайте попробуем запустить вниз для неудачной миграции SimpleTest:
$ rake db:migrate:down VERSION=20090326173033 $
ничего не происходит, и не выход. Но, может быть, он все равно управлял миграцией? Итак, давайте исправим синтаксическую ошибку в самой простой миграции и попробуем запустить ее снова.
$ 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)
Неа. Очевидно, что миграция:вниз не сработала. Это не провал, это просто не проведение.
нет способа избавиться от этой дублирующей таблицы, кроме как вручную войти в базу данных и удалить ее, а затем запустить тест. Должен быть способ получше.
9 ответов:
к сожалению, вы должны вручную очистить неудачные миграции для MySQL. MySQL не поддерживает изменения определения транзакционной базы данных.
Rails 2.2 включает в себя транзакционные миграции для PostgreSQL. Rails 2.3 включает в себя транзакционные миграции для SQLite.
Это действительно не поможет вам решить вашу проблему прямо сейчас, но если у вас есть выбор базы данных для будущих проектов, я рекомендую использовать ее с поддержкой транзакционного DDL, потому что она делает миграции гораздо приятнее.
обновление-это все еще верно в 2017 году, на Rails 4.2.7 и MySQL 5.7, о чем сообщил Алехандро Бабио в другом ответе здесь.
чтобы перейти к указанной версии просто используйте:
rake db:migrate VERSION=(the version you want to go to)
но если миграция не удается частично, вам придется сначала очистить его. Один из способов будет:
- редактировать
down
метод миграции, чтобы просто отменить частьup
что работал- перенести обратно в предыдущее состояние (где вы начали)
- исправьте миграцию (включая отмену изменений в
down
)- попробовать еще раз
ОК, ребята, вот как вы на самом деле делать это. Я не знаю, о чем говорят приведенные выше ответы.
- выясните, какая часть миграции up работала. Прокомментируйте это.
- также закомментируйте / удалите часть миграции, которая сломалась.
- запустите миграцию еще раз. Теперь он завершит не сломанные части миграции, пропуская части, которые уже были сделаны.
- раскомментируйте биты миграции, которые вы прокомментировали на шаге 1.
вы можете мигрировать вниз и обратно, если вы хотите проверить, что у вас есть это прямо сейчас.
Я согласен, что вы должны использовать PostgreSQL, когда это возможно. Однако, когда вы застряли с MySQL, вы можете избежать большинства из этих проблем, пытаясь миграции на базе тест:
rake db:migrate RAILS_ENV=test
вы можете вернуться к предыдущему состоянию и повторить попытку с
rake db:schema:load RAILS_ENV=test
в 2015 году с Rails 4.2.1 и MySQL 5.7, неудачная миграция не может быть исправлена с помощью стандартных действий rake, которые предоставляют Rails, как это было в 2009 году.
MySql не поддерживает откат DDL statments (at MySQL 5.7 Руководство). И рельсы ничего не могут с этим поделать.
кроме того, мы можем проверить, как Rails выполняет эту работу: миграция завернут в транзакцию в зависимости от того, как адаптер для подключения откликнуться на
:supports_ddl_transactions?
. После поиска этого действие на rails source (v 4.2.1), я обнаружил, что только Sqlite3 и PostgreSql поддерживает транзакции, и по умолчанию он не поддерживается.Edit Таким образом, текущий ответ на исходный вопрос: неудачная миграция MySQL должна быть исправлена вручную.
самый простой способ сделать это, чтобы обернуть все ваши действия в сделке:
class WhateverMigration < ActiveRecord::Migration def self.up ActiveRecord::Base.transaction do ... end end def self.down ActiveRecord::Base.transaction do ... end end end
Как отметил Люк Франкл," MySql [таблицы MyISAM не поддерживают] транзакции " - вот почему вы можете рассмотреть возможность избегать MySQL вообще или, по крайней мере, MyISAM в частности.
Если вы используете InnoDB MySQL, то выше будет работать просто отлично. Любые ошибки либо вверх, либо вниз будут отступать.
БУДЬТЕ В КУРСЕ некоторые типы действий не могут быть возвращены через операции. Как правило, таблица изменяется (удаление таблицы, удаление или добавление столбцов и т. д.) откат невозможен.
запустите только миграцию вниз с консоли:
http://gilesbowkett.blogspot.com/2007/07/how-to-use-migrations-from-console.html (переходим к его паштету)
у меня была опечатка (в "add_column"):
def self.вверх
add_column :medias, :title, :text add_colunm :medias, :enctype, :text
конец
def self.вниз
remove_column :medias, :title remove_column :medias, :enctype
конец
и тогда ваша проблема (не может отменить частично неудачной миграции). после некоторых неудачных поисковых запросов я запустил это:
def self.вверх
remove_column :medias, :title add_column :medias, :title, :text add_column :medias, :enctype, :text
конец
def self.вниз
remove_column :medias, :title remove_column :medias, :enctype
конец
как вы можете смотрите, я просто добавил линию коррекции вручную, а затем снова удалил ее, прежде чем я ее зарегистрировал.
ответ Алехандро Бабио выше обеспечивает лучший текущий ответ.
еще одну деталь я хочу добавить:
когда
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;
запрос.