123456789_123456789_123456789_123456789_123456789_

Class: Mongo::Server::Monitor Private

Relationships & Source Files
Namespace Children
Classes:
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, Forwardable
Instance Chain:
Inherits: Object
Defined in: lib/mongo/server/monitor.rb,
lib/mongo/server/monitor/app_metadata.rb,
lib/mongo/server/monitor/connection.rb

Overview

Responsible for periodically polling a server via hello commands to keep the server's status up to date.

Does all work in a background thread so as to not interfere with other operations performed by the driver.

Since:

  • 2.0.0

Constant Summary

::Mongo::Loggable - Included

PREFIX

Class Method Summary

Instance Attribute Summary

::Mongo::BackgroundThread - Included

::Mongo::Event::Publisher - Included

Instance Method Summary

::Mongo::BackgroundThread - Included

#run!

Start the background thread.

#stop!

Stop the background thread and wait for to terminate for a reasonable amount of time.

#do_work

Override this method to do the work in the background thread.

#pre_stop

Override this method to perform additional signaling for the background thread to stop.

#start!,
#wait_for_stop

Waits for the thread to die, with a timeout.

::Mongo::Event::Publisher - Included

#publish

Publish the provided event.

::Mongo::Loggable - Included

#log_debug

Convenience method to log debug messages with the standard prefix.

#log_error

Convenience method to log error messages with the standard prefix.

#log_fatal

Convenience method to log fatal messages with the standard prefix.

#log_info

Convenience method to log info messages with the standard prefix.

#log_warn

Convenience method to log warn messages with the standard prefix.

#logger

Get the logger instance.

#_mongo_log_prefix, #format_message

Constructor Details

.new(server, event_listeners, monitoring, options = {}) ⇒ Monitor

Note:

Monitor must never be directly instantiated outside of a ::Mongo::Server.

Create the new server monitor.

Examples:

Create the server monitor.

Mongo::Server::Monitor.new(address, listeners, monitoring)

Parameters:

  • server (Server)

    The server to monitor.

  • event_listeners (Event::Listeners)

    The event listeners.

  • monitoring (Monitoring)

    The monitoring.

  • options (Hash) (defaults to: {})

    The options.

Options Hash (options):

  • :connect_timeout (Float)

    The timeout, in seconds, to use when establishing the monitoring connection.

  • :heartbeat_interval (Float)

    The interval between regular server checks.

  • :logger (Logger)

    A custom logger to use.

  • :monitor_app_metadata (Mongo::Server::Monitor::AppMetadata)

    The metadata to use for regular monitoring connection.

  • :push_monitor_app_metadata (Mongo::Server::Monitor::AppMetadata)

    The metadata to use for push monitor's connection.

  • :socket_timeout (Float)

    The timeout, in seconds, to execute operations on the monitoring connection.

Raises:

  • (ArgumentError)

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 76

def initialize(server, event_listeners, monitoring, options = {})
  raise ArgumentError, "Wrong monitoring type: #{monitoring.inspect}" unless monitoring.is_a?(Monitoring)
  raise ArgumentError, 'App metadata is required' unless options[:]
  raise ArgumentError, 'Push monitor app metadata is required' unless options[:]

  @server = server
  @event_listeners = event_listeners
  @monitoring = monitoring
  @options = options.freeze
  @mutex = Mutex.new
  @sdam_mutex = Mutex.new
  @next_earliest_scan = @next_wanted_scan = Time.now
  @update_mutex = Mutex.new
end

Instance Attribute Details

#connectionMongo::Server::Monitor::Connection (readonly)

Returns:

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 96

attr_reader :connection

#monitoringMonitoring (readonly)

Returns:

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 118

attr_reader :monitoring

#optionsHash (readonly)

Returns:

  • (Hash)

    options The server options.

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 99

attr_reader :options

#rtt_measurement_only?true | false (readonly, private)

Returns whether this scan is only an RTT measurement, which is the case when the streaming protocol is active: a dedicated connection is already established and the PushMonitor is running as the authoritative SDAM source. In the polling protocol there is no running PushMonitor, so the connection-reuse check is a real server check and not RTT-only.

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 312

def rtt_measurement_only?
  return false if @connection.nil?

  # Only suppress the check while the server is in a known state and the
  # PushMonitor is the authoritative streaming source. If the server is
  # Unknown (e.g. an operation error or a streaming failure just marked
  # it so), the polling Monitor must run a full check to recover it
  # rather than waiting for the next streaming response - otherwise the
  # server can stay Unknown long enough to fail server selection.
  return false if server.unknown?

  pm = push_monitor
  !pm.nil? && pm.running?
end

#serverServer (readonly)

Returns:

  • (Server)

    server The server that this monitor is monitoring.

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 93

attr_reader :server

#streaming_enabled?true | false (readonly, private)

Returns whether the streaming protocol is enabled, based on the serverMonitoringMode option. Default mode is :auto.

  • :stream - always use streaming when server supports it
  • :poll - never use streaming
  • :auto - use polling on FaaS platforms, streaming otherwise

Returns:

  • (true | false)

    Whether streaming is enabled.

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 387

def streaming_enabled?
  mode = options[:server_monitoring_mode] || :auto
  case mode
  when :poll
    false
  when :stream
    true
  when :auto
    !Server::AppMetadata::Environment.new.faas?
  end
end

Instance Method Details

#check (private)

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 327

def check
  if @connection && @connection.pid != Process.pid
    log_warn("Detected PID change - Mongo client should have been reconnected (old pid #{@connection.pid}, new pid #{Process.pid}")
    @connection.disconnect!
    @connection = nil
  end

  if @connection
    result = server.round_trip_time_calculator.measure do
      doc = @connection.check_document
      cmd = Protocol::Query.new(
        Database::ADMIN, Database::COMMAND, doc, limit: -1
      )
      message = @connection.dispatch_bytes(cmd.serialize.to_s)
      message.documents.first
    rescue Mongo::Error
      @connection.disconnect!
      @connection = nil
      raise
    end
  else
    connection = Connection.new(server.address, options)
    connection.connect!
    result = server.round_trip_time_calculator.measure do
      connection.handshake!
    end
    @connection = connection
    if (tv_doc = result['topologyVersion'])
      if streaming_enabled?
        create_push_monitor!(TopologyVersion.new(tv_doc))
        push_monitor.run!
      else
        stop_push_monitor!
      end
    else
      # Failed response or pre-4.4 server
      stop_push_monitor!
    end
    result
  end
  result
end

#create_push_monitor!(topology_version)

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 162

def create_push_monitor!(topology_version)
  @update_mutex.synchronize do
    @push_monitor = nil if @push_monitor && !@push_monitor.running?

    @push_monitor ||= PushMonitor.new(
      self,
      topology_version,
      monitoring,
      **Utils.shallow_symbolize_keys(options.merge(
                                       socket_timeout: heartbeat_interval + connection.socket_timeout,
                                       app_metadata: options[:],
                                       check_document: @connection.check_document
                                     ))
    )
  end
end

#do_scan(publish_heartbeat: true) (private)

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 288

def do_scan(publish_heartbeat: true)
  if publish_heartbeat
    monitoring.publish_heartbeat(server) do
      check
    end
  else
    check
  end
rescue StandardError => e
  msg = "Error checking #{server.address}"
  Utils.warn_bg_exception(msg, e,
                          logger: options[:logger],
                          log_prefix: options[:log_prefix],
                          bg_error_backtrace: options[:bg_error_backtrace])
  raise e
end

#do_work

Perform a check of the server.

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 131

def do_work
  scan!
  # @next_wanted_scan may be updated by the push monitor.
  # However we need to check for termination flag so that the monitor
  # thread exits when requested.
  loop do
    delta = @next_wanted_scan - Time.now
    break unless delta > 0

    signaled = server.scan_semaphore.wait(delta)
    break if signaled || @stop_requested
  end
end

#heartbeat_intervalFloat

The interval between regular server checks.

Returns:

  • (Float)

    The heartbeat interval, in seconds.

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 104

def heartbeat_interval
  options[:heartbeat_interval] || DEFAULT_HEARTBEAT_INTERVAL
end

#pre_stop (private)

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 284

def pre_stop
  server.scan_semaphore.signal
end

#push_monitorServer::PushMonitor | nil

Returns:

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 122

def push_monitor
  @update_mutex.synchronize do
    @push_monitor
  end
end

#restart!Thread

Restarts the server monitor unless the current thread is alive.

Examples:

Restart the monitor.

monitor.restart!

Returns:

  • (Thread)

    The thread the monitor runs on.

Since:

  • 2.1.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 270

def restart!
  if @thread && @thread.alive?
    @thread
  else
    run!
  end
end

#run_sdam_flow(result, awaited: false, scan_error: nil, rtt_only: false)

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 230

def run_sdam_flow(result, awaited: false, scan_error: nil, rtt_only: false)
  @sdam_mutex.synchronize do
    old_description = server.description

    # An RTT-only measurement (streaming protocol active) must not update
    # the topology or publish SDAM events. The RTT it gathered is
    # incorporated into the next streaming-hello description via the
    # shared RTT calculator. The scheduling below still runs so the
    # monitor keeps pacing its checks.
    unless rtt_only
      new_description = Description.new(
        server.address,
        result,
        average_round_trip_time: server.round_trip_time_calculator.average_round_trip_time,
        minimum_round_trip_time: server.round_trip_time_calculator.minimum_round_trip_time
      )
      server.cluster.run_sdam_flow(server.description, new_description, awaited: awaited, scan_error: scan_error)
    end

    server.description.tap do |new_description|
      unless awaited
        if new_description.unknown? && !old_description.unknown?
          @next_earliest_scan = @next_wanted_scan = Time.now
        else
          @next_earliest_scan = Time.now + MIN_SCAN_INTERVAL
          @next_wanted_scan = Time.now + heartbeat_interval
        end
      end
    end
  end
end

#scan!Description

Note:

If the system clock moves backwards, this method can sleep for a very long time.

Note:

The return value of this method is deprecated. In version 3.0.0 this method will not have a return value.

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_INTERVAL seconds ago, sleep until MIN_SCAN_INTERVAL seconds have passed since the last check. Then perform the check which involves running hello on the server being monitored and updating the server description as a result.

Returns:

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 206

def scan!
  # Ordinarily the background thread would invoke this method.
  # But it is also possible to invoke scan! directly on a monitor.
  # Allow only one scan to be performed at a time.
  @mutex.synchronize do
    throttle_scan_frequency!

    # When the streaming protocol is active the PushMonitor is the
    # authoritative SDAM source and this scan only measures RTT on the
    # dedicated connection. Per the Server Monitoring spec, an RTT
    # command MUST NOT publish events or update the topology. Compute
    # this before do_scan, which may (re)connect and change the state.
    rtt_only = rtt_measurement_only?

    begin
      result = do_scan(publish_heartbeat: !rtt_only)
    rescue StandardError => e
      run_sdam_flow({}, scan_error: e, rtt_only: rtt_only)
    else
      run_sdam_flow(result, rtt_only: rtt_only)
    end
  end
end

#stop!true | false

Stop the background thread and wait for it to terminate for a reasonable amount of time.

Returns:

  • (true | false)

    Whether the thread was terminated.

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 151

def stop!
  stop_push_monitor!

  # Forward super's return value
  super.tap do
    # Important: disconnect should happen after the background thread
    # terminates.
    connection&.disconnect!
  end
end

#stop_push_monitor!

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 179

def stop_push_monitor!
  @update_mutex.synchronize do
    if @push_monitor
      @push_monitor.stop!
      @push_monitor = nil
    end
  end
end

#throttle_scan_frequency! (private)

Note:

If the system clock is set to a time in the past, this method can sleep for a very long time.

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 372

def throttle_scan_frequency!
  delta = @next_earliest_scan - Time.now
  return unless delta > 0

  sleep(delta)
end

#to_s

Since:

  • 2.0.0

[ GitHub ]

  
# File 'lib/mongo/server/monitor.rb', line 278

def to_s
  "#<#{self.class.name}:#{object_id} #{server.address}>"
end