mongo 2.19.3 → 2.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/Rakefile +27 -154
  4. data/lib/mongo/cluster/topology/base.rb +16 -0
  5. data/lib/mongo/cluster.rb +27 -1
  6. data/lib/mongo/collection/view/iterable.rb +1 -0
  7. data/lib/mongo/collection.rb +4 -2
  8. data/lib/mongo/error/transactions_not_supported.rb +34 -0
  9. data/lib/mongo/error.rb +1 -0
  10. data/lib/mongo/grid/fs_bucket.rb +6 -0
  11. data/lib/mongo/monitoring/event/secure.rb +1 -1
  12. data/lib/mongo/operation/shared/executable.rb +43 -27
  13. data/lib/mongo/operation/shared/response_handling.rb +23 -25
  14. data/lib/mongo/retryable/read_worker.rb +7 -6
  15. data/lib/mongo/retryable/write_worker.rb +7 -4
  16. data/lib/mongo/retryable.rb +2 -2
  17. data/lib/mongo/server/app_metadata/environment.rb +64 -9
  18. data/lib/mongo/server/app_metadata.rb +5 -4
  19. data/lib/mongo/server/description/features.rb +1 -0
  20. data/lib/mongo/server_selector/base.rb +32 -6
  21. data/lib/mongo/session/server_session/dirtyable.rb +52 -0
  22. data/lib/mongo/session/server_session.rb +3 -0
  23. data/lib/mongo/session/session_pool.rb +12 -18
  24. data/lib/mongo/session.rb +32 -0
  25. data/lib/mongo/uri.rb +0 -4
  26. data/lib/mongo/version.rb +1 -1
  27. data/mongo.gemspec +1 -7
  28. data/spec/atlas/atlas_connectivity_spec.rb +4 -4
  29. data/spec/faas/ruby-sam-app/Gemfile +9 -0
  30. data/spec/faas/ruby-sam-app/mongodb/Gemfile +4 -0
  31. data/spec/faas/ruby-sam-app/mongodb/app.rb +149 -0
  32. data/spec/faas/ruby-sam-app/template.yaml +48 -0
  33. data/spec/integration/client_side_encryption/corpus_spec.rb +10 -2
  34. data/spec/integration/retryable_reads_errors_spec.rb +161 -8
  35. data/spec/integration/retryable_writes_errors_spec.rb +156 -0
  36. data/spec/mongo/cluster_spec.rb +36 -0
  37. data/spec/mongo/collection/view/aggregation_spec.rb +6 -1
  38. data/spec/mongo/collection/view/explainable_spec.rb +2 -0
  39. data/spec/mongo/collection_crud_spec.rb +1 -1
  40. data/spec/mongo/operation/insert_spec.rb +1 -1
  41. data/spec/mongo/retryable/write_worker_spec.rb +39 -0
  42. data/spec/mongo/server/app_metadata/environment_spec.rb +135 -0
  43. data/spec/mongo/server/app_metadata_spec.rb +12 -2
  44. data/spec/mongo/server/connection_spec.rb +4 -0
  45. data/spec/mongo/session/session_pool_spec.rb +1 -16
  46. data/spec/mongo/session_transaction_spec.rb +15 -0
  47. data/spec/mongo/uri_spec.rb +0 -9
  48. data/spec/runners/crud/test.rb +0 -8
  49. data/spec/runners/crud.rb +1 -1
  50. data/spec/runners/transactions/test.rb +12 -3
  51. data/spec/runners/unified/assertions.rb +16 -3
  52. data/spec/runners/unified/crud_operations.rb +12 -0
  53. data/spec/runners/unified/support_operations.rb +3 -5
  54. data/spec/runners/unified/test.rb +8 -1
  55. data/spec/shared/lib/mrss/docker_runner.rb +3 -0
  56. data/spec/shared/share/Dockerfile.erb +20 -69
  57. data/spec/shared/shlib/server.sh +1 -0
  58. data/spec/shared/shlib/set_env.sh +5 -28
  59. data/spec/spec_tests/data/client_side_encryption/explain.yml +2 -2
  60. data/spec/spec_tests/data/connection_string/invalid-uris.yml +0 -10
  61. data/spec/spec_tests/data/connection_string/valid-options.yml +13 -0
  62. data/spec/spec_tests/data/crud_unified/find-test-all-options.yml +348 -0
  63. data/spec/spec_tests/data/index_management/createSearchIndex.yml +5 -3
  64. data/spec/spec_tests/data/index_management/createSearchIndexes.yml +7 -4
  65. data/spec/spec_tests/data/index_management/dropSearchIndex.yml +2 -1
  66. data/spec/spec_tests/data/index_management/listSearchIndexes.yml +13 -7
  67. data/spec/spec_tests/data/index_management/updateSearchIndex.yml +2 -1
  68. data/spec/spec_tests/data/retryable_writes/unified/bulkWrite-serverErrors.yml +3 -6
  69. data/spec/spec_tests/data/retryable_writes/unified/insertOne-serverErrors.yml +3 -6
  70. data/spec/spec_tests/data/run_command_unified/runCommand.yml +319 -0
  71. data/spec/spec_tests/data/sessions_unified/driver-sessions-dirty-session-errors.yml +351 -0
  72. data/spec/spec_tests/data/unified/valid-pass/poc-crud.yml +1 -1
  73. data/spec/spec_tests/data/unified/valid-pass/poc-retryable-writes.yml +7 -7
  74. data/spec/spec_tests/data/unified/valid-pass/poc-sessions.yml +3 -4
  75. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-convenient-api.yml +1 -1
  76. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-mongos-pin-auto.yml +1 -1
  77. data/spec/spec_tests/data/unified/valid-pass/poc-transactions.yml +3 -3
  78. data/spec/spec_tests/run_command_unified_spec.rb +13 -0
  79. data/spec/spec_tests/sdam_unified_spec.rb +2 -0
  80. data/spec/support/constraints.rb +6 -0
  81. data/spec/support/ocsp +1 -1
  82. data/spec/support/recording_logger.rb +27 -0
  83. data.tar.gz.sig +0 -0
  84. metadata +1272 -1253
  85. metadata.gz.sig +0 -0
  86. data/spec/spec_tests/data/cmap/pool-clear-interrupt-immediately.yml +0 -49
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0916a285c62c2e70c94532ca2ed61af6bfcb1ead971a2be825aefbde560209c3'
4
- data.tar.gz: a9db987bb6f1e2b7e9882c86472bc2c8b10eca0d4bdca9de2a37968de086d8f3
3
+ metadata.gz: 281c66c9e5d92b0eb8040c342bb0dfcf554c9b5420ec803bc68748472d74a1ae
4
+ data.tar.gz: 423f31b24accb3ce674ea28a7aa8fcaf028730eb1370187c23f5304ba30698f6
5
5
  SHA512:
6
- metadata.gz: ab6a8e7e008174f346f1acd96bb073a39cd4fbdd2a70e44f63f8dcdc421c4d38ba9277cc9c50099c944599886e53230597bdab8bd4c861de29af2bd66dd2be76
7
- data.tar.gz: 8fe9155b17bbb9b57021eabd7e2211baf2cb0002b211900e8564e0b1c2f9e894bf726a037b1e8b72e6dd55251edefe1e81fd8c3ea61aacd0fd5d7eb76cbc4163
6
+ metadata.gz: 4f414d30bf15f7c32ac9181e25e707caa80447975690fc6c0eb9d8fa37a784f8c4d21125b385d5a6d7efdb4bd751ee2c9c4bea313adbf78d20de3b019c25ac8a
7
+ data.tar.gz: 7a83ba71f6a36d886769d755fb5ea45e6d1fae37bd92b490c58e9f643f51c25d5a489ed79928d67975f067d258c738f54cc3e5caecca6eb0bad2e2bf49d81613
checksums.yaml.gz.sig CHANGED
Binary file
data/Rakefile CHANGED
@@ -119,6 +119,32 @@ end
119
119
 
120
120
  task :release => ['release:check_private_key', 'release:do']
121
121
 
122
+ desc 'Build and validate the evergreen config'
123
+ task eg: %w[ eg:build eg:validate ]
124
+
125
+ # 'eg' == 'evergreen', but evergreen is too many letters for convenience
126
+ namespace :eg do
127
+ desc 'Builds the .evergreen/config.yml file from the templates'
128
+ task :build do
129
+ ruby '.evergreen/update-evergreen-configs'
130
+ end
131
+
132
+ desc 'Validates the .evergreen/config.yml file'
133
+ task :validate do
134
+ system 'evergreen validate --project mongo-ruby-driver .evergreen/config.yml'
135
+ end
136
+
137
+ desc 'Updates the evergreen executable to the latest available version'
138
+ task :update do
139
+ system 'evergreen get-update --install'
140
+ end
141
+
142
+ desc 'Runs the current branch as an evergreen patch'
143
+ task :patch do
144
+ system 'evergreen patch --uncommitted --project mongo-ruby-driver --browse --auto-description --yes'
145
+ end
146
+ end
147
+
122
148
  desc "Generate all documentation"
123
149
  task :docs => 'docs:yard'
124
150
 
@@ -131,157 +157,4 @@ namespace :docs do
131
157
  end
132
158
  end
133
159
 
134
- require_relative "profile/benchmarking"
135
-
136
- # Some require data files, available from the drivers team. See the comments above each task for details."
137
- namespace :benchmark do
138
- desc "Run the driver benchmark tests."
139
-
140
- namespace :micro do
141
- desc "Run the common driver micro benchmarking tests"
142
-
143
- namespace :flat do
144
- desc "Benchmarking for flat bson documents."
145
-
146
- # Requirement: A file in Mongo::Benchmarking::DATA_PATH, called flat_bson.json.
147
- task :encode do
148
- puts "MICRO BENCHMARK:: FLAT:: ENCODE"
149
- Mongo::Benchmarking::Micro.run(:flat, :encode)
150
- end
151
-
152
- # Requirement: A file in Mongo::Benchmarking::DATA_PATH, called flat_bson.json.
153
- task :decode do
154
- puts "MICRO BENCHMARK:: FLAT:: DECODE"
155
- Mongo::Benchmarking::Micro.run(:flat, :decode)
156
- end
157
- end
158
-
159
- namespace :deep do
160
- desc "Benchmarking for deep bson documents."
161
-
162
- # Requirement: A file in Mongo::Benchmarking::DATA_PATH, called deep_bson.json.
163
- task :encode do
164
- puts "MICRO BENCHMARK:: DEEP:: ENCODE"
165
- Mongo::Benchmarking::Micro.run(:deep, :encode)
166
- end
167
-
168
- # Requirement: A file in Mongo::Benchmarking::DATA_PATH, called deep_bson.json.
169
- task :decode do
170
- puts "MICRO BENCHMARK:: DEEP:: DECODE"
171
- Mongo::Benchmarking::Micro.run(:deep, :decode)
172
- end
173
- end
174
-
175
- namespace :full do
176
- desc "Benchmarking for full bson documents."
177
-
178
- # Requirement: A file in Mongo::Benchmarking::DATA_PATH, called full_bson.json.
179
- task :encode do
180
- puts "MICRO BENCHMARK:: FULL:: ENCODE"
181
- Mongo::Benchmarking::Micro.run(:full, :encode)
182
- end
183
-
184
- # Requirement: A file in Mongo::Benchmarking::DATA_PATH, called full_bson.json.
185
- task :decode do
186
- puts "MICRO BENCHMARK:: FULL:: DECODE"
187
- Mongo::Benchmarking::Micro.run(:full, :decode)
188
- end
189
- end
190
- end
191
-
192
- namespace :single_doc do
193
- desc "Run the common driver single-document benchmarking tests"
194
- task :command do
195
- puts "SINGLE DOC BENCHMARK:: COMMAND"
196
- Mongo::Benchmarking::SingleDoc.run(:command)
197
- end
198
-
199
- # Requirement: A file in Mongo::Benchmarking::DATA_PATH, called TWEET.json.
200
- task :find_one do
201
- puts "SINGLE DOC BENCHMARK:: FIND ONE BY ID"
202
- Mongo::Benchmarking::SingleDoc.run(:find_one)
203
- end
204
-
205
- # Requirement: A file in Mongo::Benchmarking::DATA_PATH, called SMALL_DOC.json.
206
- task :insert_one_small do
207
- puts "SINGLE DOC BENCHMARK:: INSERT ONE SMALL DOCUMENT"
208
- Mongo::Benchmarking::SingleDoc.run(:insert_one_small)
209
- end
210
-
211
- # Requirement: A file in Mongo::Benchmarking::DATA_PATH, called LARGE_DOC.json.
212
- task :insert_one_large do
213
- puts "SINGLE DOC BENCHMARK:: INSERT ONE LARGE DOCUMENT"
214
- Mongo::Benchmarking::SingleDoc.run(:insert_one_large)
215
- end
216
- end
217
-
218
- namespace :multi_doc do
219
- desc "Run the common driver multi-document benchmarking tests"
220
-
221
- # Requirement: A file in Mongo::Benchmarking::DATA_PATH, called TWEET.json.
222
- task :find_many do
223
- puts "MULTI DOCUMENT BENCHMARK:: FIND MANY"
224
- Mongo::Benchmarking::MultiDoc.run(:find_many)
225
- end
226
-
227
- # Requirement: A file in Mongo::Benchmarking::DATA_PATH, called SMALL_DOC.json.
228
- task :bulk_insert_small do
229
- puts "MULTI DOCUMENT BENCHMARK:: BULK INSERT SMALL"
230
- Mongo::Benchmarking::MultiDoc.run(:bulk_insert_small)
231
- end
232
-
233
- # Requirement: A file in Mongo::Benchmarking::DATA_PATH, called LARGE_DOC.json.
234
- task :bulk_insert_large do
235
- puts "MULTI DOCUMENT BENCHMARK:: BULK INSERT LARGE"
236
- Mongo::Benchmarking::MultiDoc.run(:bulk_insert_large)
237
- end
238
-
239
- # Requirement: A file in Mongo::Benchmarking::DATA_PATH, called GRIDFS_LARGE.
240
- task :gridfs_upload do
241
- puts "MULTI DOCUMENT BENCHMARK:: GRIDFS UPLOAD"
242
- Mongo::Benchmarking::MultiDoc.run(:gridfs_upload)
243
- end
244
-
245
- # Requirement: A file in Mongo::Benchmarking::DATA_PATH, called GRIDFS_LARGE.
246
- task :gridfs_download do
247
- puts "MULTI DOCUMENT BENCHMARK:: GRIDFS DOWNLOAD"
248
- Mongo::Benchmarking::MultiDoc.run(:gridfs_download)
249
- end
250
- end
251
-
252
- namespace :parallel do
253
- desc "Run the common driver paralell ETL benchmarking tests"
254
-
255
- # Requirement: A directory in Mongo::Benchmarking::DATA_PATH, called LDJSON_MULTI,
256
- # with the files used in this task.
257
- task :import do
258
- puts "PARALLEL ETL BENCHMARK:: IMPORT"
259
- Mongo::Benchmarking::Parallel.run(:import)
260
- end
261
-
262
- # Requirement: A directory in Mongo::Benchmarking::DATA_PATH, called LDJSON_MULTI,
263
- # with the files used in this task.
264
- # Requirement: Another directory in "#{Mongo::Benchmarking::DATA_PATH}/LDJSON_MULTI"
265
- # called 'output'.
266
- task :export do
267
- puts "PARALLEL ETL BENCHMARK:: EXPORT"
268
- Mongo::Benchmarking::Parallel.run(:export)
269
- end
270
-
271
- # Requirement: A directory in Mongo::Benchmarking::DATA_PATH, called GRIDFS_MULTI,
272
- # with the files used in this task.
273
- task :gridfs_upload do
274
- puts "PARALLEL ETL BENCHMARK:: GRIDFS UPLOAD"
275
- Mongo::Benchmarking::Parallel.run(:gridfs_upload)
276
- end
277
-
278
- # Requirement: A directory in Mongo::Benchmarking::DATA_PATH, called GRIDFS_MULTI,
279
- # with the files used in this task.
280
- # Requirement: Another directory in "#{Mongo::Benchmarking::DATA_PATH}/GRIDFS_MULTI"
281
- # called 'output'.
282
- task :gridfs_download do
283
- puts "PARALLEL ETL BENCHMARK:: GRIDFS DOWNLOAD"
284
- Mongo::Benchmarking::Parallel.run(:gridfs_download)
285
- end
286
- end
287
- end
160
+ load 'profile/driver_bench/rake/tasks.rake'
@@ -211,6 +211,22 @@ module Mongo
211
211
  end
212
212
  end
213
213
 
214
+ # Compares each server address against the list of patterns.
215
+ #
216
+ # @param [ Array<String> ] patterns the URL suffixes to compare
217
+ # each server against.
218
+ #
219
+ # @return [ true | false ] whether any of the addresses match any of
220
+ # the patterns or not.
221
+ #
222
+ # @api private
223
+ def server_hosts_match_any?(patterns)
224
+ server_descriptions.any? do |addr_spec, _desc|
225
+ addr, _port = addr_spec.split(/:/)
226
+ patterns.any? { |pattern| addr.end_with?(pattern) }
227
+ end
228
+ end
229
+
214
230
  private
215
231
 
216
232
  # Validates and/or transforms options as necessary for the topology.
data/lib/mongo/cluster.rb CHANGED
@@ -157,7 +157,7 @@ module Mongo
157
157
  # @sdam_flow_lock covers just the sdam flow. Note it does not apply
158
158
  # to @topology replacements which are done under @update_lock.
159
159
  @sdam_flow_lock = Mutex.new
160
- Session::SessionPool.create(self)
160
+ @session_pool = Session::SessionPool.new(self)
161
161
 
162
162
  if seeds.empty? && load_balanced?
163
163
  raise ArgumentError, 'Load-balanced clusters with no seeds are prohibited'
@@ -186,6 +186,8 @@ module Mongo
186
186
  recreate_topology(topology, opening_topology)
187
187
  end
188
188
 
189
+ possibly_warn_about_compatibility!
190
+
189
191
  if load_balanced?
190
192
  # We are required by the specifications to produce certain SDAM events
191
193
  # when in load-balanced topology.
@@ -1082,6 +1084,30 @@ module Mongo
1082
1084
  Monitoring::Event::TopologyChanged.new(previous_topology, @topology)
1083
1085
  )
1084
1086
  end
1087
+
1088
+ COSMOSDB_HOST_PATTERNS = %w[ .cosmos.azure.com ]
1089
+ COSMOSDB_LOG_MESSAGE = 'You appear to be connected to a CosmosDB cluster. ' \
1090
+ 'For more information regarding feature compatibility and support please visit ' \
1091
+ 'https://www.mongodb.com/supportability/cosmosdb'
1092
+
1093
+ DOCUMENTDB_HOST_PATTERNS = %w[ .docdb.amazonaws.com .docdb-elastic.amazonaws.com ]
1094
+ DOCUMENTDB_LOG_MESSAGE = 'You appear to be connected to a DocumentDB cluster. ' \
1095
+ 'For more information regarding feature compatibility and support please visit ' \
1096
+ 'https://www.mongodb.com/supportability/documentdb'
1097
+
1098
+ # Compares the server hosts with address suffixes of known services
1099
+ # that provide limited MongoDB API compatibility, and warns about them.
1100
+ def possibly_warn_about_compatibility!
1101
+ if topology.server_hosts_match_any?(COSMOSDB_HOST_PATTERNS)
1102
+ log_info COSMOSDB_LOG_MESSAGE
1103
+ return
1104
+ end
1105
+
1106
+ if topology.server_hosts_match_any?(DOCUMENTDB_HOST_PATTERNS)
1107
+ log_info DOCUMENTDB_LOG_MESSAGE
1108
+ return
1109
+ end
1110
+ end
1085
1111
  end
1086
1112
  end
1087
1113
 
@@ -162,6 +162,7 @@ module Mongo
162
162
  let: options[:let],
163
163
  limit: limit,
164
164
  allow_disk_use: options[:allow_disk_use],
165
+ allow_partial_results: options[:allow_partial_results],
165
166
  read: read,
166
167
  read_concern: options[:read_concern] || read_concern,
167
168
  batch_size: batch_size,
@@ -339,7 +339,9 @@ module Mongo
339
339
  # inserted or updated documents where the clustered index key value
340
340
  # matches an existing value in the index.
341
341
  # - *:name* -- Optional. A name that uniquely identifies the clustered index.
342
- # @option opts [ Hash ] :collation The collation to use.
342
+ # @option opts [ Hash ] :collation The collation to use when creating the
343
+ # collection. This option will not be sent to the server when calling
344
+ # collection methods.
343
345
  # @option opts [ Hash ] :encrypted_fields Hash describing encrypted fields
344
346
  # for queryable encryption.
345
347
  # @option opts [ Integer ] :expire_after Number indicating
@@ -788,7 +790,7 @@ module Mongo
788
790
  def insert_one(document, opts = {})
789
791
  QueryCache.clear_namespace(namespace)
790
792
 
791
- client.send(:with_session, opts) do |session|
793
+ client.with_session(opts) do |session|
792
794
  write_concern = if opts[:write_concern]
793
795
  WriteConcern.get(opts[:write_concern])
794
796
  else
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2019-2020 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
+ class Error
19
+ # Transactions are not supported by the cluster. There might be the
20
+ # following reasons:
21
+ # - topology is standalone
22
+ # - topology is replica set and server version is < 4.0
23
+ # - topology is sharded and server version is < 4.2
24
+ #
25
+ # @param [ String ] reason The reason why transactions are no supported.
26
+ #
27
+ # @since 2.7.0
28
+ class TransactionsNotSupported < Error
29
+ def initialize(reason)
30
+ super("Transactions are not supported for the cluster: #{reason}")
31
+ end
32
+ end
33
+ end
34
+ end
data/lib/mongo/error.rb CHANGED
@@ -217,6 +217,7 @@ require 'mongo/error/missing_service_id'
217
217
  require 'mongo/error/server_api_conflict'
218
218
  require 'mongo/error/server_api_not_supported'
219
219
  require 'mongo/error/server_not_usable'
220
+ require 'mongo/error/transactions_not_supported'
220
221
  require 'mongo/error/unknown_payload_type'
221
222
  require 'mongo/error/unmet_dependency'
222
223
  require 'mongo/error/unsupported_option'
@@ -484,6 +484,12 @@ module Mongo
484
484
  end
485
485
  end
486
486
 
487
+ # Drop the collections that implement this bucket.
488
+ def drop
489
+ files_collection.drop
490
+ chunks_collection.drop
491
+ end
492
+
487
493
  private
488
494
 
489
495
  # @param [ Hash ] opts The options.
@@ -58,7 +58,7 @@ module Mongo
58
58
  # According to Command Monitoring spec,for hello/legacy hello commands
59
59
  # when speculativeAuthenticate is present, their commands AND replies
60
60
  # MUST be redacted from the events.
61
- # See https://github.com/mongodb/specifications/blob/master/source/command-monitoring/command-monitoring.rst#security
61
+ # See https://github.com/mongodb/specifications/blob/master/source/command-logging-and-monitoring/command-logging-and-monitoring.rst#security
62
62
  true
63
63
  else
64
64
  false
@@ -15,6 +15,8 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
 
18
+ require 'mongo/error'
19
+
18
20
  module Mongo
19
21
  module Operation
20
22
 
@@ -30,40 +32,42 @@ module Mongo
30
32
  session&.materialize_if_needed
31
33
  unpin_maybe(session, connection) do
32
34
  add_error_labels(connection, context) do
33
- add_server_diagnostics(connection) do
34
- get_result(connection, context, options).tap do |result|
35
- if session
36
- if session.in_transaction? &&
37
- connection.description.load_balancer?
38
- then
39
- if session.pinned_connection_global_id
40
- unless session.pinned_connection_global_id == connection.global_id
41
- raise(
42
- Error::InternalDriverError,
43
- "Expected operation to use connection #{session.pinned_connection_global_id} but it used #{connection.global_id}"
44
- )
35
+ check_for_network_error do
36
+ add_server_diagnostics(connection) do
37
+ get_result(connection, context, options).tap do |result|
38
+ if session
39
+ if session.in_transaction? &&
40
+ connection.description.load_balancer?
41
+ then
42
+ if session.pinned_connection_global_id
43
+ unless session.pinned_connection_global_id == connection.global_id
44
+ raise(
45
+ Error::InternalDriverError,
46
+ "Expected operation to use connection #{session.pinned_connection_global_id} but it used #{connection.global_id}"
47
+ )
48
+ end
49
+ else
50
+ session.pin_to_connection(connection.global_id)
51
+ connection.pin
45
52
  end
46
- else
47
- session.pin_to_connection(connection.global_id)
48
- connection.pin
49
53
  end
50
- end
51
54
 
52
- if session.snapshot? && !session.snapshot_timestamp
53
- session.snapshot_timestamp = result.snapshot_timestamp
55
+ if session.snapshot? && !session.snapshot_timestamp
56
+ session.snapshot_timestamp = result.snapshot_timestamp
57
+ end
54
58
  end
55
- end
56
59
 
57
- if result.has_cursor_id? &&
58
- connection.description.load_balancer?
59
- then
60
- if result.cursor_id == 0
61
- connection.unpin
62
- else
63
- connection.pin
60
+ if result.has_cursor_id? &&
61
+ connection.description.load_balancer?
62
+ then
63
+ if result.cursor_id == 0
64
+ connection.unpin
65
+ else
66
+ connection.pin
67
+ end
64
68
  end
69
+ process_result(result, connection)
65
70
  end
66
- process_result(result, connection)
67
71
  end
68
72
  end
69
73
  end
@@ -144,6 +148,18 @@ module Mongo
144
148
  connection.server.scan_semaphore.signal
145
149
  end
146
150
  end
151
+
152
+ NETWORK_ERRORS = [
153
+ Error::SocketError,
154
+ Error::SocketTimeoutError
155
+ ].freeze
156
+
157
+ def check_for_network_error
158
+ yield
159
+ rescue *NETWORK_ERRORS
160
+ session&.dirty!
161
+ raise
162
+ end
147
163
  end
148
164
  end
149
165
  end
@@ -50,35 +50,33 @@ module Mongo
50
50
  # the operation is performed.
51
51
  # @param [ Mongo::Operation::Context ] context The operation context.
52
52
  def add_error_labels(connection, context)
53
- begin
54
- yield
55
- rescue Mongo::Error::SocketError => e
56
- if context.in_transaction? && !context.committing_transaction?
57
- e.add_label('TransientTransactionError')
58
- end
59
- if context.committing_transaction?
60
- e.add_label('UnknownTransactionCommitResult')
61
- end
53
+ yield
54
+ rescue Mongo::Error::SocketError => e
55
+ if context.in_transaction? && !context.committing_transaction?
56
+ e.add_label('TransientTransactionError')
57
+ end
58
+ if context.committing_transaction?
59
+ e.add_label('UnknownTransactionCommitResult')
60
+ end
62
61
 
63
- maybe_add_retryable_write_error_label!(e, connection, context)
64
-
65
- raise e
66
- rescue Mongo::Error::SocketTimeoutError => e
67
- maybe_add_retryable_write_error_label!(e, connection, context)
68
- raise e
69
- rescue Mongo::Error::OperationFailure => e
70
- if context.committing_transaction?
71
- if e.write_retryable? || e.wtimeout? || (e.write_concern_error? &&
72
- !Session::UNLABELED_WRITE_CONCERN_CODES.include?(e.write_concern_error_code)
73
- ) || e.max_time_ms_expired?
74
- e.add_label('UnknownTransactionCommitResult')
75
- end
62
+ maybe_add_retryable_write_error_label!(e, connection, context)
63
+
64
+ raise e
65
+ rescue Mongo::Error::SocketTimeoutError => e
66
+ maybe_add_retryable_write_error_label!(e, connection, context)
67
+ raise e
68
+ rescue Mongo::Error::OperationFailure => e
69
+ if context.committing_transaction?
70
+ if e.write_retryable? || e.wtimeout? || (e.write_concern_error? &&
71
+ !Session::UNLABELED_WRITE_CONCERN_CODES.include?(e.write_concern_error_code)
72
+ ) || e.max_time_ms_expired?
73
+ e.add_label('UnknownTransactionCommitResult')
76
74
  end
75
+ end
77
76
 
78
- maybe_add_retryable_write_error_label!(e, connection, context)
77
+ maybe_add_retryable_write_error_label!(e, connection, context)
79
78
 
80
- raise e
81
- end
79
+ raise e
82
80
  end
83
81
 
84
82
  # Unpins the session and/or the connection if the yielded to block
@@ -190,12 +190,13 @@ module Mongo
190
190
  #
191
191
  # @return [ Result ] The result of the operation.
192
192
  def modern_read_with_retry(session, server_selector, &block)
193
- yield select_server(cluster, server_selector, session)
193
+ server = select_server(cluster, server_selector, session)
194
+ yield server
194
195
  rescue *retryable_exceptions, Error::OperationFailure, Auth::Unauthorized, Error::PoolError => e
195
196
  e.add_notes('modern retry', 'attempt 1')
196
197
  raise e if session.in_transaction?
197
198
  raise e if !is_retryable_exception?(e) && !e.write_retryable?
198
- retry_read(e, session, server_selector, &block)
199
+ retry_read(e, session, server_selector, failed_server: server, &block)
199
200
  end
200
201
 
201
202
  # Attempts to do a "legacy" read with retry. The operation will be
@@ -257,12 +258,14 @@ module Mongo
257
258
  # being run on.
258
259
  # @param [ Mongo::ServerSelector::Selectable ] server_selector Server
259
260
  # selector for the operation.
261
+ # @param [ Mongo::Server ] failed_server The server on which the original
262
+ # operation failed.
260
263
  # @param [ Proc ] block The block to execute.
261
264
  #
262
265
  # @return [ Result ] The result of the operation.
263
- def retry_read(original_error, session, server_selector, &block)
266
+ def retry_read(original_error, session, server_selector, failed_server: nil, &block)
264
267
  begin
265
- server = select_server(cluster, server_selector, session)
268
+ server = select_server(cluster, server_selector, session, failed_server)
266
269
  rescue Error, Error::AuthError => e
267
270
  original_error.add_note("later retry failed: #{e.class}: #{e}")
268
271
  raise original_error
@@ -289,8 +292,6 @@ module Mongo
289
292
  raise original_error
290
293
  end
291
294
  end
292
-
293
295
  end
294
-
295
296
  end
296
297
  end
@@ -103,8 +103,9 @@ module Mongo
103
103
  def nro_write_with_retry(write_concern, context:, &block)
104
104
  session = context.session
105
105
  server = select_server(cluster, ServerSelector.primary, session)
106
+ options = session&.client&.options || {}
106
107
 
107
- if session&.client.options[:retry_writes]
108
+ if options[:retry_writes]
108
109
  begin
109
110
  server.with_connection(connection_global_id: context.connection_global_id) do |connection|
110
111
  yield connection, nil, context
@@ -240,7 +241,7 @@ module Mongo
240
241
 
241
242
  # Context#with creates a new context, which is not necessary here
242
243
  # but the API is less prone to misuse this way.
243
- retry_write(e, txn_num, context: context.with(is_retry: true), &block)
244
+ retry_write(e, txn_num, context: context.with(is_retry: true), failed_server: server, &block)
244
245
  end
245
246
 
246
247
  # Called after a failed write, this will retry the write no more than
@@ -250,9 +251,11 @@ module Mongo
250
251
  # retry.
251
252
  # @param [ Number ] txn_num The transaction number.
252
253
  # @param [ Operation::Context ] context The context for the operation.
254
+ # @param [ Mongo::Server ] failed_server The server on which the original
255
+ # operation failed.
253
256
  #
254
257
  # @return [ Result ] The result of the operation.
255
- def retry_write(original_error, txn_num, context:, &block)
258
+ def retry_write(original_error, txn_num, context:, failed_server: nil, &block)
256
259
  session = context.session
257
260
 
258
261
  # We do not request a scan of the cluster here, because error handling
@@ -260,7 +263,7 @@ module Mongo
260
263
  # server description and/or topology as necessary (specifically,
261
264
  # a socket error or a not master error should have marked the respective
262
265
  # server unknown). Here we just need to wait for server selection.
263
- server = select_server(cluster, ServerSelector.primary, session)
266
+ server = select_server(cluster, ServerSelector.primary, session, failed_server)
264
267
 
265
268
  unless server.retry_writes?
266
269
  # Do not need to add "modern retry" here, it should already be on
@@ -46,8 +46,8 @@ module Mongo
46
46
  # @api private
47
47
  #
48
48
  # @return [ Mongo::Server ] A server matching the server preference.
49
- def select_server(cluster, server_selector, session)
50
- server_selector.select_server(cluster, nil, session)
49
+ def select_server(cluster, server_selector, session, failed_server = nil)
50
+ server_selector.select_server(cluster, nil, session, deprioritized: [failed_server].compact)
51
51
  end
52
52
 
53
53
  # Returns the read worker for handling retryable reads.