lib/mongo/server_selector/base.rb in mongo-2.19.3 vs lib/mongo/server_selector/base.rb in mongo-2.20.0
- old
+ new
@@ -162,30 +162,34 @@
# Deprecated and ignored.
# @param [ Session | nil ] session Optional session to take into account
# for mongos pinning. Added in version 2.10.0.
# @param [ true | false ] write_aggregation Whether we need a server that
# supports writing aggregations (e.g. with $merge/$out) on secondaries.
+ # @param [ Array<Server> ] deprioritized A list of servers that should
+ # be selected from only if no other servers are available. This is
+ # used to avoid selecting the same server twice in a row when
+ # retrying a command.
#
# @return [ Mongo::Server ] A server matching the server preference.
#
# @raise [ Error::NoServerAvailable ] No server was found matching the
# specified preference / pinning requirement in the server selection
# timeout.
# @raise [ Error::LintError ] An unexpected condition was detected, and
# lint mode is enabled.
#
# @since 2.0.0
- def select_server(cluster, ping = nil, session = nil, write_aggregation: false)
- select_server_impl(cluster, ping, session, write_aggregation).tap do |server|
+ def select_server(cluster, ping = nil, session = nil, write_aggregation: false, deprioritized: [])
+ select_server_impl(cluster, ping, session, write_aggregation, deprioritized).tap do |server|
if Lint.enabled? && !server.pool.ready?
raise Error::LintError, 'Server selector returning a server with a pool which is not ready'
end
end
end
# Parameters and return values are the same as for select_server.
- private def select_server_impl(cluster, ping, session, write_aggregation)
+ private def select_server_impl(cluster, ping, session, write_aggregation, deprioritized)
if cluster.topology.is_a?(Cluster::Topology::LoadBalanced)
return cluster.servers.first
end
server_selection_timeout = cluster.options[:server_selection_timeout] || SERVER_SELECTION_TIMEOUT
@@ -264,11 +268,11 @@
raise Error::LintError, "Server #{server.summary} is known but has non-ready pool"
end
end
end
- server = try_select_server(cluster, write_aggregation: write_aggregation)
+ server = try_select_server(cluster, write_aggregation: write_aggregation, deprioritized: deprioritized)
if server
unless cluster.topology.compatible?
raise Error::UnsupportedFeatures, cluster.topology.compatibility_error.to_s
end
@@ -319,15 +323,19 @@
#
# @param [ Mongo::Cluster ] cluster The cluster from which to select
# an eligible server.
# @param [ true | false ] write_aggregation Whether we need a server that
# supports writing aggregations (e.g. with $merge/$out) on secondaries.
+ # @param [ Array<Server> ] deprioritized A list of servers that should
+ # be selected from only if no other servers are available. This is
+ # used to avoid selecting the same server twice in a row when
+ # retrying a command.
#
# @return [ Server | nil ] A suitable server, if one exists.
#
# @api private
- def try_select_server(cluster, write_aggregation: false)
+ def try_select_server(cluster, write_aggregation: false, deprioritized: [])
servers = if write_aggregation && cluster.replica_set?
# 1. Check if ALL servers in cluster support secondary writes.
is_write_supported = cluster.servers.reduce(true) do |res, server|
res && server.features.merge_out_on_secondary_enabled?
end
@@ -345,11 +353,11 @@
# 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
+ server = suitable_server(servers, deprioritized)
if server
if Lint.enabled?
# It is possible for a server to have a nil average RTT here
# because the ARTT comes from description which may be updated
@@ -415,9 +423,27 @@
[]
end
end
private
+
+ # Returns a server from the list of servers that is suitable for
+ # executing the operation.
+ #
+ # @param [ Array<Server> ] servers The candidate servers.
+ # @param [ Array<Server> ] deprioritized A list of servers that should
+ # be selected from only if no other servers are available.
+ #
+ # @return [ Server | nil ] The suitable server or nil if no suitable
+ # server is available.
+ def suitable_server(servers, deprioritized)
+ preferred = servers - deprioritized
+ if preferred.empty?
+ servers.first
+ else
+ preferred.first
+ end
+ end
# Convert this server preference definition into a format appropriate
# for sending to a MongoDB server (i.e., as a command field).
#
# @return [ Hash ] The server preference formatted as a command field value.