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

connection[R]

@return [ Mongo::Server::Monitor::Connection ] connection The connection to use.

description[R]

@return [ Server::Description ] description The server

description the monitor refreshes.
last_scan[R]

@return [ Time ] #last_scan The time when the last server scan started.

@since 2.4.0

monitoring[R]

@return [ Monitoring ] monitoring The monitoring.

options[R]

@return [ Hash ] options The server options.

round_trip_time_averager[R]

@api private

scan_semaphore[R]

@api private

Public Class Methods

new(address, event_listeners, monitoring, options = {}) click to toggle source

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

heartbeat_frequency() click to toggle source

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
restart!() click to toggle source

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
run!() click to toggle source

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
scan!() click to toggle source

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
stop!(wait=false) click to toggle source

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

ismaster() click to toggle source
# 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
throttle_scan_frequency!() click to toggle source

@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