从表中删除列后如何避免崩溃Rails应用程序?

时间:2015-09-15 08:25:24

标签: ruby-on-rails caching activerecord

问题

我刚刚了解到,我们目前在ActiveRecord迁移中删除列的做法是在实际删除列之前隐藏Rails列(通过丑陋的黑客攻击,详见下文)。

这是因为Rails缓存了SHOW FULL FIELDS查询。如果我们不解决这个问题,那么(长时间运行的)迁移将删除该列,到那时,Rails将已经缓存了这些字段。迁移完成并且列消失后,应用程序将随后崩溃,因为INSERT将因缓存而为不存在的列提供值。

在迁移中使用clear_table_cache!之类的东西是没有用的,因为我们部署到N个服务器并只在其中一个服务器上运行迁移。这将清除其中一台服务器上的缓存,但不是全部。

我们目前的解决方案(a.k.a.丑陋的黑客)

我们目前正在做的是在初始化程序中覆盖ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#columns并在运行迁移之前对其进行部署。

迁移完成后,我们删除覆盖的初始化程序并再次部署。

在这一点上,我无法相信我们是唯一遇到这个问题并且必须解决它的人。 此问题还有其他解决方案吗?

5 个答案:

答案 0 :(得分:5)

Rails 5对ActiveRecord::Baseadded ignored_columns有{{3}}:

  

由于某些列会随时出现或消失,因此您希望将这些列暂时保留给AR。否则,你可能会有一些知道列和其他人的过程。

以下是一些示例代码:

class User < ApplicationRecord
  self.ignored_columns = %w(employee_email)
end

答案 1 :(得分:3)

我之前使用过this approach

  

我们覆盖要删除其列的类的ActiveRecord::Base.columns方法。然后我们调用super来检索列并去掉标记为要删除的列。现在,类可以表现得就像列不存在一样。 [...]

class << self
  RemovedColumns = {'column_to_remove' => true}
  def columns
    cols = super
    cols.reject { |col| RemovedColumns.has_key? col.name }
  end
end

答案 2 :(得分:2)

我过去遇到过这个问题。我的方法可能会根据具体情况而有所不同,但我不止一次地使用{√}来评论schema.rb中的列并在运行迁移之前进行部署。

以下是步骤:

  1. 手动更改schema.rb并注释定义要删除的列的行。
  2. 部署更改并重新启动服务器。
  3. 创建并运行迁移。
  4. 部署更改并重新启动服务器。
  5. 当该字段被注释掉时,应用程序的运行版本已经忽略它,并且该列不在模型定义中。

    因此,删除它的迁移不需要清除属性缓存,即使迁移需要更长时间才能完成,应用程序也不会崩溃。当然,该领域不得在任何地方使用。

    这种方法非常简洁,因为它不涉及猴子修补或覆盖Rails。

答案 3 :(得分:1)

我经历了很多次。

您可以使用strong_migrations gem。 https://github.com/ankane/strong_migrations#removing-a-column

当您尝试创建“危险”迁移并给出建议时,宝石会抱怨。

答案 4 :(得分:0)

与margo之前的回答相同 只是更干净的imo

def self.columns
  super.reject { |col| col.name == 'notes' }
end