mongo 2.10.2 → 2.10.3

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1924,11 +1924,11 @@ describe Mongo::BulkWrite do
1924
1924
  end
1925
1925
 
1926
1926
  let(:first_txn_number) do
1927
- started_events[-2].command['txnNumber'].instance_variable_get(:@integer)
1927
+ started_events[-2].command['txnNumber'].value
1928
1928
  end
1929
1929
 
1930
1930
  let(:second_txn_number) do
1931
- started_events[-1].command['txnNumber'].instance_variable_get(:@integer)
1931
+ started_events[-1].command['txnNumber'].value
1932
1932
  end
1933
1933
 
1934
1934
  it 'inserts the documents' do
@@ -5,7 +5,9 @@ describe Mongo::Cursor::Builder::GetMoreCommand do
5
5
  describe '#specification' do
6
6
 
7
7
  let(:reply) do
8
- Mongo::Protocol::Reply.allocate
8
+ Mongo::Protocol::Reply.allocate.tap do |reply|
9
+ allow(reply).to receive(:cursor_id).and_return(8000)
10
+ end
9
11
  end
10
12
 
11
13
  let(:result) do
@@ -54,7 +56,7 @@ describe Mongo::Cursor::Builder::GetMoreCommand do
54
56
  end
55
57
 
56
58
  it 'includes getMore with cursor id' do
57
- expect(selector[:getMore]).to eq(cursor.id)
59
+ expect(selector[:getMore]).to eq(BSON::Int64.new(8000))
58
60
  end
59
61
 
60
62
  it 'includes the collection name' do
@@ -5,7 +5,9 @@ describe Mongo::Cursor::Builder::OpGetMore do
5
5
  describe '#specification' do
6
6
 
7
7
  let(:reply) do
8
- Mongo::Protocol::Reply.allocate
8
+ Mongo::Protocol::Reply.allocate.tap do |reply|
9
+ allow(reply).to receive(:cursor_id).and_return(8000)
10
+ end
9
11
  end
10
12
 
11
13
  let(:result) do
@@ -38,7 +40,7 @@ describe Mongo::Cursor::Builder::OpGetMore do
38
40
  end
39
41
 
40
42
  it 'includes the cursor id' do
41
- expect(specification[:cursor_id]).to eq(cursor.id)
43
+ expect(specification[:cursor_id]).to eq(BSON::Int64.new(8000))
42
44
  end
43
45
 
44
46
  it 'includes the database name' do
@@ -301,7 +301,7 @@ describe Mongo::Cursor do
301
301
  Mongo::Collection::View.new(
302
302
  authorized_collection,
303
303
  {},
304
- :batch_size => 2
304
+ :batch_size => 2,
305
305
  )
306
306
  end
307
307
 
@@ -312,9 +312,9 @@ describe Mongo::Cursor do
312
312
 
313
313
  it 'schedules a kill cursors op' do
314
314
  cluster.instance_variable_get(:@periodic_executor).flush
315
- expect {
315
+ expect do
316
316
  cursor.to_a
317
- }.to raise_exception(Mongo::Error::OperationFailure, /[cC]ursor.*not found/)
317
+ end.to raise_exception(Mongo::Error::OperationFailure, /[cC]ursor.*not found/)
318
318
  end
319
319
 
320
320
  context 'when the cursor is unregistered before the kill cursors operations are executed' do
@@ -175,83 +175,62 @@ describe Mongo::Retryable do
175
175
 
176
176
  context 'when an operation failure occurs' do
177
177
 
178
- context 'when the cluster is not a mongos' do
178
+ context 'when the operation failure is not retryable' do
179
+
180
+ let(:error) do
181
+ Mongo::Error::OperationFailure.new('not authorized')
182
+ end
179
183
 
180
184
  before do
181
- expect(operation).to receive(:execute).and_raise(Mongo::Error::OperationFailure).ordered
182
- expect(cluster).to receive(:sharded?).and_return(false)
185
+ expect(operation).to receive(:execute).and_raise(error).ordered
183
186
  end
184
187
 
185
- it 'raises an exception' do
188
+ it 'raises the exception' do
186
189
  expect {
187
190
  read_operation
188
191
  }.to raise_error(Mongo::Error::OperationFailure)
189
192
  end
190
193
  end
191
194
 
192
- context 'when the cluster is a mongos' do
195
+ context 'when the operation failure is retryable' do
193
196
 
194
- context 'when the operation failure is not retryable' do
197
+ let(:error) do
198
+ Mongo::Error::OperationFailure.new('not master')
199
+ end
195
200
 
196
- let(:error) do
197
- Mongo::Error::OperationFailure.new('not authorized')
198
- end
201
+ context 'when the retry succeeds' do
199
202
 
200
203
  before do
204
+ expect(retryable).to receive(:select_server).ordered
201
205
  expect(operation).to receive(:execute).and_raise(error).ordered
202
- expect(cluster).to receive(:sharded?).and_return(true)
206
+ expect(client).to receive(:read_retry_interval).and_return(0.1).ordered
207
+ expect(retryable).to receive(:select_server).ordered
208
+ expect(operation).to receive(:execute).and_return(true).ordered
203
209
  end
204
210
 
205
- it 'raises the exception' do
206
- expect {
207
- read_operation
208
- }.to raise_error(Mongo::Error::OperationFailure)
211
+ it 'returns the result' do
212
+ expect(read_operation).to be true
209
213
  end
210
214
  end
211
215
 
212
- context 'when the operation failure is retryable' do
216
+ context 'when the retry fails once and then succeeds' do
217
+ let(:max_read_retries) { 2 }
213
218
 
214
- let(:error) do
215
- Mongo::Error::OperationFailure.new('not master')
216
- end
217
-
218
- context 'when the retry succeeds' do
219
+ before do
220
+ expect(retryable).to receive(:select_server).ordered
221
+ expect(operation).to receive(:execute).and_raise(error).ordered
219
222
 
220
- before do
221
- expect(retryable).to receive(:select_server).ordered
222
- expect(operation).to receive(:execute).and_raise(error).ordered
223
- expect(cluster).to receive(:sharded?).and_return(true)
224
- expect(client).to receive(:read_retry_interval).and_return(0.1).ordered
225
- expect(retryable).to receive(:select_server).ordered
226
- expect(operation).to receive(:execute).and_return(true).ordered
227
- end
223
+ expect(client).to receive(:read_retry_interval).and_return(0.1).ordered
224
+ expect(retryable).to receive(:select_server).ordered
225
+ expect(operation).to receive(:execute).and_raise(error).ordered
228
226
 
229
- it 'returns the result' do
230
- expect(read_operation).to be true
231
- end
227
+ expect(client).to receive(:read_retry_interval).and_return(0.1).ordered
228
+ expect(retryable).to receive(:select_server).ordered
229
+ expect(operation).to receive(:execute).and_return(true).ordered
232
230
  end
233
231
 
234
- context 'when the retry fails once and then succeeds' do
235
- let(:max_read_retries) { 2 }
236
-
237
- before do
238
- expect(retryable).to receive(:select_server).ordered
239
- expect(operation).to receive(:execute).and_raise(error).ordered
240
-
241
- expect(cluster).to receive(:sharded?).and_return(true)
242
- expect(client).to receive(:read_retry_interval).and_return(0.1).ordered
243
- expect(retryable).to receive(:select_server).ordered
244
- expect(operation).to receive(:execute).and_raise(error).ordered
245
-
246
- expect(cluster).to receive(:sharded?).and_return(true)
247
- expect(client).to receive(:read_retry_interval).and_return(0.1).ordered
248
- expect(retryable).to receive(:select_server).ordered
249
- expect(operation).to receive(:execute).and_return(true).ordered
250
- end
251
-
252
- it 'returns the result' do
253
- expect(read_operation).to be true
254
- end
232
+ it 'returns the result' do
233
+ expect(read_operation).to be true
255
234
  end
256
235
  end
257
236
  end
@@ -0,0 +1,88 @@
1
+ module Sdam
2
+ class Verifier
3
+ include RSpec::Matchers
4
+
5
+ def verify_sdam_event(expected_events, actual_events, i)
6
+ expect(expected_events.length).to be > i
7
+ expect(actual_events.length).to be > i
8
+
9
+ expected_event = expected_events[i]
10
+ actual_event = actual_events[i]
11
+
12
+ actual_event_name = Utils.underscore(actual_event.class.name.sub(/.*::/, ''))
13
+ actual_event_name = actual_event_name.to_s.sub('topology_changed', 'topology_description_changed') + '_event'
14
+ expect(actual_event_name).to eq(expected_event.name)
15
+
16
+ send("verify_#{expected_event.name}", expected_event, actual_event)
17
+ end
18
+
19
+ def verify_topology_opening_event(expected, actual)
20
+ expect(actual.topology).not_to be nil
21
+ end
22
+
23
+ def verify_topology_description_changed_event(expected, actual)
24
+ verify_topology_matches(expected.data['previousDescription'], actual.previous_topology)
25
+ verify_topology_matches(expected.data['newDescription'], actual.new_topology)
26
+ end
27
+
28
+ def verify_topology_matches(expected, actual)
29
+ expected_type = ::Mongo::Cluster::Topology.const_get(expected['topologyType'])
30
+ expect(actual).to be_a(expected_type)
31
+
32
+ expect(actual.replica_set_name).to eq(expected['setName'])
33
+
34
+ expected['servers'].each do |server|
35
+ desc = actual.server_descriptions[server['address'].to_s]
36
+ expect(desc).not_to be nil
37
+ verify_description_matches(server, desc)
38
+ end
39
+
40
+ actual.server_descriptions.keys.each do |address_str|
41
+ expect(
42
+ expected['servers'].any? { |server| server['address'] == address_str }
43
+ ).to be true
44
+ end
45
+ end
46
+
47
+ def verify_server_opening_event(expected, actual)
48
+ expect(actual.address.to_s).to eq(expected.data['address'])
49
+ end
50
+
51
+ def verify_server_description_changed_event(expected, actual)
52
+ verify_description_matches(expected.data['previousDescription'], actual.previous_description)
53
+ verify_description_matches(expected.data['newDescription'], actual.new_description)
54
+ end
55
+
56
+ def verify_description_matches(expected, actual)
57
+ case expected['type']
58
+ when 'Standalone'
59
+ expect(actual).to be_standalone
60
+ when 'RSPrimary'
61
+ expect(actual).to be_primary
62
+ when 'RSSecondary'
63
+ expect(actual).to be_secondary
64
+ when 'RSArbiter'
65
+ expect(actual).to be_arbiter
66
+ when 'Mongos'
67
+ expect(actual).to be_mongos
68
+ when 'Unknown', 'PossiblePrimary'
69
+ expect(actual).to be_unknown
70
+ when 'RSGhost'
71
+ expect(actual).to be_ghost
72
+ when 'RSOther'
73
+ expect(actual).to be_other
74
+ end
75
+
76
+ expect(actual.address.to_s).to eq(expected['address'])
77
+ expect(actual.arbiters).to eq(expected['arbiters'])
78
+ expect(actual.hosts).to eq(expected['hosts'])
79
+ expect(actual.passives).to eq(expected['passives'])
80
+ expect(actual.primary_host).to eq(expected['primary'])
81
+ expect(actual.replica_set_name).to eq(expected['setName'])
82
+ end
83
+
84
+ def verify_server_closed_event(expected, actual)
85
+ expect(actual.address.to_s).to eq(expected.data['address'])
86
+ end
87
+ end
88
+ end
@@ -12,3 +12,34 @@ describe 'Retryable reads spec tests' do
12
12
  end
13
13
  end
14
14
  end
15
+
16
+ describe 'Retryable reads spec tests - legacy' do
17
+ require_no_multi_shard
18
+
19
+ define_crud_spec_tests(RETRYABLE_READS_TESTS) do |spec, req, test|
20
+ let(:client_options) do
21
+ {
22
+ max_read_retries: 1,
23
+ read_retry_interval: 0,
24
+ retry_reads: false,
25
+ }.update(test.client_options)
26
+ end
27
+
28
+ let(:client) do
29
+ authorized_client.with(client_options).tap do |client|
30
+ client.subscribe(Mongo::Monitoring::COMMAND, event_subscriber)
31
+ end
32
+ end
33
+
34
+ around do |example|
35
+ desc = example.full_description
36
+ # Skip tests that disable modern retryable reads because they expect
37
+ # no retries - and since legacy retryable reads are used, the tests
38
+ # will fail.
39
+ if desc =~/retryReads is false|fails on first attempt/
40
+ skip 'Test not applicable to legacy read retries'
41
+ end
42
+ example.run
43
+ end
44
+ end
45
+ end
@@ -1,5 +1,7 @@
1
1
  require 'lite_spec_helper'
2
2
 
3
+ require_relative '../runners/sdam/verifier'
4
+
3
5
  describe 'SDAM Monitoring' do
4
6
  include Mongo::SDAM
5
7
 
@@ -67,12 +69,15 @@ describe 'SDAM Monitoring' do
67
69
  expect(@subscriber.phase_events(phase_index).length).to eq(phase.outcome.events.length)
68
70
  end
69
71
 
72
+ let(:verifier) do
73
+ Sdam::Verifier.new
74
+ end
75
+
70
76
  phase.outcome.events.each_with_index do |expectation, index|
71
77
 
72
- it "expects a #{expectation.name} to be published" do
73
- published_event = @subscriber.phase_events(phase_index)[index]
74
- expect(published_event).not_to be_nil
75
- expect(published_event).to match_sdam_monitoring_event(expectation)
78
+ it "expects event #{index+1} to be #{expectation.name}" do
79
+ verifier.verify_sdam_event(
80
+ phase.outcome.events, @subscriber.phase_events(phase_index), index)
76
81
  end
77
82
  end
78
83
  end
@@ -142,6 +142,7 @@ class ClusterTools
142
142
  # - call step down on the existing primary
143
143
  # - call step up on the target in a loop until it becomes the primary
144
144
  def change_primary
145
+ start = Time.now
145
146
  existing_primary = admin_client.cluster.next_primary
146
147
  existing_primary_address = existing_primary.address
147
148
 
@@ -171,7 +172,7 @@ class ClusterTools
171
172
  persistently_step_up(target.address)
172
173
 
173
174
  new_primary = admin_client.cluster.next_primary
174
- puts "#{Time.now} [CT] Primary changed to #{new_primary.address}"
175
+ puts "#{Time.now} [CT] Primary changed to #{new_primary.address}. Time to change primaries: #{Time.now - start}"
175
176
  end
176
177
 
177
178
  def persistently_step_up(address)
@@ -348,8 +349,6 @@ class ClusterTools
348
349
  end
349
350
  end
350
351
 
351
- private
352
-
353
352
  def each_server(&block)
354
353
  admin_client.cluster.servers_list.each(&block)
355
354
  end
@@ -360,6 +359,8 @@ class ClusterTools
360
359
  end
361
360
  end
362
361
 
362
+ private
363
+
363
364
  def reset_server_states
364
365
  each_server do |server|
365
366
  server.unknown!
@@ -134,6 +134,11 @@ module Mongo
134
134
  end
135
135
  if expected.keys.first == '$numberLong'
136
136
  converted = expected.values.first.to_i
137
+ if actual.is_a?(BSON::Int64)
138
+ actual = actual.value
139
+ elsif actual.is_a?(BSON::Int32)
140
+ return false
141
+ end
137
142
  (actual == converted) || actual >= 0
138
143
  else
139
144
  expected.each do |key, value|
@@ -44,6 +44,13 @@ class EventSubscriber
44
44
  end
45
45
  end
46
46
 
47
+ # Filters command started events for the specified command name.
48
+ def command_started_events(command_name)
49
+ started_events.select do |event|
50
+ event.command[command_name]
51
+ end
52
+ end
53
+
47
54
  # Locates command stated events for the specified command name,
48
55
  # asserts that there is exactly one such event, and returns it.
49
56
  def single_command_started_event(command_name)
@@ -13,123 +13,8 @@
13
13
  # limitations under the License.
14
14
  #
15
15
 
16
- RSpec::Matchers.define :match_topology_opening_event do |expectation|
17
-
18
- match do |event|
19
- event.is_a?(Mongo::Monitoring::Event::TopologyOpening) &&
20
- event.topology != nil
21
- end
22
- end
23
-
24
- RSpec::Matchers.define :match_topology_description_changed_event do |expectation|
25
- include Mongo::SDAMMonitoring::Matchable
26
-
27
- match do |event|
28
- event.is_a?(Mongo::Monitoring::Event::TopologyChanged) &&
29
- topologies_match?(event, expectation)
30
- end
31
- end
32
-
33
- RSpec::Matchers.define :match_server_opening_event do |expectation|
34
-
35
- match do |event|
36
- event.is_a?(Mongo::Monitoring::Event::ServerOpening) &&
37
- event.address.to_s == expectation.data['address']
38
- end
39
- end
40
-
41
- RSpec::Matchers.define :match_server_description_changed_event do |expectation|
42
- include Mongo::SDAMMonitoring::Matchable
43
-
44
- match do |event|
45
- event.is_a?(Mongo::Monitoring::Event::ServerDescriptionChanged) &&
46
- descriptions_match?(event, expectation)
47
- end
48
- end
49
-
50
- RSpec::Matchers.define :match_server_closed_event do |expectation|
51
-
52
- match do |event|
53
- event.is_a?(Mongo::Monitoring::Event::ServerClosed) &&
54
- event.address.to_s == expectation.data['address']
55
- end
56
- end
57
-
58
- RSpec::Matchers.define :match_sdam_monitoring_event do |expectation|
59
-
60
- match do |event|
61
- expect(event).to send("match_#{expectation.name}", expectation)
62
- end
63
- end
64
-
65
16
  module Mongo
66
17
  module SDAMMonitoring
67
- module Matchable
68
-
69
- def descriptions_match?(event, expectation)
70
- description_matches?(event.previous_description, expectation.data['previousDescription']) &&
71
- description_matches?(event.new_description, expectation.data['newDescription'])
72
- end
73
-
74
- def topologies_match?(event, expectation)
75
- unless topology_matches?(event.previous_topology, expectation.data['previousDescription'])
76
- if ENV['VERBOSE_MATCHERS']
77
- $stderr.puts "Previous topology mismatch"
78
- end
79
- return false
80
- end
81
- unless topology_matches?(event.new_topology, expectation.data['newDescription'])
82
- if ENV['VERBOSE_MATCHERS']
83
- $stderr.puts "New topology mismatch:\nHave: #{event.new_topology}\nWant: #{expectation.data['newDescription']}"
84
- end
85
- return false
86
- end
87
- true
88
- end
89
-
90
- def description_matches?(actual, expected)
91
- type_ok = case expected['type']
92
- when 'Standalone' then actual.standalone?
93
- when 'RSPrimary' then actual.primary?
94
- when 'RSSecondary' then actual.secondary?
95
- when 'RSArbiter' then actual.arbiter?
96
- when 'Mongos' then actual.mongos?
97
- when 'Unknown' then actual.unknown?
98
- when 'PossiblePrimary' then actual.unknown?
99
- when 'RSGhost' then actual.ghost?
100
- when 'RSOther' then actual.other?
101
- end
102
- return false unless type_ok
103
-
104
- return false if actual.address.to_s != expected['address']
105
- return false if actual.arbiters != expected['arbiters']
106
- return false if actual.hosts != expected['hosts']
107
- return false if actual.passives != expected['passives']
108
- return false if actual.primary_host != expected['primary']
109
- return false if actual.replica_set_name != expected['setName']
110
- true
111
- end
112
-
113
- def topology_matches?(actual, expected)
114
- expected_type = ::Mongo::Cluster::Topology.const_get(expected['topologyType'])
115
- return false unless actual.is_a?(expected_type)
116
-
117
- return false unless actual.replica_set_name == expected['setName']
118
-
119
- expected['servers'].each do |server|
120
- desc = actual.server_descriptions[server['address'].to_s]
121
- return false unless description_matches?(desc, server)
122
- end
123
-
124
- actual.server_descriptions.keys.each do |address_str|
125
- unless expected['servers'].any? { |server| server['address'] == address_str }
126
- return false
127
- end
128
- end
129
-
130
- true
131
- end
132
- end
133
18
 
134
19
  # Test subscriber for SDAM monitoring.
135
20
  #