Adds the B207 connection option which causes QUIC BBR2 to exit STARTUP if bytes in flight doesn't go below 2*BDP at any point in the last round.
I ran all simulator tests with this default enabled and none failed in a concerning way. For example, sometimes the loss rate was lower, which is to be expected.
Protected by quic_reloadable_flag_quic_bbr2_exit_startup_on_persistent_queue.
PiperOrigin-RevId: 404074041
diff --git a/quic/core/congestion_control/bbr2_misc.cc b/quic/core/congestion_control/bbr2_misc.cc
index 7bc09ae..78458cd 100644
--- a/quic/core/congestion_control/bbr2_misc.cc
+++ b/quic/core/congestion_control/bbr2_misc.cc
@@ -431,4 +431,15 @@
return return_value;
}
+void Bbr2NetworkModel::CheckPersistentQueue(
+ const Bbr2CongestionEvent& congestion_event) {
+ QUICHE_DCHECK(!full_bandwidth_reached_);
+ QUICHE_DCHECK(congestion_event.end_of_round_trip);
+ QUICHE_DCHECK_NE(0u, min_bytes_in_flight_in_round_);
+ if (min_bytes_in_flight_in_round_ >
+ (params_->startup_cwnd_gain * BDP() + QueueingThresholdExtraBytes())) {
+ full_bandwidth_reached_ = true;
+ }
+}
+
} // namespace quic
diff --git a/quic/core/congestion_control/bbr2_misc.h b/quic/core/congestion_control/bbr2_misc.h
index 648e4df..2a8bccf 100644
--- a/quic/core/congestion_control/bbr2_misc.h
+++ b/quic/core/congestion_control/bbr2_misc.h
@@ -95,10 +95,14 @@
// If false, exit STARTUP on loss only if bandwidth is below threshold.
bool always_exit_startup_on_excess_loss = false;
- // If true, inclue extra acked during STARTUP and proactively reduce extra
+ // If true, include extra acked during STARTUP and proactively reduce extra
// acked when bandwidth increases.
bool startup_include_extra_acked = false;
+ // If true, exit STARTUP if bytes in flight has not gone below 2 * BDP at
+ // any point in the last round.
+ bool exit_startup_on_persistent_queue = false;
+
/*
* DRAIN parameters.
*/
@@ -397,6 +401,10 @@
return bandwidth_sampler_.max_ack_height();
}
+ QuicByteCount QueueingThresholdExtraBytes() const {
+ return 2 * kDefaultTCPMSS;
+ }
+
bool cwnd_limited_before_aggregation_epoch() const {
return cwnd_limited_before_aggregation_epoch_;
}
@@ -465,6 +473,8 @@
BandwidthGrowth CheckBandwidthGrowth(
const Bbr2CongestionEvent& congestion_event);
+ void CheckPersistentQueue(const Bbr2CongestionEvent& congestion_event);
+
QuicPacketNumber last_sent_packet() const {
return round_trip_counter_.last_sent_packet();
}
diff --git a/quic/core/congestion_control/bbr2_probe_bw.cc b/quic/core/congestion_control/bbr2_probe_bw.cc
index 579bdba..706ecd3 100644
--- a/quic/core/congestion_control/bbr2_probe_bw.cc
+++ b/quic/core/congestion_control/bbr2_probe_bw.cc
@@ -485,7 +485,8 @@
// HasPhaseLasted(model_->MinRtt(), congestion_event)
} else if (cycle_.rounds_in_phase > 0) {
const QuicByteCount bdp = model_->BDP();
- QuicByteCount queuing_threshold_extra_bytes = 2 * kDefaultTCPMSS;
+ QuicByteCount queuing_threshold_extra_bytes =
+ model_->QueueingThresholdExtraBytes();
if (Params().probe_up_dont_exit_if_no_queue_) {
QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_no_probe_up_exit_if_no_queue, 1,
2);
diff --git a/quic/core/congestion_control/bbr2_sender.cc b/quic/core/congestion_control/bbr2_sender.cc
index 23e15b8..b016ba1 100644
--- a/quic/core/congestion_control/bbr2_sender.cc
+++ b/quic/core/congestion_control/bbr2_sender.cc
@@ -194,6 +194,11 @@
QUIC_RELOADABLE_FLAG_COUNT_N(quic_bbr2_startup_extra_acked, 2, 2);
params_.startup_include_extra_acked = true;
}
+ if (GetQuicReloadableFlag(quic_bbr2_exit_startup_on_persistent_queue) &&
+ ContainsQuicTag(connection_options, kB207)) {
+ params_.exit_startup_on_persistent_queue = true;
+ }
+
if (GetQuicReloadableFlag(
quic_bbr_start_new_aggregation_epoch_after_a_full_round) &&
ContainsQuicTag(connection_options, kBBRA)) {
diff --git a/quic/core/congestion_control/bbr2_simulator_test.cc b/quic/core/congestion_control/bbr2_simulator_test.cc
index 5214601..0968bb1 100644
--- a/quic/core/congestion_control/bbr2_simulator_test.cc
+++ b/quic/core/congestion_control/bbr2_simulator_test.cc
@@ -390,6 +390,34 @@
EXPECT_FALSE(sender_->ExportDebugState().last_sample_is_app_limited);
}
+TEST_F(Bbr2DefaultTopologyTest, NormalStartupB207) {
+ SetQuicReloadableFlag(quic_bbr2_exit_startup_on_persistent_queue, true);
+ 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]() {
+ if (max_bw < sender_->ExportDebugState().bandwidth_hi) {
+ max_bw = sender_->ExportDebugState().bandwidth_hi;
+ max_bw_round = sender_->ExportDebugState().round_trip_count;
+ }
+ return sender_->ExportDebugState().startup.full_bandwidth_reached;
+ },
+ QuicTime::Delta::FromSeconds(5));
+ ASSERT_TRUE(simulator_result);
+ EXPECT_EQ(Bbr2Mode::DRAIN, sender_->ExportDebugState().mode);
+ EXPECT_EQ(0u, sender_->ExportDebugState().round_trip_count - max_bw_round);
+ EXPECT_EQ(
+ 0u,
+ sender_->ExportDebugState().startup.round_trips_without_bandwidth_growth);
+ EXPECT_EQ(0u, sender_connection_stats().packets_lost);
+}
+
// Test a simple long data transfer in the default setup.
TEST_F(Bbr2DefaultTopologyTest, SimpleTransfer) {
DefaultTopologyParams params;
@@ -483,6 +511,26 @@
EXPECT_APPROX_EQ(params.RTT(), rtt_stats()->min_rtt(), 0.2f);
}
+TEST_F(Bbr2DefaultTopologyTest, SimpleTransferB207) {
+ SetQuicReloadableFlag(quic_bbr2_exit_startup_on_persistent_queue, true);
+ SetConnectionOption(kB207);
+ 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, SimpleTransferBBRB) {
SetQuicReloadableFlag(quic_bbr_use_send_rate_in_max_ack_height_tracker, true);
SetConnectionOption(kBBRB);
diff --git a/quic/core/congestion_control/bbr2_startup.cc b/quic/core/congestion_control/bbr2_startup.cc
index c276f36..47a1003 100644
--- a/quic/core/congestion_control/bbr2_startup.cc
+++ b/quic/core/congestion_control/bbr2_startup.cc
@@ -8,6 +8,7 @@
#include "quic/core/congestion_control/bbr2_sender.h"
#include "quic/core/quic_bandwidth.h"
#include "quic/core/quic_types.h"
+#include "quic/platform/api/quic_flag_utils.h"
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_logging.h"
@@ -46,8 +47,12 @@
const AckedPacketVector& /*acked_packets*/,
const LostPacketVector& /*lost_packets*/,
const Bbr2CongestionEvent& congestion_event) {
+ if (Params().exit_startup_on_persistent_queue) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_bbr2_exit_startup_on_persistent_queue);
+ model_->CheckPersistentQueue(congestion_event);
+ }
if (!model_->full_bandwidth_reached() && congestion_event.end_of_round_trip) {
- // TCP BBR always exits upon excessive losses. QUIC BBRv1 does not exits
+ // TCP BBR always exits upon excessive losses. QUIC BBRv1 does not exit
// upon excessive losses, if enough bandwidth growth is observed.
Bbr2NetworkModel::BandwidthGrowth bw_growth =
model_->CheckBandwidthGrowth(congestion_event);
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index f50061e..efe830a 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -165,11 +165,13 @@
// inflight dips below 1.25*BW.
const QuicTag kB203 = TAG('B', '2', '0', '3'); // Ignore inflight_hi until
// PROBE_UP is exited.
-const QuicTag kB206 = TAG('B', '2', '0', '6'); // Exit STARTUP after 2 losses.
const QuicTag kB204 = TAG('B', '2', '0', '4'); // Reduce extra acked when
// MaxBW incrases.
const QuicTag kB205 = TAG('B', '2', '0', '5'); // Add extra acked to CWND in
// STARTUP.
+const QuicTag kB206 = TAG('B', '2', '0', '6'); // Exit STARTUP after 2 losses.
+const QuicTag kB207 = TAG('B', '2', '0', '7'); // Exit STARTUP on persistent
+ // queue
const QuicTag kNTLP = TAG('N', 'T', 'L', 'P'); // No tail loss probe
const QuicTag k1TLP = TAG('1', 'T', 'L', 'P'); // 1 tail loss probe
const QuicTag k1RTO = TAG('1', 'R', 'T', 'O'); // Send 1 packet upon RTO
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 1ecde55..02032a5 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -141,6 +141,8 @@
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_ignore_inflight_hi_in_probe_up, true)
// When true, the B204 connection option enables extra acked in STARTUP, but also adds new logic to decrease it whenever max bandwidth increases.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_startup_extra_acked, true)
+// When true, the B207 connection option causes BBR2 to exit STARTUP if a persistent queue of 2*BDP has existed for the entire round.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_exit_startup_on_persistent_queue, true)
// When true, the BBQ0 connection option causes QUIC BBR2 to add bytes_acked to probe_up_acked if the connection hasn\'t been app-limited since inflight_hi was utilized.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_add_bytes_acked_after_inflight_hi_limited, true)
// When true, the BBR4 copt sets the extra_acked window to 20 RTTs and BBR5 sets it to 40 RTTs.