mongo 2.5.0.beta → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
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