【问题标题】:Rolling back a failed Rails migration回滚失败的 Rails 迁移
【发布时间】:2010-10-15 18:29:13
【问题描述】:

如何回滚失败的 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:迁移============================================== ======== -- add_column(:assets, :test, :integer) -> 0.0932 秒 -- add_column(:asset, :error) 耙中止! 发生错误,所有后续迁移均已取消: 参数数量错误(2 对 3)

好的,让我们回滚吧:

$ 耙分贝:回滚 == AddLevelsToRoles:还原 ============================================= == -- remove_column(:roles, :level) -> 0.0778 秒 == AddLevelsToRoles:恢复(0.0779s)=======================================

嗯?那是我在 SimpleTest 之前的最后一次迁移,而不是失败的迁移。 (哦,如果迁移输出包含版本号,那就太好了。)

所以让我们尝试为失败的迁移 SimpleTest 运行 down:

$ rake db:migrate:down VERSION=20090326173033 $

什么都没有发生,也没有输出。但也许它还是运行了迁移?因此,让我们修复 SimpleTest 迁移中的语法错误,并尝试再次运行它。

$ rake db:migrate:up VERSION=20090326173033 == SimpleTest:迁移============================================== ======== -- add_column(:assets, :test, :integer) 耙中止! Mysql::Error: Duplicate column name 'test': ALTER TABLE `assets` ADD `test` int(11)

不。显然 migrate:down 没有用。它没有失败,只是没有执行。

除了手动进入数据库并删除它,然后运行测试之外,没有办法摆脱那个重复的表。一定有比这更好的方法。

【问题讨论】:

  • 理论上我认为 Rails 可以“记住”已应用的操作,并为您撤消可逆操作。但它没有:(

标签: mysql ruby-on-rails rails-activerecord rails-migrations


【解决方案1】:

很遗憾,您必须手动清理 MySQL 的失败迁移。 MySQL 不支持事务性数据库定义更改。

Rails 2.2 包括 PostgreSQL 的事务迁移。 Rails 2.3 包含 SQLite 的事务迁移。

这并不能真正帮助您解决目前的问题,但如果您在未来的项目中可以选择数据库,我建议您使用支持事务性 DDL 的数据库,因为它使迁移更加愉快。

更新 - 这在 2017 年仍然如此,在 Rails 4.2.7 和 MySQL 5.7 上,由 Alejandro Babio 在此处的另一个答案中报告。

【讨论】:

  • 太好了,谢谢。我将使用 PGSQL 做新项目,所以很高兴知道这是一个选项。
  • 这仍然是最好的答案,所以这值得恕我直言。
【解决方案2】:

要转到指定的版本,只需使用:

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

但如果迁移中途失败,您必须先清理它。一种方法是:

  • 编辑迁移的down 方法,以撤消up 中起作用的部分
  • 迁移回之前的状态(您开始的地方)
  • 修复迁移(包括撤消对 down 的更改)
  • 再试一次

【讨论】:

  • 谢谢。是的,我知道我可以一直重新迁移到失败的迁移,但是如果我有很长的迁移历史,这有时可能会出现问题。理想情况下,它们应该执行得很好,但我经常让它们中途失败,然后就会出现更大的混乱:-)
【解决方案3】:

好的,伙计们,这就是你的实际操作方式。我不知道上面的答案在说什么。

  1. 找出向上迁移的哪个部分有效。将它们注释掉。
  2. 还要注释掉/删除迁移中中断的部分。
  3. 再次运行迁移。现在它将完成迁移的未损坏部分,跳过已经完成的部分。
  4. 取消注释您在第 1 步中注释掉的迁移位。

如果你想验证你现在已经得到它,你可以向下迁移并再次备份。

【讨论】:

  • 我做了一些非常相似的事情,但我将第 2 步替换为“修复损坏的迁移部分。”
  • 值得强调最后一点 - 运行bundle exec rake db:migrate:redo。它将后退一步,然后前进一步,因此您可以验证您的最新迁移是否一直运行。每当您必须将迁移与一些代码更新一起推送时,这是一个很好的做法。
【解决方案4】:

我同意您应该尽可能使用 PostgreSQL。但是,当您被 MySQL 卡住时,您可以通过首先尝试在测试数据库上迁移来避免大多数这些问题:

rake db:migrate RAILS_ENV=test

您可以恢复到以前的状态,然后再试一次

rake db:schema:load RAILS_ENV=test

【讨论】:

  • 更多的是解决方法而不是答案,但这是我以前没有想到的好主意。
  • 这是一个很好的答案 - 建议使用不同的工作流程来避免问题
【解决方案5】:

在 2015 年的 Rails 4.2.1 和 MySQL 5.7 中,无法使用 Rails 提供的标准 rake 操作修复失败的迁移,就像 2009 年一样。

MySql 不支持回滚 DDL 语句(MySQL 5.7 Manual)。而 Rails 对此无能为力。

此外,我们可以检查 Rails 是如何完成这项工作的:迁移是 wrapped in a transaction,具体取决于连接适配器如何响应 :supports_ddl_transactions?。在 rails source (v 4.2.1) 搜索此操作后,我发现只有 Sqlite3 和 PostgreSql 支持事务,而 default 不支持。

编辑 因此,当前对原始问题的回答是:必须手动修复失败的 MySQL 迁移。

【讨论】:

  • 我不太明白这个答案:除了更新版本号之外,它不会对原始接受的答案增加任何内容。
  • 非常正确,对于原始问题。对于 Andrew Grimm 的赏金开始:“想知道自 2009 年 3 月提出问题以来情况是否发生了变化。”这是一个当前的答案,并提供了一种检查未来任何变化的方法。
【解决方案6】:

执行此操作的简单方法是将所有操作包装在事务中:

class WhateverMigration < ActiveRecord::Migration

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

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

end

正如 Luke Francl 所指出的,“MySql['s MyISAM 表不] 支持事务”——这就是为什么您可以考虑总体上避免使用 MySQL 或至少特别避免使用 MyISAM。

如果您使用的是 MySQL 的 InnoDB,那么上面的操作就可以了。 up 或 down 中的任何错误都将退出。

注意某些类型的操作无法通过交易恢复。通常,表更改(删除表、删除或添加列等)无法回滚。

【讨论】:

  • 这不是 MyISAM 或 InnoDB 的问题。 InnoDB 支持事务,但不支持事务数据库定义 (DDL) 更改。在 PostgreSQL 中,您可以删除一个表,然后回滚该更改!
  • Luke 是正确的,mysql 不支持 DDL 更改的事务。我必须自己考虑清理,例如从表中添加和删除列。
【解决方案7】:

从控制台运行向下迁移:

http://gilesbowkett.blogspot.com/2007/07/how-to-use-migrations-from-console.html(点击进入他的馅饼)

【讨论】:

    【解决方案8】:

    我有一个错字(在“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
    

    结束

    如您所见,我只是手动添加了更正线,然后在签入之前再次将其删除。

    【讨论】:

      【解决方案9】:

      上述亚历杭德罗·巴比奥的答案提供了当前最佳答案。

      我想补充一点:

      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; 查询来逆转。

      【讨论】:

        猜你喜欢
        • 2011-10-31
        • 1970-01-01
        • 2018-10-31
        • 2021-01-20
        • 2019-12-06
        • 2015-12-07
        • 1970-01-01
        • 1970-01-01
        • 2017-03-23
        相关资源
        最近更新 更多