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
@@ -101,7 +101,7 @@ module Mongo
101
101
  #
102
102
  # @api private
103
103
  def read_scheduled_kill_specs
104
- while kill_spec = @kill_spec_queue.pop(true)
104
+ while (kill_spec = @kill_spec_queue.pop(true))
105
105
  if @active_cursor_ids.include?(kill_spec.cursor_id)
106
106
  @to_kill[kill_spec.server_address] ||= Set.new
107
107
  @to_kill[kill_spec.server_address] << kill_spec
@@ -188,14 +188,21 @@ module Mongo
188
188
  server_api: server.options[:server_api],
189
189
  connection_global_id: kill_spec.connection_global_id,
190
190
  }
191
- if connection = kill_spec.connection
192
- op.execute_with_connection(connection, context: Operation::Context.new(options: options))
193
- connection.connection_pool.check_in(connection)
191
+ if (connection = kill_spec.connection)
192
+ begin
193
+ op.execute_with_connection(connection, context: Operation::Context.new(options: options))
194
+ rescue Error::SocketError, Error::SocketTimeoutError, Error::ConnectionPerished
195
+ # Connection is dead. do_check_in will detect connection.error?
196
+ # and disconnect it; the pool slot becomes free for a new connection.
197
+ ensure
198
+ connection.unpin(:cursor)
199
+ connection.connection_pool.check_in(connection) unless connection.pinned?
200
+ end
194
201
  else
195
202
  op.execute(server, context: Operation::Context.new(options: options))
196
203
  end
197
204
 
198
- next unless session = kill_spec.session
205
+ next unless (session = kill_spec.session)
199
206
 
200
207
  session.end_session if session.implicit?
201
208
  end
@@ -385,8 +385,8 @@ class Mongo::Cluster
385
385
  def add_servers_from_desc(updated_desc)
386
386
  added_servers = []
387
387
  %w[hosts passives arbiters].each do |m|
388
- updated_desc.send(m).each do |address_str|
389
- if server = cluster.add(address_str, monitor: false)
388
+ updated_desc.public_send(m).each do |address_str|
389
+ if (server = cluster.add(address_str, monitor: false))
390
390
  added_servers << server
391
391
  end
392
392
  end
@@ -402,7 +402,7 @@ class Mongo::Cluster
402
402
  # good primary).
403
403
  def remove_servers_not_in_desc(updated_desc)
404
404
  updated_desc_address_strs = %w[hosts passives arbiters].map do |m|
405
- updated_desc.send(m)
405
+ updated_desc.public_send(m)
406
406
  end.flatten
407
407
  servers_list.each do |server|
408
408
  next if updated_desc_address_strs.include?(address_str = server.address.to_s)
@@ -541,7 +541,7 @@ class Mongo::Cluster
541
541
  end
542
542
 
543
543
  def disconnect_servers
544
- while server = @servers_to_disconnect.shift
544
+ while (server = @servers_to_disconnect.shift)
545
545
  if server.connected?
546
546
  # Do not publish server closed event, as this was already done
547
547
  server.disconnect!
@@ -85,7 +85,7 @@ module Mongo
85
85
  # LST is only read from data-bearing servers
86
86
  if desc.data_bearing?
87
87
  @have_data_bearing_servers = true
88
- break unless timeout = desc.logical_session_timeout
88
+ break unless (timeout = desc.logical_session_timeout)
89
89
 
90
90
  [ timeout, min || timeout ].min
91
91
  else
data/lib/mongo/cluster.rb CHANGED
@@ -269,30 +269,6 @@ module Mongo
269
269
  start_stop_srv_monitor
270
270
  end
271
271
 
272
- # Create a cluster for the provided client, for use when we don't want the
273
- # client's original cluster instance to be the same.
274
- #
275
- # @example Create a cluster for the client.
276
- # Cluster.create(client)
277
- #
278
- # @param [ Client ] client The client to create on.
279
- # @param [ Monitoring | nil ] monitoring. The monitoring instance to use
280
- # with the new cluster. If nil, a new instance of Monitoring will be
281
- # created.
282
- #
283
- # @return [ Cluster ] The cluster.
284
- #
285
- # @since 2.0.0
286
- # @api private
287
- def self.create(client, monitoring: nil)
288
- cluster = Cluster.new(
289
- client.cluster.addresses.map(&:to_s),
290
- monitoring || Monitoring.new,
291
- client.cluster_options
292
- )
293
- client.instance_variable_set(:@cluster, cluster)
294
- end
295
-
296
272
  # @return [ Hash ] The options hash.
297
273
  attr_reader :options
298
274
 
@@ -324,9 +300,7 @@ module Mongo
324
300
  # @api private
325
301
  attr_reader :seeds
326
302
 
327
- # @private
328
- #
329
- # @since 2.5.1
303
+ # @api private
330
304
  attr_reader :session_pool
331
305
 
332
306
  def_delegators :topology, :replica_set?, :replica_set_name, :sharded?,
@@ -612,8 +586,7 @@ module Mongo
612
586
  # @option options [ true | false ] :keep_connection_pool Usually when the
613
587
  # new server description is unknown, the connection pool on the
614
588
  # respective server is cleared. Set this option to true to keep the
615
- # existing connection pool (required when handling not master errors
616
- # on 4.2+ servers).
589
+ # existing connection pool (required when handling not master errors).
617
590
  # @option options [ true | false ] :awaited Whether the updated description
618
591
  # was a result of processing an awaited hello.
619
592
  # @option options [ Object ] :service_id Change state for the specified
@@ -802,7 +775,7 @@ module Mongo
802
775
  #
803
776
  # @since 2.5.0
804
777
  def update_cluster_time(result)
805
- return unless cluster_time_doc = result.cluster_time
778
+ return unless (cluster_time_doc = result.cluster_time)
806
779
 
807
780
  @cluster_time_lock.synchronize do
808
781
  advance_cluster_time(cluster_time_doc)
@@ -983,6 +956,16 @@ module Mongo
983
956
  end
984
957
  end
985
958
 
959
+ # Forces the cluster's periodic executor to run immediately. If the cluster
960
+ # has no periodic executor, this method does nothing.
961
+ #
962
+ # @api private
963
+ def trigger_periodic_executor!
964
+ return unless @periodic_executor
965
+
966
+ @periodic_executor.execute
967
+ end
968
+
986
969
  private
987
970
 
988
971
  # @api private
@@ -62,8 +62,8 @@ module Mongo
62
62
  def maybe_drop_emm_collections(encrypted_fields, client, session)
63
63
  encrypted_fields = if encrypted_fields
64
64
  encrypted_fields
65
- elsif encrypted_fields_map
66
- encrypted_fields_for_drop_from_map
65
+ elsif client.options[:auto_encryption_options]
66
+ encrypted_fields_for_drop
67
67
  else
68
68
  {}
69
69
  end
@@ -127,12 +127,13 @@ module Mongo
127
127
  {}
128
128
  end
129
129
 
130
- # Tries to return the encrypted fields from the {{encrypted_fields_map}}
131
- # value, for the current namespace.
130
+ # Tries to return the encrypted fields for the current namespace from
131
+ # the {{encrypted_fields_map}} value, falling back to the encrypted
132
+ # fields advertised by the server for the collection.
132
133
  #
133
134
  # @return [ Hash | nil ] the encrypted fields, if found
134
- def encrypted_fields_for_drop_from_map
135
- encrypted_fields_map[namespace] ||
135
+ def encrypted_fields_for_drop
136
+ (encrypted_fields_map && encrypted_fields_map[namespace]) ||
136
137
  database.list_collections(filter: { name: name })
137
138
  .first
138
139
  &.fetch(:options, {})
@@ -85,7 +85,7 @@ module Mongo
85
85
  end
86
86
 
87
87
  def server_selector
88
- @view.send(:server_selector)
88
+ @view.server_selector
89
89
  end
90
90
 
91
91
  def aggregate_spec(session, read_preference = nil)
@@ -104,10 +104,8 @@ module Mongo
104
104
 
105
105
  primary_read_preference = { mode: :primary }
106
106
  description = connection.description
107
- if description.primary?
108
- log_warn('Routing the Aggregation operation to the primary server')
109
- primary_read_preference
110
- elsif description.mongos? && !description.features.merge_out_on_secondary_enabled?
107
+ if description.primary? ||
108
+ (description.mongos? && !description.features.merge_out_on_secondary_enabled?)
111
109
  log_warn('Routing the Aggregation operation to the primary server')
112
110
  primary_read_preference
113
111
  else
@@ -105,7 +105,7 @@ module Mongo
105
105
  raise ArgumentError, "Unknown view class: #{view}"
106
106
  end
107
107
  command[:pipeline] = pipeline
108
- if read_concern = view.read_concern
108
+ if (read_concern = view.read_concern) && !read_concern.empty?
109
109
  command[:readConcern] = Options::Mapper.transform_values_to_strings(
110
110
  read_concern
111
111
  )
@@ -117,12 +117,10 @@ module Mongo
117
117
 
118
118
  def batch_size_doc
119
119
  value = options[:batch_size] || view.batch_size
120
- if value == 0 && write?
120
+ if value.nil? || (value == 0 && write?)
121
121
  {}
122
- elsif value
123
- { batchSize: value }
124
122
  else
125
- {}
123
+ { batchSize: value }
126
124
  end
127
125
  end
128
126
  end
@@ -92,7 +92,7 @@ module Mongo
92
92
  private
93
93
 
94
94
  def write?(spec)
95
- return unless out = spec[:selector][:out]
95
+ return unless (out = spec[:selector][:out])
96
96
 
97
97
  out.is_a?(String) ||
98
98
  (out.respond_to?(:keys) && out.keys.first.to_s.downcase != View::MapReduce::INLINE)
@@ -351,19 +351,24 @@ module Mongo
351
351
  # In load balanced topology, manually check out a connection
352
352
  # so it remains checked out and pinned to the cursor.
353
353
  connection = server.pool.check_out(context: context)
354
- result = send_initial_query(connection, context)
355
-
356
- start_at_operation_time = if doc = result.replies.first && result.replies.first.documents.first
357
- doc['operationTime']
358
- else
359
- nil
360
- end
361
- result
354
+ begin
355
+ result = send_initial_query(connection, context)
356
+
357
+ start_at_operation_time = if (doc = result.replies.first && result.replies.first.documents.first)
358
+ doc['operationTime']
359
+ else
360
+ nil
361
+ end
362
+ result
363
+ rescue StandardError
364
+ server.pool.check_in(connection)
365
+ raise
366
+ end
362
367
  else
363
368
  server.with_connection do |connection|
364
369
  result = send_initial_query(connection, context)
365
370
 
366
- start_at_operation_time = if doc = result.replies.first && result.replies.first.documents.first
371
+ start_at_operation_time = if (doc = result.replies.first && result.replies.first.documents.first)
367
372
  doc['operationTime']
368
373
  else
369
374
  # The above may set @start_at_operation_time to nil
@@ -227,7 +227,7 @@ module Mongo
227
227
  #
228
228
  # @since 2.5.0
229
229
  def execute
230
- view.send(:with_session, @options) do |session|
230
+ view.with_session(@options) do |session|
231
231
  write_concern = view.write_concern_with_session(session)
232
232
  context = Operation::Context.new(client: client, session: session)
233
233
  nro_write_with_retry(write_concern, context: context) do |connection, _txn_num, context|
@@ -241,7 +241,7 @@ module Mongo
241
241
  OUT_ACTIONS = %i[replace merge reduce].freeze
242
242
 
243
243
  def server_selector
244
- @view.send(:server_selector)
244
+ @view.server_selector
245
245
  end
246
246
 
247
247
  def inline?
@@ -299,15 +299,6 @@ module Mongo
299
299
  end
300
300
  end
301
301
 
302
- def fetch_query_spec
303
- Builder::MapReduce.new(map_function, reduce_function, view, options).query_specification
304
- end
305
-
306
- def find_command_spec(session)
307
- Builder::MapReduce.new(map_function, reduce_function, view,
308
- options.merge(session: session)).command_specification
309
- end
310
-
311
302
  def fetch_query_op(session)
312
303
  spec = {
313
304
  coll_name: out_collection_name,
@@ -678,7 +678,7 @@ module Mongo
678
678
  # @api private
679
679
  def read_concern
680
680
  if options[:session] && options[:session].in_transaction?
681
- options[:session].send(:txn_read_concern) || collection.client.read_concern
681
+ options[:session].txn_read_concern || collection.client.read_concern
682
682
  else
683
683
  collection.read_concern
684
684
  end
@@ -749,12 +749,10 @@ module Mongo
749
749
  end
750
750
  end
751
751
 
752
- private
753
-
754
- def collation(doc = nil)
755
- configure(:collation, doc)
756
- end
757
-
752
+ # The server selector for this view, derived from its read
753
+ # preference (or the collection/client default).
754
+ #
755
+ # @api private
758
756
  def server_selector
759
757
  @server_selector ||= if options[:session] && options[:session].in_transaction?
760
758
  ServerSelector.get(read_preference || client.server_selector)
@@ -763,6 +761,12 @@ module Mongo
763
761
  end
764
762
  end
765
763
 
764
+ private
765
+
766
+ def collation(doc = nil)
767
+ configure(:collation, doc)
768
+ end
769
+
766
770
  def validate_doc!(doc)
767
771
  raise Error::InvalidDocument.new unless doc.respond_to?(:keys)
768
772
  end
@@ -88,11 +88,6 @@ module Mongo
88
88
  operation_timeouts: operation_timeouts(opts)
89
89
  )
90
90
  write_with_retry(write_concern, context: context) do |connection, txn_num, context|
91
- gte_4_4 = connection.server.description.server_version_gte?('4.4')
92
- if !gte_4_4 && opts[:hint] && write_concern && !write_concern.acknowledged?
93
- raise Error::UnsupportedOption.hint_error(unacknowledged_write: true)
94
- end
95
-
96
91
  Operation::WriteCommand.new(
97
92
  selector: cmd,
98
93
  db_name: database.name,
@@ -216,11 +211,6 @@ module Mongo
216
211
  )
217
212
  value = tracer.trace_operation(operation, context, op_name: 'findOneAndUpdate') do
218
213
  write_with_retry(write_concern, context: context) do |connection, txn_num, context|
219
- gte_4_4 = connection.server.description.server_version_gte?('4.4')
220
- if !gte_4_4 && opts[:hint] && write_concern && !write_concern.acknowledged?
221
- raise Error::UnsupportedOption.hint_error(unacknowledged_write: true)
222
- end
223
-
224
214
  operation.txn_num = txn_num
225
215
  operation.execute_with_connection(connection, context: context)
226
216
  end
@@ -287,11 +277,6 @@ module Mongo
287
277
  )
288
278
  tracer.trace_operation(operation, context, op_name: 'deleteMany') do
289
279
  nro_write_with_retry(write_concern, context: context) do |connection, _txn_num, context|
290
- gte_4_4 = connection.server.description.server_version_gte?('4.4')
291
- if !gte_4_4 && opts[:hint] && write_concern && !write_concern.acknowledged?
292
- raise Error::UnsupportedOption.hint_error(unacknowledged_write: true)
293
- end
294
-
295
280
  operation.execute_with_connection(connection, context: context)
296
281
  end
297
282
  end
@@ -346,11 +331,6 @@ module Mongo
346
331
  operation_timeouts: operation_timeouts(opts)
347
332
  )
348
333
  write_with_retry(write_concern, context: context) do |connection, txn_num, context|
349
- gte_4_4 = connection.server.description.server_version_gte?('4.4')
350
- if !gte_4_4 && opts[:hint] && write_concern && !write_concern.acknowledged?
351
- raise Error::UnsupportedOption.hint_error(unacknowledged_write: true)
352
- end
353
-
354
334
  Operation::Delete.new(
355
335
  deletes: [ delete_doc ],
356
336
  db_name: collection.database.name,
@@ -428,11 +408,6 @@ module Mongo
428
408
  operation_timeouts: operation_timeouts(opts)
429
409
  )
430
410
  write_with_retry(write_concern, context: context) do |connection, txn_num, context|
431
- gte_4_2 = connection.server.description.server_version_gte?('4.2')
432
- if !gte_4_2 && opts[:hint] && write_concern && !write_concern.acknowledged?
433
- raise Error::UnsupportedOption.hint_error(unacknowledged_write: true)
434
- end
435
-
436
411
  Operation::Update.new(
437
412
  updates: [ update_doc ],
438
413
  db_name: collection.database.name,
@@ -507,11 +482,6 @@ module Mongo
507
482
  operation_timeouts: operation_timeouts(opts)
508
483
  )
509
484
  nro_write_with_retry(write_concern, context: context) do |connection, _txn_num, context|
510
- gte_4_2 = connection.server.description.server_version_gte?('4.2')
511
- if !gte_4_2 && opts[:hint] && write_concern && !write_concern.acknowledged?
512
- raise Error::UnsupportedOption.hint_error(unacknowledged_write: true)
513
- end
514
-
515
485
  Operation::Update.new(
516
486
  updates: [ update_doc ],
517
487
  db_name: collection.database.name,
@@ -601,11 +571,6 @@ module Mongo
601
571
  )
602
572
  tracer.trace_operation(operation, context) do
603
573
  write_with_retry(write_concern, context: context) do |connection, txn_num, context|
604
- gte_4_2 = connection.server.description.server_version_gte?('4.2')
605
- if !gte_4_2 && opts[:hint] && write_concern && !write_concern.acknowledged?
606
- raise Error::UnsupportedOption.hint_error(unacknowledged_write: true)
607
- end
608
-
609
574
  operation.txn_num = txn_num
610
575
  operation.execute_with_connection(connection, context: context)
611
576
  end
@@ -624,8 +589,8 @@ module Mongo
624
589
  # @raise [ Error::InvalidUpdateDocument ] if the first key in the
625
590
  # document does not start with a $.
626
591
  def validate_update_documents!(spec)
627
- return unless update = spec.is_a?(Array) ? spec&.first : spec
628
- return unless key = update.keys&.first
592
+ return unless (update = spec.is_a?(Array) ? spec&.first : spec)
593
+ return unless (key = update.keys&.first)
629
594
  return if key.to_s.start_with?('$')
630
595
  raise Error::InvalidUpdateDocument.new(key: key) if Mongo.validate_update_replace
631
596
 
@@ -641,8 +606,8 @@ module Mongo
641
606
  # @raise [ Error::InvalidUpdateDocument ] if the first key in the
642
607
  # document does not start with a $.
643
608
  def validate_replacement_documents!(spec)
644
- return unless replace = spec.is_a?(Array) ? spec&.first : spec
645
- return unless key = replace.keys&.first
609
+ return unless (replace = spec.is_a?(Array) ? spec&.first : spec)
610
+ return unless (key = replace.keys&.first)
646
611
  return unless key.to_s.start_with?('$')
647
612
  raise Error::InvalidReplacementDocument.new(key: key) if Mongo.validate_update_replace
648
613
 
@@ -236,6 +236,14 @@ module Mongo
236
236
  end
237
237
  end
238
238
 
239
+ # Executes the provided block within the context of a session, using
240
+ # this view's options merged with the given ones.
241
+ #
242
+ # @api private
243
+ def with_session(opts = {}, &block)
244
+ client.with_session(@options.merge(opts), &block)
245
+ end
246
+
239
247
  private
240
248
 
241
249
  def initialize_copy(other)
@@ -252,10 +260,6 @@ module Mongo
252
260
  def view
253
261
  self
254
262
  end
255
-
256
- def with_session(opts = {}, &block)
257
- client.with_session(@options.merge(opts), &block)
258
- end
259
263
  end
260
264
  end
261
265
  end
@@ -393,7 +393,7 @@ module Mongo
393
393
  operation = { create: name }.merge(options)
394
394
  operation.delete(:write)
395
395
  operation.delete(:write_concern)
396
- client.send(:with_session, opts) do |session|
396
+ client.with_session(opts) do |session|
397
397
  write_concern = if opts[:write_concern]
398
398
  WriteConcern.get(opts[:write_concern])
399
399
  else
@@ -81,6 +81,9 @@ module Mongo
81
81
  # @option options [ Boolean | nil ] :crypt_shared_lib_required Whether
82
82
  # crypt shared library is required. If 'true', an error will be raised
83
83
  # if a crypt_shared library cannot be loaded by libmongocrypt.
84
+ # @option options [ Integer | nil ] :key_expiration_ms The lifetime of the
85
+ # data encryption key cache, in milliseconds. A value of 0 means the
86
+ # cache never expires. Defaults to 60000.
84
87
  #
85
88
  # @raise [ ArgumentError ] If required options are missing or incorrectly
86
89
  # formatted.
@@ -99,7 +102,8 @@ module Mongo
99
102
  bypass_query_analysis: @options[:bypass_query_analysis],
100
103
  crypt_shared_lib_path: @options[:extra_options][:crypt_shared_lib_path],
101
104
  crypt_shared_lib_required: @options[:extra_options][:crypt_shared_lib_required],
102
- disable_crypt_shared_lib_search: @options[:extra_options][:disable_crypt_shared_lib_search]
105
+ disable_crypt_shared_lib_search: @options[:extra_options][:disable_crypt_shared_lib_search],
106
+ key_expiration_ms: @options[:key_expiration_ms]
103
107
  )
104
108
 
105
109
  @mongocryptd_options = @options[:extra_options].slice(
@@ -287,7 +291,7 @@ module Mongo
287
291
  @internal_client ||= client.with(
288
292
  auto_encryption_options: nil,
289
293
  min_pool_size: 0,
290
- monitoring: client.send(:monitoring)
294
+ monitoring: client.monitoring
291
295
  )
292
296
  end
293
297
  end
@@ -139,14 +139,17 @@ module Mongo
139
139
  # Wraps a String with a mongocrypt_binary_t, yielding an FFI::Pointer
140
140
  # to the wrapped struct.
141
141
  def self.wrap_string(str)
142
- binary_p = Binding.mongocrypt_binary_new_from_data(
143
- FFI::MemoryPointer.from_string(str),
144
- str.bytesize
145
- )
142
+ # mongocrypt_binary_new_from_data does not copy the buffer, so the
143
+ # MemoryPointer must outlive the mongocrypt_binary_t. Hold it in a
144
+ # local so it stays referenced for the whole method frame; otherwise
145
+ # GC can free the buffer while libmongocrypt still points into it.
146
+ data_p = FFI::MemoryPointer.from_string(str)
147
+ binary_p = Binding.mongocrypt_binary_new_from_data(data_p, str.bytesize)
146
148
  begin
147
149
  yield binary_p
148
150
  ensure
149
151
  Binding.mongocrypt_binary_destroy(binary_p)
152
+ data_p # rubocop:disable Lint/Void -- keep the buffer alive past destroy
150
153
  end
151
154
  end
152
155
  end