From 5544505ba2776a7dceb4d1cd1c85a3cd409377b2 Mon Sep 17 00:00:00 2001 From: Emily Date: Tue, 22 Nov 2016 13:35:37 +0100 Subject: [PATCH 1/9] RUBY-1174 RUBY-1178 Update Max Staleness implementation --- lib/mongo/cluster.rb | 5 ++++ lib/mongo/error/invalid_server_preference.rb | 2 +- lib/mongo/server_selector.rb | 5 ++++ lib/mongo/server_selector/nearest.rb | 2 +- .../server_selector/primary_preferred.rb | 2 +- lib/mongo/server_selector/secondary.rb | 2 +- .../server_selector/secondary_preferred.rb | 2 +- lib/mongo/server_selector/selectable.rb | 15 ++++++----- lib/mongo/uri.rb | 2 +- spec/mongo/max_staleness_spec.rb | 18 ++++++------- spec/mongo/server_selector/nearest_spec.rb | 10 +++---- .../server_selector/primary_preferred_spec.rb | 10 +++---- spec/mongo/server_selector/primary_spec.rb | 4 +-- .../secondary_preferred_spec.rb | 8 +++--- spec/mongo/server_selector/secondary_spec.rb | 8 +++--- spec/mongo/uri_spec.rb | 8 +++--- .../ReplicaSetNoPrimary/Incompatible.yml | 4 +-- .../ReplicaSetNoPrimary/LastUpdateTime.yml | 6 ++--- .../ReplicaSetNoPrimary/Nearest.yml | 6 ++--- .../ReplicaSetNoPrimary/Nearest2.yml | 6 ++--- .../ReplicaSetNoPrimary/NoKnownServers.yml | 16 +++++++++++ .../ReplicaSetNoPrimary/PrimaryPreferred.yml | 2 +- .../PrimaryPreferred_tags.yml | 6 ++--- .../ReplicaSetNoPrimary/Secondary.yml | 6 ++--- .../SecondaryPreferred.yml | 2 +- .../SecondaryPreferred_tags.yml | 6 ++--- .../ReplicaSetWithPrimary/Incompatible.yml | 4 +-- .../ReplicaSetWithPrimary/LastUpdateTime.yml | 10 +++---- ...ortMaxStaleness2.yml => LongHeartbeat.yml} | 8 +++--- ...ortMaxStaleness.yml => LongHeartbeat2.yml} | 16 +++++------ .../MaxStalenessTooSmall.yml | 5 ++-- .../MaxStalenessWithModePrimary.yml | 4 +-- .../ReplicaSetWithPrimary/Nearest.yml | 6 ++--- .../ReplicaSetWithPrimary/Nearest2.yml | 6 ++--- .../ReplicaSetWithPrimary/Nearest_tags.yml | 6 ++--- .../PrimaryPreferred.yml | 4 +-- .../PrimaryPreferred_incompatible.yml | 6 ++--- .../SecondaryPreferred.yml | 2 +- .../SecondaryPreferred_tags.yml | 6 ++--- .../SecondaryPreferred_tags2.yml | 6 ++--- .../ReplicaSetWithPrimary/Secondary_tags.yml | 6 ++--- .../ReplicaSetWithPrimary/Secondary_tags2.yml | 6 ++--- .../ZeroMaxStaleness.yml | 27 ------------------- .../max_staleness/Sharded/Incompatible.yml | 4 +-- .../Sharded/SmallMaxStaleness.yml | 12 +++++++-- .../max_staleness/Single/Incompatible.yml | 4 +-- .../Single/SmallMaxStaleness.yml | 4 +-- .../Unknown/SmallMaxStaleness.yml | 14 ++++++++++ spec/support/server_selection.rb | 6 ++--- 49 files changed, 178 insertions(+), 157 deletions(-) create mode 100644 spec/support/max_staleness/ReplicaSetNoPrimary/NoKnownServers.yml rename spec/support/max_staleness/ReplicaSetWithPrimary/{ShortHeartbeartShortMaxStaleness2.yml => LongHeartbeat.yml} (67%) rename spec/support/max_staleness/ReplicaSetWithPrimary/{ShortHeartbeartShortMaxStaleness.yml => LongHeartbeat2.yml} (57%) delete mode 100644 spec/support/max_staleness/ReplicaSetWithPrimary/ZeroMaxStaleness.yml create mode 100644 spec/support/max_staleness/Unknown/SmallMaxStaleness.yml 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..2b284fb2d2 100644 --- a/lib/mongo/error/invalid_server_preference.rb +++ b/lib/mongo/error/invalid_server_preference.rb @@ -33,7 +33,7 @@ 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.".freeze # Error message when max staleness cannot be used because one or more servers has version < 3.4. # 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..36f90536fa 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 @@ -258,10 +258,13 @@ 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 = cluster.options[:heartbeat_frequency] || Server::Monitor::HEARTBEAT_FREQUENCY + unless @max_staleness > 0 && + @max_staleness >= [ SMALLEST_MAX_STALENESS_SECONDS, + (heartbeat_frequency + 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..c76948358a 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,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(:options).and_return(options.merge(server_selection_timeout: 0.2)) + 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 +47,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 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/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..a330069afa 100644 --- a/spec/support/max_staleness/ReplicaSetNoPrimary/Incompatible.yml +++ b/spec/support/max_staleness/ReplicaSetNoPrimary/Incompatible.yml @@ -1,5 +1,5 @@ # During server selection, -# clients (drivers or mongos) MUST raise an error if ``maxStalenessMS`` is not zero or null, +# clients (drivers or mongos) MUST raise an error if ``maxStalenessSeconds`` is not zero or null, # and any server's ``maxWireVersion`` is less than 5 (`SERVER-23893`_). --- topology_description: @@ -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..a97b2eef87 --- /dev/null +++ b/spec/support/max_staleness/ReplicaSetNoPrimary/NoKnownServers.yml @@ -0,0 +1,16 @@ +# A too-short maxStalenessSeconds isn't enforced until there are 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 +suitable_servers: [] +in_latency_window: [] 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/ReplicaSetWithPrimary/Incompatible.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/Incompatible.yml index 783181133e..9c55395170 100644 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/Incompatible.yml +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/Incompatible.yml @@ -1,5 +1,5 @@ # During server selection, -# clients (drivers or mongos) MUST raise an error if ``maxStalenessMS`` is not zero or null, +# clients (drivers or mongos) MUST raise an error if ``maxStalenessSeconds`` is not zero or null, # and any server's ``maxWireVersion`` is less than 5 (`SERVER-23893`_). --- topology_description: @@ -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..5adfa3018d 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 not zero or null # 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 deleted file mode 100644 index 2a9634f253..0000000000 --- a/spec/support/max_staleness/ReplicaSetWithPrimary/ZeroMaxStaleness.yml +++ /dev/null @@ -1,27 +0,0 @@ -# A ``maxStalenessMS`` of zero or null MUST mean "no maximum". ---- -topology_description: - type: ReplicaSetWithPrimary - servers: - - &1 - address: a:27017 - type: RSPrimary - avg_rtt_ms: 50 # Too far. - lastUpdateTime: 0 - maxWireVersion: 5 - lastWrite: {lastWriteDate: {$numberLong: "1000001"}} - - &2 - address: b:27017 - type: RSSecondary - avg_rtt_ms: 5 - lastUpdateTime: 0 - maxWireVersion: 5 - lastWrite: {lastWriteDate: {$numberLong: "1"}} # Very stale. -read_preference: - mode: Nearest - maxStalenessMS: 0 # Means "no max staleness". -suitable_servers: # Very stale server is fine. -- *1 -- *2 -in_latency_window: -- *2 diff --git a/spec/support/max_staleness/Sharded/Incompatible.yml b/spec/support/max_staleness/Sharded/Incompatible.yml index 6188c1ebca..7f17152a4e 100644 --- a/spec/support/max_staleness/Sharded/Incompatible.yml +++ b/spec/support/max_staleness/Sharded/Incompatible.yml @@ -1,5 +1,5 @@ # During server selection, -# clients (drivers or mongos) MUST raise an error if ``maxStalenessMS`` is not zero or null, +# clients (drivers or mongos) MUST raise an error if ``maxStalenessSeconds`` is not zero or null, # and any server's ``maxWireVersion`` is less than 5 (`SERVER-23893`_). --- topology_description: @@ -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..4b2e9675cd 100644 --- a/spec/support/max_staleness/Single/Incompatible.yml +++ b/spec/support/max_staleness/Single/Incompatible.yml @@ -1,5 +1,5 @@ # During server selection, -# clients (drivers or mongos) MUST raise an error if ``maxStalenessMS`` is not zero or null, +# clients (drivers or mongos) MUST raise an error if ``maxStalenessSeconds`` is not zero or null, # and any server's ``maxWireVersion`` is less than 5 (`SERVER-23893`_). --- topology_description: @@ -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..493cfe8e97 100644 --- a/spec/support/server_selection.rb +++ b/spec/support/server_selection.rb @@ -84,10 +84,10 @@ 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'] @@ -128,7 +128,7 @@ def server_available? # @since 2.4.0 def invalid_max_staleness? @test['error'] || - candidate_servers.any? { |server| server['maxWireVersion'] < 5 } + candidate_servers.any? { |server| true unless (server['maxWireVersion'] && server['maxWireVersion'] < 5) } end # The subset of suitable servers that falls within the allowable latency From 57b30fd581efe87be84a0385549560f981603ff4 Mon Sep 17 00:00:00 2001 From: Emily Date: Tue, 22 Nov 2016 15:52:16 +0100 Subject: [PATCH 2/9] RUBY-1174 Further updates to max staleness implementation --- lib/mongo/server_selector/selectable.rb | 5 +++-- spec/mongo/max_staleness_spec.rb | 11 ++++++++++- .../ReplicaSetNoPrimary/NoKnownServers.yml | 16 ---------------- spec/support/server_selection.rb | 7 +++---- 4 files changed, 16 insertions(+), 23 deletions(-) delete mode 100644 spec/support/max_staleness/ReplicaSetNoPrimary/NoKnownServers.yml diff --git a/lib/mongo/server_selector/selectable.rb b/lib/mongo/server_selector/selectable.rb index 36f90536fa..3387ffcb44 100644 --- a/lib/mongo/server_selector/selectable.rb +++ b/lib/mongo/server_selector/selectable.rb @@ -154,8 +154,9 @@ def candidates(cluster) elsif cluster.sharded? near_servers(cluster.servers).each { |server| validate_max_staleness_support!(server) } else - validate_max_staleness_value!(cluster) - select(cluster.servers) + servers = cluster.servers + validate_max_staleness_value!(cluster) unless servers.empty? + select(servers) end end diff --git a/spec/mongo/max_staleness_spec.rb b/spec/mongo/max_staleness_spec.rb index c76948358a..136c490204 100644 --- a/spec/mongo/max_staleness_spec.rb +++ b/spec/mongo/max_staleness_spec.rb @@ -84,7 +84,16 @@ 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 'No matching server available', 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 + + context 'Valid read preference and matching server available', unless: (spec.invalid_max_staleness? || !spec.server_available?) 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) diff --git a/spec/support/max_staleness/ReplicaSetNoPrimary/NoKnownServers.yml b/spec/support/max_staleness/ReplicaSetNoPrimary/NoKnownServers.yml deleted file mode 100644 index a97b2eef87..0000000000 --- a/spec/support/max_staleness/ReplicaSetNoPrimary/NoKnownServers.yml +++ /dev/null @@ -1,16 +0,0 @@ -# A too-short maxStalenessSeconds isn't enforced until there are 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 -suitable_servers: [] -in_latency_window: [] diff --git a/spec/support/server_selection.rb b/spec/support/server_selection.rb index 493cfe8e97..23c228ed4d 100644 --- a/spec/support/server_selection.rb +++ b/spec/support/server_selection.rb @@ -88,7 +88,7 @@ def initialize(file) @read_preference = @test['read_preference'] @read_preference['mode'] = READ_PREFERENCES[@read_preference['mode']] @max_staleness = @read_preference['maxStalenessSeconds'] - @candidate_servers = @test['topology_description']['servers'] + @candidate_servers = @test['topology_description']['servers'].select { |s| s['type'] != 'Unknown' } @suitable_servers = @test['suitable_servers'] @in_latency_window = @test['in_latency_window'] @type = TOPOLOGY_TYPES[@test['topology_description']['type']] @@ -115,7 +115,7 @@ def replica_set? # # @since 2.0.0 def server_available? - !in_latency_window.empty? + in_latency_window && !in_latency_window.empty? end # Is the max staleness setting invalid. @@ -127,8 +127,7 @@ def server_available? # # @since 2.4.0 def invalid_max_staleness? - @test['error'] || - candidate_servers.any? { |server| true unless (server['maxWireVersion'] && server['maxWireVersion'] < 5) } + @test['error'] end # The subset of suitable servers that falls within the allowable latency From 9e1318d32da03bb4aa48aa9cd5e3c9c8924d6ecf Mon Sep 17 00:00:00 2001 From: Emily Date: Tue, 22 Nov 2016 17:45:36 +0100 Subject: [PATCH 3/9] RUBY-1174 Update test harness for max staleness --- spec/mongo/max_staleness_spec.rb | 36 ++++++++++++++++++-------------- spec/support/server_selection.rb | 22 +++++++++++-------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/spec/mongo/max_staleness_spec.rb b/spec/mongo/max_staleness_spec.rb index 136c490204..133492362c 100644 --- a/spec/mongo/max_staleness_spec.rb +++ b/spec/mongo/max_staleness_spec.rb @@ -65,7 +65,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,32 +84,36 @@ allow(cluster).to receive(:servers).and_return(candidate_servers) end - context 'No matching server available', if: (!spec.invalid_max_staleness? && !spec.server_available?) do + context 'when the max staleness is invalid' do + + it 'Raises an InvalidServerPreference exception', if: spec.invalid_max_staleness? do - it 'Raises a NoServerAvailable Exception' do expect do server_selector.select_server(cluster) - end.to raise_exception(Mongo::Error::NoServerAvailable) + end.to raise_exception(Mongo::Error::InvalidServerPreference) end end - context 'Valid read preference and matching server available', unless: (spec.invalid_max_staleness? || !spec.server_available?) do + context 'when the max staleness is valid' 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 + context 'when there are available servers' do - it 'Finds the most suitable server in the latency window' do - expect(in_latency_window).to include(server_selector.select_server(cluster)) + 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 - end - context 'when the max staleness cannot be applied', if: spec.invalid_max_staleness? do + context 'when there are no available servers', if: (!spec.invalid_max_staleness? && !spec.server_available?) do - it 'Raises exception' do - expect do - server_selector.select_server(cluster) - end.to raise_exception(Mongo::Error::InvalidServerPreference) + 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/support/server_selection.rb b/spec/support/server_selection.rb index 23c228ed4d..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. # @@ -88,9 +83,9 @@ def initialize(file) @read_preference = @test['read_preference'] @read_preference['mode'] = READ_PREFERENCES[@read_preference['mode']] @max_staleness = @read_preference['maxStalenessSeconds'] - @candidate_servers = @test['topology_description']['servers'].select { |s| s['type'] != 'Unknown' } - @suitable_servers = @test['suitable_servers'] - @in_latency_window = @test['in_latency_window'] + @candidate_servers = @test['topology_description']['servers'] + @suitable_servers = @test['suitable_servers'] || [] + @in_latency_window = @test['in_latency_window'] || [] @type = TOPOLOGY_TYPES[@test['topology_description']['type']] end @@ -115,7 +110,7 @@ def replica_set? # # @since 2.0.0 def server_available? - in_latency_window && !in_latency_window.empty? + !in_latency_window.empty? end # Is the max staleness setting invalid. @@ -148,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 From e2627f6b5b7a07a20729b1483ae3ee428b0ca8e4 Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 23 Nov 2016 10:42:52 +0100 Subject: [PATCH 4/9] RUBY-1174 Add spec tests and don't check max staleness value is topology type is Unknown --- lib/mongo/server_selector/selectable.rb | 5 ++-- spec/mongo/max_staleness_spec.rb | 1 + .../ReplicaSetNoPrimary/Incompatible.yml | 6 ++--- .../ReplicaSetNoPrimary/NoKnownServers.yml | 15 ++++++++++++ .../ReplicaSetNoPrimary/ZeroMaxStaleness.yml | 23 +++++++++++++++++++ .../ReplicaSetWithPrimary/Incompatible.yml | 6 ++--- .../MaxStalenessWithModePrimary.yml | 2 +- .../ZeroMaxStaleness.yml | 23 +++++++++++++++++++ .../max_staleness/Sharded/Incompatible.yml | 6 ++--- .../max_staleness/Single/Incompatible.yml | 6 ++--- 10 files changed, 77 insertions(+), 16 deletions(-) create mode 100644 spec/support/max_staleness/ReplicaSetNoPrimary/NoKnownServers.yml create mode 100644 spec/support/max_staleness/ReplicaSetNoPrimary/ZeroMaxStaleness.yml create mode 100644 spec/support/max_staleness/ReplicaSetWithPrimary/ZeroMaxStaleness.yml diff --git a/lib/mongo/server_selector/selectable.rb b/lib/mongo/server_selector/selectable.rb index 3387ffcb44..2233ebb1fa 100644 --- a/lib/mongo/server_selector/selectable.rb +++ b/lib/mongo/server_selector/selectable.rb @@ -154,9 +154,8 @@ def candidates(cluster) elsif cluster.sharded? near_servers(cluster.servers).each { |server| validate_max_staleness_support!(server) } else - servers = cluster.servers - validate_max_staleness_value!(cluster) unless servers.empty? - select(servers) + validate_max_staleness_value!(cluster) unless cluster.unknown? + select(cluster.servers) end end diff --git a/spec/mongo/max_staleness_spec.rb b/spec/mongo/max_staleness_spec.rb index 133492362c..78fd012faa 100644 --- a/spec/mongo/max_staleness_spec.rb +++ b/spec/mongo/max_staleness_spec.rb @@ -38,6 +38,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(:options).and_return(options) allow(c).to receive(:scan!).and_return(true) allow(c).to receive(:app_metadata).and_return(app_metadata) diff --git a/spec/support/max_staleness/ReplicaSetNoPrimary/Incompatible.yml b/spec/support/max_staleness/ReplicaSetNoPrimary/Incompatible.yml index a330069afa..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 ``maxStalenessSeconds`` 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 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/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 9c55395170..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 ``maxStalenessSeconds`` 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 diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml index 5adfa3018d..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 ``maxStalenessSeconds`` 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: diff --git a/spec/support/max_staleness/ReplicaSetWithPrimary/ZeroMaxStaleness.yml b/spec/support/max_staleness/ReplicaSetWithPrimary/ZeroMaxStaleness.yml new file mode 100644 index 0000000000..99d3a66f35 --- /dev/null +++ b/spec/support/max_staleness/ReplicaSetWithPrimary/ZeroMaxStaleness.yml @@ -0,0 +1,23 @@ +# maxStalenessSeconds=0 is prohibited. +--- +topology_description: + type: ReplicaSetWithPrimary + servers: + - &1 + address: a:27017 + type: RSPrimary + 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/Sharded/Incompatible.yml b/spec/support/max_staleness/Sharded/Incompatible.yml index 7f17152a4e..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 ``maxStalenessSeconds`` 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 diff --git a/spec/support/max_staleness/Single/Incompatible.yml b/spec/support/max_staleness/Single/Incompatible.yml index 4b2e9675cd..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 ``maxStalenessSeconds`` 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 From 582fc83e7ea61fc48bdcd0957a1fe8a65a030b4c Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 23 Nov 2016 10:53:51 +0100 Subject: [PATCH 5/9] RUBY-1174 Use heartbeat_frequency_seconds to be more precise about units --- lib/mongo/server.rb | 1 + lib/mongo/server_selector/selectable.rb | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) 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/selectable.rb b/lib/mongo/server_selector/selectable.rb index 2233ebb1fa..ca775cfba0 100644 --- a/lib/mongo/server_selector/selectable.rb +++ b/lib/mongo/server_selector/selectable.rb @@ -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 @@ -259,10 +259,10 @@ def validate_max_staleness_support!(server) def validate_max_staleness_value!(cluster) if @max_staleness - heartbeat_frequency = cluster.options[:heartbeat_frequency] || Server::Monitor::HEARTBEAT_FREQUENCY + heartbeat_frequency_seconds = cluster.options[:heartbeat_frequency] || Server::Monitor::HEARTBEAT_FREQUENCY unless @max_staleness > 0 && @max_staleness >= [ SMALLEST_MAX_STALENESS_SECONDS, - (heartbeat_frequency + Cluster::IDLE_WRITE_PERIOD_SECONDS) ].max + (heartbeat_frequency_seconds + Cluster::IDLE_WRITE_PERIOD_SECONDS) ].max raise Error::InvalidServerPreference.new(Error::InvalidServerPreference::INVALID_MAX_STALENESS) end end From 06a48a7cfa9571d9a6e8c8c92ed402771b8bdf91 Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 23 Nov 2016 10:56:25 +0100 Subject: [PATCH 6/9] RUBY-1174 cluster double should respond to #unknown? --- spec/mongo/server_selection_spec.rb | 1 + 1 file changed, 1 insertion(+) 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 From e3f89d4f2c84e367965d5d641d0885b75dd36d2e Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 23 Nov 2016 11:00:18 +0100 Subject: [PATCH 7/9] RUBY-1174 Add #unknown? method on cluster double --- spec/mongo/server_selector_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) 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 From 1ee8e6716018e2f8344667abc852eaa2acfe3a40 Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 23 Nov 2016 11:02:23 +0100 Subject: [PATCH 8/9] RUBY-1174 Remove redundant max_staleness check --- lib/mongo/server_selector/selectable.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/mongo/server_selector/selectable.rb b/lib/mongo/server_selector/selectable.rb index ca775cfba0..7df7276368 100644 --- a/lib/mongo/server_selector/selectable.rb +++ b/lib/mongo/server_selector/selectable.rb @@ -260,9 +260,8 @@ def validate_max_staleness_support!(server) def validate_max_staleness_value!(cluster) if @max_staleness heartbeat_frequency_seconds = cluster.options[:heartbeat_frequency] || Server::Monitor::HEARTBEAT_FREQUENCY - unless @max_staleness > 0 && - @max_staleness >= [ SMALLEST_MAX_STALENESS_SECONDS, - (heartbeat_frequency_seconds + Cluster::IDLE_WRITE_PERIOD_SECONDS) ].max + 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 From adb6fad02bfd4ae15d621cb52b4d0f523cd861cd Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 23 Nov 2016 11:25:22 +0100 Subject: [PATCH 9/9] RUBY-1174 Add details to invalid max staleness error message --- lib/mongo/error/invalid_server_preference.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/mongo/error/invalid_server_preference.rb b/lib/mongo/error/invalid_server_preference.rb index 2b284fb2d2..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 value is too small.".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. #