Jan 24, 2008 17:44
I am currently filled with hate and loathing. I'm going to need a few moments to cool off before I carry on working.
I was trying to make an http call from an irb session full of state I had carefully collected. Most of the code I used is in a script, but I had been doing a substantial amount of exploration over data from a few different place. While waiting for a large dataset from a busy server on the other end of a high-latency link, I got a timeout in the HTTP receive stuff. This in itself would not be a problem, except it crashed my irb session.
The problem is with the exception that a timeout raises:
module Timeout
class Error < Interrupt
end
end
The initiated will know that (a subset of) the Ruby exception hierarchy looks something like this:
Exception
- StandardError
- RuntimeError
- ZeroDivisionError
- ScriptError
- SyntaxError
- SystemExit
- SignalException
- Interrupt
The important bit there is that all the stuff you can reasonably expect to recover from is under StandardError. Because of this, a default rescue block will not catch anything that isn't a StandardError. The observant reader will notice that I helpfully showed Interrupt's position in the hierarchy. The observant reader will also notice that it is not a subclass of StandardError. This means that you need to catch it explicitly, or it will cause a crash.
Now, my theory is that Timeout was written by someone who misinterpreted "Interrupt" as meaning "interrupt what I'm doing", not "interrupt my application". This would be understandable, although still not acceptable, in a third-party library. Timeout is a core standard library module, however.
To summarise: If you're using the standard Ruby timeout mechanism, which you are if you use the HTTP libraries and almost certainly a whole host of others, you need to explicitly catch Timeout::Error or have a dodgy network bring your entire application crashing down around you.
programming,
ruby,
rant