【问题标题】:How can I prevent any ActiveRecord::PreparedStatementCacheExpired errors immediately after running `rake db:migrate`?如何在运行“rake db:migrate”后立即防止任何 ActiveRecord::PreparedStatementCacheExpired 错误?
【发布时间】:2020-01-03 09:25:07
【问题描述】:

我正在开发一个 Rails 5.x 应用程序,我使用 Postgres 作为我的数据库。

我经常在我的生产服务器上运行rake db:migrate。有时迁移会在数据库中添加一个新列,这会导致一些控制器操作崩溃并出现以下错误:

ActiveRecord::PreparedStatementCacheExpired: ERROR: cached plan must not change result type

这发生在需要零停机时间的关键控制器操作中,因此我需要找到一种方法来防止这种崩溃发生。

我是否应该捕获ActiveRecord::PreparedStatementCacheExpired 错误并重试save?或者我应该为这个特定的控制器操作添加一些锁定,这样我就不会在数据库迁移运行时开始处理任何新请求?

防止这种崩溃再次发生的最佳方法是什么?

【问题讨论】:

  • 我通常会挽救错误并重试保存,因为 ActiveRecord::PreparedStatementCacheExpired 实际上会在出错时清除缓存。然后应该可以进行第二次保存尝试。

标签: ruby-on-rails database postgresql transactions database-migration


【解决方案1】:

我可以通过使用这个retry_on_expired_cache helper 在某些地方解决这个问题:

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  class << self
    # Retry automatically on ActiveRecord::PreparedStatementCacheExpired.
    # (Do not use this for transactions with side-effects unless it is acceptable
    # for these side-effects to occasionally happen twice.)
    def retry_on_expired_cache(*_args)
      retried ||= false
      yield
    rescue ActiveRecord::PreparedStatementCacheExpired
      raise if retried

      retried = true
      retry
    end
  end
end

我会这样使用它:

  MyModel.retry_on_expired_cache do
    @my_model.save
  end

不幸的是,这就像玩“打地鼠”一样,因为在我的滚动部署期间,这种崩溃一直在我的应用程序中发生(我无法同时重新启动所有 Rails 进程。)

我终于知道我可以关闭prepared_statements来完全避免这个问题。 (见this other question and answers on StackOverflow。)

我担心性能损失,但我发现很多设置prepared_statements: false的人的报告,他们没有发现任何问题。例如https://news.ycombinator.com/item?id=7264171

我在config/initializers/disable_prepared_statements.rb创建了一个文件:

db_configuration = ActiveRecord::Base.configurations[Rails.env]
db_configuration.merge!('prepared_statements' => false)
ActiveRecord::Base.establish_connection(db_configuration)

这允许我继续从 DATABASE_URL 环境变量设置数据库配置,'prepared_statements' =&gt; false 将被注入到配置中。

这完全解决了ActiveRecord::PreparedStatementCacheExpired 错误,让我的服务更容易实现高可用性,同时仍然能够修改数据库。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-21
    • 1970-01-01
    • 2018-10-06
    • 1970-01-01
    相关资源
    最近更新 更多