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.
Constant Summary
-
DEFAULT_HEARTBEAT_INTERVAL =
# File 'lib/mongo/server/monitor.rb', line 36
The default interval between server status refreshes is 10 seconds.
10 -
MIN_SCAN_INTERVAL =
# File 'lib/mongo/server/monitor.rb', line 42
The minimum time between forced server scans. Is minHeartbeatFrequencyMS in the SDAM spec.
0.5 -
RTT_WEIGHT_FACTOR =
# File 'lib/mongo/server/monitor.rb', line 48Deprecated.
Will be removed in version 3.0.
The weighting factor (alpha) for calculating the average moving round trip time.
0.2
::Mongo::Loggable - Included
Class Method Summary
-
.new(server, event_listeners, monitoring, options = {}) ⇒ Monitor
constructor
Internal use only
Create the new server monitor.
Instance Attribute Summary
- #monitoring ⇒ Monitoring readonly Internal use only
- #options ⇒ Hash readonly Internal use only
- #server ⇒ Server readonly Internal use only
-
#rtt_measurement_only? ⇒ true | false
readonly
private
Internal use only
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
PushMonitoris running as the authoritative SDAM source. -
#streaming_enabled? ⇒ true | false
readonly
private
Internal use only
Returns whether the streaming protocol is enabled, based on the serverMonitoringMode option.
::Mongo::BackgroundThread - Included
::Mongo::Event::Publisher - Included
Instance Method Summary
-
#cancel_check!
Internal use only
Cancel the in-progress check and close the monitoring connection.
- #connection ⇒ Mongo::Server::Monitor::Connection | nil Internal use only
- #create_push_monitor!(topology_version, connection) Internal use only
-
#do_work
Internal use only
Perform a check of the server.
-
#heartbeat_interval ⇒ Float
Internal use only
The interval between regular server checks.
- #push_monitor ⇒ Server::PushMonitor | nil Internal use only
-
#restart! ⇒ Thread
Internal use only
Restarts the server monitor unless the current thread is alive.
- #run_sdam_flow(result, awaited: false, scan_error: nil, rtt_only: false) Internal use only
-
#scan! ⇒ Description
Internal use only
Perform a check of the server with throttling, and update the server's description and average round trip time.
-
#stop! ⇒ true | false
Stop the background thread and wait for it to terminate for a reasonable amount of time.
- #stop_push_monitor! Internal use only
- #to_s Internal use only
- #check private Internal use only
-
#clear_connection(connection)
private
Internal use only
Clear the monitoring connection, but only if it is still the one passed in.
- #do_scan(publish_heartbeat: true) private Internal use only
- #pre_stop private Internal use only
-
#store_connection(connection)
private
Internal use only
Store a freshly established monitoring connection.
- #throttle_scan_frequency! private Internal use only
::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
Monitor must never be directly instantiated outside of a ::Mongo::Server.
Create the new server monitor.
# File 'lib/mongo/server/monitor.rb', line 76
def initialize(server, event_listeners, monitoring, = {}) raise ArgumentError, "Wrong monitoring type: #{monitoring.inspect}" unless monitoring.is_a?(Monitoring) raise ArgumentError, 'App metadata is required' unless [:] raise ArgumentError, 'Push monitor app metadata is required' unless [:] @server = server @event_listeners = event_listeners @monitoring = monitoring @options = .freeze @mutex = Mutex.new @sdam_mutex = Mutex.new @next_earliest_scan = @next_wanted_scan = Time.now @update_mutex = Mutex.new # Guards reads and writes of @connection so the polling connection can # be cancelled from another thread. Per the Server Monitoring spec's # cancelCheck pseudocode, the lock is only held long enough to copy or # assign the reference - never across the blocking check. @connection_lock = Mutex.new end
Instance Attribute Details
#monitoring ⇒ Monitoring (readonly)
# File 'lib/mongo/server/monitor.rb', line 127
attr_reader :monitoring
#options ⇒ Hash (readonly)
# File 'lib/mongo/server/monitor.rb', line 108
attr_reader :
#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.
# File 'lib/mongo/server/monitor.rb', line 350
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
#server ⇒ Server (readonly)
# File 'lib/mongo/server/monitor.rb', line 98
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
# File 'lib/mongo/server/monitor.rb', line 451
def streaming_enabled? mode = [:server_monitoring_mode] || :auto case mode when :poll false when :stream true when :auto !Server::AppMetadata::Environment.new.faas? end end
Instance Method Details
#cancel_check!
Cancel the in-progress check and close the monitoring connection.
Called when the server is marked Unknown from a network error, per the
::Mongo::Server Monitoring spec ("hello or legacy hello Cancellation"). Stops the
streaming PushMonitor (interrupting its awaited hello read) and closes
the polling connection, so the next check must establish a fresh one
rather than re-validating the server over a possibly-dead socket.
# File 'lib/mongo/server/monitor.rb', line 210
def cancel_check! stop_push_monitor! # Copy the connection reference under the lock, then interrupt and close # it outside the lock. Closing the socket interrupts any in-progress # read on the monitor thread; nil-ing the reference forces the next # check to reconnect. The monitor thread is the only writer of a new # connection, so it is safe for this thread to clear it. connection = @connection_lock.synchronize do conn = @connection @connection = nil conn end connection&.disconnect! end
#check (private)
# File 'lib/mongo/server/monitor.rb', line 365
def check # Snapshot the connection under the lock. A concurrent cancel_check! # may nil @connection from another thread; working on a local copy keeps # this check consistent, and the guarded writeback below never clobbers # a connection the monitor thread did not itself establish. connection = self.connection 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! clear_connection(connection) 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 ) = connection.dispatch_bytes(cmd.serialize.to_s) .documents.first rescue Mongo::Error connection.disconnect! clear_connection(connection) raise end else connection = Connection.new(server.address, ) connection.connect! result = server.round_trip_time_calculator.measure do connection.handshake! end store_connection(connection) if (tv_doc = result['topologyVersion']) if streaming_enabled? # Run the instance we just created rather than re-reading the # push_monitor getter: a concurrent cancel_check! may have nil'd # @push_monitor between the two calls. push_monitor = create_push_monitor!(TopologyVersion.new(tv_doc), connection) push_monitor.run! else stop_push_monitor! end else # Failed response or pre-4.4 server stop_push_monitor! end result end result end
#clear_connection(connection) (private)
Clear the monitoring connection, but only if it is still the one passed in. A concurrent cancel_check! may have already cleared or replaced it, in which case we must leave the current connection alone.
# File 'lib/mongo/server/monitor.rb', line 428
def clear_connection(connection) @connection_lock.synchronize do @connection = nil if @connection.equal?(connection) end end
#connection ⇒ Mongo::Server::Monitor::Connection | nil
# File 'lib/mongo/server/monitor.rb', line 103
def connection @connection_lock.synchronize { @connection } end
#create_push_monitor!(topology_version, connection)
# File 'lib/mongo/server/monitor.rb', line 175
def create_push_monitor!(topology_version, connection) @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(.merge( socket_timeout: heartbeat_interval + connection.socket_timeout, app_metadata: [:], check_document: connection.check_document )) ) end end
#do_scan(publish_heartbeat: true) (private)
# File 'lib/mongo/server/monitor.rb', line 326
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: [:logger], log_prefix: [:log_prefix], bg_error_backtrace: [:bg_error_backtrace]) raise e end
#do_work
Perform a check of the server.
# File 'lib/mongo/server/monitor.rb', line 140
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_interval ⇒ Float
The interval between regular server checks.
# File 'lib/mongo/server/monitor.rb', line 113
def heartbeat_interval [:heartbeat_interval] || DEFAULT_HEARTBEAT_INTERVAL end
#pre_stop (private)
# File 'lib/mongo/server/monitor.rb', line 322
def pre_stop server.scan_semaphore.signal end
#push_monitor ⇒ Server::PushMonitor | nil
# File 'lib/mongo/server/monitor.rb', line 131
def push_monitor @update_mutex.synchronize do @push_monitor end end
#restart! ⇒ Thread
Restarts the server monitor unless the current thread is alive.
# File 'lib/mongo/server/monitor.rb', line 308
def restart! if @thread && @thread.alive? @thread else run! end end
#run_sdam_flow(result, awaited: false, scan_error: nil, rtt_only: false)
# File 'lib/mongo/server/monitor.rb', line 268
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
If the system clock moves backwards, this method can sleep for a very long time.
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.
# File 'lib/mongo/server/monitor.rb', line 244
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.
# File 'lib/mongo/server/monitor.rb', line 160
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!
# File 'lib/mongo/server/monitor.rb', line 192
def stop_push_monitor! @update_mutex.synchronize do if @push_monitor @push_monitor.stop! @push_monitor = nil end end end
#store_connection(connection) (private)
Store a freshly established monitoring connection.
# File 'lib/mongo/server/monitor.rb', line 419
def store_connection(connection) @connection_lock.synchronize do @connection = connection end end
#throttle_scan_frequency! (private)
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 436
def throttle_scan_frequency! delta = @next_earliest_scan - Time.now return unless delta > 0 sleep(delta) end