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