mongo 2.24.1 → 2.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) 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/result_combiner.rb +3 -3
  13. data/lib/mongo/bulk_write.rb +1 -1
  14. data/lib/mongo/client.rb +82 -12
  15. data/lib/mongo/cluster/reapers/cursor_reaper.rb +12 -5
  16. data/lib/mongo/cluster/sdam_flow.rb +4 -4
  17. data/lib/mongo/cluster/topology/base.rb +1 -1
  18. data/lib/mongo/cluster.rb +12 -28
  19. data/lib/mongo/collection/view/aggregation/behavior.rb +1 -1
  20. data/lib/mongo/collection/view/aggregation.rb +2 -4
  21. data/lib/mongo/collection/view/builder/aggregation.rb +3 -5
  22. data/lib/mongo/collection/view/builder/map_reduce.rb +1 -1
  23. data/lib/mongo/collection/view/change_stream.rb +14 -9
  24. data/lib/mongo/collection/view/map_reduce.rb +2 -11
  25. data/lib/mongo/collection/view/readable.rb +11 -7
  26. data/lib/mongo/collection/view/writable.rb +4 -4
  27. data/lib/mongo/collection/view.rb +8 -4
  28. data/lib/mongo/collection.rb +1 -1
  29. data/lib/mongo/crypt/auto_encrypter.rb +1 -1
  30. data/lib/mongo/crypt/binary.rb +7 -4
  31. data/lib/mongo/crypt/binding.rb +88 -10
  32. data/lib/mongo/crypt/context.rb +50 -19
  33. data/lib/mongo/crypt/encryption_io.rb +11 -5
  34. data/lib/mongo/crypt/handle.rb +17 -5
  35. data/lib/mongo/crypt/kms/credentials.rb +70 -31
  36. data/lib/mongo/crypt/kms/kmip/master_document.rb +30 -1
  37. data/lib/mongo/crypt/kms/master_key_document.rb +11 -6
  38. data/lib/mongo/crypt/kms.rb +12 -1
  39. data/lib/mongo/cursor.rb +8 -2
  40. data/lib/mongo/database/cursor_command_view.rb +95 -0
  41. data/lib/mongo/database.rb +144 -18
  42. data/lib/mongo/error/invalid_uri.rb +5 -1
  43. data/lib/mongo/error/operation_failure.rb +2 -2
  44. data/lib/mongo/error/parser.rb +3 -3
  45. data/lib/mongo/error/sdam_error_detection.rb +2 -3
  46. data/lib/mongo/grid/fs_bucket.rb +27 -21
  47. data/lib/mongo/grid/stream/write.rb +2 -2
  48. data/lib/mongo/index/view.rb +13 -7
  49. data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +1 -0
  50. data/lib/mongo/monitoring/event/cmap/connection_check_out_started.rb +1 -0
  51. data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +1 -0
  52. data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +1 -0
  53. data/lib/mongo/monitoring/event/cmap/connection_closed.rb +1 -0
  54. data/lib/mongo/monitoring/event/cmap/connection_created.rb +1 -0
  55. data/lib/mongo/monitoring/event/cmap/connection_ready.rb +1 -0
  56. data/lib/mongo/monitoring/event/cmap/pool_cleared.rb +1 -0
  57. data/lib/mongo/monitoring/event/cmap/pool_closed.rb +1 -0
  58. data/lib/mongo/monitoring/event/cmap/pool_created.rb +1 -0
  59. data/lib/mongo/monitoring/event/cmap/pool_ready.rb +1 -0
  60. data/lib/mongo/monitoring/event/command_failed.rb +1 -0
  61. data/lib/mongo/monitoring/event/command_started.rb +1 -0
  62. data/lib/mongo/monitoring/event/command_succeeded.rb +1 -0
  63. data/lib/mongo/monitoring/event/secure.rb +12 -17
  64. data/lib/mongo/monitoring/event/server_closed.rb +1 -0
  65. data/lib/mongo/monitoring/event/server_description_changed.rb +1 -0
  66. data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +1 -0
  67. data/lib/mongo/monitoring/event/server_heartbeat_started.rb +1 -0
  68. data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +1 -0
  69. data/lib/mongo/monitoring/event/server_opening.rb +1 -0
  70. data/lib/mongo/monitoring/event/topology_changed.rb +1 -0
  71. data/lib/mongo/monitoring/event/topology_closed.rb +1 -0
  72. data/lib/mongo/monitoring/event/topology_opening.rb +1 -0
  73. data/lib/mongo/operation/create/op_msg.rb +1 -0
  74. data/lib/mongo/operation/create_index/op_msg.rb +2 -1
  75. data/lib/mongo/operation/cursor_command/op_msg.rb +37 -0
  76. data/lib/mongo/operation/cursor_command/result.rb +60 -0
  77. data/lib/mongo/operation/cursor_command.rb +33 -0
  78. data/lib/mongo/operation/delete/op_msg.rb +1 -0
  79. data/lib/mongo/operation/drop/op_msg.rb +1 -0
  80. data/lib/mongo/operation/drop_database/op_msg.rb +1 -0
  81. data/lib/mongo/operation/drop_index/op_msg.rb +1 -0
  82. data/lib/mongo/operation/find/builder/command.rb +1 -1
  83. data/lib/mongo/operation/insert/bulk_result.rb +2 -4
  84. data/lib/mongo/operation/insert/op_msg.rb +1 -0
  85. data/lib/mongo/operation/result.rb +12 -25
  86. data/lib/mongo/operation/shared/causal_consistency_supported.rb +4 -2
  87. data/lib/mongo/operation/shared/executable.rb +1 -9
  88. data/lib/mongo/operation/shared/result/aggregatable.rb +2 -2
  89. data/lib/mongo/operation/shared/sessions_supported.rb +3 -5
  90. data/lib/mongo/operation/update/bulk_result.rb +1 -1
  91. data/lib/mongo/operation/update/op_msg.rb +1 -0
  92. data/lib/mongo/operation/write_command/op_msg.rb +2 -0
  93. data/lib/mongo/operation.rb +1 -0
  94. data/lib/mongo/options/redacted.rb +2 -2
  95. data/lib/mongo/protocol/compressed.rb +15 -11
  96. data/lib/mongo/protocol/message.rb +16 -16
  97. data/lib/mongo/protocol/registry.rb +1 -1
  98. data/lib/mongo/protocol/serializers.rb +1 -3
  99. data/lib/mongo/query_cache.rb +3 -5
  100. data/lib/mongo/server/connection.rb +3 -1
  101. data/lib/mongo/server/connection_common.rb +9 -1
  102. data/lib/mongo/server/connection_pool/generation_manager.rb +1 -1
  103. data/lib/mongo/server/connection_pool.rb +24 -10
  104. data/lib/mongo/server/description/features.rb +1 -1
  105. data/lib/mongo/server/description.rb +1 -1
  106. data/lib/mongo/server/monitor/connection.rb +3 -2
  107. data/lib/mongo/server/monitor.rb +133 -30
  108. data/lib/mongo/server/pending_connection.rb +1 -0
  109. data/lib/mongo/server/push_monitor.rb +16 -2
  110. data/lib/mongo/server.rb +27 -7
  111. data/lib/mongo/server_selector/base.rb +3 -6
  112. data/lib/mongo/server_selector/secondary_preferred.rb +4 -1
  113. data/lib/mongo/session.rb +22 -23
  114. data/lib/mongo/socket/ssl.rb +3 -3
  115. data/lib/mongo/socket.rb +1 -7
  116. data/lib/mongo/srv/monitor.rb +11 -2
  117. data/lib/mongo/srv/resolver.rb +1 -1
  118. data/lib/mongo/srv/result.rb +5 -3
  119. data/lib/mongo/timeout.rb +10 -15
  120. data/lib/mongo/tracing/open_telemetry/command_tracer.rb +16 -2
  121. data/lib/mongo/uri/options_mapper.rb +21 -9
  122. data/lib/mongo/uri/srv_protocol.rb +4 -4
  123. data/lib/mongo/uri.rb +56 -8
  124. data/lib/mongo/utils.rb +1 -1
  125. data/lib/mongo/version.rb +1 -1
  126. metadata +6 -4
  127. data/lib/mongo/auth/stringprep/unicode_normalize/normalize.rb +0 -173
  128. data/lib/mongo/auth/stringprep/unicode_normalize/tables.rb +0 -1164
@@ -41,6 +41,7 @@ module Mongo
41
41
  # @since 2.9.0
42
42
  # @api private
43
43
  def initialize(address, options, pool)
44
+ super()
44
45
  @address = address
45
46
  @options = options.dup.freeze
46
47
  @pool = pool
@@ -83,6 +83,7 @@ module Mongo
83
83
  def initialize(command_name, database_name, address,
84
84
  request_id, operation_id, message, failure, duration,
85
85
  started_event:, server_connection_id: nil, service_id: nil)
86
+ super()
86
87
  @command_name = command_name.to_s
87
88
  @database_name = database_name
88
89
  @address = address
@@ -84,6 +84,7 @@ module Mongo
84
84
  operation_id, command, socket_object_id: nil, connection_id: nil,
85
85
  connection_generation: nil, server_connection_id: nil,
86
86
  service_id: nil)
87
+ super()
87
88
  @command_name = command_name.to_s
88
89
  @database_name = database_name
89
90
  @address = address
@@ -76,6 +76,7 @@ module Mongo
76
76
  def initialize(command_name, database_name, address, request_id,
77
77
  operation_id, reply, duration, started_event:,
78
78
  server_connection_id: nil, service_id: nil)
79
+ super()
79
80
  @command_name = command_name.to_s
80
81
  @database_name = database_name
81
82
  @address = address
@@ -47,18 +47,14 @@ module Mongo
47
47
  #
48
48
  # @return [ true | false ] Whether the command is sensitive.
49
49
  def sensitive?(command_name:, document:)
50
- if REDACTED_COMMANDS.include?(command_name.to_s)
51
- true
52
- elsif %w[hello ismaster isMaster].include?(command_name.to_s) &&
53
- document['speculativeAuthenticate']
54
- # According to Command Monitoring spec, for hello/legacy hello commands
55
- # when speculativeAuthenticate is present, their commands AND replies
56
- # MUST be redacted from the events.
57
- # See https://github.com/mongodb/specifications/blob/master/source/command-logging-and-monitoring/command-logging-and-monitoring.md#security
58
- true
59
- else
60
- false
61
- end
50
+ return true if REDACTED_COMMANDS.include?(command_name.to_s)
51
+
52
+ # According to Command Monitoring spec, for hello/legacy hello commands
53
+ # when speculativeAuthenticate is present, their commands AND replies
54
+ # MUST be redacted from the events.
55
+ # See https://github.com/mongodb/specifications/blob/master/source/command-logging-and-monitoring/command-logging-and-monitoring.md#security
56
+ %w[hello ismaster isMaster].include?(command_name.to_s) &&
57
+ !!document['speculativeAuthenticate']
62
58
  end
63
59
 
64
60
  # Redact secure information from the document if:
@@ -77,11 +73,10 @@ module Mongo
77
73
  #
78
74
  # @since 2.1.0
79
75
  def redacted(command_name, document)
80
- if %w[1 true yes].include?(ENV['MONGO_RUBY_DRIVER_UNREDACT_EVENTS']&.downcase)
81
- document
82
- elsif respond_to?(:started_event) && started_event.sensitive
83
- BSON::Document.new
84
- elsif sensitive?(command_name: command_name, document: document)
76
+ return document if %w[1 true yes].include?(ENV['MONGO_RUBY_DRIVER_UNREDACT_EVENTS']&.downcase)
77
+
78
+ if (respond_to?(:started_event) && started_event.sensitive) ||
79
+ sensitive?(command_name: command_name, document: document)
85
80
  BSON::Document.new
86
81
  else
87
82
  document
@@ -37,6 +37,7 @@ module Mongo
37
37
  #
38
38
  # @since 2.4.0
39
39
  def initialize(address, topology)
40
+ super()
40
41
  @address = address
41
42
  @topology = topology
42
43
  end
@@ -58,6 +58,7 @@ module Mongo
58
58
  # @api private
59
59
  def initialize(address, topology, previous_description, new_description,
60
60
  awaited: false)
61
+ super()
61
62
  @address = address
62
63
  @topology = topology
63
64
  @previous_description = previous_description
@@ -35,6 +35,7 @@ module Mongo
35
35
  # @since 2.7.0
36
36
  # @api private
37
37
  def initialize(address, round_trip_time, error, started_event:, awaited: false)
38
+ super()
38
39
  @address = address
39
40
  @round_trip_time = round_trip_time
40
41
  @error = error
@@ -40,6 +40,7 @@ module Mongo
40
40
  # @since 2.7.0
41
41
  # @api private
42
42
  def initialize(address, awaited: false)
43
+ super()
43
44
  @address = address
44
45
  @awaited = !!awaited
45
46
  end
@@ -35,6 +35,7 @@ module Mongo
35
35
  # @since 2.7.0
36
36
  # @api private
37
37
  def initialize(address, round_trip_time, started_event:, awaited: false)
38
+ super()
38
39
  @address = address
39
40
  @round_trip_time = round_trip_time
40
41
  @awaited = !!awaited
@@ -37,6 +37,7 @@ module Mongo
37
37
  #
38
38
  # @since 2.4.0
39
39
  def initialize(address, topology)
40
+ super()
40
41
  @address = address
41
42
  @topology = topology
42
43
  end
@@ -37,6 +37,7 @@ module Mongo
37
37
  #
38
38
  # @since 2.4.0
39
39
  def initialize(previous_topology, new_topology)
40
+ super()
40
41
  @previous_topology = previous_topology
41
42
  @new_topology = new_topology
42
43
  end
@@ -33,6 +33,7 @@ module Mongo
33
33
  #
34
34
  # @since 2.4.0
35
35
  def initialize(topology)
36
+ super()
36
37
  @topology = topology
37
38
  end
38
39
 
@@ -33,6 +33,7 @@ module Mongo
33
33
  #
34
34
  # @since 2.4.0
35
35
  def initialize(topology)
36
+ super()
36
37
  @topology = topology
37
38
  end
38
39
 
@@ -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
 
@@ -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
 
@@ -33,7 +34,7 @@ module Mongo
33
34
  indexes: indexes,
34
35
  comment: spec[:comment],
35
36
  }.compact.tap do |selector|
36
- if commit_quorum = spec[:commit_quorum]
37
+ if (commit_quorum = spec[:commit_quorum])
37
38
  # There was a bug on server versions 4.2.0 - 4.2.5 where
38
39
  # the server would accept the commitQuorum option and use it internally
39
40
  # (see SERVER-47193). As a result, the drivers specifications require
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2025 MongoDB Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module Mongo
18
+ module Operation
19
+ class CursorCommand
20
+ # A cursor command operation sent as an op message.
21
+ #
22
+ # @api private
23
+ class OpMsg < OpMsgBase
24
+ include PolymorphicResult
25
+
26
+ private
27
+
28
+ # The user's command is sent verbatim. The driver MUST NOT inspect or
29
+ # modify it; $db, lsid and other internal fields are attached by the
30
+ # shared command building code.
31
+ def selector(_connection)
32
+ spec[:selector].dup
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2025 MongoDB Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module Mongo
18
+ module Operation
19
+ class CursorCommand
20
+ # Parses the cursor field of a command response so a Cursor can be built
21
+ # from the result. The parsing is identical to a find command result.
22
+ #
23
+ # @api private
24
+ class Result < Operation::Result
25
+ # @return [ true | false ] Whether the command response contained a
26
+ # cursor field.
27
+ def cursor?
28
+ !cursor_document.nil?
29
+ end
30
+
31
+ # @return [ Integer | nil ] The cursor id from the cursor document.
32
+ def cursor_id
33
+ cursor? ? cursor_document[CURSOR_ID] : super
34
+ end
35
+
36
+ # @return [ Array<BSON::Document> ] The first batch of documents.
37
+ def documents
38
+ cursor? ? cursor_document[FIRST_BATCH] : []
39
+ end
40
+
41
+ # @return [ String | nil ] The cursor namespace, "database.collection".
42
+ def namespace
43
+ cursor? ? cursor_document['ns'] : super
44
+ end
45
+
46
+ private
47
+
48
+ def cursor_document
49
+ return @cursor_document if defined?(@cursor_document)
50
+
51
+ @cursor_document = first_document[CURSOR]
52
+ end
53
+
54
+ def first_document
55
+ @first_document ||= reply.documents[0]
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2025 MongoDB Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'mongo/operation/cursor_command/op_msg'
18
+ require 'mongo/operation/cursor_command/result'
19
+
20
+ module Mongo
21
+ module Operation
22
+ # A command operation whose response is parsed as a cursor.
23
+ #
24
+ # Unlike Command, the result exposes the firstBatch, namespace, and cursor
25
+ # id from the command response so that a Cursor can be built from it.
26
+ #
27
+ # @api private
28
+ class CursorCommand
29
+ include Specifiable
30
+ include OpMsgExecutable
31
+ end
32
+ end
33
+ end
@@ -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
 
@@ -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
  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,7 +451,7 @@ 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
@@ -465,20 +466,6 @@ module Mongo
465
466
  end
466
467
  end
467
468
 
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
469
  def parser
483
470
  @parser ||= Error::Parser.new(first_document, replies)
484
471
  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
@@ -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)