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.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -5
  4. data/Rakefile +15 -9
  5. data/lib/mongo.rb +4 -2
  6. data/lib/mongo/auth/aws/request.rb +4 -2
  7. data/lib/mongo/bulk_write.rb +1 -0
  8. data/lib/mongo/client.rb +143 -21
  9. data/lib/mongo/cluster.rb +53 -17
  10. data/lib/mongo/cluster/sdam_flow.rb +13 -10
  11. data/lib/mongo/cluster/topology/replica_set_no_primary.rb +3 -2
  12. data/lib/mongo/cluster/topology/sharded.rb +1 -1
  13. data/lib/mongo/cluster/topology/single.rb +1 -1
  14. data/lib/mongo/collection.rb +17 -13
  15. data/lib/mongo/collection/view/readable.rb +3 -1
  16. data/lib/mongo/collection/view/writable.rb +41 -5
  17. data/lib/mongo/database.rb +31 -4
  18. data/lib/mongo/database/view.rb +19 -4
  19. data/lib/mongo/distinguishing_semaphore.rb +55 -0
  20. data/lib/mongo/error.rb +1 -0
  21. data/lib/mongo/error/invalid_session.rb +2 -1
  22. data/lib/mongo/error/operation_failure.rb +6 -0
  23. data/lib/mongo/error/sessions_not_supported.rb +35 -0
  24. data/lib/mongo/event/base.rb +6 -0
  25. data/lib/mongo/grid/file.rb +5 -0
  26. data/lib/mongo/grid/file/chunk.rb +2 -0
  27. data/lib/mongo/grid/fs_bucket.rb +15 -13
  28. data/lib/mongo/grid/stream/write.rb +9 -3
  29. data/lib/mongo/monitoring.rb +38 -0
  30. data/lib/mongo/monitoring/command_log_subscriber.rb +10 -2
  31. data/lib/mongo/monitoring/event/command_failed.rb +11 -0
  32. data/lib/mongo/monitoring/event/command_started.rb +37 -2
  33. data/lib/mongo/monitoring/event/command_succeeded.rb +11 -0
  34. data/lib/mongo/monitoring/event/server_closed.rb +1 -1
  35. data/lib/mongo/monitoring/event/server_description_changed.rb +27 -4
  36. data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +9 -2
  37. data/lib/mongo/monitoring/event/server_heartbeat_started.rb +9 -2
  38. data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +9 -2
  39. data/lib/mongo/monitoring/event/server_opening.rb +1 -1
  40. data/lib/mongo/monitoring/event/topology_changed.rb +1 -1
  41. data/lib/mongo/monitoring/event/topology_closed.rb +1 -1
  42. data/lib/mongo/monitoring/event/topology_opening.rb +1 -1
  43. data/lib/mongo/monitoring/publishable.rb +6 -3
  44. data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +9 -1
  45. data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +1 -1
  46. data/lib/mongo/protocol/message.rb +36 -8
  47. data/lib/mongo/protocol/msg.rb +14 -0
  48. data/lib/mongo/protocol/serializers.rb +5 -2
  49. data/lib/mongo/server.rb +10 -3
  50. data/lib/mongo/server/connection.rb +4 -4
  51. data/lib/mongo/server/connection_base.rb +3 -1
  52. data/lib/mongo/server/description.rb +5 -0
  53. data/lib/mongo/server/monitor.rb +76 -44
  54. data/lib/mongo/server/monitor/connection.rb +55 -7
  55. data/lib/mongo/server/pending_connection.rb +14 -4
  56. data/lib/mongo/server/push_monitor.rb +173 -0
  57. data/{spec/runners/transactions/context.rb → lib/mongo/server/push_monitor/connection.rb} +9 -14
  58. data/lib/mongo/server_selector.rb +0 -1
  59. data/lib/mongo/server_selector/base.rb +579 -1
  60. data/lib/mongo/server_selector/nearest.rb +1 -6
  61. data/lib/mongo/server_selector/primary.rb +1 -6
  62. data/lib/mongo/server_selector/primary_preferred.rb +7 -10
  63. data/lib/mongo/server_selector/secondary.rb +1 -6
  64. data/lib/mongo/server_selector/secondary_preferred.rb +1 -7
  65. data/lib/mongo/session.rb +2 -0
  66. data/lib/mongo/socket.rb +20 -8
  67. data/lib/mongo/socket/ssl.rb +1 -1
  68. data/lib/mongo/socket/tcp.rb +1 -1
  69. data/lib/mongo/topology_version.rb +9 -0
  70. data/lib/mongo/utils.rb +62 -0
  71. data/lib/mongo/version.rb +1 -1
  72. data/spec/README.aws-auth.md +2 -2
  73. data/spec/integration/awaited_ismaster_spec.rb +28 -0
  74. data/spec/integration/change_stream_examples_spec.rb +6 -2
  75. data/spec/integration/check_clean_slate_spec.rb +16 -0
  76. data/spec/integration/client_construction_spec.rb +1 -0
  77. data/spec/integration/connect_single_rs_name_spec.rb +5 -2
  78. data/spec/integration/connection_spec.rb +7 -4
  79. data/spec/integration/crud_spec.rb +4 -4
  80. data/spec/integration/docs_examples_spec.rb +6 -0
  81. data/spec/integration/grid_fs_bucket_spec.rb +48 -0
  82. data/spec/integration/heartbeat_events_spec.rb +4 -23
  83. data/spec/integration/read_concern_spec.rb +1 -1
  84. data/spec/integration/retryable_errors_spec.rb +1 -1
  85. data/spec/integration/retryable_writes/shared/performs_legacy_retries.rb +2 -2
  86. data/spec/integration/retryable_writes/shared/performs_modern_retries.rb +3 -3
  87. data/spec/integration/retryable_writes/shared/performs_no_retries.rb +2 -2
  88. data/spec/integration/sdam_error_handling_spec.rb +37 -15
  89. data/spec/integration/sdam_events_spec.rb +77 -6
  90. data/spec/integration/sdam_prose_spec.rb +64 -0
  91. data/spec/integration/server_monitor_spec.rb +25 -1
  92. data/spec/integration/size_limit_spec.rb +7 -3
  93. data/spec/integration/size_limit_spec.rb~12e1e9c4f... RUBY-2242 Fix zlib compression (#2021) +98 -0
  94. data/spec/integration/ssl_uri_options_spec.rb +2 -2
  95. data/spec/integration/zlib_compression_spec.rb +25 -0
  96. data/spec/lite_spec_helper.rb +12 -5
  97. data/spec/mongo/auth/aws/request_spec.rb +76 -0
  98. data/spec/mongo/auth/scram_spec.rb +1 -1
  99. data/spec/mongo/client_construction_spec.rb +207 -0
  100. data/spec/mongo/client_spec.rb +38 -3
  101. data/spec/mongo/cluster/topology/replica_set_spec.rb +52 -9
  102. data/spec/mongo/cluster/topology/single_spec.rb +4 -2
  103. data/spec/mongo/cluster_spec.rb +34 -35
  104. data/spec/mongo/collection/view/change_stream_resume_spec.rb +6 -6
  105. data/spec/mongo/collection_spec.rb +500 -0
  106. data/spec/mongo/database_spec.rb +245 -8
  107. data/spec/mongo/distinguishing_semaphore_spec.rb +63 -0
  108. data/spec/mongo/error/operation_failure_spec.rb +40 -0
  109. data/spec/mongo/index/view_spec.rb +2 -2
  110. data/spec/mongo/monitoring/event/server_description_changed_spec.rb +1 -4
  111. data/spec/mongo/protocol/msg_spec.rb +10 -0
  112. data/spec/mongo/semaphore_spec.rb +51 -0
  113. data/spec/mongo/server/connection_auth_spec.rb +2 -2
  114. data/spec/mongo/server_selector/nearest_spec.rb +23 -23
  115. data/spec/mongo/server_selector/primary_preferred_spec.rb +26 -26
  116. data/spec/mongo/server_selector/primary_spec.rb +9 -9
  117. data/spec/mongo/server_selector/secondary_preferred_spec.rb +22 -22
  118. data/spec/mongo/server_selector/secondary_spec.rb +18 -18
  119. data/spec/mongo/server_selector_spec.rb +4 -4
  120. data/spec/mongo/session_spec.rb +35 -0
  121. data/spec/runners/change_streams/test.rb +2 -2
  122. data/spec/runners/cmap.rb +1 -1
  123. data/spec/runners/command_monitoring.rb +3 -34
  124. data/spec/runners/crud/context.rb +9 -5
  125. data/spec/runners/crud/operation.rb +59 -27
  126. data/spec/runners/crud/spec.rb +0 -8
  127. data/spec/runners/crud/test.rb +1 -1
  128. data/spec/runners/sdam.rb +2 -2
  129. data/spec/runners/server_selection.rb +242 -28
  130. data/spec/runners/transactions.rb +12 -12
  131. data/spec/runners/transactions/operation.rb +151 -25
  132. data/spec/runners/transactions/test.rb +60 -16
  133. data/spec/spec_tests/command_monitoring_spec.rb +22 -12
  134. data/spec/spec_tests/crud_spec.rb +1 -1
  135. data/spec/spec_tests/data/change_streams/change-streams-errors.yml +4 -8
  136. data/spec/spec_tests/data/change_streams/change-streams-resume-whitelist.yml +66 -0
  137. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/MaxStalenessTooSmall.yml +15 -0
  138. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/NoKnownServers.yml +4 -3
  139. data/spec/spec_tests/data/max_staleness/Unknown/SmallMaxStaleness.yml +1 -0
  140. data/spec/spec_tests/data/sdam_integration/cancel-server-check.yml +96 -0
  141. data/spec/spec_tests/data/sdam_integration/connectTimeoutMS.yml +88 -0
  142. data/spec/spec_tests/data/sdam_integration/find-network-error.yml +83 -0
  143. data/spec/spec_tests/data/sdam_integration/find-shutdown-error.yml +116 -0
  144. data/spec/spec_tests/data/sdam_integration/insert-network-error.yml +86 -0
  145. data/spec/spec_tests/data/sdam_integration/insert-shutdown-error.yml +115 -0
  146. data/spec/spec_tests/data/sdam_integration/isMaster-command-error.yml +168 -0
  147. data/spec/spec_tests/data/sdam_integration/isMaster-network-error.yml +162 -0
  148. data/spec/spec_tests/data/sdam_integration/isMaster-timeout.yml +229 -0
  149. data/spec/spec_tests/data/sdam_integration/rediscover-quickly-after-step-down.yml +87 -0
  150. data/spec/spec_tests/max_staleness_spec.rb +4 -142
  151. data/spec/spec_tests/retryable_reads_spec.rb +2 -2
  152. data/spec/spec_tests/sdam_integration_spec.rb +13 -0
  153. data/spec/spec_tests/sdam_monitoring_spec.rb +1 -2
  154. data/spec/spec_tests/server_selection_spec.rb +4 -116
  155. data/spec/stress/cleanup_spec.rb +17 -2
  156. data/spec/stress/connection_pool_stress_spec.rb +10 -8
  157. data/spec/support/child_process_helper.rb +78 -0
  158. data/spec/support/client_registry.rb +1 -0
  159. data/spec/support/cluster_config.rb +4 -0
  160. data/spec/support/event_subscriber.rb +123 -33
  161. data/spec/support/keyword_struct.rb +26 -0
  162. data/spec/support/shared/server_selector.rb +13 -1
  163. data/spec/support/spec_config.rb +38 -13
  164. data/spec/support/spec_organizer.rb +129 -0
  165. data/spec/support/spec_setup.rb +1 -1
  166. data/spec/support/utils.rb +46 -0
  167. metadata +992 -942
  168. metadata.gz.sig +0 -0
  169. data/lib/mongo/server_selector/selectable.rb +0 -560
  170. 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 = Mongo::SDAMMonitoring::PhasedTestSubscriber.new
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 'spec_helper'
1
+ require 'lite_spec_helper'
2
2
 
3
3
  require 'runners/server_selection'
4
4
 
5
- describe 'Server Selection' do
5
+ SERVER_SELECTION_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/server_selection/**/*.yml").sort
6
6
 
7
- include Mongo::ServerSelection::Read
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
@@ -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
- 100.times do
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
- end_resources.should == start_resources
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
- 10.times do |j|
24
+ 100.times do |j|
25
25
  collection.find(a: i+j).to_a
26
- sleep 0.5
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) { 20 }
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 { |m, *args|
187
- if rand < 0.05
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
- expect {
194
+ server.pool.clear(stop_populator: false)
195
+
196
+ expect do
195
197
  threads.collect { |t| t.join }
196
- }.not_to raise_error
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
@@ -105,6 +105,7 @@ class ClientRegistry
105
105
  }.update(SpecConfig.instance.credentials_or_external_user(
106
106
  user: SpecConfig.instance.test_user.name,
107
107
  password: SpecConfig.instance.test_user.password,
108
+ auth_source: 'admin',
108
109
  ))
109
110
 
110
111
  Mongo::Client.new(
@@ -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
- # Cache the succeeded event.
24
- #
25
- # @param [ Event ] event The event.
26
- #
27
- # @since 2.5.0
28
- def succeeded(event)
29
- @mutex.synchronize do
30
- succeeded_events.push(event)
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
- # Cache the started event.
35
- #
36
- # @param [ Event ] event The event.
37
- #
38
- # @since 2.5.0
39
- def started(event)
40
- @mutex.synchronize do
41
- started_events.push(event)
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 = started_events.select do |event|
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
- def select_started_events(cls)
73
- @started_events.select do |event|
74
- event.is_a?(cls)
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
- def select_succeeded_events(cls)
79
- @succeeded_events.select do |event|
80
- event.is_a?(cls)
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 failed event.
138
+ # Cache the succeeded event.
85
139
  #
86
140
  # @param [ Event ] event The event.
87
141
  #
88
142
  # @since 2.5.0
89
- def failed(event)
143
+ def succeeded(event)
90
144
  @mutex.synchronize do
91
- failed_events.push(event)
145
+ succeeded_events << event
146
+ all_events << event
92
147
  end
93
148
  end
94
149
 
95
- def select_published_events(cls)
96
- @published_events.select do |event|
97
- event.is_a?(cls)
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
- @published_events << event
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
- @mutex = Mutex.new
120
- clear_events!
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