mongo 2.12.0.rc0 → 2.12.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/README.md +2 -1
- data/lib/mongo/client.rb +23 -9
- data/lib/mongo/client_encryption.rb +1 -1
- data/lib/mongo/cluster.rb +6 -2
- data/lib/mongo/crypt/auto_decryption_context.rb +3 -5
- data/lib/mongo/crypt/auto_encrypter.rb +17 -7
- data/lib/mongo/crypt/binding.rb +446 -379
- data/lib/mongo/crypt/context.rb +4 -4
- data/lib/mongo/crypt/encryption_io.rb +16 -10
- data/lib/mongo/crypt/explicit_encrypter.rb +3 -3
- data/lib/mongo/crypt/explicit_encryption_context.rb +1 -1
- data/lib/mongo/crypt/handle.rb +26 -4
- data/lib/mongo/crypt/hooks.rb +1 -1
- data/lib/mongo/database.rb +11 -1
- data/lib/mongo/error/bulk_write_error.rb +16 -14
- data/lib/mongo/error/notable.rb +0 -15
- data/lib/mongo/error/parser.rb +1 -1
- data/lib/mongo/grid/file/info.rb +1 -1
- data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +1 -1
- data/lib/mongo/operation/insert/command.rb +3 -2
- data/lib/mongo/operation/insert/legacy.rb +2 -1
- data/lib/mongo/operation/insert/op_msg.rb +1 -1
- data/lib/mongo/operation/shared/executable.rb +9 -9
- data/lib/mongo/operation/shared/op_msg_or_command.rb +2 -2
- data/lib/mongo/operation/shared/read_preference_supported.rb +68 -19
- data/lib/mongo/operation/shared/response_handling.rb +1 -1
- data/lib/mongo/operation/shared/sessions_supported.rb +44 -3
- data/lib/mongo/protocol/bit_vector.rb +2 -1
- data/lib/mongo/protocol/message.rb +22 -7
- data/lib/mongo/protocol/msg.rb +2 -5
- data/lib/mongo/protocol/serializers.rb +32 -11
- data/lib/mongo/retryable.rb +1 -1
- data/lib/mongo/server/connection.rb +1 -1
- data/lib/mongo/server/connection_base.rb +9 -4
- data/lib/mongo/server/connection_pool/populator.rb +1 -1
- data/lib/mongo/session.rb +1 -1
- data/lib/mongo/srv/monitor.rb +73 -42
- data/lib/mongo/srv/result.rb +0 -1
- data/lib/mongo/uri.rb +1 -1
- data/lib/mongo/uri/srv_protocol.rb +1 -1
- data/lib/mongo/version.rb +1 -1
- data/mongo.gemspec +0 -2
- data/spec/README.md +106 -12
- data/spec/integration/client_construction_spec.rb +29 -5
- data/spec/integration/client_side_encryption/auto_encryption_bulk_writes_spec.rb +6 -4
- data/spec/integration/client_side_encryption/auto_encryption_command_monitoring_spec.rb +19 -17
- data/spec/integration/client_side_encryption/auto_encryption_mongocryptd_spawn_spec.rb +5 -4
- data/spec/integration/client_side_encryption/auto_encryption_old_wire_version_spec.rb +11 -8
- data/spec/integration/client_side_encryption/auto_encryption_reconnect_spec.rb +14 -9
- data/spec/integration/client_side_encryption/auto_encryption_spec.rb +46 -45
- data/spec/integration/client_side_encryption/bson_size_limit_spec.rb +11 -7
- data/spec/integration/client_side_encryption/bypass_mongocryptd_spawn_spec.rb +13 -9
- data/spec/integration/client_side_encryption/client_close_spec.rb +10 -6
- data/spec/integration/client_side_encryption/corpus_spec.rb +19 -14
- data/spec/integration/client_side_encryption/data_key_spec.rb +10 -8
- data/spec/integration/client_side_encryption/external_key_vault_spec.rb +12 -8
- data/spec/integration/client_side_encryption/views_spec.rb +6 -4
- data/spec/integration/client_update_spec.rb +36 -2
- data/spec/integration/crud_spec.rb +89 -0
- data/spec/integration/read_preference_spec.rb +26 -0
- data/spec/integration/srv_monitoring_spec.rb +2 -2
- data/spec/kerberos/kerberos_spec.rb +87 -0
- data/spec/lite_spec_helper.rb +4 -8
- data/spec/mongo/bulk_write/result_spec.rb +11 -7
- data/spec/mongo/client_encryption_spec.rb +3 -6
- data/spec/mongo/crypt/auto_encrypter_spec.rb +8 -3
- data/spec/mongo/crypt/handle_spec.rb +38 -4
- data/spec/mongo/error/bulk_write_error_spec.rb +49 -0
- data/spec/mongo/error/notable_spec.rb +59 -0
- data/spec/mongo/operation/find/legacy_spec.rb +1 -0
- data/spec/mongo/operation/read_preference_legacy_spec.rb +351 -0
- data/spec/mongo/operation/read_preference_op_msg_spec.rb +194 -0
- data/spec/mongo/srv/monitor_spec.rb +88 -69
- data/spec/runners/transactions.rb +5 -7
- data/spec/spec_tests/client_side_encryption_spec.rb +0 -5
- data/spec/spec_tests/data/client_side_encryption/bulk.yml +3 -0
- data/spec/spec_tests/data/client_side_encryption/replaceOne.yml +4 -1
- data/spec/spec_tests/data/client_side_encryption/updateOne.yml +3 -0
- data/spec/support/cluster_tools.rb +6 -1
- data/spec/support/crypt.rb +14 -0
- data/spec/support/lite_constraints.rb +3 -1
- data/spec/support/spec_config.rb +10 -0
- data/spec/support/utils.rb +9 -1
- metadata +15 -14
- metadata.gz.sig +0 -0
- data/lib/mongo/cluster/srv_monitor.rb +0 -127
- data/lib/mongo/srv/warning_result.rb +0 -35
- data/spec/enterprise_auth/kerberos_spec.rb +0 -58
- data/spec/mongo/cluster/srv_monitor_spec.rb +0 -214
- data/spec/mongo/operation/read_preference_spec.rb +0 -245
@@ -68,7 +68,7 @@ module Mongo
|
|
68
68
|
# is included in BulkWrite which does not store the session in the
|
69
69
|
# receiver (despite Specifiable doing so).
|
70
70
|
#
|
71
|
-
# @param [ Session | nil ] Session to consider.
|
71
|
+
# @param [ Session | nil ] session Session to consider.
|
72
72
|
def unpin_maybe(session)
|
73
73
|
yield
|
74
74
|
rescue Mongo::Error => e
|
@@ -123,9 +123,8 @@ module Mongo
|
|
123
123
|
sel = selector(server).dup
|
124
124
|
add_write_concern!(sel)
|
125
125
|
sel[Protocol::Msg::DATABASE_IDENTIFIER] = db_name
|
126
|
-
|
127
|
-
|
128
|
-
end
|
126
|
+
|
127
|
+
add_read_preference(sel, server)
|
129
128
|
|
130
129
|
if server.features.sessions_enabled?
|
131
130
|
apply_cluster_time!(sel, server)
|
@@ -139,6 +138,48 @@ module Mongo
|
|
139
138
|
sel
|
140
139
|
end
|
141
140
|
|
141
|
+
# Adds $readPreference field to the command document.
|
142
|
+
#
|
143
|
+
# $readPreference is only sent when the server is a mongos,
|
144
|
+
# following the rules described in
|
145
|
+
# https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection.rst#passing-read-preference-to-mongos.
|
146
|
+
# The topology does not matter for figuring out whether to send
|
147
|
+
# $readPreference since the decision is always made based on
|
148
|
+
# server type.
|
149
|
+
#
|
150
|
+
# $readPreference is sent to OP_MSG-grokking replica set members.
|
151
|
+
#
|
152
|
+
# @param [ Hash ] sel Existing command document which will be mutated.
|
153
|
+
# @param [ Server ] server The server that the command is to be sent to.
|
154
|
+
def add_read_preference(sel, server)
|
155
|
+
# https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection.rst#topology-type-single
|
156
|
+
if server.standalone?
|
157
|
+
# Read preference is never sent to standalones.
|
158
|
+
elsif server.cluster.single?
|
159
|
+
# In Single topology:
|
160
|
+
# - If no read preference is specified by the application, the driver
|
161
|
+
# adds mode: primaryPreferred.
|
162
|
+
# - If a read preference is specified by the application, the driver
|
163
|
+
# replaces the mode with primaryPreferred.
|
164
|
+
read_doc = if read
|
165
|
+
BSON::Document.new(read.to_doc)
|
166
|
+
else
|
167
|
+
BSON::Document.new
|
168
|
+
end
|
169
|
+
if [nil, 'primary'].include?(read_doc['mode'])
|
170
|
+
read_doc['mode'] = 'primaryPreferred'
|
171
|
+
end
|
172
|
+
sel['$readPreference'] = read_doc
|
173
|
+
else
|
174
|
+
# In replica sets and sharded clusters, read preference is passed
|
175
|
+
# to the server if one is specified by the application, and there
|
176
|
+
# is no default.
|
177
|
+
if read
|
178
|
+
sel['$readPreference'] = read.to_doc
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
142
183
|
def apply_session_options(sel, server)
|
143
184
|
apply_cluster_time!(sel, server)
|
144
185
|
sel[:txnNumber] = BSON::Int64.new(txn_num) if txn_num
|
@@ -47,9 +47,10 @@ module Mongo
|
|
47
47
|
# Deserializes vector by decoding the symbol according to its mask
|
48
48
|
#
|
49
49
|
# @param [ String ] buffer Buffer containing the vector to be deserialized.
|
50
|
+
# @param [ Hash ] options This method does not currently accept any options.
|
50
51
|
#
|
51
52
|
# @return [ Array<Symbol> ] Flags contained in the vector
|
52
|
-
def deserialize(buffer)
|
53
|
+
def deserialize(buffer, options = {})
|
53
54
|
vector = buffer.get_int32
|
54
55
|
flags = []
|
55
56
|
@masks.each do |flag, mask|
|
@@ -194,9 +194,14 @@ module Mongo
|
|
194
194
|
#
|
195
195
|
# @param [ Integer ] max_message_size The max message size.
|
196
196
|
# @param [ IO ] io Stream containing a message
|
197
|
+
# @param [ Hash ] options
|
198
|
+
#
|
199
|
+
# @option options [ Boolean ] :deserialize_as_bson Whether to deserialize
|
200
|
+
# this message using BSON types instead of native Ruby types wherever
|
201
|
+
# possible.
|
197
202
|
#
|
198
203
|
# @return [ Message ] Instance of a Message class
|
199
|
-
def self.deserialize(io, max_message_size = MAX_MESSAGE_SIZE, expected_response_to = nil)
|
204
|
+
def self.deserialize(io, max_message_size = MAX_MESSAGE_SIZE, expected_response_to = nil, options = {})
|
200
205
|
length, _request_id, response_to, _op_code = deserialize_header(BSON::ByteBuffer.new(io.read(16)))
|
201
206
|
|
202
207
|
# Protection from potential DOS man-in-the-middle attacks. See
|
@@ -216,9 +221,9 @@ module Mongo
|
|
216
221
|
|
217
222
|
message.send(:fields).each do |field|
|
218
223
|
if field[:multi]
|
219
|
-
deserialize_array(message, buffer, field)
|
224
|
+
deserialize_array(message, buffer, field, options)
|
220
225
|
else
|
221
|
-
deserialize_field(message, buffer, field)
|
226
|
+
deserialize_field(message, buffer, field, options)
|
222
227
|
end
|
223
228
|
end
|
224
229
|
if message.is_a?(Msg)
|
@@ -363,11 +368,16 @@ module Mongo
|
|
363
368
|
# @param message [Message] Message to contain the deserialized array.
|
364
369
|
# @param io [IO] Stream containing the array to deserialize.
|
365
370
|
# @param field [Hash] Hash representing a field.
|
371
|
+
# @param options [ Hash ]
|
372
|
+
#
|
373
|
+
# @option options [ Boolean ] :deserialize_as_bson Whether to deserialize
|
374
|
+
# each of the elements in this array using BSON types wherever possible.
|
375
|
+
#
|
366
376
|
# @return [Message] Message with deserialized array.
|
367
|
-
def self.deserialize_array(message, io, field)
|
377
|
+
def self.deserialize_array(message, io, field, options)
|
368
378
|
elements = []
|
369
379
|
count = message.instance_variable_get(field[:multi])
|
370
|
-
count.times { elements << field[:type].deserialize(io) }
|
380
|
+
count.times { elements << field[:type].deserialize(io, options) }
|
371
381
|
message.instance_variable_set(field[:name], elements)
|
372
382
|
end
|
373
383
|
|
@@ -376,11 +386,16 @@ module Mongo
|
|
376
386
|
# @param message [Message] Message to contain the deserialized field.
|
377
387
|
# @param io [IO] Stream containing the field to deserialize.
|
378
388
|
# @param field [Hash] Hash representing a field.
|
389
|
+
# @param options [ Hash ]
|
390
|
+
#
|
391
|
+
# @option options [ Boolean ] :deserialize_as_bson Whether to deserialize
|
392
|
+
# this field using BSON types wherever possible.
|
393
|
+
#
|
379
394
|
# @return [Message] Message with deserialized field.
|
380
|
-
def self.deserialize_field(message, io, field)
|
395
|
+
def self.deserialize_field(message, io, field, options)
|
381
396
|
message.instance_variable_set(
|
382
397
|
field[:name],
|
383
|
-
field[:type].deserialize(io)
|
398
|
+
field[:type].deserialize(io, options)
|
384
399
|
)
|
385
400
|
end
|
386
401
|
|
data/lib/mongo/protocol/msg.rb
CHANGED
@@ -161,7 +161,7 @@ module Mongo
|
|
161
161
|
end
|
162
162
|
|
163
163
|
# Reverse-populates the instance variables after deserialization sets
|
164
|
-
# @sections to the list of documents.
|
164
|
+
# the @sections instance variable to the list of documents.
|
165
165
|
#
|
166
166
|
# TODO fix deserialization so that this method is not needed.
|
167
167
|
#
|
@@ -214,10 +214,7 @@ module Mongo
|
|
214
214
|
if cmd.key?('$db') && !enc_cmd.key?('$db')
|
215
215
|
enc_cmd['$db'] = cmd['$db']
|
216
216
|
end
|
217
|
-
|
218
|
-
if enc_cmd['txnNumber'].is_a?(Integer) && cmd[:txnNumber].is_a?(BSON::Int64)
|
219
|
-
enc_cmd['txnNumber'] = BSON::Int64.new(enc_cmd[:txnNumber])
|
220
|
-
end
|
217
|
+
|
221
218
|
Msg.new(@flags, @options, enc_cmd)
|
222
219
|
else
|
223
220
|
self
|
@@ -57,10 +57,11 @@ module Mongo
|
|
57
57
|
# Deserializes the header value from the IO stream
|
58
58
|
#
|
59
59
|
# @param [ String ] buffer Buffer containing the message header.
|
60
|
+
# @param [ Hash ] options This method currently accepts no options.
|
60
61
|
#
|
61
62
|
# @return [ Array<Fixnum> ] Array consisting of the deserialized
|
62
63
|
# length, request id, response id, and op code.
|
63
|
-
def self.deserialize(buffer)
|
64
|
+
def self.deserialize(buffer, options = {})
|
64
65
|
buffer.get_bytes(16).unpack(HEADER_PACK)
|
65
66
|
end
|
66
67
|
end
|
@@ -123,9 +124,10 @@ module Mongo
|
|
123
124
|
# Deserializes a 32-bit Fixnum from the IO stream
|
124
125
|
#
|
125
126
|
# @param [ String ] buffer Buffer containing the 32-bit integer
|
127
|
+
# @param [ Hash ] options This method currently accepts no options.
|
126
128
|
#
|
127
129
|
# @return [ Fixnum ] Deserialized Int32
|
128
|
-
def self.deserialize(buffer)
|
130
|
+
def self.deserialize(buffer, options = {})
|
129
131
|
buffer.get_int32
|
130
132
|
end
|
131
133
|
end
|
@@ -156,9 +158,10 @@ module Mongo
|
|
156
158
|
# Deserializes a 64-bit Fixnum from the IO stream
|
157
159
|
#
|
158
160
|
# @param [ String ] buffer Buffer containing the 64-bit integer.
|
161
|
+
# @param [ Hash ] options This method currently accepts no options.
|
159
162
|
#
|
160
163
|
# @return [Fixnum] Deserialized Int64.
|
161
|
-
def self.deserialize(buffer)
|
164
|
+
def self.deserialize(buffer, options = {})
|
162
165
|
buffer.get_int64
|
163
166
|
end
|
164
167
|
end
|
@@ -198,19 +201,24 @@ module Mongo
|
|
198
201
|
# Deserializes a section of an OP_MSG from the IO stream.
|
199
202
|
#
|
200
203
|
# @param [ BSON::ByteBuffer ] buffer Buffer containing the sections.
|
204
|
+
# @param [ Hash ] options
|
205
|
+
#
|
206
|
+
# @option options [ Boolean ] :deserialize_as_bson Whether to perform
|
207
|
+
# section deserialization using BSON types instead of native Ruby types
|
208
|
+
# wherever possible.
|
201
209
|
#
|
202
210
|
# @return [ Array<BSON::Document> ] Deserialized sections.
|
203
211
|
#
|
204
212
|
# @since 2.5.0
|
205
|
-
def self.deserialize(buffer)
|
213
|
+
def self.deserialize(buffer, options = {})
|
206
214
|
end_length = (@flag_bits & Msg::FLAGS.index(:checksum_present)) == 1 ? 32 : 0
|
207
215
|
sections = []
|
208
216
|
until buffer.length == end_length
|
209
217
|
case byte = buffer.get_byte
|
210
218
|
when PayloadZero::TYPE_BYTE
|
211
|
-
sections << PayloadZero.deserialize(buffer)
|
219
|
+
sections << PayloadZero.deserialize(buffer, options)
|
212
220
|
when PayloadOne::TYPE_BYTE
|
213
|
-
sections += PayloadOne.deserialize(buffer)
|
221
|
+
sections += PayloadOne.deserialize(buffer, options)
|
214
222
|
else
|
215
223
|
raise Error::UnknownPayloadType.new(byte)
|
216
224
|
end
|
@@ -260,12 +268,18 @@ module Mongo
|
|
260
268
|
# Deserializes a section of payload type 0 of an OP_MSG from the IO stream.
|
261
269
|
#
|
262
270
|
# @param [ BSON::ByteBuffer ] buffer Buffer containing the sections.
|
271
|
+
# @param [ Hash ] options
|
272
|
+
#
|
273
|
+
# @option options [ Boolean ] :deserialize_as_bson Whether to perform
|
274
|
+
# section deserialization using BSON types instead of native Ruby types
|
275
|
+
# wherever possible.
|
263
276
|
#
|
264
277
|
# @return [ Array<BSON::Document> ] Deserialized section.
|
265
278
|
#
|
266
279
|
# @since 2.5.0
|
267
|
-
def self.deserialize(buffer)
|
268
|
-
|
280
|
+
def self.deserialize(buffer, options = {})
|
281
|
+
mode = options[:deserialize_as_bson] ? :bson : nil
|
282
|
+
BSON::Document.from_bson(buffer, **{ mode: mode })
|
269
283
|
end
|
270
284
|
end
|
271
285
|
|
@@ -352,10 +366,16 @@ module Mongo
|
|
352
366
|
# Deserializes a document from the IO stream
|
353
367
|
#
|
354
368
|
# @param [ String ] buffer Buffer containing the BSON encoded document.
|
369
|
+
# @param [ Hash ] options
|
370
|
+
#
|
371
|
+
# @option options [ Boolean ] :deserialize_as_bson Whether to perform
|
372
|
+
# section deserialization using BSON types instead of native Ruby types
|
373
|
+
# wherever possible.
|
355
374
|
#
|
356
375
|
# @return [ Hash ] The decoded BSON document.
|
357
|
-
def self.deserialize(buffer)
|
358
|
-
|
376
|
+
def self.deserialize(buffer, options = {})
|
377
|
+
mode = options[:deserialize_as_bson] ? :bson : nil
|
378
|
+
BSON::Document.from_bson(buffer, **{ mode: mode })
|
359
379
|
end
|
360
380
|
|
361
381
|
# Whether there can be a size limit on this type after serialization.
|
@@ -389,11 +409,12 @@ module Mongo
|
|
389
409
|
# Deserializes a byte from the byte buffer.
|
390
410
|
#
|
391
411
|
# @param [ BSON::ByteBuffer ] buffer Buffer containing the value to read.
|
412
|
+
# @param [ Hash ] options This method currently accepts no options.
|
392
413
|
#
|
393
414
|
# @return [ String ] The byte.
|
394
415
|
#
|
395
416
|
# @since 2.5.0
|
396
|
-
def self.deserialize(buffer)
|
417
|
+
def self.deserialize(buffer, options = {})
|
397
418
|
buffer.get_byte
|
398
419
|
end
|
399
420
|
end
|
data/lib/mongo/retryable.rb
CHANGED
@@ -144,7 +144,7 @@ module Mongo
|
|
144
144
|
# @note This only retries read operations on socket errors.
|
145
145
|
#
|
146
146
|
# @param [ Hash ] options Options.
|
147
|
-
# @
|
147
|
+
# @yield Calls the provided block with no arguments
|
148
148
|
#
|
149
149
|
# @option options [ String ] :retry_message Message to log when retrying.
|
150
150
|
#
|
@@ -400,7 +400,7 @@ module Mongo
|
|
400
400
|
@auth_mechanism || (@server.features.scram_sha_1_enabled? ? :scram : :mongodb_cr)
|
401
401
|
end
|
402
402
|
|
403
|
-
def deliver(message, client)
|
403
|
+
def deliver(message, client, options = {})
|
404
404
|
begin
|
405
405
|
super
|
406
406
|
# Important: timeout errors are not handled here
|
@@ -115,11 +115,16 @@ module Mongo
|
|
115
115
|
# @param [ Array<Message> ] messages A one-element array containing
|
116
116
|
# the message to dispatch.
|
117
117
|
# @param [ Integer ] operation_id The operation id to link messages.
|
118
|
+
# @param [ Hash ] options
|
119
|
+
#
|
120
|
+
# @option options [ Boolean ] :deserialize_as_bson Whether to deserialize
|
121
|
+
# the response to this message using BSON objects in place of native
|
122
|
+
# Ruby types wherever possible.
|
118
123
|
#
|
119
124
|
# @return [ Protocol::Message | nil ] The reply if needed.
|
120
125
|
#
|
121
126
|
# @since 2.0.0
|
122
|
-
def dispatch(messages, operation_id = nil, client = nil)
|
127
|
+
def dispatch(messages, operation_id = nil, client = nil, options = {})
|
123
128
|
# The monitoring code does not correctly handle multiple messages,
|
124
129
|
# and the driver internally does not send more than one message at
|
125
130
|
# a time ever. Thus prohibit multiple message use for now.
|
@@ -127,12 +132,12 @@ module Mongo
|
|
127
132
|
raise ArgumentError, 'Can only dispatch one message at a time'
|
128
133
|
end
|
129
134
|
message = messages.first
|
130
|
-
deliver(message, client)
|
135
|
+
deliver(message, client, options)
|
131
136
|
end
|
132
137
|
|
133
138
|
private
|
134
139
|
|
135
|
-
def deliver(message, client)
|
140
|
+
def deliver(message, client, options = {})
|
136
141
|
if Lint.enabled? && !@socket
|
137
142
|
raise Error::LintError, "Trying to deliver a message over a disconnected connection (to #{address})"
|
138
143
|
end
|
@@ -146,7 +151,7 @@ module Mongo
|
|
146
151
|
begin
|
147
152
|
socket.write(buffer.to_s)
|
148
153
|
result = if message.replyable?
|
149
|
-
Protocol::Message.deserialize(socket, max_message_size, message.request_id)
|
154
|
+
Protocol::Message.deserialize(socket, max_message_size, message.request_id, options)
|
150
155
|
else
|
151
156
|
nil
|
152
157
|
end
|
@@ -21,7 +21,7 @@ module Mongo
|
|
21
21
|
class Populator
|
22
22
|
include BackgroundThread
|
23
23
|
|
24
|
-
# @param [ Server::ConnectionPool ] The connection pool.
|
24
|
+
# @param [ Server::ConnectionPool ] pool The connection pool.
|
25
25
|
# @param [ Hash ] options The options.
|
26
26
|
#
|
27
27
|
# @option options [ Logger ] :logger A custom logger to use.
|
data/lib/mongo/session.rb
CHANGED
@@ -697,7 +697,7 @@ module Mongo
|
|
697
697
|
# The exception instance should already have all of the labels set on it
|
698
698
|
# (both client- and server-side generated ones).
|
699
699
|
#
|
700
|
-
# @param [ Error ] The exception instance to process.
|
700
|
+
# @param [ Error ] error The exception instance to process.
|
701
701
|
#
|
702
702
|
# @api private
|
703
703
|
def unpin_maybe(error)
|
data/lib/mongo/srv/monitor.rb
CHANGED
@@ -13,65 +13,94 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
module Mongo
|
16
|
-
module
|
16
|
+
module Srv
|
17
17
|
|
18
|
-
#
|
19
|
-
#
|
18
|
+
# Periodically retrieves SRV records for the cluster's SRV URI, and
|
19
|
+
# sets the cluster's server list to the SRV lookup result.
|
20
|
+
#
|
21
|
+
# If an error is encountered during SRV lookup or an SRV record is invalid
|
22
|
+
# or disallowed for security reasons, a warning is logged and monitoring
|
23
|
+
# continues.
|
20
24
|
#
|
21
25
|
# @api private
|
22
26
|
class Monitor
|
23
27
|
include Loggable
|
24
|
-
|
25
|
-
|
28
|
+
include BackgroundThread
|
29
|
+
|
30
|
+
MIN_SCAN_INTERVAL = 60
|
31
|
+
|
32
|
+
DEFAULT_TIMEOUT = 10
|
33
|
+
|
34
|
+
# Creates the SRV monitor.
|
35
|
+
#
|
36
|
+
# @param [ Cluster ] cluster The cluster.
|
37
|
+
# @param [ Hash ] options The cluster options.
|
38
|
+
#
|
39
|
+
# @option options [ Float ] :timeout The timeout to use for DNS lookups.
|
40
|
+
# @option options [ URI::SRVProtocol ] :srv_uri The SRV URI to monitor.
|
41
|
+
# @option options [ Hash ] :resolv_options For internal driver use only.
|
42
|
+
# Options to pass through to Resolv::DNS constructor for SRV lookups.
|
43
|
+
def initialize(cluster, options = nil)
|
44
|
+
options = if options
|
45
|
+
options.dup
|
46
|
+
else
|
47
|
+
{}
|
48
|
+
end
|
49
|
+
@cluster = cluster
|
50
|
+
@resolver = Srv::Resolver.new(options)
|
51
|
+
unless @srv_uri = options.delete(:srv_uri)
|
52
|
+
raise ArgumentError, 'SRV URI is required'
|
53
|
+
end
|
54
|
+
@options = options.freeze
|
55
|
+
@last_result = @srv_uri.srv_result
|
56
|
+
@stop_semaphore = Semaphore.new
|
57
|
+
end
|
26
58
|
|
27
59
|
attr_reader :options
|
28
60
|
|
29
|
-
|
30
|
-
@options = options || {}
|
31
|
-
@cluster = cluster
|
32
|
-
@resolver = resolver
|
33
|
-
@records = srv_records
|
34
|
-
@no_records_found = false
|
35
|
-
end
|
61
|
+
attr_reader :cluster
|
36
62
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
scan!
|
42
|
-
end
|
43
|
-
end
|
63
|
+
# @return [ Srv::Result ] Last known SRV lookup result. Used for
|
64
|
+
# determining intervals between SRV lookups, which depend on SRV DNS
|
65
|
+
# records' TTL values.
|
66
|
+
attr_reader :last_result
|
44
67
|
|
68
|
+
def start!
|
69
|
+
super
|
45
70
|
ObjectSpace.define_finalizer(self, self.class.finalize(@thread))
|
46
71
|
end
|
47
72
|
|
73
|
+
private
|
74
|
+
|
75
|
+
def do_work
|
76
|
+
scan!
|
77
|
+
@stop_semaphore.wait(scan_interval)
|
78
|
+
end
|
79
|
+
|
48
80
|
def scan!
|
49
|
-
|
81
|
+
old_hosts = last_result.address_strs
|
50
82
|
|
51
83
|
begin
|
52
|
-
|
84
|
+
last_result = Timeout.timeout(timeout) do
|
85
|
+
@resolver.get_records(@srv_uri.query_hostname)
|
86
|
+
end
|
53
87
|
rescue Resolv::ResolvTimeout => e
|
54
|
-
log_warn("
|
88
|
+
log_warn("SRV monitor: timed out trying to resolve hostname #{@srv_uri.query_hostname}: #{e.class}: #{e}")
|
89
|
+
return
|
90
|
+
rescue ::Timeout::Error
|
91
|
+
log_warn("SRV monitor: timed out trying to resolve hostname #{@srv_uri.query_hostname} (timeout=#{timeout})")
|
55
92
|
return
|
56
93
|
rescue Resolv::ResolvError => e
|
57
|
-
log_warn("
|
94
|
+
log_warn("SRV monitor: unable to resolve hostname #{@srv_uri.query_hostname}: #{e.class}: #{e}")
|
58
95
|
return
|
59
96
|
end
|
60
97
|
|
61
|
-
if
|
62
|
-
@
|
98
|
+
if last_result.empty?
|
99
|
+
log_warn("SRV monitor: hostname #{@srv_uri.query_hostname} resolved to zero records")
|
63
100
|
return
|
64
101
|
end
|
65
102
|
|
66
|
-
@
|
67
|
-
|
68
|
-
(@old_hosts - @records.hosts).each do |host|
|
69
|
-
@cluster.remove(host)
|
70
|
-
end
|
71
|
-
|
72
|
-
(@records.hosts - @old_hosts).each do |host|
|
73
|
-
@cluster.add(host)
|
74
|
-
end
|
103
|
+
@cluster.set_server_list(last_result.address_strs)
|
75
104
|
end
|
76
105
|
|
77
106
|
def self.finalize(thread)
|
@@ -80,17 +109,19 @@ module Mongo
|
|
80
109
|
end
|
81
110
|
end
|
82
111
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
elsif @records.min_ttl.nil?
|
89
|
-
MIN_RESCAN_FREQUENCY
|
112
|
+
def scan_interval
|
113
|
+
if last_result.empty?
|
114
|
+
[cluster.heartbeat_interval, MIN_SCAN_INTERVAL].min
|
115
|
+
elsif last_result.min_ttl.nil?
|
116
|
+
MIN_SCAN_INTERVAL
|
90
117
|
else
|
91
|
-
[
|
118
|
+
[last_result.min_ttl, MIN_SCAN_INTERVAL].max
|
92
119
|
end
|
93
120
|
end
|
121
|
+
|
122
|
+
def timeout
|
123
|
+
options[:timeout] || DEFAULT_TIMEOUT
|
124
|
+
end
|
94
125
|
end
|
95
126
|
end
|
96
127
|
end
|