Rails:不可逆转的迁移是不是很糟糕?

发布于 2024-07-14 14:20:56 字数 94 浏览 9 评论 0原文

什么时候可以在迁移的 self.down 方法中引发 ActiveRecord::IrreversibleMigration 异常? 您什么时候应该努力实际实施相反的迁移?

When is it acceptable to raise an ActiveRecord::IrreversibleMigration exception in the self.down method of a migration? When should you take the effort to actually implement the reverse of the migration?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

不气馁 2024-07-21 14:20:56

如果您正在处理生产级系统,那么是的,这非常糟糕。 如果这是您自己的宠物项目,那么任何事情都是允许的(如果没有别的,这将是一次学习经历:),尽管很可能迟早,即使在一个宠物项目中,您也会发现自己已经在一个反向迁移只需在几天后撤消该迁移,无论是通过 rake 还是手动。)

在生产场景中,您应该始终努力编写和测试 一个可逆的迁移,如果您在生产中经历它,然后发现一个错误,迫使您回滚(代码架构)到某个先前的修订(待定)一些重要的修复 - 以及否则无法使用的生产系统。)

反向迁移的范围从大部分微不足道(删除迁移期间添加的列或表,和/或更改列类型等)到稍微复杂一些(JOININSERTUPDATE 的执行),但是没有什么复杂到可以证明“将其隐藏起来” ”。 如果不出意外的话,强迫自己思考实现反向迁移的方法可以让您对正向迁移正在解决的问题有新的见解。

您有时可能会遇到前向迁移删除某个功能的情况,从而导致数据从数据库中被丢弃。 由于显而易见的原因,反向迁移无法恢复丢弃的数据。尽管在这种情况下,我们可以建议让正向迁移自动保存数据或在可能发生回滚时保留数据,作为彻底的替代方法失败(保存到 yml、复制/移动到特殊表等),您不必这样做,因为测试此类自动化过程所需的时间可能超过恢复该过程所需的时间手动数据(如果需要的话)。但即使在这种情况下,您也可以随时使反向迁移有条件且暂时失败,而不仅仅是失败,以等待某些用户操作(即测试是否存在某些必须手动恢复的表,如果丢失,则输出“我失败了,因为我无法从虚无中重新创建表XYZ;手动恢复表XYZ 从备份中再次运行我,我不会让你失望的!”)

If you are dealing with production-grade systems then yes, it is very bad. If it is your own pet project, then anything is allowed (if nothing else, it will be a learning experience :) though chances are that sooner rather than later, even in a pet project, you will find yourself having put a cross over a reverse migration only to have to undo that migration a few days later, be it via rake or manually.)

In a production scenario, you should always make the effort to write and test a reversible migration in the eventuality that you go through it in production, then discover a bug which forces you to roll back (code and schema) to some previous revision (pending some non-trivial fix -- and an otherwise unusable production system.)

Reverse migrations range from mostly trivial (removing columns or tables that were added during migration, and/or changing column types, etc.) to somewhat more involved (execute of JOINed INSERTs or UPDATEs), but nothing is so complex as to justify "sweeping it under the rug". If nothing else, forcing yourself to think of ways to achieve reverse migrations can give you new insight into the very problem that your forward migration is fixing.

You might occasionally run into a situation where a forward migration removes a feature, resulting in data being discarded from the database. For obvious reasons, the reverse migration cannot resuscitate discarded data. Although one could, in such cases, recommend having the forward migration automatically save the data or keep it around in the eventuality of rollback as an alternative to outright failure (save to yml, copy/move to a special table, etc.), you don't have to, as the time required to test such an automated procedure could exceed the time required to restore the data manually (should the need arise.) But even in such cases, instead of just failing, you can always make the reverse migration conditionally and temporarily fail pending some user action (i.e. test for the existence of some required table that has to be restored manually; if missing, output "I have failed because I cannot recreate table XYZ from nothingness; manually restore table XYZ from backup then run me again, and I will not fail you!")

Bonjour°[大白 2024-07-21 14:20:56

如果要销毁数据,可以先进行备份。
例如

def self.up
  # create a backup table before destroying data 
  execute %Q[create table backup_users select * from users]  
  remove_column :users, :timezone
end  

def self.down
  add_column :users, :timezone, :string  
  execute %Q[update users U left join backup_users B on (B.id=U.id) set U.timezone = B.timezone]
  execute %Q[drop table backup_users]  
end

If you are destroying data, you can make a backup of it first.
e.g.

def self.up
  # create a backup table before destroying data 
  execute %Q[create table backup_users select * from users]  
  remove_column :users, :timezone
end  

def self.down
  add_column :users, :timezone, :string  
  execute %Q[update users U left join backup_users B on (B.id=U.id) set U.timezone = B.timezone]
  execute %Q[drop table backup_users]  
end
我很坚强 2024-07-21 14:20:56

在生产场景中,您应该始终努力编写和测试可逆迁移,以防万一您在生产中经历它,然后发现一个错误,迫使您回滚(代码和架构)到以前的某个版本修订(等待一些重要的修复——以及否则无法使用的生产系统。)

可逆迁移对于开发和登台来说是很好的,但假设代码经过良好测试,那么您想要在生产中迁移的情况应该是极其罕见的。 我在生产模式下在迁移中构建了自动不可逆迁移。 如果我确实需要撤销更改,我可以使用另一个“向上”迁移或删除异常。 但这似乎很粗略。 任何会导致如此可怕的情况的错误都表明质量检查流程严重混乱。

In a production scenario, you should always make the effort to write and test a reversible migration in the eventuality that you go through it in production, then discover a bug which forces you to roll back (code and schema) to some previous revision (pending some non-trivial fix -- and an otherwise unusable production system.)

Having a reversible migration is fine for development and staging, but assuming well tested code it should be extremely rare that you would ever want to migrate down in production. I build into my migrations an automatic IrreversibleMigration in production mode. If I really needed to reverse a change, I could use another "up" migration or remove the exception. That seems sketchy though. Any bug that would cause a scenario this dire is a sign that the QA process is seriously screwed up.

一个人练习一个人 2024-07-21 14:20:56

感觉您需要不可逆转的迁移可能表明您面临着更大的问题。 也许一些细节会有帮助?

至于你的第二个问题:我总是花“努力”来写迁移的反向内容。 当然,实际上并没有编写.down,TextMate 在创建.up 时自动插入它。

Feeling like you need an irreversible migration is probably a sign you've got bigger problems looming. Maybe some specifics would help?

As for your second question: I always take the 'effort' to write the reverse of migrations. Of course, I don't actually write the .down, TextMate inserts it automatically when creating the .up.

甜`诱少女 2024-07-21 14:20:56

可逆数据迁移让您可以轻松使用 yaml 文件创建可逆数据迁移。

class RemoveStateFromProduct < ActiveRecord::Migration
  def self.up
    backup_data = []
    Product.all.each do |product|
      backup_data << {:id => product.id, :state => product.state}
    end
    backup backup_data
    remove_column :products, :state
  end
  def self.down
    add_column :products, :state, :string
    restore Product
  end
end

Reversible Data Migration makes it easy to create reversable data migrations using yaml files.

class RemoveStateFromProduct < ActiveRecord::Migration
  def self.up
    backup_data = []
    Product.all.each do |product|
      backup_data << {:id => product.id, :state => product.state}
    end
    backup backup_data
    remove_column :products, :state
  end
  def self.down
    add_column :products, :state, :string
    restore Product
  end
end
星星的軌跡 2024-07-21 14:20:56

IIRC,在迁移中更改数据类型时,您将拥有 IrreversibleMigration。

IIRC, you'll have the IrreversibleMigration when changing a datatype in the migration.

亣腦蒛氧 2024-07-21 14:20:56

我认为另一种可以接受的情况是当您进行整合迁移时。 在这种情况下,“向下”实际上没有意义,因为它会删除所有表(合并后添加的表除外)。 这可能不是你想要的。

I think another situation when it's ok is when you have a consolidated migration. In that case a "down" doesn't really make sense, as it would drop all the tables (except tables added after the consolidation). That's probably not what you'd want.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文