mongo 2.24.1 → 2.26.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.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/bin/mongo_console +2 -2
  3. data/lib/mongo/address/validator.rb +1 -1
  4. data/lib/mongo/address.rb +1 -3
  5. data/lib/mongo/auth/aws/conversation.rb +0 -4
  6. data/lib/mongo/auth/aws/credentials_retriever.rb +3 -8
  7. data/lib/mongo/auth/base.rb +1 -1
  8. data/lib/mongo/auth/sasl_conversation_base.rb +1 -1
  9. data/lib/mongo/auth/scram_conversation_base.rb +1 -8
  10. data/lib/mongo/auth/stringprep.rb +1 -6
  11. data/lib/mongo/auth/user/view.rb +1 -1
  12. data/lib/mongo/bulk_write/combineable.rb +0 -7
  13. data/lib/mongo/bulk_write/result_combiner.rb +3 -3
  14. data/lib/mongo/bulk_write/validatable.rb +0 -4
  15. data/lib/mongo/bulk_write.rb +1 -35
  16. data/lib/mongo/client.rb +117 -17
  17. data/lib/mongo/client_encryption.rb +36 -11
  18. data/lib/mongo/cluster/reapers/cursor_reaper.rb +12 -5
  19. data/lib/mongo/cluster/sdam_flow.rb +4 -4
  20. data/lib/mongo/cluster/topology/base.rb +1 -1
  21. data/lib/mongo/cluster.rb +13 -30
  22. data/lib/mongo/collection/queryable_encryption.rb +7 -6
  23. data/lib/mongo/collection/view/aggregation/behavior.rb +1 -1
  24. data/lib/mongo/collection/view/aggregation.rb +2 -4
  25. data/lib/mongo/collection/view/builder/aggregation.rb +3 -5
  26. data/lib/mongo/collection/view/builder/map_reduce.rb +1 -1
  27. data/lib/mongo/collection/view/change_stream.rb +14 -9
  28. data/lib/mongo/collection/view/map_reduce.rb +2 -11
  29. data/lib/mongo/collection/view/readable.rb +11 -7
  30. data/lib/mongo/collection/view/writable.rb +4 -39
  31. data/lib/mongo/collection/view.rb +8 -4
  32. data/lib/mongo/collection.rb +1 -1
  33. data/lib/mongo/crypt/auto_encrypter.rb +6 -2
  34. data/lib/mongo/crypt/binary.rb +7 -4
  35. data/lib/mongo/crypt/binding.rb +154 -11
  36. data/lib/mongo/crypt/context.rb +50 -19
  37. data/lib/mongo/crypt/encryption_io.rb +11 -5
  38. data/lib/mongo/crypt/explicit_encrypter.rb +27 -12
  39. data/lib/mongo/crypt/explicit_encryption_context.rb +51 -12
  40. data/lib/mongo/crypt/handle.rb +35 -5
  41. data/lib/mongo/crypt/kms/credentials.rb +70 -31
  42. data/lib/mongo/crypt/kms/kmip/master_document.rb +30 -1
  43. data/lib/mongo/crypt/kms/master_key_document.rb +11 -6
  44. data/lib/mongo/crypt/kms.rb +12 -1
  45. data/lib/mongo/cursor.rb +8 -2
  46. data/lib/mongo/database/cursor_command_view.rb +95 -0
  47. data/lib/mongo/database.rb +144 -18
  48. data/lib/mongo/error/invalid_uri.rb +5 -1
  49. data/lib/mongo/error/operation_failure.rb +2 -2
  50. data/lib/mongo/error/parser.rb +3 -3
  51. data/lib/mongo/error/sdam_error_detection.rb +2 -3
  52. data/lib/mongo/error/unsupported_option.rb +0 -40
  53. data/lib/mongo/grid/fs_bucket.rb +29 -23
  54. data/lib/mongo/grid/stream/read.rb +2 -2
  55. data/lib/mongo/grid/stream/write.rb +3 -3
  56. data/lib/mongo/index/view.rb +13 -7
  57. data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +1 -0
  58. data/lib/mongo/monitoring/event/cmap/connection_check_out_started.rb +1 -0
  59. data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +1 -0
  60. data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +1 -0
  61. data/lib/mongo/monitoring/event/cmap/connection_closed.rb +1 -0
  62. data/lib/mongo/monitoring/event/cmap/connection_created.rb +1 -0
  63. data/lib/mongo/monitoring/event/cmap/connection_ready.rb +1 -0
  64. data/lib/mongo/monitoring/event/cmap/pool_cleared.rb +1 -0
  65. data/lib/mongo/monitoring/event/cmap/pool_closed.rb +1 -0
  66. data/lib/mongo/monitoring/event/cmap/pool_created.rb +1 -0
  67. data/lib/mongo/monitoring/event/cmap/pool_ready.rb +1 -0
  68. data/lib/mongo/monitoring/event/command_failed.rb +1 -0
  69. data/lib/mongo/monitoring/event/command_started.rb +1 -0
  70. data/lib/mongo/monitoring/event/command_succeeded.rb +1 -0
  71. data/lib/mongo/monitoring/event/secure.rb +12 -17
  72. data/lib/mongo/monitoring/event/server_closed.rb +1 -0
  73. data/lib/mongo/monitoring/event/server_description_changed.rb +1 -0
  74. data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +1 -0
  75. data/lib/mongo/monitoring/event/server_heartbeat_started.rb +1 -0
  76. data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +1 -0
  77. data/lib/mongo/monitoring/event/server_opening.rb +1 -0
  78. data/lib/mongo/monitoring/event/topology_changed.rb +1 -0
  79. data/lib/mongo/monitoring/event/topology_closed.rb +1 -0
  80. data/lib/mongo/monitoring/event/topology_opening.rb +1 -0
  81. data/lib/mongo/operation/create/op_msg.rb +1 -0
  82. data/lib/mongo/operation/create_index/op_msg.rb +2 -1
  83. data/lib/mongo/operation/cursor_command/op_msg.rb +37 -0
  84. data/lib/mongo/operation/cursor_command/result.rb +60 -0
  85. data/lib/mongo/operation/cursor_command.rb +33 -0
  86. data/lib/mongo/operation/delete/op_msg.rb +1 -0
  87. data/lib/mongo/operation/drop/op_msg.rb +1 -0
  88. data/lib/mongo/operation/drop_database/op_msg.rb +1 -0
  89. data/lib/mongo/operation/drop_index/op_msg.rb +1 -0
  90. data/lib/mongo/operation/find/builder/command.rb +1 -1
  91. data/lib/mongo/operation/insert/bulk_result.rb +2 -4
  92. data/lib/mongo/operation/insert/op_msg.rb +1 -0
  93. data/lib/mongo/operation/result.rb +21 -25
  94. data/lib/mongo/operation/shared/causal_consistency_supported.rb +4 -2
  95. data/lib/mongo/operation/shared/executable.rb +2 -15
  96. data/lib/mongo/operation/shared/result/aggregatable.rb +2 -2
  97. data/lib/mongo/operation/shared/sessions_supported.rb +3 -5
  98. data/lib/mongo/operation/update/bulk_result.rb +1 -1
  99. data/lib/mongo/operation/update/op_msg.rb +1 -0
  100. data/lib/mongo/operation/write_command/op_msg.rb +2 -0
  101. data/lib/mongo/operation.rb +1 -0
  102. data/lib/mongo/options/redacted.rb +2 -2
  103. data/lib/mongo/protocol/compressed.rb +15 -11
  104. data/lib/mongo/protocol/message.rb +16 -20
  105. data/lib/mongo/protocol/msg.rb +1 -12
  106. data/lib/mongo/protocol/registry.rb +1 -1
  107. data/lib/mongo/protocol/serializers.rb +1 -3
  108. data/lib/mongo/query_cache.rb +3 -5
  109. data/lib/mongo/retryable/backpressure.rb +17 -2
  110. data/lib/mongo/retryable/read_worker.rb +1 -1
  111. data/lib/mongo/retryable/retry_policy.rb +2 -2
  112. data/lib/mongo/retryable/write_worker.rb +2 -2
  113. data/lib/mongo/retryable.rb +1 -1
  114. data/lib/mongo/server/app_metadata.rb +1 -1
  115. data/lib/mongo/server/connection.rb +3 -1
  116. data/lib/mongo/server/connection_common.rb +9 -1
  117. data/lib/mongo/server/connection_pool/generation_manager.rb +1 -1
  118. data/lib/mongo/server/connection_pool.rb +24 -10
  119. data/lib/mongo/server/description/features.rb +1 -2
  120. data/lib/mongo/server/description.rb +1 -2
  121. data/lib/mongo/server/monitor/connection.rb +3 -2
  122. data/lib/mongo/server/monitor.rb +133 -30
  123. data/lib/mongo/server/pending_connection.rb +1 -5
  124. data/lib/mongo/server/push_monitor.rb +16 -2
  125. data/lib/mongo/server.rb +28 -9
  126. data/lib/mongo/server_selector/base.rb +3 -6
  127. data/lib/mongo/server_selector/secondary_preferred.rb +4 -1
  128. data/lib/mongo/session.rb +25 -26
  129. data/lib/mongo/socket/ssl.rb +3 -3
  130. data/lib/mongo/socket.rb +1 -7
  131. data/lib/mongo/srv/monitor.rb +11 -2
  132. data/lib/mongo/srv/resolver.rb +1 -1
  133. data/lib/mongo/srv/result.rb +5 -3
  134. data/lib/mongo/timeout.rb +10 -15
  135. data/lib/mongo/tracing/open_telemetry/command_tracer.rb +16 -2
  136. data/lib/mongo/uri/options_mapper.rb +36 -13
  137. data/lib/mongo/uri/srv_protocol.rb +4 -4
  138. data/lib/mongo/uri.rb +56 -8
  139. data/lib/mongo/utils.rb +1 -1
  140. data/lib/mongo/version.rb +1 -1
  141. metadata +6 -4
  142. data/lib/mongo/auth/stringprep/unicode_normalize/normalize.rb +0 -173
  143. data/lib/mongo/auth/stringprep/unicode_normalize/tables.rb +0 -1164
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9359ddba0c1cd025a706f5970893bbdce249c2ab5fbe9d4ce3293a308bb11ade
4
- data.tar.gz: f88046bc63c0a6b2d23017a3df2ba2cfe72bf6cd7d9565c985e64084bc41147d
3
+ metadata.gz: 439e028e8b9ce062d109e1760508977f13a87dd6ee4cb9d5221a6a3108fec214
4
+ data.tar.gz: bb346a893717256c3eac12af4a63c718bfd4f567b1ec17b95727e45f9f8e11d5
5
5
  SHA512:
6
- metadata.gz: 1ea813993a5e069434299e68eacfb1c79bcbb0161728d703dd20e3047f2699a11640c94067e22c1089e34b56398afb959843eb3d914f110878056c646402c7e8
7
- data.tar.gz: 837e4866998fc01f8523768fbee6f062a3d0564932f318c27d701e826ed123a4c329df0d74449455afa1146e2de42d7fe54941aaf25e9e9061fc83dd82d87d06
6
+ metadata.gz: 3d650edf8067fbe760d1c0526e62bef7cfda7c27284b725ffcc5a8e3c33c712011b3aa478e87db2abea876d36e332daf733d1180954ed6bd1e5003a258a05f49
7
+ data.tar.gz: 6832d11c4cae68a7776c8e5f7ef8e5bd9fcbda45bedf8e8cb208fdc5103060582a315cb75434e003462500b4da05deb5e71618a6856159262ff3655fbe16888d
data/bin/mongo_console CHANGED
@@ -4,8 +4,8 @@
4
4
  $LOAD_PATH[0, 0] = File.join(File.dirname(__FILE__), '..', 'lib')
5
5
 
6
6
  require 'mongo'
7
- # include the mongo namespace
8
- include Mongo
7
+ # include the mongo namespace so its constants are available unqualified
8
+ Object.include(Mongo)
9
9
 
10
10
  begin
11
11
  require 'pry'
@@ -24,7 +24,7 @@ module Mongo
24
24
  case address_str
25
25
  when /\A\[[\d:]+\](?::(\d+))?\z/
26
26
  # ipv6 with optional port
27
- if port_str = ::Regexp.last_match(1)
27
+ if (port_str = ::Regexp.last_match(1))
28
28
  validate_port_str!(port_str)
29
29
  end
30
30
  when %r{\A/}, /\.sock\z/
data/lib/mongo/address.rb CHANGED
@@ -302,9 +302,7 @@ module Mongo
302
302
  raise Error::TimeoutError, "#{e.class}: #{e} (for #{self})" if csot
303
303
 
304
304
  raise e
305
- rescue IOError, SystemCallError, ::SocketError => e
306
- raise Error::SocketError, "#{e.class}: #{e} (for #{self})"
307
- rescue OpenSSL::SSL::SSLError => e
305
+ rescue IOError, SystemCallError, ::SocketError, OpenSSL::SSL::SSLError => e
308
306
  raise Error::SocketError, "#{e.class}: #{e} (for #{self})"
309
307
  end
310
308
  end
@@ -74,10 +74,6 @@ module Mongo
74
74
  client_first_data.to_bson.to_s
75
75
  end
76
76
 
77
- def wrap_data(data)
78
- BSON::Binary.new(data.to_bson.to_s)
79
- end
80
-
81
77
  def client_nonce
82
78
  @client_nonce ||= SecureRandom.random_bytes(32)
83
79
  end
@@ -140,14 +140,9 @@ module Mongo
140
140
  # @raise Error::TimeoutError if credentials cannot be retrieved within
141
141
  # the timeout defined on the operation context.
142
142
  def obtain_credentials_from_endpoints(timeout_holder = nil)
143
- if (credentials = web_identity_credentials(timeout_holder)) && credentials_valid?(credentials,
144
- 'Web identity token')
145
- credentials
146
- elsif (credentials = ecs_metadata_credentials(timeout_holder)) && credentials_valid?(credentials,
147
- 'ECS task metadata')
148
- credentials
149
- elsif (credentials = ec2_metadata_credentials(timeout_holder)) && credentials_valid?(credentials,
150
- 'EC2 instance metadata')
143
+ if ((credentials = web_identity_credentials(timeout_holder)) && credentials_valid?(credentials, 'Web identity token')) ||
144
+ ((credentials = ecs_metadata_credentials(timeout_holder)) && credentials_valid?(credentials, 'ECS task metadata')) ||
145
+ ((credentials = ec2_metadata_credentials(timeout_holder)) && credentials_valid?(credentials, 'EC2 instance metadata'))
151
146
  credentials
152
147
  end
153
148
  end
@@ -102,7 +102,7 @@ module Mongo
102
102
  context = Operation::Context.new(options: {
103
103
  server_api: connection.options[:server_api],
104
104
  })
105
- if server_api = context.server_api
105
+ if (server_api = context.server_api)
106
106
  msg = msg.maybe_add_server_api(server_api)
107
107
  end
108
108
  reply = connection.dispatch([ msg ], context)
@@ -71,7 +71,7 @@ module Mongo
71
71
  mechanism: auth_mechanism_name,
72
72
  payload: BSON::Binary.new(payload)
73
73
  )
74
- if options = client_first_message_options
74
+ if (options = client_first_message_options)
75
75
  # Short SCRAM conversation,
76
76
  # https://jira.mongodb.org/browse/DRIVERS-707
77
77
  doc[:options] = options
@@ -200,7 +200,7 @@ module Mongo
200
200
  # This method can be called from different conversation steps
201
201
  # depending on whether the short SCRAM conversation is used.
202
202
  def check_server_signature(payload_data)
203
- return unless verifier = payload_data['v']
203
+ return unless (verifier = payload_data['v'])
204
204
  raise Error::InvalidSignature.new(verifier, server_signature) unless compare_digest(verifier, server_signature)
205
205
 
206
206
  @server_verified = true
@@ -281,13 +281,6 @@ module Mongo
281
281
  # @since 2.0.0
282
282
  attr_reader :iterations
283
283
 
284
- # Get the data from the returned payload.
285
- #
286
- # @api private
287
- #
288
- # @since 2.0.0
289
- attr_reader :payload_data
290
-
291
284
  # Get the server nonce from the payload.
292
285
  #
293
286
  # @api private
@@ -97,12 +97,7 @@ module Mongo
97
97
  end
98
98
 
99
99
  def normalize!(out)
100
- if String.method_defined?(:unicode_normalize!)
101
- out.unicode_normalize!(:nfkc)
102
- else
103
- require 'mongo/auth/stringprep/unicode_normalize/normalize'
104
- out.replace(UnicodeNormalize.normalize(out, :nfkc))
105
- end
100
+ out.unicode_normalize!(:nfkc)
106
101
  end
107
102
 
108
103
  def table_contains?(table, c)
@@ -152,7 +152,7 @@ module Mongo
152
152
  end
153
153
 
154
154
  def execute_operation(options)
155
- client.send(:with_session, options) do |session|
155
+ client.with_session(options) do |session|
156
156
  op = yield session
157
157
  op.execute(next_primary(nil, session), context: Operation::Context.new(client: client, session: session))
158
158
  end
@@ -39,7 +39,6 @@ module Mongo
39
39
  @requests = requests
40
40
  @has_collation = false
41
41
  @has_array_filters = false
42
- @has_hint = false
43
42
  end
44
43
 
45
44
  # @return [ Boolean ] Whether one or more operation specifies the collation
@@ -54,12 +53,6 @@ module Mongo
54
53
  @has_array_filters
55
54
  end
56
55
 
57
- # @return [ Boolean ] Whether one or more operation specifies the
58
- # hint option.
59
- def has_hint?
60
- @has_hint
61
- end
62
-
63
56
  private
64
57
 
65
58
  def combine_requests(ops)
@@ -92,7 +92,7 @@ module Mongo
92
92
 
93
93
  def combine_counts!(result)
94
94
  Result::FIELDS.each do |field|
95
- if result.respond_to?(field) && value = result.send(field)
95
+ if result.respond_to?(field) && (value = result.public_send(field))
96
96
  results.merge!(field => (results[field] || 0) + value)
97
97
  end
98
98
  end
@@ -115,7 +115,7 @@ module Mongo
115
115
  end
116
116
 
117
117
  def combine_write_errors!(result)
118
- if write_errors = result.aggregate_write_errors(count)
118
+ if (write_errors = result.aggregate_write_errors(count))
119
119
  results.merge!(
120
120
  'writeErrors' => ((results['writeErrors'] || []) << write_errors).flatten
121
121
  )
@@ -125,7 +125,7 @@ module Mongo
125
125
  end
126
126
 
127
127
  def combine_write_concern_errors!(result)
128
- return unless write_concern_errors = result.aggregate_write_concern_errors(count)
128
+ return unless (write_concern_errors = result.aggregate_write_concern_errors(count))
129
129
 
130
130
  results['writeConcernErrors'] = (results['writeConcernErrors'] || []) +
131
131
  write_concern_errors
@@ -43,10 +43,6 @@ module Mongo
43
43
  @has_collation = true if document.respond_to?(:keys) && (document[:collation] || document[Operation::COLLATION])
44
44
 
45
45
  @has_array_filters = true if document.respond_to?(:keys) && document[:array_filters]
46
-
47
- return unless document.respond_to?(:keys) && document[:hint]
48
-
49
- @has_hint = true
50
46
  end
51
47
 
52
48
  private
@@ -236,8 +236,6 @@ module Mongo
236
236
  end
237
237
 
238
238
  def execute_operation(name, values, connection, context, operation_id, result_combiner, session, txn_num = nil)
239
- validate_hint!(connection)
240
-
241
239
  unpin_maybe(session, connection) do
242
240
  if values.size > connection.description.max_write_batch_size
243
241
  split_execute(name, values, connection, context, operation_id, result_combiner, session, txn_num)
@@ -310,38 +308,6 @@ module Mongo
310
308
  Operation::Update.new(spec).bulk_execute(connection, context: context)
311
309
  end
312
310
 
313
- def validate_hint!(connection)
314
- return unless op_combiner.has_hint?
315
- return unless !can_hint?(connection) && write_concern && !write_concern.acknowledged?
316
-
317
- raise Error::UnsupportedOption.hint_error(unacknowledged_write: true)
318
- end
319
-
320
- # Loop through the requests and check if each operation is allowed to send
321
- # a hint for each operation on the given server version.
322
- #
323
- # For the following operations, the client can send a hint for all supported
324
- # server versions, and for the rest, the client can only send it for 4.4+:
325
- # - updateOne
326
- # - updateMany
327
- # - replaceOne
328
- #
329
- # @param [ Connection ] connection The connection object.
330
- #
331
- # @return [ true | false ] Whether the request is able to send hints for
332
- # the current server version.
333
- def can_hint?(connection)
334
- gte_4_4 = connection.server.description.server_version_gte?('4.4')
335
- op_combiner.requests.all? do |req|
336
- op = req.keys.first
337
- if req[op].keys.include?(:hint)
338
- %i[update_one update_many replace_one].include?(op) || gte_4_4
339
- else
340
- true
341
- end
342
- end
343
- end
344
-
345
311
  # Perform the request document validation required by driver specifications.
346
312
  # This method validates the first key of each update request document to be
347
313
  # an operator (i.e. start with $) and the first key of each replacement
@@ -360,7 +326,7 @@ module Mongo
360
326
  requests_empty = true
361
327
  @requests.each do |req|
362
328
  requests_empty = false
363
- if op = req.keys.first
329
+ if (op = req.keys.first)
364
330
  if %i[update_one update_many].include?(op)
365
331
  if (doc = maybe_first(req.dig(op, :update))) && (key = doc.keys&.first) && !key.to_s.start_with?('$')
366
332
  raise Error::InvalidUpdateDocument.new(key: key) if Mongo.validate_update_replace
data/lib/mongo/client.rb CHANGED
@@ -172,7 +172,6 @@ module Mongo
172
172
  @monitoring
173
173
  end
174
174
  end
175
- private :monitoring
176
175
 
177
176
  # Determine if this client is equivalent to another object.
178
177
  #
@@ -242,7 +241,11 @@ module Mongo
242
241
  # printed to the mongod logs upon establishing a connection
243
242
  # @option options [ Symbol ] :auth_mech The authentication mechanism to
244
243
  # use. One of :mongodb_cr, :mongodb_x509, :plain, :scram, :scram256
245
- # @option options [ Hash ] :auth_mech_properties
244
+ # @option options [ Hash ] :auth_mech_properties When passed in a
245
+ # connection string as authMechanismProperties, key-value pairs are
246
+ # delimited by commas, so a value containing a comma (",") must not be
247
+ # provided as part of the connection string. Such a value must be passed
248
+ # through this option instead. Values may contain colons (":").
246
249
  # @option options [ String ] :auth_source The source to authenticate from.
247
250
  # @option options [ true | false | nil | Integer ] :bg_error_backtrace
248
251
  # Experimental. Set to true to log complete backtraces for errors in
@@ -265,6 +268,11 @@ module Mongo
265
268
  # @option options [ Float ] :connect_timeout The timeout, in seconds, to
266
269
  # attempt a connection.
267
270
  # @option options [ String ] :database The database to connect to.
271
+ # @option options [ true | false ] :enable_overload_retargeting Whether
272
+ # the driver deprioritizes a server that returns an overload error,
273
+ # reducing the likelihood of retrying on the same overloaded server.
274
+ # This option works with MongoDB Atlas Server Version 9.0 and above.
275
+ # Default: false.
268
276
  # @option options [ Float ] :heartbeat_frequency The interval, in seconds,
269
277
  # for the server monitor to refresh its description via hello.
270
278
  # @option options [ Object ] :id_generator A custom object to generate ids
@@ -277,6 +285,9 @@ module Mongo
277
285
  # @option options [ String ] :log_prefix A custom log prefix to use when
278
286
  # logging. This option is experimental and subject to change in a future
279
287
  # version of the driver.
288
+ # @option options [ Integer ] :max_adaptive_retries The maximum number of
289
+ # retries to attempt when the driver encounters overload errors. This
290
+ # option works with MongoDB Atlas Server Version 9.0 and above. Default: 2.
280
291
  # @option options [ Integer ] :max_connecting The maximum number of
281
292
  # connections that can be connecting simultaneously. The default is 2.
282
293
  # This option should be increased if there are many threads that share
@@ -288,9 +299,13 @@ module Mongo
288
299
  # @option options [ Integer ] :max_pool_size The maximum size of the
289
300
  # connection pool. Setting this option to zero creates an unlimited connection pool.
290
301
  # @option options [ Integer ] :max_read_retries The maximum number of read
291
- # retries when legacy read retries are in use.
302
+ # retries when legacy read retries are in use. Deprecated: this option
303
+ # only affects the legacy retry implementation, which is deprecated and
304
+ # will be removed in a future version.
292
305
  # @option options [ Integer ] :max_write_retries The maximum number of write
293
- # retries when legacy write retries are in use.
306
+ # retries when legacy write retries are in use. Deprecated: this option
307
+ # only affects the legacy retry implementation, which is deprecated and
308
+ # will be removed in a future version.
294
309
  # @option options [ Integer ] :min_pool_size The minimum size of the
295
310
  # connection pool.
296
311
  # @option options [ true, false ] :monitoring If false is given, the
@@ -325,7 +340,9 @@ module Mongo
325
340
  # - *:local_threshold*.
326
341
  # @option options [ Hash ] :read_concern The read concern option.
327
342
  # @option options [ Float ] :read_retry_interval The interval, in seconds,
328
- # in which reads on a mongos are retried.
343
+ # in which reads on a mongos are retried. Deprecated: this option only
344
+ # affects the legacy retry implementation, which is deprecated and will
345
+ # be removed in a future version.
329
346
  # @option options [ Symbol ] :replica_set The name of the replica set to
330
347
  # connect to. Servers not in this replica set will be ignored.
331
348
  # @option options [ true | false ] :retry_reads If true, modern retryable
@@ -496,6 +513,10 @@ module Mongo
496
513
  # (this part of the API is subject to change).
497
514
  # - :encrypted_fields_map => Hash | nil, maps a collection namespace to
498
515
  # a hash describing encrypted fields for queryable encryption.
516
+ # - Note: Supplying an encrypted_fields_map provides more security
517
+ # than relying on an encryptedFields obtained from the server. It
518
+ # protects against a malicious server advertising a false
519
+ # encryptedFields.
499
520
  # - Note: If a collection is present on both the encryptedFieldsMap
500
521
  # and schemaMap, an error will be raised.
501
522
  # - :bypass_query_analysis => Boolean | nil, when true disables automatic
@@ -506,6 +527,10 @@ module Mongo
506
527
  # - :crypt_shared_lib_required => [ Boolean | nil ] Whether
507
528
  # crypt shared library is required. If 'true', an error will be raised
508
529
  # if a crypt_shared library cannot be loaded by libmongocrypt.
530
+ # - :key_expiration_ms => Integer | nil, the lifetime of the data
531
+ # encryption key cache, in milliseconds. Must be a non-negative
532
+ # integer. A value of 0 means the cache never expires. Defaults to
533
+ # 60000.
509
534
  #
510
535
  # Notes on automatic encryption:
511
536
  # - Automatic encryption is an enterprise only feature that only applies
@@ -588,9 +613,7 @@ module Mongo
588
613
  sdam_proc.call(self) if sdam_proc
589
614
 
590
615
  @connect_lock = Mutex.new
591
- @retry_policy = Retryable::RetryPolicy.new(
592
- max_retries: @options[:max_adaptive_retries] || Retryable::Backpressure::DEFAULT_MAX_RETRIES
593
- )
616
+ @retry_policy = build_retry_policy
594
617
  @connect_lock.synchronize do
595
618
  @cluster = Cluster.new(
596
619
  addresses,
@@ -608,7 +631,11 @@ module Mongo
608
631
  build_encrypter
609
632
  end
610
633
  end
611
- rescue StandardError
634
+ # ScriptError is rescued in addition to StandardError because
635
+ # Mongo::Crypt::Binding raises a LoadError when libmongocrypt cannot be
636
+ # found. Without it the cluster built above would be left open, leaking
637
+ # its monitoring and connection pool threads.
638
+ rescue StandardError, ScriptError
612
639
  begin
613
640
  @cluster.close
614
641
  rescue StandardError => e
@@ -782,10 +809,10 @@ module Mongo
782
809
  def with(new_options = nil)
783
810
  clone.tap do |client|
784
811
  opts = client.update_options(new_options || Options::Redacted.new)
785
- Database.create(client)
812
+ client.reset_database!
786
813
  # We can't use the same cluster if some options that would affect it
787
814
  # have changed.
788
- Cluster.create(client, monitoring: opts[:monitoring]) if cluster_modifying?(opts)
815
+ client.reset_cluster!(monitoring: opts[:monitoring]) if cluster_modifying?(opts)
789
816
  end
790
817
  end
791
818
 
@@ -817,6 +844,12 @@ module Mongo
817
844
  options.update(opts)
818
845
  @options = options.freeze
819
846
 
847
+ # The retry policy is built from the options, so a client created by
848
+ # #with needs its own policy when the option changed.
849
+ if @options[:max_adaptive_retries] != old_options[:max_adaptive_retries]
850
+ @retry_policy = build_retry_policy
851
+ end
852
+
820
853
  auto_encryption_options_changed =
821
854
  @options[:auto_encryption_options] != old_options[:auto_encryption_options]
822
855
 
@@ -841,6 +874,31 @@ module Mongo
841
874
  end
842
875
  end
843
876
 
877
+ # Replaces this client's database with a fresh instance built from the
878
+ # client's current options. Used by #with so a reconfigured client does
879
+ # not share its database with the client it was cloned from.
880
+ #
881
+ # @api private
882
+ def reset_database!
883
+ @database = Database.new(self, options[:database], options)
884
+ end
885
+
886
+ # Replaces this client's cluster with a fresh instance built from the
887
+ # client's current options. Used by #with so a reconfigured client does
888
+ # not share its cluster with the client it was cloned from.
889
+ #
890
+ # @param [ Monitoring | nil ] monitoring The monitoring instance to use
891
+ # with the new cluster. If nil, a new instance of Monitoring is created.
892
+ #
893
+ # @api private
894
+ def reset_cluster!(monitoring: nil)
895
+ @cluster = Cluster.new(
896
+ cluster.addresses.map(&:to_s),
897
+ monitoring || Monitoring.new,
898
+ cluster_options
899
+ )
900
+ end
901
+
844
902
  # Get the read concern for this client.
845
903
  #
846
904
  # @example Get the client read concern.
@@ -1159,7 +1217,7 @@ module Mongo
1159
1217
  #
1160
1218
  # @api private
1161
1219
  def with_session(options = {})
1162
- # TODO: Add this back in RUBY-3174.
1220
+ # RUBY-3174 will re-enable this guard; see #assert_not_closed.
1163
1221
  # assert_not_closed
1164
1222
 
1165
1223
  session = get_session(options)
@@ -1222,6 +1280,16 @@ module Mongo
1222
1280
 
1223
1281
  private
1224
1282
 
1283
+ # Builds the retry policy for the backpressure retry loops from the
1284
+ # client's options.
1285
+ #
1286
+ # @return [ Retryable::RetryPolicy ] The retry policy.
1287
+ def build_retry_policy
1288
+ Retryable::RetryPolicy.new(
1289
+ max_retries: @options[:max_adaptive_retries] || Retryable::Backpressure::DEFAULT_MAX_RETRIES
1290
+ )
1291
+ end
1292
+
1225
1293
  # Attempts to parse the given list of addresses, using the provided options.
1226
1294
  #
1227
1295
  # @param [ String | Array<String> ] addresses the list of addresses
@@ -1397,7 +1465,7 @@ module Mongo
1397
1465
  log_warn("Read concern has invalid keys: #{invalid_keys.join(',')}.") unless invalid_keys.empty?
1398
1466
  end
1399
1467
 
1400
- if server_api = opts[:server_api]
1468
+ if (server_api = opts[:server_api])
1401
1469
  raise ArgumentError, ":server_api value must be a hash: #{server_api}" unless server_api.is_a?(Hash)
1402
1470
 
1403
1471
  extra_keys = server_api.keys - %w[version strict deprecation_errors]
@@ -1444,7 +1512,28 @@ module Mongo
1444
1512
  # Validates all options after they are set on the client.
1445
1513
  # This method is intended to catch combinations of options which are
1446
1514
  # not allowed.
1515
+ # Issues a deprecation warning for each legacy retry tuning option that is
1516
+ # explicitly set. These options only affect the legacy retry
1517
+ # implementation, which is deprecated and will be removed in a future
1518
+ # version. Modern retryable reads and writes (enabled by default) ignore
1519
+ # them.
1520
+ def deprecate_legacy_retry_options!
1521
+ %i[max_read_retries read_retry_interval max_write_retries].each do |key|
1522
+ next unless options.key?(key)
1523
+
1524
+ Mongo::Deprecations.warn(
1525
+ "legacy_retry_option_#{key}",
1526
+ "The :#{key} option is deprecated. It only affects the legacy retry " \
1527
+ 'implementation, which is deprecated and will be removed in a future ' \
1528
+ 'version. Modern retryable reads and writes are enabled by default ' \
1529
+ 'and do not use this option.'
1530
+ )
1531
+ end
1532
+ end
1533
+
1447
1534
  def validate_options!(addresses = nil, is_srv: nil)
1535
+ deprecate_legacy_retry_options!
1536
+
1448
1537
  if options[:write] && options[:write_concern] && options[:write] != options[:write_concern]
1449
1538
  raise ArgumentError, "If :write and :write_concern are both given, they must be identical: #{options.inspect}"
1450
1539
  end
@@ -1502,12 +1591,12 @@ module Mongo
1502
1591
  end
1503
1592
 
1504
1593
  %i[connect_timeout socket_timeout].each do |key|
1505
- next unless value = options[key]
1594
+ next unless (value = options[key])
1506
1595
  raise ArgumentError, "#{key} must be a non-negative number: #{value}" unless value.is_a?(Numeric)
1507
1596
  raise ArgumentError, "#{key} must be a non-negative number: #{value}" if value < 0
1508
1597
  end
1509
1598
 
1510
- if value = options[:bg_error_backtrace]
1599
+ if (value = options[:bg_error_backtrace])
1511
1600
  case value
1512
1601
  when Integer
1513
1602
  if value <= 0
@@ -1522,7 +1611,7 @@ module Mongo
1522
1611
  end
1523
1612
  end
1524
1613
 
1525
- if libraries = options[:wrapping_libraries]
1614
+ if (libraries = options[:wrapping_libraries])
1526
1615
  unless libraries.is_a?(Array)
1527
1616
  raise ArgumentError, ":wrapping_libraries must be an array of hashes: #{libraries}"
1528
1617
  end
@@ -1684,7 +1773,7 @@ module Mongo
1684
1773
  "the read preference must be specified as a hash: { mode: #{read.inspect} }")
1685
1774
  end
1686
1775
 
1687
- if mode = read[:mode]
1776
+ if (mode = read[:mode])
1688
1777
  mode = mode.to_sym
1689
1778
  unless Mongo::ServerSelector::PREFERENCES.include?(mode)
1690
1779
  raise Error::InvalidReadOption.new(read, "mode #{mode} is not one of recognized modes")
@@ -1694,6 +1783,17 @@ module Mongo
1694
1783
  true
1695
1784
  end
1696
1785
 
1786
+ # Raises Error::ClientClosed if this client has been closed.
1787
+ #
1788
+ # This is the guard for RUBY-3174 (disallow closed clients from
1789
+ # performing operations). The implementation is complete but not yet
1790
+ # wired in: the intended call site in #with_session is currently
1791
+ # commented out, pending that ticket. Do not delete this method or
1792
+ # Error::ClientClosed as "unused" -- both are here deliberately.
1793
+ #
1794
+ # @raise [ Error::ClientClosed ] if the client has been closed.
1795
+ #
1796
+ # @api private
1697
1797
  def assert_not_closed
1698
1798
  return unless closed?
1699
1799
 
@@ -42,6 +42,10 @@ module Mongo
42
42
  # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds.
43
43
  # Must be a non-negative integer. An explicit value of 0 means infinite.
44
44
  # The default value is unset which means the feature is disabled.
45
+ # @option options [ Integer ] :key_expiration_ms The lifetime of the data
46
+ # encryption key cache, in milliseconds. Must be a non-negative integer.
47
+ # An explicit value of 0 means the cache never expires. The default is
48
+ # 60000.
45
49
  #
46
50
  # @raise [ ArgumentError ] If required options are missing or incorrectly
47
51
  # formatted.
@@ -50,7 +54,9 @@ module Mongo
50
54
  key_vault_client,
51
55
  options[:key_vault_namespace],
52
56
  Crypt::KMS::Credentials.new(options[:kms_providers]),
53
- Crypt::KMS::Validations.validate_tls_options(options[:kms_tls_options])
57
+ Crypt::KMS::Validations.validate_tls_options(options[:kms_tls_options]),
58
+ options[:timeout_ms],
59
+ options[:key_expiration_ms]
54
60
  )
55
61
  end
56
62
 
@@ -100,15 +106,34 @@ module Mongo
100
106
  # encryption key.
101
107
  # @option options [ String ] :algorithm The algorithm used to encrypt the value.
102
108
  # Valid algorithms are "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
103
- # "AEAD_AES_256_CBC_HMAC_SHA_512-Random", "Indexed", "Unindexed".
109
+ # "AEAD_AES_256_CBC_HMAC_SHA_512-Random", "Indexed", "Unindexed", "Range",
110
+ # "String".
104
111
  # @option options [ Integer | nil ] :contention_factor Contention factor
105
- # to be applied if encryption algorithm is set to "Indexed". If not
106
- # provided, it defaults to a value of 0. Contention factor should be set
107
- # only if encryption algorithm is set to "Indexed".
112
+ # to be applied if encryption algorithm is set to "Indexed", "Range", or
113
+ # "String". If not provided, it defaults to a value of 0. Contention factor
114
+ # should be set only if encryption algorithm is set to "Indexed", "Range",
115
+ # or "String".
108
116
  # @option options [ String | nil ] query_type Query type to be applied
109
- # if encryption algorithm is set to "Indexed". Query type should be set
110
- # only if encryption algorithm is set to "Indexed". The only allowed
111
- # value is "equality".
117
+ # if encryption algorithm is set to "Indexed", "Range", or "String".
118
+ # Allowed values are "equality" (for "Indexed"), "range" (for "Range"),
119
+ # and "prefix", "suffix", "substring" (for "String").
120
+ # @option options [ Hash | nil ] :range_opts Specifies index options for a
121
+ # Queryable Encryption field supporting "range" queries. Required when
122
+ # algorithm is "Range". Allowed options are :min, :max, :trim_factor,
123
+ # :sparsity, :precision.
124
+ # @option options [ Hash | nil ] :string_opts Specifies index options for a
125
+ # Queryable Encryption field supporting "prefix", "suffix", or "substring"
126
+ # queries. Required when algorithm is "String". Allowed options are
127
+ # :case_sensitive, :diacritic_sensitive, :prefix, :suffix, :substring.
128
+ #
129
+ # @note The result of explicit encryption with the "Indexed", "Range", or
130
+ # "String" algorithm must be processed by the server to insert or query.
131
+ # To insert or query with such a payload, use a Mongo::Client configured
132
+ # with :auto_encryption_options. The :bypass_query_analysis option may be
133
+ # true; the :bypass_auto_encryption option must be false.
134
+ #
135
+ # @note The "substring" query type is unstable and subject to backwards
136
+ # breaking changes.
112
137
  #
113
138
  # @note The :key_id and :key_alt_name options are mutually exclusive. Only
114
139
  # one is required to perform explicit encryption.
@@ -116,8 +141,8 @@ module Mongo
116
141
  # @return [ BSON::Binary ] A BSON Binary object of subtype 6 (ciphertext)
117
142
  # representing the encrypted value.
118
143
  #
119
- # @raise [ ArgumentError ] if either contention_factor or query_type
120
- # is set, and algorithm is not "Indexed".
144
+ # @raise [ ArgumentError ] if either contention_factor or query_type is set,
145
+ # and algorithm is not "Indexed", "Range", or "String".
121
146
  def encrypt(value, options = {})
122
147
  @encrypter.encrypt(value, options)
123
148
  end
@@ -148,7 +173,7 @@ module Mongo
148
173
  # expression. The only allowed value is "Range"
149
174
  # @option options [ Integer | nil ] :contention_factor Contention factor
150
175
  # to be applied If not provided, it defaults to a value of 0.
151
- # @option options [ String | nil ] query_type Query type to be applied.
176
+ # @option options [ String | nil ] :query_type Query type to be applied.
152
177
  # The only allowed value is "range".
153
178
  #
154
179
  # @note The :key_id and :key_alt_name options are mutually exclusive. Only