Module: Mongo::ServerSelector::Selectable

Included in:
Nearest, Primary, PrimaryPreferred, Secondary, SecondaryPreferred
Defined in:
lib/mongo/server_selector/selectable.rb

Overview

Provides common behavior for filtering a list of servers by server mode or tag set.

Since:

  • 2.0.0

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#max_stalenessInteger (readonly)

Returns max_staleness The maximum replication lag, in seconds, that a secondary can suffer and still be eligible for a read.

Returns:

  • (Integer)

    max_staleness The maximum replication lag, in seconds, that a secondary can suffer and still be eligible for a read.

Since:

  • 2.4.0



65
66
67
# File 'lib/mongo/server_selector/selectable.rb', line 65

def max_staleness
  @max_staleness
end

#optionsHash (readonly)

Returns options The options.

Returns:

  • (Hash)

    options The options.

Since:

  • 2.0.0



56
57
58
# File 'lib/mongo/server_selector/selectable.rb', line 56

def options
  @options
end

#tag_setsArray (readonly)

Returns tag_sets The tag sets used to select servers.

Returns:

  • (Array)

    tag_sets The tag sets used to select servers.

Since:

  • 2.0.0



59
60
61
# File 'lib/mongo/server_selector/selectable.rb', line 59

def tag_sets
  @tag_sets
end

Instance Method Details

#==(other) ⇒ true, false

Check equality of two server selector.

Examples:

Check server selector equality.

preference == other

Parameters:

  • other (Object)

    The other preference.

Returns:

  • (true, false)

    Whether the objects are equal.

Since:

  • 2.0.0



77
78
79
80
81
# File 'lib/mongo/server_selector/selectable.rb', line 77

def ==(other)
  name == other.name &&
    tag_sets == other.tag_sets &&
      max_staleness == other.max_staleness
end

#candidates(cluster) ⇒ Array<Server>

Get the potential candidates to select from the cluster.

Examples:

Get the server candidates.

selectable.candidates(cluster)

Parameters:

  • cluster (Cluster)

    The cluster.

Returns:

  • (Array<Server>)

    The candidate servers.

Since:

  • 2.4.0



308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/mongo/server_selector/selectable.rb', line 308

def candidates(cluster)
  if cluster.single?
    cluster.servers.each { |server| validate_max_staleness_support!(server) }
  elsif cluster.sharded?
    local_threshold = local_threshold_with_cluster(cluster)
    near_servers(cluster.servers, local_threshold).each do |server|
      validate_max_staleness_support!(server)
    end
  else
    validate_max_staleness_value!(cluster) unless cluster.unknown?
    select(cluster.servers)
  end
end

#initialize(options = nil) ⇒ Object

Initialize the server selector.

Examples:

Initialize the selector.

Mongo::ServerSelector::Secondary.new(:tag_sets => [{'dc' => 'nyc'}])

Initialize the preference with no options.

Mongo::ServerSelector::Secondary.new

Parameters:

  • options (Hash) (defaults to: nil)

    The server preference options.

Options Hash (options):

  • :local_threshold (Integer)

    The local threshold boundary for nearest selection in seconds.

  • max_staleness (Integer)

    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.

Raises:

Since:

  • 2.0.0



44
45
46
47
48
49
50
51
52
53
# File 'lib/mongo/server_selector/selectable.rb', line 44

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

#inspectString

Inspect the server selector.

Examples:

Inspect the server selector.

selector.inspect

Returns:

  • (String)

    The inspection.

Since:

  • 2.2.0



91
92
93
# File 'lib/mongo/server_selector/selectable.rb', line 91

def inspect
  "#<#{self.class.name}:0x#{object_id} tag_sets=#{tag_sets.inspect} max_staleness=#{max_staleness.inspect}>"
end

#local_thresholdFloat

Deprecated.

This setting is now taken from the cluster options when a server is selected. Will be removed in 3.0.

Get the local threshold boundary for nearest selection in seconds.

Examples:

Get the local threshold.

selector.local_threshold

Returns:

  • (Float)

    The local threshold.

Since:

  • 2.0.0



294
295
296
# File 'lib/mongo/server_selector/selectable.rb', line 294

def local_threshold
  @local_threshold ||= (options[:local_threshold] || ServerSelector::LOCAL_THRESHOLD)
end

#local_threshold_with_cluster(cluster) ⇒ Object

Since:

  • 2.0.0



279
280
281
# File 'lib/mongo/server_selector/selectable.rb', line 279

def local_threshold_with_cluster(cluster)
  options[:local_threshold] || cluster.options[:local_threshold] || LOCAL_THRESHOLD
end

#select_server(cluster, ping = nil, session = nil) ⇒ Mongo::Server

Select a server from the specified cluster, taking into account mongos pinning for the specified session.

If the session is given and has a pinned server, this server is the only server considered for selection. If the server is of type mongos, it is returned immediately; otherwise monitoring checks on this server are initiated to update its status, and if the server becomes a mongos within the server selection timeout, it is returned.

If no session is given or the session does not have a pinned server, normal server selection process is performed among all servers in the specified cluster matching the preference of this server selector object. Monitoring checks are initiated on servers in the cluster until a suitable server is found, up to the server selection timeout.

If a suitable server is not found within the server selection timeout, this method raises Error::NoServerAvailable.

Parameters:

  • cluster (Mongo::Cluster)

    The cluster from which to select an eligible server.

  • ping (true, false) (defaults to: nil)

    Whether to ping the server before selection. Deprecated and ignored.

  • session (Session | nil) (defaults to: nil)

    Optional session to take into account for mongos pinning. Added in version 2.10.0.

Returns:

Raises:

  • (Error::NoServerAvailable)

    No server was found matching the specified preference / pinning requirement in the server selection timeout.

  • (Error::LintError)

    An unexpected condition was detected, and lint mode is enabled.

Since:

  • 2.0.0



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/mongo/server_selector/selectable.rb', line 129

def select_server(cluster, ping = nil, session = nil)
  server_selection_timeout = cluster.options[:server_selection_timeout] || SERVER_SELECTION_TIMEOUT

  # Special handling for zero timeout: if we have to select a server,
  # and the timeout is zero, fail immediately (since server selection
  # will take some non-zero amount of time in any case).
  if server_selection_timeout == 0
    msg = "Failing server selection due to zero timeout. " +
      " Requested #{name} in cluster: #{cluster.summary}"
    raise Error::NoServerAvailable.new(self, cluster, msg)
  end

  deadline = Time.now + server_selection_timeout

  if session && session.pinned_server
    if Mongo::Lint.enabled?
      unless cluster.sharded?
        raise Error::LintError, "Session has a pinned server in a non-sharded topology: #{topology}"
      end
    end

    if !session.in_transaction?
      session.unpin
    end

    if server = session.pinned_server
      # Here we assume that a mongos stays in the topology indefinitely.
      # This will no longer be the case once SRV polling is implemented.

      unless server.mongos?
        while (time_remaining = deadline - Time.now) > 0
          wait_for_server_selection(cluster, time_remaining)
        end

        unless server.mongos?
          msg = "The session being used is pinned to the server which is not a mongos: #{server.summary} " +
            "(after #{server_selection_timeout} seconds)"
          raise Error::NoServerAvailable.new(self, cluster, msg)
        end
      end

      return server
    end
  end

  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

  loop do
    servers = candidates(cluster)
    if Lint.enabled?
      servers.each do |server|
        # It is possible for a server to have a nil average RTT here
        # because the ARTT comes from description which may be updated
        # by a background thread while server selection is running.
        # Currently lint mode is not a public feature, if/when this
        # changes (https://jira.mongodb.org/browse/RUBY-1576) the
        # requirement for ARTT to be not nil would need to be removed.
        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

      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

      if session && session.starting_transaction? && cluster.sharded?
        session.pin(server)
      end

      return server
    end

    cluster.scan!(false)

    time_remaining = deadline - Time.now
    if time_remaining > 0
      wait_for_server_selection(cluster, time_remaining)

      # If we wait for server selection, perform another round of
      # attempting to locate a suitable server. Otherwise server selection
      # can raise NoServerAvailable message when the diagnostics
      # reports an available server of the requested type.
    else
      break
    end
  end

  msg = "No #{name} server is available in cluster: #{cluster.summary} " +
          "with timeout=#{server_selection_timeout}, " +
          "LT=#{local_threshold_with_cluster(cluster)}"
  msg += server_selection_diagnostic_message(cluster)
  raise Error::NoServerAvailable.new(self, cluster, msg)
rescue Error::NoServerAvailable => e
  if session && session.in_transaction? && !session.committing_transaction?
    e.add_label('TransientTransactionError')
  end
  if session && session.committing_transaction?
    e.add_label('UnknownTransactionCommitResult')
  end
  raise e
end

#server_selection_timeoutFloat

Deprecated.

This setting is now taken from the cluster options when a server is selected. Will be removed in 3.0.

Get the timeout for server selection.

Examples:

Get the server selection timeout, in seconds.

selector.server_selection_timeout

Returns:

  • (Float)

    The timeout.

Since:

  • 2.0.0



274
275
276
277
# File 'lib/mongo/server_selector/selectable.rb', line 274

def server_selection_timeout
  @server_selection_timeout ||=
    (options[:server_selection_timeout] || ServerSelector::SERVER_SELECTION_TIMEOUT)
end