bson 4.13.0-java → 4.15.0-java

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.
@@ -52,7 +52,7 @@ module Mrss
52
52
  end
53
53
 
54
54
  def require_topology(*topologies)
55
- invalid_topologies = topologies - [:single, :replica_set, :sharded]
55
+ invalid_topologies = topologies - [:single, :replica_set, :sharded, :load_balanced]
56
56
 
57
57
  unless invalid_topologies.empty?
58
58
  raise ArgumentError, "Invalid topologies requested: #{invalid_topologies.join(', ')}"
@@ -82,7 +82,7 @@ module Mrss
82
82
  unless ClusterConfig.instance.server_version >= '4.0'
83
83
  skip 'Transactions tests in a replica set topology require server 4.0+'
84
84
  end
85
- when :sharded
85
+ when :sharded, :load_balanced
86
86
  unless ClusterConfig.instance.server_version >= '4.2'
87
87
  skip 'Transactions tests in a sharded cluster topology require server 4.2+'
88
88
  end
@@ -113,6 +113,14 @@ module Mrss
113
113
  end
114
114
  end
115
115
 
116
+ def require_retry_writes
117
+ before(:all) do
118
+ unless SpecConfig.instance.retry_writes?
119
+ skip "Retry writes is disabled"
120
+ end
121
+ end
122
+ end
123
+
116
124
  def require_no_retry_writes
117
125
  before(:all) do
118
126
  if SpecConfig.instance.retry_writes?
@@ -215,7 +223,8 @@ module Mrss
215
223
 
216
224
  def require_no_auth
217
225
  before(:all) do
218
- if (ENV['AUTH'] && ENV['AUTH'] != 'noauth') || SpecConfig.instance.user || ClusterConfig.instance.auth_enabled?
226
+ auth = ENV.fetch('AUTH', '')
227
+ if (!auth.empty? && auth != 'noauth') || SpecConfig.instance.user || ClusterConfig.instance.auth_enabled?
219
228
  skip "Auth not allowed"
220
229
  end
221
230
  end
@@ -241,31 +250,52 @@ module Mrss
241
250
  # (mongos 4.0+ overrides the write concern)
242
251
  def require_set_write_concern
243
252
  before(:all) do
244
- if ClusterConfig.instance.topology == :sharded && ClusterConfig.instance.short_server_version >= '4.0'
253
+ if %i(sharded load_balanced).include?(ClusterConfig.instance.topology) &&
254
+ ClusterConfig.instance.short_server_version >= '4.0'
255
+ then
245
256
  skip "mongos 4.0+ overrides write concern"
246
257
  end
247
258
  end
248
259
  end
249
260
 
250
- def require_multi_shard
261
+ def require_multi_mongos
251
262
  before(:all) do
252
263
  if ClusterConfig.instance.topology == :sharded && SpecConfig.instance.addresses.length == 1
253
- skip 'Test requires a minimum of two shards if run in sharded topology'
264
+ skip 'Test requires a minimum of two mongoses if run in sharded topology'
265
+ end
266
+
267
+ if ClusterConfig.instance.topology == :load_balanced && SpecConfig.instance.single_mongos?
268
+ skip 'Test requires a minimum of two mongoses if run in load-balanced topology'
254
269
  end
255
270
  end
256
271
  end
257
272
 
258
- def require_no_multi_shard
273
+ # In sharded topology operations are distributed to the mongoses.
274
+ # When we set fail points, the fail point may be set on one mongos and
275
+ # operation may be executed on another mongos, causing failures.
276
+ # Tests that are not setting targeted fail points should utilize this
277
+ # method to restrict themselves to single mongos.
278
+ #
279
+ # In load-balanced topology, the same problem can happen when there is
280
+ # more than one mongos behind the load balancer.
281
+ def require_no_multi_mongos
259
282
  before(:all) do
260
283
  if ClusterConfig.instance.topology == :sharded && SpecConfig.instance.addresses.length > 1
261
- skip 'Test requires a single shard if run in sharded topology'
284
+ skip 'Test requires a single mongos if run in sharded topology'
285
+ end
286
+ if ClusterConfig.instance.topology == :load_balanced && !SpecConfig.instance.single_mongos?
287
+ skip 'Test requires a single mongos, as indicated by SINGLE_MONGOS=1 environment variable, if run in load-balanced topology'
262
288
  end
263
289
  end
264
290
  end
265
291
 
292
+ alias :require_no_multi_shard :require_no_multi_mongos
293
+
266
294
  def require_wired_tiger
267
295
  before(:all) do
268
- if ClusterConfig.instance.storage_engine != :wired_tiger
296
+ # Storage detection fails for serverless instances. However, it is safe to
297
+ # assume that a serverless instance uses WiredTiger Storage Engine.
298
+ if !SpecConfig.instance.serverless? && ClusterConfig.instance.storage_engine != :wired_tiger
269
299
  skip 'Test requires WiredTiger storage engine'
270
300
  end
271
301
  end
@@ -274,7 +304,9 @@ module Mrss
274
304
  def require_wired_tiger_on_36
275
305
  before(:all) do
276
306
  if ClusterConfig.instance.short_server_version >= '3.6'
277
- if ClusterConfig.instance.storage_engine != :wired_tiger
307
+ # Storage detection fails for serverless instances. However, it is safe to
308
+ # assume that a serverless instance uses WiredTiger Storage Engine.
309
+ if !SpecConfig.instance.serverless? && ClusterConfig.instance.storage_engine != :wired_tiger
278
310
  skip 'Test requires WiredTiger storage engine on 3.6+ servers'
279
311
  end
280
312
  end
@@ -283,7 +315,7 @@ module Mrss
283
315
 
284
316
  def require_mmapv1
285
317
  before(:all) do
286
- if ClusterConfig.instance.storage_engine != :mmapv1
318
+ if SpecConfig.instance.serverless? || ClusterConfig.instance.storage_engine != :mmapv1
287
319
  skip 'Test requires MMAPv1 storage engine'
288
320
  end
289
321
  end
@@ -342,5 +374,13 @@ module Mrss
342
374
  end
343
375
  end
344
376
  end
377
+
378
+ def require_unix_socket
379
+ before(:all) do
380
+ if ENV['TOPOLOGY'] == 'load-balanced'
381
+ skip 'Load balancer does not listen on Unix sockets'
382
+ end
383
+ end
384
+ end
345
385
  end
346
386
  end
@@ -111,7 +111,7 @@ module Mrss
111
111
  '.'])
112
112
  end
113
113
 
114
- BASE_TEST_COMMAND = %w(docker run -i --tmpfs /tmpfs:exec).freeze
114
+ BASE_TEST_COMMAND = %w(docker run --rm -i --tmpfs /tmpfs:exec).freeze
115
115
 
116
116
  def run_tests
117
117
  run_command(BASE_TEST_COMMAND + tty_arg + extra_env + [image_tag] +
@@ -173,15 +173,17 @@ module Mrss
173
173
  end
174
174
 
175
175
  def distro
176
- @options[:distro] || 'ubuntu1604'
176
+ @options[:distro] || 'ubuntu1804'
177
177
  end
178
178
 
179
179
  BASE_IMAGES = {
180
180
  'debian81' => 'debian:jessie',
181
181
  'debian92' => 'debian:stretch',
182
+ 'debian10' => 'debian:buster',
182
183
  'ubuntu1404' => 'ubuntu:trusty',
183
184
  'ubuntu1604' => 'ubuntu:xenial',
184
185
  'ubuntu1804' => 'ubuntu:bionic',
186
+ 'ubuntu2004' => 'ubuntu:focal',
185
187
  'rhel62' => 'centos:6',
186
188
  'rhel70' => 'centos:7',
187
189
  }.freeze
@@ -198,6 +200,10 @@ module Mrss
198
200
  ruby == 'ruby-head'
199
201
  end
200
202
 
203
+ def system_ruby?
204
+ %w(1 true yes).include?(@env['SYSTEM_RUBY']&.downcase)
205
+ end
206
+
201
207
  def server_version
202
208
  @env['MONGODB_VERSION']
203
209
  end
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mrss
4
+ # Test event subscriber.
5
+ class EventSubscriber
6
+
7
+ # The mappings of event names to types.
8
+ MAPPINGS = {
9
+ 'topology_opening_event' => Mongo::Monitoring::Event::TopologyOpening,
10
+ 'topology_description_changed_event' => Mongo::Monitoring::Event::TopologyChanged,
11
+ 'topology_closed_event' => Mongo::Monitoring::Event::TopologyClosed,
12
+ 'server_opening_event' => Mongo::Monitoring::Event::ServerOpening,
13
+ 'server_description_changed_event' => Mongo::Monitoring::Event::ServerDescriptionChanged,
14
+ 'server_closed_event' => Mongo::Monitoring::Event::ServerClosed
15
+ }.freeze
16
+
17
+ attr_reader :all_events
18
+
19
+ attr_reader :started_events
20
+
21
+ attr_reader :succeeded_events
22
+
23
+ attr_reader :failed_events
24
+
25
+ attr_reader :published_events
26
+
27
+ # @param [ String ] name Optional name for the event subscriber.
28
+ def initialize(name: nil)
29
+ @mutex = Mutex.new
30
+ clear_events!
31
+ @name = name
32
+ end
33
+
34
+ def to_s
35
+ %Q`#<EventSubscriber:#{@name ? "\"#{@name}\"" : '%x' % object_id} \
36
+ started=#{started_events.length} \
37
+ succeeded=#{succeeded_events.length} \
38
+ failed=#{failed_events.length} \
39
+ published=#{published_events.length}>`
40
+ end
41
+
42
+ alias :inspect :to_s
43
+
44
+ # Event retrieval
45
+
46
+ def select_started_events(cls)
47
+ started_events.select do |event|
48
+ event.is_a?(cls)
49
+ end
50
+ end
51
+
52
+ def select_succeeded_events(cls)
53
+ succeeded_events.select do |event|
54
+ event.is_a?(cls)
55
+ end
56
+ end
57
+
58
+ def select_completed_events(*classes)
59
+ (succeeded_events + failed_events).select do |event|
60
+ classes.any? { |c| c === event }
61
+ end
62
+ end
63
+
64
+ def select_published_events(cls)
65
+ published_events.select do |event|
66
+ event.is_a?(cls)
67
+ end
68
+ end
69
+
70
+ # Filters command started events for the specified command name.
71
+ def command_started_events(command_name)
72
+ started_events.select do |event|
73
+ event.command[command_name]
74
+ end
75
+ end
76
+
77
+ def non_auth_command_started_events
78
+ started_events.reject do |event|
79
+ %w(authenticate getnonce saslSstart saslContinue).any? do |cmd|
80
+ event.command[cmd]
81
+ end
82
+ end
83
+ end
84
+
85
+ # Locates command stated events for the specified command name,
86
+ # asserts that there is exactly one such event, and returns it.
87
+ def single_command_started_event(command_name, include_auth: false)
88
+ events = if include_auth
89
+ started_events
90
+ else
91
+ non_auth_command_started_events
92
+ end
93
+ events.select! do |event|
94
+ event.command[command_name]
95
+ end
96
+ if events.length != 1
97
+ raise "Expected a single #{command_name} event but we have #{events.length}"
98
+ end
99
+ events.first
100
+ end
101
+
102
+
103
+ # Get the first succeeded event published for the name, and then delete it.
104
+ #
105
+ # @param [ String ] name The event name.
106
+ #
107
+ # @return [ Event ] The matching event.
108
+ def first_event(name)
109
+ cls = MAPPINGS[name]
110
+ if cls.nil?
111
+ raise ArgumentError, "Bogus event name #{name}"
112
+ end
113
+ matching = succeeded_events.find do |event|
114
+ cls === event
115
+ end
116
+ succeeded_events.delete(matching)
117
+ matching
118
+ end
119
+
120
+ # Event recording
121
+
122
+ # Cache the started event.
123
+ #
124
+ # @param [ Event ] event The event.
125
+ def started(event)
126
+ @mutex.synchronize do
127
+ started_events << event
128
+ all_events << event
129
+ end
130
+ end
131
+
132
+ # Cache the succeeded event.
133
+ #
134
+ # @param [ Event ] event The event.
135
+ def succeeded(event)
136
+ @mutex.synchronize do
137
+ succeeded_events << event
138
+ all_events << event
139
+ end
140
+ end
141
+
142
+ # Cache the failed event.
143
+ #
144
+ # @param [ Event ] event The event.
145
+ def failed(event)
146
+ @mutex.synchronize do
147
+ failed_events << event
148
+ all_events << event
149
+ end
150
+ end
151
+
152
+ def published(event)
153
+ @mutex.synchronize do
154
+ published_events << event
155
+ all_events << event
156
+ end
157
+ end
158
+
159
+ # Clear all cached events.
160
+ def clear_events!
161
+ @all_events = []
162
+ @started_events = []
163
+ @succeeded_events = []
164
+ @failed_events = []
165
+ @published_events = []
166
+ self
167
+ end
168
+ end
169
+ # Only handles succeeded events correctly.
170
+ class PhasedEventSubscriber < EventSubscriber
171
+ def initialize
172
+ super
173
+ @phase_events = {}
174
+ end
175
+
176
+ def phase_finished(phase_index)
177
+ @phase_events[phase_index] = succeeded_events
178
+ @succeeded_events = []
179
+ end
180
+
181
+ def phase_events(phase_index)
182
+ @phase_events[phase_index]
183
+ end
184
+
185
+ def event_count
186
+ @phase_events.inject(0) do |sum, event|
187
+ sum + event.length
188
+ end
189
+ end
190
+ end
191
+
192
+ class VerboseEventSubscriber < EventSubscriber
193
+ %w(started succeeded failed published).each do |meth|
194
+ define_method(meth) do |event|
195
+ puts event.summary
196
+ super(event)
197
+ end
198
+ end
199
+ end
200
+ end
@@ -48,19 +48,24 @@ module Mrss
48
48
  url = dl['archive']['url']
49
49
  end
50
50
  rescue MissingDownloadUrl
51
- if %w(4.7 4.7.0).include?(desired_version)
52
- # 4.7.0 has no advertised downloads but it is downloadable and
53
- # we do need it. Dirty hack below.
54
- registry = self.class.new('4.4.3', arch)
55
- registry.download_url.sub('4.4.3', '4.7.0').tap do |url|
56
- # Sanity check - ensure the URL we hacked up is a valid one
57
- io = uri_open(url)
58
- begin
59
- io.read(1)
60
- ensure
61
- io.close
62
- end
51
+ if %w(2.6 3.0).include?(desired_version) && arch == 'ubuntu1604'
52
+ # 2.6 and 3.0 are only available for ubuntu1204 and ubuntu1404.
53
+ # Those ubuntus have ancient Pythons that don't work due to not
54
+ # implementing recent TLS protocols.
55
+ # Because of this we test on ubuntu1604 which has a newer Python.
56
+ # But we still need to retrieve ubuntu1404-targeting builds.
57
+ url = self.class.new('3.2', arch).download_url
58
+ unless url.include?('3.2.')
59
+ raise 'URL not in expected format'
63
60
  end
61
+ url = case desired_version
62
+ when '2.6'
63
+ url.sub(/\b3\.2\.\d+/, '2.6.12')
64
+ when '3.0'
65
+ url.sub(/\b3\.2\.\d+/, '3.0.15')
66
+ else
67
+ raise NotImplementedError
68
+ end.sub('ubuntu1604', 'ubuntu1404')
64
69
  else
65
70
  raise
66
71
  end
@@ -25,18 +25,28 @@ module Mrss
25
25
  end
26
26
 
27
27
  def initialize(root: nil, classifiers:, priority_order:,
28
- spec_root: nil, rspec_json_path: nil, rspec_all_json_path: nil
28
+ spec_root: nil, rspec_json_path: nil, rspec_all_json_path: nil,
29
+ randomize: false
29
30
  )
30
31
  @spec_root = spec_root || File.join(root, 'spec')
31
32
  @classifiers = classifiers
32
33
  @priority_order = priority_order
33
34
  @rspec_json_path = rspec_json_path || File.join(root, 'tmp/rspec.json')
34
35
  @rspec_all_json_path = rspec_all_json_path || File.join(root, 'tmp/rspec-all.json')
36
+ @randomize = !!randomize
35
37
  end
36
38
 
37
39
  attr_reader :spec_root, :classifiers, :priority_order
38
40
  attr_reader :rspec_json_path, :rspec_all_json_path
39
41
 
42
+ def randomize?
43
+ @randomize
44
+ end
45
+
46
+ def seed
47
+ @seed ||= (rand * 100_000).to_i
48
+ end
49
+
40
50
  def buckets
41
51
  @buckets ||= {}.tap do |buckets|
42
52
  Find.find(spec_root) do |path|
@@ -81,10 +91,20 @@ module Mrss
81
91
  end
82
92
 
83
93
  def run
94
+ run_buckets(*buckets.keys)
95
+ end
96
+
97
+ def run_buckets(*buckets)
84
98
  FileUtils.rm_f(rspec_all_json_path)
85
99
 
100
+ buckets.each do |bucket|
101
+ if bucket && !self.buckets[bucket]
102
+ raise "Unknown bucket #{bucket}"
103
+ end
104
+ end
105
+ buckets = Hash[self.buckets.select { |k, v| buckets.include?(k) }]
106
+
86
107
  failed = []
87
- buckets = self.buckets.dup
88
108
 
89
109
  priority_order.each do |category|
90
110
  if files = buckets.delete(category)
@@ -112,8 +132,12 @@ module Mrss
112
132
  puts "Running #{category.to_s.gsub('_', ' ')} tests"
113
133
  FileUtils.rm_f(rspec_json_path)
114
134
  cmd = %w(rspec) + paths
135
+ if randomize?
136
+ cmd += %W(--order rand:#{seed})
137
+ end
115
138
 
116
139
  begin
140
+ puts "Running #{cmd.join(' ')}"
117
141
  ChildProcessHelper.check_call(cmd)
118
142
  ensure
119
143
  if File.exist?(rspec_json_path)
@@ -139,6 +163,9 @@ module Mrss
139
163
  end
140
164
  new.delete('version')
141
165
  new.delete('summary_line')
166
+ # The spec organizer runs all buckets with the same seed, hence
167
+ # we can drop the seed from new results.
168
+ new.delete('seed')
142
169
  unless new.empty?
143
170
  raise "Unhandled rspec results keys: #{new.keys.join(', ')}"
144
171
  end