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);