RUBY-236 set op_timeout for socket receive timeouts

This commit is contained in:
Kyle Banker 2011-03-28 11:09:27 -04:00
parent b48a2bd84f
commit e49d50acc2
4 changed files with 49 additions and 11 deletions

View File

@ -272,6 +272,14 @@ Notes:
* Cursors will timeout on the server after 10 minutes. If you need to keep a cursor
open for more than 10 minutes, specify `:timeout => false` when you create the cursor.
## Socket timeouts
The Ruby driver support timeouts on socket read operations. To enable them, set the
`:op_timeout` option when you create a `Mongo::Connection` object.
If implementing higher-level timeouts, using tools like `Rack::Timeout`, it's very important
to call `Mongo::Connection#close` to prevent the subsequent operation from receiving the previous
request.
# Testing

View File

@ -76,4 +76,19 @@ if RUBY_PLATFORM =~ /java/
end
require 'mongo/gridfs/grid_file_system'
# Use SystemTimer on Ruby 1.8
if !defined?(RUBY_ENGINE) || (RUBY_ENGINE == 'ruby' && RUBY_VERSION < '1.9.0')
begin
require 'system_timer'
Mongo::TimeoutHandler = SystemTimer
rescue LoadError
warn "Could not load SystemTimer gem. Falling back to timeout.rb." +
"SystemTimer is STRONGLY recommended for timeouts in Ruby 1.8.7. " +
"See http://ph7spot.com/musings/system-timer for details."
require 'timeout'
Mongo::TimeoutHandler = Timeout
end
else
require 'timeout'
Mongo::TimeoutHandler = Timeout
end

View File

@ -68,6 +68,8 @@ module Mongo
# @option opts [Float] :timeout (5.0) When all of the connections a pool are checked out,
# this is the number of seconds to wait for a new connection to be released before throwing an exception.
# Note: this setting is relevant only for multi-threaded applications (which in Ruby are rare).
# @option opts [Float] :op_timeout (nil) The number of seconds to wait for a read operation to time out.
# Disabled by default.
#
# @example localhost, 27017
# Connection.new
@ -598,6 +600,9 @@ module Mongo
@pool_size = opts[:pool_size] || 1
@timeout = opts[:timeout] || 5.0
# Timeout on socket read operation.
@op_timeout = opts[:op_timeout] || nil
# Mutex for synchronizing pool access
@connection_mutex = Mutex.new
@ -833,19 +838,26 @@ module Mongo
def receive_message_on_socket(length, socket)
begin
message = new_binary_string
socket.read(length, message)
raise ConnectionFailure, "connection closed" unless message && message.length > 0
if message.length < length
chunk = new_binary_string
while message.length < length
socket.read(length - message.length, chunk)
raise ConnectionFailure, "connection closed" unless chunk.length > 0
message << chunk
Mongo::TimeoutHandler.timeout(@op_timeout, OperationTimeout) do
socket.read(length, message)
raise ConnectionFailure, "connection closed" unless message && message.length > 0
if message.length < length
chunk = new_binary_string
while message.length < length
socket.read(length - message.length, chunk)
raise ConnectionFailure, "connection closed" unless chunk.length > 0
message << chunk
end
end
end
rescue => ex
rescue => ex
close
raise ConnectionFailure, "Operation failed with the following exception: #{ex}"
if ex.class == OperationTimeout
raise OperationTimeout, "Timed out waiting in socket read."
else
raise ConnectionFailure, "Operation failed with the following exception: #{ex}"
end
end
message
end

View File

@ -57,6 +57,9 @@ module Mongo
# Raised when a database operation fails.
class OperationFailure < MongoDBError; end
# Raised when a socket read operation times out.
class OperationTimeout < ::Timeout::Error; end
# Raised when a client attempts to perform an invalid operation.
class InvalidOperation < MongoDBError; end