mongo 2.13.0.beta1 → 2.13.0.rc1
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 +1 -5
- data/Rakefile +15 -9
- data/lib/mongo.rb +4 -2
- data/lib/mongo/auth/aws/request.rb +4 -2
- data/lib/mongo/bulk_write.rb +1 -0
- data/lib/mongo/client.rb +143 -21
- data/lib/mongo/cluster.rb +53 -17
- data/lib/mongo/cluster/sdam_flow.rb +13 -10
- data/lib/mongo/cluster/topology/replica_set_no_primary.rb +3 -2
- data/lib/mongo/cluster/topology/sharded.rb +1 -1
- data/lib/mongo/cluster/topology/single.rb +1 -1
- data/lib/mongo/collection.rb +17 -13
- data/lib/mongo/collection/view/readable.rb +3 -1
- data/lib/mongo/collection/view/writable.rb +41 -5
- data/lib/mongo/database.rb +31 -4
- data/lib/mongo/database/view.rb +19 -4
- data/lib/mongo/distinguishing_semaphore.rb +55 -0
- data/lib/mongo/error.rb +1 -0
- data/lib/mongo/error/invalid_session.rb +2 -1
- data/lib/mongo/error/operation_failure.rb +6 -0
- data/lib/mongo/error/sessions_not_supported.rb +35 -0
- data/lib/mongo/event/base.rb +6 -0
- data/lib/mongo/grid/file.rb +5 -0
- data/lib/mongo/grid/file/chunk.rb +2 -0
- data/lib/mongo/grid/fs_bucket.rb +15 -13
- data/lib/mongo/grid/stream/write.rb +9 -3
- data/lib/mongo/monitoring.rb +38 -0
- data/lib/mongo/monitoring/command_log_subscriber.rb +10 -2
- data/lib/mongo/monitoring/event/command_failed.rb +11 -0
- data/lib/mongo/monitoring/event/command_started.rb +37 -2
- data/lib/mongo/monitoring/event/command_succeeded.rb +11 -0
- data/lib/mongo/monitoring/event/server_closed.rb +1 -1
- data/lib/mongo/monitoring/event/server_description_changed.rb +27 -4
- data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +9 -2
- data/lib/mongo/monitoring/event/server_heartbeat_started.rb +9 -2
- data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +9 -2
- data/lib/mongo/monitoring/event/server_opening.rb +1 -1
- data/lib/mongo/monitoring/event/topology_changed.rb +1 -1
- data/lib/mongo/monitoring/event/topology_closed.rb +1 -1
- data/lib/mongo/monitoring/event/topology_opening.rb +1 -1
- data/lib/mongo/monitoring/publishable.rb +6 -3
- data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +9 -1
- data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +1 -1
- data/lib/mongo/protocol/message.rb +36 -8
- data/lib/mongo/protocol/msg.rb +14 -0
- data/lib/mongo/protocol/serializers.rb +5 -2
- data/lib/mongo/server.rb +10 -3
- data/lib/mongo/server/connection.rb +4 -4
- data/lib/mongo/server/connection_base.rb +3 -1
- data/lib/mongo/server/description.rb +5 -0
- data/lib/mongo/server/monitor.rb +76 -44
- data/lib/mongo/server/monitor/connection.rb +55 -7
- data/lib/mongo/server/pending_connection.rb +14 -4
- data/lib/mongo/server/push_monitor.rb +173 -0
- data/{spec/runners/transactions/context.rb → lib/mongo/server/push_monitor/connection.rb} +9 -14
- data/lib/mongo/server_selector.rb +0 -1
- data/lib/mongo/server_selector/base.rb +579 -1
- data/lib/mongo/server_selector/nearest.rb +1 -6
- data/lib/mongo/server_selector/primary.rb +1 -6
- data/lib/mongo/server_selector/primary_preferred.rb +7 -10
- data/lib/mongo/server_selector/secondary.rb +1 -6
- data/lib/mongo/server_selector/secondary_preferred.rb +1 -7
- data/lib/mongo/session.rb +2 -0
- data/lib/mongo/socket.rb +20 -8
- data/lib/mongo/socket/ssl.rb +1 -1
- data/lib/mongo/socket/tcp.rb +1 -1
- data/lib/mongo/topology_version.rb +9 -0
- data/lib/mongo/utils.rb +62 -0
- data/lib/mongo/version.rb +1 -1
- data/spec/README.aws-auth.md +2 -2
- data/spec/integration/awaited_ismaster_spec.rb +28 -0
- data/spec/integration/change_stream_examples_spec.rb +6 -2
- data/spec/integration/check_clean_slate_spec.rb +16 -0
- data/spec/integration/client_construction_spec.rb +1 -0
- data/spec/integration/connect_single_rs_name_spec.rb +5 -2
- data/spec/integration/connection_spec.rb +7 -4
- data/spec/integration/crud_spec.rb +4 -4
- data/spec/integration/docs_examples_spec.rb +6 -0
- data/spec/integration/grid_fs_bucket_spec.rb +48 -0
- data/spec/integration/heartbeat_events_spec.rb +4 -23
- data/spec/integration/read_concern_spec.rb +1 -1
- data/spec/integration/retryable_errors_spec.rb +1 -1
- data/spec/integration/retryable_writes/shared/performs_legacy_retries.rb +2 -2
- data/spec/integration/retryable_writes/shared/performs_modern_retries.rb +3 -3
- data/spec/integration/retryable_writes/shared/performs_no_retries.rb +2 -2
- data/spec/integration/sdam_error_handling_spec.rb +37 -15
- data/spec/integration/sdam_events_spec.rb +77 -6
- data/spec/integration/sdam_prose_spec.rb +64 -0
- data/spec/integration/server_monitor_spec.rb +25 -1
- data/spec/integration/size_limit_spec.rb +7 -3
- data/spec/integration/size_limit_spec.rb~12e1e9c4f... RUBY-2242 Fix zlib compression (#2021) +98 -0
- data/spec/integration/ssl_uri_options_spec.rb +2 -2
- data/spec/integration/zlib_compression_spec.rb +25 -0
- data/spec/lite_spec_helper.rb +12 -5
- data/spec/mongo/auth/aws/request_spec.rb +76 -0
- data/spec/mongo/auth/scram_spec.rb +1 -1
- data/spec/mongo/client_construction_spec.rb +207 -0
- data/spec/mongo/client_spec.rb +38 -3
- data/spec/mongo/cluster/topology/replica_set_spec.rb +52 -9
- data/spec/mongo/cluster/topology/single_spec.rb +4 -2
- data/spec/mongo/cluster_spec.rb +34 -35
- data/spec/mongo/collection/view/change_stream_resume_spec.rb +6 -6
- data/spec/mongo/collection_spec.rb +500 -0
- data/spec/mongo/database_spec.rb +245 -8
- data/spec/mongo/distinguishing_semaphore_spec.rb +63 -0
- data/spec/mongo/error/operation_failure_spec.rb +40 -0
- data/spec/mongo/index/view_spec.rb +2 -2
- data/spec/mongo/monitoring/event/server_description_changed_spec.rb +1 -4
- data/spec/mongo/protocol/msg_spec.rb +10 -0
- data/spec/mongo/semaphore_spec.rb +51 -0
- data/spec/mongo/server/connection_auth_spec.rb +2 -2
- data/spec/mongo/server_selector/nearest_spec.rb +23 -23
- data/spec/mongo/server_selector/primary_preferred_spec.rb +26 -26
- data/spec/mongo/server_selector/primary_spec.rb +9 -9
- data/spec/mongo/server_selector/secondary_preferred_spec.rb +22 -22
- data/spec/mongo/server_selector/secondary_spec.rb +18 -18
- data/spec/mongo/server_selector_spec.rb +4 -4
- data/spec/mongo/session_spec.rb +35 -0
- data/spec/runners/change_streams/test.rb +2 -2
- data/spec/runners/cmap.rb +1 -1
- data/spec/runners/command_monitoring.rb +3 -34
- data/spec/runners/crud/context.rb +9 -5
- data/spec/runners/crud/operation.rb +59 -27
- data/spec/runners/crud/spec.rb +0 -8
- data/spec/runners/crud/test.rb +1 -1
- data/spec/runners/sdam.rb +2 -2
- data/spec/runners/server_selection.rb +242 -28
- data/spec/runners/transactions.rb +12 -12
- data/spec/runners/transactions/operation.rb +151 -25
- data/spec/runners/transactions/test.rb +60 -16
- data/spec/spec_tests/command_monitoring_spec.rb +22 -12
- data/spec/spec_tests/crud_spec.rb +1 -1
- data/spec/spec_tests/data/change_streams/change-streams-errors.yml +4 -8
- data/spec/spec_tests/data/change_streams/change-streams-resume-whitelist.yml +66 -0
- data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/MaxStalenessTooSmall.yml +15 -0
- data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/NoKnownServers.yml +4 -3
- data/spec/spec_tests/data/max_staleness/Unknown/SmallMaxStaleness.yml +1 -0
- data/spec/spec_tests/data/sdam_integration/cancel-server-check.yml +96 -0
- data/spec/spec_tests/data/sdam_integration/connectTimeoutMS.yml +88 -0
- data/spec/spec_tests/data/sdam_integration/find-network-error.yml +83 -0
- data/spec/spec_tests/data/sdam_integration/find-shutdown-error.yml +116 -0
- data/spec/spec_tests/data/sdam_integration/insert-network-error.yml +86 -0
- data/spec/spec_tests/data/sdam_integration/insert-shutdown-error.yml +115 -0
- data/spec/spec_tests/data/sdam_integration/isMaster-command-error.yml +168 -0
- data/spec/spec_tests/data/sdam_integration/isMaster-network-error.yml +162 -0
- data/spec/spec_tests/data/sdam_integration/isMaster-timeout.yml +229 -0
- data/spec/spec_tests/data/sdam_integration/rediscover-quickly-after-step-down.yml +87 -0
- data/spec/spec_tests/max_staleness_spec.rb +4 -142
- data/spec/spec_tests/retryable_reads_spec.rb +2 -2
- data/spec/spec_tests/sdam_integration_spec.rb +13 -0
- data/spec/spec_tests/sdam_monitoring_spec.rb +1 -2
- data/spec/spec_tests/server_selection_spec.rb +4 -116
- data/spec/stress/cleanup_spec.rb +17 -2
- data/spec/stress/connection_pool_stress_spec.rb +10 -8
- data/spec/support/child_process_helper.rb +78 -0
- data/spec/support/client_registry.rb +1 -0
- data/spec/support/cluster_config.rb +4 -0
- data/spec/support/event_subscriber.rb +123 -33
- data/spec/support/keyword_struct.rb +26 -0
- data/spec/support/shared/server_selector.rb +13 -1
- data/spec/support/spec_config.rb +38 -13
- data/spec/support/spec_organizer.rb +129 -0
- data/spec/support/spec_setup.rb +1 -1
- data/spec/support/utils.rb +46 -0
- metadata +992 -942
- metadata.gz.sig +0 -0
- data/lib/mongo/server_selector/selectable.rb +0 -560
- data/spec/runners/sdam_monitoring.rb +0 -89
@@ -8,7 +8,7 @@ describe 'Retryable reads spec tests' do
|
|
8
8
|
|
9
9
|
define_crud_spec_tests(RETRYABLE_READS_TESTS) do |spec, req, test|
|
10
10
|
let(:client) do
|
11
|
-
authorized_client.with({max_read_retries: 0}.update(test.client_options)).tap do |client|
|
11
|
+
authorized_client.use(spec.database_name).with({max_read_retries: 0}.update(test.client_options)).tap do |client|
|
12
12
|
client.subscribe(Mongo::Monitoring::COMMAND, event_subscriber)
|
13
13
|
end
|
14
14
|
end
|
@@ -28,7 +28,7 @@ describe 'Retryable reads spec tests - legacy' do
|
|
28
28
|
end
|
29
29
|
|
30
30
|
let(:client) do
|
31
|
-
authorized_client.with(client_options).tap do |client|
|
31
|
+
authorized_client.use(spec.database_name).with(client_options).tap do |client|
|
32
32
|
client.subscribe(Mongo::Monitoring::COMMAND, event_subscriber)
|
33
33
|
end
|
34
34
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'runners/crud'
|
4
|
+
require 'runners/transactions'
|
5
|
+
|
6
|
+
SDAM_INTEGRATION_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/sdam_integration/*.yml").sort
|
7
|
+
|
8
|
+
describe 'SDAM integration tests' do
|
9
|
+
require_no_multi_shard
|
10
|
+
require_wired_tiger
|
11
|
+
|
12
|
+
define_transactions_spec_tests(SDAM_INTEGRATION_TESTS)
|
13
|
+
end
|
@@ -2,7 +2,6 @@ require 'lite_spec_helper'
|
|
2
2
|
|
3
3
|
require 'runners/sdam'
|
4
4
|
require 'runners/sdam/verifier'
|
5
|
-
require 'runners/sdam_monitoring'
|
6
5
|
|
7
6
|
describe 'SDAM Monitoring' do
|
8
7
|
include Mongo::SDAM
|
@@ -14,7 +13,7 @@ describe 'SDAM Monitoring' do
|
|
14
13
|
context("#{spec.description} (#{file.sub(%r'.*/data/sdam_monitoring/', '')})") do
|
15
14
|
|
16
15
|
before(:all) do
|
17
|
-
@subscriber =
|
16
|
+
@subscriber = PhasedEventSubscriber.new
|
18
17
|
sdam_proc = lambda do |client|
|
19
18
|
client.subscribe(Mongo::Monitoring::SERVER_OPENING, @subscriber)
|
20
19
|
client.subscribe(Mongo::Monitoring::SERVER_CLOSED, @subscriber)
|
@@ -1,121 +1,9 @@
|
|
1
|
-
require '
|
1
|
+
require 'lite_spec_helper'
|
2
2
|
|
3
3
|
require 'runners/server_selection'
|
4
4
|
|
5
|
-
|
5
|
+
SERVER_SELECTION_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/server_selection/**/*.yml").sort
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
SERVER_SELECTION_TESTS.each do |file|
|
10
|
-
|
11
|
-
spec = Mongo::ServerSelection::Read::Spec.new(file)
|
12
|
-
|
13
|
-
context(spec.description) do
|
14
|
-
|
15
|
-
let(:monitoring) do
|
16
|
-
Mongo::Monitoring.new(monitoring: false)
|
17
|
-
end
|
18
|
-
|
19
|
-
# Cluster needs a topology and topology needs a cluster...
|
20
|
-
# This temporary cluster is used for topology construction.
|
21
|
-
let(:temp_cluster) do
|
22
|
-
double('temp cluster').tap do |cluster|
|
23
|
-
allow(cluster).to receive(:servers_list).and_return([])
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
let(:topology) do
|
28
|
-
options = if spec.type <= Mongo::Cluster::Topology::ReplicaSetNoPrimary
|
29
|
-
{replica_set_name: 'foo'}
|
30
|
-
else
|
31
|
-
{}
|
32
|
-
end
|
33
|
-
spec.type.new(options, monitoring, temp_cluster)
|
34
|
-
end
|
35
|
-
|
36
|
-
let(:listeners) do
|
37
|
-
Mongo::Event::Listeners.new
|
38
|
-
end
|
39
|
-
|
40
|
-
let(:cluster) do
|
41
|
-
double('cluster').tap do |c|
|
42
|
-
allow(c).to receive(:connected?).and_return(true)
|
43
|
-
allow(c).to receive(:summary)
|
44
|
-
allow(c).to receive(:topology).and_return(topology)
|
45
|
-
allow(c).to receive(:single?).and_return(topology.single?)
|
46
|
-
allow(c).to receive(:sharded?).and_return(topology.sharded?)
|
47
|
-
allow(c).to receive(:replica_set?).and_return(topology.replica_set?)
|
48
|
-
allow(c).to receive(:unknown?).and_return(topology.unknown?)
|
49
|
-
allow(c).to receive(:app_metadata).and_return(app_metadata)
|
50
|
-
allow(c).to receive(:options).and_return({})
|
51
|
-
allow(c).to receive(:server_selection_semaphore).and_return(nil)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
let(:candidate_servers) do
|
56
|
-
spec.candidate_servers.collect do |server|
|
57
|
-
address = Mongo::Address.new(server['address'])
|
58
|
-
Mongo::Server.new(address, cluster, monitoring, listeners,
|
59
|
-
{monitoring_io: false}.update(SpecConfig.instance.test_options)
|
60
|
-
).tap do |s|
|
61
|
-
allow(s).to receive(:average_round_trip_time).and_return(server['avg_rtt_ms'] / 1000.0)
|
62
|
-
allow(s).to receive(:tags).and_return(server['tags'])
|
63
|
-
allow(s).to receive(:secondary?).and_return(server['type'] == 'RSSecondary')
|
64
|
-
allow(s).to receive(:primary?).and_return(server['type'] == 'RSPrimary')
|
65
|
-
allow(s).to receive(:connectable?).and_return(true)
|
66
|
-
allow(s).to receive(:check_driver_support!).and_return(true)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
let(:in_latency_window) do
|
72
|
-
spec.in_latency_window.collect do |server|
|
73
|
-
address = Mongo::Address.new(server['address'])
|
74
|
-
Mongo::Server.new(address, cluster, monitoring, listeners,
|
75
|
-
{monitoring_io: false}.update(SpecConfig.instance.test_options)
|
76
|
-
).tap do |s|
|
77
|
-
allow(s).to receive(:average_round_trip_time).and_return(server['avg_rtt_ms'] / 1000.0)
|
78
|
-
allow(s).to receive(:tags).and_return(server['tags'])
|
79
|
-
allow(s).to receive(:connectable?).and_return(true)
|
80
|
-
allow(s).to receive(:check_driver_support!).and_return(true)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
let(:server_selector) do
|
86
|
-
Mongo::ServerSelector.get(:mode => spec.read_preference['mode'],
|
87
|
-
:tag_sets => spec.read_preference['tag_sets'])
|
88
|
-
end
|
89
|
-
|
90
|
-
before do
|
91
|
-
allow(cluster).to receive(:servers).and_return(candidate_servers)
|
92
|
-
allow(cluster).to receive(:servers_list).and_return(candidate_servers)
|
93
|
-
allow(cluster).to receive(:addresses).and_return(candidate_servers.map(&:address))
|
94
|
-
allow(cluster).to receive(:options).and_return(server_selection_timeout: 0.2)
|
95
|
-
allow(cluster).to receive(:scan!).and_return(true)
|
96
|
-
allow(cluster).to receive(:app_metadata).and_return(app_metadata)
|
97
|
-
end
|
98
|
-
|
99
|
-
context 'Valid read preference and matching server available', if: spec.server_available? do
|
100
|
-
|
101
|
-
it 'Finds all suitable servers in the latency window', if: spec.replica_set? do
|
102
|
-
expect(server_selector.send(:select, cluster.servers)).to match_array(in_latency_window)
|
103
|
-
end
|
104
|
-
|
105
|
-
it 'Finds the most suitable server in the latency window' do
|
106
|
-
expect(in_latency_window).to include(server_selector.select_server(cluster))
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
context 'No matching server available', if: !spec.server_available? do
|
111
|
-
skip_if_linting
|
112
|
-
|
113
|
-
it 'Raises exception' do
|
114
|
-
expect do
|
115
|
-
server_selector.select_server(cluster)
|
116
|
-
end.to raise_exception(Mongo::Error::NoServerAvailable)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
7
|
+
describe 'Server selection spec tests' do
|
8
|
+
define_server_selection_spec_tests(SERVER_SELECTION_TESTS)
|
121
9
|
end
|
data/spec/stress/cleanup_spec.rb
CHANGED
@@ -20,17 +20,32 @@ describe 'Cleanup stress test' do
|
|
20
20
|
|
21
21
|
it 'cleans up' do
|
22
22
|
client
|
23
|
+
client.cluster.servers_list.map(&:scan!)
|
24
|
+
|
25
|
+
sleep 1
|
26
|
+
GC.start
|
23
27
|
|
24
28
|
start_resources = resources
|
25
29
|
|
26
|
-
|
30
|
+
500.times do
|
27
31
|
client.close
|
28
32
|
client.reconnect
|
29
33
|
end
|
30
34
|
|
35
|
+
sleep 1
|
36
|
+
GC.start
|
37
|
+
|
31
38
|
end_resources = resources
|
32
39
|
|
33
|
-
|
40
|
+
# There seem to be a temporary file descriptor leak in CI,
|
41
|
+
# where we start with 75 fds and end with 77 fds.
|
42
|
+
# Allow a few to be leaked, run more iterations to ensure the leak
|
43
|
+
# is not a real one.
|
44
|
+
# Sometimes we end with fewer fds than we started with also...
|
45
|
+
end_resources[:open_file_count].should >= start_resources[:open_file_count] - 3
|
46
|
+
end_resources[:open_file_count].should <= start_resources[:open_file_count] + 3
|
47
|
+
|
48
|
+
end_resources[:running_thread_count].should == start_resources[:running_thread_count]
|
34
49
|
end
|
35
50
|
end
|
36
51
|
|
@@ -21,9 +21,9 @@ describe 'Connection pool stress test' do
|
|
21
21
|
[].tap do |threads|
|
22
22
|
thread_count.times do |i|
|
23
23
|
threads << Thread.new do
|
24
|
-
|
24
|
+
100.times do |j|
|
25
25
|
collection.find(a: i+j).to_a
|
26
|
-
sleep 0.
|
26
|
+
sleep 0.1
|
27
27
|
collection.find(a: i+j).to_a
|
28
28
|
end
|
29
29
|
end
|
@@ -132,7 +132,7 @@ describe 'Connection pool stress test' do
|
|
132
132
|
end
|
133
133
|
|
134
134
|
context '25 threads, max pool size 5' do
|
135
|
-
let(:thread_count) {
|
135
|
+
let(:thread_count) { 25 }
|
136
136
|
|
137
137
|
it_behaves_like 'does not raise error'
|
138
138
|
end
|
@@ -183,17 +183,19 @@ describe 'Connection pool stress test' do
|
|
183
183
|
# The populator calls create_and_add_connection, which calls connection.connect!
|
184
184
|
# and raises if an error occurs (eg, an auth error), so in this test we
|
185
185
|
# randomly raise errors on this method.
|
186
|
-
expect(server.pool).to receive(:create_and_add_connection).at_least(:once).and_wrap_original
|
187
|
-
if rand < 0.
|
186
|
+
expect(server.pool).to receive(:create_and_add_connection).at_least(:once).and_wrap_original do |m, *args|
|
187
|
+
if rand < 0.1
|
188
188
|
raise Mongo::Error::SocketError
|
189
189
|
else
|
190
190
|
m.call(*args)
|
191
191
|
end
|
192
|
-
|
192
|
+
end
|
193
193
|
|
194
|
-
|
194
|
+
server.pool.clear(stop_populator: false)
|
195
|
+
|
196
|
+
expect do
|
195
197
|
threads.collect { |t| t.join }
|
196
|
-
|
198
|
+
end.not_to raise_error
|
197
199
|
end
|
198
200
|
end
|
199
201
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
autoload :ChildProcess, 'childprocess'
|
5
|
+
autoload :Tempfile, 'tempfile'
|
6
|
+
|
7
|
+
module ChildProcessHelper
|
8
|
+
class SpawnError < StandardError; end
|
9
|
+
|
10
|
+
module_function def call(cmd, env: nil, cwd: nil)
|
11
|
+
process = ChildProcess.new(*cmd)
|
12
|
+
process.io.inherit!
|
13
|
+
if cwd
|
14
|
+
process.cwd = cwd
|
15
|
+
end
|
16
|
+
if env
|
17
|
+
env.each do |k, v|
|
18
|
+
process.environment[k.to_s] = v
|
19
|
+
end
|
20
|
+
end
|
21
|
+
process.start
|
22
|
+
process.wait
|
23
|
+
process
|
24
|
+
end
|
25
|
+
|
26
|
+
module_function def check_call(cmd, env: nil, cwd: nil)
|
27
|
+
process = call(cmd, env: env, cwd: cwd)
|
28
|
+
unless process.exit_code == 0
|
29
|
+
raise SpawnError, "Failed to execute: #{cmd}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module_function def get_output(cmd, env: nil, cwd: nil)
|
34
|
+
process = ChildProcess.new(*cmd)
|
35
|
+
process.io.inherit!
|
36
|
+
if cwd
|
37
|
+
process.cwd = cwd
|
38
|
+
end
|
39
|
+
if env
|
40
|
+
env.each do |k, v|
|
41
|
+
process.environment[k.to_s] = v
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
output = ''
|
46
|
+
r, w = IO.pipe
|
47
|
+
|
48
|
+
begin
|
49
|
+
process.io.stdout = w
|
50
|
+
process.start
|
51
|
+
w.close
|
52
|
+
|
53
|
+
thread = Thread.new do
|
54
|
+
begin
|
55
|
+
loop do
|
56
|
+
output << r.readpartial(16384)
|
57
|
+
end
|
58
|
+
rescue EOFError
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
process.wait
|
63
|
+
thread.join
|
64
|
+
ensure
|
65
|
+
r.close
|
66
|
+
end
|
67
|
+
|
68
|
+
[process, output]
|
69
|
+
end
|
70
|
+
|
71
|
+
module_function def check_output(*args)
|
72
|
+
process, output = get_output(*args)
|
73
|
+
unless process.exit_code == 0
|
74
|
+
raise SpawnError,"Failed to execute: #{args}"
|
75
|
+
end
|
76
|
+
output
|
77
|
+
end
|
78
|
+
end
|
@@ -38,6 +38,10 @@ class ClusterConfig
|
|
38
38
|
# less than 3.4. This method returns FCV on 3.4+ servers when in single
|
39
39
|
# or RS topologies, and otherwise returns the major.minor server version.
|
40
40
|
def fcv_ish
|
41
|
+
if server_version.nil?
|
42
|
+
raise "Deployment server version not known - check that connection to deployment succeeded"
|
43
|
+
end
|
44
|
+
|
41
45
|
if server_version >= '3.4' && topology != :sharded
|
42
46
|
fcv
|
43
47
|
else
|
@@ -3,6 +3,21 @@
|
|
3
3
|
# @since 2.5.0
|
4
4
|
class EventSubscriber
|
5
5
|
|
6
|
+
# The mappings of event names to types.
|
7
|
+
#
|
8
|
+
# @since 2.4.0
|
9
|
+
MAPPINGS = {
|
10
|
+
'topology_opening_event' => Mongo::Monitoring::Event::TopologyOpening,
|
11
|
+
'topology_description_changed_event' => Mongo::Monitoring::Event::TopologyChanged,
|
12
|
+
'topology_closed_event' => Mongo::Monitoring::Event::TopologyClosed,
|
13
|
+
'server_opening_event' => Mongo::Monitoring::Event::ServerOpening,
|
14
|
+
'server_description_changed_event' => Mongo::Monitoring::Event::ServerDescriptionChanged,
|
15
|
+
'server_closed_event' => Mongo::Monitoring::Event::ServerClosed
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
# All events.
|
19
|
+
attr_reader :all_events
|
20
|
+
|
6
21
|
# The started events.
|
7
22
|
#
|
8
23
|
# @since 2.5.0
|
@@ -20,25 +35,40 @@ class EventSubscriber
|
|
20
35
|
|
21
36
|
attr_reader :published_events
|
22
37
|
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
38
|
+
# @param [ String ] name Optional name for the event subscriber.
|
39
|
+
def initialize(name: nil)
|
40
|
+
@mutex = Mutex.new
|
41
|
+
clear_events!
|
42
|
+
@name = name
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
%Q`#<EventSubscriber:#{@name ? "\"#{@name}\"" : '%x' % object_id} \
|
47
|
+
started=#{started_events.length} \
|
48
|
+
succeeded=#{succeeded_events.length} \
|
49
|
+
failed=#{failed_events.length} \
|
50
|
+
published=#{published_events.length}>`
|
51
|
+
end
|
52
|
+
|
53
|
+
alias :inspect :to_s
|
54
|
+
|
55
|
+
# Event retrieval
|
56
|
+
|
57
|
+
def select_started_events(cls)
|
58
|
+
started_events.select do |event|
|
59
|
+
event.is_a?(cls)
|
31
60
|
end
|
32
61
|
end
|
33
62
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
63
|
+
def select_succeeded_events(cls)
|
64
|
+
succeeded_events.select do |event|
|
65
|
+
event.is_a?(cls)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def select_published_events(cls)
|
70
|
+
published_events.select do |event|
|
71
|
+
event.is_a?(cls)
|
42
72
|
end
|
43
73
|
end
|
44
74
|
|
@@ -59,8 +89,13 @@ class EventSubscriber
|
|
59
89
|
|
60
90
|
# Locates command stated events for the specified command name,
|
61
91
|
# asserts that there is exactly one such event, and returns it.
|
62
|
-
def single_command_started_event(command_name)
|
63
|
-
events =
|
92
|
+
def single_command_started_event(command_name, include_auth: false)
|
93
|
+
events = if include_auth
|
94
|
+
started_events
|
95
|
+
else
|
96
|
+
non_auth_command_started_events
|
97
|
+
end
|
98
|
+
events.select! do |event|
|
64
99
|
event.command[command_name]
|
65
100
|
end
|
66
101
|
if events.length != 1
|
@@ -69,38 +104,65 @@ class EventSubscriber
|
|
69
104
|
events.first
|
70
105
|
end
|
71
106
|
|
72
|
-
|
73
|
-
|
74
|
-
|
107
|
+
# Get the first succeeded event published for the name, and then delete it.
|
108
|
+
#
|
109
|
+
# @param [ String ] name The event name.
|
110
|
+
#
|
111
|
+
# @return [ Event ] The matching event.
|
112
|
+
def first_event(name)
|
113
|
+
cls = MAPPINGS[name]
|
114
|
+
if cls.nil?
|
115
|
+
raise ArgumentError, "Bogus event name #{name}"
|
75
116
|
end
|
117
|
+
matching = succeeded_events.find do |event|
|
118
|
+
cls === event
|
119
|
+
end
|
120
|
+
succeeded_events.delete(matching)
|
121
|
+
matching
|
76
122
|
end
|
77
123
|
|
78
|
-
|
79
|
-
|
80
|
-
|
124
|
+
# Event recording
|
125
|
+
|
126
|
+
# Cache the started event.
|
127
|
+
#
|
128
|
+
# @param [ Event ] event The event.
|
129
|
+
#
|
130
|
+
# @since 2.5.0
|
131
|
+
def started(event)
|
132
|
+
@mutex.synchronize do
|
133
|
+
started_events << event
|
134
|
+
all_events << event
|
81
135
|
end
|
82
136
|
end
|
83
137
|
|
84
|
-
# Cache the
|
138
|
+
# Cache the succeeded event.
|
85
139
|
#
|
86
140
|
# @param [ Event ] event The event.
|
87
141
|
#
|
88
142
|
# @since 2.5.0
|
89
|
-
def
|
143
|
+
def succeeded(event)
|
90
144
|
@mutex.synchronize do
|
91
|
-
|
145
|
+
succeeded_events << event
|
146
|
+
all_events << event
|
92
147
|
end
|
93
148
|
end
|
94
149
|
|
95
|
-
|
96
|
-
|
97
|
-
|
150
|
+
# Cache the failed event.
|
151
|
+
#
|
152
|
+
# @param [ Event ] event The event.
|
153
|
+
#
|
154
|
+
# @since 2.5.0
|
155
|
+
def failed(event)
|
156
|
+
@mutex.synchronize do
|
157
|
+
failed_events << event
|
158
|
+
all_events << event
|
98
159
|
end
|
99
160
|
end
|
100
161
|
|
101
162
|
def published(event)
|
102
163
|
@mutex.synchronize do
|
103
|
-
|
164
|
+
published_events << event
|
165
|
+
all_events << event
|
104
166
|
end
|
105
167
|
end
|
106
168
|
|
@@ -108,15 +170,43 @@ class EventSubscriber
|
|
108
170
|
#
|
109
171
|
# @since 2.5.1
|
110
172
|
def clear_events!
|
173
|
+
@all_events = []
|
111
174
|
@started_events = []
|
112
175
|
@succeeded_events = []
|
113
176
|
@failed_events = []
|
114
177
|
@published_events = []
|
115
178
|
self
|
116
179
|
end
|
180
|
+
end
|
117
181
|
|
182
|
+
# Only handles succeeded events correctly.
|
183
|
+
class PhasedEventSubscriber < EventSubscriber
|
118
184
|
def initialize
|
119
|
-
|
120
|
-
|
185
|
+
super
|
186
|
+
@phase_events = {}
|
187
|
+
end
|
188
|
+
|
189
|
+
def phase_finished(phase_index)
|
190
|
+
@phase_events[phase_index] = succeeded_events
|
191
|
+
@succeeded_events = []
|
192
|
+
end
|
193
|
+
|
194
|
+
def phase_events(phase_index)
|
195
|
+
@phase_events[phase_index]
|
196
|
+
end
|
197
|
+
|
198
|
+
def event_count
|
199
|
+
@phase_events.inject(0) do |sum, event|
|
200
|
+
sum + event.length
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
class VerboseEventSubscriber < EventSubscriber
|
206
|
+
%w(started succeeded failed published).each do |meth|
|
207
|
+
define_method(meth) do |event|
|
208
|
+
puts event.summary
|
209
|
+
super(event)
|
210
|
+
end
|
121
211
|
end
|
122
212
|
end
|