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



我使用的是Ruby 1.9.2。

我有一个线程运行,使定期调用数据库。调用可能相当长,有时(由于各种原因)DB连接会消失。如果它消失了,线程就会永远挂起。

我想把它包装在一个timeout中来处理这个。问题是,在第二次调用超时时(总是第二次),它仍然只是挂起。超时时间不会生效。我知道这个问题在1.8中存在,但我被引导相信超时。

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

知道为什么会发生这种情况,我能做些什么吗?

一种可能是你的线程没有挂起,它实际上死了。下面是你应该做的事情来弄清楚发生了什么。在创建工作线程之前添加以下内容:

Thread.abort_on_exception = true

当一个异常在你的线程中被抛出时,你的整个进程被终止,你可以看到是哪个异常被抛出了。否则(这是默认值),线程将被终止。

如果不是的问题,请继续阅读…

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

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

相关内容

  • 没有找到相关文章

最新更新