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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/mongo/collection/view/readable.rb +2 -2
- data/lib/mongo/cursor/builder/get_more_command.rb +4 -1
- data/lib/mongo/cursor/builder/kill_cursors_command.rb +16 -5
- data/lib/mongo/cursor/builder/op_get_more.rb +2 -2
- data/lib/mongo/cursor/builder/op_kill_cursors.rb +17 -5
- data/lib/mongo/error/operation_failure.rb +3 -3
- data/lib/mongo/protocol/get_more.rb +2 -1
- data/lib/mongo/protocol/kill_cursors.rb +6 -13
- data/lib/mongo/protocol/serializers.rb +10 -4
- data/lib/mongo/retryable.rb +34 -9
- data/lib/mongo/version.rb +1 -1
- data/mongo.gemspec +1 -1
- data/spec/integration/cursor_reaping_spec.rb +1 -1
- data/spec/integration/get_more_spec.rb +32 -0
- data/spec/integration/retryable_errors_spec.rb +265 -0
- data/spec/integration/retryable_writes_spec.rb +36 -36
- data/spec/mongo/bulk_write_spec.rb +2 -2
- data/spec/mongo/cursor/builder/get_more_command_spec.rb +4 -2
- data/spec/mongo/cursor/builder/op_get_more_spec.rb +4 -2
- data/spec/mongo/cursor_spec.rb +3 -3
- data/spec/mongo/retryable_spec.rb +31 -52
- data/spec/runners/sdam/verifier.rb +88 -0
- data/spec/spec_tests/retryable_reads_spec.rb +31 -0
- data/spec/spec_tests/sdam_monitoring_spec.rb +9 -4
- data/spec/support/cluster_tools.rb +4 -3
- data/spec/support/command_monitoring.rb +5 -0
- data/spec/support/event_subscriber.rb +7 -0
- data/spec/support/sdam_monitoring.rb +0 -115
- data/spec/support/spec_config.rb +1 -1
- data/spec/support/utils.rb +1 -1
- metadata +10 -4
- 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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
346
|
+
end.to raise_error(Mongo::Error::SocketError)
|
347
347
|
expect(expectation).to eq(unsuccessful_retry_value)
|
348
348
|
end
|
349
349
|
end
|