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