Использование тайм-аута ruby ​​в потоке, выполняющем вызов базы данных - PullRequest
5 голосов
/ 04 октября 2011

Я использую 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

Есть идеи, почему это происходит, и что я могу с этим поделать?

1 Ответ

5 голосов
/ 05 октября 2011

Возможно, что ваш поток не зависает, на самом деле умирает . Вот что вы должны сделать, чтобы выяснить, что происходит. Добавьте это, прежде чем создавать свой рабочий поток:

Thread.abort_on_exception = true

Когда в вашем потоке возникает исключение, которое никогда не перехватывается, весь ваш процесс завершается, и вы видите, какое исключение было вызвано. В противном случае (и это по умолчанию) ваш поток уничтожается.

Если выясняется, что проблема не , читайте дальше ...

Реализация тайм-аутов в Ruby довольно наивна. Он устанавливает отдельный поток, который спит в течение n секунд, а затем вслепую вызывает исключение Timeout внутри исходного потока.

Теперь исходный код может фактически находиться в середине блока rescue или ensure. Вызов исключения в таком блоке молча прервет любой вид кода очистки. Это может привести к превышению времени ожидания кода в неправильном состоянии.

Довольно сложно сказать, является ли это именно вашей проблемой, но, видя, как обработчики базы данных могут выполнять значительную часть блокировок и обработки исключений, это может быть весьма вероятным. Вот статья, которая более подробно объясняет проблему .

Есть ли способ использовать встроенную обработку тайм-аутов вашей библиотеки базы данных? Это может быть реализовано на более низком уровне, без использования реализации времени ожидания 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

Вы также можете использовать ключевое слово retry в блоке rescue, чтобы повторить попытку немедленно, но вам, вероятно, следует сохранить счетчик, чтобы избежать случайных повторных попыток в течение неопределенного времени при возникновении неисправимой ошибки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...