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
@@ -24,6 +24,7 @@ module Mongo
24
24
  # @since 2.5.2
25
25
  class OpMsg < OpMsgBase
26
26
  include ExecutableTransactionLabel
27
+ include CausalConsistencySupported
27
28
  end
28
29
  end
29
30
  end
@@ -24,6 +24,7 @@ module Mongo
24
24
  # @since 2.5.2
25
25
  class OpMsg < OpMsgBase
26
26
  include ExecutableTransactionLabel
27
+ include CausalConsistencySupported
27
28
 
28
29
  private
29
30
 
@@ -60,7 +60,7 @@ module Mongo
60
60
  end
61
61
  end
62
62
 
63
- if rc = selector[:readConcern]
63
+ if (rc = selector[:readConcern])
64
64
  selector[:readConcern] = Options::Mapper.transform_values_to_strings(rc)
65
65
  end
66
66
 
@@ -47,11 +47,9 @@ module Mongo
47
47
  # @since 2.0.0
48
48
  # @api private
49
49
  def initialize(replies, connection_description, connection_global_id, ids)
50
- @replies = [ *replies ] if replies
51
- @connection_description = connection_description
52
- @connection_global_id = connection_global_id
50
+ super(replies, connection_description, connection_global_id)
53
51
  if replies && replies.first && (doc = replies.first.documents.first)
54
- if errors = doc['writeErrors']
52
+ if (errors = doc['writeErrors'])
55
53
  # some documents were potentially inserted
56
54
  bad_indices = {}
57
55
  errors.map do |error|
@@ -28,6 +28,7 @@ module Mongo
28
28
  include ExecutableNoValidate
29
29
  include ExecutableTransactionLabel
30
30
  include PolymorphicResult
31
+ include CausalConsistencySupported
31
32
 
32
33
  private
33
34
 
@@ -162,15 +162,6 @@ module Mongo
162
162
  !!@replies
163
163
  end
164
164
 
165
- # Whether the result contains cursor_id
166
- #
167
- # @return [ true, false ] If the result contains cursor_id.
168
- #
169
- # @api private
170
- def has_cursor_id?
171
- acknowledged? && replies.last.respond_to?(:cursor_id)
172
- end
173
-
174
165
  # Get the cursor id if the response is acknowledged.
175
166
  #
176
167
  # @note Cursor ids of 0 indicate there is no cursor on the server.
@@ -183,7 +174,17 @@ module Mongo
183
174
  # @since 2.0.0
184
175
  # @api private
185
176
  def cursor_id
186
- acknowledged? ? replies.last.cursor_id : 0
177
+ return 0 unless acknowledged?
178
+
179
+ if replies.last.respond_to?(:cursor_id)
180
+ # Legacy OP_REPLY replies expose the cursor id directly.
181
+ replies.last.cursor_id
182
+ elsif first_document.key?(CURSOR)
183
+ # OP_MSG command replies carry the cursor id in the `cursor` subdocument.
184
+ first_document[CURSOR][CURSOR_ID] || 0
185
+ else
186
+ 0
187
+ end
187
188
  end
188
189
 
189
190
  # Get the namespace of the cursor. The method should be defined in
@@ -450,11 +451,20 @@ module Mongo
450
451
  end
451
452
 
452
453
  def snapshot_timestamp
453
- return unless doc = reply.documents.first
454
+ return unless (doc = reply.documents.first)
454
455
 
455
456
  doc['cursor']&.[]('atClusterTime') || doc['atClusterTime']
456
457
  end
457
458
 
459
+ # Returns the base backoff in milliseconds for a server overload error, if present.
460
+ #
461
+ # @return [ Integer | nil ] The base backoff in milliseconds.
462
+ #
463
+ # @api private
464
+ def base_backoff_ms
465
+ first_document && first_document['baseBackoffMS']
466
+ end
467
+
458
468
  private
459
469
 
460
470
  def operation_failure_class
@@ -465,20 +475,6 @@ module Mongo
465
475
  end
466
476
  end
467
477
 
468
- def aggregate_returned_count
469
- replies.reduce(0) do |n, reply|
470
- n += reply.number_returned
471
- n
472
- end
473
- end
474
-
475
- def aggregate_written_count
476
- documents.reduce(0) do |n, document|
477
- n += document[N] || 0
478
- n
479
- end
480
- end
481
-
482
478
  def parser
483
479
  @parser ||= Error::Parser.new(first_document, replies)
484
480
  end
@@ -28,8 +28,10 @@ module Mongo
28
28
  #
29
29
  # This method overrides the causal consistency addition logic of
30
30
  # SessionsSupported and is meant to be used with operations classified
31
- # as "read operations accepting a read concern", as these are defined
32
- # in the causal consistency spec.
31
+ # as "read and write operations accepting a read concern", as these are
32
+ # defined in the causal consistency spec. For write operations this
33
+ # attaches only afterClusterTime (no read concern level) so that causal
34
+ # ordering is preserved across writes in a causally consistent session.
33
35
  #
34
36
  # In order for the override to work correctly the
35
37
  # CausalConsistencySupported module must be included after
@@ -64,14 +64,6 @@ module Mongo
64
64
  end
65
65
  end
66
66
 
67
- if result.has_cursor_id? &&
68
- connection.description.load_balancer?
69
- if result.cursor_id == 0
70
- connection.unpin
71
- else
72
- connection.pin
73
- end
74
- end
75
67
  process_result(result, connection)
76
68
  end
77
69
  end
@@ -121,7 +113,7 @@ module Mongo
121
113
  # @param [ Mongo::Operation::Context ] context The operation context.
122
114
  def build_message(connection, context)
123
115
  msg = message(connection)
124
- if server_api = context.server_api
116
+ if (server_api = context.server_api)
125
117
  msg = msg.maybe_add_server_api(server_api)
126
118
  end
127
119
  msg
@@ -140,12 +132,7 @@ module Mongo
140
132
  def process_result_for_sdam(result, connection)
141
133
  if (result.not_master? || result.node_recovering?) &&
142
134
  connection.generation >= connection.server.pool.generation(service_id: connection.service_id)
143
- keep_pool = if result.node_shutting_down?
144
- false
145
- else
146
- # Max wire version needs to be examined while the server is known
147
- connection.description.server_version_gte?('4.2')
148
- end
135
+ keep_pool = !result.node_shutting_down?
149
136
 
150
137
  connection.server.unknown!(
151
138
  keep_connection_pool: keep_pool,
@@ -36,7 +36,7 @@ module Mongo
36
36
  return unless @replies
37
37
 
38
38
  @replies.reduce(nil) do |errors, reply|
39
- next unless write_errors = reply.documents.first['writeErrors']
39
+ next unless (write_errors = reply.documents.first['writeErrors'])
40
40
 
41
41
  wes = write_errors.collect do |we|
42
42
  we.merge!('index' => count + we['index'])
@@ -59,7 +59,7 @@ module Mongo
59
59
  return unless @replies
60
60
 
61
61
  @replies.each_with_index.reduce(nil) do |errors, (reply, _)|
62
- next unless write_concern_errors = reply.documents.first['writeConcernErrors']
62
+ next unless (write_concern_errors = reply.documents.first['writeConcernErrors'])
63
63
 
64
64
  (errors || []) << write_concern_errors.reduce(nil) do |errs, wce|
65
65
  wce.merge!('index' => count + wce['index'])
@@ -61,7 +61,7 @@ module Mongo
61
61
  def apply_causal_consistency_if_possible(selector, connection)
62
62
  return if connection.description.standalone?
63
63
 
64
- cc_doc = session.send(:causal_consistency_doc)
64
+ cc_doc = session.causal_consistency_doc
65
65
  return unless cc_doc
66
66
 
67
67
  rc_doc = (selector[:readConcern] || read_concern || {}).merge(cc_doc)
@@ -165,9 +165,7 @@ module Mongo
165
165
  read_doc = if connection.description.standalone?
166
166
  # Read preference is never sent to standalones.
167
167
  nil
168
- elsif connection.server.load_balancer?
169
- read&.to_mongos
170
- elsif connection.description.mongos?
168
+ elsif connection.server.load_balancer? || connection.description.mongos?
171
169
  # When server is a mongos:
172
170
  # - $readPreference is never sent when mode is 'primary'
173
171
  # - Otherwise $readPreference is sent
@@ -243,7 +241,7 @@ module Mongo
243
241
  end
244
242
 
245
243
  super.tap do |message|
246
- if session = context.session
244
+ if (session = context.session)
247
245
  # Serialize the message to detect client-side problems,
248
246
  # such as invalid BSON keys or too large messages.
249
247
  # The message will be serialized again
@@ -107,7 +107,7 @@ module Mongo
107
107
  return [] unless acknowledged?
108
108
 
109
109
  @replies.reduce([]) do |ids, reply|
110
- if upserted_ids = reply.documents.first[UPSERTED]
110
+ if (upserted_ids = reply.documents.first[UPSERTED])
111
111
  ids += upserted_ids
112
112
  end
113
113
  ids
@@ -27,6 +27,7 @@ module Mongo
27
27
  include ExecutableNoValidate
28
28
  include ExecutableTransactionLabel
29
29
  include PolymorphicResult
30
+ include CausalConsistencySupported
30
31
 
31
32
  private
32
33
 
@@ -21,6 +21,8 @@ module Mongo
21
21
  #
22
22
  # @api private
23
23
  class OpMsg < OpMsgBase
24
+ include CausalConsistencySupported
25
+
24
26
  private
25
27
 
26
28
  def selector(connection)
@@ -38,6 +38,7 @@ require 'mongo/operation/drop'
38
38
  require 'mongo/operation/drop_database'
39
39
  require 'mongo/operation/get_more'
40
40
  require 'mongo/operation/find'
41
+ require 'mongo/operation/cursor_command'
41
42
  require 'mongo/operation/explain'
42
43
  require 'mongo/operation/kill_cursors'
43
44
  require 'mongo/operation/indexes'
@@ -143,14 +143,14 @@ module Mongo
143
143
 
144
144
  def redacted_string(method)
145
145
  '{' + reduce([]) do |list, (k, v)|
146
- list << "#{k.send(method)}=>#{redact(k, v, method)}"
146
+ list << "#{k.public_send(method)}=>#{redact(k, v, method)}"
147
147
  end.join(', ') + '}'
148
148
  end
149
149
 
150
150
  def redact(k, v, method)
151
151
  return STRING_REPLACEMENT if SENSITIVE_OPTIONS.include?(k.to_sym)
152
152
 
153
- v.send(method)
153
+ v.public_send(method)
154
154
  end
155
155
  end
156
156
  end
@@ -74,12 +74,15 @@ module Mongo
74
74
  #
75
75
  # @since 2.5.0
76
76
  def initialize(message, compressor, zlib_compression_level = nil)
77
+ super()
77
78
  @original_message = message
78
79
  @original_op_code = message.op_code
79
80
  @uncompressed_size = 0
80
81
  @compressor_id = COMPRESSOR_ID_MAP[compressor]
81
82
  @compressed_message = ''
82
83
  @zlib_compression_level = zlib_compression_level if zlib_compression_level && zlib_compression_level != -1
84
+ # A compressed message must reuse the wrapped message's request id,
85
+ # overriding the fresh id assigned by the superclass initializer.
83
86
  @request_id = message.request_id
84
87
  end
85
88
 
@@ -93,11 +96,11 @@ module Mongo
93
96
  message = Registry.get(@original_op_code).allocate
94
97
  buf = decompress(@compressed_message)
95
98
 
96
- message.send(:fields).each do |field|
99
+ message.fields.each do |field|
97
100
  if field[:multi]
98
- Message.deserialize_array(message, buf, field)
101
+ message.deserialize_array(buf, field)
99
102
  else
100
- Message.deserialize_field(message, buf, field)
103
+ message.deserialize_field(buf, field)
101
104
  end
102
105
  end
103
106
  message.fix_after_deserialization if message.is_a?(Msg)
@@ -116,6 +119,15 @@ module Mongo
116
119
  @original_message.replyable?
117
120
  end
118
121
 
122
+ # @api private
123
+ def serialize_fields(buffer, max_bson_size)
124
+ buf = BSON::ByteBuffer.new
125
+ @original_message.serialize_fields(buf, max_bson_size)
126
+ @uncompressed_size = buf.length
127
+ @compressed_message = compress(buf)
128
+ super
129
+ end
130
+
119
131
  private
120
132
 
121
133
  # The operation code for a +Compressed+ message.
@@ -140,14 +152,6 @@ module Mongo
140
152
  # @return [ String ] The actual compressed message bytes.
141
153
  field :compressed_message, Bytes
142
154
 
143
- def serialize_fields(buffer, max_bson_size)
144
- buf = BSON::ByteBuffer.new
145
- @original_message.send(:serialize_fields, buf, max_bson_size)
146
- @uncompressed_size = buf.length
147
- @compressed_message = compress(buf)
148
- super
149
- end
150
-
151
155
  def compress(buffer)
152
156
  if @compressor_id == NOOP_BYTE
153
157
  buffer.to_s.force_encoding(BSON::BINARY)
@@ -148,10 +148,6 @@ module Mongo
148
148
  # @return [ Mongo::Protocol::Msg ] The decrypted message, or the original
149
149
  # message if decryption was not possible or necessary.
150
150
  def maybe_decrypt(_context)
151
- # TODO: determine if we should be decrypting data coming from pre-4.2
152
- # servers, potentially using legacy wire protocols. If so we need
153
- # to implement decryption for those wire protocols as our current
154
- # encryption/decryption code is OP_MSG-specific.
155
151
  self
156
152
  end
157
153
 
@@ -268,11 +264,11 @@ module Mongo
268
264
  buf = BSON::ByteBuffer.new(chunk)
269
265
 
270
266
  message = Registry.get(_op_code).allocate
271
- message.send(:fields).each do |field|
267
+ message.fields.each do |field|
272
268
  if field[:multi]
273
- deserialize_array(message, buf, field, options)
269
+ message.deserialize_array(buf, field, options)
274
270
  else
275
- deserialize_field(message, buf, field, options)
271
+ message.deserialize_field(buf, field, options)
276
272
  end
277
273
  end
278
274
  message.fix_after_deserialization if message.is_a?(Msg)
@@ -369,7 +365,6 @@ module Mongo
369
365
  # deserialized field specified in the class by the field dsl under
370
366
  # the key +:multi+
371
367
  #
372
- # @param message [Message] Message to contain the deserialized array.
373
368
  # @param io [IO] Stream containing the array to deserialize.
374
369
  # @param field [Hash] Hash representing a field.
375
370
  # @param options [ Hash ]
@@ -377,18 +372,17 @@ module Mongo
377
372
  # @option options [ Boolean ] :deserialize_as_bson Whether to deserialize
378
373
  # each of the elements in this array using BSON types wherever possible.
379
374
  #
380
- # @return [Message] Message with deserialized array.
375
+ # @return [Array] The deserialized array.
381
376
  # @api private
382
- def self.deserialize_array(message, io, field, options = {})
377
+ def deserialize_array(io, field, options = {})
383
378
  elements = []
384
- count = message.instance_variable_get(field[:multi])
379
+ count = instance_variable_get(field[:multi])
385
380
  count.times { elements << field[:type].deserialize(io, options) }
386
- message.instance_variable_set(field[:name], elements)
381
+ instance_variable_set(field[:name], elements)
387
382
  end
388
383
 
389
- # Deserializes a single field in a message
384
+ # Deserializes a single field into this message.
390
385
  #
391
- # @param message [Message] Message to contain the deserialized field.
392
386
  # @param io [IO] Stream containing the field to deserialize.
393
387
  # @param field [Hash] Hash representing a field.
394
388
  # @param options [ Hash ]
@@ -396,20 +390,19 @@ module Mongo
396
390
  # @option options [ Boolean ] :deserialize_as_bson Whether to deserialize
397
391
  # this field using BSON types wherever possible.
398
392
  #
399
- # @return [Message] Message with deserialized field.
393
+ # @return [Object] The deserialized field value.
400
394
  # @api private
401
- def self.deserialize_field(message, io, field, options = {})
402
- message.instance_variable_set(
395
+ def deserialize_field(io, field, options = {})
396
+ instance_variable_set(
403
397
  field[:name],
404
398
  field[:type].deserialize(io, options)
405
399
  )
406
400
  end
407
401
 
408
- private
409
-
410
402
  # A method for getting the fields for a message class
411
403
  #
412
- # @return [Integer] the fields for the message class
404
+ # @return [Array<Hash>] The fields for the message class
405
+ # @api private
413
406
  def fields
414
407
  self.class.fields
415
408
  end
@@ -418,6 +411,7 @@ module Mongo
418
411
  #
419
412
  # @param buffer [String] buffer to receive the field
420
413
  # @return [String] buffer with serialized field
414
+ # @api private
421
415
  def serialize_fields(buffer, max_bson_size = nil)
422
416
  fields.each do |field|
423
417
  value = instance_variable_get(field[:name])
@@ -437,6 +431,8 @@ module Mongo
437
431
  end
438
432
  end
439
433
 
434
+ private
435
+
440
436
  # Serializes the header of the message consisting of 4 32bit integers
441
437
  #
442
438
  # The integers represent a message length placeholder (calculation of
@@ -201,23 +201,12 @@ module Mongo
201
201
  # represents one of the command types allow-listed by libmongocrypt and it
202
202
  # contains data that is required to be encrypted by a local or remote json schema.
203
203
  #
204
- # @param [ Mongo::Server::Connection ] connection The connection on which
205
- # the operation is performed.
206
204
  # @param [ Mongo::Operation::Context ] context The operation context.
207
205
  #
208
206
  # @return [ Mongo::Protocol::Msg ] The encrypted message, or the original
209
207
  # message if encryption was not possible or necessary.
210
- def maybe_encrypt(connection, context)
208
+ def maybe_encrypt(_connection, context)
211
209
  if context.encrypt?
212
- if connection.description.max_wire_version < 8
213
- raise Error::CryptError.new(
214
- 'Cannot perform encryption against a MongoDB server older than ' +
215
- '4.2 (wire version less than 8). Currently connected to server ' +
216
- "with max wire version #{connection.description.max_wire_version}} " +
217
- '(Auto-encryption requires a minimum MongoDB version of 4.2)'
218
- )
219
- end
220
-
221
210
  db_name = @main_document[DATABASE_IDENTIFIER]
222
211
  cmd = merge_sections
223
212
  enc_cmd = context.encrypt(db_name, cmd)
@@ -36,7 +36,7 @@ module Mongo
36
36
  #
37
37
  # @since 2.5.0
38
38
  def get(op_code, _message = nil)
39
- if type = MAPPINGS[op_code]
39
+ if (type = MAPPINGS[op_code])
40
40
  type
41
41
  else
42
42
  handle_unsupported_op_code!(op_code)
@@ -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)
@@ -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
- if limit && (caching_cursor_limit.nil? || caching_cursor_limit >= limit)
203
- caching_cursor
204
- elsif limit.nil? && caching_cursor_limit.nil?
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)
@@ -23,9 +23,24 @@ module Mongo
23
23
  # a random value. Can be injected for deterministic testing.
24
24
  #
25
25
  # @return [ Float ] The backoff delay in seconds.
26
- def self.backoff_delay(attempt, jitter: rand)
27
- jitter * [ MAX_BACKOFF, BASE_BACKOFF * (2**(attempt - 1)) ].min
26
+ def self.backoff_delay(attempt, jitter: rand, err: nil)
27
+ jitter * [ MAX_BACKOFF, base_backoff(err) * (2**attempt) ].min
28
28
  end
29
+
30
+ def self.base_backoff(err)
31
+ return BASE_BACKOFF if err.nil?
32
+ return BASE_BACKOFF unless err.respond_to?(:result) && err.result.respond_to?(:base_backoff_ms)
33
+
34
+ base_backoff_ms = err.result.base_backoff_ms
35
+
36
+ if base_backoff_ms && base_backoff_ms > 0
37
+ err.result.base_backoff_ms / 1000.0
38
+ else
39
+ BASE_BACKOFF
40
+ end
41
+ end
42
+
43
+ private_class_method :base_backoff
29
44
  end
30
45
  end
31
46
  end
@@ -340,7 +340,7 @@ module Mongo
340
340
  def overload_read_retry(last_error, session, server_selector, context, failed_server, error_count:)
341
341
  last_was_overload = true
342
342
  loop do
343
- delay = last_was_overload ? retry_policy.backoff_delay(error_count) : 0
343
+ delay = last_was_overload ? retry_policy.backoff_delay(error_count, err: last_error) : 0
344
344
  raise last_error unless retry_policy.should_retry_overload?(error_count, delay, context: context)
345
345
 
346
346
  log_retry(last_error, message: 'Read retry (overload backoff)')
@@ -27,8 +27,8 @@ module Mongo
27
27
  # @param [ Float ] jitter A random float in [0.0, 1.0).
28
28
  #
29
29
  # @return [ Float ] The backoff delay in seconds.
30
- def backoff_delay(attempt, jitter: rand)
31
- Backpressure.backoff_delay(attempt, jitter: jitter)
30
+ def backoff_delay(attempt, jitter: rand, err: nil)
31
+ Backpressure.backoff_delay(attempt, jitter: jitter, err: err)
32
32
  end
33
33
 
34
34
  # Determine whether an overload retry should be attempted.
@@ -123,7 +123,7 @@ module Mongo
123
123
  unless e.respond_to?(:label?) && e.label?('NoWritesPerformed')
124
124
  error_to_raise = e
125
125
  end
126
- delay = retry_policy.backoff_delay(error_count)
126
+ delay = retry_policy.backoff_delay(error_count, err: e)
127
127
  raise error_to_raise unless retry_policy.should_retry_overload?(error_count, delay, context: context)
128
128
 
129
129
  log_retry(e, message: 'Write retry (overload backoff)')
@@ -386,7 +386,7 @@ module Mongo
386
386
  last_was_overload = true
387
387
 
388
388
  loop do
389
- delay = last_was_overload ? retry_policy.backoff_delay(error_count) : 0
389
+ delay = last_was_overload ? retry_policy.backoff_delay(error_count, err: last_error) : 0
390
390
  raise error_to_raise unless retry_policy.should_retry_overload?(error_count, delay, context: context)
391
391
 
392
392
  log_retry(last_error, message: 'Write retry (overload backoff)')
@@ -129,7 +129,7 @@ module Mongo
129
129
 
130
130
  error_count += 1
131
131
  policy = client.retry_policy
132
- delay = policy.backoff_delay(error_count)
132
+ delay = policy.backoff_delay(error_count, err: e)
133
133
  raise e unless policy.should_retry_overload?(error_count, delay, context: context)
134
134
 
135
135
  Logger.logger.warn("Overload retry due to: #{e.class.name}: #{e.message}")
@@ -130,7 +130,7 @@ module Mongo
130
130
  doc[:driver] = driver_doc
131
131
  doc[:os] = os_doc
132
132
  doc[:platform] = platform_string
133
- doc[:backpressure] = true
133
+ doc[:backpressure] = '2'
134
134
  env_doc.tap { |env| doc[:env] = env if env }
135
135
  end
136
136
  end
@@ -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
@@ -125,7 +125,7 @@ module Mongo
125
125
 
126
126
  # Close all fds scheduled for closing.
127
127
  def close_all_scheduled
128
- while pipe = @scheduled_for_close.pop
128
+ while (pipe = @scheduled_for_close.pop)
129
129
  pipe.close
130
130
  end
131
131
  end