class Mongo::Server::Monitor
This object is responsible for keeping server status up to date, running in a separate thread as to not disrupt other operations.
@since 2.0.0
Constants
- HEARTBEAT_FREQUENCY
The default time for a server to refresh its status is 10 seconds.
@since 2.0.0
- MIN_SCAN_FREQUENCY
The minimum time between forced server scans. Is minHeartbeatFrequencyMS in the SDAM spec.
@since 2.0.0
- RTT_WEIGHT_FACTOR
The weighting factor (alpha) for calculating the average moving round trip time.
@since 2.0.0 @deprecated Will be removed in version 3.0.
Attributes
@return [ Mongo::Server::Monitor::Connection
] connection The connection to use.
@return [ Server::Description
] description The server
description the monitor refreshes.
@return [ Time ] last_scan
The time when the last server scan started.
@since 2.4.0
@return [ Monitoring
] monitoring The monitoring.
@return [ Hash ] options The server options.
@api private
@api private
Public Class Methods
Create the new server monitor.
@example Create the server monitor.
Mongo::Server::Monitor.new(address, listeners, monitoring)
@note Monitor
must never be directly instantiated outside of a Server
.
@param [ Address
] address The address to monitor. @param [ Event::Listeners
] event_listeners The event listeners. @param [ Monitoring
] monitoring The monitoring.. @param [ Hash ] options The options. @option options [ Float ] :heartbeat_frequency The interval, in seconds,
between server description refreshes via ismaster.
@since 2.0.0 @api private
# File lib/mongo/server/monitor.rb, line 60 def initialize(address, event_listeners, monitoring, options = {}) unless monitoring.is_a?(Monitoring) raise ArgumentError, "Wrong monitoring type: #{monitoring.inspect}" end @description = Description.new(address, {}) @event_listeners = event_listeners @monitoring = monitoring @options = options.freeze @round_trip_time_averager = RoundTripTimeAverager.new @scan_semaphore = Semaphore.new # This is a Mongo::Server::Monitor::Connection @connection = Connection.new(address, options) @last_scan = nil @mutex = Mutex.new end
Public Instance Methods
Get the refresh interval for the server. This will be defined via an option or will default to 10.
@example Get the refresh interval.
server.heartbeat_frequency
@return [ Integer ] The heartbeat frequency, in seconds.
@since 2.0.0
# File lib/mongo/server/monitor.rb, line 107 def heartbeat_frequency @heartbeat_frequency ||= options[:heartbeat_frequency] || HEARTBEAT_FREQUENCY end
Restarts the server monitor unless the current thread is alive.
@example Restart the monitor.
monitor.restart!
@return [ Thread ] The thread the monitor runs on.
@since 2.1.0
# File lib/mongo/server/monitor.rb, line 212 def restart! if @thread && @thread.alive? @thread else run! end end
Runs the server monitor. Refreshing happens on a separate thread per server.
@example Run the monitor.
monitor.run
@return [ Thread ] The thread the monitor runs on.
@since 2.0.0
# File lib/mongo/server/monitor.rb, line 123 def run! @thread = Thread.new(heartbeat_frequency) do |i| loop do scan! @scan_semaphore.wait(i) end end end
Perform a check of the server with throttling, and update the server's description and average round trip time.
If the server was checked less than MIN_SCAN_FREQUENCY
seconds ago, sleep until MIN_SCAN_FREQUENCY
seconds have passed since the last check. Then perform the check which involves running isMaster on the server being monitored and updating the server description as a result.
@note If the system clock is set to a time in the past, this method
can sleep for a very long time.
@example Run a scan.
monitor.scan!
@return [ Description
] The updated description.
@since 2.0.0
# File lib/mongo/server/monitor.rb, line 150 def scan! throttle_scan_frequency! result = ismaster new_description = Description.new(description.address, result, @round_trip_time_averager.average_round_trip_time) publish(Event::DESCRIPTION_CHANGED, description, new_description) # If this server's response has a mismatched me, or for other reasons, # this server may be removed from topology. When this happens the # monitor thread gets killed. As a result, any code after the publish # call may not run in a particular monitor instance, hence there # shouldn't be any code here. @description = new_description # This call can be after the publish event because if the # monitoring thread gets killed the server is closed and no client # should be waiting for it if options[:server_selection_semaphore] options[:server_selection_semaphore].broadcast end @description end
Stops the server monitor. Kills the thread so it doesn't continue taking memory and sending commands to the connection.
@example Stop the monitor.
monitor.stop!
@param [ Boolean ] wait Whether to wait for background threads to
finish running.
@return [ Boolean ] Is the thread stopped?
@since 2.0.0
# File lib/mongo/server/monitor.rb, line 183 def stop!(wait=false) # Although disconnect! documentation implies a possibility of # failure, all of our disconnects always return true if connection.disconnect! if @thread @thread.kill if wait @thread.join @thread = nil true else !@thread.alive? end else true end else false end end
Private Instance Methods
# File lib/mongo/server/monitor.rb, line 225 def ismaster @mutex.synchronize do if monitoring.monitoring? monitoring.started( Monitoring::SERVER_HEARTBEAT, Monitoring::Event::ServerHeartbeatStarted.new(description.address) ) end result, exc, rtt, average_rtt = round_trip_time_averager.measure do connection.ismaster end if exc log_debug("Error running ismaster on #{description.address}: #{exc.message}") if monitoring.monitoring? monitoring.failed( Monitoring::SERVER_HEARTBEAT, Monitoring::Event::ServerHeartbeatFailed.new(description.address, rtt, exc) ) end result = {} else if monitoring.monitoring? monitoring.succeeded( Monitoring::SERVER_HEARTBEAT, Monitoring::Event::ServerHeartbeatSucceeded.new(description.address, rtt) ) end end result end end
@note If the system clock is set to a time in the past, this method
can sleep for a very long time.
# File lib/mongo/server/monitor.rb, line 260 def throttle_scan_frequency! if @last_scan difference = (Time.now - @last_scan) throttle_time = (MIN_SCAN_FREQUENCY - difference) sleep(throttle_time) if throttle_time > 0 end @last_scan = Time.now end