Use max(max_bw, send_rate) as the estimated bandwidth in QUIC's MaxAckHeightTracker. Enabled by the BBRB connection option.

Protected by FLAGS_quic_reloadable_flag_quic_bbr_use_send_rate_in_max_ack_height_tracker.

PiperOrigin-RevId: 400206122
diff --git a/quic/core/congestion_control/bandwidth_sampler.cc b/quic/core/congestion_control/bandwidth_sampler.cc
index 81f9e91..49f2b72 100644
--- a/quic/core/congestion_control/bandwidth_sampler.cc
+++ b/quic/core/congestion_control/bandwidth_sampler.cc
@@ -112,7 +112,8 @@
       unacked_packet_map_(unacked_packet_map),
       max_ack_height_tracker_(max_height_tracker_window_length),
       total_bytes_acked_after_last_ack_event_(0),
-      overestimate_avoidance_(false) {}
+      overestimate_avoidance_(false),
+      limit_max_ack_height_tracker_by_send_rate_(false) {}
 
 BandwidthSampler::BandwidthSampler(const BandwidthSampler& other)
     : total_bytes_sent_(other.total_bytes_sent_),
@@ -135,7 +136,9 @@
       max_ack_height_tracker_(other.max_ack_height_tracker_),
       total_bytes_acked_after_last_ack_event_(
           other.total_bytes_acked_after_last_ack_event_),
-      overestimate_avoidance_(other.overestimate_avoidance_) {}
+      overestimate_avoidance_(other.overestimate_avoidance_),
+      limit_max_ack_height_tracker_by_send_rate_(
+          other.limit_max_ack_height_tracker_by_send_rate_) {}
 
 void BandwidthSampler::EnableOverestimateAvoidance() {
   if (overestimate_avoidance_) {
@@ -264,6 +267,7 @@
   }
 
   SendTimeState last_acked_packet_send_state;
+  QuicBandwidth max_send_rate = QuicBandwidth::Zero();
   for (const auto& packet : acked_packets) {
     BandwidthSample sample =
         OnPacketAcknowledged(ack_time, packet.packet_number);
@@ -280,6 +284,9 @@
       event_sample.sample_max_bandwidth = sample.bandwidth;
       event_sample.sample_is_app_limited = sample.state_at_send.is_app_limited;
     }
+    if (!sample.send_rate.IsInfinite()) {
+      max_send_rate = std::max(max_send_rate, sample.send_rate);
+    }
     const QuicByteCount inflight_sample =
         total_bytes_acked() - last_acked_packet_send_state.total_bytes_acked;
     if (inflight_sample > event_sample.sample_max_inflight) {
@@ -303,6 +310,9 @@
   }
 
   max_bandwidth = std::max(max_bandwidth, event_sample.sample_max_bandwidth);
+  if (limit_max_ack_height_tracker_by_send_rate_) {
+    max_bandwidth = std::max(max_bandwidth, max_send_rate);
+  }
   event_sample.extra_acked = OnAckEventEnd(
       std::min(est_bandwidth_upper_bound, max_bandwidth), round_trip_count);
 
@@ -433,6 +443,7 @@
   // means that the RTT measurements here can be artificially high, especially
   // on low bandwidth connections.
   sample.rtt = ack_time - sent_packet.sent_time;
+  sample.send_rate = send_rate;
   SentPacketToSendTimeState(sent_packet, &sample.state_at_send);
 
   if (sample.bandwidth.IsZero()) {
diff --git a/quic/core/congestion_control/bandwidth_sampler.h b/quic/core/congestion_control/bandwidth_sampler.h
index 4c72939..bef8250 100644
--- a/quic/core/congestion_control/bandwidth_sampler.h
+++ b/quic/core/congestion_control/bandwidth_sampler.h
@@ -81,17 +81,18 @@
 struct QUIC_EXPORT_PRIVATE BandwidthSample {
   // The bandwidth at that particular sample. Zero if no valid bandwidth sample
   // is available.
-  QuicBandwidth bandwidth;
+  QuicBandwidth bandwidth = QuicBandwidth::Zero();
 
   // The RTT measurement at this particular sample.  Zero if no RTT sample is
   // available.  Does not correct for delayed ack time.
-  QuicTime::Delta rtt;
+  QuicTime::Delta rtt = QuicTime::Delta::Zero();
+
+  // |send_rate| is computed from the current packet being acked('P') and an
+  // earlier packet that is acked before P was sent.
+  QuicBandwidth send_rate = QuicBandwidth::Infinite();
 
   // States captured when the packet was sent.
   SendTimeState state_at_send;
-
-  BandwidthSample()
-      : bandwidth(QuicBandwidth::Zero()), rtt(QuicTime::Delta::Zero()) {}
 };
 
 // MaxAckHeightTracker is part of the BandwidthSampler. It is called after every
@@ -368,6 +369,10 @@
     max_ack_height_tracker_.SetStartNewAggregationEpochAfterFullRound(value);
   }
 
+  void SetLimitMaxAckHeightTrackerBySendRate(bool value) {
+    limit_max_ack_height_tracker_by_send_rate_ = value;
+  }
+
   // AckPoint represents a point on the ack line.
   struct QUIC_NO_EXPORT AckPoint {
     QuicTime ack_time = QuicTime::Zero();
@@ -579,6 +584,9 @@
 
   // True if connection option 'BSAO' is set.
   bool overestimate_avoidance_;
+
+  // True if connection option 'BBRB' is set.
+  bool limit_max_ack_height_tracker_by_send_rate_;
 };
 
 }  // namespace quic
diff --git a/quic/core/congestion_control/bbr2_misc.h b/quic/core/congestion_control/bbr2_misc.h
index b3a5679..fb66b98 100644
--- a/quic/core/congestion_control/bbr2_misc.h
+++ b/quic/core/congestion_control/bbr2_misc.h
@@ -422,6 +422,10 @@
     bandwidth_sampler_.SetStartNewAggregationEpochAfterFullRound(value);
   }
 
+  void SetLimitMaxAckHeightTrackerBySendRate(bool value) {
+    bandwidth_sampler_.SetLimitMaxAckHeightTrackerBySendRate(value);
+  }
+
   bool MaybeExpireMinRtt(const Bbr2CongestionEvent& congestion_event);
 
   QuicBandwidth BandwidthEstimate() const {
diff --git a/quic/core/congestion_control/bbr2_sender.cc b/quic/core/congestion_control/bbr2_sender.cc
index b0f664f..f1c96d6 100644
--- a/quic/core/congestion_control/bbr2_sender.cc
+++ b/quic/core/congestion_control/bbr2_sender.cc
@@ -170,6 +170,12 @@
       ContainsQuicTag(connection_options, kBBRA)) {
     model_.SetStartNewAggregationEpochAfterFullRound(true);
   }
+  if (GetQuicReloadableFlag(quic_bbr_use_send_rate_in_max_ack_height_tracker) &&
+      ContainsQuicTag(connection_options, kBBRB)) {
+    QUIC_RELOADABLE_FLAG_COUNT_N(
+        quic_bbr_use_send_rate_in_max_ack_height_tracker, 2, 2);
+    model_.SetLimitMaxAckHeightTrackerBySendRate(true);
+  }
   if (GetQuicReloadableFlag(
           quic_bbr2_add_bytes_acked_after_inflight_hi_limited) &&
       ContainsQuicTag(connection_options, kBBQ0)) {
diff --git a/quic/core/congestion_control/bbr2_simulator_test.cc b/quic/core/congestion_control/bbr2_simulator_test.cc
index 4d9e020..3b08905 100644
--- a/quic/core/congestion_control/bbr2_simulator_test.cc
+++ b/quic/core/congestion_control/bbr2_simulator_test.cc
@@ -463,6 +463,26 @@
   EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
 }
 
+TEST_F(Bbr2DefaultTopologyTest, SimpleTransferBBRB) {
+  SetQuicReloadableFlag(quic_bbr_use_send_rate_in_max_ack_height_tracker, true);
+  SetConnectionOption(kBBRB);
+  DefaultTopologyParams params;
+  CreateNetwork(params);
+
+  // Transfer 12MB.
+  DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
+  EXPECT_TRUE(Bbr2ModeIsOneOf({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(Bbr2DefaultTopologyTest, SimpleTransferSmallBuffer) {
   DefaultTopologyParams params;
   params.switch_queue_capacity_in_bdp = 0.5;
diff --git a/quic/core/congestion_control/bbr_sender.cc b/quic/core/congestion_control/bbr_sender.cc
index 4d6ae70..ba7a357 100644
--- a/quic/core/congestion_control/bbr_sender.cc
+++ b/quic/core/congestion_control/bbr_sender.cc
@@ -283,6 +283,12 @@
       ContainsQuicTag(connection_options, kBBRA)) {
     sampler_.SetStartNewAggregationEpochAfterFullRound(true);
   }
+  if (GetQuicReloadableFlag(quic_bbr_use_send_rate_in_max_ack_height_tracker) &&
+      ContainsQuicTag(connection_options, kBBRB)) {
+    QUIC_RELOADABLE_FLAG_COUNT_N(
+        quic_bbr_use_send_rate_in_max_ack_height_tracker, 1, 2);
+    sampler_.SetLimitMaxAckHeightTrackerBySendRate(true);
+  }
 }
 
 void BbrSender::AdjustNetworkParameters(const NetworkParams& params) {
diff --git a/quic/core/congestion_control/bbr_sender_test.cc b/quic/core/congestion_control/bbr_sender_test.cc
index ed57eb4..754cf20 100644
--- a/quic/core/congestion_control/bbr_sender_test.cc
+++ b/quic/core/congestion_control/bbr_sender_test.cc
@@ -329,6 +329,39 @@
   EXPECT_APPROX_EQ(kTestRtt, rtt_stats_->smoothed_rtt(), 0.2f);
 }
 
+TEST_F(BbrSenderTest, SimpleTransferBBRB) {
+  SetQuicReloadableFlag(quic_bbr_use_send_rate_in_max_ack_height_tracker, true);
+  SetConnectionOption(kBBRB);
+  CreateDefaultSetup();
+
+  // At startup make sure we are at the default.
+  EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
+  // At startup make sure we can send.
+  EXPECT_TRUE(sender_->CanSend(0));
+  // And that window is un-affected.
+  EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
+
+  // Verify that Sender is in slow start.
+  EXPECT_TRUE(sender_->InSlowStart());
+
+  // Verify that pacing rate is based on the initial RTT.
+  QuicBandwidth expected_pacing_rate = QuicBandwidth::FromBytesAndTimeDelta(
+      2.885 * kDefaultWindowTCP, rtt_stats_->initial_rtt());
+  EXPECT_APPROX_EQ(expected_pacing_rate.ToBitsPerSecond(),
+                   sender_->PacingRate(0).ToBitsPerSecond(), 0.01f);
+
+  ASSERT_GE(kTestBdp, kDefaultWindowTCP + kDefaultTCPMSS);
+
+  DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(30));
+  EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
+  EXPECT_EQ(0u, bbr_sender_.connection()->GetStats().packets_lost);
+  EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
+
+  // The margin here is quite high, since there exists a possibility that the
+  // connection just exited high gain cycle.
+  EXPECT_APPROX_EQ(kTestRtt, rtt_stats_->smoothed_rtt(), 0.2f);
+}
+
 // Test a simple transfer in a situation when the buffer is less than BDP.
 TEST_F(BbrSenderTest, SimpleTransferSmallBuffer) {
   CreateSmallBufferSetup();
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index 7719644..2afa363 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -102,6 +102,8 @@
 const QuicTag kBBRA = TAG('B', 'B', 'R', 'A');   // Starts a new ack aggregation
                                                  // epoch if a full round has
                                                  // passed
+const QuicTag kBBRB = TAG('B', 'B', 'R', 'B');   // Use send rate in BBR's
+                                                 // MaxAckHeightTracker
 const QuicTag kBBRS = TAG('B', 'B', 'R', 'S');   // DEPRECATED
 const QuicTag kBBQ1 = TAG('B', 'B', 'Q', '1');   // BBR with lower 2.77 STARTUP
                                                  // pacing and CWND gain.
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 70e8e8e..727f1e2 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -107,6 +107,8 @@
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_suppress_write_mid_packet_processing, true)
 // If true, use BBRv2 as the default congestion controller. Takes precedence over --quic_default_to_bbr.
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_default_to_bbr_v2, false)
+// If true, use max(max_bw, send_rate) as the estimated bandwidth in QUIC\'s MaxAckHeightTracker.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr_use_send_rate_in_max_ack_height_tracker, true)
 // If true, use new connection ID in connection migration.
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_connection_migration_use_new_cid_v2, true)
 // If true, uses conservative cwnd gain and pacing gain when cwnd gets bootstrapped.