mongo 2.10.5 → 2.11.0.rc0
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/CONTRIBUTING.md +1 -1
- data/lib/mongo.rb +2 -0
- data/lib/mongo/address.rb +4 -0
- data/lib/mongo/address/validator.rb +99 -0
- data/lib/mongo/auth.rb +7 -2
- data/lib/mongo/auth/user.rb +1 -7
- data/lib/mongo/background_thread.rb +135 -0
- data/lib/mongo/bulk_write/transformable.rb +3 -3
- data/lib/mongo/client.rb +74 -16
- data/lib/mongo/cluster.rb +193 -41
- data/lib/mongo/cluster/periodic_executor.rb +31 -43
- data/lib/mongo/cluster/sdam_flow.rb +26 -3
- data/lib/mongo/cluster/srv_monitor.rb +127 -0
- data/lib/mongo/collection/view/readable.rb +3 -5
- data/lib/mongo/collection/view/writable.rb +3 -3
- data/lib/mongo/cursor/builder/get_more_command.rb +1 -4
- data/lib/mongo/cursor/builder/kill_cursors_command.rb +5 -23
- data/lib/mongo/cursor/builder/op_get_more.rb +2 -2
- data/lib/mongo/cursor/builder/op_kill_cursors.rb +5 -24
- data/lib/mongo/error.rb +1 -0
- data/lib/mongo/error/auth_error.rb +1 -1
- data/lib/mongo/error/connection_check_out_timeout.rb +7 -8
- data/lib/mongo/error/invalid_address.rb +24 -0
- data/lib/mongo/error/notable.rb +2 -2
- data/lib/mongo/error/operation_failure.rb +3 -3
- data/lib/mongo/error/pool_closed_error.rb +11 -4
- data/lib/mongo/event.rb +1 -1
- data/lib/mongo/grid/file.rb +0 -5
- data/lib/mongo/grid/file/chunk.rb +0 -2
- data/lib/mongo/grid/fs_bucket.rb +13 -15
- data/lib/mongo/grid/stream/write.rb +3 -9
- data/lib/mongo/loggable.rb +5 -1
- data/lib/mongo/monitoring.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +7 -0
- data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +11 -3
- data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +11 -3
- data/lib/mongo/monitoring/event/cmap/pool_closed.rb +11 -3
- data/lib/mongo/monitoring/event/cmap/pool_created.rb +12 -3
- data/lib/mongo/monitoring/unified_sdam_log_subscriber.rb +62 -0
- data/lib/mongo/operation/shared/executable.rb +5 -10
- data/lib/mongo/operation/shared/sessions_supported.rb +1 -5
- data/lib/mongo/protocol/get_more.rb +1 -2
- data/lib/mongo/protocol/kill_cursors.rb +13 -6
- data/lib/mongo/protocol/serializers.rb +4 -20
- data/lib/mongo/retryable.rb +9 -34
- data/lib/mongo/semaphore.rb +1 -1
- data/lib/mongo/server.rb +113 -42
- data/lib/mongo/server/connection.rb +12 -5
- data/lib/mongo/server/connection_pool.rb +250 -40
- data/lib/mongo/server/connection_pool/populator.rb +58 -0
- data/lib/mongo/server/description.rb +9 -2
- data/lib/mongo/server/monitor.rb +68 -93
- data/lib/mongo/server/monitor/connection.rb +2 -0
- data/lib/mongo/server_selector/selectable.rb +13 -5
- data/lib/mongo/session.rb +0 -13
- data/lib/mongo/srv.rb +17 -0
- data/lib/mongo/srv/monitor.rb +96 -0
- data/lib/mongo/srv/resolver.rb +130 -0
- data/lib/mongo/srv/result.rb +126 -0
- data/lib/mongo/srv/warning_result.rb +35 -0
- data/lib/mongo/uri.rb +45 -55
- data/lib/mongo/uri/srv_protocol.rb +89 -42
- data/lib/mongo/version.rb +1 -1
- data/mongo.gemspec +3 -4
- data/spec/README.md +6 -1
- data/spec/enterprise_auth/kerberos_spec.rb +7 -6
- data/spec/integration/change_stream_examples_spec.rb +0 -4
- data/spec/integration/client_construction_spec.rb +14 -2
- data/spec/integration/connect_single_rs_name_spec.rb +2 -2
- data/spec/integration/connection_pool_populator_spec.rb +296 -0
- data/spec/integration/connection_spec.rb +31 -22
- data/spec/integration/cursor_reaping_spec.rb +1 -2
- data/spec/integration/docs_examples_spec.rb +0 -4
- data/spec/integration/heartbeat_events_spec.rb +17 -15
- data/spec/integration/reconnect_spec.rb +144 -1
- data/spec/integration/retryable_writes_errors_spec.rb +0 -4
- data/spec/integration/retryable_writes_spec.rb +36 -36
- data/spec/integration/sdam_error_handling_spec.rb +31 -25
- data/spec/integration/sdam_events_spec.rb +2 -6
- data/spec/integration/server_monitor_spec.rb +28 -0
- data/spec/integration/server_selector_spec.rb +7 -5
- data/spec/integration/srv_monitoring_spec.rb +360 -0
- data/spec/integration/step_down_spec.rb +4 -6
- data/spec/lite_spec_helper.rb +22 -0
- data/spec/mongo/address/validator_spec.rb +51 -0
- data/spec/mongo/auth/cr_spec.rb +1 -29
- data/spec/mongo/auth/ldap_spec.rb +1 -29
- data/spec/mongo/auth/scram/conversation_spec.rb +0 -2
- data/spec/mongo/auth/scram/negotiation_spec.rb +1 -1
- data/spec/mongo/auth/scram_spec.rb +1 -29
- data/spec/mongo/auth/user/view_spec.rb +1 -36
- data/spec/mongo/auth/user_spec.rb +0 -12
- data/spec/mongo/auth/x509_spec.rb +1 -29
- data/spec/mongo/bulk_write_spec.rb +2 -2
- data/spec/mongo/client_construction_spec.rb +56 -15
- data/spec/mongo/client_spec.rb +31 -27
- data/spec/mongo/cluster/periodic_executor_spec.rb +16 -0
- data/spec/mongo/cluster/srv_monitor_spec.rb +214 -0
- data/spec/mongo/cluster/topology/replica_set_spec.rb +16 -11
- data/spec/mongo/cluster/topology/sharded_spec.rb +12 -9
- data/spec/mongo/cluster/topology/single_spec.rb +20 -11
- data/spec/mongo/cluster_spec.rb +45 -29
- data/spec/mongo/collection/view/map_reduce_spec.rb +14 -9
- data/spec/mongo/collection/view/readable_spec.rb +0 -16
- data/spec/mongo/collection_spec.rb +0 -44
- data/spec/mongo/cursor/builder/get_more_command_spec.rb +2 -4
- data/spec/mongo/cursor/builder/op_get_more_spec.rb +2 -4
- data/spec/mongo/cursor_spec.rb +27 -7
- data/spec/mongo/monitoring/event/cmap/connection_checked_in_spec.rb +10 -3
- data/spec/mongo/monitoring/event/cmap/connection_checked_out_spec.rb +10 -3
- data/spec/mongo/monitoring/event/cmap/pool_closed_spec.rb +10 -3
- data/spec/mongo/monitoring/event/cmap/pool_created_spec.rb +10 -3
- data/spec/mongo/operation/delete/op_msg_spec.rb +17 -8
- data/spec/mongo/operation/insert/op_msg_spec.rb +50 -35
- data/spec/mongo/operation/update/op_msg_spec.rb +14 -7
- data/spec/mongo/retryable_spec.rb +52 -31
- data/spec/mongo/server/app_metadata_spec.rb +0 -8
- data/spec/mongo/server/connection_auth_spec.rb +5 -2
- data/spec/mongo/server/connection_pool/populator_spec.rb +101 -0
- data/spec/mongo/server/connection_pool_spec.rb +256 -107
- data/spec/mongo/server/connection_spec.rb +22 -33
- data/spec/mongo/server/description_spec.rb +42 -4
- data/spec/mongo/server/monitor/connection_spec.rb +22 -11
- data/spec/mongo/server/monitor_spec.rb +66 -107
- data/spec/mongo/server_spec.rb +82 -60
- data/spec/mongo/session/session_pool_spec.rb +1 -5
- data/spec/mongo/session_spec.rb +0 -4
- data/spec/mongo/socket/ssl_spec.rb +2 -2
- data/spec/mongo/srv/monitor_spec.rb +211 -0
- data/spec/mongo/srv/result_spec.rb +54 -0
- data/spec/mongo/uri/srv_protocol_spec.rb +30 -15
- data/spec/mongo/uri_spec.rb +125 -4
- data/spec/spec_helper.rb +6 -0
- data/spec/spec_tests/auth_spec.rb +39 -0
- data/spec/spec_tests/cmap_spec.rb +55 -8
- data/spec/spec_tests/connection_string_spec.rb +6 -31
- data/spec/spec_tests/data/auth/connection-string.yml +297 -0
- data/spec/spec_tests/data/cmap/pool-checkout-error-closed.yml +4 -1
- data/spec/spec_tests/data/cmap/pool-create-with-options.yml +1 -0
- data/spec/spec_tests/data/command_monitoring/insertMany.yml +1 -1
- data/spec/spec_tests/data/connection_string/invalid-uris.yml +20 -0
- data/spec/spec_tests/data/connection_string/valid-auth.yml +16 -0
- data/spec/spec_tests/data/connection_string/valid-warnings.yml +26 -30
- data/spec/spec_tests/data/transactions/abort.yml +3 -3
- data/spec/spec_tests/data/transactions/error-labels.yml +3 -3
- data/spec/spec_tests/data/transactions_api/callback-retry.yml +3 -3
- data/spec/spec_tests/data/uri_options/auth-options.yml +1 -1
- data/spec/spec_tests/max_staleness_spec.rb +7 -2
- data/spec/spec_tests/retryable_reads_spec.rb +0 -31
- data/spec/spec_tests/sdam_monitoring_spec.rb +12 -12
- data/spec/spec_tests/sdam_spec.rb +4 -7
- data/spec/spec_tests/server_selection_spec.rb +6 -2
- data/spec/spec_tests/transactions_spec.rb +0 -2
- data/spec/spec_tests/uri_options_spec.rb +4 -2
- data/spec/stress/connection_pool_stress_spec.rb +203 -0
- data/spec/stress/connection_pool_timing_spec.rb +181 -0
- data/spec/support/auth.rb +113 -0
- data/spec/support/background_thread_registry.rb +63 -0
- data/spec/support/client_registry.rb +11 -2
- data/spec/support/cluster_config.rb +65 -46
- data/spec/support/cluster_tools.rb +2 -2
- data/spec/support/cmap.rb +13 -14
- data/spec/support/cmap/verifier.rb +4 -5
- data/spec/support/command_monitoring.rb +0 -5
- data/spec/support/common_shortcuts.rb +101 -1
- data/spec/support/constraints.rb +25 -0
- data/spec/support/dns.rb +13 -0
- data/spec/support/event_subscriber.rb +0 -7
- data/spec/support/json_ext_formatter.rb +5 -1
- data/spec/support/lite_constraints.rb +22 -6
- data/spec/support/local_resource_registry.rb +34 -0
- data/spec/support/sdam_monitoring.rb +115 -0
- data/spec/support/spec_config.rb +20 -6
- data/spec/support/spec_setup.rb +2 -2
- data/spec/support/transactions.rb +1 -1
- data/spec/support/transactions/test.rb +1 -1
- data/spec/support/utils.rb +1 -16
- metadata +685 -659
- metadata.gz.sig +0 -0
- data/lib/mongo/event/description_changed.rb +0 -52
- data/spec/integration/bson_symbol_spec.rb +0 -34
- data/spec/integration/crud_spec.rb +0 -45
- data/spec/integration/get_more_spec.rb +0 -32
- data/spec/integration/grid_fs_bucket_spec.rb +0 -48
- data/spec/integration/retryable_errors_spec.rb +0 -265
- data/spec/integration/size_limit_spec.rb~12e1e9c4f... RUBY-2242 Fix zlib compression (#2021) +0 -98
- data/spec/mongo/cursor/builder/op_kill_cursors_spec.rb +0 -56
- data/spec/runners/sdam/verifier.rb +0 -88
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Connection pool timing test' do
|
4
|
+
before(:all) do
|
5
|
+
if !SpecConfig.instance.stress_spec?
|
6
|
+
skip 'Stress spec not enabled'
|
7
|
+
end
|
8
|
+
|
9
|
+
ClientRegistry.instance.close_all_clients
|
10
|
+
|
11
|
+
# This set up is taken from the step_down_spec file. In a future PR, ClusterTools
|
12
|
+
# may be modified so this set up is no longer necessary.
|
13
|
+
if ClusterConfig.instance.fcv_ish >= '4.2' && ClusterConfig.instance.topology == :replica_set
|
14
|
+
ClusterTools.instance.set_election_timeout(5)
|
15
|
+
ClusterTools.instance.set_election_handoff(false)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
after(:all) do
|
20
|
+
if ClusterConfig.instance.fcv_ish >= '4.2' && ClusterConfig.instance.topology == :replica_set
|
21
|
+
ClusterTools.instance.set_election_timeout(10)
|
22
|
+
ClusterTools.instance.set_election_handoff(true)
|
23
|
+
ClusterTools.instance.reset_priorities
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
let(:client) do
|
28
|
+
authorized_client.with(options.merge(monitoring: true))
|
29
|
+
end
|
30
|
+
|
31
|
+
let!(:collection) do
|
32
|
+
client[authorized_collection.name].tap do |collection|
|
33
|
+
collection.drop
|
34
|
+
collection.insert_many(documents)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
let(:documents) do
|
39
|
+
[].tap do |documents|
|
40
|
+
10000.times do |i|
|
41
|
+
documents << { a: i}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
let(:operation_threads) do
|
47
|
+
[].tap do |threads|
|
48
|
+
thread_count.times do |i|
|
49
|
+
threads << Thread.new do
|
50
|
+
100.times do |j|
|
51
|
+
collection.find(a: i+j).to_a
|
52
|
+
sleep 0.01
|
53
|
+
collection.find(a: i+j).to_a
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
let(:thread_count) { 5 }
|
61
|
+
|
62
|
+
context 'when there is no max idle time' do
|
63
|
+
let(:options) do
|
64
|
+
{ max_pool_size: 10, min_pool_size: 5 }
|
65
|
+
end
|
66
|
+
|
67
|
+
let(:threads) { operation_threads }
|
68
|
+
|
69
|
+
it 'does not error' do
|
70
|
+
start = Time.now
|
71
|
+
expect {
|
72
|
+
threads.collect { |t| t.join }
|
73
|
+
}.not_to raise_error
|
74
|
+
puts "[Connection Pool Timing] Duration with no max idle time: #{Time.now - start}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when there is a low max idle time' do
|
79
|
+
let(:options) do
|
80
|
+
{ max_pool_size: 10, min_pool_size: 5, max_idle_time: 0.1 }
|
81
|
+
end
|
82
|
+
|
83
|
+
let(:threads) { operation_threads }
|
84
|
+
|
85
|
+
it 'does not error' do
|
86
|
+
start = Time.now
|
87
|
+
expect {
|
88
|
+
threads.collect { |t| t.join }
|
89
|
+
}.not_to raise_error
|
90
|
+
puts "[Connection Pool Timing] Duration with low max idle time: #{Time.now - start}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'when clear is called periodically' do
|
95
|
+
let(:options) do
|
96
|
+
{ max_pool_size: 10, min_pool_size: 5 }
|
97
|
+
end
|
98
|
+
|
99
|
+
let(:threads) do
|
100
|
+
threads = operation_threads
|
101
|
+
threads << Thread.new do
|
102
|
+
10.times do
|
103
|
+
sleep 0.1
|
104
|
+
client.cluster.next_primary.pool.clear
|
105
|
+
end
|
106
|
+
end
|
107
|
+
threads
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'does not error' do
|
111
|
+
start = Time.now
|
112
|
+
expect {
|
113
|
+
threads.collect { |t| t.join }
|
114
|
+
}.not_to raise_error
|
115
|
+
puts "[Connection Pool Timing] Duration when clear is called periodically: #{Time.now - start}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'when primary is changed, then more operations are performed' do
|
120
|
+
min_server_fcv '4.2'
|
121
|
+
require_topology :replica_set
|
122
|
+
|
123
|
+
let(:options) do
|
124
|
+
{ max_pool_size: 10, min_pool_size: 5 }
|
125
|
+
end
|
126
|
+
|
127
|
+
let(:more_threads) do
|
128
|
+
PossiblyConcurrentArray.new.tap do |more_threads|
|
129
|
+
5.times do |i|
|
130
|
+
more_threads << Thread.new do
|
131
|
+
10.times do |j|
|
132
|
+
collection.find(a: i+j).to_a
|
133
|
+
sleep 0.01
|
134
|
+
collection.find(a: i+j).to_a
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
let(:threads) do
|
142
|
+
threads = PossiblyConcurrentArray.new
|
143
|
+
|
144
|
+
5.times do |i|
|
145
|
+
threads << Thread.new do
|
146
|
+
10.times do |j|
|
147
|
+
collection.find(a: i+j).to_a
|
148
|
+
sleep 0.01
|
149
|
+
collection.find(a: i+j).to_a
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
threads << Thread.new do
|
155
|
+
# Wait for other threads to terminate first, otherwise we get an error
|
156
|
+
# when trying to perform operations during primary change
|
157
|
+
sleep 1
|
158
|
+
|
159
|
+
@primary_chane_start = Time.now
|
160
|
+
ClusterTools.instance.change_primary
|
161
|
+
@primary_change_end = Time.now
|
162
|
+
|
163
|
+
# Primary change is complete; execute more operations
|
164
|
+
more_threads.collect { |t| t.join }
|
165
|
+
end
|
166
|
+
threads
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'does not error' do
|
170
|
+
threads
|
171
|
+
start = Time.now
|
172
|
+
expect do
|
173
|
+
threads.each do |t|
|
174
|
+
t.join
|
175
|
+
end
|
176
|
+
end.not_to raise_error
|
177
|
+
puts "[Connection Pool Timing] Duration before primary change: #{@primary_chane_start - start}. "\
|
178
|
+
"Duration after primary change: #{Time.now - @primary_change_end}"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# Copyright (C) 2014-2019 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
RSpec::Matchers.define :have_blank_credentials do
|
15
|
+
match do |client|
|
16
|
+
%i(auth_mech auth_mech_properties auth_source password user).all? do |key|
|
17
|
+
client.options[key].nil?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
failure_message do |client|
|
22
|
+
"Expected client to have blank credentials, but got the following credentials: \n\n" +
|
23
|
+
client.options.inspect
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Mongo
|
28
|
+
module Auth
|
29
|
+
class Spec
|
30
|
+
|
31
|
+
attr_reader :description
|
32
|
+
attr_reader :tests
|
33
|
+
|
34
|
+
def initialize(file)
|
35
|
+
file = File.new(file)
|
36
|
+
@spec = YAML.load(ERB.new(file.read).result)
|
37
|
+
file.close
|
38
|
+
@description = File.basename(file)
|
39
|
+
end
|
40
|
+
|
41
|
+
def tests
|
42
|
+
@tests ||= @spec['tests'].collect do |spec|
|
43
|
+
Test.new(spec)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Test
|
49
|
+
attr_reader :description
|
50
|
+
attr_reader :uri_string
|
51
|
+
|
52
|
+
def initialize(spec)
|
53
|
+
@spec = spec
|
54
|
+
@description = @spec['description']
|
55
|
+
@uri_string = @spec['uri']
|
56
|
+
end
|
57
|
+
|
58
|
+
def valid?
|
59
|
+
@spec['valid']
|
60
|
+
end
|
61
|
+
|
62
|
+
def credential
|
63
|
+
@spec['credential']
|
64
|
+
end
|
65
|
+
|
66
|
+
def client
|
67
|
+
@client ||= ClientRegistry.instance.new_local_client(@spec['uri'], monitoring_io: false)
|
68
|
+
end
|
69
|
+
|
70
|
+
def expected_credential
|
71
|
+
expected_credential = {
|
72
|
+
'auth_source' => expected_auth_source,
|
73
|
+
}
|
74
|
+
|
75
|
+
if credential['username']
|
76
|
+
expected_credential['user'] = credential['username']
|
77
|
+
expected_credential['password'] = credential['password']
|
78
|
+
end
|
79
|
+
|
80
|
+
if credential['mechanism']
|
81
|
+
expected_credential['auth_mech'] = expected_auth_mech
|
82
|
+
end
|
83
|
+
|
84
|
+
if credential['mechanism_properties']
|
85
|
+
expected_credential['auth_mech_properties'] = expected_auth_mech_properties
|
86
|
+
end
|
87
|
+
|
88
|
+
expected_credential
|
89
|
+
end
|
90
|
+
|
91
|
+
def received_credential
|
92
|
+
client.options.select do |k, _|
|
93
|
+
%w(auth_mech auth_mech_properties auth_source password user).include?(k)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def expected_auth_mech
|
100
|
+
Mongo::URI::AUTH_MECH_MAP[credential['mechanism']]
|
101
|
+
end
|
102
|
+
|
103
|
+
def expected_auth_mech_properties
|
104
|
+
credential['mechanism_properties'].keys.map(&:downcase)
|
105
|
+
end
|
106
|
+
|
107
|
+
def expected_auth_source
|
108
|
+
return :external if credential['source'] == '$external'
|
109
|
+
credential['source']
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module Mongo
|
5
|
+
module BackgroundThread
|
6
|
+
|
7
|
+
alias :start_without_tracking! :start!
|
8
|
+
|
9
|
+
def start!
|
10
|
+
start_without_tracking!.tap do |thread|
|
11
|
+
BackgroundThreadRegistry.instance.register(self, thread)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class BackgroundThreadRegistry
|
18
|
+
include Singleton
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@lock = Mutex.new
|
22
|
+
@records = []
|
23
|
+
end
|
24
|
+
|
25
|
+
def register(object, thread)
|
26
|
+
@lock.synchronize do
|
27
|
+
@records << OpenStruct.new(
|
28
|
+
thread: thread,
|
29
|
+
object: object,
|
30
|
+
example: $current_example,
|
31
|
+
)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def verify_empty!
|
36
|
+
@lock.synchronize do
|
37
|
+
alive_thread_records = @records.select { |record| record.thread.alive? }
|
38
|
+
if alive_thread_records.any?
|
39
|
+
msg = "Live background threads after closing all clients:"
|
40
|
+
alive_thread_records.each do |record|
|
41
|
+
msg << "\n #{record.object}"
|
42
|
+
if record.object.respond_to?(:options)
|
43
|
+
msg << "\n #{record.object.options}"
|
44
|
+
end
|
45
|
+
msg << "\n in #{record.example.id} #{record.example.full_description}"
|
46
|
+
end
|
47
|
+
raise msg
|
48
|
+
end
|
49
|
+
@records.clear
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
RSpec.configure do |config|
|
55
|
+
config.around do |example|
|
56
|
+
$current_example = example
|
57
|
+
begin
|
58
|
+
example.run
|
59
|
+
ensure
|
60
|
+
$current_example = nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -209,7 +209,16 @@ class ClientRegistry
|
|
209
209
|
|
210
210
|
def close_local_clients
|
211
211
|
@lock.synchronize do
|
212
|
-
@local_clients.
|
212
|
+
@local_clients.each do |client|
|
213
|
+
cluster = client.cluster
|
214
|
+
# Disconnect this cluster if and only if it is not shared with
|
215
|
+
# any of the global clients we know about.
|
216
|
+
if @global_clients.none? { |name, global_client|
|
217
|
+
cluster.object_id == global_client.cluster.object_id
|
218
|
+
}
|
219
|
+
cluster.disconnect!
|
220
|
+
end
|
221
|
+
end
|
213
222
|
@local_clients = []
|
214
223
|
end
|
215
224
|
end
|
@@ -219,7 +228,7 @@ class ClientRegistry
|
|
219
228
|
close_local_clients
|
220
229
|
@lock.synchronize do
|
221
230
|
@global_clients.each do |name, client|
|
222
|
-
client.close
|
231
|
+
client.close
|
223
232
|
end
|
224
233
|
end
|
225
234
|
end
|
@@ -3,36 +3,19 @@ require 'singleton'
|
|
3
3
|
class ClusterConfig
|
4
4
|
include Singleton
|
5
5
|
|
6
|
-
def basic_client
|
7
|
-
# Do not cache the result here so that if the client gets closed,
|
8
|
-
# client registry reconnects it in subsequent tests
|
9
|
-
ClientRegistry.instance.global_client('basic')
|
10
|
-
end
|
11
|
-
|
12
6
|
def single_server?
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
def mongos?
|
17
|
-
if @mongos.nil?
|
18
|
-
basic_client.cluster.next_primary
|
19
|
-
@mongos = basic_client.cluster.topology.is_a?(Mongo::Cluster::Topology::Sharded)
|
20
|
-
end
|
21
|
-
@mongos
|
7
|
+
determine_cluster_config
|
8
|
+
@single_server
|
22
9
|
end
|
23
10
|
|
24
11
|
def replica_set_name
|
25
|
-
|
26
|
-
|
27
|
-
basic_client.cluster.topology.replica_set_name
|
28
|
-
end
|
12
|
+
determine_cluster_config
|
13
|
+
@replica_set_name
|
29
14
|
end
|
30
15
|
|
31
16
|
def server_version
|
32
|
-
|
33
|
-
|
34
|
-
client.database.command(buildInfo: 1).first['version']
|
35
|
-
end
|
17
|
+
determine_cluster_config
|
18
|
+
@server_version
|
36
19
|
end
|
37
20
|
|
38
21
|
def short_server_version
|
@@ -40,11 +23,8 @@ class ClusterConfig
|
|
40
23
|
end
|
41
24
|
|
42
25
|
def fcv
|
43
|
-
|
44
|
-
|
45
|
-
rv = client.use(:admin).command(getParameter: 1, featureCompatibilityVersion: 1).first['featureCompatibilityVersion']
|
46
|
-
rv['version'] || rv
|
47
|
-
end
|
26
|
+
determine_cluster_config
|
27
|
+
@fcv
|
48
28
|
end
|
49
29
|
|
50
30
|
# Per https://jira.mongodb.org/browse/SERVER-39052, working with FCV
|
@@ -52,7 +32,7 @@ class ClusterConfig
|
|
52
32
|
# less than 3.4. This method returns FCV on 3.4+ servers when in single
|
53
33
|
# or RS topologies, and otherwise returns the major.minor server version.
|
54
34
|
def fcv_ish
|
55
|
-
if server_version >= '3.4' &&
|
35
|
+
if server_version >= '3.4' && topology != :sharded
|
56
36
|
fcv
|
57
37
|
else
|
58
38
|
if short_server_version == '4.1'
|
@@ -64,18 +44,13 @@ class ClusterConfig
|
|
64
44
|
end
|
65
45
|
|
66
46
|
def primary_address
|
67
|
-
|
68
|
-
|
69
|
-
if client.cluster.topology.is_a?(Mongo::Cluster::Topology::ReplicaSetWithPrimary)
|
70
|
-
client.cluster.servers.detect { |server| server.primary? }.address
|
71
|
-
else
|
72
|
-
client.cluster.servers.first.address
|
73
|
-
end.seed
|
74
|
-
end
|
47
|
+
determine_cluster_config
|
48
|
+
@primary_address
|
75
49
|
end
|
76
50
|
|
77
51
|
def primary_address_str
|
78
|
-
|
52
|
+
determine_cluster_config
|
53
|
+
@primary_address.seed
|
79
54
|
end
|
80
55
|
|
81
56
|
def primary_address_host
|
@@ -88,6 +63,11 @@ class ClusterConfig
|
|
88
63
|
both.split(':')[1] || 27017
|
89
64
|
end
|
90
65
|
|
66
|
+
def primary_description
|
67
|
+
determine_cluster_config
|
68
|
+
@primary_description
|
69
|
+
end
|
70
|
+
|
91
71
|
# Try running a command on the admin database to see if the mongod was
|
92
72
|
# started with auth.
|
93
73
|
def auth_enabled?
|
@@ -102,14 +82,8 @@ class ClusterConfig
|
|
102
82
|
end
|
103
83
|
|
104
84
|
def topology
|
105
|
-
|
106
|
-
|
107
|
-
topology = topology.gsub(/([A-Z])/) { |match| '_' + match.downcase }.sub(/^_/, '')
|
108
|
-
if topology =~ /^replica_set/
|
109
|
-
topology = 'replica_set'
|
110
|
-
end
|
111
|
-
topology.to_sym
|
112
|
-
end
|
85
|
+
determine_cluster_config
|
86
|
+
@topology
|
113
87
|
end
|
114
88
|
|
115
89
|
def storage_engine
|
@@ -121,6 +95,9 @@ class ClusterConfig
|
|
121
95
|
client = ClientRegistry.instance.global_client('root_authorized')
|
122
96
|
if topology == :sharded
|
123
97
|
shards = client.use(:admin).command(listShards: 1).first
|
98
|
+
if shards['shards'].empty?
|
99
|
+
raise 'Shards are empty'
|
100
|
+
end
|
124
101
|
shard = shards['shards'].first
|
125
102
|
address_str = shard['host'].sub(/^.*\//, '').sub(/,.*/, '')
|
126
103
|
client = ClusterTools.instance.direct_client(address_str,
|
@@ -136,4 +113,46 @@ class ClusterConfig
|
|
136
113
|
end
|
137
114
|
end
|
138
115
|
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def determine_cluster_config
|
120
|
+
return if @primary_address
|
121
|
+
|
122
|
+
# Run all commands to figure out the cluster configuration from the same
|
123
|
+
# client. This is somewhat wasteful when running a single test, but reduces
|
124
|
+
# test runtime for the suite overall because all commands are sent on the
|
125
|
+
# same connection rather than each command connecting to the cluster by
|
126
|
+
# itself.
|
127
|
+
client = ClientRegistry.instance.global_client('root_authorized')
|
128
|
+
|
129
|
+
primary = client.cluster.next_primary
|
130
|
+
@primary_address = primary.address
|
131
|
+
@primary_description = primary.description
|
132
|
+
@replica_set_name = client.cluster.topology.replica_set_name
|
133
|
+
|
134
|
+
@topology ||= begin
|
135
|
+
topology = client.cluster.topology.class.name.sub(/.*::/, '')
|
136
|
+
topology = topology.gsub(/([A-Z])/) { |match| '_' + match.downcase }.sub(/^_/, '')
|
137
|
+
if topology =~ /^replica_set/
|
138
|
+
topology = 'replica_set'
|
139
|
+
end
|
140
|
+
topology.to_sym
|
141
|
+
end
|
142
|
+
|
143
|
+
@single_server = client.cluster.servers_list.length == 1
|
144
|
+
|
145
|
+
@server_version = client.database.command(buildInfo: 1).first['version']
|
146
|
+
|
147
|
+
if @topology != :sharded && short_server_version >= '3.4'
|
148
|
+
rv = client.use(:admin).command(getParameter: 1, featureCompatibilityVersion: 1).first['featureCompatibilityVersion']
|
149
|
+
@fcv = rv['version'] || rv
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def basic_client
|
154
|
+
# Do not cache the result here so that if the client gets closed,
|
155
|
+
# client registry reconnects it in subsequent tests
|
156
|
+
ClientRegistry.instance.global_client('basic')
|
157
|
+
end
|
139
158
|
end
|