mongo 2.5.0.beta → 2.5.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 (172) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo/address.rb +1 -1
  5. data/lib/mongo/address/unix.rb +1 -1
  6. data/lib/mongo/auth/user.rb +0 -5
  7. data/lib/mongo/auth/user/view.rb +4 -4
  8. data/lib/mongo/bulk_write.rb +60 -32
  9. data/lib/mongo/client.rb +44 -8
  10. data/lib/mongo/cluster.rb +14 -12
  11. data/lib/mongo/cluster/periodic_executor.rb +106 -0
  12. data/lib/mongo/cluster/{cursor_reaper.rb → reapers/cursor_reaper.rb} +5 -37
  13. data/lib/mongo/cluster/reapers/socket_reaper.rb +59 -0
  14. data/lib/mongo/collection.rb +9 -6
  15. data/lib/mongo/collection/view.rb +2 -2
  16. data/lib/mongo/collection/view/builder/aggregation.rb +2 -1
  17. data/lib/mongo/collection/view/builder/find_command.rb +1 -1
  18. data/lib/mongo/collection/view/change_stream.rb +14 -1
  19. data/lib/mongo/collection/view/map_reduce.rb +30 -13
  20. data/lib/mongo/collection/view/readable.rb +5 -5
  21. data/lib/mongo/collection/view/writable.rb +98 -51
  22. data/lib/mongo/error.rb +3 -0
  23. data/lib/mongo/error/invalid_txt_record.rb +27 -0
  24. data/lib/mongo/error/invalid_uri.rb +7 -6
  25. data/lib/mongo/error/mismatched_domain.rb +27 -0
  26. data/lib/mongo/error/no_srv_records.rb +26 -0
  27. data/lib/mongo/error/unsupported_features.rb +0 -18
  28. data/lib/mongo/index/view.rb +2 -2
  29. data/lib/mongo/operation.rb +1 -0
  30. data/lib/mongo/operation/causally_consistent.rb +33 -0
  31. data/lib/mongo/operation/commands.rb +2 -1
  32. data/lib/mongo/operation/commands/aggregate.rb +2 -7
  33. data/lib/mongo/operation/commands/count.rb +27 -0
  34. data/lib/mongo/operation/commands/distinct.rb +27 -0
  35. data/lib/mongo/operation/commands/find.rb +3 -1
  36. data/lib/mongo/operation/commands/map_reduce.rb +1 -0
  37. data/lib/mongo/operation/commands/parallel_scan.rb +1 -0
  38. data/lib/mongo/operation/specifiable.rb +12 -0
  39. data/lib/mongo/operation/uses_command_op_msg.rb +36 -5
  40. data/lib/mongo/operation/write.rb +0 -5
  41. data/lib/mongo/operation/write/bulk/bulkable.rb +4 -8
  42. data/lib/mongo/operation/write/bulk/mergable.rb +2 -0
  43. data/lib/mongo/operation/write/command/create_index.rb +19 -0
  44. data/lib/mongo/operation/write/command/create_user.rb +19 -0
  45. data/lib/mongo/operation/write/command/delete.rb +1 -2
  46. data/lib/mongo/operation/write/command/drop_index.rb +19 -0
  47. data/lib/mongo/operation/write/command/insert.rb +1 -2
  48. data/lib/mongo/operation/write/command/remove_user.rb +19 -0
  49. data/lib/mongo/operation/write/command/update.rb +1 -2
  50. data/lib/mongo/operation/write/command/update_user.rb +19 -0
  51. data/lib/mongo/operation/write/write_command_enabled.rb +1 -3
  52. data/lib/mongo/protocol/compressed.rb +2 -1
  53. data/lib/mongo/protocol/serializers.rb +6 -6
  54. data/lib/mongo/retryable.rb +48 -5
  55. data/lib/mongo/server.rb +15 -0
  56. data/lib/mongo/server/connection.rb +21 -1
  57. data/lib/mongo/server/connection_pool.rb +3 -0
  58. data/lib/mongo/server/connection_pool/queue.rb +50 -5
  59. data/lib/mongo/server/description.rb +11 -3
  60. data/lib/mongo/server/description/features.rb +26 -7
  61. data/lib/mongo/session.rb +133 -6
  62. data/lib/mongo/session/server_session.rb +30 -0
  63. data/lib/mongo/session/session_pool.rb +20 -20
  64. data/lib/mongo/uri.rb +88 -44
  65. data/lib/mongo/uri/srv_protocol.rb +158 -0
  66. data/lib/mongo/version.rb +1 -1
  67. data/lib/mongo/write_concern/normalizable.rb +12 -0
  68. data/mongo.gemspec +1 -2
  69. data/spec/mongo/address_spec.rb +12 -0
  70. data/spec/mongo/auth/user/view_spec.rb +1 -5
  71. data/spec/mongo/bulk_write_spec.rb +232 -401
  72. data/spec/mongo/change_stream_examples_spec.rb +150 -0
  73. data/spec/mongo/client_spec.rb +142 -2
  74. data/spec/mongo/cluster/cursor_reaper_spec.rb +0 -70
  75. data/spec/mongo/cluster/socket_reaper_spec.rb +32 -0
  76. data/spec/mongo/cluster_spec.rb +11 -7
  77. data/spec/mongo/collection/view/aggregation_spec.rb +46 -1
  78. data/spec/mongo/collection/view/builder/find_command_spec.rb +15 -0
  79. data/spec/mongo/collection/view/change_stream_spec.rb +79 -12
  80. data/spec/mongo/collection/view/map_reduce_spec.rb +120 -4
  81. data/spec/mongo/collection/view/readable_spec.rb +23 -5
  82. data/spec/mongo/collection_spec.rb +292 -102
  83. data/spec/mongo/command_monitoring_spec.rb +26 -32
  84. data/spec/mongo/crud_spec.rb +1 -1
  85. data/spec/mongo/cursor_spec.rb +2 -3
  86. data/spec/mongo/database_spec.rb +30 -14
  87. data/spec/mongo/dns_seedlist_discovery_spec.rb +94 -0
  88. data/spec/mongo/grid/fs_bucket_spec.rb +1 -1
  89. data/spec/mongo/grid/stream/write_spec.rb +1 -1
  90. data/spec/mongo/index/view_spec.rb +8 -46
  91. data/spec/mongo/operation/write/bulk/delete_spec.rb +2 -2
  92. data/spec/mongo/operation/write/bulk/insert_spec.rb +2 -10
  93. data/spec/mongo/operation/write/{create_index_spec.rb → command/create_index_spec.rb} +2 -6
  94. data/spec/mongo/operation/write/command/delete_spec.rb +35 -7
  95. data/spec/mongo/operation/write/{drop_index_spec.rb → command/drop_index_spec.rb} +1 -1
  96. data/spec/mongo/operation/write/command/insert_spec.rb +37 -6
  97. data/spec/mongo/operation/write/{remove_user_spec.rb → command/remove_user_spec.rb} +2 -6
  98. data/spec/mongo/operation/write/command/update_spec.rb +34 -7
  99. data/spec/mongo/operation/write/{update_user_spec.rb → command/update_user_spec.rb} +1 -1
  100. data/spec/mongo/operation/write/create_user_spec.rb +1 -1
  101. data/spec/mongo/operation/write/delete_spec.rb +1 -1
  102. data/spec/mongo/operation/write/insert_spec.rb +2 -10
  103. data/spec/mongo/operation/write/update_spec.rb +3 -15
  104. data/spec/mongo/retryable_spec.rb +1 -1
  105. data/spec/mongo/retryable_writes_spec.rb +815 -0
  106. data/spec/mongo/server/connection_pool/queue_spec.rb +35 -2
  107. data/spec/mongo/server/connection_pool_spec.rb +234 -1
  108. data/spec/mongo/server/connection_spec.rb +10 -6
  109. data/spec/mongo/server/description/features_spec.rb +51 -37
  110. data/spec/mongo/server/description_spec.rb +6 -3
  111. data/spec/mongo/server_spec.rb +87 -0
  112. data/spec/mongo/session/server_session_spec.rb +43 -0
  113. data/spec/mongo/session/session_pool_spec.rb +63 -27
  114. data/spec/mongo/session_spec.rb +247 -0
  115. data/spec/mongo/shell_examples_spec.rb +2 -2
  116. data/spec/mongo/uri/srv_protocol_spec.rb +933 -0
  117. data/spec/mongo/uri_spec.rb +42 -3
  118. data/spec/mongo/write_concern/acknowledged_spec.rb +11 -0
  119. data/spec/mongo/write_concern/unacknowledged_spec.rb +11 -0
  120. data/spec/spec_helper.rb +11 -25
  121. data/spec/support/authorization.rb +2 -1
  122. data/spec/support/connection_string.rb +8 -4
  123. data/spec/support/crud.rb +38 -24
  124. data/spec/support/crud/write.rb +30 -3
  125. data/spec/support/crud_tests/read/aggregate-out.yml +21 -0
  126. data/spec/support/crud_tests/write/bulkWrite-arrayFilters.yml +44 -0
  127. data/spec/support/crud_tests/write/findOneAndUpdate-arrayFilters.yml +1 -1
  128. data/spec/support/crud_tests/write/insertMany.yml +1 -3
  129. data/spec/support/crud_tests/write/replaceOne.yml +1 -1
  130. data/spec/support/crud_tests/write/updateMany-arrayFilters.yml +1 -1
  131. data/spec/support/crud_tests/write/updateOne-arrayFilters.yml +1 -1
  132. data/spec/support/dns_seedlist_discovery_tests/longer-parent-in-return.yml +11 -0
  133. data/spec/support/dns_seedlist_discovery_tests/misformatted-option.yml +5 -0
  134. data/spec/support/dns_seedlist_discovery_tests/no-results.yml +5 -0
  135. data/spec/support/dns_seedlist_discovery_tests/not-enough-parts.yml +5 -0
  136. data/spec/support/dns_seedlist_discovery_tests/one-result-default-port.yml +10 -0
  137. data/spec/support/dns_seedlist_discovery_tests/one-txt-record-multiple-strings.yml +10 -0
  138. data/spec/support/dns_seedlist_discovery_tests/one-txt-record.yml +11 -0
  139. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch1.yml +5 -0
  140. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch2.yml +5 -0
  141. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch3.yml +5 -0
  142. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch4.yml +5 -0
  143. data/spec/support/dns_seedlist_discovery_tests/parent-part-mismatch5.yml +5 -0
  144. data/spec/support/dns_seedlist_discovery_tests/returned-parent-too-short.yml +5 -0
  145. data/spec/support/dns_seedlist_discovery_tests/returned-parent-wrong.yml +5 -0
  146. data/spec/support/dns_seedlist_discovery_tests/two-results-default-port.yml +11 -0
  147. data/spec/support/dns_seedlist_discovery_tests/two-results-nonstandard-port.yml +11 -0
  148. data/spec/support/dns_seedlist_discovery_tests/two-txt-records.yml +5 -0
  149. data/spec/support/dns_seedlist_discovery_tests/txt-record-not-allowed-option.yml +5 -0
  150. data/spec/support/dns_seedlist_discovery_tests/txt-record-with-overridden-ssl-option.yml +11 -0
  151. data/spec/support/dns_seedlist_discovery_tests/txt-record-with-overridden-uri-option.yml +11 -0
  152. data/spec/support/dns_seedlist_discovery_tests/txt-record-with-unallowed-option.yml +5 -0
  153. data/spec/support/dns_seedlist_discovery_tests/uri-with-port.yml +5 -0
  154. data/spec/support/dns_seedlist_discovery_tests/uri-with-two-hosts.yml +5 -0
  155. data/spec/support/retryable_writes_tests/bulkWrite.yml +305 -0
  156. data/spec/support/retryable_writes_tests/deleteOne.yml +51 -0
  157. data/spec/support/retryable_writes_tests/findOneAndDelete.yml +52 -0
  158. data/spec/support/retryable_writes_tests/findOneAndReplace.yml +57 -0
  159. data/spec/support/retryable_writes_tests/findOneAndUpdate.yml +56 -0
  160. data/spec/support/retryable_writes_tests/insertMany.yml +72 -0
  161. data/spec/support/retryable_writes_tests/insertOne.yml +55 -0
  162. data/spec/support/retryable_writes_tests/replaceOne.yml +60 -0
  163. data/spec/support/retryable_writes_tests/updateOne.yml +120 -0
  164. data/spec/support/shared/session.rb +525 -24
  165. metadata +437 -350
  166. metadata.gz.sig +0 -0
  167. data/lib/mongo/operation/commands/user_query.rb +0 -72
  168. data/lib/mongo/operation/write/create_index.rb +0 -67
  169. data/lib/mongo/operation/write/create_user.rb +0 -50
  170. data/lib/mongo/operation/write/drop_index.rb +0 -63
  171. data/lib/mongo/operation/write/remove_user.rb +0 -48
  172. data/lib/mongo/operation/write/update_user.rb +0 -50
@@ -29,7 +29,7 @@ describe Mongo::Retryable do
29
29
  end
30
30
 
31
31
  def write
32
- write_with_retry(nil, Proc.new { cluster.next_primary }) do
32
+ write_with_retry(nil, nil) do
33
33
  operation.execute
34
34
  end
35
35
  end
@@ -0,0 +1,815 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Retryable Writes' do
4
+
5
+ RETRYABLE_WRITES_TESTS.each do |file|
6
+
7
+ spec = Mongo::CRUD::Spec.new(file)
8
+
9
+ context(spec.description) do
10
+
11
+ spec.tests.each do |test|
12
+
13
+ context(test.description) do
14
+
15
+ let(:collection) do
16
+ client[TEST_COLL]
17
+ end
18
+
19
+ let(:client) do
20
+ authorized_client.with(heartbeat_frequency: 100, retry_writes: true).tap do |cl|
21
+ cl.subscribe(Mongo::Monitoring::COMMAND, subscriber)
22
+ end
23
+ end
24
+
25
+ let(:subscriber) do
26
+ EventSubscriber.new
27
+ end
28
+
29
+ before do
30
+ test.setup_test(collection)
31
+ end
32
+
33
+ after do
34
+ test.clear_fail_point(collection)
35
+ collection.delete_many
36
+ end
37
+
38
+ let(:results) do
39
+ if test.error?
40
+ error = nil
41
+ begin; test.run(collection); rescue => e; error = e; end
42
+ error
43
+ else
44
+ test.run(collection)
45
+ end
46
+ end
47
+
48
+ it 'has the correct data in the collection', if: (sessions_enabled? && replica_set? && test.outcome_collection_data) do
49
+ skip 'Test cannot be run on this server version' unless spec.server_version_satisfied?(client)
50
+ results
51
+ expect(collection.find.to_a).to match_collection_data(test)
52
+ end
53
+
54
+ if test.error?
55
+ it 'raises an error', if: sessions_enabled? && replica_set? do
56
+ skip 'Test cannot be run on this server version' unless spec.server_version_satisfied?(client)
57
+ expect(results).to be_a(Mongo::Error)
58
+ end
59
+ else
60
+ it 'returns the correct result', if: sessions_enabled? && replica_set? do
61
+ skip 'Test cannot be run on this server version' unless spec.server_version_satisfied?(client)
62
+ expect(results).to match_operation_result(test)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ describe 'Retryable writes integration tests' do
71
+
72
+ let(:primary) do
73
+ primary = client.cluster.next_primary
74
+ end
75
+
76
+ let(:primary_connection) do
77
+ connection = primary.pool.checkout
78
+ connection.connect!
79
+ primary.pool.checkin(connection)
80
+ connection
81
+ end
82
+
83
+ let(:primary_socket) do
84
+ primary_connection.send(:socket)
85
+ end
86
+
87
+ after do
88
+ authorized_collection.delete_many
89
+ end
90
+
91
+ shared_examples_for 'an operation that is retried' do
92
+
93
+ context 'when the operation fails on the first attempt' do
94
+
95
+ before do
96
+ # Note that for writes, server.connectable? is called, refreshing the socket
97
+ allow(primary).to receive(:connectable?).and_return(true)
98
+ expect(primary_socket).to receive(:write).and_raise(error)
99
+ end
100
+
101
+ context 'when the error is retryable' do
102
+
103
+ before do
104
+ expect(Mongo::Logger.logger).to receive(:warn).once.and_call_original
105
+ expect(client.cluster).to receive(:scan!)
106
+ end
107
+
108
+ context 'when the error is a SocketError' do
109
+
110
+ let(:error) do
111
+ Mongo::Error::SocketError
112
+ end
113
+
114
+ it 'retries writes' do
115
+ operation
116
+ expect(expectation).to eq(successful_retry_value)
117
+ end
118
+ end
119
+
120
+ context 'when the error is a SocketTimeoutError' do
121
+
122
+ let(:error) do
123
+ Mongo::Error::SocketTimeoutError
124
+ end
125
+
126
+ it 'retries writes' do
127
+ operation
128
+ expect(expectation).to eq(successful_retry_value)
129
+ end
130
+ end
131
+
132
+ context 'when the error is a retryable OperationFailure' do
133
+
134
+ let(:error) do
135
+ Mongo::Error::OperationFailure.new('not master')
136
+ end
137
+
138
+ it 'retries writes' do
139
+ operation
140
+ expect(expectation).to eq(successful_retry_value)
141
+ end
142
+ end
143
+ end
144
+
145
+ context 'when the error is not retryable' do
146
+
147
+ context 'when the error is a non-retryable OperationFailure' do
148
+
149
+ let(:error) do
150
+ Mongo::Error::OperationFailure.new('other error')
151
+ end
152
+
153
+ it 'does not retry writes' do
154
+ expect {
155
+ operation
156
+ }.to raise_error(error)
157
+ expect(expectation).to eq(unsuccessful_retry_value)
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ context 'when the operation fails on the first attempt and again on the second attempt' do
164
+
165
+ before do
166
+ # Note that for writes, server.connectable? is called, refreshing the socket
167
+ allow(primary).to receive(:connectable?).and_return(true)
168
+ allow(primary_socket).to receive(:write).and_raise(error)
169
+ end
170
+
171
+ context 'when the selected server does not support retryable writes' do
172
+
173
+ before do
174
+ legacy_primary = double('legacy primary', :'retry_writes?' => false)
175
+ allow(client.cluster).to receive(:next_primary).and_return(primary, legacy_primary)
176
+ expect(primary_socket).to receive(:write).and_raise(error)
177
+ end
178
+
179
+ context 'when the error is a SocketError' do
180
+
181
+ let(:error) do
182
+ Mongo::Error::SocketError
183
+ end
184
+
185
+ it 'does not retry writes and raises the original error' do
186
+ expect {
187
+ operation
188
+ }.to raise_error(error)
189
+ expect(expectation).to eq(unsuccessful_retry_value)
190
+ end
191
+ end
192
+
193
+ context 'when the error is a SocketTimeoutError' do
194
+
195
+ let(:error) do
196
+ Mongo::Error::SocketTimeoutError
197
+ end
198
+
199
+ it 'does not retry writes and raises the original error' do
200
+ expect {
201
+ operation
202
+ }.to raise_error(error)
203
+ expect(expectation).to eq(unsuccessful_retry_value)
204
+ end
205
+ end
206
+
207
+ context 'when the error is a retryable OperationFailure' do
208
+
209
+ let(:error) do
210
+ Mongo::Error::OperationFailure.new('not master')
211
+ end
212
+
213
+ it 'does not retry writes and raises the original error' do
214
+ expect {
215
+ operation
216
+ }.to raise_error(error)
217
+ expect(expectation).to eq(unsuccessful_retry_value)
218
+ end
219
+ end
220
+ end
221
+
222
+ [Mongo::Error::SocketError,
223
+ Mongo::Error::SocketTimeoutError,
224
+ Mongo::Error::OperationFailure.new('not master')].each do |retryable_error|
225
+
226
+ context "when the first error is a #{retryable_error}" do
227
+
228
+ let(:error) do
229
+ retryable_error
230
+ end
231
+
232
+ before do
233
+ bad_socket = primary_connection.address.socket(primary_connection.socket_timeout,
234
+ primary_connection.send(:ssl_options))
235
+ good_socket = primary_connection.address.socket(primary_connection.socket_timeout,
236
+ primary_connection.send(:ssl_options))
237
+ allow(bad_socket).to receive(:write).and_raise(second_error)
238
+ allow(primary_connection.address).to receive(:socket).and_return(bad_socket, good_socket)
239
+ end
240
+
241
+ context 'when the second error is a SocketError' do
242
+
243
+ let(:second_error) do
244
+ Mongo::Error::SocketError
245
+ end
246
+
247
+ before do
248
+ expect(client.cluster).to receive(:scan!).twice
249
+ end
250
+
251
+ it 'does not retry writes and raises the second error' do
252
+ expect {
253
+ operation
254
+ }.to raise_error(second_error)
255
+ expect(expectation).to eq(unsuccessful_retry_value)
256
+ end
257
+ end
258
+
259
+ context 'when the second error is a SocketTimeoutError' do
260
+
261
+ before do
262
+ expect(client.cluster).to receive(:scan!).twice
263
+ end
264
+
265
+ let(:second_error) do
266
+ Mongo::Error::SocketTimeoutError
267
+ end
268
+
269
+ it 'does not retry writes and raises the second error' do
270
+ expect {
271
+ operation
272
+ }.to raise_error(second_error)
273
+ expect(expectation).to eq(unsuccessful_retry_value)
274
+ end
275
+ end
276
+
277
+ context 'when the second error is a retryable OperationFailure' do
278
+
279
+ before do
280
+ expect(client.cluster).to receive(:scan!).twice
281
+ end
282
+
283
+ let(:second_error) do
284
+ Mongo::Error::OperationFailure.new('not master')
285
+ end
286
+
287
+ it 'does not retry writes and raises the second error' do
288
+ expect {
289
+ operation
290
+ }.to raise_error(second_error)
291
+ expect(expectation).to eq(unsuccessful_retry_value)
292
+ end
293
+ end
294
+
295
+ context 'when the second error is a non-retryable OperationFailure' do
296
+
297
+ before do
298
+ expect(client.cluster).to receive(:scan!).once
299
+ end
300
+
301
+ let(:second_error) do
302
+ Mongo::Error::OperationFailure.new('other error')
303
+ end
304
+
305
+ it 'does not retry writes and raises the first error' do
306
+ expect {
307
+ operation
308
+ }.to raise_error(error)
309
+ expect(expectation).to eq(unsuccessful_retry_value)
310
+ end
311
+ end
312
+
313
+ context 'when the second error is a another error' do
314
+
315
+ let(:second_error) do
316
+ StandardError
317
+ end
318
+
319
+ it 'does not retry writes and raises the first error' do
320
+ expect {
321
+ operation
322
+ }.to raise_error(error)
323
+ expect(expectation).to eq(unsuccessful_retry_value)
324
+ end
325
+ end
326
+ end
327
+ end
328
+ end
329
+ end
330
+
331
+ shared_examples_for 'an operation that is not retried' do
332
+
333
+ before do
334
+ # Note that for writes, server.connectable? is called, refreshing the socket
335
+ allow(primary).to receive(:connectable?).and_return(true)
336
+ expect(primary_socket).to receive(:write).and_raise(Mongo::Error::SocketError)
337
+ expect(client.cluster).not_to receive(:scan!)
338
+ end
339
+
340
+ it 'does not retry writes' do
341
+ expect {
342
+ operation
343
+ }.to raise_error(Mongo::Error::SocketError)
344
+ expect(expectation).to eq(unsuccessful_retry_value)
345
+ end
346
+ end
347
+
348
+ shared_examples_for 'an operation that does not support retryable writes' do
349
+
350
+ let!(:client) do
351
+ authorized_client.with(retry_writes: true)
352
+ end
353
+
354
+ let!(:collection) do
355
+ client[TEST_COLL, write: WRITE_CONCERN]
356
+ end
357
+
358
+ before do
359
+ # Note that for writes, server.connectable? is called, refreshing the socket
360
+ allow(primary).to receive(:connectable?).and_return(true)
361
+ expect(primary_socket).to receive(:write).and_raise(Mongo::Error::SocketError)
362
+ expect(client.cluster).not_to receive(:scan!)
363
+ end
364
+
365
+ it 'does not retry writes' do
366
+ expect {
367
+ operation
368
+ }.to raise_error(Mongo::Error::SocketError)
369
+ expect(expectation).to eq(unsuccessful_retry_value)
370
+ end
371
+ end
372
+
373
+ shared_examples_for 'supported retryable writes' do
374
+
375
+ context 'when the client has retry_writes set to true' do
376
+
377
+ let!(:client) do
378
+ authorized_client.with(retry_writes: true)
379
+ end
380
+
381
+ context 'when the collection has write concern acknowledged' do
382
+
383
+ let!(:collection) do
384
+ client[TEST_COLL, write: WRITE_CONCERN]
385
+ end
386
+
387
+ context 'when the server supports retryable writes' do
388
+
389
+ before do
390
+ allow(primary).to receive(:retry_writes?).and_return(true)
391
+ end
392
+
393
+ if standalone? && sessions_enabled?
394
+ it_behaves_like 'an operation that is not retried'
395
+ elsif sessions_enabled?
396
+ it_behaves_like 'an operation that is retried'
397
+ end
398
+ end
399
+
400
+ context 'when the server does not support retryable writes' do
401
+
402
+ before do
403
+ allow(primary).to receive(:retry_writes?).and_return(false)
404
+ end
405
+
406
+ it_behaves_like 'an operation that is not retried'
407
+ end
408
+ end
409
+
410
+ context 'when the collection has write concern unacknowledged' do
411
+
412
+ let!(:collection) do
413
+ client[TEST_COLL, write: { w: 0 }]
414
+ end
415
+
416
+ it_behaves_like 'an operation that is not retried'
417
+ end
418
+
419
+ context 'when the collection has write concern not set' do
420
+
421
+ let!(:collection) do
422
+ client[TEST_COLL]
423
+ end
424
+
425
+ context 'when the server supports retryable writes' do
426
+
427
+ before do
428
+ allow(primary).to receive(:retry_writes?).and_return(true)
429
+ end
430
+
431
+ if standalone? && sessions_enabled?
432
+ it_behaves_like 'an operation that is not retried'
433
+ elsif sessions_enabled?
434
+ it_behaves_like 'an operation that is retried'
435
+ end
436
+ end
437
+
438
+ context 'when the server does not support retryable writes' do
439
+
440
+ before do
441
+ allow(primary).to receive(:retry_writes?).and_return(false)
442
+ end
443
+
444
+ it_behaves_like 'an operation that is not retried'
445
+ end
446
+ end
447
+ end
448
+
449
+ context 'when the client has retry_writes set to false' do
450
+
451
+ let!(:client) do
452
+ authorized_client.with(retry_writes: false)
453
+ end
454
+
455
+ context 'when the collection has write concern acknowledged' do
456
+
457
+ let!(:collection) do
458
+ client[TEST_COLL, write: WRITE_CONCERN]
459
+ end
460
+
461
+ it_behaves_like 'an operation that is not retried'
462
+ end
463
+
464
+ context 'when the collection has write concern unacknowledged' do
465
+
466
+ let!(:collection) do
467
+ client[TEST_COLL, write: { w: 0 }]
468
+ end
469
+
470
+ it_behaves_like 'an operation that is not retried'
471
+ end
472
+
473
+ context 'when the collection has write concern not set' do
474
+
475
+ let!(:collection) do
476
+ client[TEST_COLL]
477
+ end
478
+
479
+ it_behaves_like 'an operation that is not retried'
480
+ end
481
+ end
482
+
483
+ context 'when the client has retry_writes not set' do
484
+
485
+ let!(:client) do
486
+ authorized_client
487
+ end
488
+
489
+ context 'when the collection has write concern acknowledged' do
490
+
491
+ let!(:collection) do
492
+ client[TEST_COLL, write: WRITE_CONCERN]
493
+ end
494
+
495
+ it_behaves_like 'an operation that is not retried'
496
+ end
497
+
498
+ context 'when the collection has write concern unacknowledged' do
499
+
500
+ let!(:collection) do
501
+ client[TEST_COLL, write: { w: 0 }]
502
+ end
503
+
504
+ it_behaves_like 'an operation that is not retried'
505
+ end
506
+
507
+ context 'when the collection has write concern not set' do
508
+
509
+ let!(:collection) do
510
+ client[TEST_COLL]
511
+ end
512
+
513
+ it_behaves_like 'an operation that is not retried'
514
+ end
515
+ end
516
+ end
517
+
518
+ context 'when the operation is insert_one' do
519
+
520
+ let(:operation) do
521
+ collection.insert_one(a:1)
522
+ end
523
+
524
+ let(:expectation) do
525
+ collection.find(a: 1).count
526
+ end
527
+
528
+ let(:successful_retry_value) do
529
+ 1
530
+ end
531
+
532
+ let(:unsuccessful_retry_value) do
533
+ 0
534
+ end
535
+
536
+ it_behaves_like 'supported retryable writes'
537
+ end
538
+
539
+ context 'when the operation is update_one' do
540
+
541
+ before do
542
+ # Account for when the collection has unacknowledged write concern and use authorized_collection here.
543
+ authorized_collection.insert_one(a:0)
544
+ end
545
+
546
+ let(:operation) do
547
+ collection.update_one({ a: 0 }, { '$set' => { a: 1 } })
548
+ end
549
+
550
+ let(:expectation) do
551
+ collection.find(a: 1).count
552
+ end
553
+
554
+ let(:successful_retry_value) do
555
+ 1
556
+ end
557
+
558
+ let(:unsuccessful_retry_value) do
559
+ 0
560
+ end
561
+
562
+ it_behaves_like 'supported retryable writes'
563
+ end
564
+
565
+ context 'when the operation is replace_one' do
566
+
567
+ before do
568
+ # Account for when the collection has unacknowledged write concern and use authorized_collection here.
569
+ authorized_collection.insert_one(a:0)
570
+ end
571
+
572
+ let(:operation) do
573
+ collection.replace_one({ a: 0 }, { a: 1 })
574
+ end
575
+
576
+ let(:expectation) do
577
+ collection.find(a: 1).count
578
+ end
579
+
580
+ let(:successful_retry_value) do
581
+ 1
582
+ end
583
+
584
+ let(:unsuccessful_retry_value) do
585
+ 0
586
+ end
587
+
588
+ it_behaves_like 'supported retryable writes'
589
+ end
590
+
591
+ context 'when the operation is delete_one' do
592
+
593
+ before do
594
+ # Account for when the collection has unacknowledged write concern and use authorized_collection here.
595
+ authorized_collection.insert_one(a:1)
596
+ end
597
+
598
+ let(:operation) do
599
+ collection.delete_one(a:1)
600
+ end
601
+
602
+ let(:expectation) do
603
+ collection.find(a: 1).count
604
+ end
605
+
606
+ let(:successful_retry_value) do
607
+ 0
608
+ end
609
+
610
+ let(:unsuccessful_retry_value) do
611
+ 1
612
+ end
613
+
614
+ it_behaves_like 'supported retryable writes'
615
+ end
616
+
617
+ context 'when the operation is find_one_and_update' do
618
+
619
+ before do
620
+ # Account for when the collection has unacknowledged write concern and use authorized_collection here.
621
+ authorized_collection.insert_one(a:0)
622
+ end
623
+
624
+ let(:operation) do
625
+ collection.find_one_and_update({ a: 0 }, { '$set' => { a: 1 } })
626
+ end
627
+
628
+ let(:expectation) do
629
+ collection.find(a: 1).count
630
+ end
631
+
632
+ let(:successful_retry_value) do
633
+ 1
634
+ end
635
+
636
+ let(:unsuccessful_retry_value) do
637
+ 0
638
+ end
639
+
640
+ it_behaves_like 'supported retryable writes'
641
+ end
642
+
643
+ context 'when the operation is find_one_and_replace' do
644
+
645
+ before do
646
+ # Account for when the collection has unacknowledged write concern and use authorized_collection here.
647
+ authorized_collection.insert_one(a:0)
648
+ end
649
+
650
+ let(:operation) do
651
+ collection.find_one_and_replace({ a: 0 }, { a: 3 })
652
+ end
653
+
654
+ let(:expectation) do
655
+ collection.find(a: 3).count
656
+ end
657
+
658
+ let(:successful_retry_value) do
659
+ 1
660
+ end
661
+
662
+ let(:unsuccessful_retry_value) do
663
+ 0
664
+ end
665
+
666
+ it_behaves_like 'supported retryable writes'
667
+ end
668
+
669
+ context 'when the operation is find_one_and_delete' do
670
+
671
+ before do
672
+ # Account for when the collection has unacknowledged write concern and use authorized_collection here.
673
+ authorized_collection.insert_one(a:1)
674
+ end
675
+
676
+ let(:operation) do
677
+ collection.find_one_and_delete({ a: 1 })
678
+ end
679
+
680
+ let(:expectation) do
681
+ collection.find(a: 1).count
682
+ end
683
+
684
+ let(:successful_retry_value) do
685
+ 0
686
+ end
687
+
688
+ let(:unsuccessful_retry_value) do
689
+ 1
690
+ end
691
+
692
+ it_behaves_like 'supported retryable writes'
693
+ end
694
+
695
+ context 'when the operation is update_many' do
696
+
697
+ before do
698
+ # Account for when the collection has unacknowledged write concern and use authorized_collection here.
699
+ authorized_collection.insert_one(a:0)
700
+ authorized_collection.insert_one(a:0)
701
+ end
702
+
703
+ let(:operation) do
704
+ collection.update_many({ a: 0 }, { '$set' => { a: 1 } })
705
+ end
706
+
707
+ let(:expectation) do
708
+ collection.find(a: 1).count
709
+ end
710
+
711
+ let(:unsuccessful_retry_value) do
712
+ 0
713
+ end
714
+
715
+ it_behaves_like 'an operation that does not support retryable writes'
716
+ end
717
+
718
+ context 'when the operation is delete_many' do
719
+
720
+ before do
721
+ # Account for when the collection has unacknowledged write concern and use authorized_collection here.
722
+ authorized_collection.insert_one(a:1)
723
+ authorized_collection.insert_one(a:1)
724
+ end
725
+
726
+ let(:operation) do
727
+ collection.delete_many(a: 1)
728
+ end
729
+
730
+ let(:expectation) do
731
+ collection.find(a: 1).count
732
+ end
733
+
734
+ let(:unsuccessful_retry_value) do
735
+ 2
736
+ end
737
+
738
+ it_behaves_like 'an operation that does not support retryable writes'
739
+ end
740
+
741
+ context 'when the operation is a bulk write' do
742
+
743
+ before do
744
+ # Account for when the collection has unacknowledged write concern and use authorized_collection here.
745
+ authorized_collection.insert_one(a: 1)
746
+ end
747
+
748
+ let(:operation) do
749
+ collection.bulk_write([{ delete_one: { filter: { a: 1 } } },
750
+ { insert_one: { a: 1 } },
751
+ { insert_one: { a: 1 } }])
752
+ end
753
+
754
+ let(:expectation) do
755
+ collection.find(a: 1).count
756
+ end
757
+
758
+ let(:successful_retry_value) do
759
+ 2
760
+ end
761
+
762
+ let(:unsuccessful_retry_value) do
763
+ 1
764
+ end
765
+
766
+ it_behaves_like 'supported retryable writes'
767
+ end
768
+
769
+ context 'when the operation is bulk write including delete_many' do
770
+
771
+ before do
772
+ # Account for when the collection has unacknowledged write concern and use authorized_collection here.
773
+ authorized_collection.insert_one(a:1)
774
+ authorized_collection.insert_one(a:1)
775
+ end
776
+
777
+ let(:operation) do
778
+ collection.bulk_write([{ delete_many: { filter: { a: 1 } } }])
779
+ end
780
+
781
+ let(:expectation) do
782
+ collection.find(a: 1).count
783
+ end
784
+
785
+ let(:unsuccessful_retry_value) do
786
+ 2
787
+ end
788
+
789
+ it_behaves_like 'an operation that does not support retryable writes'
790
+ end
791
+
792
+ context 'when the operation is bulk write including update_many' do
793
+
794
+ before do
795
+ # Account for when the collection has unacknowledged write concern and use authorized_collection here.
796
+ authorized_collection.insert_one(a:0)
797
+ authorized_collection.insert_one(a:0)
798
+ end
799
+
800
+ let(:operation) do
801
+ collection.bulk_write([{ update_many: { filter: { a: 0 }, update: { a: 1 } } }])
802
+ end
803
+
804
+ let(:expectation) do
805
+ collection.find(a: 1).count
806
+ end
807
+
808
+ let(:unsuccessful_retry_value) do
809
+ 0
810
+ end
811
+
812
+ it_behaves_like 'an operation that does not support retryable writes'
813
+ end
814
+ end
815
+ end