如何在运行`rake db:migrate`之后立即防止任何ActiveRecord :: PreparedStatementCacheExpired错误?

问题描述 投票:2回答:1

我正在使用Rails 5.x应用程序,并且我使用Postgres作为数据库。

我经常在生产服务器上运行rake db:migrate。有时,迁移会将新列添加到数据库,这会导致某些控制器操作因以下错误而崩溃:

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

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

我是否应该捕获ActiveRecord::PreparedStatementCacheExpired错误并重试save?还是应该向此特定的控制器操作添加一些锁定,以便在数据库迁移运行时不开始处理任何新请求?

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

ruby-on-rails database postgresql transactions database-migration
1个回答
0
投票

通过使用此retry_on_expired_cache帮助程序,我能够在某些地方解决此问题:

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

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

我终于知道我可以关闭prepare_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 env变量设置数据库配置,并且'prepared_statements' => false将被注入到配置中。

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

© www.soinside.com 2019 - 2024. All rights reserved.