gfe-relnote: In QUIC, add 2 connection options to set the start point of exponential backoff when calculating PTO timeout. Protected by exisiting gfe2_reloadable_flag_quic_enable_pto. PiperOrigin-RevId: 278370348 Change-Id: I3bbafdc9789b4a38f2fa7175febf86cb8f419a29
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h index 0e99be9..0aeb1f9 100644 --- a/quic/core/crypto/crypto_protocol.h +++ b/quic/core/crypto/crypto_protocol.h
@@ -198,6 +198,10 @@ // when computing PTO timeout // if an immediate ACK is // expected. +const QuicTag kPEB1 = TAG('P', 'E', 'B', '1'); // Start exponential backoff + // since 1st PTO. +const QuicTag kPEB2 = TAG('P', 'E', 'B', '2'); // Start exponential backoff + // since 2nd PTO. // Optional support of truncated Connection IDs. If sent by a peer, the value // is the minimum number of bytes allowed for the connection ID sent to the
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc index d2ec4c8..bf100c5 100644 --- a/quic/core/quic_connection.cc +++ b/quic/core/quic_connection.cc
@@ -444,11 +444,11 @@ } if (config.HasClientSentConnectionOption(k7PTO, perspective_)) { max_consecutive_ptos_ = 6; - QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 3, 5); + QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 3, 7); } if (config.HasClientSentConnectionOption(k8PTO, perspective_)) { max_consecutive_ptos_ = 7; - QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 4, 5); + QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 4, 7); } } if (config.HasClientSentConnectionOption(kNSTP, perspective_)) {
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc index 48a11c8..5880ea9 100644 --- a/quic/core/quic_sent_packet_manager.cc +++ b/quic/core/quic_sent_packet_manager.cc
@@ -104,6 +104,7 @@ forward_secure_packet_acked_(false), skip_packet_number_for_pto_(false), always_include_max_ack_delay_for_pto_timeout_(true), + pto_exponential_backoff_start_point_(0), neuter_handshake_packets_once_( GetQuicReloadableFlag(quic_neuter_handshake_packets_once)) { SetSendAlgorithm(congestion_control_type); @@ -150,12 +151,12 @@ if (GetQuicReloadableFlag(quic_enable_pto)) { if (config.HasClientSentConnectionOption(k2PTO, perspective)) { pto_enabled_ = true; - QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 2, 5); + QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 2, 7); } if (config.HasClientSentConnectionOption(k1PTO, perspective)) { pto_enabled_ = true; max_probe_packets_per_pto_ = 1; - QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 1, 5); + QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 1, 7); } } @@ -171,10 +172,19 @@ skip_packet_number_for_pto_ = true; } - if (pto_enabled_ && - config.HasClientSentConnectionOption(kPTOA, perspective)) { - QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 5, 5); - always_include_max_ack_delay_for_pto_timeout_ = false; + if (pto_enabled_) { + if (config.HasClientSentConnectionOption(kPTOA, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 5, 7); + always_include_max_ack_delay_for_pto_timeout_ = false; + } + if (config.HasClientSentConnectionOption(kPEB1, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 6, 7); + StartExponentialBackoffAfterNthPto(1); + } + if (config.HasClientSentConnectionOption(kPEB2, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 7, 7); + StartExponentialBackoffAfterNthPto(2); + } } // Configure congestion control. @@ -800,6 +810,11 @@ uber_loss_algorithm_.SetLossDetectionType(kIetfLossDetection); } +void QuicSentPacketManager::StartExponentialBackoffAfterNthPto( + size_t exponential_backoff_start_point) { + pto_exponential_backoff_start_point_ = exponential_backoff_start_point; +} + QuicSentPacketManager::RetransmissionTimeoutMode QuicSentPacketManager::GetRetransmissionMode() const { DCHECK(unacked_packets_.HasInFlightPackets() || @@ -1026,7 +1041,9 @@ std::max(4 * rtt_stats_.mean_deviation(), QuicTime::Delta::FromMilliseconds(1)) + (ShouldAddMaxAckDelay() ? peer_max_ack_delay_ : QuicTime::Delta::Zero()); - return pto_delay * (1 << consecutive_pto_count_); + return pto_delay * (1 << (consecutive_pto_count_ - + std::min(consecutive_pto_count_, + pto_exponential_backoff_start_point_))); } QuicTime::Delta QuicSentPacketManager::GetSlowStartDuration() const {
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h index f15b3d5..5d362f4 100644 --- a/quic/core/quic_sent_packet_manager.h +++ b/quic/core/quic_sent_packet_manager.h
@@ -383,6 +383,11 @@ // Also enable IETF loss detection. void EnableIetfPtoAndLossDetection(); + // Called to set the start point of doing exponential backoff when calculating + // PTO timeout. + void StartExponentialBackoffAfterNthPto( + size_t exponential_backoff_start_point); + bool supports_multiple_packet_number_spaces() const { return unacked_packets_.supports_multiple_packet_number_spaces(); } @@ -631,6 +636,11 @@ // If true, always include peer_max_ack_delay_ when calculating PTO timeout. bool always_include_max_ack_delay_for_pto_timeout_; + // When calculating PTO timeout, the start point of doing exponential backoff. + // For example, 0 : always do exponential backoff. n : do exponential backoff + // since nth PTO. + size_t pto_exponential_backoff_start_point_; + // Latched value of quic_neuter_handshake_packets_once. const bool neuter_handshake_packets_once_; };
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc index f2a72e0..c9d70b9 100644 --- a/quic/core/quic_sent_packet_manager_test.cc +++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -2882,6 +2882,121 @@ manager_.GetRetransmissionTime()); } +TEST_F(QuicSentPacketManagerTest, StartExponentialBackoffSince2ndPto) { + EnablePto(k2PTO); + QuicConfig config; + QuicTagVector options; + options.push_back(kPEB2); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.SetFromConfig(config); + + EXPECT_CALL(*send_algorithm_, PacingRate(_)) + .WillRepeatedly(Return(QuicBandwidth::Zero())); + EXPECT_CALL(*send_algorithm_, GetCongestionWindow()) + .WillRepeatedly(Return(10 * kDefaultTCPMSS)); + RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(100), + QuicTime::Delta::Zero(), QuicTime::Zero()); + QuicTime::Delta srtt = rtt_stats->smoothed_rtt(); + + SendDataPacket(1, ENCRYPTION_FORWARD_SECURE); + // Verify PTO is correctly set. + QuicTime::Delta expected_pto_delay = + srtt + 4 * rtt_stats->mean_deviation() + + QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs); + EXPECT_EQ(clock_.Now() + expected_pto_delay, + manager_.GetRetransmissionTime()); + + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + SendDataPacket(2, ENCRYPTION_FORWARD_SECURE); + // Verify PTO is correctly set based on sent time of packet 2. + EXPECT_EQ(clock_.Now() + expected_pto_delay, + manager_.GetRetransmissionTime()); + EXPECT_EQ(0u, stats_.pto_count); + + // Invoke PTO. + clock_.AdvanceTime(expected_pto_delay); + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now())); + EXPECT_EQ(1u, stats_.pto_count); + + // Verify two probe packets get sent. + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .Times(2) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(3, type, ENCRYPTION_FORWARD_SECURE); + }))) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(4, type, ENCRYPTION_FORWARD_SECURE); + }))); + manager_.MaybeSendProbePackets(); + // Verify no exponential backoff. + EXPECT_EQ(clock_.Now() + expected_pto_delay, + manager_.GetRetransmissionTime()); + + // Invoke 2nd PTO. + clock_.AdvanceTime(expected_pto_delay); + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now())); + EXPECT_EQ(2u, stats_.pto_count); + + // Verify two probe packets get sent. + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .Times(2) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(5, type, ENCRYPTION_FORWARD_SECURE); + }))) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(6, type, ENCRYPTION_FORWARD_SECURE); + }))); + manager_.MaybeSendProbePackets(); + // Verify still no exponential backoff. + EXPECT_EQ(clock_.Now() + expected_pto_delay, + manager_.GetRetransmissionTime()); + + // Invoke 3rd PTO. + clock_.AdvanceTime(expected_pto_delay); + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now())); + EXPECT_EQ(3u, stats_.pto_count); + + // Verify two probe packets get sent. + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .Times(2) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(7, type, ENCRYPTION_FORWARD_SECURE); + }))) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(8, type, ENCRYPTION_FORWARD_SECURE); + }))); + manager_.MaybeSendProbePackets(); + // Verify exponential backoff starts. + EXPECT_EQ(clock_.Now() + expected_pto_delay * 2, + manager_.GetRetransmissionTime()); + + // Invoke 4th PTO. + clock_.AdvanceTime(expected_pto_delay * 2); + manager_.OnRetransmissionTimeout(); + EXPECT_EQ(QuicTime::Delta::Zero(), manager_.TimeUntilSend(clock_.Now())); + EXPECT_EQ(4u, stats_.pto_count); + + // Verify two probe packets get sent. + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .Times(2) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(9, type, ENCRYPTION_FORWARD_SECURE); + }))) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(10, type, ENCRYPTION_FORWARD_SECURE); + }))); + manager_.MaybeSendProbePackets(); + // Verify exponential backoff continues. + EXPECT_EQ(clock_.Now() + expected_pto_delay * 4, + manager_.GetRetransmissionTime()); +} + } // namespace } // namespace test } // namespace quic