在进行数据库调用的线程中使用ruby超时

Hsi*_*Dai 5 ruby multithreading timeout

我使用的是Ruby 1.9.2.

我有一个运行的线程,它定期调用数据库.调用可能很长,有时(由于各种原因)数据库连接消失.如果它确实消失了,那么线程就会一直默默地挂在那里.

所以,我想在超时中包装它以处理这个问题.问题是,在第二次应该调用超时(总是第二次)时,它仍然只是挂起.超时永远不会生效.我知道这个问题存在于1.8中,但我认为timeout.rb工作在1.9.

t = Thread.new do
  while true do
    sleep SLEEPTIME
    begin
      Timeout::timeout(TIMEOUTTIME) do
        puts "About to do DB stuff, it will hang here on the second timeout"
        db.do_db_stuff()
        process_db_stuff()
      end
    rescue Timeout::Error
      puts "Timed out"
      #handle stuff here
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

知道为什么会这样,我能做些什么呢?

mol*_*olf 5

一种可能性是你的线程没有挂起,它实际上已经死了.这是你应该做些什么来弄清楚发生了什么.在创建工作线程之前添加此项:

Thread.abort_on_exception = true
Run Code Online (Sandbox Code Playgroud)

当你的线程中引发一个从未被捕获的异常时,你的整个进程就会被终止,你可以看到引发了哪个异常.否则(这是默认值),您的线程将被终止.

如果事实证明这不是问题,请继续阅读......

Ruby的超时实现非常幼稚.它设置一个单独的线程,它休眠n秒,然后盲目地在原始线程内引发一个Timeout异常.

现在,原始代码实际上可能位于块rescueensure块的中间.在这样的块中引发异常将无声地中止任何类型的清理代码.这可能会使代码超时处于不正确的状态.

很难确定这是否是你的问题,但看看数据库处理程序如何进行相当多的锁定和异常处理,可能很有可能.这篇文章更深入地解释了这个问题.

有什么方法可以使用数据库库的内置超时处理吗?它可能在较低级别实现,而不是使用Ruby的超时实现.

一个简单的替代方法是在单独的进程中调度数据库调用.每次执行繁重的数据库提升时,您都可以分叉主进程.或者您可以设置一个简单的cronjob来执行执行它的脚本.如果您需要与主线程进行通信,这将稍微困难一些.如果您想了解哪种选择可能符合您的需求,请留下更多详细信息.


根据你的评论,线程正在消亡.这可能是您可能或可能无法修复的库或应用程序代码中的错误.如果您希望捕获由数据库处理代码生成的任何错误并随后重试,您可以尝试以下操作:

t = Thread.new do
  loop do
    sleep INTERVAL
    begin
      # Execute database queries and process data
    rescue StandardError
      # Log error or recover from error situation before retrying
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

您也可以使用块中的retry关键字rescue立即重试,但是您可能应该保留一个计数器,以确保在不可恢复的错误不断发生时您不会无意中重试.