diff --git a/lib/mongo/connection.rb b/lib/mongo/connection.rb index b8d83a0..c43bc56 100644 --- a/lib/mongo/connection.rb +++ b/lib/mongo/connection.rb @@ -795,7 +795,7 @@ module Mongo begin message = new_binary_string socket.read(length, message) - raise ConnectionFailure, "connection closed" unless message.length > 0 + raise ConnectionFailure, "connection closed" unless message && message.length > 0 if message.length < length chunk = new_binary_string while message.length < length diff --git a/lib/mongo/repl_set_connection.rb b/lib/mongo/repl_set_connection.rb index a247153..4b54037 100644 --- a/lib/mongo/repl_set_connection.rb +++ b/lib/mongo/repl_set_connection.rb @@ -119,8 +119,10 @@ module Mongo BSON::BSON_CODER.update_max_bson_size(self) else if @secondary_pools.empty? + close # close any existing pools and sockets raise ConnectionFailure, "Failed to connect any given host:port" else + close # close any existing pools and sockets raise ConnectionFailure, "Failed to connect to primary node." end end @@ -136,7 +138,7 @@ module Mongo # # @return [Boolean] def read_primary? - !@read_pool || @read_pool.length.zero? + !@read_pool end alias :primary? :read_primary? @@ -194,9 +196,13 @@ module Mongo check_set_name(config, socket) rescue OperationFailure, SocketError, SystemCallError, IOError => ex - close unless connected? + # It's necessary to rescue here. The #connect method will keep trying + # until it has no more nodes to try and raise a ConnectionFailure if + # it can't connect to a primary. ensure + socket.close if socket @nodes_tried << node + if config nodes = [] nodes += config['hosts'] if config['hosts'] @@ -208,8 +214,6 @@ module Mongo @logger.warn("MONGODB #{config['msg']}") end end - - socket.close if socket end config diff --git a/lib/mongo/util/pool.rb b/lib/mongo/util/pool.rb index 3130fe4..61cf394 100644 --- a/lib/mongo/util/pool.rb +++ b/lib/mongo/util/pool.rb @@ -76,7 +76,7 @@ module Mongo socket = TCPSocket.new(@host, @port) socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) rescue => ex - raise ConnectionFailure, "Failed to connect socket: #{ex}" + raise ConnectionFailure, "Failed to connect to host #{@host} and port #{@port}: #{ex}" end # If any saved authentications exist, we want to apply those diff --git a/test/replica_sets/query_secondaries.rb b/test/replica_sets/query_secondaries.rb index a6f1e1a..39cb040 100644 --- a/test/replica_sets/query_secondaries.rb +++ b/test/replica_sets/query_secondaries.rb @@ -17,8 +17,10 @@ class ReplicaSetQuerySecondariesTest < Test::Unit::TestCase end def test_read_primary - assert !@conn.read_primary? - assert !@conn.primary? + rescue_connection_failure do + assert !@conn.read_primary? + assert !@conn.primary? + end end def test_con @@ -59,6 +61,12 @@ class ReplicaSetQuerySecondariesTest < Test::Unit::TestCase # Should still be able to read immediately after killing master node RS.kill_primary assert_equal 2, @coll.find.to_a.length + rescue_connection_failure do + @coll.save({:a => 50}, :safe => {:w => 2, :wtimeout => 10000}) + end + RS.restart_killed_nodes + @coll.save({:a => 50}, :safe => {:w => 2, :wtimeout => 10000}) + assert_equal 4, @coll.find.to_a.length end def test_kill_secondary @@ -71,6 +79,7 @@ class ReplicaSetQuerySecondariesTest < Test::Unit::TestCase RS.kill(read_node) # Should fail immediately on next read + old_read_pool_port = @conn.read_pool.port assert_raise ConnectionFailure do @coll.find.to_a.length end @@ -80,6 +89,8 @@ class ReplicaSetQuerySecondariesTest < Test::Unit::TestCase length = @coll.find.to_a.length assert_equal 2, length end + new_read_pool_port = @conn.read_pool.port + assert old_read_pool != new_read_pool end end diff --git a/test/replica_sets/rs_test_helper.rb b/test/replica_sets/rs_test_helper.rb index 0611fa4..0864cf0 100644 --- a/test/replica_sets/rs_test_helper.rb +++ b/test/replica_sets/rs_test_helper.rb @@ -16,7 +16,7 @@ class Test::Unit::TestCase begin yield rescue Mongo::ConnectionFailure => ex - puts "Rescue attempt #{retries}" + puts "Rescue attempt #{retries}: from #{ex}" retries += 1 raise ex if retries > max_retries sleep(1)