gfe-relnote: In QUIC, add a connection option to make first n PTOs more aggressive (i.e., 2 * srtt) when calculating PTO timeout. Protected by existing gfe2_reloadable_flag_quic_enable_pto. PiperOrigin-RevId: 288538935 Change-Id: Ieee2bfa5035a42da104107ed7a36b4e4275d0bf6
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h index 9707fb5..1a6e5f2 100644 --- a/quic/core/crypto/crypto_protocol.h +++ b/quic/core/crypto/crypto_protocol.h
@@ -209,6 +209,9 @@ // since 2nd PTO. const QuicTag kPVS1 = TAG('P', 'V', 'S', '1'); // Use 2 * rttvar when // calculating PTO timeout. +const QuicTag kPAG1 = TAG('P', 'A', 'G', '1'); // Make 1st PTO more aggressive +const QuicTag kPAG2 = TAG('P', 'A', 'G', '2'); // Make first 2 PTOs more + // aggressive // 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_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc index f2a2894..6f5e348 100644 --- a/quic/core/quic_sent_packet_manager.cc +++ b/quic/core/quic_sent_packet_manager.cc
@@ -105,6 +105,7 @@ always_include_max_ack_delay_for_pto_timeout_(true), pto_exponential_backoff_start_point_(0), pto_rttvar_multiplier_(4), + num_tlp_timeout_ptos_(0), sanitize_ack_delay_(GetQuicReloadableFlag(quic_sanitize_ack_delay)) { SetSendAlgorithm(congestion_control_type); } @@ -188,6 +189,14 @@ QUIC_RELOADABLE_FLAG_COUNT_N(quic_enable_pto, 8, 8); pto_rttvar_multiplier_ = 2; } + if (config.HasClientSentConnectionOption(kPAG1, perspective)) { + QUIC_CODE_COUNT(one_aggressive_pto); + num_tlp_timeout_ptos_ = 1; + } + if (config.HasClientSentConnectionOption(kPAG2, perspective)) { + QUIC_CODE_COUNT(two_aggressive_ptos); + num_tlp_timeout_ptos_ = 2; + } } // Configure congestion control. @@ -1067,14 +1076,25 @@ } return 2 * rtt_stats_.initial_rtt(); } - const QuicTime::Delta pto_delay = + QuicTime::Delta pto_delay = rtt_stats_.smoothed_rtt() + std::max(pto_rttvar_multiplier_ * rtt_stats_.mean_deviation(), kAlarmGranularity) + (ShouldAddMaxAckDelay() ? peer_max_ack_delay_ : QuicTime::Delta::Zero()); - return pto_delay * (1 << (consecutive_pto_count_ - - std::min(consecutive_pto_count_, - pto_exponential_backoff_start_point_))); + pto_delay = + pto_delay * (1 << (consecutive_pto_count_ - + std::min(consecutive_pto_count_, + pto_exponential_backoff_start_point_))); + if (consecutive_pto_count_ < num_tlp_timeout_ptos_) { + // Make first n PTOs similar to TLPs. + if (pto_delay > 2 * rtt_stats_.smoothed_rtt()) { + QUIC_CODE_COUNT(quic_delayed_pto); + pto_delay = std::max(kAlarmGranularity, 2 * rtt_stats_.smoothed_rtt()); + } else { + QUIC_CODE_COUNT(quic_faster_pto); + } + } + return pto_delay; } QuicTime::Delta QuicSentPacketManager::GetSlowStartDuration() const {
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h index adacf37..39a559c 100644 --- a/quic/core/quic_sent_packet_manager.h +++ b/quic/core/quic_sent_packet_manager.h
@@ -652,6 +652,9 @@ // The multiplier of rttvar when calculating PTO timeout. int pto_rttvar_multiplier_; + // Number of PTOs similar to TLPs. + size_t num_tlp_timeout_ptos_; + // Latched value of quic_sanitize_ack_delay. const bool sanitize_ack_delay_; };
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc index 8e2fc68..7e8e3e7 100644 --- a/quic/core/quic_sent_packet_manager_test.cc +++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -3201,6 +3201,118 @@ manager_.OnRetransmissionTimeout(); } +TEST_F(QuicSentPacketManagerTest, Aggressive1Pto) { + EnablePto(k1PTO); + // Let the first PTO be aggressive. + QuicConfig config; + QuicTagVector options; + options.push_back(kPTOS); + options.push_back(kPAG1); + 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 = 2 * srtt; + EXPECT_EQ(clock_.Now() + expected_pto_delay, + manager_.GetRetransmissionTime()); + + // 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 1 probe packets get sent and packet number gets skipped. + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(3, type, ENCRYPTION_FORWARD_SECURE); + }))); + manager_.MaybeSendProbePackets(); + + // Verify PTO period gets set correctly. + QuicTime sent_time = clock_.Now(); + expected_pto_delay = + srtt + 4 * rtt_stats->mean_deviation() + + QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs); + EXPECT_EQ(sent_time + expected_pto_delay * 2, + manager_.GetRetransmissionTime()); +} + +TEST_F(QuicSentPacketManagerTest, Aggressive2Ptos) { + EnablePto(k1PTO); + // Let the first PTO be aggressive. + QuicConfig config; + QuicTagVector options; + options.push_back(kPTOS); + options.push_back(kPAG2); + 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 = 2 * srtt; + EXPECT_EQ(clock_.Now() + expected_pto_delay, + manager_.GetRetransmissionTime()); + + // 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 1 probe packets get sent and packet number gets skipped. + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(3, type, ENCRYPTION_FORWARD_SECURE); + }))); + manager_.MaybeSendProbePackets(); + + // Verify PTO period gets set correctly. + 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 1 probe packets get sent and packet number gets skipped. + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(5, type, ENCRYPTION_FORWARD_SECURE); + }))); + manager_.MaybeSendProbePackets(); + expected_pto_delay = + srtt + 4 * rtt_stats->mean_deviation() + + QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs); + + // Verify PTO period gets set correctly. + EXPECT_EQ(clock_.Now() + expected_pto_delay * 4, + manager_.GetRetransmissionTime()); +} + } // namespace } // namespace test } // namespace quic