Skip to content

Commit 0a28656

Browse files
p-mongop
andauthored
RUBY-2166 Mark servers unknown when connections experience network errors during setup (#2111)
* RUBY-2166 Mark servers unknown when connections experience network errors during setup * exclude 4.4 servers Co-authored-by: Oleg Pudeyev <[email protected]>
1 parent 45946fc commit 0a28656

File tree

3 files changed

+72
-1
lines changed

3 files changed

+72
-1
lines changed

lib/mongo/server.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ def start_monitoring
311311
#
312312
# @since 2.0.0
313313
def inspect
314-
"#<Mongo::Server:0x#{object_id} address=#{address.host}:#{address.port}>"
314+
"#<Mongo::Server:0x#{object_id} address=#{address.host}:#{address.port} #{status}>"
315315
end
316316

317317
# @return [ String ] String representing server status (e.g. PRIMARY).

lib/mongo/server/connection_pool.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,9 @@ def connect_connection(connection)
766766
connection.disconnect!(reason: :error)
767767
raise
768768
end
769+
rescue Error::SocketError, Error::SocketTimeoutError => exc
770+
@server.unknown!(generation: exc.generation, stop_push_monitor: true)
771+
raise
769772
end
770773

771774
def check_invariants

spec/integration/sdam_error_handling_spec.rb

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,74 @@
197197
end
198198
end
199199

200+
describe 'when there is an error during connection establishment' do
201+
require_topology :single
202+
203+
# The push monitor creates sockets unpredictably and interferes with this
204+
# test.
205+
max_server_version '4.2'
206+
207+
# When TLS is used there are two socket classes and we can't simply
208+
# mock the base Socket class.
209+
require_no_tls
210+
211+
{
212+
SystemCallError => Mongo::Error::SocketError,
213+
Errno::ETIMEDOUT => Mongo::Error::SocketTimeoutError,
214+
}.each do |raw_error_cls, mapped_error_cls|
215+
context raw_error_cls.name do
216+
let(:socket) do
217+
double('mock socket').tap do |socket|
218+
allow(socket).to receive(:set_encoding)
219+
allow(socket).to receive(:setsockopt)
220+
allow(socket).to receive(:getsockopt)
221+
allow(socket).to receive(:connect)
222+
allow(socket).to receive(:close)
223+
socket.should receive(:write).and_raise(raw_error_cls, 'mocked failure')
224+
end
225+
end
226+
227+
it 'marks server unknown' do
228+
server = client.cluster.next_primary
229+
client.cluster.servers.map(&:disconnect!)
230+
231+
RSpec::Mocks.with_temporary_scope do
232+
233+
Socket.should receive(:new).with(any_args).ordered.once.and_return(socket)
234+
235+
lambda do
236+
client.command(ping: 1)
237+
end.should raise_error(mapped_error_cls, /mocked failure/)
238+
239+
server.should be_unknown
240+
end
241+
end
242+
243+
it 'recovers' do
244+
server = client.cluster.next_primary
245+
# If we do not kill the monitor, the client will recover automatically.
246+
247+
RSpec::Mocks.with_temporary_scope do
248+
249+
Socket.should receive(:new).with(any_args).ordered.once.and_return(socket)
250+
Socket.should receive(:new).with(any_args).ordered.once.and_call_original
251+
252+
lambda do
253+
client.command(ping: 1)
254+
end.should raise_error(mapped_error_cls, /mocked failure/)
255+
256+
client.command(ping: 1)
257+
end
258+
end
259+
end
260+
end
261+
262+
after do
263+
# Since we stopped monitoring on the client, close it.
264+
ClientRegistry.instance.close_all_clients
265+
end
266+
end
267+
200268
describe 'when there is an error on monitoring connection' do
201269
clean_slate_for_all
202270

0 commit comments

Comments
 (0)