BBR3: Update startup pacing gain, drain gain, and max ack height window to match the BBR IETF draft: https://datatracker.ietf.org/doc/draft-ietf-ccwg-bbr/

The exact round Max Bandwidth is reached in Startup can change slightly, so tests are adjusted accordingly.

BB07 and BB2S were removed from BBRv3 because it's unlikely the queue will ever be drained in Startup, particularly with extra_acked being added to CWND.

PiperOrigin-RevId: 906354645
diff --git a/quiche/quic/core/congestion_control/bbr2_misc.h b/quiche/quic/core/congestion_control/bbr2_misc.h
index 3da0471..83ee77b 100644
--- a/quiche/quic/core/congestion_control/bbr2_misc.h
+++ b/quiche/quic/core/congestion_control/bbr2_misc.h
@@ -72,6 +72,10 @@
 inline constexpr QuicByteCount kDefaultMinimumCongestionWindow =
     4 * kMaxSegmentSize;
 inline constexpr float kInitialPacingGain = 2.885f;
+// The derived startup pacing gain for 2x growth: (4 * ln(2)) = 2.773.
+inline constexpr float kDerivedStartupPacingGain = 2.773f;
+// The derived startup CWND gain for 2x growth.  Also the default CWND gain.
+inline constexpr float kDerivedDefaultCwndGain = 2.0f;
 
 // Bbr2Params contains all parameters of a Bbr2Sender.
 struct QUICHE_EXPORT Bbr2Params {
@@ -83,9 +87,8 @@
    */
 
   // The gain for CWND in startup.
-  float startup_cwnd_gain = 2.0;
-  // TODO(wub): Maybe change to the newly derived value of 2.773 (4 * ln(2)).
-  float startup_pacing_gain = 2.885;
+  float startup_cwnd_gain = kDerivedDefaultCwndGain;
+  float startup_pacing_gain = kInitialPacingGain;
 
   // STARTUP or PROBE_UP are exited if the total bandwidth growth is less than
   // |full_bw_threshold| in the last |startup_full_bw_rounds| round trips.
@@ -96,6 +99,7 @@
   // Number of rounds to stay in STARTUP when there's a sufficient queue that
   // bytes_in_flight never drops below the target (1.75 * BDP).  0 indicates the
   // feature is disabled and we never exit due to queueing.
+  // NOT supported in BBR3.
   QuicRoundTripCount max_startup_queue_rounds = 0;
 
   // The minimum number of loss marking events to exit STARTUP.
@@ -114,8 +118,8 @@
   /*
    * DRAIN parameters.
    */
-  float drain_cwnd_gain = 2.0;
-  float drain_pacing_gain = 1.0 / 2.885;
+  float drain_cwnd_gain = kDerivedDefaultCwndGain;
+  float drain_pacing_gain = 1.0 / kInitialPacingGain;
 
   /*
    * PROBE_BW parameters.
@@ -149,7 +153,7 @@
   float probe_bw_probe_down_pacing_gain = 0.91;
   float probe_bw_default_pacing_gain = 1.0;
 
-  float probe_bw_cwnd_gain = 2.0;
+  float probe_bw_cwnd_gain = kDerivedDefaultCwndGain;
 
   /*
    * PROBE_UP parameters.
diff --git a/quiche/quic/core/congestion_control/bbr3_sender.cc b/quiche/quic/core/congestion_control/bbr3_sender.cc
index e80e03e..7067b28 100644
--- a/quiche/quic/core/congestion_control/bbr3_sender.cc
+++ b/quiche/quic/core/congestion_control/bbr3_sender.cc
@@ -57,9 +57,15 @@
   if (!connection_stats_->slowstart_duration.IsRunning()) {
     connection_stats_->slowstart_duration.Start(now);
   }
-  // Enter() is never called for Startup, so the gains needs to be set here.
-  model_.set_pacing_gain(params_.startup_pacing_gain);
-  model_.set_cwnd_gain(params_.startup_cwnd_gain);
+  // The BBR IETF draft uses 0.5 as the drain pacing gain.
+  params_.drain_pacing_gain = 0.5f;
+  // STARTUP has a shorter MaxAckHeightTracker window of 1 round.
+  params_.startup_include_extra_acked = true;
+  model_.SetMaxAckHeightTrackerWindowLength(1);
+  // Use the derived startup pacing gain in the IETF draft.
+  model_.set_pacing_gain(kDerivedStartupPacingGain);
+
+  QUICHE_DCHECK_EQ(model_.cwnd_gain(), 2.0);
   QUIC_DVLOG(2) << this << " Initializing Bbr3Sender. mode:" << mode_
                 << ", PacingRate:" << pacing_rate_ << ", Cwnd:" << cwnd_
                 << ", CwndLimits:" << params_.cwnd_limits << "  @ " << now;
@@ -68,43 +74,36 @@
 
 void Bbr3Sender::SetFromConfig(const QuicConfig& config,
                                Perspective perspective) {
-  if (config.HasClientRequestedIndependentOption(kB2NA, perspective)) {
-    params_.add_ack_height_to_queueing_threshold = false;
-  }
-  if (config.HasClientRequestedIndependentOption(kB2RP, perspective)) {
-    params_.avoid_unnecessary_probe_rtt = false;
-  }
-  if (config.HasClientRequestedIndependentOption(k1RTT, perspective)) {
-    params_.startup_full_bw_rounds = 1;
-  }
-  if (config.HasClientRequestedIndependentOption(k2RTT, perspective)) {
-    params_.startup_full_bw_rounds = 2;
-  }
-  if (config.HasClientRequestedIndependentOption(kB2HR, perspective)) {
-    params_.inflight_hi_headroom = 0.15;
-  }
-  if (config.HasClientRequestedIndependentOption(kICW1, perspective)) {
-    max_cwnd_when_network_parameters_adjusted_ = 100 * kDefaultTCPMSS;
-  }
-
   ApplyConnectionOptions(config.ClientRequestedIndependentOptions(perspective));
 }
 
 void Bbr3Sender::ApplyConnectionOptions(
     const QuicTagVector& connection_options) {
-  if (GetQuicReloadableFlag(quic_bbr2_extra_acked_window) &&
-      ContainsQuicTag(connection_options, kBBR4)) {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_extra_acked_window, 1, 2);
-    model_.SetMaxAckHeightTrackerWindowLength(20);
+  if (ContainsQuicTag(connection_options, kB2NA)) {
+    params_.add_ack_height_to_queueing_threshold = false;
   }
-  if (GetQuicReloadableFlag(quic_bbr2_extra_acked_window) &&
-      ContainsQuicTag(connection_options, kBBR5)) {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_extra_acked_window, 2, 2);
-    model_.SetMaxAckHeightTrackerWindowLength(40);
+  if (ContainsQuicTag(connection_options, kB2RP)) {
+    params_.avoid_unnecessary_probe_rtt = false;
   }
-  if (ContainsQuicTag(connection_options, kBBQ1)) {
-    params_.startup_pacing_gain = 2.773;
-    params_.drain_pacing_gain = 1.0 / params_.drain_cwnd_gain;
+  if (ContainsQuicTag(connection_options, k1RTT)) {
+    params_.startup_full_bw_rounds = 1;
+  }
+  if (ContainsQuicTag(connection_options, k2RTT)) {
+    params_.startup_full_bw_rounds = 2;
+  }
+  if (ContainsQuicTag(connection_options, kB2HR)) {
+    params_.inflight_hi_headroom = 0.15;
+  }
+  if (ContainsQuicTag(connection_options, kICW1)) {
+    max_cwnd_when_network_parameters_adjusted_ = 100 * kDefaultTCPMSS;
+  }
+  if (ContainsQuicTag(connection_options, kBBR4)) {
+    QUICHE_DCHECK_EQ(mode_, Bbr2Mode::STARTUP);
+    max_ack_height_window_length_ = 20;
+  }
+  if (ContainsQuicTag(connection_options, kBBR5)) {
+    QUICHE_DCHECK_EQ(mode_, Bbr2Mode::STARTUP);
+    max_ack_height_window_length_ = 40;
   }
   if (ContainsQuicTag(connection_options, kBBQ2)) {
     params_.startup_cwnd_gain = 2.885;
@@ -156,9 +155,6 @@
   if (ContainsQuicTag(connection_options, kB205)) {
     params_.startup_include_extra_acked = true;
   }
-  if (ContainsQuicTag(connection_options, kB207)) {
-    params_.max_startup_queue_rounds = 1;
-  }
   if (ContainsQuicTag(connection_options, kBBRA)) {
     model_.SetStartNewAggregationEpochAfterFullRound(true);
   }
@@ -180,9 +176,6 @@
   if (ContainsQuicTag(connection_options, kBB2U)) {
     params_.max_probe_up_queue_rounds = 2;
   }
-  if (ContainsQuicTag(connection_options, kBB2S)) {
-    params_.max_startup_queue_rounds = 2;
-  }
 }
 
 Limits<QuicByteCount> Bbr3Sender::GetCwndLimitsByMode() const {
@@ -439,6 +432,7 @@
   } else if (prior_cwnd < target_cwnd || prior_cwnd < 2 * initial_cwnd_) {
     cwnd_ = prior_cwnd + bytes_acked;
   }
+
   const QuicByteCount desired_cwnd = cwnd_;
 
   cwnd_ = GetCwndLimitsByMode().ApplyLimits(cwnd_);
@@ -493,7 +487,6 @@
 }
 
 QuicByteCount Bbr3Sender::GetCongestionWindow() const {
-  // TODO(wub): Implement Recovery?
   return cwnd_;
 }
 
@@ -525,6 +518,8 @@
   connection_stats_->slowstart_duration.Stop(now);
   // Clear bandwidth_lo if it's set during STARTUP.
   model_.clear_bandwidth_lo();
+  // Increase the max_ack_height_tracker window when exiting STARTUP from 1.
+  model_.SetMaxAckHeightTrackerWindowLength(max_ack_height_window_length_);
 }
 
 void Bbr3Sender::OnExitQuiescence(QuicTime now) {
@@ -620,17 +615,8 @@
     return Bbr2Mode::STARTUP;
   }
   bool has_bandwidth_growth = model_.HasBandwidthGrowth(congestion_event);
-  if (params_.max_startup_queue_rounds > 0 && !has_bandwidth_growth) {
-    // 1.75 is less than the 2x CWND gain, but substantially more than 1.25x,
-    // the minimum bandwidth increase expected during STARTUP.
-    model_.CheckPersistentQueue(congestion_event, 1.75);
-  }
-  // TCP BBR always exits upon excessive losses. QUIC BBRv1 does not exit
-  // upon excessive losses, if enough bandwidth growth is observed or if the
-  // sample was app limited.
-  if (params_.always_exit_startup_on_excess_loss ||
-      (!congestion_event.last_packet_send_state.is_app_limited &&
-       !has_bandwidth_growth)) {
+  // By default, don't exit STARTUP if there was bandwidth growth.
+  if (params_.always_exit_startup_on_excess_loss || !has_bandwidth_growth) {
     CheckExcessiveLosses(congestion_event);
   }
 
diff --git a/quiche/quic/core/congestion_control/bbr3_sender.h b/quiche/quic/core/congestion_control/bbr3_sender.h
index f6e0f9e..88b0855 100644
--- a/quiche/quic/core/congestion_control/bbr3_sender.h
+++ b/quiche/quic/core/congestion_control/bbr3_sender.h
@@ -199,6 +199,7 @@
   Bbr2NetworkModel model_;
 
   const QuicByteCount initial_cwnd_;
+  uint64_t max_ack_height_window_length_ = 10;
 
   // Current cwnd and pacing rate.
   QuicByteCount cwnd_;
diff --git a/quiche/quic/core/congestion_control/bbr3_simulator_test.cc b/quiche/quic/core/congestion_control/bbr3_simulator_test.cc
index 54369bd..c8b625f 100644
--- a/quiche/quic/core/congestion_control/bbr3_simulator_test.cc
+++ b/quiche/quic/core/congestion_control/bbr3_simulator_test.cc
@@ -379,7 +379,7 @@
   ASSERT_TRUE(simulator_result);
   const auto debug_state = sender_->ExportDebugState();
   EXPECT_EQ(Bbr2Mode::DRAIN, debug_state.mode);
-  EXPECT_EQ(3u, debug_state.round_trip_count - max_bw_round);
+  EXPECT_EQ(2u, debug_state.round_trip_count - max_bw_round);
   EXPECT_EQ(3u, debug_state.startup.round_trips_without_bandwidth_growth);
   EXPECT_EQ(0u, sender_connection_stats().packets_lost);
   EXPECT_APPROX_EQ(params.BottleneckBandwidth(), debug_state.bandwidth_hi,
@@ -387,97 +387,6 @@
   EXPECT_FALSE(debug_state.last_sample_is_app_limited);
 }
 
-TEST_F(Bbr3DefaultTopologyTest, NormalStartupB207) {
-  SetConnectionOption(kB207);
-  DefaultTopologyParams params;
-  CreateNetwork(params);
-
-  // Run until the full bandwidth is reached and check how many rounds it was.
-  sender_endpoint_.AddBytesToTransfer(12 * 1024 * 1024);
-  QuicRoundTripCount max_bw_round = 0;
-  QuicBandwidth max_bw(QuicBandwidth::Zero());
-  bool simulator_result = simulator_.RunUntilOrTimeout(
-      [this, &max_bw, &max_bw_round]() {
-        const auto debug_state = sender_->ExportDebugState();
-        if (max_bw < debug_state.bandwidth_hi) {
-          max_bw = debug_state.bandwidth_hi;
-          max_bw_round = debug_state.round_trip_count;
-        }
-        return debug_state.startup.full_bandwidth_reached;
-      },
-      QuicTime::Delta::FromSeconds(5));
-  ASSERT_TRUE(simulator_result);
-  const auto debug_state = sender_->ExportDebugState();
-  EXPECT_EQ(Bbr2Mode::DRAIN, debug_state.mode);
-  EXPECT_EQ(1u, debug_state.round_trip_count - max_bw_round);
-  EXPECT_EQ(1u, debug_state.startup.round_trips_without_bandwidth_growth);
-  EXPECT_APPROX_EQ(params.BottleneckBandwidth(), debug_state.bandwidth_hi,
-                   0.01f);
-  EXPECT_EQ(0u, sender_connection_stats().packets_lost);
-}
-
-// Add extra_acked to CWND in STARTUP and exit STARTUP on a persistent queue.
-TEST_F(Bbr3DefaultTopologyTest, NormalStartupB207andB205) {
-  SetConnectionOption(kB205);
-  SetConnectionOption(kB207);
-  DefaultTopologyParams params;
-  CreateNetwork(params);
-
-  // Run until the full bandwidth is reached and check how many rounds it was.
-  sender_endpoint_.AddBytesToTransfer(12 * 1024 * 1024);
-  QuicRoundTripCount max_bw_round = 0;
-  QuicBandwidth max_bw(QuicBandwidth::Zero());
-  bool simulator_result = simulator_.RunUntilOrTimeout(
-      [this, &max_bw, &max_bw_round]() {
-        const auto debug_state = sender_->ExportDebugState();
-        if (max_bw < debug_state.bandwidth_hi) {
-          max_bw = debug_state.bandwidth_hi;
-          max_bw_round = debug_state.round_trip_count;
-        }
-        return debug_state.startup.full_bandwidth_reached;
-      },
-      QuicTime::Delta::FromSeconds(5));
-  ASSERT_TRUE(simulator_result);
-  const auto debug_state = sender_->ExportDebugState();
-  EXPECT_EQ(Bbr2Mode::DRAIN, debug_state.mode);
-  EXPECT_EQ(1u, debug_state.round_trip_count - max_bw_round);
-  EXPECT_EQ(2u, debug_state.startup.round_trips_without_bandwidth_growth);
-  EXPECT_APPROX_EQ(params.BottleneckBandwidth(), debug_state.bandwidth_hi,
-                   0.01f);
-  EXPECT_EQ(0u, sender_connection_stats().packets_lost);
-}
-
-// Add extra_acked to CWND in STARTUP and exit STARTUP on a persistent queue.
-TEST_F(Bbr3DefaultTopologyTest, NormalStartupBB2S) {
-  SetConnectionOption(kBB2S);
-  DefaultTopologyParams params;
-  CreateNetwork(params);
-
-  // Run until the full bandwidth is reached and check how many rounds it was.
-  sender_endpoint_.AddBytesToTransfer(12 * 1024 * 1024);
-  QuicRoundTripCount max_bw_round = 0;
-  QuicBandwidth max_bw(QuicBandwidth::Zero());
-  bool simulator_result = simulator_.RunUntilOrTimeout(
-      [this, &max_bw, &max_bw_round]() {
-        const auto debug_state = sender_->ExportDebugState();
-        if (max_bw * 1.001 < debug_state.bandwidth_hi) {
-          max_bw = debug_state.bandwidth_hi;
-          max_bw_round = debug_state.round_trip_count;
-        }
-        return debug_state.startup.full_bandwidth_reached;
-      },
-      QuicTime::Delta::FromSeconds(5));
-  ASSERT_TRUE(simulator_result);
-  const auto debug_state = sender_->ExportDebugState();
-  EXPECT_EQ(Bbr2Mode::DRAIN, debug_state.mode);
-  // BB2S reduces 3 rounds without bandwidth growth to 2.
-  EXPECT_EQ(2u, debug_state.round_trip_count - max_bw_round);
-  EXPECT_EQ(2u, debug_state.startup.round_trips_without_bandwidth_growth);
-  EXPECT_APPROX_EQ(params.BottleneckBandwidth(), debug_state.bandwidth_hi,
-                   0.01f);
-  EXPECT_EQ(0u, sender_connection_stats().packets_lost);
-}
-
 // Test a simple long data transfer in the default setup.
 TEST_F(Bbr3DefaultTopologyTest, SimpleTransfer) {
   DefaultTopologyParams params;
@@ -568,25 +477,6 @@
   EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
 }
 
-TEST_F(Bbr3DefaultTopologyTest, SimpleTransferB207) {
-  SetConnectionOption(kB207);
-  DefaultTopologyParams params;
-  CreateNetwork(params);
-
-  // Transfer 12MB.
-  DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
-  EXPECT_TRUE(Bbr3ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
-
-  EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
-                   sender_->ExportDebugState().bandwidth_hi, 0.01f);
-
-  EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
-  // The margin here is high, because the aggregation greatly increases
-  // smoothed rtt.
-  EXPECT_GE(params.RTT() * 4, rtt_stats()->smoothed_rtt());
-  EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
-}
-
 TEST_F(Bbr3DefaultTopologyTest, SimpleTransferBBRB) {
   SetConnectionOption(kBBRB);
   DefaultTopologyParams params;
@@ -706,7 +596,7 @@
   EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
                    sender_->ExportDebugState().bandwidth_hi, 0.01f);
 
-  EXPECT_EQ(sender_loss_rate_in_packets(), 0);
+  EXPECT_LE(sender_loss_rate_in_packets(), 0.01);
   // 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());
@@ -1131,7 +1021,7 @@
   EXPECT_TRUE(simulator_result);
   // Ensure at least 18% of the bandwidth is discovered.
   EXPECT_APPROX_EQ(params.test_link.bandwidth,
-                   sender_->ExportDebugState().bandwidth_hi, 0.85f);
+                   sender_->ExportDebugState().bandwidth_hi, 0.90f);
 }
 
 // Test Bbr2's reaction to a 100x bandwidth increase during a transfer with B204
@@ -1408,7 +1298,7 @@
   CreateNetwork(params);
 
   DriveOutOfStartup(params);
-  EXPECT_LE(sender_loss_rate_in_packets(), 0.0575);
+  EXPECT_LE(sender_loss_rate_in_packets(), 0.1);
   // bandwidth_lo is cleared exiting STARTUP.
   EXPECT_EQ(sender_->ExportDebugState().bandwidth_lo,
             QuicBandwidth::Infinite());
@@ -1438,7 +1328,7 @@
   CreateNetwork(params);
 
   DriveOutOfStartup(params);
-  EXPECT_LE(sender_loss_rate_in_packets(), 0.065);
+  EXPECT_LE(sender_loss_rate_in_packets(), 0.085);
   // bandwidth_lo is cleared exiting STARTUP.
   EXPECT_EQ(sender_->ExportDebugState().bandwidth_lo,
             QuicBandwidth::Infinite());
@@ -1453,7 +1343,7 @@
   CreateNetwork(params);
 
   DriveOutOfStartup(params);
-  EXPECT_LE(sender_loss_rate_in_packets(), 0.065);
+  EXPECT_LE(sender_loss_rate_in_packets(), 0.085);
   // bandwidth_lo is cleared exiting STARTUP.
   EXPECT_EQ(sender_->ExportDebugState().bandwidth_lo,
             QuicBandwidth::Infinite());
@@ -1515,7 +1405,7 @@
       timeout);
   ASSERT_TRUE(simulator_result);
   ASSERT_EQ(Bbr2Mode::DRAIN, sender_->ExportDebugState().mode);
-  EXPECT_APPROX_EQ(sender_->BandwidthEstimate() * (1 / 2.885f),
+  EXPECT_APPROX_EQ(sender_->BandwidthEstimate() * (1 / kDerivedDefaultCwndGain),
                    sender_->PacingRate(0), 0.01f);
 
   // BBR uses CWND gain of 2 during STARTUP, hence it will fill the buffer with
@@ -1636,7 +1526,7 @@
   const auto debug_state = sender_->ExportDebugState();
   EXPECT_EQ(Bbr2Mode::DRAIN, debug_state.mode);
   EXPECT_GE(2u, debug_state.round_trip_count - max_bw_round);
-  EXPECT_EQ(1u, debug_state.startup.round_trips_without_bandwidth_growth);
+  EXPECT_EQ(2u, debug_state.startup.round_trips_without_bandwidth_growth);
   EXPECT_NE(0u, sender_connection_stats().packets_lost);
   EXPECT_FALSE(debug_state.last_sample_is_app_limited);
 
@@ -1669,7 +1559,7 @@
   const auto debug_state = sender_->ExportDebugState();
   EXPECT_EQ(Bbr2Mode::DRAIN, debug_state.mode);
   EXPECT_GE(2u, debug_state.round_trip_count - max_bw_round);
-  EXPECT_EQ(1u, debug_state.startup.round_trips_without_bandwidth_growth);
+  EXPECT_EQ(2u, debug_state.startup.round_trips_without_bandwidth_growth);
   EXPECT_NE(0u, sender_connection_stats().packets_lost);
   EXPECT_FALSE(debug_state.last_sample_is_app_limited);
 
@@ -1687,7 +1577,7 @@
 
   SetConnectionOption(kB2NE);
   DefaultTopologyParams params;
-  params.switch_queue_capacity_in_bdp = 0.5;
+  params.switch_queue_capacity_in_bdp = 0.1;
   CreateNetwork(params);
 
   // Run until the full bandwidth is reached and check how many rounds it was.
@@ -1707,7 +1597,6 @@
   ASSERT_TRUE(simulator_result);
   const auto debug_state = sender_->ExportDebugState();
   EXPECT_EQ(Bbr2Mode::DRAIN, debug_state.mode);
-  EXPECT_EQ(debug_state.round_trip_count, max_bw_round);
   EXPECT_EQ(0u, debug_state.startup.round_trips_without_bandwidth_growth);
   EXPECT_NE(0u, sender_connection_stats().packets_lost);
 }