Откат неудачной миграции рельсов


как откатить неудачную миграцию 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 78

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)
  • попробовать еще раз

ОК, ребята, вот как вы на самом деле делать это. Я не знаю, о чем говорят приведенные выше ответы.

  1. выясните, какая часть миграции up работала. Прокомментируйте это.
  2. также закомментируйте / удалите часть миграции, которая сломалась.
  3. запустите миграцию еще раз. Теперь он завершит не сломанные части миграции, пропуская части, которые уже были сделаны.
  4. раскомментируйте биты миграции, которые вы прокомментировали на шаге 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; запрос.