mongo 2.19.3 → 2.20.1

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 (178) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +37 -1
  3. data/Rakefile +81 -172
  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/config.rb +2 -2
  9. data/lib/mongo/error/transactions_not_supported.rb +34 -0
  10. data/lib/mongo/error.rb +1 -0
  11. data/lib/mongo/grid/fs_bucket.rb +6 -0
  12. data/lib/mongo/monitoring/event/secure.rb +1 -1
  13. data/lib/mongo/operation/shared/executable.rb +43 -27
  14. data/lib/mongo/operation/shared/response_handling.rb +23 -25
  15. data/lib/mongo/retryable/base_worker.rb +28 -3
  16. data/lib/mongo/retryable/read_worker.rb +16 -14
  17. data/lib/mongo/retryable/write_worker.rb +11 -8
  18. data/lib/mongo/retryable.rb +2 -2
  19. data/lib/mongo/server/app_metadata/environment.rb +64 -9
  20. data/lib/mongo/server/app_metadata.rb +5 -4
  21. data/lib/mongo/server/description/features.rb +1 -0
  22. data/lib/mongo/server/pending_connection.rb +19 -6
  23. data/lib/mongo/server_selector/base.rb +32 -6
  24. data/lib/mongo/session/server_session/dirtyable.rb +52 -0
  25. data/lib/mongo/session/server_session.rb +3 -0
  26. data/lib/mongo/session/session_pool.rb +12 -18
  27. data/lib/mongo/session.rb +32 -0
  28. data/lib/mongo/socket/ssl.rb +22 -1
  29. data/lib/mongo/uri.rb +0 -4
  30. data/lib/mongo/version.rb +1 -5
  31. data/mongo.gemspec +9 -18
  32. data/spec/atlas/atlas_connectivity_spec.rb +4 -4
  33. data/spec/faas/ruby-sam-app/Gemfile +9 -0
  34. data/spec/faas/ruby-sam-app/mongodb/Gemfile +4 -0
  35. data/spec/faas/ruby-sam-app/mongodb/app.rb +149 -0
  36. data/spec/faas/ruby-sam-app/template.yaml +48 -0
  37. data/spec/integration/client_side_encryption/corpus_spec.rb +10 -2
  38. data/spec/integration/client_side_encryption/range_explicit_encryption_prose_spec.rb +3 -0
  39. data/spec/integration/retryable_reads_errors_spec.rb +196 -31
  40. data/spec/integration/retryable_writes_errors_spec.rb +156 -0
  41. data/spec/integration/sdam_error_handling_spec.rb +2 -0
  42. data/spec/lite_spec_helper.rb +0 -10
  43. data/spec/mongo/cluster_spec.rb +36 -0
  44. data/spec/mongo/collection/view/aggregation_spec.rb +6 -1
  45. data/spec/mongo/collection/view/explainable_spec.rb +2 -0
  46. data/spec/mongo/collection_crud_spec.rb +2 -1
  47. data/spec/mongo/operation/insert_spec.rb +1 -1
  48. data/spec/mongo/retryable/write_worker_spec.rb +39 -0
  49. data/spec/mongo/server/app_metadata/environment_spec.rb +135 -0
  50. data/spec/mongo/server/app_metadata_spec.rb +12 -2
  51. data/spec/mongo/server/connection_spec.rb +26 -0
  52. data/spec/mongo/session/session_pool_spec.rb +1 -16
  53. data/spec/mongo/session_transaction_spec.rb +15 -0
  54. data/spec/mongo/uri_spec.rb +0 -9
  55. data/spec/runners/crud/test.rb +0 -8
  56. data/spec/runners/crud.rb +1 -1
  57. data/spec/runners/transactions/test.rb +12 -3
  58. data/spec/runners/unified/assertions.rb +16 -3
  59. data/spec/runners/unified/crud_operations.rb +12 -0
  60. data/spec/runners/unified/support_operations.rb +3 -5
  61. data/spec/runners/unified/test.rb +8 -1
  62. data/spec/spec_tests/data/client_side_encryption/explain.yml +2 -2
  63. data/spec/spec_tests/data/client_side_encryption/fle2v2-BypassQueryAnalysis.yml +1 -0
  64. data/spec/spec_tests/data/client_side_encryption/fle2v2-Compact.yml +1 -0
  65. data/spec/spec_tests/data/client_side_encryption/fle2v2-CreateCollection.yml +1 -0
  66. data/spec/spec_tests/data/client_side_encryption/fle2v2-DecryptExistingData.yml +1 -0
  67. data/spec/spec_tests/data/client_side_encryption/fle2v2-Delete.yml +1 -0
  68. data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.yml +1 -0
  69. data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFields-vs-jsonSchema.yml +1 -0
  70. data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFieldsMap-defaults.yml +1 -0
  71. data/spec/spec_tests/data/client_side_encryption/fle2v2-FindOneAndUpdate.yml +1 -0
  72. data/spec/spec_tests/data/client_side_encryption/fle2v2-InsertFind-Indexed.yml +1 -0
  73. data/spec/spec_tests/data/client_side_encryption/fle2v2-InsertFind-Unindexed.yml +1 -0
  74. data/spec/spec_tests/data/client_side_encryption/fle2v2-MissingKey.yml +1 -0
  75. data/spec/spec_tests/data/client_side_encryption/fle2v2-NoEncryption.yml +1 -0
  76. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Aggregate.yml +1 -0
  77. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Correctness.yml +1 -0
  78. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Delete.yml +1 -0
  79. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-FindOneAndUpdate.yml +1 -0
  80. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-InsertFind.yml +1 -0
  81. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Update.yml +1 -0
  82. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Aggregate.yml +1 -0
  83. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Correctness.yml +1 -0
  84. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Delete.yml +1 -0
  85. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-FindOneAndUpdate.yml +1 -0
  86. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-InsertFind.yml +1 -0
  87. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Update.yml +1 -0
  88. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Aggregate.yml +1 -0
  89. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Correctness.yml +1 -0
  90. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Delete.yml +1 -0
  91. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.yml +1 -0
  92. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-InsertFind.yml +1 -0
  93. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Update.yml +1 -0
  94. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Aggregate.yml +1 -0
  95. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Correctness.yml +1 -0
  96. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Delete.yml +1 -0
  97. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-FindOneAndUpdate.yml +1 -0
  98. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-InsertFind.yml +1 -0
  99. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Update.yml +1 -0
  100. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Aggregate.yml +1 -0
  101. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Correctness.yml +1 -0
  102. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Delete.yml +1 -0
  103. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-FindOneAndUpdate.yml +1 -0
  104. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-InsertFind.yml +1 -0
  105. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Update.yml +1 -0
  106. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Aggregate.yml +1 -0
  107. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Correctness.yml +1 -0
  108. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Delete.yml +1 -0
  109. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-FindOneAndUpdate.yml +1 -0
  110. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-InsertFind.yml +1 -0
  111. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Update.yml +1 -0
  112. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Aggregate.yml +1 -0
  113. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Correctness.yml +1 -0
  114. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Delete.yml +1 -0
  115. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-FindOneAndUpdate.yml +1 -0
  116. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-InsertFind.yml +1 -0
  117. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Update.yml +1 -0
  118. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-WrongType.yml +1 -0
  119. data/spec/spec_tests/data/client_side_encryption/fle2v2-Update.yml +1 -0
  120. data/spec/spec_tests/data/client_side_encryption/fle2v2-validatorAndPartialFieldExpression.yml +2 -1
  121. data/spec/spec_tests/data/connection_string/invalid-uris.yml +0 -10
  122. data/spec/spec_tests/data/connection_string/valid-options.yml +13 -0
  123. data/spec/spec_tests/data/crud_unified/aggregate-write-readPreference.yml +2 -0
  124. data/spec/spec_tests/data/crud_unified/db-aggregate-write-readPreference.yml +2 -0
  125. data/spec/spec_tests/data/crud_unified/find-test-all-options.yml +348 -0
  126. data/spec/spec_tests/data/index_management/createSearchIndex.yml +5 -3
  127. data/spec/spec_tests/data/index_management/createSearchIndexes.yml +7 -4
  128. data/spec/spec_tests/data/index_management/dropSearchIndex.yml +2 -1
  129. data/spec/spec_tests/data/index_management/listSearchIndexes.yml +13 -7
  130. data/spec/spec_tests/data/index_management/updateSearchIndex.yml +2 -1
  131. data/spec/spec_tests/data/retryable_writes/unified/bulkWrite-serverErrors.yml +3 -6
  132. data/spec/spec_tests/data/retryable_writes/unified/insertOne-serverErrors.yml +3 -6
  133. data/spec/spec_tests/data/run_command_unified/runCommand.yml +319 -0
  134. data/spec/spec_tests/data/sessions_unified/driver-sessions-dirty-session-errors.yml +351 -0
  135. data/spec/spec_tests/data/unified/valid-pass/poc-crud.yml +1 -1
  136. data/spec/spec_tests/data/unified/valid-pass/poc-retryable-writes.yml +7 -7
  137. data/spec/spec_tests/data/unified/valid-pass/poc-sessions.yml +3 -4
  138. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-convenient-api.yml +1 -1
  139. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-mongos-pin-auto.yml +1 -1
  140. data/spec/spec_tests/data/unified/valid-pass/poc-transactions.yml +3 -3
  141. data/spec/spec_tests/run_command_unified_spec.rb +13 -0
  142. data/spec/spec_tests/sdam_unified_spec.rb +2 -0
  143. data/spec/spec_tests/transactions_unified_spec.rb +2 -1
  144. data/spec/support/certificates/atlas-ocsp-ca.crt +89 -77
  145. data/spec/support/certificates/atlas-ocsp.crt +117 -122
  146. data/spec/support/certificates/retrieve-atlas-cert +1 -1
  147. data/spec/support/constraints.rb +6 -0
  148. data/spec/support/ocsp +1 -1
  149. data/spec/support/recording_logger.rb +27 -0
  150. metadata +1245 -1298
  151. checksums.yaml.gz.sig +0 -0
  152. data/spec/shared/LICENSE +0 -20
  153. data/spec/shared/bin/get-mongodb-download-url +0 -17
  154. data/spec/shared/bin/s3-copy +0 -45
  155. data/spec/shared/bin/s3-upload +0 -69
  156. data/spec/shared/lib/mrss/child_process_helper.rb +0 -80
  157. data/spec/shared/lib/mrss/cluster_config.rb +0 -231
  158. data/spec/shared/lib/mrss/constraints.rb +0 -378
  159. data/spec/shared/lib/mrss/docker_runner.rb +0 -295
  160. data/spec/shared/lib/mrss/eg_config_utils.rb +0 -51
  161. data/spec/shared/lib/mrss/event_subscriber.rb +0 -210
  162. data/spec/shared/lib/mrss/lite_constraints.rb +0 -238
  163. data/spec/shared/lib/mrss/server_version_registry.rb +0 -113
  164. data/spec/shared/lib/mrss/session_registry.rb +0 -69
  165. data/spec/shared/lib/mrss/session_registry_legacy.rb +0 -60
  166. data/spec/shared/lib/mrss/spec_organizer.rb +0 -179
  167. data/spec/shared/lib/mrss/utils.rb +0 -37
  168. data/spec/shared/share/Dockerfile.erb +0 -330
  169. data/spec/shared/share/haproxy-1.conf +0 -16
  170. data/spec/shared/share/haproxy-2.conf +0 -17
  171. data/spec/shared/shlib/config.sh +0 -27
  172. data/spec/shared/shlib/distro.sh +0 -74
  173. data/spec/shared/shlib/server.sh +0 -416
  174. data/spec/shared/shlib/set_env.sh +0 -169
  175. data/spec/spec_tests/data/cmap/pool-clear-interrupt-immediately.yml +0 -49
  176. data/spec/support/faas/app/aws_lambda/mongodb/Gemfile.lock +0 -19
  177. data.tar.gz.sig +0 -3
  178. metadata.gz.sig +0 -0
@@ -4,6 +4,7 @@
4
4
  require 'spec_helper'
5
5
 
6
6
  describe 'Retryable reads errors tests' do
7
+ retry_test
7
8
 
8
9
  let(:client) { authorized_client.with(options.merge(retry_reads: true)) }
9
10
 
@@ -20,14 +21,14 @@ describe 'Retryable reads errors tests' do
20
21
 
21
22
  let(:failpoint) do
22
23
  {
23
- configureFailPoint: "failCommand",
24
- mode: { times: 1 },
25
- data: {
26
- failCommands: [ "find" ],
27
- errorCode: 91,
28
- blockConnection: true,
29
- blockTimeMS: 1000
30
- }
24
+ configureFailPoint: "failCommand",
25
+ mode: { times: 1 },
26
+ data: {
27
+ failCommands: [ "find" ],
28
+ errorCode: 91,
29
+ blockConnection: true,
30
+ blockTimeMS: 1000
31
+ }
31
32
  }
32
33
  end
33
34
 
@@ -73,31 +74,42 @@ describe 'Retryable reads errors tests' do
73
74
  client.subscribe(Mongo::Monitoring::CONNECTION_POOL, subscriber)
74
75
  end
75
76
 
76
- it "retries on PoolClearedError" do
77
- # After the first find fails, the pool is paused and retry is triggered.
78
- # Now, a race is started between the second find acquiring a connection,
79
- # and the first retrying the read. Now, retry reads cause the cluster to
80
- # be rescanned and the pool to be unpaused, allowing the second checkout
81
- # to succeed (when it should fail). Therefore we want the second find's
82
- # check out to win the race. This gives the check out a little head start.
83
- allow_any_instance_of(Mongo::Server::ConnectionPool).to receive(:ready).and_wrap_original do |m, *args, &block|
84
- ::Utils.wait_for_condition(5) do
85
- # check_out_results should contain:
86
- # - find1 connection check out successful
87
- # - pool cleared
88
- # - find2 connection check out failed
89
- # We wait here for the third event to happen before we ready the pool.
90
- cmap_events.select do |e|
91
- event_types.include?(e.class)
92
- end.length >= 3
77
+ shared_examples_for 'retries on PoolClearedError' do
78
+ it "retries on PoolClearedError" do
79
+ # After the first find fails, the pool is paused and retry is triggered.
80
+ # Now, a race is started between the second find acquiring a connection,
81
+ # and the first retrying the read. Now, retry reads cause the cluster to
82
+ # be rescanned and the pool to be unpaused, allowing the second checkout
83
+ # to succeed (when it should fail). Therefore we want the second find's
84
+ # check out to win the race. This gives the check out a little head start.
85
+ allow_any_instance_of(Mongo::Server::ConnectionPool).to receive(:ready).and_wrap_original do |m, *args, &block|
86
+ ::Utils.wait_for_condition(5) do
87
+ # check_out_results should contain:
88
+ # - find1 connection check out successful
89
+ # - pool cleared
90
+ # - find2 connection check out failed
91
+ # We wait here for the third event to happen before we ready the pool.
92
+ cmap_events.select do |e|
93
+ event_types.include?(e.class)
94
+ end.length >= 3
95
+ end
96
+ m.call(*args, &block)
93
97
  end
94
- m.call(*args, &block)
98
+ threads.map(&:join)
99
+ expect(check_out_results[0]).to be_a(Mongo::Monitoring::Event::Cmap::ConnectionCheckedOut)
100
+ expect(check_out_results[1]).to be_a(Mongo::Monitoring::Event::Cmap::PoolCleared)
101
+ expect(check_out_results[2]).to be_a(Mongo::Monitoring::Event::Cmap::ConnectionCheckOutFailed)
102
+ expect(find_events.length).to eq(3)
95
103
  end
96
- threads.map(&:join)
97
- expect(check_out_results[0]).to be_a(Mongo::Monitoring::Event::Cmap::ConnectionCheckedOut)
98
- expect(check_out_results[1]).to be_a(Mongo::Monitoring::Event::Cmap::PoolCleared)
99
- expect(check_out_results[2]).to be_a(Mongo::Monitoring::Event::Cmap::ConnectionCheckOutFailed)
100
- expect(find_events.length).to eq(3)
104
+ end
105
+
106
+ it_behaves_like 'retries on PoolClearedError'
107
+
108
+ context 'legacy read retries' do
109
+
110
+ let(:client) { authorized_client.with(options.merge(retry_reads: false, max_read_retries: 1)) }
111
+
112
+ it_behaves_like 'retries on PoolClearedError'
101
113
  end
102
114
 
103
115
  after do
@@ -107,4 +119,157 @@ describe 'Retryable reads errors tests' do
107
119
  })
108
120
  end
109
121
  end
122
+
123
+ context 'Retries in a sharded cluster' do
124
+ require_topology :sharded
125
+ min_server_version '4.2'
126
+ require_no_auth
127
+
128
+ let(:subscriber) { Mrss::EventSubscriber.new }
129
+
130
+ let(:find_started_events) do
131
+ subscriber.started_events.select { |e| e.command_name == "find" }
132
+ end
133
+
134
+ let(:find_failed_events) do
135
+ subscriber.failed_events.select { |e| e.command_name == "find" }
136
+ end
137
+
138
+ let(:find_succeeded_events) do
139
+ subscriber.succeeded_events.select { |e| e.command_name == "find" }
140
+ end
141
+
142
+ context 'when another mongos is available' do
143
+
144
+ let(:first_mongos) do
145
+ Mongo::Client.new(
146
+ [SpecConfig.instance.addresses.first],
147
+ direct_connection: true,
148
+ database: 'admin'
149
+ )
150
+ end
151
+
152
+ let(:second_mongos) do
153
+ Mongo::Client.new(
154
+ [SpecConfig.instance.addresses.last],
155
+ direct_connection: false,
156
+ database: 'admin'
157
+ )
158
+ end
159
+
160
+ let(:client) do
161
+ new_local_client(
162
+ [
163
+ SpecConfig.instance.addresses.first,
164
+ SpecConfig.instance.addresses.last,
165
+ ],
166
+ SpecConfig.instance.test_options.merge(retry_reads: true)
167
+ )
168
+ end
169
+
170
+ let(:expected_servers) do
171
+ [
172
+ SpecConfig.instance.addresses.first.to_s,
173
+ SpecConfig.instance.addresses.last.to_s
174
+ ].sort
175
+ end
176
+
177
+ before do
178
+ skip 'This test requires at least two mongos' if SpecConfig.instance.addresses.length < 2
179
+
180
+ first_mongos.database.command(
181
+ configureFailPoint: 'failCommand',
182
+ mode: { times: 1 },
183
+ data: {
184
+ failCommands: %w(find),
185
+ closeConnection: false,
186
+ errorCode: 6
187
+ }
188
+ )
189
+
190
+ second_mongos.database.command(
191
+ configureFailPoint: 'failCommand',
192
+ mode: { times: 1 },
193
+ data: {
194
+ failCommands: %w(find),
195
+ closeConnection: false,
196
+ errorCode: 6
197
+ }
198
+ )
199
+ end
200
+
201
+ after do
202
+ [first_mongos, second_mongos].each do |admin_client|
203
+ admin_client.database.command(
204
+ configureFailPoint: 'failCommand',
205
+ mode: 'off'
206
+ )
207
+ admin_client.close
208
+ end
209
+ client.close
210
+ end
211
+
212
+ it 'retries on different mongos' do
213
+ client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
214
+ expect { collection.find.first }.to raise_error(Mongo::Error::OperationFailure)
215
+ expect(find_started_events.map { |e| e.address.to_s }.sort).to eq(expected_servers)
216
+ expect(find_failed_events.map { |e| e.address.to_s }.sort).to eq(expected_servers)
217
+ end
218
+ end
219
+
220
+ context 'when no other mongos is available' do
221
+ let(:mongos) do
222
+ Mongo::Client.new(
223
+ [SpecConfig.instance.addresses.first],
224
+ direct_connection: true,
225
+ database: 'admin'
226
+ )
227
+ end
228
+
229
+ let(:client) do
230
+ new_local_client(
231
+ [
232
+ SpecConfig.instance.addresses.first
233
+ ],
234
+ SpecConfig.instance.test_options.merge(retry_reads: true)
235
+ )
236
+ end
237
+
238
+ before do
239
+ mongos.database.command(
240
+ configureFailPoint: 'failCommand',
241
+ mode: { times: 1 },
242
+ data: {
243
+ failCommands: %w(find),
244
+ closeConnection: false,
245
+ errorCode: 6
246
+ }
247
+ )
248
+ end
249
+
250
+ after do
251
+ mongos.database.command(
252
+ configureFailPoint: 'failCommand',
253
+ mode: 'off'
254
+ )
255
+ mongos.close
256
+ client.close
257
+ end
258
+
259
+ it 'retries on the same mongos' do
260
+ client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
261
+ expect { collection.find.first }.not_to raise_error
262
+ expect(find_started_events.map { |e| e.address.to_s }.sort).to eq([
263
+ SpecConfig.instance.addresses.first.to_s,
264
+ SpecConfig.instance.addresses.first.to_s
265
+ ])
266
+ expect(find_failed_events.map { |e| e.address.to_s }.sort).to eq([
267
+ SpecConfig.instance.addresses.first.to_s
268
+ ])
269
+ expect(find_succeeded_events.map { |e| e.address.to_s }.sort).to eq([
270
+ SpecConfig.instance.addresses.first.to_s
271
+ ])
272
+ end
273
+ end
274
+ end
110
275
  end
@@ -189,4 +189,160 @@ describe 'Retryable writes errors tests' do
189
189
  })
190
190
  end
191
191
  end
192
+
193
+ context 'Retries in a sharded cluster' do
194
+ require_topology :sharded
195
+ min_server_version '4.2'
196
+ require_no_auth
197
+
198
+ let(:subscriber) { Mrss::EventSubscriber.new }
199
+
200
+ let(:insert_started_events) do
201
+ subscriber.started_events.select { |e| e.command_name == "insert" }
202
+ end
203
+
204
+ let(:insert_failed_events) do
205
+ subscriber.failed_events.select { |e| e.command_name == "insert" }
206
+ end
207
+
208
+ let(:insert_succeeded_events) do
209
+ subscriber.succeeded_events.select { |e| e.command_name == "insert" }
210
+ end
211
+
212
+ context 'when another mongos is available' do
213
+
214
+ let(:first_mongos) do
215
+ Mongo::Client.new(
216
+ [SpecConfig.instance.addresses.first],
217
+ direct_connection: true,
218
+ database: 'admin'
219
+ )
220
+ end
221
+
222
+ let(:second_mongos) do
223
+ Mongo::Client.new(
224
+ [SpecConfig.instance.addresses.last],
225
+ direct_connection: false,
226
+ database: 'admin'
227
+ )
228
+ end
229
+
230
+ let(:client) do
231
+ new_local_client(
232
+ [
233
+ SpecConfig.instance.addresses.first,
234
+ SpecConfig.instance.addresses.last,
235
+ ],
236
+ SpecConfig.instance.test_options.merge(retry_writes: true)
237
+ )
238
+ end
239
+
240
+ let(:expected_servers) do
241
+ [
242
+ SpecConfig.instance.addresses.first.to_s,
243
+ SpecConfig.instance.addresses.last.to_s
244
+ ].sort
245
+ end
246
+
247
+ before do
248
+ skip 'This test requires at least two mongos' if SpecConfig.instance.addresses.length < 2
249
+
250
+ first_mongos.database.command(
251
+ configureFailPoint: 'failCommand',
252
+ mode: { times: 1 },
253
+ data: {
254
+ failCommands: %w(insert),
255
+ closeConnection: false,
256
+ errorCode: 6,
257
+ errorLabels: ['RetryableWriteError']
258
+ }
259
+ )
260
+
261
+ second_mongos.database.command(
262
+ configureFailPoint: 'failCommand',
263
+ mode: { times: 1 },
264
+ data: {
265
+ failCommands: %w(insert),
266
+ closeConnection: false,
267
+ errorCode: 6,
268
+ errorLabels: ['RetryableWriteError']
269
+ }
270
+ )
271
+ end
272
+
273
+ after do
274
+ [first_mongos, second_mongos].each do |admin_client|
275
+ admin_client.database.command(
276
+ configureFailPoint: 'failCommand',
277
+ mode: 'off'
278
+ )
279
+ admin_client.close
280
+ end
281
+ client.close
282
+ end
283
+
284
+ it 'retries on different mongos' do
285
+ client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
286
+ expect { collection.insert_one(x: 1) }.to raise_error(Mongo::Error::OperationFailure)
287
+ expect(insert_started_events.map { |e| e.address.to_s }.sort).to eq(expected_servers)
288
+ expect(insert_failed_events.map { |e| e.address.to_s }.sort).to eq(expected_servers)
289
+ end
290
+ end
291
+
292
+ context 'when no other mongos is available' do
293
+ let(:mongos) do
294
+ Mongo::Client.new(
295
+ [SpecConfig.instance.addresses.first],
296
+ direct_connection: true,
297
+ database: 'admin'
298
+ )
299
+ end
300
+
301
+ let(:client) do
302
+ new_local_client(
303
+ [
304
+ SpecConfig.instance.addresses.first
305
+ ],
306
+ SpecConfig.instance.test_options.merge(retry_writes: true)
307
+ )
308
+ end
309
+
310
+ before do
311
+ mongos.database.command(
312
+ configureFailPoint: 'failCommand',
313
+ mode: { times: 1 },
314
+ data: {
315
+ failCommands: %w(insert),
316
+ closeConnection: false,
317
+ errorCode: 6,
318
+ errorLabels: ['RetryableWriteError']
319
+ }
320
+ )
321
+ end
322
+
323
+ after do
324
+ mongos.database.command(
325
+ configureFailPoint: 'failCommand',
326
+ mode: 'off'
327
+ )
328
+ mongos.close
329
+ client.close
330
+ end
331
+
332
+ it 'retries on the same mongos' do
333
+ client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
334
+ expect { collection.insert_one(x: 1) }.not_to raise_error
335
+ expect(insert_started_events.map { |e| e.address.to_s }.sort).to eq([
336
+ SpecConfig.instance.addresses.first.to_s,
337
+ SpecConfig.instance.addresses.first.to_s
338
+ ])
339
+ expect(insert_failed_events.map { |e| e.address.to_s }.sort).to eq([
340
+ SpecConfig.instance.addresses.first.to_s
341
+ ])
342
+ expect(insert_succeeded_events.map { |e| e.address.to_s }.sort).to eq([
343
+ SpecConfig.instance.addresses.first.to_s
344
+ ])
345
+ end
346
+ end
347
+ end
192
348
  end
@@ -8,6 +8,8 @@ describe 'SDAM error handling' do
8
8
 
9
9
  clean_slate
10
10
 
11
+ retry_test
12
+
11
13
  after do
12
14
  # Close all clients after every test to avoid leaking expectations into
13
15
  # subsequent tests because we set global assertions on sockets.
@@ -168,16 +168,6 @@ RSpec.configure do |config|
168
168
  end
169
169
  end
170
170
 
171
- if SpecConfig.instance.ci? && !%w(1 true yes).include?(ENV['INTERACTIVE']&.downcase)
172
- # Tests should take under 10 seconds ideally but it seems
173
- # we have some that run for more than 10 seconds in CI.
174
- config.around(:each) do |example|
175
- TimeoutInterrupt.timeout(example_timeout_seconds, ExampleTimeout) do
176
- example.run
177
- end
178
- end
179
- end
180
-
181
171
  if SpecConfig.instance.ci?
182
172
  if defined?(Rfc::Rif)
183
173
  unless BSON::Environment.jruby?
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'spec_helper'
4
+ require 'support/recording_logger'
4
5
 
5
6
  # let these existing styles stand, rather than going in for a deep refactoring
6
7
  # of these specs.
@@ -84,6 +85,41 @@ describe Mongo::Cluster do
84
85
  )
85
86
  end
86
87
  end
88
+
89
+ context 'when a non-genuine host is detected' do
90
+ before { described_class.new(host_names, monitoring, logger: logger, monitoring_io: false) }
91
+
92
+ let(:logger) { RecordingLogger.new }
93
+
94
+ shared_examples 'an action that logs' do
95
+ it 'writes a warning to the log' do
96
+ expect(logger.lines).to include(a_string_matching(expected_log_output))
97
+ end
98
+ end
99
+
100
+ context 'when CosmosDB is detected' do
101
+ let(:host_names) { %w[ xyz.cosmos.azure.com ] }
102
+ let(:expected_log_output) { %r{https://www.mongodb.com/supportability/cosmosdb} }
103
+
104
+ it_behaves_like 'an action that logs'
105
+ end
106
+
107
+ context 'when DocumentDB is detected' do
108
+ let(:expected_log_output) { %r{https://www.mongodb.com/supportability/documentdb} }
109
+
110
+ context 'with docdb uri' do
111
+ let(:host_names) { [ 'xyz.docdb.amazonaws.com' ] }
112
+
113
+ it_behaves_like 'an action that logs'
114
+ end
115
+
116
+ context 'with docdb-elastic uri' do
117
+ let(:host_names) { [ 'xyz.docdb-elastic.amazonaws.com' ] }
118
+
119
+ it_behaves_like 'an action that logs'
120
+ end
121
+ end
122
+ end
87
123
  end
88
124
 
89
125
  describe '#==' do
@@ -321,7 +321,12 @@ describe Mongo::Collection::View::Aggregation do
321
321
  min_server_fcv '4.2'
322
322
 
323
323
  let(:result) do
324
- aggregation.explain['queryPlanner']['collation']['locale']
324
+ if aggregation.explain.key?('queryPlanner')
325
+ aggregation.explain['queryPlanner']['collation']['locale']
326
+ else
327
+ # 7.2+ sharded cluster
328
+ aggregation.explain['shards'].first.last['queryPlanner']['collation']['locale']
329
+ end
325
330
  end
326
331
 
327
332
  it_behaves_like 'applies the collation'
@@ -42,6 +42,7 @@ describe Mongo::Collection::View::Explainable do
42
42
  max_server_version '3.0'
43
43
 
44
44
  it 'executes the explain' do
45
+ skip 'https://jira.mongodb.org/browse/RUBY-3399'
45
46
  explain[:queryPlanner][:parsedQuery].should be_a(Hash)
46
47
  end
47
48
  end
@@ -50,6 +51,7 @@ describe Mongo::Collection::View::Explainable do
50
51
  min_server_fcv '3.2'
51
52
 
52
53
  it 'executes the explain' do
54
+ skip 'https://jira.mongodb.org/browse/RUBY-3399'
53
55
  explain[:queryPlanner][:mongosPlannerVersion].should == 1
54
56
  end
55
57
  end
@@ -4,6 +4,7 @@
4
4
  require 'spec_helper'
5
5
 
6
6
  describe Mongo::Collection do
7
+ retry_test
7
8
 
8
9
  let(:subscriber) { Mrss::EventSubscriber.new }
9
10
 
@@ -2491,7 +2492,7 @@ describe Mongo::Collection do
2491
2492
  end
2492
2493
 
2493
2494
  let(:updated) do
2494
- authorized_collection.find.to_a.last
2495
+ authorized_collection.find.sort(_id: 1).to_a.last
2495
2496
  end
2496
2497
 
2497
2498
  it 'reports that a document was written' do
@@ -177,7 +177,7 @@ describe Mongo::Operation::Insert do
177
177
  end
178
178
 
179
179
  it 'inserts the documents into the collection' do
180
- expect(authorized_collection.find.to_a). to eq(documents)
180
+ expect(authorized_collection.find.sort(_id: 1).to_a). to eq(documents)
181
181
  end
182
182
  end
183
183
 
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Mongo::Retryable::WriteWorker do
6
+ describe '#nro_write_with_retry' do
7
+ context 'when session is nil' do
8
+ let(:retryable) do
9
+ authorized_client['write_worker_test']
10
+ end
11
+
12
+ let(:write_concern) do
13
+ Mongo::WriteConcern.get(w: 0)
14
+ end
15
+
16
+ let(:write_worker) do
17
+ described_class.new(retryable)
18
+ end
19
+
20
+ let(:context) do
21
+ instance_double(Mongo::Operation::Context).tap do |context|
22
+ allow(context).to receive(:session).and_return(nil)
23
+ end
24
+ end
25
+
26
+ before do
27
+ # We avoid actual execution of the operation to speed up and simplify
28
+ # the spec.
29
+ allow(write_worker).to receive(:legacy_write_with_retry).and_return(nil)
30
+ end
31
+
32
+ it 'does not raise' do
33
+ expect do
34
+ write_worker.nro_write_with_retry(write_concern, context: context)
35
+ end.not_to raise_error
36
+ end
37
+ end
38
+ end
39
+ end