RUBY-374 Close connection on SystemStackError, NoMemoryError, and SystemCallError

This commit is contained in:
Kyle Banker 2011-12-02 15:37:05 -05:00
parent d3c9637268
commit 03303b8409
5 changed files with 180 additions and 41 deletions

View File

@ -518,13 +518,21 @@ module Mongo
end end
def checkout_socket_from_connection def checkout_socket_from_connection
@checkin_connection = true socket = nil
if @command || @read_preference == :primary begin
@connection.checkout_writer @checkin_connection = true
else if @command || @read_preference == :primary
@read_pool = @connection.read_pool socket = @connection.checkout_writer
@connection.checkout_reader else
@read_pool = @connection.read_pool
socket = @connection.checkout_reader
end
rescue SystemStackError, NoMemoryError, SystemCallError => ex
@connection.close
raise ex
end end
socket
end end
def checkout_socket_for_op_get_more def checkout_socket_for_op_get_more
@ -540,9 +548,15 @@ module Mongo
pool.host == @read_pool.host && pool.port == @read_pool.port pool.host == @read_pool.host && pool.port == @read_pool.port
end end
if new_pool if new_pool
@read_pool = new_pool sock = nil
sock = new_pool.checkout begin
@checkin_read_pool = true @read_pool = new_pool
sock = new_pool.checkout
@checkin_read_pool = true
rescue SystemStackError, NoMemoryError, SystemCallError => ex
@connection.close
raise ex
end
return sock return sock
else else
raise Mongo::OperationFailure, "Failure to continue iterating " + raise Mongo::OperationFailure, "Failure to continue iterating " +

View File

@ -28,19 +28,25 @@ module Mongo
add_message_headers(message, operation) add_message_headers(message, operation)
packed_message = message.to_s packed_message = message.to_s
if connection == :writer sock = nil
sock = checkout_writer
else
sock = checkout_reader
end
begin begin
send_message_on_socket(packed_message, sock)
ensure
if connection == :writer if connection == :writer
checkin_writer(sock) sock = checkout_writer
else else
checkin_reader(sock) sock = checkout_reader
end
send_message_on_socket(packed_message, sock)
rescue SystemStackError, NoMemoryError, SystemCallError => ex
close
raise ex
ensure
if sock
if connection == :writer
checkin_writer(sock)
else
checkin_reader(sock)
end
end end
end end
end end
@ -66,14 +72,18 @@ module Mongo
last_error_id = add_message_headers(last_error_message, Mongo::Constants::OP_QUERY) last_error_id = add_message_headers(last_error_message, Mongo::Constants::OP_QUERY)
packed_message = message.append!(last_error_message).to_s packed_message = message.append!(last_error_message).to_s
sock = checkout_writer sock = nil
begin begin
sock = checkout_writer
send_message_on_socket(packed_message, sock) send_message_on_socket(packed_message, sock)
docs, num_received, cursor_id = receive(sock, last_error_id) docs, num_received, cursor_id = receive(sock, last_error_id)
checkin_writer(sock) checkin_writer(sock)
rescue ConnectionFailure, OperationFailure, OperationTimeout => ex rescue ConnectionFailure, OperationFailure, OperationTimeout => ex
checkin_writer(sock) checkin_writer(sock)
raise ex raise ex
rescue SystemStackError, NoMemoryError, SystemCallError => ex
close
raise ex
end end
if num_received == 1 && (error = docs[0]['err'] || docs[0]['errmsg']) if num_received == 1 && (error = docs[0]['err'] || docs[0]['errmsg'])
@ -103,24 +113,29 @@ module Mongo
read=:primary, exhaust=false) read=:primary, exhaust=false)
request_id = add_message_headers(message, operation) request_id = add_message_headers(message, operation)
packed_message = message.to_s packed_message = message.to_s
if socket
sock = socket
should_checkin = false
else
if command || read == :primary
sock = checkout_writer
elsif read == :secondary
sock = checkout_reader
else
sock = checkout_tagged(read)
end
should_checkin = true
end
result = '' result = ''
sock = nil
begin begin
if socket
sock = socket
should_checkin = false
else
if command || read == :primary
sock = checkout_writer
elsif read == :secondary
sock = checkout_reader
else
sock = checkout_tagged(read)
end
should_checkin = true
end
send_message_on_socket(packed_message, sock) send_message_on_socket(packed_message, sock)
result = receive(sock, request_id, exhaust) result = receive(sock, request_id, exhaust)
rescue SystemStackError, NoMemoryError, SystemCallError => ex
close
raise ex
ensure ensure
if should_checkin if should_checkin
if command || read == :primary if command || read == :primary

View File

@ -300,11 +300,16 @@ module Mongo
# if no read pool has been defined. # if no read pool has been defined.
def checkout_reader def checkout_reader
connect unless connected? connect unless connected?
socket = get_socket_from_pool(self.read_pool) begin
socket = get_socket_from_pool(self.read_pool)
if !socket if !socket
connect connect
socket = get_socket_from_pool(self.primary_pool) socket = get_socket_from_pool(self.primary_pool)
end
rescue => ex
checkin(socket) if socket
raise ex
end end
if socket if socket
@ -317,11 +322,16 @@ module Mongo
# Checkout a socket for writing (i.e., a primary node). # Checkout a socket for writing (i.e., a primary node).
def checkout_writer def checkout_writer
connect unless connected? connect unless connected?
socket = get_socket_from_pool(self.primary_pool) begin
if !socket
connect
socket = get_socket_from_pool(self.primary_pool) socket = get_socket_from_pool(self.primary_pool)
if !socket
connect
socket = get_socket_from_pool(self.primary_pool)
end
rescue => ex
checkin(socket)
raise ex
end end
if socket if socket

View File

@ -278,6 +278,58 @@ class TestConnection < Test::Unit::TestCase
end end
end end
context "Socket pools" do
context "checking out writers" do
setup do
@con = standard_connection(:pool_size => 10, :timeout => 10)
@coll = @con[MONGO_TEST_DB]['test-connection-exceptions']
end
should "close the connection on send_message for major exceptions" do
@con.expects(:checkout_writer).raises(SystemStackError)
@con.expects(:close)
begin
@coll.insert({:foo => "bar"})
rescue SystemStackError
end
end
should "close the connection on send_message_with_safe_check for major exceptions" do
@con.expects(:checkout_writer).raises(SystemStackError)
@con.expects(:close)
begin
@coll.insert({:foo => "bar"}, :safe => true)
rescue SystemStackError
end
end
should "close the connection on receive_message for major exceptions" do
@con.expects(:checkout_writer).raises(SystemStackError)
@con.expects(:close)
begin
@coll.find.next
rescue SystemStackError
end
end
end
context "checking out readers" do
setup do
@con = standard_connection(:pool_size => 10, :timeout => 10, :slave_ok => true)
@coll = @con[MONGO_TEST_DB]['test-connection-exceptions']
end
should "close the connection on receive_message for major exceptions" do
@con.expects(:checkout_reader).raises(SystemStackError)
@con.expects(:close)
begin
@coll.find.next
rescue SystemStackError
end
end
end
end
context "Connection exceptions" do context "Connection exceptions" do
setup do setup do
@con = standard_connection(:pool_size => 10, :timeout => 10) @con = standard_connection(:pool_size => 10, :timeout => 10)

View File

@ -43,4 +43,52 @@ class BasicTest < Test::Unit::TestCase
assert_equal 90, @conn.refresh_interval assert_equal 90, @conn.refresh_interval
assert_equal @conn.refresh_mode, false assert_equal @conn.refresh_mode, false
end end
context "Socket pools" do
context "checking out writers" do
setup do
seeds = [[self.rs.host, self.rs.ports[0]], [self.rs.host, self.rs.ports[1]],
[self.rs.host, self.rs.ports[2]]]
args = seeds << {:name => self.rs.name}
@con = ReplSetConnection.new(*args)
@coll = @con[MONGO_TEST_DB]['test-connection-exceptions']
end
should "close the connection on send_message for major exceptions" do
@con.expects(:checkout_writer).raises(SystemStackError)
@con.expects(:close)
begin
@coll.insert({:foo => "bar"})
rescue SystemStackError
end
end
should "close the connection on send_message_with_safe_check for major exceptions" do
@con.expects(:checkout_writer).raises(SystemStackError)
@con.expects(:close)
begin
@coll.insert({:foo => "bar"}, :safe => true)
rescue SystemStackError
end
end
should "close the connection on receive_message for major exceptions" do
@con.expects(:checkout_writer).raises(SystemStackError)
@con.expects(:close)
begin
@coll.find.next
rescue SystemStackError
end
end
should "close the connection on receive_message for major exceptions" do
@con.expects(:checkout_reader).raises(SystemStackError)
@con.expects(:close)
begin
@coll.find({}, :read => :secondary).next
rescue SystemStackError
end
end
end
end
end end