lib/mongo/server_selector/selectable.rb in mongo-2.7.0 vs lib/mongo/server_selector/selectable.rb in mongo-2.7.1

- old
+ new

@@ -18,10 +18,42 @@ # Provides common behavior for filtering a list of servers by server mode or tag set. # # @since 2.0.0 module Selectable + # Initialize the server selector. + # + # @example Initialize the selector. + # Mongo::ServerSelector::Secondary.new(:tag_sets => [{'dc' => 'nyc'}]) + # + # @example Initialize the preference with no options. + # Mongo::ServerSelector::Secondary.new + # + # @param [ Hash ] options The server preference options. + # + # @option options [ Integer ] :local_threshold The local threshold boundary for + # nearest selection in seconds. + # @option options [ Integer ] max_staleness The maximum replication lag, + # in seconds, that a secondary can suffer and still be eligible for a read. + # A value of -1 is treated identically to nil, which is to not + # have a maximum staleness. + # + # @raise [ Error::InvalidServerPreference ] If tag sets are specified + # but not allowed. + # + # @since 2.0.0 + def initialize(options = nil) + options = options ? options.dup : {} + if options[:max_staleness] == -1 + options.delete(:max_staleness) + end + @options = options.freeze + @tag_sets = (options[:tag_sets] || []).freeze + @max_staleness = options[:max_staleness] + validate! + end + # @return [ Hash ] options The options. attr_reader :options # @return [ Array ] tag_sets The tag sets used to select servers. attr_reader :tag_sets @@ -46,38 +78,10 @@ name == other.name && tag_sets == other.tag_sets && max_staleness == other.max_staleness end - # Initialize the server selector. - # - # @example Initialize the selector. - # Mongo::ServerSelector::Secondary.new(:tag_sets => [{'dc' => 'nyc'}]) - # - # @example Initialize the preference with no options. - # Mongo::ServerSelector::Secondary.new - # - # @param [ Hash ] options The server preference options. - # - # @option options [ Integer ] :local_threshold The local threshold boundary for - # nearest selection in seconds. - # @option options [ Integer ] max_staleness The maximum replication lag, - # in seconds, that a secondary can suffer and still be eligible for a read. - # A value of -1 is treated identically to nil, which is to not - # have a maximum staleness. - # - # @raise [ Error::InvalidServerPreference ] If tag sets are specified - # but not allowed. - # - # @since 2.0.0 - def initialize(options = {}) - @options = (options || {}).freeze - @tag_sets = (options[:tag_sets] || []).freeze - @max_staleness = options[:max_staleness] unless options[:max_staleness] == -1 - validate! - end - # Inspect the server selector. # # @example Inspect the server selector. # selector.inspect # @@ -97,10 +101,28 @@ # # @return [ Mongo::Server ] A server matching the server preference. # # @since 2.0.0 def select_server(cluster, ping = nil) + if cluster.replica_set? + validate_max_staleness_value_early! + end + if cluster.addresses.empty? + if Lint.enabled? + unless cluster.servers.empty? + raise Error::LintError, "Cluster has no addresses but has servers: #{cluster.servers.map(&:inspect).join(', ')}" + end + end + msg = "Cluster has no addresses, and therefore will never have a server" + raise Error::NoServerAvailable.new(self, cluster, msg) + end +=begin Add this check in version 3.0.0 + unless cluster.connected? + msg = 'Cluster is disconnected' + raise Error::NoServerAvailable.new(self, cluster, msg) + end +=end @local_threshold = cluster.options[:local_threshold] || LOCAL_THRESHOLD @server_selection_timeout = cluster.options[:server_selection_timeout] || SERVER_SELECTION_TIMEOUT deadline = Time.now + server_selection_timeout while (deadline - Time.now) > 0 servers = candidates(cluster) @@ -132,11 +154,28 @@ return server end cluster.scan!(false) end - raise Error::NoServerAvailable.new(self, cluster) + + msg = "No #{name} server is available in cluster: #{cluster.summary} " + + "with timeout=#{server_selection_timeout}, " + + "LT=#{local_threshold}" + dead_monitors = [] + cluster.servers_list.each do |server| + thread = server.monitor.instance_variable_get('@thread') + if thread.nil? || !thread.alive? + dead_monitors << server + end + end + if dead_monitors.any? + msg += ". The following servers have dead monitor threads: #{dead_monitors.map(&:summary).join(', ')}" + end + unless cluster.connected? + msg += ". The cluster is disconnected (client may have been closed)" + end + raise Error::NoServerAvailable.new(self, cluster, msg) end # Get the timeout for server selection. # # @example Get the server selection timeout, in seconds. @@ -290,15 +329,30 @@ if @max_staleness && !server.features.max_staleness_enabled? raise Error::InvalidServerPreference.new(Error::InvalidServerPreference::NO_MAX_STALENESS_WITH_LEGACY_SERVER) end end + def validate_max_staleness_value_early! + if @max_staleness + unless @max_staleness >= SMALLEST_MAX_STALENESS_SECONDS + msg = "`max_staleness` value (#{@max_staleness}) is too small - it must be at least " + + "`Mongo::ServerSelector::SMALLEST_MAX_STALENESS_SECONDS` (#{ServerSelector::SMALLEST_MAX_STALENESS_SECONDS})" + raise Error::InvalidServerPreference.new(msg) + end + end + end + def validate_max_staleness_value!(cluster) if @max_staleness heartbeat_frequency_seconds = cluster.options[:heartbeat_frequency] || Server::Monitor::HEARTBEAT_FREQUENCY - unless @max_staleness >= [ SMALLEST_MAX_STALENESS_SECONDS, - (heartbeat_frequency_seconds + Cluster::IDLE_WRITE_PERIOD_SECONDS) ].max - raise Error::InvalidServerPreference.new(Error::InvalidServerPreference::INVALID_MAX_STALENESS) + unless @max_staleness >= [ + SMALLEST_MAX_STALENESS_SECONDS, + min_cluster_staleness = heartbeat_frequency_seconds + Cluster::IDLE_WRITE_PERIOD_SECONDS, + ].max + msg = "`max_staleness` value (#{@max_staleness}) is too small - it must be at least " + + "`Mongo::ServerSelector::SMALLEST_MAX_STALENESS_SECONDS` (#{ServerSelector::SMALLEST_MAX_STALENESS_SECONDS}) and (the cluster's heartbeat_frequency " + + "setting + `Mongo::Cluster::IDLE_WRITE_PERIOD_SECONDS`) (#{min_cluster_staleness})" + raise Error::InvalidServerPreference.new(msg) end end end end end