Do not add lumpy tokens to QUIC's PacingSender when CWND limited.
Protected by quic_reloadable_flag_quic_fix_pacing_sender_bursts.
PiperOrigin-RevId: 378501542
diff --git a/quic/core/congestion_control/bbr2_simulator_test.cc b/quic/core/congestion_control/bbr2_simulator_test.cc
index 99acf2c..7216eea 100644
--- a/quic/core/congestion_control/bbr2_simulator_test.cc
+++ b/quic/core/congestion_control/bbr2_simulator_test.cc
@@ -477,7 +477,11 @@
EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
sender_->ExportDebugState().bandwidth_hi, 0.01f);
- EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
+ if (GetQuicReloadableFlag(quic_fix_pacing_sender_bursts)) {
+ EXPECT_EQ(sender_loss_rate_in_packets(), 0);
+ } else {
+ EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
+ }
// The margin here is high, because both link level aggregation and ack
// decimation can greatly increase smoothed rtt.
EXPECT_GE(params.RTT() * 5, rtt_stats()->smoothed_rtt());
@@ -558,6 +562,9 @@
[this]() { return sender_endpoint_.bytes_to_transfer() == 0; },
QuicTime::Delta::FromSeconds(50));
EXPECT_TRUE(simulator_result);
+ // Ensure the full bandwidth is discovered.
+ EXPECT_APPROX_EQ(params.test_link.bandwidth,
+ sender_->ExportDebugState().bandwidth_hi, 0.02f);
}
// Test the number of losses incurred by the startup phase in a situation when
diff --git a/quic/core/congestion_control/pacing_sender.cc b/quic/core/congestion_control/pacing_sender.cc
index a43601e..265a422 100644
--- a/quic/core/congestion_control/pacing_sender.cc
+++ b/quic/core/congestion_control/pacing_sender.cc
@@ -102,6 +102,12 @@
// is about 10ms of queueing.
lumpy_tokens_ = 1u;
}
+ if (GetQuicReloadableFlag(quic_fix_pacing_sender_bursts) &&
+ (bytes_in_flight + bytes) >= sender_->GetCongestionWindow()) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_fix_pacing_sender_bursts);
+ // Don't add lumpy_tokens if the congestion controller is CWND limited.
+ lumpy_tokens_ = 1u;
+ }
}
--lumpy_tokens_;
if (pacing_limited_) {
diff --git a/quic/core/congestion_control/pacing_sender_test.cc b/quic/core/congestion_control/pacing_sender_test.cc
index 9c28e9f..ec3119d 100644
--- a/quic/core/congestion_control/pacing_sender_test.cc
+++ b/quic/core/congestion_control/pacing_sender_test.cc
@@ -7,6 +7,7 @@
#include <memory>
#include <utility>
+#include "quic/core/quic_constants.h"
#include "quic/core/quic_packets.h"
#include "quic/platform/api/quic_flag_utils.h"
#include "quic/platform/api/quic_flags.h"
@@ -96,7 +97,6 @@
OnPacketSent(clock_.Now(), prior_in_flight, packet_number_,
kMaxOutgoingPacketSize, retransmittable_data));
EXPECT_CALL(*mock_sender_, GetCongestionWindow())
- .Times(AtMost(1))
.WillRepeatedly(Return(cwnd * kDefaultTCPMSS));
EXPECT_CALL(*mock_sender_,
CanSend(prior_in_flight + kMaxOutgoingPacketSize))
@@ -459,7 +459,7 @@
}
TEST_F(PacingSenderTest, NoLumpyPacingForLowBandwidthFlows) {
- // Set lumpy size to be 3, and cwnd faction to 0.5
+ // Set lumpy size to be 3, and cwnd fraction to 0.5
SetQuicFlag(FLAGS_quic_lumpy_pacing_size, 3);
SetQuicFlag(FLAGS_quic_lumpy_pacing_cwnd_fraction, 0.5f);
@@ -484,6 +484,43 @@
}
}
+// Regression test for b/184471302 to ensure that ACKs received back-to-back
+// don't cause bursts in sending.
+TEST_F(PacingSenderTest, NoBurstsForLumpyPacingWithAckAggregation) {
+ // Configure pacing rate of 1 packet per millisecond.
+ QuicTime::Delta inter_packet_delay = QuicTime::Delta::FromMilliseconds(1);
+ InitPacingRate(kInitialBurstPackets,
+ QuicBandwidth::FromBytesAndTimeDelta(kMaxOutgoingPacketSize,
+ inter_packet_delay));
+ UpdateRtt();
+
+ // Send kInitialBurstPackets packets, and verify that they are not paced.
+ for (int i = 0; i < kInitialBurstPackets; ++i) {
+ CheckPacketIsSentImmediately();
+ }
+ // The last packet of the burst causes the sender to be CWND limited.
+ CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA,
+ 10 * kMaxOutgoingPacketSize, false, 10);
+
+ if (GetQuicReloadableFlag(quic_fix_pacing_sender_bursts)) {
+ // The last sent packet made the connection CWND limited, so no lumpy tokens
+ // should be available.
+ EXPECT_EQ(0u, pacing_sender_->lumpy_tokens());
+ CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA,
+ 10 * kMaxOutgoingPacketSize, false, 10);
+ EXPECT_EQ(0u, pacing_sender_->lumpy_tokens());
+ CheckPacketIsDelayed(2 * inter_packet_delay);
+ } else {
+ EXPECT_EQ(1u, pacing_sender_->lumpy_tokens());
+ // Repeatedly send single packets to make the sender CWND limited and
+ // observe that there's no pacing without the fix.
+ for (int i = 0; i < 10; ++i) {
+ CheckPacketIsSentImmediately(HAS_RETRANSMITTABLE_DATA,
+ 10 * kMaxOutgoingPacketSize, false, 10);
+ }
+ }
+}
+
TEST_F(PacingSenderTest, IdealNextPacketSendTimeWithLumpyPacing) {
// Set lumpy size to be 3, and cwnd faction to 0.5
SetQuicFlag(FLAGS_quic_lumpy_pacing_size, 3);
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 6c6df06..0afbf73 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -131,6 +131,8 @@
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_h3_datagram, false)
// When true, defaults to BBR congestion control instead of Cubic.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_to_bbr, false)
+// When true, prevents QUIC\'s PacingSender from generating bursts when the congestion controller is CWND limited and not pacing limited.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_pacing_sender_bursts, false)
// When true, set the initial congestion control window from connection options in QuicSentPacketManager rather than TcpCubicSenderBytes.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_unified_iw_options, false)