diff --git a/lib/mongo/cluster.rb b/lib/mongo/cluster.rb index 933d80a6b5..2134d5ec8c 100644 --- a/lib/mongo/cluster.rb +++ b/lib/mongo/cluster.rb @@ -37,6 +37,11 @@ class Cluster # @since 2.1.1 READ_RETRY_INTERVAL = 5 + # How often an idle primary writes a no-op to the oplog. + # + # @since 2.4.0 + IDLE_WRITE_PERIOD_SECONDS = 10 + # @return [ Hash ] The options hash. attr_reader :options diff --git a/lib/mongo/error/invalid_server_preference.rb b/lib/mongo/error/invalid_server_preference.rb index 441b6d16da..eb7c87549a 100644 --- a/lib/mongo/error/invalid_server_preference.rb +++ b/lib/mongo/error/invalid_server_preference.rb @@ -33,7 +33,9 @@ class InvalidServerPreference < Error # Error message for when the max staleness is not at least twice the heartbeat frequency. # # @since 2.4.0 - INVALID_MAX_STALENESS = "max_staleness must be at least twice the client's heartbeat frequency.".freeze + INVALID_MAX_STALENESS = "`max_staleness` value is too small. It must be at least " + + "`ServerSelector::SMALLEST_MAX_STALENESS_SECONDS` and (the cluster's heartbeat_frequency " + + "setting + `Cluster::IDLE_WRITE_PERIOD_SECONDS`).".freeze # Error message when max staleness cannot be used because one or more servers has version < 3.4. # diff --git a/lib/mongo/server.rb b/lib/mongo/server.rb index a8ede780a8..4bfe1f341e 100644 --- a/lib/mongo/server.rb +++ b/lib/mongo/server.rb @@ -45,6 +45,7 @@ class Server # Get the description from the monitor and scan on monitor. def_delegators :monitor, :description, :scan!, :heartbeat_frequency, :last_scan + alias :heartbeat_frequency_seconds :heartbeat_frequency # Delegate convenience methods to the monitor description. def_delegators :description, diff --git a/lib/mongo/server_selector.rb b/lib/mongo/server_selector.rb index 1d13ad520d..54eeef09da 100644 --- a/lib/mongo/server_selector.rb +++ b/lib/mongo/server_selector.rb @@ -38,6 +38,11 @@ module ServerSelector # @since 2.0.0 SERVER_SELECTION_TIMEOUT = 30.freeze + # The smallest allowed max staleness value, in seconds. + # + # @since 2.4.0 + SMALLEST_MAX_STALENESS_SECONDS = 90 + # Primary read preference. # # @since 2.1.0 diff --git a/lib/mongo/server_selector/nearest.rb b/lib/mongo/server_selector/nearest.rb index 6011617255..b170178c98 100644 --- a/lib/mongo/server_selector/nearest.rb +++ b/lib/mongo/server_selector/nearest.rb @@ -67,7 +67,7 @@ def tags_allowed? def to_mongos preference = { :mode => 'nearest' } preference.merge!({ :tags => tag_sets }) unless tag_sets.empty? - preference.merge!({ maxStalenessMS: max_staleness * 1000 }) if max_staleness + preference.merge!({ maxStalenessSeconds: max_staleness }) if max_staleness preference end diff --git a/lib/mongo/server_selector/primary_preferred.rb b/lib/mongo/server_selector/primary_preferred.rb index f82afcb9ab..b8e70a4bf0 100644 --- a/lib/mongo/server_selector/primary_preferred.rb +++ b/lib/mongo/server_selector/primary_preferred.rb @@ -68,7 +68,7 @@ def tags_allowed? def to_mongos preference = { :mode => 'primaryPreferred' } preference.merge!({ :tags => tag_sets }) unless tag_sets.empty? - preference.merge!({ maxStalenessMS: max_staleness * 1000 }) if max_staleness + preference.merge!({ maxStalenessSeconds: max_staleness }) if max_staleness preference end diff --git a/lib/mongo/server_selector/secondary.rb b/lib/mongo/server_selector/secondary.rb index 01fb156031..7defaff877 100644 --- a/lib/mongo/server_selector/secondary.rb +++ b/lib/mongo/server_selector/secondary.rb @@ -68,7 +68,7 @@ def tags_allowed? def to_mongos preference = { :mode => 'secondary' } preference.merge!({ :tags => tag_sets }) unless tag_sets.empty? - preference.merge!({ maxStalenessMS: max_staleness * 1000 }) if max_staleness + preference.merge!({ maxStalenessSeconds: max_staleness }) if max_staleness preference end diff --git a/lib/mongo/server_selector/secondary_preferred.rb b/lib/mongo/server_selector/secondary_preferred.rb index 5ab4218dbc..e1609135df 100644 --- a/lib/mongo/server_selector/secondary_preferred.rb +++ b/lib/mongo/server_selector/secondary_preferred.rb @@ -71,7 +71,7 @@ def to_mongos return nil if tag_sets.empty? && max_staleness.nil? preference = { mode: 'secondaryPreferred' } preference.merge!({ tags: tag_sets }) unless tag_sets.empty? - preference.merge!({ maxStalenessMS: max_staleness * 1000 }) if max_staleness + preference.merge!({ maxStalenessSeconds: max_staleness }) if max_staleness preference end diff --git a/lib/mongo/server_selector/selectable.rb b/lib/mongo/server_selector/selectable.rb index eb976af32b..7df7276368 100644 --- a/lib/mongo/server_selector/selectable.rb +++ b/lib/mongo/server_selector/selectable.rb @@ -26,7 +26,7 @@ module Selectable # @return [ Array ] tag_sets The tag sets used to select servers. attr_reader :tag_sets - # @return [ Float ] max_staleness The maximum replication lag, in seconds, that a + # @return [ 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 @@ -68,7 +68,7 @@ def ==(other) def initialize(options = {}) @options = (options || {}).freeze @tag_sets = (options[:tag_sets] || []).freeze - @max_staleness = options[:max_staleness] if options[:max_staleness] && options[:max_staleness] > 0 + @max_staleness = options[:max_staleness] unless options[:max_staleness] == -1 validate! end @@ -154,7 +154,7 @@ def candidates(cluster) elsif cluster.sharded? near_servers(cluster.servers).each { |server| validate_max_staleness_support!(server) } else - validate_max_staleness_value!(cluster) + validate_max_staleness_value!(cluster) unless cluster.unknown? select(cluster.servers) end end @@ -230,14 +230,14 @@ def filter_stale_servers(candidates, primary = nil) validate_max_staleness_support!(server) staleness = (server.last_scan - server.last_write_date) - (primary.last_scan - primary.last_write_date) + - (server.heartbeat_frequency * 1000) + (server.heartbeat_frequency_seconds * 1000) staleness <= max_staleness_ms 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 * 1000) + staleness = max_write_date - server.last_write_date + (server.heartbeat_frequency_seconds * 1000) staleness <= max_staleness_ms end end @@ -258,10 +258,12 @@ def validate_max_staleness_support!(server) end def validate_max_staleness_value!(cluster) - return unless @max_staleness - heartbeat_frequency = cluster.options[:heartbeat_frequency] || Server::Monitor::HEARTBEAT_FREQUENCY - if @max_staleness < heartbeat_frequency * 2 - raise Error::InvalidServerPreference.new(Error::InvalidServerPreference::INVALID_MAX_STALENESS) + 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) + end end end end diff --git a/lib/mongo/uri.rb b/lib/mongo/uri.rb index 16ba96d814..699e534368 100644 --- a/lib/mongo/uri.rb +++ b/lib/mongo/uri.rb @@ -374,7 +374,7 @@ def self.uri_option(uri_key, name, extra = {}) # Read Options uri_option 'readpreference', :mode, :group => :read, :type => :read_mode uri_option 'readpreferencetags', :tag_sets, :group => :read, :type => :read_tags - uri_option 'maxstalenessms', :max_staleness, :group => :read, :type => :ms_convert + uri_option 'maxstalenessseconds', :max_staleness, :group => :read # Pool options uri_option 'minpoolsize', :min_pool_size diff --git a/spec/mongo/max_staleness_spec.rb b/spec/mongo/max_staleness_spec.rb index c028b3d5e2..78fd012faa 100644 --- a/spec/mongo/max_staleness_spec.rb +++ b/spec/mongo/max_staleness_spec.rb @@ -26,10 +26,10 @@ if spec.heartbeat_frequency TEST_OPTIONS.merge(heartbeat_frequency: spec.heartbeat_frequency) else - copy = TEST_OPTIONS.dup - copy.delete(:heartbeat_frequency) - copy - end.merge!(server_selection_timeout: 0.2) + TEST_OPTIONS.dup.tap do |opts| + opts.delete(:heartbeat_frequency) + end + end.merge!(server_selection_timeout: 0.2, connect_timeout: 0.1) end let(:cluster) do @@ -38,7 +38,8 @@ allow(c).to receive(:single?).and_return(topology.single?) allow(c).to receive(:sharded?).and_return(topology.sharded?) allow(c).to receive(:replica_set?).and_return(topology.replica_set?) - allow(c).to receive(:options).and_return(options.merge(server_selection_timeout: 0.2)) + allow(c).to receive(:unknown?).and_return(topology.unknown?) + allow(c).to receive(:options).and_return(options) allow(c).to receive(:scan!).and_return(true) allow(c).to receive(:app_metadata).and_return(app_metadata) end @@ -47,17 +48,17 @@ let(:candidate_servers) do spec.candidate_servers.collect do |server| features = double('features').tap do |feat| - allow(feat).to receive(:max_staleness_enabled?).and_return(server['maxWireVersion'] >= 5) + allow(feat).to receive(:max_staleness_enabled?).and_return(server['maxWireVersion'] && server['maxWireVersion'] >= 5) end address = Mongo::Address.new(server['address']) Mongo::Server.new(address, cluster, monitoring, listeners, options).tap do |s| - allow(s).to receive(:average_round_trip_time).and_return(server['avg_rtt_ms'] / 1000.0) + allow(s).to receive(:average_round_trip_time).and_return(server['avg_rtt_ms'] / 1000.0) if server['avg_rtt_ms'] allow(s).to receive(:tags).and_return(server['tags']) allow(s).to receive(:secondary?).and_return(server['type'] == 'RSSecondary') allow(s).to receive(:primary?).and_return(server['type'] == 'RSPrimary') allow(s).to receive(:connectable?).and_return(true) - allow(s).to receive(:last_write_date).and_return(server['lastWrite']['lastWriteDate']['$numberLong'].to_i * 1000) - allow(s).to receive(:last_scan).and_return(server['lastUpdateTime'] * 1000) + allow(s).to receive(:last_write_date).and_return(server['lastWrite']['lastWriteDate']['$numberLong'].to_i) if server['lastWrite'] + allow(s).to receive(:last_scan).and_return(server['lastUpdateTime']) allow(s).to receive(:features).and_return(features) end end @@ -65,7 +66,7 @@ let(:in_latency_window) do spec.in_latency_window.collect do |server| - Mongo::Server.new(Mongo::Address.new(server['address']), cluster, monitoring, listeners) + Mongo::Server.new(Mongo::Address.new(server['address']), cluster, monitoring, listeners, options) end end @@ -84,23 +85,36 @@ allow(cluster).to receive(:servers).and_return(candidate_servers) end - context 'Valid read preference and matching server available', unless: spec.invalid_max_staleness? do + context 'when the max staleness is invalid' do - it 'Finds all suitable servers in the latency window', if: spec.replica_set? do - expect(server_selector.send(:select, cluster.servers)).to match_array(in_latency_window) - end + it 'Raises an InvalidServerPreference exception', if: spec.invalid_max_staleness? do - it 'Finds the most suitable server in the latency window' do - expect(in_latency_window).to include(server_selector.select_server(cluster)) + expect do + server_selector.select_server(cluster) + end.to raise_exception(Mongo::Error::InvalidServerPreference) end end - context 'when the max staleness cannot be applied', if: spec.invalid_max_staleness? do + context 'when the max staleness is valid' do - it 'Raises exception' do - expect do - server_selector.select_server(cluster) - end.to raise_exception(Mongo::Error::InvalidServerPreference) + context 'when there are available servers' do + + it 'Finds all suitable servers in the latency window', if: (spec.replica_set? && !spec.invalid_max_staleness? && spec.server_available?) do + expect(server_selector.send(:select, cluster.servers)).to match_array(in_latency_window) + end + + it 'Finds the most suitable server in the latency window', if: (!spec.invalid_max_staleness? && spec.server_available?) do + expect(in_latency_window).to include(server_selector.select_server(cluster)) + end + end + + context 'when there are no available servers', if: (!spec.invalid_max_staleness? && !spec.server_available?) do + + it 'Raises a NoServerAvailable Exception' do + expect do + server_selector.select_server(cluster) + end.to raise_exception(Mongo::Error::NoServerAvailable) + end end end end diff --git a/spec/mongo/server_selection_spec.rb b/spec/mongo/server_selection_spec.rb index ab64da3bcd..50b4e55a28 100644 --- a/spec/mongo/server_selection_spec.rb +++ b/spec/mongo/server_selection_spec.rb @@ -28,6 +28,7 @@ allow(c).to receive(:single?).and_return(topology.single?) allow(c).to receive(:sharded?).and_return(topology.sharded?) allow(c).to receive(:replica_set?).and_return(topology.replica_set?) + allow(c).to receive(:unknown?).and_return(topology.unknown?) allow(c).to receive(:app_metadata).and_return(app_metadata) end end diff --git a/spec/mongo/server_selector/nearest_spec.rb b/spec/mongo/server_selector/nearest_spec.rb index 1336b5f158..e6fe31d17d 100644 --- a/spec/mongo/server_selector/nearest_spec.rb +++ b/spec/mongo/server_selector/nearest_spec.rb @@ -18,7 +18,7 @@ context 'when max_staleness is provided' do let(:options) do - { max_staleness: 60 } + { max_staleness: 95 } end it 'sets the max_staleness option' do @@ -32,7 +32,7 @@ context 'when max staleness is the same' do let(:options) do - { max_staleness: 60 } + { max_staleness: 95 } end let(:other) do @@ -47,7 +47,7 @@ context 'when max staleness is different' do let(:other_options) do - { max_staleness: 30 } + { max_staleness: 100 } end let(:other) do @@ -100,11 +100,11 @@ context 'max staleness provided' do let(:max_staleness) do - 60 + 100 end let(:expected) do - { :mode => 'nearest', maxStalenessMS: 60000 } + { :mode => 'nearest', maxStalenessSeconds: 100 } end it 'returns a read preference formatted for mongos' do diff --git a/spec/mongo/server_selector/primary_preferred_spec.rb b/spec/mongo/server_selector/primary_preferred_spec.rb index a7ed464d30..52070df3de 100644 --- a/spec/mongo/server_selector/primary_preferred_spec.rb +++ b/spec/mongo/server_selector/primary_preferred_spec.rb @@ -18,7 +18,7 @@ context 'when max_staleness is provided' do let(:options) do - { max_staleness: 60 } + { max_staleness: 95 } end it 'sets the max_staleness option' do @@ -32,7 +32,7 @@ context 'when max staleness is the same' do let(:options) do - { max_staleness: 60 } + { max_staleness: 95 } end let(:other) do @@ -47,7 +47,7 @@ context 'when max staleness is different' do let(:other_options) do - { max_staleness: 30 } + { max_staleness: 100 } end let(:other) do @@ -93,11 +93,11 @@ context 'max staleness provided' do let(:max_staleness) do - 60 + 100 end let(:expected) do - { :mode => 'primaryPreferred', maxStalenessMS: 60000 } + { :mode => 'primaryPreferred', maxStalenessSeconds: 100 } end it 'returns a read preference formatted for mongos' do diff --git a/spec/mongo/server_selector/primary_spec.rb b/spec/mongo/server_selector/primary_spec.rb index 61c62e9cbc..94cc0ae358 100644 --- a/spec/mongo/server_selector/primary_spec.rb +++ b/spec/mongo/server_selector/primary_spec.rb @@ -16,7 +16,7 @@ context 'when max_staleness is provided' do let(:options) do - { max_staleness: 60 } + { max_staleness: 100 } end it 'raises an exception' do @@ -66,7 +66,7 @@ context 'max staleness provided' do let(:max_staleness) do - 60 + 100 end it 'raises an error' do diff --git a/spec/mongo/server_selector/secondary_preferred_spec.rb b/spec/mongo/server_selector/secondary_preferred_spec.rb index 6fd1205d76..9faeaa95d3 100644 --- a/spec/mongo/server_selector/secondary_preferred_spec.rb +++ b/spec/mongo/server_selector/secondary_preferred_spec.rb @@ -18,7 +18,7 @@ context 'when max_staleness is provided' do let(:options) do - { max_staleness: 60 } + { max_staleness: 95 } end it 'sets the max_staleness option' do @@ -32,7 +32,7 @@ context 'when max staleness is the same' do let(:options) do - { max_staleness: 60 } + { max_staleness: 90 } end let(:other) do @@ -47,7 +47,7 @@ context 'when max staleness is different' do let(:other_options) do - { max_staleness: 30 } + { max_staleness: 100 } end let(:other) do @@ -100,7 +100,7 @@ end let(:expected) do - { :mode => 'secondaryPreferred', maxStalenessMS: 60000 } + { :mode => 'secondaryPreferred', maxStalenessSeconds: 60 } end it 'returns a read preference formatted for mongos' do diff --git a/spec/mongo/server_selector/secondary_spec.rb b/spec/mongo/server_selector/secondary_spec.rb index de59d283d6..fe457fee98 100644 --- a/spec/mongo/server_selector/secondary_spec.rb +++ b/spec/mongo/server_selector/secondary_spec.rb @@ -18,7 +18,7 @@ context 'when max_staleness is provided' do let(:options) do - { max_staleness: 60 } + { max_staleness: 100 } end it 'sets the max_staleness option' do @@ -32,7 +32,7 @@ context 'when max staleness is the same' do let(:options) do - { max_staleness: 60 } + { max_staleness: 90 } end let(:other) do @@ -47,7 +47,7 @@ context 'when max staleness is different' do let(:other_options) do - { max_staleness: 30 } + { max_staleness: 95 } end let(:other) do @@ -96,7 +96,7 @@ end let(:expected) do - { :mode => 'secondary', maxStalenessMS: 60000 } + { :mode => 'secondary', maxStalenessSeconds: 60 } end it 'returns a read preference formatted for mongos' do diff --git a/spec/mongo/server_selector_spec.rb b/spec/mongo/server_selector_spec.rb index 4b1ae086ac..711e467515 100644 --- a/spec/mongo/server_selector_spec.rb +++ b/spec/mongo/server_selector_spec.rb @@ -153,6 +153,7 @@ allow(c).to receive(:servers).and_return(servers) allow(c).to receive(:single?).and_return(false) allow(c).to receive(:sharded?).and_return(false) + allow(c).to receive(:unknown?).and_return(false) allow(c).to receive(:scan!).and_return(true) allow(c).to receive(:options).and_return(server_selection_timeout: 0.1) end @@ -182,6 +183,7 @@ allow(c).to receive(:servers).and_return(servers) allow(c).to receive(:single?).and_return(false) allow(c).to receive(:sharded?).and_return(false) + allow(c).to receive(:unknown?).and_return(false) allow(c).to receive(:scan!).and_return(true) allow(c).to receive(:options).and_return(server_selection_timeout: 0) end @@ -223,6 +225,7 @@ allow(c).to receive(:servers).and_return(servers) allow(c).to receive(:single?).and_return(false) allow(c).to receive(:sharded?).and_return(false) + allow(c).to receive(:unknown?).and_return(false) allow(c).to receive(:scan!).and_return(true) allow(c).to receive(:options).and_return(local_threshold: 0.050) end @@ -251,6 +254,7 @@ allow(c).to receive(:servers).and_return(servers) allow(c).to receive(:single?).and_return(single) allow(c).to receive(:sharded?).and_return(sharded) + allow(c).to receive(:unknown?).and_return(false) allow(c).to receive(:scan!).and_return(true) allow(c).to receive(:options).and_return(server_selection_timeout: 0.1) end diff --git a/spec/mongo/uri_spec.rb b/spec/mongo/uri_spec.rb index e8bd11367e..4c4ef0b77c 100644 --- a/spec/mongo/uri_spec.rb +++ b/spec/mongo/uri_spec.rb @@ -541,7 +541,7 @@ context 'read preference max staleness option provided' do let(:options) do - 'readPreference=Secondary&maxStalenessMS=120000' + 'readPreference=Secondary&maxStalenessSeconds=120' end let(:read) do @@ -561,7 +561,7 @@ context 'when max staleness is combined with read preference mode primary' do let(:options) do - 'readPreference=primary&maxStalenessMS=120000' + 'readPreference=primary&maxStalenessSeconds=120' end it 'raises an exception when read preference is accessed on the client' do @@ -571,10 +571,10 @@ end end - context 'when the max staleness value is not at least twice heartbeat frequency' do + context 'when the max staleness value is too small' do let(:options) do - 'readPreference=secondary&maxStalenessMS=1' + 'readPreference=secondary&maxStalenessSeconds=89' end it 'does not raise an exception until the read preference is used' do diff --git a/spec/support/max_staleness/ReplicaSetNoPrimary/Incompatible.yml b/spec/support/max_staleness/ReplicaSetNoPrimary/Incompatible.yml index e987a6d837..74032747ec 100644 --- a/spec/support/max_staleness/ReplicaSetNoPrimary/Incompatible.yml +++ b/spec/support/max_staleness/ReplicaSetNoPrimary/Incompatible.yml @@ -1,6 +1,6 @@ -# During server selection, -# clients (drivers or mongos) MUST raise an error if ``maxStalenessMS`` is not zero or null, -# and any server's ``maxWireVersion`` is less than 5 (`SERVER-23893`_). +# During server selection, clients (drivers or mongos) MUST raise an error if +# maxStalenessSeconds is defined and not -1 and any server's ``maxWireVersion`` +# is less than 5 (`SERVER-23893`_). --- topology_description: type: ReplicaSetNoPrimary @@ -21,5 +21,5 @@ topology_description: lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: Nearest - maxStalenessMS: 120000 + maxStalenessSeconds: 120 error: true diff --git a/spec/support/max_staleness/ReplicaSetNoPrimary/LastUpdateTime.yml b/spec/support/max_staleness/ReplicaSetNoPrimary/LastUpdateTime.yml index 17b922e67f..3acc594c71 100644 --- a/spec/support/max_staleness/ReplicaSetNoPrimary/LastUpdateTime.yml +++ b/spec/support/max_staleness/ReplicaSetNoPrimary/LastUpdateTime.yml @@ -7,14 +7,14 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 1 - lastWrite: {lastWriteDate: {$numberLong: "25002"}} + lastWrite: {lastWriteDate: {$numberLong: "125002"}} maxWireVersion: 5 - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 50 # Too far. lastUpdateTime: 25002 # Not used when there's no primary. - lastWrite: {lastWriteDate: {$numberLong: "2"}} # 25 sec stale + 25 sec heartbeat <= 50 sec maxStaleness. + lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. maxWireVersion: 5 - &3 address: c:27017 @@ -25,7 +25,7 @@ topology_description: maxWireVersion: 5 read_preference: mode: Nearest - maxStalenessMS: 50000 + maxStalenessSeconds: 150 suitable_servers: - *1 - *2 diff --git a/spec/support/max_staleness/ReplicaSetNoPrimary/Nearest.yml b/spec/support/max_staleness/ReplicaSetNoPrimary/Nearest.yml index 8474e07198..6f71c92b7d 100644 --- a/spec/support/max_staleness/ReplicaSetNoPrimary/Nearest.yml +++ b/spec/support/max_staleness/ReplicaSetNoPrimary/Nearest.yml @@ -7,14 +7,14 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - lastWrite: {lastWriteDate: {$numberLong: "25002"}} + lastWrite: {lastWriteDate: {$numberLong: "125002"}} maxWireVersion: 5 - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 50 # Too far. lastUpdateTime: 0 - lastWrite: {lastWriteDate: {$numberLong: "2"}} # 25 sec stale + 25 sec heartbeat <= 50 sec maxStaleness. + lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. maxWireVersion: 5 - &3 address: c:27017 @@ -25,7 +25,7 @@ topology_description: maxWireVersion: 5 read_preference: mode: Nearest - maxStalenessMS: 50000 + maxStalenessSeconds: 150 suitable_servers: - *1 - *2 diff --git a/spec/support/max_staleness/ReplicaSetNoPrimary/Nearest2.yml b/spec/support/max_staleness/ReplicaSetNoPrimary/Nearest2.yml index 32aaa70fb2..ecf74e7e43 100644 --- a/spec/support/max_staleness/ReplicaSetNoPrimary/Nearest2.yml +++ b/spec/support/max_staleness/ReplicaSetNoPrimary/Nearest2.yml @@ -7,14 +7,14 @@ topology_description: type: RSSecondary avg_rtt_ms: 50 # Too far. lastUpdateTime: 0 - lastWrite: {lastWriteDate: {$numberLong: "25002"}} + lastWrite: {lastWriteDate: {$numberLong: "125002"}} maxWireVersion: 5 - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - lastWrite: {lastWriteDate: {$numberLong: "2"}} # 25 sec stale + 25 sec heartbeat <= 50 sec maxStaleness. + lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. maxWireVersion: 5 - &3 address: c:27017 @@ -25,7 +25,7 @@ topology_description: maxWireVersion: 5 read_preference: mode: Nearest - maxStalenessMS: 50000 + maxStalenessSeconds: 150 suitable_servers: - *1 - *2 diff --git a/spec/support/max_staleness/ReplicaSetNoPrimary/NoKnownServers.yml b/spec/support/max_staleness/ReplicaSetNoPrimary/NoKnownServers.yml new file mode 100644 index 0000000000..575c367b83 --- /dev/null +++ b/spec/support/max_staleness/ReplicaSetNoPrimary/NoKnownServers.yml @@ -0,0 +1,15 @@ +# maxStalenessSeconds must be at least 90 seconds, even with no known servers. +--- +topology_description: + type: ReplicaSetNoPrimary + servers: + - &1 + address: a:27017 + type: Unknown + - &2 + address: b:27017 + type: Unknown +read_preference: + mode: Nearest + maxStalenessSeconds: 1 # Too small. +error: true diff --git a/spec/support/max_staleness/ReplicaSetNoPrimary/PrimaryPreferred.yml b/spec/support/max_staleness/ReplicaSetNoPrimary/PrimaryPreferred.yml index b562adf00d..6260eb85c0 100644 --- a/spec/support/max_staleness/ReplicaSetNoPrimary/PrimaryPreferred.yml +++ b/spec/support/max_staleness/ReplicaSetNoPrimary/PrimaryPreferred.yml @@ -20,7 +20,7 @@ topology_description: lastWrite: {lastWriteDate: {$numberLong: "1"}} # Very stale. read_preference: mode: PrimaryPreferred - maxStalenessMS: 50000 + maxStalenessSeconds: 90 suitable_servers: - *1 in_latency_window: diff --git a/spec/support/max_staleness/ReplicaSetNoPrimary/PrimaryPreferred_tags.yml b/spec/support/max_staleness/ReplicaSetNoPrimary/PrimaryPreferred_tags.yml index 6aea50ab8a..19c297abe8 100644 --- a/spec/support/max_staleness/ReplicaSetNoPrimary/PrimaryPreferred_tags.yml +++ b/spec/support/max_staleness/ReplicaSetNoPrimary/PrimaryPreferred_tags.yml @@ -1,4 +1,4 @@ -# maxStalenessMS is applied before tag sets. With tag sets +# maxStalenessSeconds is applied before tag sets. With tag sets # [{data_center: nyc}, {data_center: tokyo}], if the only node in NYC is stale # then use Tokyo. --- @@ -11,7 +11,7 @@ topology_description: type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - lastWrite: {lastWriteDate: {$numberLong: "25002"}} + lastWrite: {lastWriteDate: {$numberLong: "125002"}} maxWireVersion: 5 tags: data_center: tokyo # Matches second tag set. @@ -26,7 +26,7 @@ topology_description: data_center: nyc read_preference: mode: PrimaryPreferred - maxStalenessMS: 50000 + maxStalenessSeconds: 150 tag_sets: - data_center: nyc - data_center: tokyo diff --git a/spec/support/max_staleness/ReplicaSetNoPrimary/Secondary.yml b/spec/support/max_staleness/ReplicaSetNoPrimary/Secondary.yml index ebac70a91e..3d6832ff42 100644 --- a/spec/support/max_staleness/ReplicaSetNoPrimary/Secondary.yml +++ b/spec/support/max_staleness/ReplicaSetNoPrimary/Secondary.yml @@ -10,7 +10,7 @@ topology_description: avg_rtt_ms: 5 lastUpdateTime: 0 maxWireVersion: 5 - lastWrite: {lastWriteDate: {$numberLong: "25002"}} + lastWrite: {lastWriteDate: {$numberLong: "125002"}} tags: data_center: tokyo # No match, but its lastWriteDate is used in estimate. - &2 @@ -19,7 +19,7 @@ topology_description: avg_rtt_ms: 5 lastUpdateTime: 0 maxWireVersion: 5 - lastWrite: {lastWriteDate: {$numberLong: "2"}} # 25 sec stale + 25 sec heartbeat <= 50 sec maxStaleness. + lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. tags: data_center: nyc - &3 @@ -42,7 +42,7 @@ topology_description: data_center: tokyo # No match. read_preference: mode: Secondary - maxStalenessMS: 50000 + maxStalenessSeconds: 150 tag_sets: - data_center: nyc suitable_servers: diff --git a/spec/support/max_staleness/ReplicaSetNoPrimary/SecondaryPreferred.yml b/spec/support/max_staleness/ReplicaSetNoPrimary/SecondaryPreferred.yml index 6189bf32a3..4c6a49d0cb 100644 --- a/spec/support/max_staleness/ReplicaSetNoPrimary/SecondaryPreferred.yml +++ b/spec/support/max_staleness/ReplicaSetNoPrimary/SecondaryPreferred.yml @@ -19,7 +19,7 @@ topology_description: lastWrite: {lastWriteDate: {$numberLong: "1"}} # Very stale. read_preference: mode: SecondaryPreferred - maxStalenessMS: 120000 + maxStalenessSeconds: 120 suitable_servers: - *1 in_latency_window: diff --git a/spec/support/max_staleness/ReplicaSetNoPrimary/SecondaryPreferred_tags.yml b/spec/support/max_staleness/ReplicaSetNoPrimary/SecondaryPreferred_tags.yml index 427a8bb275..f0e26742f2 100644 --- a/spec/support/max_staleness/ReplicaSetNoPrimary/SecondaryPreferred_tags.yml +++ b/spec/support/max_staleness/ReplicaSetNoPrimary/SecondaryPreferred_tags.yml @@ -10,7 +10,7 @@ topology_description: avg_rtt_ms: 5 lastUpdateTime: 0 maxWireVersion: 5 - lastWrite: {lastWriteDate: {$numberLong: "25002"}} + lastWrite: {lastWriteDate: {$numberLong: "125002"}} tags: data_center: tokyo # No match, but its lastWriteDate is used in estimate. - &2 @@ -19,7 +19,7 @@ topology_description: avg_rtt_ms: 5 lastUpdateTime: 0 maxWireVersion: 5 - lastWrite: {lastWriteDate: {$numberLong: "2"}} # 25 sec stale + 25 sec heartbeat <= 50 sec maxStaleness. + lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. tags: data_center: nyc - &3 @@ -42,7 +42,7 @@ topology_description: data_center: tokyo # No match. read_preference: mode: SecondaryPreferred - maxStalenessMS: 50000 + maxStalenessSeconds: 150 tag_sets: - data_center: nyc suitable_servers: diff --git a/spec/support/max_staleness/ReplicaSetNoPrimary/ZeroMaxStaleness.yml b/spec/support/max_staleness/ReplicaSetNoPrimary/ZeroMaxStaleness.yml new file mode 100644 index 0000000000..59e9536daf --- /dev/null +++ b/spec/support/max_staleness/ReplicaSetNoPrimary/ZeroMaxStaleness.yml @@ -0,0 +1,23 @@ +# maxStalenessSeconds=0 is prohibited. +--- +topology_description: + type: ReplicaSetNoPrimary + servers: + - &1 + address: a:27017 + type: RSSecondary + avg_rtt_ms: 5 + lastUpdateTime: 0 + maxWireVersion: 5 + lastWrite: {lastWriteDate: {$numberLong: "2"}} + - &2 + address: b:27017 + type: RSSecondary + avg_rtt_ms: 5 + lastUpdateTime: 0 + maxWireVersion: 4 # Incompatible. + lastWrite: {lastWriteDate: {$numberLong: "1"}} +read_preference: + mode: Nearest + maxStalenessSeconds: 0 +error: true diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/Incompatible.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/Incompatible.yml index 783181133e..9265526895 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/Incompatible.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/Incompatible.yml @@ -1,6 +1,6 @@ -# During server selection, -# clients (drivers or mongos) MUST raise an error if ``maxStalenessMS`` is not zero or null, -# and any server's ``maxWireVersion`` is less than 5 (`SERVER-23893`_). +# During server selection, clients (drivers or mongos) MUST raise an error if +# maxStalenessSeconds is defined and not -1 and any server's ``maxWireVersion`` +# is less than 5 (`SERVER-23893`_). --- topology_description: type: ReplicaSetWithPrimary @@ -21,5 +21,5 @@ topology_description: lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: Nearest - maxStalenessMS: 120000 + maxStalenessSeconds: 120 error: true diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/LastUpdateTime.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/LastUpdateTime.yml index 4e6bb6411f..129310744f 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/LastUpdateTime.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/LastUpdateTime.yml @@ -13,21 +13,21 @@ topology_description: address: b:27017 type: RSSecondary avg_rtt_ms: 5 - lastUpdateTime: 25001 - # Updated 25 sec after primary, so 25 sec stale. - # 25 sec stale + 25 sec heartbeat <= 50 sec maxStaleness. + lastUpdateTime: 125001 + # Updated 125 sec after primary, so 125 sec stale. + # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. lastWrite: {lastWriteDate: {$numberLong: "2"}} maxWireVersion: 5 - &3 address: c:27017 type: RSSecondary avg_rtt_ms: 5 - lastUpdateTime: 25001 + lastUpdateTime: 125001 lastWrite: {lastWriteDate: {$numberLong: "1"}} # Too stale. maxWireVersion: 5 read_preference: mode: Nearest - maxStalenessMS: 50000 + maxStalenessSeconds: 150 suitable_servers: - *1 - *2 diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/ShortHeartbeartShortMaxStaleness2.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/LongHeartbeat.yml similarity index 67% rename from spec/support/max_staleness/ReplicaSetWithPrimary/ShortHeartbeartShortMaxStaleness2.yml rename to spec/support/max_staleness/ReplicaSetWithPrimary/LongHeartbeat.yml index 4a803ef22f..455696492d 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/ShortHeartbeartShortMaxStaleness2.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/LongHeartbeat.yml @@ -1,7 +1,7 @@ -# Users can configure a shorter ``heartbeatFrequencyMS`` than the default to -# allow a smaller ``maxStalenessMS`` with replica sets. +# If users configure a longer ``heartbeatFrequencyMS`` than the default, +# ``maxStalenessSeconds`` might have a larger minimum. --- -heartbeatFrequencyMS: 1000 # 1 second. +heartbeatFrequencyMS: 120000 # 120 seconds. topology_description: type: ReplicaSetWithPrimary servers: @@ -21,7 +21,7 @@ topology_description: lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: Nearest - maxStalenessMS: 2000 # 2 seconds, permitted. + maxStalenessSeconds: 130 # OK, must be 120 + 10 = 130 seconds. suitable_servers: - *1 - *2 diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/ShortHeartbeartShortMaxStaleness.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/LongHeartbeat2.yml similarity index 57% rename from spec/support/max_staleness/ReplicaSetWithPrimary/ShortHeartbeartShortMaxStaleness.yml rename to spec/support/max_staleness/ReplicaSetWithPrimary/LongHeartbeat2.yml index a49d75873c..3b6b5adc50 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/ShortHeartbeartShortMaxStaleness.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/LongHeartbeat2.yml @@ -1,14 +1,14 @@ -# Users can configure a shorter ``heartbeatFrequencyMS`` than the default to -# allow a smaller ``maxStalenessMS`` with replica sets. +# If users configure a longer ``heartbeatFrequencyMS`` than the default, +# ``maxStalenessSeconds`` might have a larger minimum. --- -heartbeatFrequencyMS: 1000 # 1 second. +heartbeatFrequencyMS: 120000 # 120 seconds. topology_description: type: ReplicaSetWithPrimary servers: - &1 address: a:27017 type: RSPrimary - avg_rtt_ms: 50 # Too far. + avg_rtt_ms: 5 lastUpdateTime: 0 maxWireVersion: 5 lastWrite: {lastWriteDate: {$numberLong: "1"}} @@ -21,9 +21,5 @@ topology_description: lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: Nearest - maxStalenessMS: 2000 # 2 seconds, permitted. -suitable_servers: -- *1 -- *2 -in_latency_window: -- *2 + maxStalenessSeconds: 129 # Too small, must be 120 + 10 = 130 seconds. +error: true diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/MaxStalenessTooSmall.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/MaxStalenessTooSmall.yml index addd0f8a09..2b5126d4f6 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/MaxStalenessTooSmall.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/MaxStalenessTooSmall.yml @@ -1,7 +1,8 @@ # A driver MUST raise an error # if the TopologyType is ReplicaSetWithPrimary or ReplicaSetNoPrimary -# and ``maxStalenessMS`` is less than twice the client's `heartbeatFrequencyMS`. +# and ``maxStalenessSeconds`` is less than 90. --- +heartbeatFrequencyMS: 500 topology_description: type: ReplicaSetWithPrimary servers: @@ -21,5 +22,5 @@ topology_description: lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: Nearest - maxStalenessMS: 1 # Too small. + maxStalenessSeconds: 89 # Too small. error: true diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml index 4bd0a158a8..1872d3b365 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml @@ -1,4 +1,4 @@ -# Drivers MUST raise an error if ``maxStalenessMS`` is not zero or null +# Drivers MUST raise an error if maxStalenessSeconds is defined and not -1 # and the ``mode`` field is 'primary'. --- topology_description: @@ -19,5 +19,5 @@ topology_description: maxWireVersion: 5 lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: - maxStalenessMS: 120000 + maxStalenessSeconds: 120 error: true diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/Nearest.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/Nearest.yml index b2516efedb..76915f22b5 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/Nearest.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/Nearest.yml @@ -7,14 +7,14 @@ topology_description: type: RSPrimary avg_rtt_ms: 5 lastUpdateTime: 0 - lastWrite: {lastWriteDate: {$numberLong: "25002"}} + lastWrite: {lastWriteDate: {$numberLong: "125002"}} maxWireVersion: 5 - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 50 # Too far. lastUpdateTime: 0 - lastWrite: {lastWriteDate: {$numberLong: "2"}} # 25 sec stale + 25 sec heartbeat <= 50 sec maxStaleness. + lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. maxWireVersion: 5 - &3 address: c:27017 @@ -25,7 +25,7 @@ topology_description: maxWireVersion: 5 read_preference: mode: Nearest - maxStalenessMS: 50000 + maxStalenessSeconds: 150 suitable_servers: - *1 - *2 diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/Nearest2.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/Nearest2.yml index 7e1ac10092..3b249d8f3b 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/Nearest2.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/Nearest2.yml @@ -7,14 +7,14 @@ topology_description: type: RSPrimary avg_rtt_ms: 50 # Too far. lastUpdateTime: 0 - lastWrite: {lastWriteDate: {$numberLong: "25002"}} + lastWrite: {lastWriteDate: {$numberLong: "125002"}} maxWireVersion: 5 - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - lastWrite: {lastWriteDate: {$numberLong: "2"}} # 25 sec stale + 25 sec heartbeat <= 50 sec maxStaleness. + lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. maxWireVersion: 5 - &3 address: c:27017 @@ -25,7 +25,7 @@ topology_description: maxWireVersion: 5 read_preference: mode: Nearest - maxStalenessMS: 50000 + maxStalenessSeconds: 150 suitable_servers: - *1 - *2 diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/Nearest_tags.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/Nearest_tags.yml index f44505131f..0c2b6e3ea2 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/Nearest_tags.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/Nearest_tags.yml @@ -1,4 +1,4 @@ -# maxStalenessMS is applied before tag sets. With tag sets +# maxStalenessSeconds is applied before tag sets. With tag sets # [{data_center: nyc}, {data_center: tokyo}], if the only node in NYC is stale # then use Tokyo. --- @@ -11,7 +11,7 @@ topology_description: type: RSPrimary avg_rtt_ms: 5 lastUpdateTime: 0 - lastWrite: {lastWriteDate: {$numberLong: "25002"}} + lastWrite: {lastWriteDate: {$numberLong: "125002"}} maxWireVersion: 5 tags: data_center: tokyo @@ -26,7 +26,7 @@ topology_description: data_center: nyc read_preference: mode: Nearest - maxStalenessMS: 50000 + maxStalenessSeconds: 150 tag_sets: - data_center: nyc - data_center: tokyo diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/PrimaryPreferred.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/PrimaryPreferred.yml index 5717109937..8caa31ed8f 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/PrimaryPreferred.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/PrimaryPreferred.yml @@ -1,4 +1,4 @@ -# Ignore maxStalenessMS is primary is available. +# Ignore maxStalenessSeconds if primary is available. --- heartbeatFrequencyMS: 25000 # 25 seconds. topology_description: @@ -20,7 +20,7 @@ topology_description: lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: PrimaryPreferred - maxStalenessMS: 50000 + maxStalenessSeconds: 150 suitable_servers: - *1 in_latency_window: diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/PrimaryPreferred_incompatible.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/PrimaryPreferred_incompatible.yml index 554a9d1021..677a52ce9a 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/PrimaryPreferred_incompatible.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/PrimaryPreferred_incompatible.yml @@ -1,6 +1,6 @@ # Primary has wire version 5, secondary has 4, read preference primaryPreferred -# with maxStalenessMS. The client must error, even though it uses primary and -# never applies maxStalenessMS. Proves that the compatibility check precedes +# with maxStalenessSeconds. The client must error, even though it uses primary and +# never applies maxStalenessSeconds. Proves that the compatibility check precedes # filtration. --- topology_description: @@ -22,6 +22,6 @@ topology_description: lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: PrimaryPreferred - maxStalenessMS: 50000 + maxStalenessSeconds: 150 error: true diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred.yml index b56e4ac2e0..ac61c3a994 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred.yml @@ -19,7 +19,7 @@ topology_description: lastWrite: {lastWriteDate: {$numberLong: "1"}} # Very stale. read_preference: mode: SecondaryPreferred - maxStalenessMS: 120000 + maxStalenessSeconds: 120 suitable_servers: - *1 in_latency_window: diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags.yml index 38cf94ecba..c32e8c41af 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags.yml @@ -10,14 +10,14 @@ topology_description: avg_rtt_ms: 5 lastUpdateTime: 0 maxWireVersion: 5 - lastWrite: {lastWriteDate: {$numberLong: "25002"}} + lastWrite: {lastWriteDate: {$numberLong: "125002"}} - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 maxWireVersion: 5 - lastWrite: {lastWriteDate: {$numberLong: "2"}} # 25 sec stale + 25 sec heartbeat <= 50 sec maxStaleness. + lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. tags: data_center: nyc - &3 @@ -49,7 +49,7 @@ topology_description: data_center: tokyo # No match. read_preference: mode: SecondaryPreferred - maxStalenessMS: 50000 + maxStalenessSeconds: 150 tag_sets: - data_center: nyc suitable_servers: diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags2.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags2.yml index 1c61a030f1..eea732a8ce 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags2.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags2.yml @@ -1,4 +1,4 @@ -# maxStalenessMS is applied before tag sets. With tag sets +# maxStalenessSeconds is applied before tag sets. With tag sets # [{data_center: nyc}, {data_center: tokyo}], if the only secondary in NYC is # stale then use Tokyo. --- @@ -11,7 +11,7 @@ topology_description: type: RSPrimary avg_rtt_ms: 5 lastUpdateTime: 0 - lastWrite: {lastWriteDate: {$numberLong: "25002"}} + lastWrite: {lastWriteDate: {$numberLong: "125002"}} maxWireVersion: 5 - &2 address: b:27017 @@ -33,7 +33,7 @@ topology_description: data_center: nyc read_preference: mode: SecondaryPreferred - maxStalenessMS: 50000 + maxStalenessSeconds: 150 tag_sets: - data_center: nyc - data_center: tokyo diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/Secondary_tags.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/Secondary_tags.yml index 27aa4e1757..3f529477a8 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/Secondary_tags.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/Secondary_tags.yml @@ -10,14 +10,14 @@ topology_description: avg_rtt_ms: 5 lastUpdateTime: 0 maxWireVersion: 5 - lastWrite: {lastWriteDate: {$numberLong: "25002"}} + lastWrite: {lastWriteDate: {$numberLong: "125002"}} - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 maxWireVersion: 5 - lastWrite: {lastWriteDate: {$numberLong: "2"}} # 25 sec stale + 25 sec heartbeat <= 50 sec maxStaleness. + lastWrite: {lastWriteDate: {$numberLong: "2"}} # 125 sec stale + 25 sec heartbeat <= 150 sec maxStaleness. tags: data_center: nyc - &3 @@ -49,7 +49,7 @@ topology_description: data_center: tokyo # No match. read_preference: mode: Secondary - maxStalenessMS: 50000 + maxStalenessSeconds: 150 tag_sets: - data_center: nyc suitable_servers: diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/Secondary_tags2.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/Secondary_tags2.yml index 9d46e85769..e15450b58e 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/Secondary_tags2.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/Secondary_tags2.yml @@ -1,4 +1,4 @@ -# maxStalenessMS is applied before tag sets. With tag sets +# maxStalenessSeconds is applied before tag sets. With tag sets # [{data_center: nyc}, {data_center: tokyo}], if the only secondary in NYC is # stale then use Tokyo. --- @@ -11,7 +11,7 @@ topology_description: type: RSPrimary avg_rtt_ms: 5 lastUpdateTime: 0 - lastWrite: {lastWriteDate: {$numberLong: "25002"}} + lastWrite: {lastWriteDate: {$numberLong: "125002"}} maxWireVersion: 5 - &2 address: b:27017 @@ -33,7 +33,7 @@ topology_description: data_center: nyc read_preference: mode: Secondary - maxStalenessMS: 50000 + maxStalenessSeconds: 150 tag_sets: - data_center: nyc - data_center: tokyo diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/ZeroMaxStaleness.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/ZeroMaxStaleness.yml index 2a9634f253..99d3a66f35 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/ZeroMaxStaleness.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/ZeroMaxStaleness.yml @@ -1,4 +1,4 @@ -# A ``maxStalenessMS`` of zero or null MUST mean "no maximum". +# maxStalenessSeconds=0 is prohibited. --- topology_description: type: ReplicaSetWithPrimary @@ -6,22 +6,18 @@ topology_description: - &1 address: a:27017 type: RSPrimary - avg_rtt_ms: 50 # Too far. + avg_rtt_ms: 5 lastUpdateTime: 0 maxWireVersion: 5 - lastWrite: {lastWriteDate: {$numberLong: "1000001"}} + lastWrite: {lastWriteDate: {$numberLong: "2"}} - &2 address: b:27017 type: RSSecondary avg_rtt_ms: 5 lastUpdateTime: 0 - maxWireVersion: 5 - lastWrite: {lastWriteDate: {$numberLong: "1"}} # Very stale. + maxWireVersion: 4 # Incompatible. + lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: Nearest - maxStalenessMS: 0 # Means "no max staleness". -suitable_servers: # Very stale server is fine. -- *1 -- *2 -in_latency_window: -- *2 + maxStalenessSeconds: 0 +error: true diff --git a/spec/support/max_staleness/Sharded/Incompatible.yml b/spec/support/max_staleness/Sharded/Incompatible.yml index 6188c1ebca..7658136271 100644 --- a/spec/support/max_staleness/Sharded/Incompatible.yml +++ b/spec/support/max_staleness/Sharded/Incompatible.yml @@ -1,6 +1,6 @@ -# During server selection, -# clients (drivers or mongos) MUST raise an error if ``maxStalenessMS`` is not zero or null, -# and any server's ``maxWireVersion`` is less than 5 (`SERVER-23893`_). +# During server selection, clients (drivers or mongos) MUST raise an error if +# maxStalenessSeconds is defined and not -1 and any server's ``maxWireVersion`` +# is less than 5 (`SERVER-23893`_). --- topology_description: type: Sharded @@ -21,5 +21,5 @@ topology_description: lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: Nearest - maxStalenessMS: 120000 + maxStalenessSeconds: 120 error: true diff --git a/spec/support/max_staleness/Sharded/SmallMaxStaleness.yml b/spec/support/max_staleness/Sharded/SmallMaxStaleness.yml index ee8c2b139a..2a3d8e490d 100644 --- a/spec/support/max_staleness/Sharded/SmallMaxStaleness.yml +++ b/spec/support/max_staleness/Sharded/SmallMaxStaleness.yml @@ -1,4 +1,4 @@ -# Driver doesn't validate maxStalenessMS for mongos +# Driver doesn't validate maxStalenessSeconds for mongos --- heartbeatFrequencyMS: 10000 topology_description: @@ -11,10 +11,18 @@ topology_description: lastUpdateTime: 0 maxWireVersion: 5 lastWrite: {lastWriteDate: {$numberLong: "1"}} + - &2 + address: b:27017 + type: Mongos + avg_rtt_ms: 50 # Too far. + lastUpdateTime: 0 + maxWireVersion: 5 + lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: Nearest - maxStalenessMS: 1 # OK for sharding. + maxStalenessSeconds: 1 # OK for sharding. suitable_servers: - *1 +- *2 in_latency_window: - *1 diff --git a/spec/support/max_staleness/Single/Incompatible.yml b/spec/support/max_staleness/Single/Incompatible.yml index a6b79b3a14..d85d9714d9 100644 --- a/spec/support/max_staleness/Single/Incompatible.yml +++ b/spec/support/max_staleness/Single/Incompatible.yml @@ -1,6 +1,6 @@ -# During server selection, -# clients (drivers or mongos) MUST raise an error if ``maxStalenessMS`` is not zero or null, -# and any server's ``maxWireVersion`` is less than 5 (`SERVER-23893`_). +# During server selection, clients (drivers or mongos) MUST raise an error if +# maxStalenessSeconds is defined and not -1 and any server's ``maxWireVersion`` +# is less than 5 (`SERVER-23893`_). --- topology_description: type: Single @@ -14,5 +14,5 @@ topology_description: lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: Nearest - maxStalenessMS: 120000 + maxStalenessSeconds: 120 error: true diff --git a/spec/support/max_staleness/Single/SmallMaxStaleness.yml b/spec/support/max_staleness/Single/SmallMaxStaleness.yml index 74a7ba25f3..c4285092b2 100644 --- a/spec/support/max_staleness/Single/SmallMaxStaleness.yml +++ b/spec/support/max_staleness/Single/SmallMaxStaleness.yml @@ -1,4 +1,4 @@ -# Driver doesn't validate maxStalenessMS for direct connection. +# Driver doesn't validate maxStalenessSeconds for direct connection. --- heartbeatFrequencyMS: 10000 topology_description: @@ -13,7 +13,7 @@ topology_description: lastWrite: {lastWriteDate: {$numberLong: "1"}} read_preference: mode: Nearest - maxStalenessMS: 1 + maxStalenessSeconds: 1 suitable_servers: - *1 in_latency_window: diff --git a/spec/support/max_staleness/Unknown/SmallMaxStaleness.yml b/spec/support/max_staleness/Unknown/SmallMaxStaleness.yml new file mode 100644 index 0000000000..7daaa1fb4a --- /dev/null +++ b/spec/support/max_staleness/Unknown/SmallMaxStaleness.yml @@ -0,0 +1,14 @@ +# Driver doesn't validate maxStalenessSeconds while TopologyType is Unknown. +--- +heartbeatFrequencyMS: 10000 +topology_description: + type: Unknown + servers: + - &1 + address: a:27017 + type: Unknown +read_preference: + mode: Nearest + maxStalenessSeconds: 1 +suitable_servers: [] +in_latency_window: [] diff --git a/spec/support/server_selection.rb b/spec/support/server_selection.rb index 4a16ee6c70..b078f68a1c 100644 --- a/spec/support/server_selection.rb +++ b/spec/support/server_selection.rb @@ -49,11 +49,6 @@ class Spec # @since 2.4.0 attr_reader :max_staleness - # @return [ Array ] candidate_servers The candidate servers. - # - # @since 2.0.0 - attr_reader :candidate_servers - # @return [ Array ] eligible_servers The eligible servers before the latency # window is taken into account. # @@ -84,13 +79,13 @@ def initialize(file) @test = YAML.load(ERB.new(file.read).result) file.close @description = "#{@test['topology_description']['type']}: #{File.basename(file)}" - @heartbeat_frequency = @test['heartbeatFrequencyMS'] + @heartbeat_frequency = @test['heartbeatFrequencyMS'] / 1000 if @test['heartbeatFrequencyMS'] @read_preference = @test['read_preference'] @read_preference['mode'] = READ_PREFERENCES[@read_preference['mode']] - @max_staleness = @read_preference['maxStalenessMS'] + @max_staleness = @read_preference['maxStalenessSeconds'] @candidate_servers = @test['topology_description']['servers'] - @suitable_servers = @test['suitable_servers'] - @in_latency_window = @test['in_latency_window'] + @suitable_servers = @test['suitable_servers'] || [] + @in_latency_window = @test['in_latency_window'] || [] @type = TOPOLOGY_TYPES[@test['topology_description']['type']] end @@ -127,8 +122,7 @@ def server_available? # # @since 2.4.0 def invalid_max_staleness? - @test['error'] || - candidate_servers.any? { |server| server['maxWireVersion'] < 5 } + @test['error'] end # The subset of suitable servers that falls within the allowable latency @@ -149,6 +143,15 @@ def in_latency_window @in_latency_window end + # The servers a topology would return as candidates for selection. + # + # @return [ Array ] candidate_servers The candidate servers. + # + # @since 2.0.0 + def candidate_servers + @candidate_servers.select { |s| s['type'] != 'Unknown' } + end + private def primary