lib/mongo/server_selector/selectable.rb in mongo-2.6.4 vs lib/mongo/server_selector/selectable.rb in mongo-2.7.0.rc0

- old
+ new

@@ -1,6 +1,6 @@ -# Copyright (C) 2014-2018 MongoDB, Inc. +# Copyright (C) 2014-2019 MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # @@ -58,10 +58,14 @@ # # @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 @@ -98,23 +102,41 @@ @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) + if Lint.enabled? + servers.each do |server| + if server.average_round_trip_time.nil? + raise Error::LintError, "Server #{server.address} has nil average rtt" + end + end + end if servers && !servers.compact.empty? + unless cluster.topology.compatible? + raise Error::UnsupportedFeatures, cluster.topology.compatibility_error.to_s + end + + # This list of servers may be ordered in a specific way + # by the selector (e.g. for secondary preferred, the first + # server may be a secondary and the second server may be primary) + # and we should take the first server here respecting the order server = servers.first - # HACK: all servers in a topology must satisfy wire protocol - # constraints. There is probably a better implementation than - # checking all servers here - cluster.servers.each do |a_server| - a_server.check_driver_support! + + if cluster.topology.single? && + cluster.topology.replica_set_name && + cluster.topology.replica_set_name != server.description.replica_set_name + then + msg = "Cluster topology specifies replica set name #{cluster.topology.replica_set_name}, but the server has replica set name #{server.description.replica_set_name || '<nil>'}" + raise Error::NoServerAvailable.new(self, cluster, msg) end + return server end - cluster.scan! + cluster.scan!(false) end - raise Error::NoServerAvailable.new(self) + raise Error::NoServerAvailable.new(self, cluster) end # Get the timeout for server selection. # # @example Get the server selection timeout, in seconds. @@ -193,10 +215,14 @@ # @since 2.0.0 def secondaries(candidates) matching_servers = candidates.select(&:secondary?) matching_servers = filter_stale_servers(matching_servers, primary(candidates).first) matching_servers = match_tag_sets(matching_servers) unless tag_sets.empty? + # Per server selection spec the server selected MUST be a random + # one matching staleness and latency requirements. + # Selectors always pass the output of #secondaries to #nearest + # which shuffles the server list, fulfilling this requirement. matching_servers end # Select the near servers from a list of provided candidates, taking the # local threshold into account. @@ -231,25 +257,24 @@ matches || [] end def filter_stale_servers(candidates, primary = nil) return candidates unless @max_staleness - max_staleness_ms = @max_staleness * 1000 if primary candidates.select do |server| validate_max_staleness_support!(server) staleness = (server.last_scan - server.last_write_date) - (primary.last_scan - primary.last_write_date) + - (server.heartbeat_frequency_seconds * 1000) - staleness <= max_staleness_ms + server.heartbeat_frequency_seconds + staleness <= @max_staleness end else max_write_date = candidates.collect(&:last_write_date).max candidates.select do |server| validate_max_staleness_support!(server) - staleness = max_write_date - server.last_write_date + (server.heartbeat_frequency_seconds * 1000) - staleness <= max_staleness_ms + staleness = max_write_date - server.last_write_date + server.heartbeat_frequency_seconds + staleness <= @max_staleness end end end def validate!