Home

Awesome

Why Timeout module is broken

Find out by yourself by running the scripts. You need local MongoDB running on standard port 27017. You may need to change the timeout values for some of the examples to work on your machine.

$ bundle install

$ ruby 1_connect_and_insert.rb
................................................................................
................................................................................
................................................................................
................................................................................
............................................................................^C

Mongo takes its time, no connection errors happen for the most part.

$ ruby 2_connect_and_insert_with_timeout.rb
....................TTTTT....T...........................................T......
................................TT.T...T....................................TTTT
.........TT.....................................................................
....T..................T....................................T...........TTTTT...
............................................................................^C

Placing a time limit will cause timeouts for some requests, as expected. This will leak connections over time though.

$ ruby 3_connect_once_then_insert_with_timeout.rb
..........................................T.....................................
.............................TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT^C

Unfortunately, timeout implementation may cause a deadlock, if the timed out block still holds the lock to a resource (in this case some internal lock of the mongo driver). Killing threads isn't such a good idea after all.

$ ruby 4_connect_with_pool_once_then_insert_with_timeout.rb
................................................................................
................................................................................
................................................................................
.........................................................TTTTTTTTTTTTTTTTTTTTTTT
TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT^C

Maintaining a connections pool doesn't solve anything either.

$ ruby 5_connect_with_op_timeout_then_insert.rb
................................................................................
................................................................................
........................T.......................................................
................................................................................
.................................................T..........................^C

Using Mongo's op_timeout option does the trick as it uses IO.select instead of Timeout.

If you use Timeout in a web application, you may experience connections hang in SYN_RECV state. Stracing the Ruby process will reveal it is hanging on a call to futex.

Affected versions

I tested this under the following Ruby versions:

Further reading