mongo 2.24.1 → 2.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/mongo_console +2 -2
- data/lib/mongo/address/validator.rb +1 -1
- data/lib/mongo/address.rb +1 -3
- data/lib/mongo/auth/aws/conversation.rb +0 -4
- data/lib/mongo/auth/aws/credentials_retriever.rb +3 -8
- data/lib/mongo/auth/base.rb +1 -1
- data/lib/mongo/auth/sasl_conversation_base.rb +1 -1
- data/lib/mongo/auth/scram_conversation_base.rb +1 -8
- data/lib/mongo/auth/stringprep.rb +1 -6
- data/lib/mongo/auth/user/view.rb +1 -1
- data/lib/mongo/bulk_write/result_combiner.rb +3 -3
- data/lib/mongo/bulk_write.rb +1 -1
- data/lib/mongo/client.rb +82 -12
- data/lib/mongo/cluster/reapers/cursor_reaper.rb +12 -5
- data/lib/mongo/cluster/sdam_flow.rb +4 -4
- data/lib/mongo/cluster/topology/base.rb +1 -1
- data/lib/mongo/cluster.rb +12 -28
- data/lib/mongo/collection/view/aggregation/behavior.rb +1 -1
- data/lib/mongo/collection/view/aggregation.rb +2 -4
- data/lib/mongo/collection/view/builder/aggregation.rb +3 -5
- data/lib/mongo/collection/view/builder/map_reduce.rb +1 -1
- data/lib/mongo/collection/view/change_stream.rb +14 -9
- data/lib/mongo/collection/view/map_reduce.rb +2 -11
- data/lib/mongo/collection/view/readable.rb +11 -7
- data/lib/mongo/collection/view/writable.rb +4 -4
- data/lib/mongo/collection/view.rb +8 -4
- data/lib/mongo/collection.rb +1 -1
- data/lib/mongo/crypt/auto_encrypter.rb +1 -1
- data/lib/mongo/crypt/binary.rb +7 -4
- data/lib/mongo/crypt/binding.rb +88 -10
- data/lib/mongo/crypt/context.rb +50 -19
- data/lib/mongo/crypt/encryption_io.rb +11 -5
- data/lib/mongo/crypt/handle.rb +17 -5
- data/lib/mongo/crypt/kms/credentials.rb +70 -31
- data/lib/mongo/crypt/kms/kmip/master_document.rb +30 -1
- data/lib/mongo/crypt/kms/master_key_document.rb +11 -6
- data/lib/mongo/crypt/kms.rb +12 -1
- data/lib/mongo/cursor.rb +8 -2
- data/lib/mongo/database/cursor_command_view.rb +95 -0
- data/lib/mongo/database.rb +144 -18
- data/lib/mongo/error/invalid_uri.rb +5 -1
- data/lib/mongo/error/operation_failure.rb +2 -2
- data/lib/mongo/error/parser.rb +3 -3
- data/lib/mongo/error/sdam_error_detection.rb +2 -3
- data/lib/mongo/grid/fs_bucket.rb +27 -21
- data/lib/mongo/grid/stream/write.rb +2 -2
- data/lib/mongo/index/view.rb +13 -7
- data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_check_out_started.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_closed.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_created.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_ready.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/pool_cleared.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/pool_closed.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/pool_created.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/pool_ready.rb +1 -0
- data/lib/mongo/monitoring/event/command_failed.rb +1 -0
- data/lib/mongo/monitoring/event/command_started.rb +1 -0
- data/lib/mongo/monitoring/event/command_succeeded.rb +1 -0
- data/lib/mongo/monitoring/event/secure.rb +12 -17
- data/lib/mongo/monitoring/event/server_closed.rb +1 -0
- data/lib/mongo/monitoring/event/server_description_changed.rb +1 -0
- data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +1 -0
- data/lib/mongo/monitoring/event/server_heartbeat_started.rb +1 -0
- data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +1 -0
- data/lib/mongo/monitoring/event/server_opening.rb +1 -0
- data/lib/mongo/monitoring/event/topology_changed.rb +1 -0
- data/lib/mongo/monitoring/event/topology_closed.rb +1 -0
- data/lib/mongo/monitoring/event/topology_opening.rb +1 -0
- data/lib/mongo/operation/create/op_msg.rb +1 -0
- data/lib/mongo/operation/create_index/op_msg.rb +2 -1
- data/lib/mongo/operation/cursor_command/op_msg.rb +37 -0
- data/lib/mongo/operation/cursor_command/result.rb +60 -0
- data/lib/mongo/operation/cursor_command.rb +33 -0
- data/lib/mongo/operation/delete/op_msg.rb +1 -0
- data/lib/mongo/operation/drop/op_msg.rb +1 -0
- data/lib/mongo/operation/drop_database/op_msg.rb +1 -0
- data/lib/mongo/operation/drop_index/op_msg.rb +1 -0
- data/lib/mongo/operation/find/builder/command.rb +1 -1
- data/lib/mongo/operation/insert/bulk_result.rb +2 -4
- data/lib/mongo/operation/insert/op_msg.rb +1 -0
- data/lib/mongo/operation/result.rb +12 -25
- data/lib/mongo/operation/shared/causal_consistency_supported.rb +4 -2
- data/lib/mongo/operation/shared/executable.rb +1 -9
- data/lib/mongo/operation/shared/result/aggregatable.rb +2 -2
- data/lib/mongo/operation/shared/sessions_supported.rb +3 -5
- data/lib/mongo/operation/update/bulk_result.rb +1 -1
- data/lib/mongo/operation/update/op_msg.rb +1 -0
- data/lib/mongo/operation/write_command/op_msg.rb +2 -0
- data/lib/mongo/operation.rb +1 -0
- data/lib/mongo/options/redacted.rb +2 -2
- data/lib/mongo/protocol/compressed.rb +15 -11
- data/lib/mongo/protocol/message.rb +16 -16
- data/lib/mongo/protocol/registry.rb +1 -1
- data/lib/mongo/protocol/serializers.rb +1 -3
- data/lib/mongo/query_cache.rb +3 -5
- data/lib/mongo/server/connection.rb +3 -1
- data/lib/mongo/server/connection_common.rb +9 -1
- data/lib/mongo/server/connection_pool/generation_manager.rb +1 -1
- data/lib/mongo/server/connection_pool.rb +24 -10
- data/lib/mongo/server/description/features.rb +1 -1
- data/lib/mongo/server/description.rb +1 -1
- data/lib/mongo/server/monitor/connection.rb +3 -2
- data/lib/mongo/server/monitor.rb +133 -30
- data/lib/mongo/server/pending_connection.rb +1 -0
- data/lib/mongo/server/push_monitor.rb +16 -2
- data/lib/mongo/server.rb +27 -7
- data/lib/mongo/server_selector/base.rb +3 -6
- data/lib/mongo/server_selector/secondary_preferred.rb +4 -1
- data/lib/mongo/session.rb +22 -23
- data/lib/mongo/socket/ssl.rb +3 -3
- data/lib/mongo/socket.rb +1 -7
- data/lib/mongo/srv/monitor.rb +11 -2
- data/lib/mongo/srv/resolver.rb +1 -1
- data/lib/mongo/srv/result.rb +5 -3
- data/lib/mongo/timeout.rb +10 -15
- data/lib/mongo/tracing/open_telemetry/command_tracer.rb +16 -2
- data/lib/mongo/uri/options_mapper.rb +21 -9
- data/lib/mongo/uri/srv_protocol.rb +4 -4
- data/lib/mongo/uri.rb +56 -8
- data/lib/mongo/utils.rb +1 -1
- data/lib/mongo/version.rb +1 -1
- metadata +6 -4
- data/lib/mongo/auth/stringprep/unicode_normalize/normalize.rb +0 -173
- data/lib/mongo/auth/stringprep/unicode_normalize/tables.rb +0 -1164
|
@@ -268,11 +268,11 @@ module Mongo
|
|
|
268
268
|
buf = BSON::ByteBuffer.new(chunk)
|
|
269
269
|
|
|
270
270
|
message = Registry.get(_op_code).allocate
|
|
271
|
-
message.
|
|
271
|
+
message.fields.each do |field|
|
|
272
272
|
if field[:multi]
|
|
273
|
-
deserialize_array(
|
|
273
|
+
message.deserialize_array(buf, field, options)
|
|
274
274
|
else
|
|
275
|
-
deserialize_field(
|
|
275
|
+
message.deserialize_field(buf, field, options)
|
|
276
276
|
end
|
|
277
277
|
end
|
|
278
278
|
message.fix_after_deserialization if message.is_a?(Msg)
|
|
@@ -369,7 +369,6 @@ module Mongo
|
|
|
369
369
|
# deserialized field specified in the class by the field dsl under
|
|
370
370
|
# the key +:multi+
|
|
371
371
|
#
|
|
372
|
-
# @param message [Message] Message to contain the deserialized array.
|
|
373
372
|
# @param io [IO] Stream containing the array to deserialize.
|
|
374
373
|
# @param field [Hash] Hash representing a field.
|
|
375
374
|
# @param options [ Hash ]
|
|
@@ -377,18 +376,17 @@ module Mongo
|
|
|
377
376
|
# @option options [ Boolean ] :deserialize_as_bson Whether to deserialize
|
|
378
377
|
# each of the elements in this array using BSON types wherever possible.
|
|
379
378
|
#
|
|
380
|
-
# @return [
|
|
379
|
+
# @return [Array] The deserialized array.
|
|
381
380
|
# @api private
|
|
382
|
-
def
|
|
381
|
+
def deserialize_array(io, field, options = {})
|
|
383
382
|
elements = []
|
|
384
|
-
count =
|
|
383
|
+
count = instance_variable_get(field[:multi])
|
|
385
384
|
count.times { elements << field[:type].deserialize(io, options) }
|
|
386
|
-
|
|
385
|
+
instance_variable_set(field[:name], elements)
|
|
387
386
|
end
|
|
388
387
|
|
|
389
|
-
# Deserializes a single field
|
|
388
|
+
# Deserializes a single field into this message.
|
|
390
389
|
#
|
|
391
|
-
# @param message [Message] Message to contain the deserialized field.
|
|
392
390
|
# @param io [IO] Stream containing the field to deserialize.
|
|
393
391
|
# @param field [Hash] Hash representing a field.
|
|
394
392
|
# @param options [ Hash ]
|
|
@@ -396,20 +394,19 @@ module Mongo
|
|
|
396
394
|
# @option options [ Boolean ] :deserialize_as_bson Whether to deserialize
|
|
397
395
|
# this field using BSON types wherever possible.
|
|
398
396
|
#
|
|
399
|
-
# @return [
|
|
397
|
+
# @return [Object] The deserialized field value.
|
|
400
398
|
# @api private
|
|
401
|
-
def
|
|
402
|
-
|
|
399
|
+
def deserialize_field(io, field, options = {})
|
|
400
|
+
instance_variable_set(
|
|
403
401
|
field[:name],
|
|
404
402
|
field[:type].deserialize(io, options)
|
|
405
403
|
)
|
|
406
404
|
end
|
|
407
405
|
|
|
408
|
-
private
|
|
409
|
-
|
|
410
406
|
# A method for getting the fields for a message class
|
|
411
407
|
#
|
|
412
|
-
# @return [
|
|
408
|
+
# @return [Array<Hash>] The fields for the message class
|
|
409
|
+
# @api private
|
|
413
410
|
def fields
|
|
414
411
|
self.class.fields
|
|
415
412
|
end
|
|
@@ -418,6 +415,7 @@ module Mongo
|
|
|
418
415
|
#
|
|
419
416
|
# @param buffer [String] buffer to receive the field
|
|
420
417
|
# @return [String] buffer with serialized field
|
|
418
|
+
# @api private
|
|
421
419
|
def serialize_fields(buffer, max_bson_size = nil)
|
|
422
420
|
fields.each do |field|
|
|
423
421
|
value = instance_variable_get(field[:name])
|
|
@@ -437,6 +435,8 @@ module Mongo
|
|
|
437
435
|
end
|
|
438
436
|
end
|
|
439
437
|
|
|
438
|
+
private
|
|
439
|
+
|
|
440
440
|
# Serializes the header of the message consisting of 4 32bit integers
|
|
441
441
|
#
|
|
442
442
|
# The integers represent a message length placeholder (calculation of
|
|
@@ -181,9 +181,7 @@ module Mongo
|
|
|
181
181
|
def self.serialize(buffer, value, max_bson_size = nil, _validating_keys = nil)
|
|
182
182
|
value.each do |section|
|
|
183
183
|
case section[:type]
|
|
184
|
-
when PayloadZero::TYPE
|
|
185
|
-
PayloadZero.serialize(buffer, section[:payload], max_bson_size)
|
|
186
|
-
when nil
|
|
184
|
+
when PayloadZero::TYPE, nil
|
|
187
185
|
PayloadZero.serialize(buffer, section[:payload], max_bson_size)
|
|
188
186
|
when PayloadOne::TYPE
|
|
189
187
|
PayloadOne.serialize(buffer, section[:payload], max_bson_size)
|
data/lib/mongo/query_cache.rb
CHANGED
|
@@ -199,11 +199,9 @@ module Mongo
|
|
|
199
199
|
# Otherwise, return nil because the stored cursor will not satisfy
|
|
200
200
|
# the query.
|
|
201
201
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
caching_cursor
|
|
206
|
-
end
|
|
202
|
+
return unless caching_cursor_limit.nil? || (limit && caching_cursor_limit >= limit)
|
|
203
|
+
|
|
204
|
+
caching_cursor
|
|
207
205
|
end
|
|
208
206
|
|
|
209
207
|
def normalized_limit(limit)
|
|
@@ -88,6 +88,7 @@ module Mongo
|
|
|
88
88
|
#
|
|
89
89
|
# @since 2.0.0
|
|
90
90
|
def initialize(server, options = {})
|
|
91
|
+
super()
|
|
91
92
|
if server.load_balancer? && options[:generation]
|
|
92
93
|
raise ArgumentError, 'Generation cannot be set when server is a load balancer'
|
|
93
94
|
end
|
|
@@ -422,7 +423,8 @@ module Mongo
|
|
|
422
423
|
generation: e.generation,
|
|
423
424
|
# or description.service_id?
|
|
424
425
|
service_id: e.service_id,
|
|
425
|
-
stop_push_monitor: true
|
|
426
|
+
stop_push_monitor: true,
|
|
427
|
+
network_error: true
|
|
426
428
|
)
|
|
427
429
|
raise
|
|
428
430
|
rescue Error::SocketTimeoutError => e
|
|
@@ -99,6 +99,14 @@ module Mongo
|
|
|
99
99
|
end
|
|
100
100
|
end
|
|
101
101
|
|
|
102
|
+
# Closes the underlying socket, if one is open, to interrupt an
|
|
103
|
+
# in-progress blocking read on the connection.
|
|
104
|
+
#
|
|
105
|
+
# @api private
|
|
106
|
+
def interrupt_socket
|
|
107
|
+
socket&.close
|
|
108
|
+
end
|
|
109
|
+
|
|
102
110
|
private
|
|
103
111
|
|
|
104
112
|
HELLO_DOC = BSON::Document.new({ hello: 1 }).freeze
|
|
@@ -112,7 +120,7 @@ module Mongo
|
|
|
112
120
|
|
|
113
121
|
return unless options[:compressors]
|
|
114
122
|
|
|
115
|
-
if intersection = (server_compressors & options[:compressors])
|
|
123
|
+
if (intersection = (server_compressors & options[:compressors]))
|
|
116
124
|
@compressor = intersection.first
|
|
117
125
|
else
|
|
118
126
|
msg = if server_compressors
|
|
@@ -869,6 +869,22 @@ module Mongo
|
|
|
869
869
|
end
|
|
870
870
|
end
|
|
871
871
|
|
|
872
|
+
# Returns statistics about the internal state of the connection pool,
|
|
873
|
+
# primarily for testing and debugging purposes.
|
|
874
|
+
#
|
|
875
|
+
# @return [ Hash ] The state of the connection pool
|
|
876
|
+
#
|
|
877
|
+
# @api private
|
|
878
|
+
def state
|
|
879
|
+
@lock.synchronize do
|
|
880
|
+
{ available_connections: @available_connections.length,
|
|
881
|
+
checked_out_connections: @checked_out_connections.length,
|
|
882
|
+
pending_connections: @pending_connections.length,
|
|
883
|
+
interrupt_connections: @interrupt_connections.length,
|
|
884
|
+
connection_requests: @connection_requests }
|
|
885
|
+
end
|
|
886
|
+
end
|
|
887
|
+
|
|
872
888
|
private
|
|
873
889
|
|
|
874
890
|
# Returns the next available connection, optionally with given
|
|
@@ -946,7 +962,7 @@ module Mongo
|
|
|
946
962
|
|
|
947
963
|
# Removes and disconnects all stale available connections.
|
|
948
964
|
def remove_stale_connection
|
|
949
|
-
return unless conn = @available_connections.detect { |c| connection_stale_unlocked?(c) }
|
|
965
|
+
return unless (conn = @available_connections.detect { |c| connection_stale_unlocked?(c) })
|
|
950
966
|
|
|
951
967
|
conn.disconnect!(reason: :stale)
|
|
952
968
|
@available_connections.delete(conn)
|
|
@@ -958,7 +974,7 @@ module Mongo
|
|
|
958
974
|
return false if @interrupt_connections.empty?
|
|
959
975
|
|
|
960
976
|
gens = Set.new
|
|
961
|
-
while conn = @interrupt_connections.pop
|
|
977
|
+
while (conn = @interrupt_connections.pop)
|
|
962
978
|
if @checked_out_connections.include?(conn)
|
|
963
979
|
# If the connection has been checked out, mark it as interrupted and it will
|
|
964
980
|
# be disconnected on check in.
|
|
@@ -1042,7 +1058,11 @@ module Mongo
|
|
|
1042
1058
|
@server.unknown!(
|
|
1043
1059
|
generation: e.generation,
|
|
1044
1060
|
service_id: e.service_id,
|
|
1045
|
-
stop_push_monitor: true
|
|
1061
|
+
stop_push_monitor: true,
|
|
1062
|
+
# Only a network error makes the monitor connection suspect and
|
|
1063
|
+
# requires cancelling its check; an auth failure does not.
|
|
1064
|
+
network_error: e.is_a?(Mongo::Error::SocketError) ||
|
|
1065
|
+
e.is_a?(Mongo::Error::SocketTimeoutError)
|
|
1046
1066
|
)
|
|
1047
1067
|
end
|
|
1048
1068
|
connection.disconnect!(reason: :error)
|
|
@@ -1146,12 +1166,6 @@ module Mongo
|
|
|
1146
1166
|
raise Error::ConnectionCheckOutTimeout.new(msg, address: @server.address)
|
|
1147
1167
|
end
|
|
1148
1168
|
|
|
1149
|
-
def raise_check_out_timeout_locked!(connection_global_id)
|
|
1150
|
-
@lock.synchronize do
|
|
1151
|
-
raise_check_out_timeout!(connection_global_id)
|
|
1152
|
-
end
|
|
1153
|
-
end
|
|
1154
|
-
|
|
1155
1169
|
def raise_if_pool_closed!
|
|
1156
1170
|
return unless closed?
|
|
1157
1171
|
|
|
@@ -1246,7 +1260,7 @@ module Mongo
|
|
|
1246
1260
|
# @raise [ Timeout::Error ] If the connection pool is at maximum size
|
|
1247
1261
|
# and remains so for longer than the wait timeout.
|
|
1248
1262
|
def get_connection(pid, connection_global_id)
|
|
1249
|
-
if connection = next_available_connection(connection_global_id)
|
|
1263
|
+
if (connection = next_available_connection(connection_global_id))
|
|
1250
1264
|
return nil unless valid_available_connection?(connection, pid, connection_global_id)
|
|
1251
1265
|
|
|
1252
1266
|
# We've got a connection, so we decrement the number of connection
|
|
@@ -74,7 +74,7 @@ module Mongo
|
|
|
74
74
|
#
|
|
75
75
|
# If there are no currently-deprecated wire versions, this should be
|
|
76
76
|
# set to an empty range (e.g. the EMPTY_RANGE constant).
|
|
77
|
-
DEPRECATED_WIRE_VERSIONS =
|
|
77
|
+
DEPRECATED_WIRE_VERSIONS = 8..8
|
|
78
78
|
|
|
79
79
|
# make sure the deprecated versions are valid
|
|
80
80
|
if DEPRECATED_WIRE_VERSIONS.min && (DRIVER_WIRE_VERSIONS.min > DEPRECATED_WIRE_VERSIONS.max)
|
|
@@ -279,7 +279,7 @@ module Mongo
|
|
|
279
279
|
"The server at #{address.seed} did not provide a service id in handshake response"
|
|
280
280
|
end
|
|
281
281
|
|
|
282
|
-
fake_service_id = if process_id = topology_version && topology_version['processId']
|
|
282
|
+
fake_service_id = if (process_id = topology_version && topology_version['processId'])
|
|
283
283
|
"process:#{process_id}"
|
|
284
284
|
else
|
|
285
285
|
"fake:#{rand((2**32) - 1) + 1}"
|
|
@@ -54,9 +54,10 @@ module Mongo
|
|
|
54
54
|
#
|
|
55
55
|
# @since 2.0.0
|
|
56
56
|
def initialize(address, options = {})
|
|
57
|
+
super()
|
|
57
58
|
@address = address
|
|
58
59
|
@options = options.dup.freeze
|
|
59
|
-
raise ArgumentError, 'App metadata is required' unless @app_metadata = options[:app_metadata]
|
|
60
|
+
raise ArgumentError, 'App metadata is required' unless (@app_metadata = options[:app_metadata])
|
|
60
61
|
|
|
61
62
|
@socket = nil
|
|
62
63
|
@pid = Process.pid
|
|
@@ -232,7 +233,7 @@ module Mongo
|
|
|
232
233
|
end
|
|
233
234
|
# compressors must be set to maintain correct compression status
|
|
234
235
|
# in the server description. See RUBY-2427
|
|
235
|
-
if compressors = options[:compressors]
|
|
236
|
+
if (compressors = options[:compressors])
|
|
236
237
|
doc = doc.merge(compression: compressors)
|
|
237
238
|
end
|
|
238
239
|
doc
|
data/lib/mongo/server/monitor.rb
CHANGED
|
@@ -86,14 +86,23 @@ module Mongo
|
|
|
86
86
|
@sdam_mutex = Mutex.new
|
|
87
87
|
@next_earliest_scan = @next_wanted_scan = Time.now
|
|
88
88
|
@update_mutex = Mutex.new
|
|
89
|
+
# Guards reads and writes of @connection so the polling connection can
|
|
90
|
+
# be cancelled from another thread. Per the Server Monitoring spec's
|
|
91
|
+
# cancelCheck pseudocode, the lock is only held long enough to copy or
|
|
92
|
+
# assign the reference - never across the blocking check.
|
|
93
|
+
@connection_lock = Mutex.new
|
|
89
94
|
end
|
|
90
95
|
|
|
91
96
|
# @return [ Server ] server The server that this monitor is monitoring.
|
|
92
97
|
# @api private
|
|
93
98
|
attr_reader :server
|
|
94
99
|
|
|
95
|
-
# @return [ Mongo::Server::Monitor::Connection ]
|
|
96
|
-
|
|
100
|
+
# @return [ Mongo::Server::Monitor::Connection | nil ] The connection to
|
|
101
|
+
# use, read under @connection_lock so callers never observe a stale
|
|
102
|
+
# reference after a concurrent cancel_check! clears it.
|
|
103
|
+
def connection
|
|
104
|
+
@connection_lock.synchronize { @connection }
|
|
105
|
+
end
|
|
97
106
|
|
|
98
107
|
# @return [ Hash ] options The server options.
|
|
99
108
|
attr_reader :options
|
|
@@ -159,7 +168,11 @@ module Mongo
|
|
|
159
168
|
end
|
|
160
169
|
end
|
|
161
170
|
|
|
162
|
-
|
|
171
|
+
# @param [ Mongo::Server::Monitor::Connection ] connection The freshly
|
|
172
|
+
# established monitoring connection. Passed explicitly (rather than read
|
|
173
|
+
# from @connection) so a concurrent cancel_check! cannot nil it out from
|
|
174
|
+
# under us between establishing it and building the PushMonitor.
|
|
175
|
+
def create_push_monitor!(topology_version, connection)
|
|
163
176
|
@update_mutex.synchronize do
|
|
164
177
|
@push_monitor = nil if @push_monitor && !@push_monitor.running?
|
|
165
178
|
|
|
@@ -170,7 +183,7 @@ module Mongo
|
|
|
170
183
|
**Utils.shallow_symbolize_keys(options.merge(
|
|
171
184
|
socket_timeout: heartbeat_interval + connection.socket_timeout,
|
|
172
185
|
app_metadata: options[:push_monitor_app_metadata],
|
|
173
|
-
check_document:
|
|
186
|
+
check_document: connection.check_document
|
|
174
187
|
))
|
|
175
188
|
)
|
|
176
189
|
end
|
|
@@ -185,6 +198,31 @@ module Mongo
|
|
|
185
198
|
end
|
|
186
199
|
end
|
|
187
200
|
|
|
201
|
+
# Cancel the in-progress check and close the monitoring connection.
|
|
202
|
+
#
|
|
203
|
+
# Called when the server is marked Unknown from a network error, per the
|
|
204
|
+
# Server Monitoring spec ("hello or legacy hello Cancellation"). Stops the
|
|
205
|
+
# streaming PushMonitor (interrupting its awaited hello read) and closes
|
|
206
|
+
# the polling connection, so the next check must establish a fresh one
|
|
207
|
+
# rather than re-validating the server over a possibly-dead socket.
|
|
208
|
+
#
|
|
209
|
+
# @api private
|
|
210
|
+
def cancel_check!
|
|
211
|
+
stop_push_monitor!
|
|
212
|
+
|
|
213
|
+
# Copy the connection reference under the lock, then interrupt and close
|
|
214
|
+
# it outside the lock. Closing the socket interrupts any in-progress
|
|
215
|
+
# read on the monitor thread; nil-ing the reference forces the next
|
|
216
|
+
# check to reconnect. The monitor thread is the only writer of a new
|
|
217
|
+
# connection, so it is safe for this thread to clear it.
|
|
218
|
+
connection = @connection_lock.synchronize do
|
|
219
|
+
conn = @connection
|
|
220
|
+
@connection = nil
|
|
221
|
+
conn
|
|
222
|
+
end
|
|
223
|
+
connection&.disconnect!
|
|
224
|
+
end
|
|
225
|
+
|
|
188
226
|
# Perform a check of the server with throttling, and update
|
|
189
227
|
# the server's description and average round trip time.
|
|
190
228
|
#
|
|
@@ -210,28 +248,41 @@ module Mongo
|
|
|
210
248
|
@mutex.synchronize do
|
|
211
249
|
throttle_scan_frequency!
|
|
212
250
|
|
|
251
|
+
# When the streaming protocol is active the PushMonitor is the
|
|
252
|
+
# authoritative SDAM source and this scan only measures RTT on the
|
|
253
|
+
# dedicated connection. Per the Server Monitoring spec, an RTT
|
|
254
|
+
# command MUST NOT publish events or update the topology. Compute
|
|
255
|
+
# this before do_scan, which may (re)connect and change the state.
|
|
256
|
+
rtt_only = rtt_measurement_only?
|
|
257
|
+
|
|
213
258
|
begin
|
|
214
|
-
result = do_scan
|
|
259
|
+
result = do_scan(publish_heartbeat: !rtt_only)
|
|
215
260
|
rescue StandardError => e
|
|
216
|
-
run_sdam_flow({}, scan_error: e)
|
|
261
|
+
run_sdam_flow({}, scan_error: e, rtt_only: rtt_only)
|
|
217
262
|
else
|
|
218
|
-
run_sdam_flow(result)
|
|
263
|
+
run_sdam_flow(result, rtt_only: rtt_only)
|
|
219
264
|
end
|
|
220
265
|
end
|
|
221
266
|
end
|
|
222
267
|
|
|
223
|
-
def run_sdam_flow(result, awaited: false, scan_error: nil)
|
|
268
|
+
def run_sdam_flow(result, awaited: false, scan_error: nil, rtt_only: false)
|
|
224
269
|
@sdam_mutex.synchronize do
|
|
225
270
|
old_description = server.description
|
|
226
271
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
272
|
+
# An RTT-only measurement (streaming protocol active) must not update
|
|
273
|
+
# the topology or publish SDAM events. The RTT it gathered is
|
|
274
|
+
# incorporated into the next streaming-hello description via the
|
|
275
|
+
# shared RTT calculator. The scheduling below still runs so the
|
|
276
|
+
# monitor keeps pacing its checks.
|
|
277
|
+
unless rtt_only
|
|
278
|
+
new_description = Description.new(
|
|
279
|
+
server.address,
|
|
280
|
+
result,
|
|
281
|
+
average_round_trip_time: server.round_trip_time_calculator.average_round_trip_time,
|
|
282
|
+
minimum_round_trip_time: server.round_trip_time_calculator.minimum_round_trip_time
|
|
283
|
+
)
|
|
284
|
+
server.cluster.run_sdam_flow(server.description, new_description, awaited: awaited, scan_error: scan_error)
|
|
285
|
+
end
|
|
235
286
|
|
|
236
287
|
server.description.tap do |new_description|
|
|
237
288
|
unless awaited
|
|
@@ -272,8 +323,12 @@ module Mongo
|
|
|
272
323
|
server.scan_semaphore.signal
|
|
273
324
|
end
|
|
274
325
|
|
|
275
|
-
def do_scan
|
|
276
|
-
|
|
326
|
+
def do_scan(publish_heartbeat: true)
|
|
327
|
+
if publish_heartbeat
|
|
328
|
+
monitoring.publish_heartbeat(server) do
|
|
329
|
+
check
|
|
330
|
+
end
|
|
331
|
+
else
|
|
277
332
|
check
|
|
278
333
|
end
|
|
279
334
|
rescue StandardError => e
|
|
@@ -285,24 +340,53 @@ module Mongo
|
|
|
285
340
|
raise e
|
|
286
341
|
end
|
|
287
342
|
|
|
343
|
+
# Returns whether this scan is only an RTT measurement, which is the case
|
|
344
|
+
# when the streaming protocol is active: a dedicated connection is already
|
|
345
|
+
# established and the PushMonitor is running as the authoritative SDAM
|
|
346
|
+
# source. In the polling protocol there is no running PushMonitor, so the
|
|
347
|
+
# connection-reuse check is a real server check and not RTT-only.
|
|
348
|
+
#
|
|
349
|
+
# @return [ true | false ]
|
|
350
|
+
def rtt_measurement_only?
|
|
351
|
+
return false if connection.nil?
|
|
352
|
+
|
|
353
|
+
# Only suppress the check while the server is in a known state and the
|
|
354
|
+
# PushMonitor is the authoritative streaming source. If the server is
|
|
355
|
+
# Unknown (e.g. an operation error or a streaming failure just marked
|
|
356
|
+
# it so), the polling Monitor must run a full check to recover it
|
|
357
|
+
# rather than waiting for the next streaming response - otherwise the
|
|
358
|
+
# server can stay Unknown long enough to fail server selection.
|
|
359
|
+
return false if server.unknown?
|
|
360
|
+
|
|
361
|
+
pm = push_monitor
|
|
362
|
+
!pm.nil? && pm.running?
|
|
363
|
+
end
|
|
364
|
+
|
|
288
365
|
def check
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
366
|
+
# Snapshot the connection under the lock. A concurrent cancel_check!
|
|
367
|
+
# may nil @connection from another thread; working on a local copy keeps
|
|
368
|
+
# this check consistent, and the guarded writeback below never clobbers
|
|
369
|
+
# a connection the monitor thread did not itself establish.
|
|
370
|
+
connection = self.connection
|
|
371
|
+
|
|
372
|
+
if connection && connection.pid != Process.pid
|
|
373
|
+
log_warn("Detected PID change - Mongo client should have been reconnected (old pid #{connection.pid}, new pid #{Process.pid}")
|
|
374
|
+
connection.disconnect!
|
|
375
|
+
clear_connection(connection)
|
|
376
|
+
connection = nil
|
|
293
377
|
end
|
|
294
378
|
|
|
295
|
-
if
|
|
379
|
+
if connection
|
|
296
380
|
result = server.round_trip_time_calculator.measure do
|
|
297
|
-
doc =
|
|
381
|
+
doc = connection.check_document
|
|
298
382
|
cmd = Protocol::Query.new(
|
|
299
383
|
Database::ADMIN, Database::COMMAND, doc, limit: -1
|
|
300
384
|
)
|
|
301
|
-
message =
|
|
385
|
+
message = connection.dispatch_bytes(cmd.serialize.to_s)
|
|
302
386
|
message.documents.first
|
|
303
387
|
rescue Mongo::Error
|
|
304
|
-
|
|
305
|
-
|
|
388
|
+
connection.disconnect!
|
|
389
|
+
clear_connection(connection)
|
|
306
390
|
raise
|
|
307
391
|
end
|
|
308
392
|
else
|
|
@@ -311,10 +395,13 @@ module Mongo
|
|
|
311
395
|
result = server.round_trip_time_calculator.measure do
|
|
312
396
|
connection.handshake!
|
|
313
397
|
end
|
|
314
|
-
|
|
315
|
-
if tv_doc = result['topologyVersion']
|
|
398
|
+
store_connection(connection)
|
|
399
|
+
if (tv_doc = result['topologyVersion'])
|
|
316
400
|
if streaming_enabled?
|
|
317
|
-
|
|
401
|
+
# Run the instance we just created rather than re-reading the
|
|
402
|
+
# push_monitor getter: a concurrent cancel_check! may have nil'd
|
|
403
|
+
# @push_monitor between the two calls.
|
|
404
|
+
push_monitor = create_push_monitor!(TopologyVersion.new(tv_doc), connection)
|
|
318
405
|
push_monitor.run!
|
|
319
406
|
else
|
|
320
407
|
stop_push_monitor!
|
|
@@ -328,6 +415,22 @@ module Mongo
|
|
|
328
415
|
result
|
|
329
416
|
end
|
|
330
417
|
|
|
418
|
+
# Store a freshly established monitoring connection.
|
|
419
|
+
def store_connection(connection)
|
|
420
|
+
@connection_lock.synchronize do
|
|
421
|
+
@connection = connection
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# Clear the monitoring connection, but only if it is still the one passed
|
|
426
|
+
# in. A concurrent cancel_check! may have already cleared or replaced it,
|
|
427
|
+
# in which case we must leave the current connection alone.
|
|
428
|
+
def clear_connection(connection)
|
|
429
|
+
@connection_lock.synchronize do
|
|
430
|
+
@connection = nil if @connection.equal?(connection)
|
|
431
|
+
end
|
|
432
|
+
end
|
|
433
|
+
|
|
331
434
|
# @note If the system clock is set to a time in the past, this method
|
|
332
435
|
# can sleep for a very long time.
|
|
333
436
|
def throttle_scan_frequency!
|
|
@@ -72,7 +72,7 @@ module Mongo
|
|
|
72
72
|
# Interrupt any in-progress exhausted hello reads by
|
|
73
73
|
# disconnecting the connection.
|
|
74
74
|
begin
|
|
75
|
-
@connection.
|
|
75
|
+
@connection.interrupt_socket
|
|
76
76
|
rescue StandardError
|
|
77
77
|
nil
|
|
78
78
|
end
|
|
@@ -118,6 +118,13 @@ module Mongo
|
|
|
118
118
|
log_prefix: options[:log_prefix],
|
|
119
119
|
bg_error_backtrace: options[:bg_error_backtrace])
|
|
120
120
|
|
|
121
|
+
# The streaming check is the authoritative SDAM source, so its failure
|
|
122
|
+
# must mark the server Unknown and clear the pool here. While streaming
|
|
123
|
+
# is active the polling Monitor only measures RTT and ignores errors
|
|
124
|
+
# (per the Server Monitoring spec), so it can no longer be relied on to
|
|
125
|
+
# notice the failure.
|
|
126
|
+
monitor.run_sdam_flow({}, scan_error: e, awaited: true)
|
|
127
|
+
|
|
121
128
|
# If a request failed on a connection, stop push monitoring.
|
|
122
129
|
# In case the server is dead we don't want to have two connections
|
|
123
130
|
# trying to connect unsuccessfully at the same time.
|
|
@@ -142,6 +149,13 @@ module Mongo
|
|
|
142
149
|
@server_pushing = false
|
|
143
150
|
connection = PushMonitor::Connection.new(server.address, options)
|
|
144
151
|
connection.connect!
|
|
152
|
+
# Identify the streaming connection to the server with a metadata
|
|
153
|
+
# handshake (carrying the appName) before streaming, mirroring the
|
|
154
|
+
# polling Monitor and the Server Monitoring spec, which establishes
|
|
155
|
+
# the monitoring connection with a handshake before issuing the
|
|
156
|
+
# awaitable hello. Without this, appName-scoped server behaviour
|
|
157
|
+
# never applies to the streaming hello.
|
|
158
|
+
connection.handshake!
|
|
145
159
|
@connection = connection
|
|
146
160
|
end
|
|
147
161
|
end
|
|
@@ -174,7 +188,7 @@ module Mongo
|
|
|
174
188
|
end
|
|
175
189
|
|
|
176
190
|
def read_response
|
|
177
|
-
if timeout = options[:connect_timeout]
|
|
191
|
+
if (timeout = options[:connect_timeout])
|
|
178
192
|
if timeout < 0
|
|
179
193
|
raise Mongo::SocketTimeoutError, 'Requested to read with a negative timeout: '
|
|
180
194
|
elsif timeout > 0
|
data/lib/mongo/server.rb
CHANGED
|
@@ -489,7 +489,8 @@ module Mongo
|
|
|
489
489
|
unknown!(
|
|
490
490
|
generation: e.generation,
|
|
491
491
|
service_id: e.service_id,
|
|
492
|
-
stop_push_monitor: true
|
|
492
|
+
stop_push_monitor: true,
|
|
493
|
+
network_error: true
|
|
493
494
|
)
|
|
494
495
|
end
|
|
495
496
|
raise
|
|
@@ -514,11 +515,13 @@ module Mongo
|
|
|
514
515
|
raise
|
|
515
516
|
rescue Mongo::Error::SocketError, Auth::Unauthorized => e
|
|
516
517
|
# non-timeout network error or auth error, clear the pool and mark the
|
|
517
|
-
# topology as unknown
|
|
518
|
+
# topology as unknown. Only a network error makes the monitor connection
|
|
519
|
+
# suspect; a pure authentication failure must not close it.
|
|
518
520
|
unknown!(
|
|
519
521
|
generation: e.generation,
|
|
520
522
|
service_id: e.service_id,
|
|
521
|
-
stop_push_monitor: true
|
|
523
|
+
stop_push_monitor: true,
|
|
524
|
+
network_error: e.is_a?(Mongo::Error::SocketError)
|
|
522
525
|
)
|
|
523
526
|
raise
|
|
524
527
|
end
|
|
@@ -568,8 +571,15 @@ module Mongo
|
|
|
568
571
|
# on 4.2+ servers).
|
|
569
572
|
# @option options [ TopologyVersion ] :topology_version Topology version
|
|
570
573
|
# of the error response that is causing the server to be marked unknown.
|
|
571
|
-
# @option options [ true | false ] :stop_push_monitor
|
|
572
|
-
#
|
|
574
|
+
# @option options [ true | false ] :stop_push_monitor Set when the server
|
|
575
|
+
# is marked Unknown from a connection-establishment error (e.g. an
|
|
576
|
+
# authentication failure). Stops the streaming PushMonitor associated with
|
|
577
|
+
# the server, if any.
|
|
578
|
+
# @option options [ true | false ] :network_error Set when the server is
|
|
579
|
+
# marked Unknown from a network error. In addition to stopping the
|
|
580
|
+
# PushMonitor, cancels the monitor's in-progress check and closes its
|
|
581
|
+
# connection so the next check must establish a fresh one, per the Server
|
|
582
|
+
# Monitoring spec.
|
|
573
583
|
# @option options [ Object ] :service_id Discard state for the specified
|
|
574
584
|
# service id only.
|
|
575
585
|
#
|
|
@@ -590,7 +600,7 @@ module Mongo
|
|
|
590
600
|
# for example if there haven't been any successful connections yet to
|
|
591
601
|
# this server, but the server can still be marked unknown if one
|
|
592
602
|
# of such connections failed midway through its establishment.
|
|
593
|
-
if service_id = options[:service_id]
|
|
603
|
+
if (service_id = options[:service_id])
|
|
594
604
|
pool&.disconnect!(service_id: service_id)
|
|
595
605
|
end
|
|
596
606
|
return
|
|
@@ -605,7 +615,17 @@ module Mongo
|
|
|
605
615
|
return
|
|
606
616
|
end
|
|
607
617
|
|
|
608
|
-
|
|
618
|
+
if options[:network_error]
|
|
619
|
+
# A network error implies every connection to the server is suspect
|
|
620
|
+
# (Server Monitoring spec, "hello or legacy hello Cancellation"): cancel
|
|
621
|
+
# the monitor's in-progress check and close its connection so the next
|
|
622
|
+
# check must establish a fresh one.
|
|
623
|
+
monitor&.cancel_check!
|
|
624
|
+
elsif options[:stop_push_monitor]
|
|
625
|
+
# Non-network errors (e.g. an authentication failure) only tear down the
|
|
626
|
+
# streaming PushMonitor; the monitor's connection is not suspect.
|
|
627
|
+
monitor&.stop_push_monitor!
|
|
628
|
+
end
|
|
609
629
|
|
|
610
630
|
# SDAM flow will update description on the server without in-place
|
|
611
631
|
# mutations and invoke SDAM transitions as needed.
|