Skip to content

Commit 955d4f5

Browse files
RUBY-3368 Measure minimum RTT (#2837)
1 parent e1158d3 commit 955d4f5

File tree

7 files changed

+156
-69
lines changed

7 files changed

+156
-69
lines changed

lib/mongo/server.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def initialize(address, cluster, monitoring, event_listeners, options = {})
8080
include Id
8181
end
8282
@scan_semaphore = DistinguishingSemaphore.new
83-
@round_trip_time_averager = RoundTripTimeAverager.new
83+
@round_trip_time_calculator = RoundTripTimeCalculator.new
8484
@description = Description.new(address, {},
8585
load_balancer: !!@options[:load_balancer],
8686
force_load_balancer: force_load_balancer?,
@@ -228,9 +228,9 @@ def compressor
228228
# @api private
229229
attr_reader :scan_semaphore
230230

231-
# @return [ RoundTripTimeAverager ] Round trip time averager object.
231+
# @return [ RoundTripTimeCalculator ] Round trip time calculator object.
232232
# @api private
233-
attr_reader :round_trip_time_averager
233+
attr_reader :round_trip_time_calculator
234234

235235
# Is this server equal to another?
236236
#
@@ -701,5 +701,5 @@ def update_last_scan
701701
require 'mongo/server/connection_pool'
702702
require 'mongo/server/description'
703703
require 'mongo/server/monitor'
704-
require 'mongo/server/round_trip_time_averager'
704+
require 'mongo/server/round_trip_time_calculator'
705705
require 'mongo/server/push_monitor'

lib/mongo/server/monitor.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ def run_sdam_flow(result, awaited: false, scan_error: nil)
238238
old_description = server.description
239239

240240
new_description = Description.new(server.address, result,
241-
average_round_trip_time: server.round_trip_time_averager.average_round_trip_time
241+
average_round_trip_time: server.round_trip_time_calculator.average_round_trip_time
242242
)
243243

244244
server.cluster.run_sdam_flow(server.description, new_description, awaited: awaited, scan_error: scan_error)
@@ -306,7 +306,7 @@ def check
306306
end
307307

308308
if @connection
309-
result = server.round_trip_time_averager.measure do
309+
result = server.round_trip_time_calculator.measure do
310310
begin
311311
doc = @connection.check_document
312312
cmd = Protocol::Query.new(
@@ -323,7 +323,7 @@ def check
323323
else
324324
connection = Connection.new(server.address, options)
325325
connection.connect!
326-
result = server.round_trip_time_averager.measure do
326+
result = server.round_trip_time_calculator.measure do
327327
connection.handshake!
328328
end
329329
@connection = connection

lib/mongo/server/pending_connection.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def handshake!(speculative_auth_doc: nil)
131131
doc = nil
132132
@server.handle_handshake_failure! do
133133
begin
134-
response = @server.round_trip_time_averager.measure do
134+
response = @server.round_trip_time_calculator.measure do
135135
add_server_diagnostics do
136136
socket.write(hello_command.serialize.to_s)
137137
Protocol::Message.deserialize(socket, Protocol::Message::MAX_MESSAGE_SIZE)
@@ -155,7 +155,7 @@ def handshake!(speculative_auth_doc: nil)
155155
doc['serviceId'] ||= "fake:#{rand(2**32-1)+1}"
156156
end
157157

158-
post_handshake(doc, @server.round_trip_time_averager.average_round_trip_time)
158+
post_handshake(doc, @server.round_trip_time_calculator.average_round_trip_time)
159159

160160
doc
161161
end

lib/mongo/server/round_trip_time_averager.rb renamed to lib/mongo/server/round_trip_time_calculator.rb

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,29 @@
1818
module Mongo
1919
class Server
2020
# @api private
21-
class RoundTripTimeAverager
21+
class RoundTripTimeCalculator
2222

2323
# The weighting factor (alpha) for calculating the average moving
2424
# round trip time.
2525
RTT_WEIGHT_FACTOR = 0.2.freeze
2626
private_constant :RTT_WEIGHT_FACTOR
2727

28+
RTT_SAMPLES_FOR_MINIMUM = 10
29+
private_constant :RTT_SAMPLES_FOR_MINIMUM
30+
31+
MIN_SAMPLES = 3
32+
private_constant :MIN_SAMPLES
33+
2834
def initialize
2935
@last_round_trip_time = nil
3036
@average_round_trip_time = nil
37+
@minimum_round_trip_time = 0
38+
@rtts = []
3139
end
3240

3341
attr_reader :last_round_trip_time
3442
attr_reader :average_round_trip_time
43+
attr_reader :minimum_round_trip_time
3544

3645
def measure
3746
start = Utils.monotonic_time
@@ -44,14 +53,15 @@ def measure
4453
rescue Error, Error::AuthError => exc
4554
# For other errors, RTT is valid.
4655
end
47-
last_round_trip_time = Utils.monotonic_time - start
56+
last_rtt = Utils.monotonic_time - start
4857

4958
# If hello fails, we need to return the last round trip time
5059
# because it is used in the heartbeat failed SDAM event,
5160
# but we must not update the round trip time recorded in the server.
5261
unless exc
53-
@last_round_trip_time = last_round_trip_time
62+
@last_round_trip_time = last_rtt
5463
update_average_round_trip_time
64+
update_minimum_round_trip_time
5565
end
5666

5767
if exc
@@ -61,16 +71,21 @@ def measure
6171
end
6272
end
6373

64-
private
65-
66-
# This method is separate for testing purposes.
6774
def update_average_round_trip_time
6875
@average_round_trip_time = if average_round_trip_time
6976
RTT_WEIGHT_FACTOR * last_round_trip_time + (1 - RTT_WEIGHT_FACTOR) * average_round_trip_time
7077
else
7178
last_round_trip_time
7279
end
7380
end
81+
82+
def update_minimum_round_trip_time
83+
@rtts.push(@last_round_trip_time) unless @last_round_trip_time.nil?
84+
@minimum_round_trip_time = 0 and return if @rtts.size < MIN_SAMPLES
85+
86+
@rtts.shift if @rtts.size > RTT_SAMPLES_FOR_MINIMUM
87+
@minimum_round_trip_time = @rtts.compact.min
88+
end
7489
end
7590
end
7691
end

spec/mongo/server/round_trip_time_averager_spec.rb

Lines changed: 0 additions & 48 deletions
This file was deleted.
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# frozen_string_literal: true
2+
# rubocop:todo all
3+
4+
require 'spec_helper'
5+
6+
describe Mongo::Server::RoundTripTimeCalculator do
7+
let(:calculator) { Mongo::Server::RoundTripTimeCalculator.new }
8+
9+
describe '#update_average_round_trip_time' do
10+
context 'no existing average rtt' do
11+
it 'updates average rtt' do
12+
calculator.instance_variable_set('@last_round_trip_time', 5)
13+
calculator.update_average_round_trip_time
14+
expect(calculator.average_round_trip_time).to eq(5)
15+
end
16+
end
17+
18+
context 'with existing average rtt' do
19+
it 'averages with existing average rtt' do
20+
calculator.instance_variable_set('@last_round_trip_time', 5)
21+
calculator.instance_variable_set('@average_round_trip_time', 10)
22+
calculator.update_average_round_trip_time
23+
expect(calculator.average_round_trip_time).to eq(9)
24+
end
25+
end
26+
end
27+
28+
describe '#update_minimum_round_trip_time' do
29+
context 'with no samples' do
30+
it 'sets minimum_round_trip_time to zero' do
31+
calculator.update_minimum_round_trip_time
32+
expect(calculator.minimum_round_trip_time).to eq(0)
33+
end
34+
end
35+
36+
context 'with one sample' do
37+
before do
38+
calculator.instance_variable_set('@last_round_trip_time', 5)
39+
end
40+
41+
it 'sets minimum_round_trip_time to zero' do
42+
calculator.update_minimum_round_trip_time
43+
expect(calculator.minimum_round_trip_time).to eq(0)
44+
end
45+
end
46+
47+
context 'with two samples' do
48+
before do
49+
calculator.instance_variable_set('@last_round_trip_time', 10)
50+
calculator.instance_variable_set('@rtts', [5])
51+
end
52+
53+
it 'sets minimum_round_trip_time to zero' do
54+
calculator.update_minimum_round_trip_time
55+
expect(calculator.minimum_round_trip_time).to eq(0)
56+
end
57+
end
58+
59+
context 'with samples less than maximum' do
60+
before do
61+
calculator.instance_variable_set('@last_round_trip_time', 10)
62+
calculator.instance_variable_set('@rtts', [5, 4, 120])
63+
end
64+
65+
it 'properly sets minimum_round_trip_time' do
66+
calculator.update_minimum_round_trip_time
67+
expect(calculator.minimum_round_trip_time).to eq(4)
68+
end
69+
end
70+
71+
context 'with more than maximum samples' do
72+
before do
73+
calculator.instance_variable_set('@last_round_trip_time', 2)
74+
calculator.instance_variable_set('@rtts', [1, 20, 15, 4, 5, 6, 7, 39, 8, 4])
75+
end
76+
77+
it 'properly sets minimum_round_trip_time' do
78+
calculator.update_minimum_round_trip_time
79+
expect(calculator.minimum_round_trip_time).to eq(2)
80+
end
81+
end
82+
83+
end
84+
85+
describe '#measure' do
86+
context 'block does not raise' do
87+
it 'updates average rtt' do
88+
expect(calculator).to receive(:update_average_round_trip_time)
89+
calculator.measure do
90+
end
91+
end
92+
93+
it 'updates minimum rtt' do
94+
expect(calculator).to receive(:update_minimum_round_trip_time)
95+
calculator.measure do
96+
end
97+
end
98+
end
99+
100+
context 'block raises' do
101+
it 'does not update average rtt' do
102+
expect(calculator).not_to receive(:update_average_round_trip_time)
103+
expect do
104+
calculator.measure do
105+
raise "Problem"
106+
end
107+
end.to raise_error(/Problem/)
108+
end
109+
110+
it 'does not update minimum rtt' do
111+
expect(calculator).not_to receive(:update_minimum_round_trip_time)
112+
expect do
113+
calculator.measure do
114+
raise "Problem"
115+
end
116+
end.to raise_error(/Problem/)
117+
end
118+
end
119+
end
120+
end

spec/spec_tests/server_selection_rtt_spec.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,18 @@
1515

1616
context(spec.description) do
1717

18-
let(:averager) do
19-
Mongo::Server::RoundTripTimeAverager.new
18+
let(:calculator) do
19+
Mongo::Server::RoundTripTimeCalculator.new
2020
end
2121

2222
before do
23-
averager.instance_variable_set(:@average_round_trip_time, spec.average_rtt)
24-
averager.instance_variable_set(:@last_round_trip_time, spec.new_rtt)
25-
averager.send(:update_average_round_trip_time)
23+
calculator.instance_variable_set(:@average_round_trip_time, spec.average_rtt)
24+
calculator.instance_variable_set(:@last_round_trip_time, spec.new_rtt)
25+
calculator.update_average_round_trip_time
2626
end
2727

2828
it 'correctly calculates the moving average round trip time' do
29-
expect(averager.average_round_trip_time).to eq(spec.new_average_rtt)
29+
expect(calculator.average_round_trip_time).to eq(spec.new_average_rtt)
3030
end
3131
end
3232
end

0 commit comments

Comments
 (0)