Відкат невдалої міграції Rails


82

Як відкотити невдалу міграцію рейок? Я би очікував, що 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: міграція =============================================== ========
- add_column (: активи,: тест,: ціле число)
   -> 0,0932 с
- add_column (: актив,: помилка)
граблі перервано!
Сталася помилка, усі пізніші міграції скасовано:

неправильна кількість аргументів (2 на 3)

гаразд, давайте відкотимо назад:

$ rake db: відкат
== AddLevelsToRoles: повернення ================================================= ==
- remove_column (: role,: level)
   -> 0,0778 с
== AddLevelsToRoles: відновлено (0,0779 с) ========================================

так? це була моя остання міграція до SimpleTest, а не невдала міграція. (І о, було б непогано, якби вихідні дані міграції включали номер версії.)

Тож давайте спробуємо запустити вниз для невдалої міграції SimpleTest:

$ rake db: migrate: down VERSION = 20090326173033
$

Нічого не відбувається, і результатів теж немає. Але, можливо, це все-таки перенесло міграцію? Тож дозволяє виправити синтаксичну помилку в міграції SimpleTest і спробувати запустити її знову.

$ rake db: migrate: up VERSION = 20090326173033
== SimpleTest: міграція =============================================== ========
- add_column (: активи,: тест,: ціле число)
граблі перервано!
Mysql :: Помилка: Повторювана назва стовпця 'test': ALTER TABLE `assets` ADD` test` int (11)

Ні. Очевидно, міграція: вниз не спрацювала. Це не підводить, це просто не виконує.

Неможливо позбутися цієї дублікатної таблиці, крім як вручну зайти в базу даних та видалити її, а потім запустити тест. Має бути кращий спосіб, ніж це.

Відповіді:


79

На жаль, ви повинні вручну очистити невдалі міграції для MySQL. MySQL не підтримує зміни визначення бази даних транзакцій.

Rails 2.2 включає транзакційні міграції для PostgreSQL. Rails 2.3 включає транзакційні міграції для SQLite.

Зараз це насправді не допомагає вам вирішити проблему, але якщо у вас є вибір бази даних щодо майбутніх проектів, я рекомендую використовувати базу даних із підтримкою транзакційних DDL, оскільки це робить міграції набагато приємнішими.

Оновлення - це все ще вірно в 2017 році, на Rails 4.2.7 та MySQL 5.7, про що повідомляє Алехандро Бабіо в іншій відповіді тут.


1
Чудово, дякую. Я буду робити нові проекти з PGSQL, тому добре знати, що це варіант.
insane.dreamer

Це все ще найкраща відповідь, тож це заслуговує щедрості imho.
nathanvda

20

Щоб перейти до вказаної версії, просто використовуйте:

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

Але якщо міграція не вдається частково, вам доведеться спочатку її очистити. Один із способів:

  • відредагуйте downметод міграції, щоб просто скасувати ту частину, upяка спрацювала
  • перейти до попереднього стану (там, де ви починали)
  • виправити міграцію (включаючи скасування змін до down)
  • спробуйте ще раз

Дякую. Так, я знаю, що міг би здійснити повторну міграцію аж до невдалої міграції, але у випадках, коли я маю довгу історію міграцій, це іноді може бути проблематичним. В ідеалі вони повинні виконувати все просто чудово, але частіше за все у мене трапляються невдачі, і тоді є більший безлад :-)
insane.dreamer

20

Добре, люди, ось як ви насправді це робите. Не знаю, про що говорять наведені вище відповіді.

  1. З’ясуйте, яка частина вищої міграції спрацювала. Прокоментуйте їх.
  2. Також прокоментуйте / видаліть частину перенесеної міграції.
  3. Знову запустіть міграцію. Тепер він завершить неполамані частини міграції, пропускаючи вже зроблені частини.
  4. Розкоментуйте біти міграції, які ви прокоментували на кроці 1.

Ви можете перейти вниз і назад ще раз, якщо хочете підтвердити, що у вас це зараз.


2
Я роблю щось дуже подібне, але замінюю крок 2 на "виправити порушену частину міграції".
Дон Кіркбі

2
Варто наголосити на останньому пункті - бігати bundle exec rake db:migrate:redo. Він піде на крок назад і на крок вперед, щоб ви могли переконатися, що ваша остання міграція виконана повністю. Це хороша практика в будь-який час, коли вам потрібно просувати міграцію разом із деякими оновленнями коду.
mahemoff

12

Я погоджуюсь, що вам слід використовувати PostgreSQL, коли це можливо. Однак, коли ви застрягли в MySQL, ви можете уникнути більшості з цих проблем, спершу спробувавши перенести вашу тестову базу даних:

rake db:migrate RAILS_ENV=test

Ви можете повернутися до попереднього стану та повторити спробу за допомогою

rake db:schema:load RAILS_ENV=test

Більше обхідного шляху, ніж відповіді, але це гарна ідея, яка мені раніше не спала на думку.
Емілі

10

У 2015 році з Rails 4.2.1 та MySQL 5.7 не вдалося перенести невдалу міграцію за допомогою стандартних грабежних дій, які надає Rails, як це було у 2009 році.

MySql не підтримує відкат статистів DDL (на MySQL 5.7 Manual ). І Rails нічого не може з цим зробити.

Крім того, ми можемо перевірити, як Rails виконує цю роботу: міграція обертається транзакцією залежно від реакції адаптера підключення :supports_ddl_transactions?. Після пошуку цієї дії у джерелі рейок (v 4.2.1), я виявив, що лише Sqlite3 та PostgreSql підтримують транзакції, і за замовчуванням це не підтримується.

Змінити Отже, поточна відповідь на вихідне запитання: Невдалу міграцію MySQL потрібно виправити вручну.


Я не зовсім розумію цю відповідь: окрім оновлення номерів версій, це нічого не додає до початкової прийнятої відповіді.
nathanvda

1
Дуже вірно, що стосується оригінального питання. За щедрість, яку розпочав Ендрю Грімм: "Хочу знати, чи змінилася ситуація після того, як питання було задано в березні 2009 року" Це поточна відповідь і дає спосіб перевірити будь-які зміни в майбутньому.
Алехандро Бабіо

8

Найпростіший спосіб зробити це - обернути всі свої дії транзакцією:

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, то вищезазначене працюватиме чудово. Будь-які помилки вгору чи вниз повернуться назад.

УВАЖАЙТЕ, що деякі типи дій не можуть бути скасовані за допомогою транзакцій. Як правило, зміни таблиці (скидання таблиці, видалення або додавання стовпців тощо) не можна відкотити назад.


5
Це не питання MyISAM чи InnoDB. InnoDB підтримує транзакції, але не підтримує зміни у визначенні транзакційної бази даних (DDL). У PostgreSQL ви можете скинути таблицю, а потім відкотити цю зміну!
Люк Франкл,

1
Лука правильний, mysql не підтримує транзакції при змінах DDL. Я повинен розглянути можливість самостійного очищення, наприклад, додавання та видалення стовпця з таблиць.
Леон Гуан,


1

У мене була помилка (у "add_column"):

def self.up

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

кінець

def self.down

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

кінець

а потім ваша проблема (неможливо скасувати частково невдалу міграцію). після невдалого гуглювання я запустив це:

def self.up

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

кінець

def self.down

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

кінець

як ви можете бачити, я просто додав лінію корекції від руки, а потім знову видалив її, перш ніж перевірити.


1

Відповідь Алехандро Бабіо надає найкращу поточну відповідь.

Ще одну деталь, яку я хочу додати:

Коли 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;запиту.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.