mongo 2.10.2 → 2.10.3

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 (35) 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/collection/view/readable.rb +2 -2
  5. data/lib/mongo/cursor/builder/get_more_command.rb +4 -1
  6. data/lib/mongo/cursor/builder/kill_cursors_command.rb +16 -5
  7. data/lib/mongo/cursor/builder/op_get_more.rb +2 -2
  8. data/lib/mongo/cursor/builder/op_kill_cursors.rb +17 -5
  9. data/lib/mongo/error/operation_failure.rb +3 -3
  10. data/lib/mongo/protocol/get_more.rb +2 -1
  11. data/lib/mongo/protocol/kill_cursors.rb +6 -13
  12. data/lib/mongo/protocol/serializers.rb +10 -4
  13. data/lib/mongo/retryable.rb +34 -9
  14. data/lib/mongo/version.rb +1 -1
  15. data/mongo.gemspec +1 -1
  16. data/spec/integration/cursor_reaping_spec.rb +1 -1
  17. data/spec/integration/get_more_spec.rb +32 -0
  18. data/spec/integration/retryable_errors_spec.rb +265 -0
  19. data/spec/integration/retryable_writes_spec.rb +36 -36
  20. data/spec/mongo/bulk_write_spec.rb +2 -2
  21. data/spec/mongo/cursor/builder/get_more_command_spec.rb +4 -2
  22. data/spec/mongo/cursor/builder/op_get_more_spec.rb +4 -2
  23. data/spec/mongo/cursor_spec.rb +3 -3
  24. data/spec/mongo/retryable_spec.rb +31 -52
  25. data/spec/runners/sdam/verifier.rb +88 -0
  26. data/spec/spec_tests/retryable_reads_spec.rb +31 -0
  27. data/spec/spec_tests/sdam_monitoring_spec.rb +9 -4
  28. data/spec/support/cluster_tools.rb +4 -3
  29. data/spec/support/command_monitoring.rb +5 -0
  30. data/spec/support/event_subscriber.rb +7 -0
  31. data/spec/support/sdam_monitoring.rb +0 -115
  32. data/spec/support/spec_config.rb +1 -1
  33. data/spec/support/utils.rb +1 -1
  34. metadata +10 -4
  35. metadata.gz.sig +0 -0
@@ -0,0 +1,265 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Failing retryable operations' do
4
+ # Requirement for fail point
5
+ min_server_fcv '4.0'
6
+
7
+ let(:client) do
8
+ subscribed_client
9
+ end
10
+
11
+ let(:collection) do
12
+ client['retryable-errors-spec']
13
+ end
14
+
15
+ context 'when operation fails' do
16
+ require_topology :replica_set
17
+
18
+
19
+ let(:clear_fail_point_command) do
20
+ {
21
+ configureFailPoint: 'failCommand',
22
+ mode: 'off',
23
+ }
24
+ end
25
+
26
+ after do
27
+ ClusterTools.instance.direct_client_for_each_server do |client|
28
+ client.use(:admin).database.command(clear_fail_point_command)
29
+ end
30
+ end
31
+
32
+ let(:collection) do
33
+ client['retryable-errors-spec', read: {mode: :secondary_preferred}]
34
+ end
35
+
36
+ let(:first_server) do
37
+ client.cluster.servers_list.detect do |server|
38
+ server.address.seed == events.first.address.seed
39
+ end
40
+ end
41
+
42
+ let(:second_server) do
43
+ client.cluster.servers_list.detect do |server|
44
+ server.address.seed == events.last.address.seed
45
+ end
46
+ end
47
+
48
+ shared_context 'read operation' do
49
+ let(:fail_point_command) do
50
+ {
51
+ configureFailPoint: 'failCommand',
52
+ mode: {times: 1},
53
+ data: {
54
+ failCommands: ['find'],
55
+ errorCode: 11600,
56
+ },
57
+ }
58
+ end
59
+
60
+ let(:set_fail_point) do
61
+ client.cluster.servers_list.each do |server|
62
+ server.scan!
63
+ server.monitor.stop!
64
+ end
65
+
66
+ ClusterTools.instance.direct_client_for_each_server do |client|
67
+ client.use(:admin).database.command(fail_point_command)
68
+ end
69
+ end
70
+
71
+ let(:operation_exception) do
72
+ set_fail_point
73
+
74
+ begin
75
+ collection.find(a: 1).to_a
76
+ rescue Mongo::Error::OperationFailure => exception
77
+ else
78
+ fail('Expected operation to fail')
79
+ end
80
+
81
+ puts exception.message
82
+
83
+ exception
84
+ end
85
+
86
+ let(:events) do
87
+ EventSubscriber.command_started_events('find')
88
+ end
89
+ end
90
+
91
+ shared_context 'write operation' do
92
+ let(:fail_point_command) do
93
+ {
94
+ configureFailPoint: 'failCommand',
95
+ mode: {times: 2},
96
+ data: {
97
+ failCommands: ['insert'],
98
+ errorCode: 11600,
99
+ },
100
+ }
101
+ end
102
+
103
+ let(:set_fail_point) do
104
+ client.use(:admin).database.command(fail_point_command)
105
+ end
106
+
107
+ let(:operation_exception) do
108
+ set_fail_point
109
+
110
+ begin
111
+ collection.insert_one(a: 1)
112
+ rescue Mongo::Error::OperationFailure => exception
113
+ else
114
+ fail('Expected operation to fail')
115
+ end
116
+
117
+ #puts exception.message
118
+
119
+ exception
120
+ end
121
+
122
+ let(:events) do
123
+ EventSubscriber.command_started_events('insert')
124
+ end
125
+ end
126
+
127
+ shared_examples_for 'failing retry' do
128
+
129
+ it 'indicates second attempt' do
130
+ expect(operation_exception.message).to include('attempt 2')
131
+ expect(operation_exception.message).not_to include('attempt 1')
132
+ expect(operation_exception.message).not_to include('attempt 3')
133
+ end
134
+
135
+ it 'publishes two events' do
136
+
137
+ expect(events.length).to eq(2)
138
+ end
139
+ end
140
+
141
+ shared_examples_for 'failing single attempt' do
142
+
143
+ it 'does not indicate attempt' do
144
+ expect(operation_exception.message).not_to include('attempt 1')
145
+ expect(operation_exception.message).not_to include('attempt 2')
146
+ expect(operation_exception.message).not_to include('attempt 3')
147
+ end
148
+
149
+ it 'publishes one event' do
150
+
151
+ expect(events.length).to eq(1)
152
+ end
153
+ end
154
+
155
+ shared_examples_for 'failing retry on the same server' do
156
+ it 'is reported on the server of the second attempt' do
157
+ expect(operation_exception.message).to include(second_server.address.seed)
158
+ end
159
+ end
160
+
161
+ shared_examples_for 'failing retry on a different server' do
162
+ it 'is reported on the server of the second attempt' do
163
+ expect(operation_exception.message).not_to include(first_server.address.seed)
164
+ expect(operation_exception.message).to include(second_server.address.seed)
165
+ end
166
+
167
+ it 'marks servers used in both attempts unknown' do
168
+ operation_exception
169
+
170
+ expect(first_server).to be_unknown
171
+
172
+ expect(second_server).to be_unknown
173
+ end
174
+
175
+ it 'publishes events for the different server addresses' do
176
+
177
+ expect(events.length).to eq(2)
178
+ expect(events.first.address.seed).not_to eq(events.last.address.seed)
179
+ end
180
+ end
181
+
182
+ shared_examples_for 'modern retry' do
183
+ it 'indicates modern retry' do
184
+ expect(operation_exception.message).to include('modern retry')
185
+ expect(operation_exception.message).not_to include('legacy retry')
186
+ expect(operation_exception.message).not_to include('retries disabled')
187
+ end
188
+ end
189
+
190
+ shared_examples_for 'legacy retry' do
191
+ it 'indicates legacy retry' do
192
+ expect(operation_exception.message).to include('legacy retry')
193
+ expect(operation_exception.message).not_to include('modern retry')
194
+ expect(operation_exception.message).not_to include('retries disabled')
195
+ end
196
+ end
197
+
198
+ shared_examples_for 'disabled retry' do
199
+ it 'indicates retries are disabled' do
200
+ expect(operation_exception.message).to include('retries disabled')
201
+ expect(operation_exception.message).not_to include('legacy retry')
202
+ expect(operation_exception.message).not_to include('modern retry')
203
+ end
204
+ end
205
+
206
+ context 'when read is retried and retry fails' do
207
+ include_context 'read operation'
208
+
209
+ context 'modern read retries' do
210
+ require_wired_tiger_on_36
211
+
212
+ let(:client) do
213
+ subscribed_client.with(retry_reads: true)
214
+ end
215
+
216
+ it_behaves_like 'failing retry'
217
+ it_behaves_like 'modern retry'
218
+ end
219
+
220
+ context 'legacy read retries' do
221
+ let(:client) do
222
+ subscribed_client.with(retry_reads: false, read_retry_interval: 0)
223
+ end
224
+
225
+ it_behaves_like 'failing retry'
226
+ it_behaves_like 'legacy retry'
227
+ end
228
+ end
229
+
230
+ context 'when read retries are disabled' do
231
+ let(:client) do
232
+ subscribed_client.with(retry_reads: false, max_read_retries: 0)
233
+ end
234
+
235
+ include_context 'read operation'
236
+
237
+ it_behaves_like 'failing single attempt'
238
+ it_behaves_like 'disabled retry'
239
+ end
240
+
241
+ context 'when write is retried and retry fails' do
242
+ include_context 'write operation'
243
+
244
+ context 'modern write retries' do
245
+ require_wired_tiger_on_36
246
+
247
+ let(:client) do
248
+ subscribed_client.with(retry_writes: true)
249
+ end
250
+
251
+ it_behaves_like 'failing retry'
252
+ it_behaves_like 'modern retry'
253
+ end
254
+
255
+ context 'legacy write' do
256
+ let(:client) do
257
+ subscribed_client.with(retry_writes: false)
258
+ end
259
+
260
+ it_behaves_like 'failing retry'
261
+ it_behaves_like 'legacy retry'
262
+ end
263
+ end
264
+ end
265
+ end
@@ -102,22 +102,22 @@ describe 'Retryable writes integration tests' do
102
102
  end
103
103
 
104
104
  it 'does not retry writes' do
105
- expect {
105
+ expect do
106
106
  operation
107
- }.to raise_error(Mongo::Error::OperationFailure, /other error/)
107
+ end.to raise_error(Mongo::Error::OperationFailure, /other error/)
108
108
  expect(expectation).to eq(unsuccessful_retry_value)
109
109
  end
110
110
 
111
111
  it 'indicates server used for operation' do
112
- expect {
112
+ expect do
113
113
  operation
114
- }.to raise_error(Mongo::Error::OperationFailure, /on #{ClusterConfig.instance.primary_address_str}/)
114
+ end.to raise_error(Mongo::Error::OperationFailure, /on #{ClusterConfig.instance.primary_address_str}/)
115
115
  end
116
116
 
117
117
  it 'indicates first attempt' do
118
- expect {
118
+ expect do
119
119
  operation
120
- }.to raise_error(Mongo::Error::OperationFailure, /attempt 1/)
120
+ end.to raise_error(Mongo::Error::OperationFailure, /attempt 1/)
121
121
  end
122
122
  end
123
123
  end
@@ -144,9 +144,9 @@ describe 'Retryable writes integration tests' do
144
144
  end
145
145
 
146
146
  it 'does not retry writes and raises the original error' do
147
- expect {
147
+ expect do
148
148
  operation
149
- }.to raise_error(error)
149
+ end.to raise_error(error)
150
150
  expect(expectation).to eq(unsuccessful_retry_value)
151
151
  end
152
152
  end
@@ -158,9 +158,9 @@ describe 'Retryable writes integration tests' do
158
158
  end
159
159
 
160
160
  it 'does not retry writes and raises the original error' do
161
- expect {
161
+ expect do
162
162
  operation
163
- }.to raise_error(error)
163
+ end.to raise_error(error)
164
164
  expect(expectation).to eq(unsuccessful_retry_value)
165
165
  end
166
166
  end
@@ -172,9 +172,9 @@ describe 'Retryable writes integration tests' do
172
172
  end
173
173
 
174
174
  it 'does not retry writes and raises the original error' do
175
- expect {
175
+ expect do
176
176
  operation
177
- }.to raise_error(error)
177
+ end.to raise_error(error)
178
178
  expect(expectation).to eq(unsuccessful_retry_value)
179
179
  end
180
180
  end
@@ -211,22 +211,22 @@ describe 'Retryable writes integration tests' do
211
211
  end
212
212
 
213
213
  it 'raises the second error' do
214
- expect {
214
+ expect do
215
215
  operation
216
- }.to raise_error(second_error)
216
+ end.to raise_error(second_error)
217
217
  expect(expectation).to eq(unsuccessful_retry_value)
218
218
  end
219
219
 
220
220
  it 'indicates server used for operation' do
221
- expect {
221
+ expect do
222
222
  operation
223
- }.to raise_error(Mongo::Error, /on #{ClusterConfig.instance.primary_address_str}/)
223
+ end.to raise_error(Mongo::Error, /on #{ClusterConfig.instance.primary_address_str}/)
224
224
  end
225
225
 
226
226
  it 'indicates second attempt' do
227
- expect {
227
+ expect do
228
228
  operation
229
- }.to raise_error(Mongo::Error, /attempt 2/)
229
+ end.to raise_error(Mongo::Error, /attempt 2/)
230
230
  end
231
231
  end
232
232
 
@@ -237,9 +237,9 @@ describe 'Retryable writes integration tests' do
237
237
  end
238
238
 
239
239
  it 'raises the second error' do
240
- expect {
240
+ expect do
241
241
  operation
242
- }.to raise_error(second_error)
242
+ end.to raise_error(second_error)
243
243
  expect(expectation).to eq(unsuccessful_retry_value)
244
244
  end
245
245
  end
@@ -251,9 +251,9 @@ describe 'Retryable writes integration tests' do
251
251
  end
252
252
 
253
253
  it 'raises the second error' do
254
- expect {
254
+ expect do
255
255
  operation
256
- }.to raise_error(second_error)
256
+ end.to raise_error(second_error)
257
257
  expect(expectation).to eq(unsuccessful_retry_value)
258
258
  end
259
259
  end
@@ -265,9 +265,9 @@ describe 'Retryable writes integration tests' do
265
265
  end
266
266
 
267
267
  it 'does not retry writes and raises the first error' do
268
- expect {
268
+ expect do
269
269
  operation
270
- }.to raise_error(error)
270
+ end.to raise_error(error)
271
271
  expect(expectation).to eq(unsuccessful_retry_value)
272
272
  end
273
273
  end
@@ -279,28 +279,28 @@ describe 'Retryable writes integration tests' do
279
279
  end
280
280
 
281
281
  it 'raises the first error' do
282
- expect {
282
+ expect do
283
283
  operation
284
- }.to raise_error(error)
284
+ end.to raise_error(error)
285
285
  expect(expectation).to eq(unsuccessful_retry_value)
286
286
  end
287
287
 
288
288
  it 'indicates server used for operation' do
289
- expect {
289
+ expect do
290
290
  operation
291
- }.to raise_error(Mongo::Error, /on #{ClusterConfig.instance.primary_address_str}/)
291
+ end.to raise_error(Mongo::Error, /on #{ClusterConfig.instance.primary_address_str}/)
292
292
  end
293
293
 
294
294
  it 'indicates first attempt' do
295
- expect {
295
+ expect do
296
296
  operation
297
- }.to raise_error(Mongo::Error, /attempt 1/)
297
+ end.to raise_error(Mongo::Error, /attempt 1/)
298
298
  end
299
299
 
300
300
  it 'indicates retry was performed' do
301
- expect {
301
+ expect do
302
302
  operation
303
- }.to raise_error(Mongo::Error, /later retry failed: StandardError/)
303
+ end.to raise_error(Mongo::Error, /later retry failed: StandardError/)
304
304
  end
305
305
  end
306
306
  end
@@ -319,9 +319,9 @@ describe 'Retryable writes integration tests' do
319
319
  end
320
320
 
321
321
  it 'does not retry writes' do
322
- expect {
322
+ expect do
323
323
  operation
324
- }.to raise_error(Mongo::Error::SocketError)
324
+ end.to raise_error(Mongo::Error::SocketError)
325
325
  expect(expectation).to eq(unsuccessful_retry_value)
326
326
  end
327
327
  end
@@ -341,9 +341,9 @@ describe 'Retryable writes integration tests' do
341
341
  end
342
342
 
343
343
  it 'does not retry writes' do
344
- expect {
344
+ expect do
345
345
  operation
346
- }.to raise_error(Mongo::Error::SocketError)
346
+ end.to raise_error(Mongo::Error::SocketError)
347
347
  expect(expectation).to eq(unsuccessful_retry_value)
348
348
  end
349
349
  end