Using Net::HTTP, I periodically find that the code below rescues from StandardError with a message of "execution expired", despite the fact that the web server logs from the accessed URL show the corresponding response was sent quickly. When the web server logs show a response that takes more than 5 seconds, I generally see the code rescue from Timeout::Error.
What case would cause the code below to rescue from StandardError with "execution expired" instead of rescuing from Timeout::Error?
This code is running in a multi-threaded program on a relatively ancient Ruby 1.9.3, on a platform that does not support newer versions of Ruby. Although the program is multi-threaded, the code shown only runs on a single thread.
begin
connection = Net::HTTP.new(uri.host, uri.port)
connection.open_timeout = 5
connection.read_timeout = 5
connection.start do |http|
request = Net::HTTP::Post.new("/reader_events")
request.body = body
response = http.request(request)
end
rescue StandardError => std_error
log "error sending event to server: #{std_error}"
rescue Timeout::Error => error
log "timeout sending event to server"
end
This is because how rescue
works. Take a look at documentation page for Exception
class. Basically you can create many exceptions that inherits from a single one, and handle all of them using rescue with the parent class:
begin
...
rescue Exception => exception
...
end
This code will rescue all types of exceptions as Exception
is the root (other exceptions inherits from it). In your case Timeout::Error
inherits from RuntimeError
which inherits from StandardError
:
Timeout::Error.ancestors
=> [Timeout::Error, RuntimeError, StandardError, Exception, Object, PP::ObjectMixin, Kernel, BasicObject]
As the result it is kind of Exception
:
Timeout::Error.new.is_a?(StandardError)
=> true
Another thing in your case is that interpreter will check each rescue
statement from the top to the bottom. It means that first it will check if the exception
is kind of StandardError
and later it will move to the following rescue
block. You should always list rescue
blocks from the most specific to the most general one.
Change the order of rescue
blocks to fix the code.