Откат неудачной миграции Rails
-
22-08-2019 - |
Вопрос
Как откатить неудачную миграцию 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)
Неа.Очевидно, что migrate: down не сработал.Это не сбой, это просто не выполняется.
Нет другого способа избавиться от этой дублирующейся таблицы, кроме как вручную зайти в базу данных и удалить ее, а затем запустить тест.Должен быть способ получше этого.
Решение
К сожалению, вы должны вручную очистить неудачные миграции для 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
) - попробуй еще раз
Хорошо, ребята, вот как вы на самом деле это делаете.Я не знаю, о чем говорят приведенные выше ответы.
- Выясните, какая часть миграции вверх сработала.Прокомментируйте их.
- Также закомментируйте / удалите ту часть миграции, которая вышла из строя.
- Запустите миграцию еще раз.Теперь он завершит неповрежденные части миграции, пропустив те части, которые уже были выполнены.
- Раскомментируйте фрагменты миграции, которые вы прокомментировали на шаге 1.
Вы можете выполнить миграцию вниз и создать резервную копию снова, если хотите убедиться, что все готово прямо сейчас.
Я согласен, что вам следует использовать PostgreSQL, когда это возможно.Однако, когда вы застряли с MySQL, вы можете избежать большинства этих проблем, сначала попробовав миграцию в своей тестовой базе данных:
rake db:migrate RAILS_ENV=test
Вы можете вернуться к предыдущему состоянию и повторить попытку с помощью
rake db:schema:load RAILS_ENV=test
В 2015 году с Rails 4.2.1 и MySQL 5.7 неудачная миграция не может быть исправлена стандартными рейковыми действиями, предоставляемыми Rails, как это было в 2009 году.
MySQL не поддерживает откат статментов DDL (при Руководство пользователя MySQL 5.7).И Rails ничего не может с этим поделать.
Кроме того, мы можем проверить, как Rails выполняет эту работу:Миграция - это завернутый в транзакцию в зависимости от того, как адаптер подключения реагирует на :supports_ddl_transactions?
.После поиска этого действия в исходном коде rails (версия 4.2.1) я обнаружил, что только Sqlite3 и PostgreSQL поддерживает транзакции, и с помощью По умолчанию это не поддерживается.
Редактировать Таким образом, текущий ответ на исходный вопрос:Неудачная миграция 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, то вышеописанное будет работать просто отлично.Любые ошибки как в up, так и в down будут устранены.
БУДЬТЕ ВНИМАТЕЛЬНЫ некоторые типы действий не могут быть отменены с помощью транзакций.Как правило, изменения в таблице (удаление таблицы, удаление или добавление столбцов и т.д.) откату не подлежат.
Запустите просто миграцию вниз с консоли:
http://gilesbowkett.blogspot.com/2007/07/how-to-use-migrations-from-console.html (перейдите к его пирожному)
У меня была опечатка (в "add_column"):
защитите себя.вверх
add_column :medias, :title, :text add_colunm :medias, :enctype, :text
конец
защитите себя.вниз
remove_column :medias, :title remove_column :medias, :enctype
конец
и тогда ваша проблема (не удается отменить частично неудачную миграцию).после некоторого неудачного поиска в Google я запустил это:
защитите себя.вверх
remove_column :medias, :title add_column :medias, :title, :text add_column :medias, :enctype, :text
конец
защитите себя.вниз
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;
запрос.