gfe-relnote: Mimic QUIC BBRv2 style loss based exit in BBRv1 code. Protected by --gfe2_reloadable_flag_quic_bbr_loss_based_startup_exit _and_ connection option 'LRTT'.

PiperOrigin-RevId: 289688810
Change-Id: Iaaf5b4dfade2a75fc40777b578b54558648fcacf
diff --git a/quic/core/congestion_control/bbr_sender.cc b/quic/core/congestion_control/bbr_sender.cc
index 6ef3572..b4566bd 100644
--- a/quic/core/congestion_control/bbr_sender.cc
+++ b/quic/core/congestion_control/bbr_sender.cc
@@ -93,6 +93,8 @@
       mode_(STARTUP),
       sampler_(unacked_packets, kBandwidthWindowSize),
       round_trip_count_(0),
+      num_loss_events_in_round_(0),
+      bytes_lost_in_round_(0),
       max_bandwidth_(kBandwidthWindowSize, QuicBandwidth::Zero(), 0),
       min_rtt_(QuicTime::Delta::Zero()),
       min_rtt_timestamp_(QuicTime::Zero()),
@@ -138,7 +140,13 @@
       min_rtt_since_last_probe_rtt_(QuicTime::Delta::Infinite()),
       network_parameters_adjusted_(false),
       bytes_lost_with_network_parameters_adjusted_(0),
-      bytes_lost_multiplier_with_network_parameters_adjusted_(2) {
+      bytes_lost_multiplier_with_network_parameters_adjusted_(2),
+      loss_based_startup_exit_(
+          GetQuicReloadableFlag(quic_bbr_loss_based_startup_exit) &&
+          sampler_.one_bw_sample_per_ack_event()) {
+  if (loss_based_startup_exit_) {
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_loss_based_startup_exit, 1, 2);
+  }
   if (stats_) {
     // Clear some startup stats if |stats_| has been used by another sender,
     // which happens e.g. when QuicConnection switch send algorithms.
@@ -253,8 +261,10 @@
 
 void BbrSender::SetFromConfig(const QuicConfig& config,
                               Perspective perspective) {
-  if (config.HasClientRequestedIndependentOption(kLRTT, perspective)) {
+  if (loss_based_startup_exit_ &&
+      config.HasClientRequestedIndependentOption(kLRTT, perspective)) {
     exit_startup_on_loss_ = true;
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr_loss_based_startup_exit, 2, 2);
   }
   if (config.HasClientRequestedIndependentOption(k1RTT, perspective)) {
     num_startup_rtts_ = 1;
@@ -410,6 +420,11 @@
   QuicByteCount excess_acked = 0;
   QuicByteCount bytes_lost = 0;
 
+  // The send state of the largest packet in acked_packets, unless it is
+  // empty. If acked_packets is empty, it's the send state of the largest
+  // packet in lost_packets.
+  SendTimeState last_packet_send_state;
+
   if (!sampler_.one_bw_sample_per_ack_event()) {
     DiscardLostPackets(lost_packets);
 
@@ -462,6 +477,12 @@
       }
     }
     excess_acked = sample.extra_acked;
+    last_packet_send_state = sample.last_packet_send_state;
+
+    if (loss_based_startup_exit_ && !lost_packets.empty()) {
+      ++num_loss_events_in_round_;
+      bytes_lost_in_round_ += bytes_lost;
+    }
   }
 
   // Handle logic specific to PROBE_BW mode.
@@ -471,7 +492,7 @@
 
   // Handle logic specific to STARTUP and DRAIN modes.
   if (is_round_start && !is_at_full_bandwidth_) {
-    CheckIfFullBandwidthReached();
+    CheckIfFullBandwidthReached(last_packet_send_state);
   }
   MaybeExitStartupOrDrain(event_time);
 
@@ -495,6 +516,10 @@
 
   // Cleanup internal state.
   sampler_.RemoveObsoletePackets(unacked_packets_->GetLeastUnacked());
+  if (loss_based_startup_exit_ && is_round_start) {
+    num_loss_events_in_round_ = 0;
+    bytes_lost_in_round_ = 0;
+  }
 }
 
 CongestionControlType BbrSender::GetCongestionControlType() const {
@@ -700,7 +725,8 @@
   }
 }
 
-void BbrSender::CheckIfFullBandwidthReached() {
+void BbrSender::CheckIfFullBandwidthReached(
+    const SendTimeState& last_packet_send_state) {
   if (last_sample_is_app_limited_) {
     return;
   }
@@ -717,7 +743,8 @@
   }
 
   rounds_without_bandwidth_gain_++;
-  if (rounds_without_bandwidth_gain_ >= num_startup_rtts_) {
+  if ((rounds_without_bandwidth_gain_ >= num_startup_rtts_) ||
+      ShouldExitStartupDueToLoss(last_packet_send_state)) {
     DCHECK(has_non_app_limited_sample_);
     is_at_full_bandwidth_ = true;
   }
@@ -743,6 +770,29 @@
   }
 }
 
+bool BbrSender::ShouldExitStartupDueToLoss(
+    const SendTimeState& last_packet_send_state) const {
+  if (!exit_startup_on_loss_) {
+    return false;
+  }
+
+  if (num_loss_events_in_round_ <
+          GetQuicFlag(FLAGS_quic_bbr2_default_startup_full_loss_count) ||
+      !last_packet_send_state.is_valid) {
+    return false;
+  }
+
+  const QuicByteCount inflight_at_send = last_packet_send_state.bytes_in_flight;
+
+  if (inflight_at_send > 0 && bytes_lost_in_round_ > 0) {
+    return bytes_lost_in_round_ >
+           inflight_at_send *
+               GetQuicFlag(FLAGS_quic_bbr2_default_loss_threshold);
+  }
+
+  return false;
+}
+
 void BbrSender::MaybeEnterOrExitProbeRtt(QuicTime now,
                                          bool is_round_start,
                                          bool min_rtt_expired) {
@@ -791,6 +841,11 @@
 void BbrSender::UpdateRecoveryState(QuicPacketNumber last_acked_packet,
                                     bool has_losses,
                                     bool is_round_start) {
+  // Disable recovery in startup, if loss-based exit is enabled.
+  if (exit_startup_on_loss_ && !is_at_full_bandwidth_) {
+    return;
+  }
+
   // Exit recovery when there are no losses for a round.
   if (has_losses) {
     end_recovery_at_ = last_sent_packet_;
diff --git a/quic/core/congestion_control/bbr_sender.h b/quic/core/congestion_control/bbr_sender.h
index 3db423d..fa50de8 100644
--- a/quic/core/congestion_control/bbr_sender.h
+++ b/quic/core/congestion_control/bbr_sender.h
@@ -212,7 +212,7 @@
                             bool has_losses);
   // Tracks for how many round-trips the bandwidth has not increased
   // significantly.
-  void CheckIfFullBandwidthReached();
+  void CheckIfFullBandwidthReached(const SendTimeState& last_packet_send_state);
   // Transitions from STARTUP to DRAIN and from DRAIN to PROBE_BW if
   // appropriate.
   void MaybeExitStartupOrDrain(QuicTime now);
@@ -249,6 +249,10 @@
   // Called right before exiting STARTUP.
   void OnExitStartup(QuicTime now);
 
+  // Return whether we should exit STARTUP due to excessive loss.
+  bool ShouldExitStartupDueToLoss(
+      const SendTimeState& last_packet_send_state) const;
+
   const RttStats* rtt_stats_;
   const QuicUnackedPacketMap* unacked_packets_;
   QuicRandom* random_;
@@ -269,6 +273,12 @@
   // the round trip counter to advance.
   QuicPacketNumber current_round_trip_end_;
 
+  // Number of congestion events with some losses, in the current round.
+  int64_t num_loss_events_in_round_;
+
+  // Number of total bytes lost in the current round.
+  QuicByteCount bytes_lost_in_round_;
+
   // The filter that tracks the maximum bandwidth over the multiple recent
   // round-trips.
   MaxBandwidthFilter max_bandwidth_;
@@ -313,8 +323,10 @@
   const float congestion_window_gain_constant_;
   // The number of RTTs to stay in STARTUP mode.  Defaults to 3.
   QuicRoundTripCount num_startup_rtts_;
-  // If true, exit startup if 1RTT has passed with no bandwidth increase and
-  // the connection is in recovery.
+  // If true, exit startup if all of the following conditions are met:
+  // - 1RTT has passed with no bandwidth increase,
+  // - Some number of congestion events happened with loss, in the last round.
+  // - Some amount of inflight bytes (at the start of the last round) are lost.
   bool exit_startup_on_loss_;
 
   // Number of round-trips in PROBE_BW mode, used for determining the current
@@ -402,6 +414,10 @@
   // bytes_lost_with_network_parameters_adjusted_ *
   // bytes_lost_multiplier_with_network_parameters_adjusted_ > IW.
   uint8_t bytes_lost_multiplier_with_network_parameters_adjusted_;
+
+  // Latched value of --quic_bbr_loss_based_startup_exit &&
+  // sampler_.one_bw_sample_per_ack_event().
+  const bool loss_based_startup_exit_;
 };
 
 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 2ff1f8a..beb4bff 100644
--- a/quic/core/congestion_control/bbr_sender_test.cc
+++ b/quic/core/congestion_control/bbr_sender_test.cc
@@ -990,7 +990,13 @@
 }
 
 // Test exiting STARTUP earlier upon loss due to the LRTT connection option.
-TEST_F(BbrSenderTest, DISABLED_SimpleTransferLRTTStartup) {
+TEST_F(BbrSenderTest, SimpleTransferLRTTStartup) {
+  if (!GetQuicReloadableFlag(
+          quic_bw_sampler_remove_packets_once_per_congestion_event2) ||
+      !GetQuicReloadableFlag(quic_one_bw_sample_per_ack_event2) ||
+      !GetQuicReloadableFlag(quic_bbr_loss_based_startup_exit)) {
+    return;
+  }
   CreateDefaultSetup();
 
   SetConnectionOption(kLRTT);
@@ -1018,7 +1024,13 @@
 }
 
 // Test exiting STARTUP earlier upon loss due to the LRTT connection option.
-TEST_F(BbrSenderTest, DISABLED_SimpleTransferLRTTStartupSmallBuffer) {
+TEST_F(BbrSenderTest, SimpleTransferLRTTStartupSmallBuffer) {
+  if (!GetQuicReloadableFlag(
+          quic_bw_sampler_remove_packets_once_per_congestion_event2) ||
+      !GetQuicReloadableFlag(quic_one_bw_sample_per_ack_event2) ||
+      !GetQuicReloadableFlag(quic_bbr_loss_based_startup_exit)) {
+    return;
+  }
   CreateSmallBufferSetup();
 
   SetConnectionOption(kLRTT);