HTTP 応答時間を定期的にテストする必要がある URL でいっぱいのデータベースがあります。最近テストされていない URL に対して、常に多くのワーカー スレッドがデータベースを結合するようにしたいと考えています。URL が見つかった場合は、それをテストします。
もちろん、これにより、複数のスレッドがデータベースから同じ URL を取得する可能性があります。私はこれをしたくありません。そのため、これを防ぐためにミューテックスを使用しようとしています。データベース レベルには他のオプション (楽観的ロック、悲観的ロック) があることは認識していますが、少なくともこれが機能しない理由を理解したいと思います。
私が書いたこのテストコードを見てください:
threads = []
mutex = Mutex.new
50.times do |i|
threads << Thread.new do
while true do
url = nil
mutex.synchronize do
url = URL.first(:locked_for_testing => false, :times_tested.lt => 150)
if url
url.locked_for_testing = true
url.save
end
end
if url
# simulate testing the url
sleep 1
url.times_tested += 1
url.save
mutex.synchronize do
url.locked_for_testing = false
url.save
end
end
end
sleep 1
end
end
threads.each { |t| t.join }
もちろん、実際の URL テストはありません。しかし、最終的には、各 URL の "times_tested" が 150 になるはずですよね?
(私は基本的に、ミューテックスとワーカースレッドの考え方が機能していることを確認しようとしています)
しかし、私がそれを実行するたびに、あちこちのいくつかの奇妙な URL は、times_tested がはるかに小さい数、たとえば 37 に等しくなり、locked_for_testing が「true」でフリーズします。
コードからわかる限り、いずれかの URL がロックされた場合は、ロックを解除する必要があります。そのため、一部の URL がそのように「凍結」してしまう理由がわかりません。
例外はなく、begin/ensure を追加しようとしましたが、何もしませんでした。
何か案は?