18

在数据库停机后,Rails 将首先抛出一次此错误:

ActiveRecord::StatementInvalid: NativeException: org.postgresql.util.PSQLException: 连接被拒绝。检查主机名和端口是否正确以及 postmaster 是否接受 TCP/IP 连接。

从那时起,每个数据库调用都会出现以下错误,即使在数据库备份之后:

ActiveRecord::StatementInvalid: ActiveRecord::JDBCError: 此连接已关闭。

为了让服务器再次运行,我必须重新启动 rails 服务器。这对我们来说并不理想,因为我们的产品工程师希望对我们的数据库进行维护,而不必同时恢复所有依赖于数据库的服务。所以,我想知道 - 有没有办法自动让 Rails 尝试重新建立数据库连接或推荐的方法来获得这种行为?

我尝试过的事情:

我已经尝试在我的数据库选项中将 reconnect 设置为 true,这样,我可以终止单个数据库连接,并且 rails 将重新建立连接。但是,它不会在数据库中断之后。我发现从命令控制台我可以通过调用来恢复连接

ActiveRecord::Base::建立连接

所以也许找到一个干净的地方让rails调用上面的命令这会起作用吗?有什么建议么?

4

5 回答 5

3

我对 Mysql2Adapter 有同样的问题。我通过一个很长的查询复制了失败User.find_all_by_id((1..1000000).to_a):从现在开始,所有 ActiveRecord 请求都失败(User.first 失败)

这是我解决它的方法:

问题很简单:每当遇到上述异常时,我们都想重新建立连接并重试。我们通过给execute方法起别名,用begin rescue包装它,并在rescue中重新建立数据库连接来解决这个问题。

对于 Mysql,代码在 Mysql2Adapter 中,下面是修复:

将此代码放在 config/initializers/active_record.rb

module ActiveRecord
  module ConnectionAdapters
    class Mysql2Adapter < AbstractMysqlAdapter
      alias_method :old_execute, :execute

      def execute(sql, name=nil)
        begin
          old_execute(sql, name)
        rescue ActiveRecord::StatementInvalid
          # you can do some logging here

          ActiveRecord::Base.establish_connection

          old_execute(sql, name)
        end
      end
    end
  end
end

您需要对 postgres 适配器执行相同的操作。

https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb

我认为由于 select 是最常用的查询,您可以为 select 设置别名,一旦失败,连接就会重新建立。

您想创建一个初始化程序 (config/initializers/active_record.rb) 和别名 select_rows (它可能是别的东西,只需找到正确的方法并修补它。它可能是 async_exec 或执行,我对 Postgres 的研究不多适配器)在:

module ConnectionAdapters::PostgreSQLAdapter
      module DatabaseStatements
      end
end
于 2013-04-03T19:09:42.983 回答
2

这是一个非常丑陋的解决方案,但我认为它应该有效。

  • 将 reconnect 设置为 true,就像您之前所做的那样。

  • 编辑文件activerecord-X.Y.Z/lib/active_record/connection_adapters/postgresql_adapter.rb并更改reconnect!方法说

    def reconnect!
      clear_cache!
      ActiveRecord::Base.establish_connection
    end
    

需要更多的研究

  • 检查它是否真的有效
  • 检查它是否没有同时多次调用建立连接(在这种情况下你需要一个锁)
  • 检查是否有更好的地方放置此代码。Ruby 允许您在运行时重新定义任何方法,但您需要加载符号。换句话说,您需要存在 PostgreSQLAdapter 类。我最接近加载该符号的是config/environment.rbafter initialize!,但堆栈中的深度仍不足以加载该符号。

如果您确实在 ActiveRecord 代码之外找到了已加载符号的位置,并且您可以编辑其方法,则将以下代码放入其中:

class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::StatementPool
  def reconnect!
    clear_cache!
    ActiveRecord::Base.establish_connection
  end
end

更重要的是,实际调用establish_connection. 可以在该方法中调用重要的东西,以避免一些开销。

让我知道这是否有帮助,以及您是否取得了任何进展。

于 2012-11-01T14:04:40.007 回答
0

我不确定如何完全按照您的要求进行操作,但我有另一个“流程”建议:设置简单的脚本,以便您的产品工程师可以轻松地停止和启动所有应用程序。

开发一组 capistrano 配方(或其他脚本),您的产品工程师可以使用它们来停止和启动所有应用程序。对于一个普通的 Rails 应用程序,您真正需要做的就是放置一个维护页面,以便 nginx 或 apache 为该页面提供服务,而不是将请求转发到 rails 实例。理想情况下,rails worker 停止接收请求,db 关闭,db 出现,然后维护页面被删除,workers 再次收到请求,从来没有意识到数据库消失了一段时间。

在后台工作人员的情况下,他们可能需要由脚本实际停止和启动,除非他们的队列为空并保持为空。如果任何计划的 rake 任务或其他计划的作业都依赖于数据库并在数据库关闭时运行,它们可能会失败,因此当您通常进行数据库维护时,您需要尝试安排它们在窗口外运行。

如果您的产品工程师不喜欢运行脚本(!),您可能可以设置一个不错的 Web 界面以方便他们使用。这可能不仅对处理数据库连接错误有用,因为它将使组织中的更多人能够处理基本的事情,例如停止和启动应用程序。

于 2012-11-01T13:17:24.133 回答
0

我建议使用外部工具/脚本来监控此类事件。当数据库在一定的空闲时间后终止连接时,Activerecord 的重新连接起作用,并且在一定的故障时间后它会放弃。所以这对你的情况没有帮助。

我认为您应该编写自己的脚本来监视数据库的状态。如果它在一段时间后恢复,只需重新启动您的 rails 应用程序。

此外,无论如何你都需要那些监控的东西,比如你的服务器的内存、cpu 和磁盘使用情况、服务器负载、数据库状态等等。只是一个稍微定制的监视器规则。

于 2012-11-11T15:53:14.723 回答
0

在某处插入救援

rescue ActiveRecord::StatementInvalid: ActiveRecord::JDBCError
  ActiveRecord::Base::establish_connection
  retry

但是哪里?我不知道

您也可以在 ApplicationController 中使用救援。但这不会重试失败的操作,因此您可能还应该渲染一些错误模板

rescue_from ActiveRecord::StatementInvalid: ActiveRecord::JDBCError do
  ActiveRecord::Base::establish_connection
  render 'errors/error', :status => 500
end
于 2012-09-26T11:48:41.830 回答