ndb*_*ent 5 database postgresql transactions ruby-on-rails database-migration
我正在开发 Rails 5.x 应用程序,并使用 Postgres 作为数据库。
rake db:migrate我经常在生产服务器上运行。有时,迁移会向数据库添加新列,这会导致某些控制器操作崩溃并出现以下错误:
ActiveRecord::PreparedStatementCacheExpired: ERROR: cached plan must not change result type
Run Code Online (Sandbox Code Playgroud)
这种情况发生在需要零停机时间的关键控制器操作中,因此我需要找到一种方法来防止这种崩溃发生。
我应该捕获ActiveRecord::PreparedStatementCacheExpired错误并重试吗save?或者我应该向这个特定的控制器操作添加一些锁定,以便在数据库迁移运行时不会开始服务任何新请求?
防止此类事故再次发生的最佳方法是什么?
我可以使用这个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
Run Code Online (Sandbox Code Playgroud)
我会这样使用它:
MyModel.retry_on_expired_cache do
@my_model.save
end
Run Code Online (Sandbox Code Playgroud)
不幸的是,这就像玩“打地鼠”游戏,因为在滚动部署期间,这种崩溃在我的应用程序中不断发生(我无法同时重新启动所有 Rails 进程。)
我终于了解到我可以关闭prepared_statements来完全避免这个问题。(请参阅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)
Run Code Online (Sandbox Code Playgroud)
这允许我继续从DATABASE_URL环境变量设置数据库配置,并将'prepared_statements' => false被注入到配置中。
这完全解决了ActiveRecord::PreparedStatementCacheExpired错误,并使我的服务更容易实现高可用性,同时仍然能够修改数据库。
| 归档时间: |
|
| 查看次数: |
2145 次 |
| 最近记录: |