Internal QUICHE change

PiperOrigin-RevId: 296522161
Change-Id: I078e53c1ad6b63b052fc1f0993ebfd5097aa69dd
diff --git a/quic/core/congestion_control/bandwidth_sampler.cc b/quic/core/congestion_control/bandwidth_sampler.cc
index 6272664..1a56e62 100644
--- a/quic/core/congestion_control/bandwidth_sampler.cc
+++ b/quic/core/congestion_control/bandwidth_sampler.cc
@@ -32,18 +32,19 @@
   // Reset the current aggregation epoch as soon as the ack arrival rate is less
   // than or equal to the max bandwidth.
   if (aggregation_epoch_bytes_ <=
-      GetQuicFlag(FLAGS_quic_ack_aggregation_bandwidth_threshold) *
-          expected_bytes_acked) {
+      ack_aggregation_bandwidth_threshold_ * expected_bytes_acked) {
     QUIC_DVLOG(3) << "Starting a new aggregation epoch because "
                      "aggregation_epoch_bytes_ "
                   << aggregation_epoch_bytes_
                   << " is smaller than expected. "
-                     "quic_ack_aggregation_bandwidth_threshold:"
-                  << GetQuicFlag(FLAGS_quic_ack_aggregation_bandwidth_threshold)
+                     "ack_aggregation_bandwidth_threshold_:"
+                  << ack_aggregation_bandwidth_threshold_
                   << ", expected_bytes_acked:" << expected_bytes_acked
                   << ", bandwidth_estimate:" << bandwidth_estimate
                   << ", aggregation_duration:"
-                  << (ack_time - aggregation_epoch_start_time_);
+                  << (ack_time - aggregation_epoch_start_time_)
+                  << ", new_aggregation_epoch:" << ack_time
+                  << ", new_aggregation_bytes_acked:" << bytes_acked;
     // Reset to start measuring a new aggregation epoch.
     aggregation_epoch_bytes_ = bytes_acked;
     aggregation_epoch_start_time_ = ack_time;
@@ -82,7 +83,8 @@
       max_tracked_packets_(GetQuicFlag(FLAGS_quic_max_tracked_packet_count)),
       unacked_packet_map_(unacked_packet_map),
       max_ack_height_tracker_(max_height_tracker_window_length),
-      total_bytes_acked_after_last_ack_event_(0) {
+      total_bytes_acked_after_last_ack_event_(0),
+      overestimate_avoidance_(false) {
   if (remove_packets_once_per_congestion_event_) {
     QUIC_RELOADABLE_FLAG_COUNT(
         quic_bw_sampler_remove_packets_once_per_congestion_event2);
@@ -92,6 +94,17 @@
   }
 }
 
+void BandwidthSampler::EnableOverestimateAvoidance() {
+  if (overestimate_avoidance_) {
+    return;
+  }
+
+  overestimate_avoidance_ = true;
+  // TODO(wub): Change the default value of
+  // --quic_ack_aggregation_bandwidth_threshold to 2.0.
+  max_ack_height_tracker_.SetAckAggregationBandwidthThreshold(2.0);
+}
+
 BandwidthSampler::~BandwidthSampler() {}
 
 void BandwidthSampler::OnPacketSent(
@@ -116,6 +129,12 @@
   // importantly at the beginning of the connection.
   if (bytes_in_flight == 0) {
     last_acked_packet_ack_time_ = sent_time;
+    if (overestimate_avoidance_) {
+      recent_ack_points_.Clear();
+      recent_ack_points_.Update(sent_time, total_bytes_acked_);
+      a0_candidates_.clear();
+      a0_candidates_.push_back(recent_ack_points_.MostRecentPoint());
+    }
     total_bytes_sent_at_last_acked_packet_ = total_bytes_sent_;
 
     // In this situation ack compression is not a concern, set send rate to
@@ -240,9 +259,17 @@
   }
   total_bytes_acked_after_last_ack_event_ = total_bytes_acked_;
 
-  return max_ack_height_tracker_.Update(bandwidth_estimate, round_trip_count,
-                                        last_acked_packet_ack_time_,
-                                        newly_acked_bytes);
+  QuicByteCount extra_acked = max_ack_height_tracker_.Update(
+      bandwidth_estimate, round_trip_count, last_acked_packet_ack_time_,
+      newly_acked_bytes);
+  // If |extra_acked| is zero, i.e. this ack event marks the start of a new ack
+  // aggregation epoch, save LessRecentPoint, which is the last ack point of the
+  // previous epoch, as a A0 candidate.
+  if (overestimate_avoidance_ && extra_acked == 0) {
+    a0_candidates_.push_back(recent_ack_points_.LessRecentPoint());
+    QUIC_DVLOG(1) << "New a0_candidate:" << a0_candidates_.back();
+  }
+  return extra_acked;
 }
 
 BandwidthSample BandwidthSampler::OnPacketAcknowledged(
@@ -271,6 +298,9 @@
       sent_packet.send_time_state.total_bytes_sent;
   last_acked_packet_sent_time_ = sent_packet.sent_time;
   last_acked_packet_ack_time_ = ack_time;
+  if (overestimate_avoidance_) {
+    recent_ack_points_.Update(ack_time, total_bytes_acked_);
+  }
 
   // Exit app-limited phase once a packet that was sent while the connection is
   // not app-limited is acknowledged.
@@ -296,13 +326,22 @@
         sent_packet.sent_time - sent_packet.last_acked_packet_sent_time);
   }
 
+  AckPoint a0;
+  if (overestimate_avoidance_ &&
+      ChooseA0Point(sent_packet.send_time_state.total_bytes_acked, &a0)) {
+    QUIC_DVLOG(2) << "Using a0 point: " << a0;
+  } else {
+    a0.ack_time = sent_packet.last_acked_packet_ack_time,
+    a0.total_bytes_acked = sent_packet.send_time_state.total_bytes_acked;
+  }
+
   // During the slope calculation, ensure that ack time of the current packet is
   // always larger than the time of the previous packet, otherwise division by
   // zero or integer underflow can occur.
-  if (ack_time <= sent_packet.last_acked_packet_ack_time) {
+  if (ack_time <= a0.ack_time) {
     // TODO(wub): Compare this code count before and after fixing clock jitter
     // issue.
-    if (sent_packet.last_acked_packet_ack_time == sent_packet.sent_time) {
+    if (a0.ack_time == sent_packet.sent_time) {
       // This is the 1st packet after quiescense.
       QUIC_CODE_COUNT_N(quic_prev_ack_time_larger_than_current_ack_time, 1, 2);
     } else {
@@ -310,14 +349,13 @@
     }
     QUIC_LOG_EVERY_N_SEC(ERROR, 60)
         << "Time of the previously acked packet:"
-        << sent_packet.last_acked_packet_ack_time.ToDebuggingValue()
+        << a0.ack_time.ToDebuggingValue()
         << " is larger than the ack time of the current packet:"
         << ack_time.ToDebuggingValue();
     return BandwidthSample();
   }
   QuicBandwidth ack_rate = QuicBandwidth::FromBytesAndTimeDelta(
-      total_bytes_acked_ - sent_packet.send_time_state.total_bytes_acked,
-      ack_time - sent_packet.last_acked_packet_ack_time);
+      total_bytes_acked_ - a0.total_bytes_acked, ack_time - a0.ack_time);
 
   BandwidthSample sample;
   sample.bandwidth = std::min(send_rate, ack_rate);
@@ -329,6 +367,35 @@
   return sample;
 }
 
+bool BandwidthSampler::ChooseA0Point(QuicByteCount total_bytes_acked,
+                                     AckPoint* a0) {
+  if (a0_candidates_.empty()) {
+    QUIC_BUG << "No A0 point candicates. total_bytes_acked:"
+             << total_bytes_acked;
+    return false;
+  }
+
+  if (a0_candidates_.size() == 1) {
+    *a0 = a0_candidates_.front();
+    return true;
+  }
+
+  for (size_t i = 1; i < a0_candidates_.size(); ++i) {
+    if (a0_candidates_[i].total_bytes_acked > total_bytes_acked) {
+      *a0 = a0_candidates_[i - 1];
+      if (i > 1) {
+        a0_candidates_.pop_front_n(i - 1);
+      }
+      return true;
+    }
+  }
+
+  // All candidates' total_bytes_acked is <= |total_bytes_acked|.
+  *a0 = a0_candidates_.back();
+  a0_candidates_.pop_front_n(a0_candidates_.size() - 1);
+  return true;
+}
+
 SendTimeState BandwidthSampler::OnPacketLost(QuicPacketNumber packet_number,
                                              QuicPacketLength bytes_lost) {
   // TODO(vasilvv): see the comment for the case of missing packets in
diff --git a/quic/core/congestion_control/bandwidth_sampler.h b/quic/core/congestion_control/bandwidth_sampler.h
index 90b20fc..210a219 100644
--- a/quic/core/congestion_control/bandwidth_sampler.h
+++ b/quic/core/congestion_control/bandwidth_sampler.h
@@ -9,6 +9,7 @@
 #include "net/third_party/quiche/src/quic/core/congestion_control/windowed_filter.h"
 #include "net/third_party/quiche/src/quic/core/packet_number_indexed_queue.h"
 #include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
+#include "net/third_party/quiche/src/quic/core/quic_circular_deque.h"
 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
 #include "net/third_party/quiche/src/quic/core/quic_time.h"
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
@@ -110,6 +111,14 @@
     max_ack_height_filter_.Reset(new_height, new_time);
   }
 
+  void SetAckAggregationBandwidthThreshold(double threshold) {
+    ack_aggregation_bandwidth_threshold_ = threshold;
+  }
+
+  double ack_aggregation_bandwidth_threshold() const {
+    return ack_aggregation_bandwidth_threshold_;
+  }
+
   uint64_t num_ack_aggregation_epochs() const {
     return num_ack_aggregation_epochs_;
   }
@@ -130,6 +139,8 @@
   // The number of ack aggregation epochs ever started, including the ongoing
   // one. Stats only.
   uint64_t num_ack_aggregation_epochs_ = 0;
+  double ack_aggregation_bandwidth_threshold_ =
+      GetQuicFlag(FLAGS_quic_ack_aggregation_bandwidth_threshold);
 };
 
 // An interface common to any class that can provide bandwidth samples from the
@@ -245,8 +256,8 @@
 // For that purpose, BandwidthSampler always keeps track of the most recently
 // acknowledged packet, and records it together with every outgoing packet.
 // When a packet gets acknowledged (A_1), it has not only information about when
-// it itself was sent (S_1), but also the information about the latest
-// acknowledged packet right before it was sent (S_0 and A_0).
+// it itself was sent (S_1), but also the information about a previously
+// acknowledged packet before it was sent (S_0 and A_0).
 //
 // Based on that data, send and ack rate are estimated as:
 //   send_rate = (bytes(S_1) - bytes(S_0)) / (time(S_1) - time(S_0))
@@ -362,6 +373,57 @@
     return one_bw_sample_per_ack_event_;
   }
 
+  // AckPoint represents a point on the ack line.
+  struct QUIC_NO_EXPORT AckPoint {
+    QuicTime ack_time = QuicTime::Zero();
+    QuicByteCount total_bytes_acked = 0;
+
+    friend QUIC_NO_EXPORT std::ostream& operator<<(std::ostream& os,
+                                                   const AckPoint& ack_point) {
+      return os << ack_point.ack_time << ":" << ack_point.total_bytes_acked;
+    }
+  };
+
+  // RecentAckPoints maintains the most recent 2 ack points at distinct times.
+  class QUIC_NO_EXPORT RecentAckPoints {
+   public:
+    void Update(QuicTime ack_time, QuicByteCount total_bytes_acked) {
+      DCHECK_GE(total_bytes_acked, ack_points_[1].total_bytes_acked);
+
+      if (ack_time < ack_points_[1].ack_time) {
+        // This can only happen when time goes backwards, we use the smaller
+        // timestamp for the most recent ack point in that case.
+        // TODO(wub): Add a QUIC_BUG if ack time stops going backwards.
+        ack_points_[1].ack_time = ack_time;
+      } else if (ack_time > ack_points_[1].ack_time) {
+        ack_points_[0] = ack_points_[1];
+        ack_points_[1].ack_time = ack_time;
+      }
+
+      ack_points_[1].total_bytes_acked = total_bytes_acked;
+    }
+
+    void Clear() { ack_points_[0] = ack_points_[1] = AckPoint(); }
+
+    const AckPoint& MostRecentPoint() const { return ack_points_[1]; }
+
+    const AckPoint& LessRecentPoint() const {
+      if (ack_points_[0].total_bytes_acked != 0) {
+        return ack_points_[0];
+      }
+
+      return ack_points_[1];
+    }
+
+   private:
+    AckPoint ack_points_[2];
+  };
+
+  void EnableOverestimateAvoidance();
+  bool IsOverestimateAvoidanceEnabled() const {
+    return overestimate_avoidance_;
+  }
+
  private:
   friend class test::BandwidthSamplerPeer;
 
@@ -428,6 +490,20 @@
   void SentPacketToSendTimeState(const ConnectionStateOnSentPacket& sent_packet,
                                  SendTimeState* send_time_state) const;
 
+  // Choose the best a0 from |a0_candidates_| to calculate the ack rate.
+  // |total_bytes_acked| is the total bytes acked when the packet being acked is
+  // sent. The best a0 is chosen as follows:
+  // - If there's only one candidate, use it.
+  // - If there are multiple candidates, let a[n] be the nth candidate, and
+  //   a[n-1].total_bytes_acked <= |total_bytes_acked| < a[n].total_bytes_acked,
+  //   use a[n-1].
+  // - If all candidates's total_bytes_acked is > |total_bytes_acked|, use a[0].
+  //   This may happen when acks are received out of order, and ack[n] caused
+  //   some candidates of ack[n-x] to be removed.
+  // - If all candidates's total_bytes_acked is <= |total_bytes_acked|, use
+  //   a[a.size()-1].
+  bool ChooseA0Point(QuicByteCount total_bytes_acked, AckPoint* a0);
+
   // The total number of congestion controlled bytes sent during the connection.
   QuicByteCount total_bytes_sent_;
 
@@ -466,6 +542,9 @@
   // sent, indexed by the packet number.
   PacketNumberIndexedQueue<ConnectionStateOnSentPacket> connection_state_map_;
 
+  RecentAckPoints recent_ack_points_;
+  QuicCircularDeque<AckPoint> a0_candidates_;
+
   // Maximum number of tracked packets.
   const QuicPacketCount max_tracked_packets_;
 
@@ -493,6 +572,10 @@
   const bool one_bw_sample_per_ack_event_ =
       remove_packets_once_per_congestion_event_ &&
       GetQuicReloadableFlag(quic_one_bw_sample_per_ack_event2);
+
+  // True if --quic_avoid_overestimate_bandwidth_with_aggregation=true and
+  // connection option 'BSAO' is set.
+  bool overestimate_avoidance_;
 };
 
 }  // namespace quic
diff --git a/quic/core/congestion_control/bandwidth_sampler_test.cc b/quic/core/congestion_control/bandwidth_sampler_test.cc
index 13fa1c0..2529ba1 100644
--- a/quic/core/congestion_control/bandwidth_sampler_test.cc
+++ b/quic/core/congestion_control/bandwidth_sampler_test.cc
@@ -45,6 +45,10 @@
         round_trip_count_(0) {
     // Ensure that the clock does not start at zero.
     clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
+    if (GetQuicReloadableFlag(
+            quic_avoid_overestimate_bandwidth_with_aggregation)) {
+      sampler_.EnableOverestimateAvoidance();
+    }
   }
 
   MockClock clock_;
@@ -286,7 +290,7 @@
   QuicBandwidth last_bandwidth = QuicBandwidth::Zero();
   for (int i = 21; i <= 40; i++) {
     last_bandwidth = AckPacket(i);
-    EXPECT_EQ(expected_bandwidth, last_bandwidth);
+    EXPECT_EQ(expected_bandwidth, last_bandwidth) << "i is " << i;
     clock_.AdvanceTime(time_between_packets);
   }
   if (sampler_.remove_packets_once_per_congestion_event()) {
@@ -690,7 +694,7 @@
 
   // Send and ack packet 2, 3 and 4.
   round_trip_count_++;
-  est_bandwidth_upper_bound_ = first_packet_sending_rate * 0.9;
+  est_bandwidth_upper_bound_ = first_packet_sending_rate * 0.3;
   SendPacket(2);
   SendPacket(3);
   SendPacket(4);
@@ -704,7 +708,12 @@
 
 class MaxAckHeightTrackerTest : public QuicTest {
  protected:
-  MaxAckHeightTrackerTest() : tracker_(/*initial_filter_window=*/10) {}
+  MaxAckHeightTrackerTest() : tracker_(/*initial_filter_window=*/10) {
+    if (GetQuicReloadableFlag(
+            quic_avoid_overestimate_bandwidth_with_aggregation)) {
+      tracker_.SetAckAggregationBandwidthThreshold(1.8);
+    }
+  }
 
   // Run a full aggregation episode, which is one or more aggregated acks,
   // followed by a quiet period in which no ack happens.
@@ -787,9 +796,15 @@
                      1200, true);
   now_ = now_ - QuicTime::Delta::FromMilliseconds(1);
 
-  AggregationEpisode(bandwidth_ * 20, QuicTime::Delta::FromMilliseconds(6),
-                     1200, false);
-  EXPECT_EQ(2u, tracker_.num_ack_aggregation_epochs());
+  if (tracker_.ack_aggregation_bandwidth_threshold() > 1.1) {
+    AggregationEpisode(bandwidth_ * 20, QuicTime::Delta::FromMilliseconds(6),
+                       1200, true);
+    EXPECT_EQ(3u, tracker_.num_ack_aggregation_epochs());
+  } else {
+    AggregationEpisode(bandwidth_ * 20, QuicTime::Delta::FromMilliseconds(6),
+                       1200, false);
+    EXPECT_EQ(2u, tracker_.num_ack_aggregation_epochs());
+  }
 }
 
 TEST_F(MaxAckHeightTrackerTest, VeryAggregatedSmallAcks) {
@@ -799,9 +814,15 @@
                      true);
   now_ = now_ - QuicTime::Delta::FromMilliseconds(1);
 
-  AggregationEpisode(bandwidth_ * 20, QuicTime::Delta::FromMilliseconds(6), 300,
-                     false);
-  EXPECT_EQ(2u, tracker_.num_ack_aggregation_epochs());
+  if (tracker_.ack_aggregation_bandwidth_threshold() > 1.1) {
+    AggregationEpisode(bandwidth_ * 20, QuicTime::Delta::FromMilliseconds(6),
+                       300, true);
+    EXPECT_EQ(3u, tracker_.num_ack_aggregation_epochs());
+  } else {
+    AggregationEpisode(bandwidth_ * 20, QuicTime::Delta::FromMilliseconds(6),
+                       300, false);
+    EXPECT_EQ(2u, tracker_.num_ack_aggregation_epochs());
+  }
 }
 
 TEST_F(MaxAckHeightTrackerTest, SomewhatAggregatedLargeAck) {
@@ -811,9 +832,15 @@
                      1000, true);
   now_ = now_ - QuicTime::Delta::FromMilliseconds(1);
 
-  AggregationEpisode(bandwidth_ * 2, QuicTime::Delta::FromMilliseconds(50),
-                     1000, false);
-  EXPECT_EQ(2u, tracker_.num_ack_aggregation_epochs());
+  if (tracker_.ack_aggregation_bandwidth_threshold() > 1.1) {
+    AggregationEpisode(bandwidth_ * 2, QuicTime::Delta::FromMilliseconds(50),
+                       1000, true);
+    EXPECT_EQ(3u, tracker_.num_ack_aggregation_epochs());
+  } else {
+    AggregationEpisode(bandwidth_ * 2, QuicTime::Delta::FromMilliseconds(50),
+                       1000, false);
+    EXPECT_EQ(2u, tracker_.num_ack_aggregation_epochs());
+  }
 }
 
 TEST_F(MaxAckHeightTrackerTest, SomewhatAggregatedSmallAcks) {
@@ -823,9 +850,15 @@
                      true);
   now_ = now_ - QuicTime::Delta::FromMilliseconds(1);
 
-  AggregationEpisode(bandwidth_ * 2, QuicTime::Delta::FromMilliseconds(50), 100,
-                     false);
-  EXPECT_EQ(2u, tracker_.num_ack_aggregation_epochs());
+  if (tracker_.ack_aggregation_bandwidth_threshold() > 1.1) {
+    AggregationEpisode(bandwidth_ * 2, QuicTime::Delta::FromMilliseconds(50),
+                       100, true);
+    EXPECT_EQ(3u, tracker_.num_ack_aggregation_epochs());
+  } else {
+    AggregationEpisode(bandwidth_ * 2, QuicTime::Delta::FromMilliseconds(50),
+                       100, false);
+    EXPECT_EQ(2u, tracker_.num_ack_aggregation_epochs());
+  }
 }
 
 TEST_F(MaxAckHeightTrackerTest, NotAggregated) {
diff --git a/quic/core/congestion_control/bbr2_misc.h b/quic/core/congestion_control/bbr2_misc.h
index 2226566..191790b 100644
--- a/quic/core/congestion_control/bbr2_misc.h
+++ b/quic/core/congestion_control/bbr2_misc.h
@@ -359,6 +359,10 @@
     return bandwidth_sampler_.max_ack_height();
   }
 
+  void EnableOverestimateAvoidance() {
+    bandwidth_sampler_.EnableOverestimateAvoidance();
+  }
+
   void OnPacketNeutered(QuicPacketNumber packet_number) {
     bandwidth_sampler_.OnPacketNeutered(packet_number);
   }
diff --git a/quic/core/congestion_control/bbr2_sender.cc b/quic/core/congestion_control/bbr2_sender.cc
index 108cb34..60fb374 100644
--- a/quic/core/congestion_control/bbr2_sender.cc
+++ b/quic/core/congestion_control/bbr2_sender.cc
@@ -93,6 +93,13 @@
   if (config.HasClientRequestedIndependentOption(kBBR9, perspective)) {
     flexible_app_limited_ = true;
   }
+  if (GetQuicReloadableFlag(
+          quic_avoid_overestimate_bandwidth_with_aggregation) &&
+      config.HasClientRequestedIndependentOption(kBSAO, perspective)) {
+    QUIC_RELOADABLE_FLAG_COUNT_N(
+        quic_avoid_overestimate_bandwidth_with_aggregation, 4, 4);
+    model_.EnableOverestimateAvoidance();
+  }
 }
 
 Limits<QuicByteCount> Bbr2Sender::GetCwndLimitsByMode() const {
diff --git a/quic/core/congestion_control/bbr2_simulator_test.cc b/quic/core/congestion_control/bbr2_simulator_test.cc
index d109d54..e80b5ac 100644
--- a/quic/core/congestion_control/bbr2_simulator_test.cc
+++ b/quic/core/congestion_control/bbr2_simulator_test.cc
@@ -16,6 +16,7 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_optional.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_sent_packet_manager_peer.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
@@ -304,6 +305,14 @@
                                               aggregation_timeout);
   }
 
+  void SetConnectionOption(QuicTag option) {
+    QuicConfig config;
+    QuicTagVector options;
+    options.push_back(option);
+    QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+    sender_->SetFromConfig(config, Perspective::IS_SERVER);
+  }
+
   bool Bbr2ModeIsOneOf(const std::vector<Bbr2Mode>& expected_modes) const {
     const Bbr2Mode mode = sender_->ExportDebugState().mode;
     for (Bbr2Mode expected_mode : expected_modes) {
@@ -416,6 +425,10 @@
 }
 
 TEST_F(Bbr2DefaultTopologyTest, SimpleTransfer2RTTAggregationBytes) {
+  if (GetQuicReloadableFlag(
+          quic_avoid_overestimate_bandwidth_with_aggregation)) {
+    SetConnectionOption(kBSAO);
+  }
   DefaultTopologyParams params;
   CreateNetwork(params);
   // 2 RTTs of aggregation, with a max of 10kb.
@@ -425,11 +438,17 @@
   DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
   EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
 
-  EXPECT_LE(params.BottleneckBandwidth() * 0.99f,
-            sender_->ExportDebugState().bandwidth_hi);
-  // TODO(b/36022633): Bandwidth sampler overestimates with aggregation.
-  EXPECT_GE(params.BottleneckBandwidth() * 1.5f,
-            sender_->ExportDebugState().bandwidth_hi);
+  if (GetQuicReloadableFlag(
+          quic_avoid_overestimate_bandwidth_with_aggregation)) {
+    EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
+                     sender_->ExportDebugState().bandwidth_hi, 0.01f);
+  } else {
+    EXPECT_LE(params.BottleneckBandwidth() * 0.99f,
+              sender_->ExportDebugState().bandwidth_hi);
+    // TODO(b/36022633): Bandwidth sampler overestimates with aggregation.
+    EXPECT_GE(params.BottleneckBandwidth() * 1.5f,
+              sender_->ExportDebugState().bandwidth_hi);
+  }
   EXPECT_LE(sender_loss_rate_in_packets(), 0.05);
   // The margin here is high, because the aggregation greatly increases
   // smoothed rtt.
@@ -438,6 +457,10 @@
 }
 
 TEST_F(Bbr2DefaultTopologyTest, SimpleTransferAckDecimation) {
+  if (GetQuicReloadableFlag(
+          quic_avoid_overestimate_bandwidth_with_aggregation)) {
+    SetConnectionOption(kBSAO);
+  }
   // Enable Ack Decimation on the receiver.
   QuicConnectionPeer::SetAckMode(receiver_endpoint_.connection(),
                                  AckMode::ACK_DECIMATION);
@@ -448,11 +471,17 @@
   DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
   EXPECT_TRUE(Bbr2ModeIsOneOf({Bbr2Mode::PROBE_BW, Bbr2Mode::PROBE_RTT}));
 
-  EXPECT_LE(params.BottleneckBandwidth() * 0.99f,
-            sender_->ExportDebugState().bandwidth_hi);
-  // TODO(b/36022633): Bandwidth sampler overestimates with aggregation.
-  EXPECT_GE(params.BottleneckBandwidth() * 1.1f,
-            sender_->ExportDebugState().bandwidth_hi);
+  if (GetQuicReloadableFlag(
+          quic_avoid_overestimate_bandwidth_with_aggregation)) {
+    EXPECT_APPROX_EQ(params.BottleneckBandwidth(),
+                     sender_->ExportDebugState().bandwidth_hi, 0.01f);
+  } else {
+    EXPECT_LE(params.BottleneckBandwidth() * 0.99f,
+              sender_->ExportDebugState().bandwidth_hi);
+    // TODO(b/36022633): Bandwidth sampler overestimates with aggregation.
+    EXPECT_GE(params.BottleneckBandwidth() * 1.1f,
+              sender_->ExportDebugState().bandwidth_hi);
+  }
   EXPECT_LE(sender_loss_rate_in_packets(), 0.001);
   EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
   // The margin here is high, because the aggregation greatly increases
diff --git a/quic/core/congestion_control/bbr_sender.cc b/quic/core/congestion_control/bbr_sender.cc
index da723c4..fba16b5 100644
--- a/quic/core/congestion_control/bbr_sender.cc
+++ b/quic/core/congestion_control/bbr_sender.cc
@@ -330,6 +330,13 @@
     max_congestion_window_with_network_parameters_adjusted_ =
         100 * kDefaultTCPMSS;
   }
+  if (GetQuicReloadableFlag(
+          quic_avoid_overestimate_bandwidth_with_aggregation) &&
+      config.HasClientRequestedIndependentOption(kBSAO, perspective)) {
+    QUIC_RELOADABLE_FLAG_COUNT_N(
+        quic_avoid_overestimate_bandwidth_with_aggregation, 3, 4);
+    sampler_.EnableOverestimateAvoidance();
+  }
 }
 
 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 096b7fa..2b262c9 100644
--- a/quic/core/congestion_control/bbr_sender_test.cc
+++ b/quic/core/congestion_control/bbr_sender_test.cc
@@ -430,20 +430,32 @@
 // Flaky in Chromium: https://crbug.com/1042575
 TEST_F(BbrSenderTest,
        QUIC_TEST_DISABLED_IN_CHROME(SimpleTransfer2RTTAggregationBytes)) {
+  if (GetQuicReloadableFlag(
+          quic_avoid_overestimate_bandwidth_with_aggregation)) {
+    SetConnectionOption(kBSAO);
+  }
   CreateDefaultSetup();
   // 2 RTTs of aggregation, with a max of 10kb.
   EnableAggregation(10 * 1024, 2 * kTestRtt);
 
   // Transfer 12MB.
   DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
-  EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
-  // It's possible to read a bandwidth as much as 50% too high with aggregation.
-  EXPECT_LE(kTestLinkBandwidth * 0.99f,
-            sender_->ExportDebugState().max_bandwidth);
-  // TODO(ianswett): Tighten this bound once we understand why BBR is
-  // overestimating bandwidth with aggregation. b/36022633
-  EXPECT_GE(kTestLinkBandwidth * 1.5f,
-            sender_->ExportDebugState().max_bandwidth);
+  EXPECT_TRUE(sender_->ExportDebugState().mode == BbrSender::PROBE_BW ||
+              sender_->ExportDebugState().mode == BbrSender::PROBE_RTT);
+  if (GetQuicReloadableFlag(
+          quic_avoid_overestimate_bandwidth_with_aggregation)) {
+    EXPECT_APPROX_EQ(kTestLinkBandwidth,
+                     sender_->ExportDebugState().max_bandwidth, 0.01f);
+  } else {
+    // It's possible to read a bandwidth as much as 50% too high with
+    // aggregation.
+    EXPECT_LE(kTestLinkBandwidth * 0.93f,
+              sender_->ExportDebugState().max_bandwidth);
+    // TODO(ianswett): Tighten this bound once we understand why BBR is
+    // overestimating bandwidth with aggregation. b/36022633
+    EXPECT_GE(kTestLinkBandwidth * 1.5f,
+              sender_->ExportDebugState().max_bandwidth);
+  }
   // The margin here is high, because the aggregation greatly increases
   // smoothed rtt.
   EXPECT_GE(kTestRtt * 4, rtt_stats_->smoothed_rtt());
@@ -452,6 +464,10 @@
 
 // Test a simple long data transfer with 2 rtts of aggregation.
 TEST_F(BbrSenderTest, SimpleTransferAckDecimation) {
+  if (GetQuicReloadableFlag(
+          quic_avoid_overestimate_bandwidth_with_aggregation)) {
+    SetConnectionOption(kBSAO);
+  }
   // Decrease the CWND gain so extra CWND is required with stretch acks.
   SetQuicFlag(FLAGS_quic_bbr_cwnd_gain, 1.0);
   sender_ = new BbrSender(
@@ -470,13 +486,21 @@
   // Transfer 12MB.
   DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
   EXPECT_EQ(BbrSender::PROBE_BW, sender_->ExportDebugState().mode);
-  // It's possible to read a bandwidth as much as 50% too high with aggregation.
-  EXPECT_LE(kTestLinkBandwidth * 0.99f,
-            sender_->ExportDebugState().max_bandwidth);
-  // TODO(ianswett): Tighten this bound once we understand why BBR is
-  // overestimating bandwidth with aggregation. b/36022633
-  EXPECT_GE(kTestLinkBandwidth * 1.5f,
-            sender_->ExportDebugState().max_bandwidth);
+
+  if (GetQuicReloadableFlag(
+          quic_avoid_overestimate_bandwidth_with_aggregation)) {
+    EXPECT_APPROX_EQ(kTestLinkBandwidth,
+                     sender_->ExportDebugState().max_bandwidth, 0.01f);
+  } else {
+    // It's possible to read a bandwidth as much as 50% too high with
+    // aggregation.
+    EXPECT_LE(kTestLinkBandwidth * 0.93f,
+              sender_->ExportDebugState().max_bandwidth);
+    // TODO(ianswett): Tighten this bound once we understand why BBR is
+    // overestimating bandwidth with aggregation. b/36022633
+    EXPECT_GE(kTestLinkBandwidth * 1.5f,
+              sender_->ExportDebugState().max_bandwidth);
+  }
   // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures
   // bandwidth higher than the link rate.
   EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
@@ -488,6 +512,10 @@
 
 // Test a simple long data transfer with 2 rtts of aggregation.
 TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes20RTTWindow) {
+  if (GetQuicReloadableFlag(
+          quic_avoid_overestimate_bandwidth_with_aggregation)) {
+    SetConnectionOption(kBSAO);
+  }
   // Disable Ack Decimation on the receiver, because it can increase srtt.
   QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING);
   CreateDefaultSetup();
@@ -499,13 +527,20 @@
   DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
   EXPECT_TRUE(sender_->ExportDebugState().mode == BbrSender::PROBE_BW ||
               sender_->ExportDebugState().mode == BbrSender::PROBE_RTT);
-  // It's possible to read a bandwidth as much as 50% too high with aggregation.
-  EXPECT_LE(kTestLinkBandwidth * 0.99f,
-            sender_->ExportDebugState().max_bandwidth);
-  // TODO(ianswett): Tighten this bound once we understand why BBR is
-  // overestimating bandwidth with aggregation. b/36022633
-  EXPECT_GE(kTestLinkBandwidth * 1.5f,
-            sender_->ExportDebugState().max_bandwidth);
+  if (GetQuicReloadableFlag(
+          quic_avoid_overestimate_bandwidth_with_aggregation)) {
+    EXPECT_APPROX_EQ(kTestLinkBandwidth,
+                     sender_->ExportDebugState().max_bandwidth, 0.01f);
+  } else {
+    // It's possible to read a bandwidth as much as 50% too high with
+    // aggregation.
+    EXPECT_LE(kTestLinkBandwidth * 0.93f,
+              sender_->ExportDebugState().max_bandwidth);
+    // TODO(ianswett): Tighten this bound once we understand why BBR is
+    // overestimating bandwidth with aggregation. b/36022633
+    EXPECT_GE(kTestLinkBandwidth * 1.5f,
+              sender_->ExportDebugState().max_bandwidth);
+  }
   // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures
   // bandwidth higher than the link rate.
   // The margin here is high, because the aggregation greatly increases
@@ -516,6 +551,10 @@
 
 // Test a simple long data transfer with 2 rtts of aggregation.
 TEST_F(BbrSenderTest, SimpleTransfer2RTTAggregationBytes40RTTWindow) {
+  if (GetQuicReloadableFlag(
+          quic_avoid_overestimate_bandwidth_with_aggregation)) {
+    SetConnectionOption(kBSAO);
+  }
   // Disable Ack Decimation on the receiver, because it can increase srtt.
   QuicConnectionPeer::SetAckMode(receiver_.connection(), AckMode::TCP_ACKING);
   CreateDefaultSetup();
@@ -527,13 +566,20 @@
   DoSimpleTransfer(12 * 1024 * 1024, QuicTime::Delta::FromSeconds(35));
   EXPECT_TRUE(sender_->ExportDebugState().mode == BbrSender::PROBE_BW ||
               sender_->ExportDebugState().mode == BbrSender::PROBE_RTT);
-  // It's possible to read a bandwidth as much as 50% too high with aggregation.
-  EXPECT_LE(kTestLinkBandwidth * 0.99f,
-            sender_->ExportDebugState().max_bandwidth);
-  // TODO(ianswett): Tighten this bound once we understand why BBR is
-  // overestimating bandwidth with aggregation. b/36022633
-  EXPECT_GE(kTestLinkBandwidth * 1.5f,
-            sender_->ExportDebugState().max_bandwidth);
+  if (GetQuicReloadableFlag(
+          quic_avoid_overestimate_bandwidth_with_aggregation)) {
+    EXPECT_APPROX_EQ(kTestLinkBandwidth,
+                     sender_->ExportDebugState().max_bandwidth, 0.01f);
+  } else {
+    // It's possible to read a bandwidth as much as 50% too high with
+    // aggregation.
+    EXPECT_LE(kTestLinkBandwidth * 0.93f,
+              sender_->ExportDebugState().max_bandwidth);
+    // TODO(ianswett): Tighten this bound once we understand why BBR is
+    // overestimating bandwidth with aggregation. b/36022633
+    EXPECT_GE(kTestLinkBandwidth * 1.5f,
+              sender_->ExportDebugState().max_bandwidth);
+  }
   // TODO(ianswett): Expect 0 packets are lost once BBR no longer measures
   // bandwidth higher than the link rate.
   // The margin here is high, because the aggregation greatly increases