1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 */
8
9 #include <quic/congestion_control/Pacer.h>
10
11 #include <folly/portability/GTest.h>
12 #include <quic/congestion_control/TokenlessPacer.h>
13
14 using namespace testing;
15
16 namespace quic {
17 namespace test {
18
19 class TokenlessPacerTest : public Test {
20 public:
SetUp()21 void SetUp() override {
22 conn.transportSettings.pacingTimerTickInterval = 1us;
23 }
24
25 protected:
26 QuicConnectionStateBase conn{QuicNodeType::Client};
27 TokenlessPacer pacer{conn, conn.transportSettings.minCwndInMss};
28 };
29
TEST_F(TokenlessPacerTest,RateCalculator)30 TEST_F(TokenlessPacerTest, RateCalculator) {
31 pacer.setPacingRateCalculator([](const QuicConnectionStateBase&,
32 uint64_t,
33 uint64_t,
34 std::chrono::microseconds) {
35 return PacingRate::Builder().setInterval(1234us).setBurstSize(4321).build();
36 });
37 pacer.refreshPacingRate(200000, 200us);
38 EXPECT_EQ(0us, pacer.getTimeUntilNextWrite());
39 EXPECT_EQ(4321, pacer.updateAndGetWriteBatchSize(Clock::now()));
40 EXPECT_NEAR(1234, pacer.getTimeUntilNextWrite().count(), 100);
41 }
42
TEST_F(TokenlessPacerTest,NoCompensateTimerDrift)43 TEST_F(TokenlessPacerTest, NoCompensateTimerDrift) {
44 pacer.setPacingRateCalculator([](const QuicConnectionStateBase&,
45 uint64_t,
46 uint64_t,
47 std::chrono::microseconds) {
48 return PacingRate::Builder().setInterval(1000us).setBurstSize(10).build();
49 });
50 auto currentTime = Clock::now();
51 pacer.refreshPacingRate(20, 100us); // These two values do not matter here
52 EXPECT_EQ(10, pacer.updateAndGetWriteBatchSize(currentTime + 1000us));
53 EXPECT_EQ(10, pacer.updateAndGetWriteBatchSize(currentTime + 2000us));
54 }
55
TEST_F(TokenlessPacerTest,NextWriteTime)56 TEST_F(TokenlessPacerTest, NextWriteTime) {
57 EXPECT_EQ(0us, pacer.getTimeUntilNextWrite());
58
59 pacer.setPacingRateCalculator([](const QuicConnectionStateBase&,
60 uint64_t,
61 uint64_t,
62 std::chrono::microseconds rtt) {
63 return PacingRate::Builder().setInterval(rtt).setBurstSize(10).build();
64 });
65 pacer.refreshPacingRate(20, 1000us);
66 // Right after refresh, it's always 0us. You can always send right after an
67 // ack.
68 EXPECT_EQ(0us, pacer.getTimeUntilNextWrite());
69 EXPECT_EQ(10, pacer.updateAndGetWriteBatchSize(Clock::now()));
70
71 // Then we use real delay:
72 EXPECT_NEAR(1000, pacer.getTimeUntilNextWrite().count(), 100);
73 }
74
TEST_F(TokenlessPacerTest,RttFactor)75 TEST_F(TokenlessPacerTest, RttFactor) {
76 auto realRtt = 100ms;
77 bool calculatorCalled = false;
78 pacer.setRttFactor(1, 2);
79 pacer.setPacingRateCalculator([&](const QuicConnectionStateBase&,
80 uint64_t,
81 uint64_t,
82 std::chrono::microseconds rtt) {
83 EXPECT_EQ(rtt, realRtt / 2);
84 calculatorCalled = true;
85 return PacingRate::Builder().setInterval(rtt).setBurstSize(10).build();
86 });
87 pacer.refreshPacingRate(20, realRtt);
88 EXPECT_TRUE(calculatorCalled);
89 }
90
TEST_F(TokenlessPacerTest,ImpossibleToPace)91 TEST_F(TokenlessPacerTest, ImpossibleToPace) {
92 conn.transportSettings.pacingTimerTickInterval = 1ms;
93 pacer.setPacingRateCalculator([](const QuicConnectionStateBase& conn,
94 uint64_t cwndBytes,
95 uint64_t,
96 std::chrono::microseconds rtt) {
97 return PacingRate::Builder()
98 .setInterval(rtt)
99 .setBurstSize(cwndBytes / conn.udpSendPacketLen)
100 .build();
101 });
102 pacer.refreshPacingRate(200 * conn.udpSendPacketLen, 100us);
103 EXPECT_EQ(0us, pacer.getTimeUntilNextWrite());
104 EXPECT_EQ(
105 conn.transportSettings.writeConnectionDataPacketsLimit,
106 pacer.updateAndGetWriteBatchSize(Clock::now()));
107 }
108
TEST_F(TokenlessPacerTest,ChangeMaxPacingRate)109 TEST_F(TokenlessPacerTest, ChangeMaxPacingRate) {
110 int calculatorCallCount = 0;
111 pacer.setPacingRateCalculator([&calculatorCallCount](
112 const QuicConnectionStateBase& conn,
113 uint64_t cwndBytes,
114 uint64_t,
115 std::chrono::microseconds rtt) {
116 calculatorCallCount++;
117 return PacingRate::Builder()
118 .setInterval(rtt)
119 .setBurstSize(cwndBytes / conn.udpSendPacketLen)
120 .build();
121 });
122 auto rtt = 500 * 1000us;
123 auto timestamp = Clock::now();
124 // Request pacing at 50 Mbps
125 pacer.refreshPacingRate(3125000, rtt);
126 EXPECT_EQ(1, calculatorCallCount);
127 EXPECT_EQ(
128 3125000 / kDefaultUDPSendPacketLen,
129 pacer.updateAndGetWriteBatchSize(timestamp));
130 EXPECT_EQ(rtt.count(), pacer.getTimeUntilNextWrite(timestamp).count());
131
132 // Set max pacing rate to 40 Mbps
133 pacer.setMaxPacingRate(5 * 1000 * 1000u); // Bytes per second
134 // This should bring down the pacer rate to 40 Mbps
135 EXPECT_EQ(0us, pacer.getTimeUntilNextWrite(timestamp));
136 auto burst = pacer.updateAndGetWriteBatchSize(timestamp);
137 auto interval = pacer.getTimeUntilNextWrite(timestamp);
138 uint64_t pacerRate =
139 burst * kDefaultUDPSendPacketLen * std::chrono::seconds{1} / interval;
140 EXPECT_EQ(5 * 1000 * 1000u, pacerRate);
141 pacer.reset();
142 // Requesting a rate of 50 Mbps should not change interval or burst
143 pacer.refreshPacingRate(3125000, rtt);
144 EXPECT_EQ(1, calculatorCallCount); // Calculator not called again.
145 EXPECT_EQ(burst, pacer.updateAndGetWriteBatchSize(timestamp));
146 EXPECT_EQ(interval.count(), pacer.getTimeUntilNextWrite(timestamp).count());
147 pacer.reset();
148
149 // The setPacingRate API shouldn't make changes either
150 pacer.setPacingRate(6250 * 1000u); // 50 Mbps
151 EXPECT_EQ(burst, pacer.updateAndGetWriteBatchSize(timestamp));
152 EXPECT_EQ(interval.count(), pacer.getTimeUntilNextWrite(timestamp).count());
153 pacer.reset();
154
155 // Increasing max pacing rate to 75 Mbps shouldn't make changes
156 pacer.setMaxPacingRate(9375 * 1000u);
157 EXPECT_EQ(burst, pacer.updateAndGetWriteBatchSize(timestamp));
158 EXPECT_EQ(interval.count(), pacer.getTimeUntilNextWrite(timestamp).count());
159 pacer.reset();
160
161 // Increase pacing to 50 Mbps and ensure it takes effect
162 pacer.refreshPacingRate(3125000, rtt);
163 EXPECT_EQ(2, calculatorCallCount); // Calculator called
164 EXPECT_EQ(
165 3125000 / kDefaultUDPSendPacketLen,
166 pacer.updateAndGetWriteBatchSize(timestamp));
167 EXPECT_EQ(rtt.count(), pacer.getTimeUntilNextWrite(timestamp).count());
168 pacer.reset();
169
170 // Increase pacing to 80 Mbps using alternative API and ensure rate is limited
171 // to 75 Mbps
172 pacer.setPacingRate(10 * 1000 * 1000u);
173 burst = pacer.updateAndGetWriteBatchSize(timestamp);
174 interval = pacer.getTimeUntilNextWrite(timestamp);
175 pacerRate =
176 burst * kDefaultUDPSendPacketLen * std::chrono::seconds{1} / interval;
177 EXPECT_NEAR(9375 * 1000u, pacerRate, 1000); // To accommodate rounding
178 }
179
TEST_F(TokenlessPacerTest,SetMaxPacingRateOnUnlimitedPacer)180 TEST_F(TokenlessPacerTest, SetMaxPacingRateOnUnlimitedPacer) {
181 auto timestamp = Clock::now();
182 // Pacing is currently not pacing
183 EXPECT_EQ(0us, pacer.getTimeUntilNextWrite(timestamp));
184 EXPECT_NE(0, pacer.updateAndGetWriteBatchSize(timestamp));
185 EXPECT_EQ(0us, pacer.getTimeUntilNextWrite(timestamp));
186
187 // Set max pacing rate 40 Mbps and ensure it took effect
188 pacer.setMaxPacingRate(5 * 1000 * 1000u); // Bytes per second
189 EXPECT_EQ(0us, pacer.getTimeUntilNextWrite(timestamp));
190 auto burst = pacer.updateAndGetWriteBatchSize(timestamp);
191 auto interval = pacer.getTimeUntilNextWrite(timestamp);
192 uint64_t pacerRate =
193 burst * kDefaultUDPSendPacketLen * std::chrono::seconds{1} / interval;
194 EXPECT_NEAR(5 * 1000 * 1000u, pacerRate, 1000); // To accommodate rounding
195 }
196
TEST_F(TokenlessPacerTest,SetZeroPacingRate)197 TEST_F(TokenlessPacerTest, SetZeroPacingRate) {
198 auto timestamp = Clock::now();
199 // A Zero pacing rate should not result in a divide-by-zero
200 conn.transportSettings.pacingTimerTickInterval = 1000us;
201 pacer.setPacingRate(0);
202 EXPECT_EQ(0, pacer.updateAndGetWriteBatchSize(timestamp));
203 EXPECT_EQ(1000, pacer.getTimeUntilNextWrite(timestamp).count());
204 }
205
TEST_F(TokenlessPacerTest,RefreshPacingRateWhenRTTIsZero)206 TEST_F(TokenlessPacerTest, RefreshPacingRateWhenRTTIsZero) {
207 auto timestamp = Clock::now();
208 // rtt=0 should not result in a divide-by-zero
209 conn.transportSettings.pacingTimerTickInterval = 1000us;
210 pacer.refreshPacingRate(100, 0us);
211 // Verify burst is writeConnectionDataPacketsLimit and interval is
212 // 0us right after writing
213 EXPECT_EQ(
214 conn.transportSettings.writeConnectionDataPacketsLimit,
215 pacer.updateAndGetWriteBatchSize(timestamp));
216 EXPECT_EQ(0us, pacer.getTimeUntilNextWrite(timestamp));
217 }
218
219 } // namespace test
220 } // namespace quic
221