mongo 2.10.5 → 2.11.0.rc0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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