mongo 2.10.5 → 2.11.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CONTRIBUTING.md +1 -1
  5. data/lib/mongo.rb +2 -0
  6. data/lib/mongo/address.rb +4 -0
  7. data/lib/mongo/address/validator.rb +99 -0
  8. data/lib/mongo/auth.rb +7 -2
  9. data/lib/mongo/auth/user.rb +1 -7
  10. data/lib/mongo/background_thread.rb +135 -0
  11. data/lib/mongo/bulk_write/transformable.rb +3 -3
  12. data/lib/mongo/client.rb +74 -16
  13. data/lib/mongo/cluster.rb +193 -41
  14. data/lib/mongo/cluster/periodic_executor.rb +31 -43
  15. data/lib/mongo/cluster/sdam_flow.rb +26 -3
  16. data/lib/mongo/cluster/srv_monitor.rb +127 -0
  17. data/lib/mongo/collection/view/readable.rb +3 -5
  18. data/lib/mongo/collection/view/writable.rb +3 -3
  19. data/lib/mongo/cursor/builder/get_more_command.rb +1 -4
  20. data/lib/mongo/cursor/builder/kill_cursors_command.rb +5 -23
  21. data/lib/mongo/cursor/builder/op_get_more.rb +2 -2
  22. data/lib/mongo/cursor/builder/op_kill_cursors.rb +5 -24
  23. data/lib/mongo/error.rb +1 -0
  24. data/lib/mongo/error/auth_error.rb +1 -1
  25. data/lib/mongo/error/connection_check_out_timeout.rb +7 -8
  26. data/lib/mongo/error/invalid_address.rb +24 -0
  27. data/lib/mongo/error/notable.rb +2 -2
  28. data/lib/mongo/error/operation_failure.rb +3 -3
  29. data/lib/mongo/error/pool_closed_error.rb +11 -4
  30. data/lib/mongo/event.rb +1 -1
  31. data/lib/mongo/grid/file.rb +0 -5
  32. data/lib/mongo/grid/file/chunk.rb +0 -2
  33. data/lib/mongo/grid/fs_bucket.rb +13 -15
  34. data/lib/mongo/grid/stream/write.rb +3 -9
  35. data/lib/mongo/loggable.rb +5 -1
  36. data/lib/mongo/monitoring.rb +1 -0
  37. data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +7 -0
  38. data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +11 -3
  39. data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +11 -3
  40. data/lib/mongo/monitoring/event/cmap/pool_closed.rb +11 -3
  41. data/lib/mongo/monitoring/event/cmap/pool_created.rb +12 -3
  42. data/lib/mongo/monitoring/unified_sdam_log_subscriber.rb +62 -0
  43. data/lib/mongo/operation/shared/executable.rb +5 -10
  44. data/lib/mongo/operation/shared/sessions_supported.rb +1 -5
  45. data/lib/mongo/protocol/get_more.rb +1 -2
  46. data/lib/mongo/protocol/kill_cursors.rb +13 -6
  47. data/lib/mongo/protocol/serializers.rb +4 -20
  48. data/lib/mongo/retryable.rb +9 -34
  49. data/lib/mongo/semaphore.rb +1 -1
  50. data/lib/mongo/server.rb +113 -42
  51. data/lib/mongo/server/connection.rb +12 -5
  52. data/lib/mongo/server/connection_pool.rb +250 -40
  53. data/lib/mongo/server/connection_pool/populator.rb +58 -0
  54. data/lib/mongo/server/description.rb +9 -2
  55. data/lib/mongo/server/monitor.rb +68 -93
  56. data/lib/mongo/server/monitor/connection.rb +2 -0
  57. data/lib/mongo/server_selector/selectable.rb +13 -5
  58. data/lib/mongo/session.rb +0 -13
  59. data/lib/mongo/srv.rb +17 -0
  60. data/lib/mongo/srv/monitor.rb +96 -0
  61. data/lib/mongo/srv/resolver.rb +130 -0
  62. data/lib/mongo/srv/result.rb +126 -0
  63. data/lib/mongo/srv/warning_result.rb +35 -0
  64. data/lib/mongo/uri.rb +45 -55
  65. data/lib/mongo/uri/srv_protocol.rb +89 -42
  66. data/lib/mongo/version.rb +1 -1
  67. data/mongo.gemspec +3 -4
  68. data/spec/README.md +6 -1
  69. data/spec/enterprise_auth/kerberos_spec.rb +7 -6
  70. data/spec/integration/change_stream_examples_spec.rb +0 -4
  71. data/spec/integration/client_construction_spec.rb +14 -2
  72. data/spec/integration/connect_single_rs_name_spec.rb +2 -2
  73. data/spec/integration/connection_pool_populator_spec.rb +296 -0
  74. data/spec/integration/connection_spec.rb +31 -22
  75. data/spec/integration/cursor_reaping_spec.rb +1 -2
  76. data/spec/integration/docs_examples_spec.rb +0 -4
  77. data/spec/integration/heartbeat_events_spec.rb +17 -15
  78. data/spec/integration/reconnect_spec.rb +144 -1
  79. data/spec/integration/retryable_writes_errors_spec.rb +0 -4
  80. data/spec/integration/retryable_writes_spec.rb +36 -36
  81. data/spec/integration/sdam_error_handling_spec.rb +31 -25
  82. data/spec/integration/sdam_events_spec.rb +2 -6
  83. data/spec/integration/server_monitor_spec.rb +28 -0
  84. data/spec/integration/server_selector_spec.rb +7 -5
  85. data/spec/integration/srv_monitoring_spec.rb +360 -0
  86. data/spec/integration/step_down_spec.rb +4 -6
  87. data/spec/lite_spec_helper.rb +22 -0
  88. data/spec/mongo/address/validator_spec.rb +51 -0
  89. data/spec/mongo/auth/cr_spec.rb +1 -29
  90. data/spec/mongo/auth/ldap_spec.rb +1 -29
  91. data/spec/mongo/auth/scram/conversation_spec.rb +0 -2
  92. data/spec/mongo/auth/scram/negotiation_spec.rb +1 -1
  93. data/spec/mongo/auth/scram_spec.rb +1 -29
  94. data/spec/mongo/auth/user/view_spec.rb +1 -36
  95. data/spec/mongo/auth/user_spec.rb +0 -12
  96. data/spec/mongo/auth/x509_spec.rb +1 -29
  97. data/spec/mongo/bulk_write_spec.rb +2 -2
  98. data/spec/mongo/client_construction_spec.rb +56 -15
  99. data/spec/mongo/client_spec.rb +31 -27
  100. data/spec/mongo/cluster/periodic_executor_spec.rb +16 -0
  101. data/spec/mongo/cluster/srv_monitor_spec.rb +214 -0
  102. data/spec/mongo/cluster/topology/replica_set_spec.rb +16 -11
  103. data/spec/mongo/cluster/topology/sharded_spec.rb +12 -9
  104. data/spec/mongo/cluster/topology/single_spec.rb +20 -11
  105. data/spec/mongo/cluster_spec.rb +45 -29
  106. data/spec/mongo/collection/view/map_reduce_spec.rb +14 -9
  107. data/spec/mongo/collection/view/readable_spec.rb +0 -16
  108. data/spec/mongo/collection_spec.rb +0 -44
  109. data/spec/mongo/cursor/builder/get_more_command_spec.rb +2 -4
  110. data/spec/mongo/cursor/builder/op_get_more_spec.rb +2 -4
  111. data/spec/mongo/cursor_spec.rb +27 -7
  112. data/spec/mongo/monitoring/event/cmap/connection_checked_in_spec.rb +10 -3
  113. data/spec/mongo/monitoring/event/cmap/connection_checked_out_spec.rb +10 -3
  114. data/spec/mongo/monitoring/event/cmap/pool_closed_spec.rb +10 -3
  115. data/spec/mongo/monitoring/event/cmap/pool_created_spec.rb +10 -3
  116. data/spec/mongo/operation/delete/op_msg_spec.rb +17 -8
  117. data/spec/mongo/operation/insert/op_msg_spec.rb +50 -35
  118. data/spec/mongo/operation/update/op_msg_spec.rb +14 -7
  119. data/spec/mongo/retryable_spec.rb +52 -31
  120. data/spec/mongo/server/app_metadata_spec.rb +0 -8
  121. data/spec/mongo/server/connection_auth_spec.rb +5 -2
  122. data/spec/mongo/server/connection_pool/populator_spec.rb +101 -0
  123. data/spec/mongo/server/connection_pool_spec.rb +256 -107
  124. data/spec/mongo/server/connection_spec.rb +22 -33
  125. data/spec/mongo/server/description_spec.rb +42 -4
  126. data/spec/mongo/server/monitor/connection_spec.rb +22 -11
  127. data/spec/mongo/server/monitor_spec.rb +66 -107
  128. data/spec/mongo/server_spec.rb +82 -60
  129. data/spec/mongo/session/session_pool_spec.rb +1 -5
  130. data/spec/mongo/session_spec.rb +0 -4
  131. data/spec/mongo/socket/ssl_spec.rb +2 -2
  132. data/spec/mongo/srv/monitor_spec.rb +211 -0
  133. data/spec/mongo/srv/result_spec.rb +54 -0
  134. data/spec/mongo/uri/srv_protocol_spec.rb +30 -15
  135. data/spec/mongo/uri_spec.rb +125 -4
  136. data/spec/spec_helper.rb +6 -0
  137. data/spec/spec_tests/auth_spec.rb +39 -0
  138. data/spec/spec_tests/cmap_spec.rb +55 -8
  139. data/spec/spec_tests/connection_string_spec.rb +6 -31
  140. data/spec/spec_tests/data/auth/connection-string.yml +297 -0
  141. data/spec/spec_tests/data/cmap/pool-checkout-error-closed.yml +4 -1
  142. data/spec/spec_tests/data/cmap/pool-create-with-options.yml +1 -0
  143. data/spec/spec_tests/data/command_monitoring/insertMany.yml +1 -1
  144. data/spec/spec_tests/data/connection_string/invalid-uris.yml +20 -0
  145. data/spec/spec_tests/data/connection_string/valid-auth.yml +16 -0
  146. data/spec/spec_tests/data/connection_string/valid-warnings.yml +26 -30
  147. data/spec/spec_tests/data/transactions/abort.yml +3 -3
  148. data/spec/spec_tests/data/transactions/error-labels.yml +3 -3
  149. data/spec/spec_tests/data/transactions_api/callback-retry.yml +3 -3
  150. data/spec/spec_tests/data/uri_options/auth-options.yml +1 -1
  151. data/spec/spec_tests/max_staleness_spec.rb +7 -2
  152. data/spec/spec_tests/retryable_reads_spec.rb +0 -31
  153. data/spec/spec_tests/sdam_monitoring_spec.rb +12 -12
  154. data/spec/spec_tests/sdam_spec.rb +4 -7
  155. data/spec/spec_tests/server_selection_spec.rb +6 -2
  156. data/spec/spec_tests/transactions_spec.rb +0 -2
  157. data/spec/spec_tests/uri_options_spec.rb +4 -2
  158. data/spec/stress/connection_pool_stress_spec.rb +203 -0
  159. data/spec/stress/connection_pool_timing_spec.rb +181 -0
  160. data/spec/support/auth.rb +113 -0
  161. data/spec/support/background_thread_registry.rb +63 -0
  162. data/spec/support/client_registry.rb +11 -2
  163. data/spec/support/cluster_config.rb +65 -46
  164. data/spec/support/cluster_tools.rb +2 -2
  165. data/spec/support/cmap.rb +13 -14
  166. data/spec/support/cmap/verifier.rb +4 -5
  167. data/spec/support/command_monitoring.rb +0 -5
  168. data/spec/support/common_shortcuts.rb +101 -1
  169. data/spec/support/constraints.rb +25 -0
  170. data/spec/support/dns.rb +13 -0
  171. data/spec/support/event_subscriber.rb +0 -7
  172. data/spec/support/json_ext_formatter.rb +5 -1
  173. data/spec/support/lite_constraints.rb +22 -6
  174. data/spec/support/local_resource_registry.rb +34 -0
  175. data/spec/support/sdam_monitoring.rb +115 -0
  176. data/spec/support/spec_config.rb +20 -6
  177. data/spec/support/spec_setup.rb +2 -2
  178. data/spec/support/transactions.rb +1 -1
  179. data/spec/support/transactions/test.rb +1 -1
  180. data/spec/support/utils.rb +1 -16
  181. metadata +685 -659
  182. metadata.gz.sig +0 -0
  183. data/lib/mongo/event/description_changed.rb +0 -52
  184. data/spec/integration/bson_symbol_spec.rb +0 -34
  185. data/spec/integration/crud_spec.rb +0 -45
  186. data/spec/integration/get_more_spec.rb +0 -32
  187. data/spec/integration/grid_fs_bucket_spec.rb +0 -48
  188. data/spec/integration/retryable_errors_spec.rb +0 -265
  189. data/spec/integration/size_limit_spec.rb~12e1e9c4f... RUBY-2242 Fix zlib compression (#2021) +0 -98
  190. data/spec/mongo/cursor/builder/op_kill_cursors_spec.rb +0 -56
  191. data/spec/runners/sdam/verifier.rb +0 -88
data/lib/mongo/cluster.rb CHANGED
@@ -93,11 +93,15 @@ module Mongo
93
93
  # to clean up server sessions when the cluster is disconnected, and to
94
94
  # to not start the periodic executor. If :monitoring_io is false,
95
95
  # :cleanup automatically defaults to false as well.
96
+ # @option options [ Float ] :heartbeat_frequency The interval, in seconds,
97
+ # for the server monitor to refresh its description via ismaster.
98
+ # @option options [ Hash ] :resolv_options For internal driver use only.
99
+ # Options to pass through to Resolv::DNS constructor for SRV lookups.
96
100
  #
97
101
  # @since 2.0.0
98
102
  def initialize(seeds, monitoring, options = Options::Redacted.new)
99
- if options[:monitoring_io] != false && !options[:server_selection_semaphore]
100
- raise ArgumentError, 'Need server selection semaphore'
103
+ if seeds.nil?
104
+ raise ArgumentError, 'Seeds cannot be nil'
101
105
  end
102
106
 
103
107
  if options[:monitoring_io] == false && !options.key?(:cleanup)
@@ -105,8 +109,6 @@ module Mongo
105
109
  options[:cleanup] = false
106
110
  end
107
111
 
108
- seeds = seeds.uniq
109
-
110
112
  @servers = []
111
113
  @monitoring = monitoring
112
114
  @event_listeners = Event::Listeners.new
@@ -116,6 +118,8 @@ module Mongo
116
118
  @sdam_flow_lock = Mutex.new
117
119
  @cluster_time = nil
118
120
  @cluster_time_lock = Mutex.new
121
+ @srv_monitor_lock = Mutex.new
122
+ @server_selection_semaphore = Semaphore.new
119
123
  @topology = Topology.initial(self, monitoring, options)
120
124
  Session::SessionPool.create(self)
121
125
 
@@ -128,8 +132,6 @@ module Mongo
128
132
  Monitoring::Event::TopologyOpening.new(opening_topology)
129
133
  )
130
134
 
131
- subscribe_to(Event::DESCRIPTION_CHANGED, Event::DescriptionChanged.new(self))
132
-
133
135
  @seeds = seeds
134
136
  servers = seeds.map do |seed|
135
137
  # Server opening events must be sent after topology change events.
@@ -148,27 +150,36 @@ module Mongo
148
150
  )
149
151
  end
150
152
 
151
- servers.each do |server|
152
- server.start_monitoring
153
- end
154
-
155
153
  if options[:monitoring_io] == false
156
154
  # Omit periodic executor construction, because without servers
157
155
  # no commands can be sent to the cluster and there shouldn't ever
158
156
  # be anything that needs to be cleaned up.
159
157
  #
160
- # Also omit legacy single round of SDAM on the main thread,
161
- # as it would race with tests that mock SDAM responses.
158
+ # Omit monitoring individual servers and the legacy single round of
159
+ # of SDAM on the main thread, as it would race with tests that mock
160
+ # SDAM responses.
161
+ @connecting = @connected = false
162
162
  return
163
163
  end
164
164
 
165
+ # Need to record start time prior to starting monitoring
166
+ start_time = Time.now
167
+
168
+ servers.each do |server|
169
+ server.start_monitoring
170
+ end
171
+
165
172
  if options[:cleanup] != false
166
173
  @cursor_reaper = CursorReaper.new
167
174
  @socket_reaper = SocketReaper.new(self)
168
- @periodic_executor = PeriodicExecutor.new(@cursor_reaper, @socket_reaper)
169
- @periodic_executor.run!
175
+ @periodic_executor = PeriodicExecutor.new([
176
+ @cursor_reaper, @socket_reaper,
177
+ ], options)
178
+
179
+ ObjectSpace.define_finalizer(self, self.class.finalize(
180
+ {}, @periodic_executor, @session_pool))
170
181
 
171
- ObjectSpace.define_finalizer(self, self.class.finalize({}, @periodic_executor, @session_pool))
182
+ @periodic_executor.run!
172
183
  end
173
184
 
174
185
  @connecting = false
@@ -184,7 +195,6 @@ module Mongo
184
195
  if server_selection_timeout < 3
185
196
  server_selection_timeout = 3
186
197
  end
187
- start_time = Time.now
188
198
  deadline = start_time + server_selection_timeout
189
199
  # Wait for the first scan of each server to complete, for
190
200
  # backwards compatibility.
@@ -192,16 +202,26 @@ module Mongo
192
202
  # wait for these servers to also be queried, and so on, up to the
193
203
  # server selection timeout or the 3 second minimum.
194
204
  loop do
195
- servers = servers_list.dup
196
- if servers.all? { |server| server.description.last_update_time >= start_time }
205
+ # Ensure we do not try to read the servers list while SDAM is running
206
+ servers = @sdam_flow_lock.synchronize do
207
+ servers_list.dup
208
+ end
209
+ if servers.all? { |server| server.last_scan && server.last_scan >= start_time }
197
210
  break
198
211
  end
199
212
  if (time_remaining = deadline - Time.now) <= 0
200
213
  break
201
214
  end
202
- options[:server_selection_semaphore].wait(time_remaining)
215
+ log_debug("Waiting for up to #{'%.2f' % time_remaining} seconds for servers to be scanned: #{summary}")
216
+ # Since the semaphore may have been signaled between us checking
217
+ # the servers list above and the wait call below, we should not
218
+ # wait for the full remaining time - wait for up to 1 second, then
219
+ # recheck the state.
220
+ server_selection_semaphore.wait([time_remaining, 1].min)
203
221
  end
204
222
  end
223
+
224
+ start_stop_srv_monitor
205
225
  end
206
226
 
207
227
  # Create a cluster for the provided client, for use when we don't want the
@@ -265,6 +285,9 @@ module Mongo
265
285
  end
266
286
  end
267
287
 
288
+ # @api private
289
+ attr_reader :srv_monitor
290
+
268
291
  # Get the maximum number of times the client can retry a read operation
269
292
  # when using legacy read retries.
270
293
  #
@@ -301,6 +324,17 @@ module Mongo
301
324
  options[:read_retry_interval] || READ_RETRY_INTERVAL
302
325
  end
303
326
 
327
+ # Get the refresh interval for the server. This will be defined via an
328
+ # option or will default to 10.
329
+ #
330
+ # @return [ Float ] The heartbeat interval, in seconds.
331
+ #
332
+ # @since 2.10.0
333
+ # @api private
334
+ def heartbeat_interval
335
+ options[:heartbeat_frequency] || Server::Monitor::HEARTBEAT_FREQUENCY
336
+ end
337
+
304
338
  # Whether the cluster object is connected to its cluster.
305
339
  #
306
340
  # @return [ true|false ] Whether the cluster is connected.
@@ -369,9 +403,7 @@ module Mongo
369
403
  end
370
404
 
371
405
  # @api private
372
- def server_selection_semaphore
373
- options[:server_selection_semaphore]
374
- end
406
+ attr_reader :server_selection_semaphore
375
407
 
376
408
  # Finalize the cluster for garbage collection.
377
409
  #
@@ -392,34 +424,35 @@ module Mongo
392
424
  end
393
425
  end
394
426
 
395
- # Disconnect all servers.
427
+ # Closes the cluster.
396
428
  #
397
429
  # @note Applications should call Client#close to disconnect from
398
430
  # the cluster rather than calling this method. This method is for
399
431
  # internal driver use only.
400
432
  #
401
- # @example Disconnect the cluster's servers.
402
- # cluster.disconnect!
403
- #
404
- # @param [ Boolean ] wait Whether to wait for background threads to
405
- # finish running.
433
+ # Disconnects all servers in the cluster, publishing appropriate SDAM
434
+ # events in the process. Stops SRV monitoring if it is active.
435
+ # Marks the cluster disconnected.
406
436
  #
407
437
  # @return [ true ] Always true.
408
438
  #
409
439
  # @since 2.1.0
410
- def disconnect!(wait=false)
440
+ def disconnect!
411
441
  unless @connecting || @connected
412
442
  return true
413
443
  end
414
444
  if options[:cleanup] != false
415
- if wait
416
- session_pool.end_sessions
445
+ session_pool.end_sessions
446
+ @periodic_executor.stop!
447
+ end
448
+ @srv_monitor_lock.synchronize do
449
+ if @srv_monitor
450
+ @srv_monitor.stop!
417
451
  end
418
- @periodic_executor.stop!(wait)
419
452
  end
420
453
  @servers.each do |server|
421
454
  if server.connected?
422
- server.disconnect!(wait)
455
+ server.disconnect!
423
456
  publish_sdam_event(
424
457
  Monitoring::SERVER_CLOSED,
425
458
  Monitoring::Event::ServerClosed.new(server.address, topology)
@@ -481,16 +514,106 @@ module Mongo
481
514
  def scan!(sync=true)
482
515
  if sync
483
516
  servers_list.each do |server|
484
- server.scan!
517
+ if server.monitor
518
+ server.monitor.scan!
519
+ else
520
+ log_warn("Synchronous scan requested on cluster #{summary} but server #{server} has no monitor")
521
+ end
485
522
  end
486
523
  else
487
524
  servers_list.each do |server|
488
- server.monitor.scan_semaphore.signal
525
+ server.scan_semaphore.signal
489
526
  end
490
527
  end
491
528
  true
492
529
  end
493
530
 
531
+ # Runs SDAM flow on the cluster.
532
+ #
533
+ # This method can be invoked to process a new server description returned
534
+ # by the server on a monitoring or non-monitoring connection, and also
535
+ # by the driver when it marks a server unknown as a result of a (network)
536
+ # error.
537
+ #
538
+ # @param [ Server::Description ] previous_desc Previous server description.
539
+ # @param [ Server::Description ] updated_desc The changed description.
540
+ # @param [ Hash ] options Options.
541
+ #
542
+ # @option options [ true | false ] :keep_connection_pool Usually when the
543
+ # new server description is unknown, the connection pool on the
544
+ # respective server is cleared. Set this option to true to keep the
545
+ # existing connection pool (required when handling not master errors
546
+ # on 4.2+ servers).
547
+ #
548
+ # @api private
549
+ def run_sdam_flow(previous_desc, updated_desc, options = {})
550
+ @sdam_flow_lock.synchronize do
551
+ flow = SdamFlow.new(self, previous_desc, updated_desc)
552
+ flow.server_description_changed
553
+
554
+ # SDAM flow may alter the updated description - grab the final
555
+ # version for the purposes of broadcasting if a server is available
556
+ updated_desc = flow.updated_desc
557
+
558
+ unless options[:keep_connection_pool]
559
+ if flow.became_unknown?
560
+ servers_list.each do |server|
561
+ if server.address == updated_desc.address
562
+ server.clear_connection_pool
563
+ end
564
+ end
565
+ end
566
+ end
567
+
568
+ start_stop_srv_monitor
569
+ end
570
+
571
+ # Some updated descriptions, e.g. a mismatched me one, result in the
572
+ # server whose description we are processing being removed from
573
+ # the topology. When this happens, the server's monitoring thread gets
574
+ # killed. As a result, any code after the flow invocation may not run
575
+ # a particular monitor instance, hence there should generally not be
576
+ # any code in this method past the flow invocation.
577
+ #
578
+ # However, this broadcast call can be here because if the monitoring
579
+ # thread got killed the server should have been closed and no client
580
+ # should be currently waiting for it, thus not signaling the semaphore
581
+ # shouldn't cause any problems.
582
+ unless updated_desc.unknown?
583
+ server_selection_semaphore.broadcast
584
+ end
585
+ end
586
+
587
+ # Sets the list of servers to the addresses in the provided list of address
588
+ # strings.
589
+ #
590
+ # This method is called by the SRV monitor after receiving new DNS records
591
+ # for the monitored hostname.
592
+ #
593
+ # Removes servers in the cluster whose addresses are not in the passed
594
+ # list of server addresses, and adds servers for any addresses in the
595
+ # argument which are not already in the cluster.
596
+ #
597
+ # @param [ Array<String> ] server_address_strs List of server addresses
598
+ # to sync the cluster servers to.
599
+ #
600
+ # @api private
601
+ def set_server_list(server_address_strs)
602
+ @sdam_flow_lock.synchronize do
603
+ server_address_strs.each do |address_str|
604
+ unless servers_list.any? { |server| server.address.seed == address_str }
605
+ add(address_str)
606
+ end
607
+ end
608
+
609
+ servers_list.each do |server|
610
+ unless server_address_strs.any? { |address_str| server.address.seed == address_str }
611
+ remove(server.address.seed)
612
+ end
613
+ end
614
+ end
615
+ end
616
+
494
617
  # Determine if this cluster of servers is equal to another object. Checks the
495
618
  # servers currently in the cluster, not what was configured.
496
619
  #
@@ -504,9 +627,7 @@ module Mongo
504
627
  # @since 2.0.0
505
628
  def ==(other)
506
629
  return false unless other.is_a?(Cluster)
507
- addresses == other.addresses &&
508
- options.merge(server_selection_semaphore: nil) ==
509
- other.options.merge(server_selection_semaphore: nil)
630
+ addresses == other.addresses && options == other.options
510
631
  end
511
632
 
512
633
  # Determine if the cluster would select a readable server for the
@@ -657,9 +778,6 @@ module Mongo
657
778
  @update_lock.synchronize { @servers.dup }
658
779
  end
659
780
 
660
- # @api private
661
- attr_reader :sdam_flow_lock
662
-
663
781
  private
664
782
 
665
783
  # If options[:session] is set, validates that session and returns it.
@@ -706,7 +824,41 @@ module Mongo
706
824
  false
707
825
  end
708
826
  end
827
+
828
+ # @api private
829
+ def start_stop_srv_monitor
830
+ # SRV URI is either always given or not for a given cluster, if one
831
+ # wasn't given we shouldn't ever have an SRV monitor to manage.
832
+ return unless options[:srv_uri]
833
+
834
+ if topology.is_a?(Topology::Sharded) || topology.is_a?(Topology::Unknown)
835
+ # Start SRV monitor
836
+ @srv_monitor_lock.synchronize do
837
+ unless @srv_monitor
838
+ monitor_options = options.merge(
839
+ timeout: options[:connect_timeout] || Server::CONNECT_TIMEOUT)
840
+ @srv_monitor = _srv_monitor = SrvMonitor.new(self, monitor_options)
841
+ finalizer = lambda do
842
+ _srv_monitor.stop!
843
+ end
844
+ ObjectSpace.define_finalizer(self, finalizer)
845
+ end
846
+ @srv_monitor.run!
847
+ end
848
+ else
849
+ # Stop SRV monitor if running. This path is taken when the client
850
+ # is given an SRV URI to a standalone/replica set; when the topology
851
+ # is discovered, since it's not a sharded cluster, the SRV monitor
852
+ # needs to be stopped.
853
+ @srv_monitor_lock.synchronize do
854
+ if @srv_monitor
855
+ @srv_monitor.stop!
856
+ end
857
+ end
858
+ end
859
+ end
709
860
  end
710
861
  end
711
862
 
712
863
  require 'mongo/cluster/sdam_flow'
864
+ require 'mongo/cluster/srv_monitor'
@@ -22,6 +22,7 @@ module Mongo
22
22
  #
23
23
  # @since 2.5.0
24
24
  class PeriodicExecutor
25
+ include BackgroundThread
25
26
 
26
27
  # The default time interval for the periodic executor to execute.
27
28
  #
@@ -31,47 +32,43 @@ module Mongo
31
32
  # Create a periodic executor.
32
33
  #
33
34
  # @example Create a PeriodicExecutor.
34
- # Mongo::Cluster::PeriodicExecutor.new(reaper, reaper2)
35
+ # Mongo::Cluster::PeriodicExecutor.new([reaper, reaper2])
36
+ # @param [ Hash ] options The options.
37
+ #
38
+ # @option options [ Logger ] :logger A custom logger to use.
35
39
  #
36
40
  # @api private
37
41
  #
38
42
  # @since 2.5.0
39
- def initialize(*executors)
43
+ def initialize(executors = [], options = {})
40
44
  @thread = nil
41
45
  @executors = executors
46
+ @stop_semaphore = Semaphore.new
47
+ @options = options
42
48
  end
43
49
 
44
- # Start the thread.
45
- #
46
- # @example Start the periodic executor's thread.
47
- # periodic_executor.run!
48
- #
49
- # @api private
50
- #
51
- # @since 2.5.0
52
- def run!
53
- @thread && @thread.alive? ? @thread : start!
54
- end
50
+ attr_reader :options
51
+
55
52
  alias :restart! :run!
56
53
 
57
- # Stop the executor's thread.
58
- #
59
- # @example Stop the executors's thread.
60
- # periodic_executor.stop!
61
- #
62
- # @param [ Boolean ] wait Whether to wait for background threads to
63
- # finish running.
64
- #
65
- # @api private
66
- #
67
- # @since 2.5.0
68
- def stop!(wait=false)
69
- begin; flush; rescue; end
70
- @thread.kill
71
- if wait
72
- @thread.join
54
+ def do_work
55
+ execute
56
+ @stop_semaphore.wait(FREQUENCY)
57
+ end
58
+
59
+ def pre_stop
60
+ @stop_semaphore.signal
61
+ end
62
+
63
+ def stop(final = false)
64
+ super
65
+
66
+ begin
67
+ flush
68
+ rescue
73
69
  end
74
- !@thread.alive?
70
+
71
+ true
75
72
  end
76
73
 
77
74
  # Trigger an execute call on each reaper.
@@ -83,7 +80,8 @@ module Mongo
83
80
  #
84
81
  # @since 2.5.0
85
82
  def execute
86
- @executors.each(&:execute) and true
83
+ @executors.each(&:execute)
84
+ true
87
85
  end
88
86
 
89
87
  # Execute all pending operations.
@@ -95,18 +93,8 @@ module Mongo
95
93
  #
96
94
  # @since 2.5.0
97
95
  def flush
98
- @executors.each(&:flush) and true
99
- end
100
-
101
- private
102
-
103
- def start!
104
- @thread = Thread.new(FREQUENCY) do |i|
105
- loop do
106
- sleep(i)
107
- execute
108
- end
109
- end
96
+ @executors.each(&:flush)
97
+ true
110
98
  end
111
99
  end
112
100
  end