gfe-relnote: In QUIC, when doing cwnd bootstrap, slow pacing rate if it is likely causing overshoot. Protected by gfe2_reloadable_flag_quic_bbr_mitigate_overly_large_bandwidth_sample. PiperOrigin-RevId: 288958446 Change-Id: Ia076b0f27d7d9302aa567a8577b25b0ad10da7cc
diff --git a/quic/core/congestion_control/bbr_sender.cc b/quic/core/congestion_control/bbr_sender.cc index b750e0a..bb63b55 100644 --- a/quic/core/congestion_control/bbr_sender.cc +++ b/quic/core/congestion_control/bbr_sender.cc
@@ -135,7 +135,9 @@ probe_rtt_skipped_if_similar_rtt_(false), probe_rtt_disabled_if_app_limited_(false), app_limited_since_last_probe_rtt_(false), - min_rtt_since_last_probe_rtt_(QuicTime::Delta::Infinite()) { + min_rtt_since_last_probe_rtt_(QuicTime::Delta::Infinite()), + network_parameters_adjusted_(false), + bytes_lost_with_network_parameters_adjusted_(0) { if (stats_) { // Clear some startup stats if |stats_| has been used by another sender, // which happens e.g. when QuicConnection switch send algorithms. @@ -377,6 +379,12 @@ QuicBandwidth new_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta(congestion_window_, GetMinRtt()); pacing_rate_ = std::max(pacing_rate_, new_pacing_rate); + if (GetQuicReloadableFlag( + quic_bbr_mitigate_overly_large_bandwidth_sample)) { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_bbr_mitigate_overly_large_bandwidth_sample, 1, 4); + network_parameters_adjusted_ = true; + } } } } @@ -473,7 +481,7 @@ // After the model is updated, recalculate the pacing rate and congestion // window. - CalculatePacingRate(); + CalculatePacingRate(bytes_lost); CalculateCongestionWindow(bytes_acked, excess_acked); CalculateRecoveryWindow(bytes_acked, bytes_lost); @@ -811,7 +819,7 @@ } } -void BbrSender::CalculatePacingRate() { +void BbrSender::CalculatePacingRate(QuicByteCount bytes_lost) { if (BandwidthEstimate().IsZero()) { return; } @@ -829,6 +837,40 @@ initial_congestion_window_, rtt_stats_->min_rtt()); return; } + + if (network_parameters_adjusted_) { + bytes_lost_with_network_parameters_adjusted_ += bytes_lost; + // Check for overshooting with network parameters adjusted when pacing rate + // > target_rate and loss has been detected. + if (pacing_rate_ > target_rate && + bytes_lost_with_network_parameters_adjusted_ > 0) { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_bbr_mitigate_overly_large_bandwidth_sample, 2, 4); + if (has_non_app_limited_sample_ || + bytes_lost_with_network_parameters_adjusted_ * 2 > + initial_congestion_window_) { + // We are fairly sure overshoot happens if 1) there is at least one + // non app-limited bw sample or 2) half of IW gets lost. Slow pacing + // rate. + if (has_non_app_limited_sample_) { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_bbr_mitigate_overly_large_bandwidth_sample, 3, 4); + } else { + QUIC_RELOADABLE_FLAG_COUNT_N( + quic_bbr_mitigate_overly_large_bandwidth_sample, 4, 4); + } + // Do not let the pacing rate drop below the connection's initial pacing + // rate. + pacing_rate_ = + std::max(target_rate, + QuicBandwidth::FromBytesAndTimeDelta( + initial_congestion_window_, rtt_stats_->min_rtt())); + bytes_lost_with_network_parameters_adjusted_ = 0; + network_parameters_adjusted_ = false; + } + } + } + // Slow the pacing rate in STARTUP once loss has ever been detected. const bool has_ever_detected_loss = end_recovery_at_.IsInitialized(); if (slower_startup_ && has_ever_detected_loss &&
diff --git a/quic/core/congestion_control/bbr_sender.h b/quic/core/congestion_control/bbr_sender.h index 9a811fa..db0f330 100644 --- a/quic/core/congestion_control/bbr_sender.h +++ b/quic/core/congestion_control/bbr_sender.h
@@ -233,7 +233,7 @@ QuicByteCount newly_acked_bytes); // Determines the appropriate pacing rate for the connection. - void CalculatePacingRate(); + void CalculatePacingRate(QuicByteCount bytes_lost); // Determines the appropriate congestion window for the connection. void CalculateCongestionWindow(QuicByteCount bytes_acked, QuicByteCount excess_acked); @@ -392,6 +392,12 @@ bool probe_rtt_disabled_if_app_limited_; bool app_limited_since_last_probe_rtt_; QuicTime::Delta min_rtt_since_last_probe_rtt_; + + // True if network parameters are adjusted, and this will be reset if + // overshooting is detected and pacing rate gets slowed. + bool network_parameters_adjusted_; + // Bytes lost after network parameters gets adjusted. + QuicByteCount bytes_lost_with_network_parameters_adjusted_; }; QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
diff --git a/quic/core/congestion_control/bbr_sender_test.cc b/quic/core/congestion_control/bbr_sender_test.cc index d34d15b..c8b1b77 100644 --- a/quic/core/congestion_control/bbr_sender_test.cc +++ b/quic/core/congestion_control/bbr_sender_test.cc
@@ -1421,5 +1421,39 @@ } } +TEST_F(BbrSenderTest, MitigateCwndBootstrappingOvershoot) { + SetQuicReloadableFlag(quic_bbr_mitigate_overly_large_bandwidth_sample, true); + CreateDefaultSetup(); + bbr_sender_.AddBytesToTransfer(1 * 1024 * 1024); + + // Wait until an ACK comes back. + const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5); + bool simulator_result = simulator_.RunUntilOrTimeout( + [this]() { return !sender_->ExportDebugState().min_rtt.IsZero(); }, + timeout); + ASSERT_TRUE(simulator_result); + + // Bootstrap cwnd by a overly large bandwidth sample. + bbr_sender_.connection()->AdjustNetworkParameters( + SendAlgorithmInterface::NetworkParams(8 * kTestLinkBandwidth, + QuicTime::Delta::Zero(), false)); + QuicBandwidth pacing_rate = sender_->PacingRate(0); + EXPECT_EQ(8 * kTestLinkBandwidth, pacing_rate); + + // Wait until pacing_rate decreases. + simulator_result = simulator_.RunUntilOrTimeout( + [this, pacing_rate]() { return sender_->PacingRate(0) < pacing_rate; }, + timeout); + ASSERT_TRUE(simulator_result); + EXPECT_EQ(BbrSender::STARTUP, sender_->ExportDebugState().mode); + if (GetQuicReloadableFlag(quic_conservative_cwnd_and_pacing_gains)) { + EXPECT_APPROX_EQ(2.0f * sender_->BandwidthEstimate(), + sender_->PacingRate(0), 0.01f); + } else { + EXPECT_APPROX_EQ(2.885f * sender_->BandwidthEstimate(), + sender_->PacingRate(0), 0.01f); + } +} + } // namespace test } // namespace quic