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
@@ -12,7 +12,6 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- require 'runners/transactions/context'
16
15
  require 'runners/transactions/operation'
17
16
  require 'runners/transactions/spec'
18
17
  require 'runners/transactions/test'
@@ -26,21 +25,22 @@ def define_transactions_spec_tests(test_paths)
26
25
  context(spec.description) do
27
26
 
28
27
  define_spec_tests_with_requirements(spec) do |req|
28
+
29
29
  spec.tests.each do |test|
30
30
 
31
- before do
32
- if ClusterConfig.instance.topology == :sharded
33
- if test.multiple_mongoses? && SpecConfig.instance.addresses.length == 1
34
- skip "Test requires multiple mongoses"
35
- elsif !test.multiple_mongoses? && SpecConfig.instance.addresses.length > 1
36
- # Many transaction spec tests that do not specifically deal with
37
- # sharded transactions fail when run against a multi-mongos cluster
38
- skip "Test does not specify multiple mongoses"
31
+ context(test.description) do
32
+
33
+ before(:all) do
34
+ if ClusterConfig.instance.topology == :sharded
35
+ if test.multiple_mongoses? && SpecConfig.instance.addresses.length == 1
36
+ skip "Test requires multiple mongoses"
37
+ elsif !test.multiple_mongoses? && SpecConfig.instance.addresses.length > 1
38
+ # Many transaction spec tests that do not specifically deal with
39
+ # sharded transactions fail when run against a multi-mongos cluster
40
+ skip "Test does not specify multiple mongoses"
41
+ end
39
42
  end
40
43
  end
41
- end
42
-
43
- context(test.description) do
44
44
 
45
45
  if test.skip_reason
46
46
  before(:all) do
@@ -21,23 +21,8 @@ module Mongo
21
21
  arguments && arguments['session'] || object =~ /session/
22
22
  end
23
23
 
24
- def execute(target, session0, session1, active_session=nil)
25
- session = case arguments && arguments['session']
26
- when 'session0'
27
- session0
28
- when 'session1'
29
- session1
30
- else
31
- # active session could be nil
32
- active_session
33
- end
34
-
35
- context = Context.new(
36
- session0,
37
- session1,
38
- session)
39
-
40
- op_name = Utils.underscore(name).to_sym
24
+ def execute(target, context)
25
+ op_name = ::Utils.underscore(name).to_sym
41
26
  if op_name == :with_transaction
42
27
  args = [target]
43
28
  else
@@ -53,9 +38,14 @@ module Mongo
53
38
  result['error'] = false
54
39
  end
55
40
  end
41
+
56
42
  result
57
43
  rescue Mongo::Error::OperationFailure => e
58
- err_doc = e.instance_variable_get(:@result).send(:first_document)
44
+ result = e.instance_variable_get(:@result)
45
+ if result.nil?
46
+ raise "OperationFailure had nil result: #{e}"
47
+ end
48
+ err_doc = result.send(:first_document)
59
49
  error_code_name = err_doc['codeName'] || err_doc['writeConcernError'] && err_doc['writeConcernError']['codeName']
60
50
  if error_code_name.nil?
61
51
  # Sometimes the server does not return the error code name,
@@ -102,13 +92,13 @@ module Mongo
102
92
  command_value = cmd.delete(command_name)
103
93
  cmd = { command_name.to_sym => command_value }.merge(cmd)
104
94
 
105
- opts = Utils.snakeize_hash(context.transform_arguments(options)).dup
95
+ opts = ::Utils.snakeize_hash(transformed_options(context)).dup
106
96
  opts[:read] = opts.delete(:read_preference)
107
97
  database.command(cmd, opts).documents.first
108
98
  end
109
99
 
110
100
  def start_transaction(session, context)
111
- session.start_transaction(Utils.convert_operation_options(arguments['options']))
101
+ session.start_transaction(::Utils.convert_operation_options(arguments['options']))
112
102
  nil
113
103
  end
114
104
 
@@ -128,7 +118,7 @@ module Mongo
128
118
  end
129
119
 
130
120
  if arguments['options']
131
- options = Utils.snakeize_hash(arguments['options'])
121
+ options = ::Utils.snakeize_hash(arguments['options'])
132
122
  else
133
123
  options = nil
134
124
  end
@@ -136,7 +126,7 @@ module Mongo
136
126
  callback['operations'].each do |op_spec|
137
127
  op = Operation.new(@crud_test, op_spec)
138
128
  target = @crud_test.resolve_target(@crud_test.test_client, op)
139
- rv = op.execute(target, context.session0, context.session1, session)
129
+ rv = op.execute(target, context)
140
130
  if rv && rv['exception']
141
131
  raise rv['exception']
142
132
  end
@@ -151,7 +141,7 @@ module Mongo
151
141
  end
152
142
 
153
143
  def targeted_fail_point(collection, context)
154
- args = context.transform_arguments(options)
144
+ args = transformed_options(context)
155
145
  session = args[:session]
156
146
  unless session.pinned_server
157
147
  raise ArgumentError, 'Targeted fail point requires session to be pinned to a server'
@@ -169,7 +159,7 @@ module Mongo
169
159
  end
170
160
 
171
161
  def assert_session_pinned(collection, context)
172
- args = context.transform_arguments(options)
162
+ args = transformed_options(context)
173
163
  session = args[:session]
174
164
  unless session.pinned_server
175
165
  raise ArgumentError, 'Expected session to be pinned'
@@ -177,12 +167,148 @@ module Mongo
177
167
  end
178
168
 
179
169
  def assert_session_unpinned(collection, context)
180
- args = context.transform_arguments(options)
170
+ args = transformed_options(context)
181
171
  session = args[:session]
182
172
  if session.pinned_server
183
173
  raise ArgumentError, 'Expected session to not be pinned'
184
174
  end
185
175
  end
176
+
177
+ def wait_for_event(client, context)
178
+ deadline = Time.now + 5
179
+ loop do
180
+ events = _select_events(context)
181
+ if events.length >= arguments['count']
182
+ break
183
+ end
184
+ if Time.now >= deadline
185
+ raise "Did not receive an event matching #{arguments} in 5 seconds; received #{events.length} but expected #{arguments['count']} events"
186
+ else
187
+ sleep 0.1
188
+ end
189
+ end
190
+ end
191
+
192
+ def assert_event_count(client, context)
193
+ events = _select_events(context)
194
+ unless events.length == arguments['count']
195
+ raise "Exppected #{arguments['count']} #{arguments['event']} events, but have #{events.length}"
196
+ end
197
+ end
198
+
199
+ def _select_events(context)
200
+ case arguments['event']
201
+ when 'ServerMarkedUnknownEvent'
202
+ context.sdam_subscriber.all_events.select do |event|
203
+ event.is_a?(Mongo::Monitoring::Event::ServerDescriptionChanged) &&
204
+ event.new_description.unknown?
205
+ end
206
+ else
207
+ context.sdam_subscriber.all_events.select do |event|
208
+ event.class.name.sub(/.*::/, '') == arguments['event'].sub(/Event$/, '')
209
+ end
210
+ end
211
+ end
212
+
213
+ class ThreadContext
214
+ def initialize
215
+ @operations = Queue.new
216
+ @unexpected_operation_results = []
217
+ end
218
+
219
+ def stop?
220
+ !!@stop
221
+ end
222
+
223
+ def signal_stop
224
+ @stop = true
225
+ end
226
+
227
+ attr_reader :operations
228
+ attr_reader :unexpected_operation_results
229
+ end
230
+
231
+ def start_thread(client, context)
232
+ thread_context = ThreadContext.new
233
+ thread = Thread.new do
234
+ loop do
235
+ begin
236
+ op_spec = thread_context.operations.pop(true)
237
+ op = Operation.new(@crud_test, op_spec)
238
+ target = @crud_test.resolve_target(@crud_test.test_client, op)
239
+ result = op.execute(target, context)
240
+ if op_spec['error']
241
+ unless result['error']
242
+ thread_context.unexpected_operation_results << result
243
+ end
244
+ else
245
+ if result['error']
246
+ thread_context.unexpected_operation_results << result
247
+ end
248
+ end
249
+ rescue ThreadError
250
+ # Queue is empty
251
+ end
252
+ if thread_context.stop?
253
+ break
254
+ else
255
+ sleep 1
256
+ end
257
+ end
258
+ end
259
+ class << thread
260
+ attr_accessor :context
261
+ end
262
+ thread.context = thread_context
263
+ unless context.threads
264
+ context.threads ||= {}
265
+ end
266
+ context.threads[arguments['name']] = thread
267
+ end
268
+
269
+ def run_on_thread(client, context)
270
+ thread = context.threads.fetch(arguments['name'])
271
+ thread.context.operations << arguments['operation']
272
+ end
273
+
274
+ def wait_for_thread(client, context)
275
+ thread = context.threads.fetch(arguments['name'])
276
+ thread.context.signal_stop
277
+ thread.join
278
+ unless thread.context.unexpected_operation_results.empty?
279
+ raise "Thread #{arguments['name']} had #{thread.context.unexpected_operation_results}.length unexpected operation results"
280
+ end
281
+ end
282
+
283
+ def wait(client, context)
284
+ sleep arguments['ms'] / 1000.0
285
+ end
286
+
287
+ def record_primary(client, context)
288
+ context.primary_address = client.cluster.next_primary.address
289
+ end
290
+
291
+ def run_admin_command(support_client, context)
292
+ support_client.use('admin').database.command(arguments['command'])
293
+ end
294
+
295
+ def wait_for_primary_change(client, context)
296
+ timeout = if arguments['timeoutMS']
297
+ arguments['timeoutMS'] / 1000.0
298
+ else
299
+ 10
300
+ end
301
+ deadline = Time.now + timeout
302
+ loop do
303
+ client.cluster.scan!
304
+ if client.cluster.next_primary.address != context.primary_address
305
+ break
306
+ end
307
+ if Time.now >= deadline
308
+ raise "Failed to change primary in #{timeout} seconds"
309
+ end
310
+ end
311
+ end
186
312
  end
187
313
  end
188
314
  end
@@ -41,13 +41,20 @@ module Mongo
41
41
  @spec = crud_spec
42
42
  @data = data
43
43
  @description = test['description']
44
- @client_options = Utils.convert_client_options(test['clientOptions'] || {})
44
+ @client_options = {
45
+ # Disable legacy read & write retries, so that when spec tests
46
+ # disable modern retries we do not retry at all instead of using
47
+ # legacy retries which is contrary to what the tests want.
48
+ max_read_retries: 0,
49
+ max_write_retries: 0,
50
+ app_name: 'Tx spec - test client',
51
+ }.update(::Utils.convert_client_options(test['clientOptions'] || {}))
45
52
 
46
53
  @fail_point_command = test['failPoint']
47
54
 
48
55
  @session_options = if opts = test['sessionOptions']
49
56
  Hash[opts.map do |session_name, options|
50
- [session_name.to_sym, Utils.convert_operation_options(options)]
57
+ [session_name.to_sym, ::Utils.convert_operation_options(options)]
51
58
  end]
52
59
  else
53
60
  {}
@@ -107,15 +114,35 @@ module Mongo
107
114
  end
108
115
 
109
116
  def test_client
110
- @test_client ||= ClientRegistry.instance.global_client(
111
- 'authorized_without_retry_writes'
112
- ).with(@client_options.merge(
113
- database: @spec.database_name,
114
- ))
117
+ @test_client ||= begin
118
+ sdam_proc = lambda do |test_client|
119
+ test_client.subscribe(Mongo::Monitoring::COMMAND, command_subscriber)
120
+
121
+ test_client.subscribe(Mongo::Monitoring::TOPOLOGY_OPENING, sdam_subscriber)
122
+ test_client.subscribe(Mongo::Monitoring::SERVER_OPENING, sdam_subscriber)
123
+ test_client.subscribe(Mongo::Monitoring::SERVER_DESCRIPTION_CHANGED, sdam_subscriber)
124
+ test_client.subscribe(Mongo::Monitoring::TOPOLOGY_CHANGED, sdam_subscriber)
125
+ test_client.subscribe(Mongo::Monitoring::SERVER_CLOSED, sdam_subscriber)
126
+ test_client.subscribe(Mongo::Monitoring::TOPOLOGY_CLOSED, sdam_subscriber)
127
+ test_client.subscribe(Mongo::Monitoring::CONNECTION_POOL, sdam_subscriber)
128
+ end
129
+
130
+ ClientRegistry.instance.new_local_client(
131
+ SpecConfig.instance.addresses,
132
+ SpecConfig.instance.authorized_test_options.merge(
133
+ database: @spec.database_name,
134
+ auth_source: SpecConfig.instance.auth_options[:auth_source] || 'admin',
135
+ sdam_proc: sdam_proc,
136
+ ).merge(@client_options))
137
+ end
115
138
  end
116
139
 
117
- def event_subscriber
118
- @event_subscriber ||= EventSubscriber.new
140
+ def command_subscriber
141
+ @command_subscriber ||= EventSubscriber.new
142
+ end
143
+
144
+ def sdam_subscriber
145
+ @sdam_subscriber ||= EventSubscriber.new(name: 'sdam subscriber')
119
146
  end
120
147
 
121
148
  # Run the test.
@@ -127,17 +154,33 @@ module Mongo
127
154
  #
128
155
  # @since 2.6.0
129
156
  def run
130
- test_client.subscribe(Mongo::Monitoring::COMMAND, event_subscriber)
157
+ @threads = {}
131
158
 
132
159
  results = @operations.map do |op|
133
160
  target = resolve_target(test_client, op)
134
161
  if op.needs_session?
135
- op.execute(target, session0, session1)
162
+ context = CRUD::Context.new(
163
+ session0: session0,
164
+ session1: session1,
165
+ sdam_subscriber: sdam_subscriber,
166
+ threads: @threads,
167
+ primary_address: @primary_address,
168
+ )
136
169
  else
137
170
  # Hack to support write concern operations tests, which are
138
171
  # defined to use transactions format but target pre-3.6 servers
139
172
  # that do not support sessions
140
- op.execute(target || support_client, nil, nil)
173
+ target ||= support_client
174
+ context = CRUD::Context.new(
175
+ sdam_subscriber: sdam_subscriber,
176
+ threads: @threads,
177
+ primary_address: @primary_address,
178
+ )
179
+ end
180
+
181
+ op.execute(target, context).tap do
182
+ @threads = context.threads
183
+ @primary_address = context.primary_address
141
184
  end
142
185
  end
143
186
 
@@ -147,7 +190,7 @@ module Mongo
147
190
  @session0&.end_session
148
191
  @session1&.end_session
149
192
 
150
- actual_events = Utils.yamlify_command_events(event_subscriber.started_events)
193
+ actual_events = ::Utils.yamlify_command_events(command_subscriber.started_events)
151
194
  actual_events = actual_events.reject do |event|
152
195
  event['command_started_event']['command']['endSessions']
153
196
  end
@@ -169,7 +212,7 @@ module Mongo
169
212
  contents: @result_collection.with(
170
213
  read: {mode: 'primary'},
171
214
  read_concern: { level: 'local' },
172
- ).find.to_a,
215
+ ).find.sort(_id: 1).to_a,
173
216
  events: actual_events,
174
217
  }
175
218
  end
@@ -216,9 +259,10 @@ module Mongo
216
259
 
217
260
  coll.insert_many(@data) unless @data.empty?
218
261
 
219
- $distinct_ran ||= if description =~ /distinct/ || @operations.any? { |op| op.name == 'distinct' }
262
+ $distinct_ran ||= {}
263
+ $distinct_ran[@spec.database_name] ||= if description =~ /distinct/ || @operations.any? { |op| op.name == 'distinct' }
220
264
  mongos_each_direct_client do |direct_client|
221
- direct_client['test'].distinct('foo').to_a
265
+ direct_client.use(@spec.database_name)['test'].distinct('foo').to_a
222
266
  end
223
267
  end
224
268
 
@@ -20,7 +20,7 @@ describe 'Command Monitoring Events' do
20
20
  end
21
21
 
22
22
  let(:subscriber) do
23
- Mongo::CommandMonitoring::TestSubscriber.new
23
+ EventSubscriber.new
24
24
  end
25
25
 
26
26
  let(:monitoring) do
@@ -32,25 +32,35 @@ describe 'Command Monitoring Events' do
32
32
  authorized_client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
33
33
  end
34
34
 
35
- after do
36
- monitoring.subscribers[Mongo::Monitoring::COMMAND].delete(subscriber)
37
- authorized_collection.find.delete_many
38
- end
39
-
40
- test.expectations.each do |expectation|
35
+ test.expectations.each_with_index do |expectation, index|
41
36
 
42
37
  it "generates a #{expectation.event_name} for #{expectation.command_name}" do
43
38
  begin
44
- test.run(authorized_collection)
45
- event = subscriber.send(expectation.event_type)[expectation.command_name]
46
- expect(event).to send(expectation.matcher, expectation)
39
+ test.run(authorized_collection, subscriber)
40
+ check_event(subscriber, index, expectation)
47
41
  rescue Mongo::Error::OperationFailure, Mongo::Error::BulkWriteError
48
- event = subscriber.send(expectation.event_type)[expectation.command_name]
49
- expect(event).to send(expectation.matcher, expectation)
42
+ check_event(subscriber, index, expectation)
50
43
  end
51
44
  end
52
45
  end
53
46
  end
54
47
  end
55
48
  end
49
+
50
+ def check_event(subscriber, index, expectation)
51
+ subscriber.all_events.length.should > index
52
+ # TODO move this filtering into EventSubscriber
53
+ events = subscriber.all_events.reject do |event|
54
+ (
55
+ event.is_a?(Mongo::Monitoring::Event::CommandStarted) ||
56
+ event.is_a?(Mongo::Monitoring::Event::CommandSucceeded) ||
57
+ event.is_a?(Mongo::Monitoring::Event::CommandFailed)
58
+ ) &&
59
+ %w(authenticate getnonce saslStart saslContinue).include?(event.command_name)
60
+ end
61
+ actual_event = events[index]
62
+ expected_event_type = expectation.event_type.sub(/_event$/, '')
63
+ Utils.underscore(actual_event.class.name.sub(/.*::/, '')).to_s.should == expected_event_type
64
+ expect(actual_event).to send(expectation.matcher, expectation)
65
+ end
56
66
  end