gfe-relnote: In QUIC, arm 1st PTO based on the earliest in flight packet sent time. Protected by gfe2_reloadable_flag_quic_arm_pto_with_earliest_sent_time. PiperOrigin-RevId: 297905248 Change-Id: Ieacee74c6fcd9b83f44c57d4ddc73cb2007c4eac
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h index aa383b9..e0395da 100644 --- a/quic/core/crypto/crypto_protocol.h +++ b/quic/core/crypto/crypto_protocol.h
@@ -213,6 +213,8 @@ 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 +const QuicTag kPLE1 = TAG('P', 'L', 'E', '1'); // Arm the 1st PTO with + // earliest in flight sent time // 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/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc index 82df3bb..39fc5f5 100644 --- a/quic/core/http/end_to_end_test.cc +++ b/quic/core/http/end_to_end_test.cc
@@ -400,6 +400,7 @@ if (VersionHasIetfQuicFrames(negotiated_version_.transport_version)) { copt.push_back(kILD0); } + copt.push_back(kPLE1); client_config_.SetConnectionOptionsToSend(copt); // Start the server first, because CreateQuicClient() attempts
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc index fc7287f..df4ad4b 100644 --- a/quic/core/quic_sent_packet_manager.cc +++ b/quic/core/quic_sent_packet_manager.cc
@@ -105,7 +105,8 @@ pto_rttvar_multiplier_(4), num_tlp_timeout_ptos_(0), one_rtt_packet_acked_(false), - one_rtt_packet_sent_(false) { + one_rtt_packet_sent_(false), + arm_1st_pto_with_earliest_inflight_sent_time_(false) { SetSendAlgorithm(congestion_control_type); } @@ -186,6 +187,11 @@ QUIC_CODE_COUNT(two_aggressive_ptos); num_tlp_timeout_ptos_ = 2; } + if (GetQuicReloadableFlag(quic_arm_pto_with_earliest_sent_time) && + config.HasClientSentConnectionOption(kPLE1, perspective)) { + QUIC_RELOADABLE_FLAG_COUNT(quic_arm_pto_with_earliest_sent_time); + arm_1st_pto_with_earliest_inflight_sent_time_ = true; + } } // Configure congestion control. @@ -997,6 +1003,19 @@ } case PTO_MODE: { if (!supports_multiple_packet_number_spaces()) { + if (arm_1st_pto_with_earliest_inflight_sent_time_ && + unacked_packets_.HasInFlightPackets() && + consecutive_pto_count_ == 0) { + // Arm 1st PTO with earliest in flight sent time, and make sure at + // least half RTT has been passed since last sent packet. + return std::max( + clock_->ApproximateNow(), + std::max(unacked_packets_.GetFirstInFlightTransmissionInfo() + ->sent_time + + GetProbeTimeoutDelay(), + unacked_packets_.GetLastInFlightPacketSentTime() + + 0.5 * rtt_stats_.SmoothedOrInitialRtt())); + } // Ensure PTO never gets set to a time in the past. return std::max(clock_->ApproximateNow(), unacked_packets_.GetLastInFlightPacketSentTime() + @@ -1004,9 +1023,30 @@ } PacketNumberSpace packet_number_space; + // earliest_right_edge is the earliest sent time of the last in flight + // packet of all packet number spaces. + const QuicTime earliest_right_edge = + GetEarliestPacketSentTimeForPto(&packet_number_space); + if (arm_1st_pto_with_earliest_inflight_sent_time_ && + packet_number_space == APPLICATION_DATA && + consecutive_pto_count_ == 0) { + const QuicTransmissionInfo* first_application_info = + unacked_packets_.GetFirstInFlightTransmissionInfoOfSpace( + APPLICATION_DATA); + if (first_application_info != nullptr) { + // Arm 1st PTO with earliest in flight sent time, and make sure at + // least half RTT has been passed since last sent packet. Only do this + // for application data. + return std::max( + clock_->ApproximateNow(), + std::max( + first_application_info->sent_time + GetProbeTimeoutDelay(), + earliest_right_edge + + 0.5 * rtt_stats_.SmoothedOrInitialRtt())); + } + } return std::max(clock_->ApproximateNow(), - GetEarliestPacketSentTimeForPto(&packet_number_space) + - GetProbeTimeoutDelay()); + earliest_right_edge + GetProbeTimeoutDelay()); } } DCHECK(false);
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h index 59bb356..891ce1f 100644 --- a/quic/core/quic_sent_packet_manager.h +++ b/quic/core/quic_sent_packet_manager.h
@@ -649,6 +649,9 @@ // True if any 1-RTT packet gets sent. bool one_rtt_packet_sent_; + // If true, arm the 1st PTO with earliest in flight sent time. + bool arm_1st_pto_with_earliest_inflight_sent_time_; + const bool avoid_overestimate_bandwidth_with_aggregation_ = GetQuicReloadableFlag(quic_avoid_overestimate_bandwidth_with_aggregation); };
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc index 35feefc..4ccb5ca 100644 --- a/quic/core/quic_sent_packet_manager_test.cc +++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -3348,6 +3348,157 @@ manager_.GetRetransmissionTime()); } +TEST_F(QuicSentPacketManagerTest, ComputingProbeTimeoutByLeftEdge) { + SetQuicReloadableFlag(quic_arm_pto_with_earliest_sent_time, true); + EnablePto(k1PTO); + // Use PTOS and PLE1. + QuicConfig config; + QuicTagVector options; + options.push_back(kPTOS); + options.push_back(kPLE1); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.SetFromConfig(config); + EXPECT_TRUE(manager_.skip_packet_number_for_pto()); + EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true)); + 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); + const QuicTime packet1_sent_time = clock_.Now(); + EXPECT_EQ(packet1_sent_time + expected_pto_delay, + manager_.GetRetransmissionTime()); + + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + SendDataPacket(2, ENCRYPTION_FORWARD_SECURE); + // Verify PTO is still based on packet 1. + EXPECT_EQ(packet1_sent_time + 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); + + EXPECT_CALL(notifier_, RetransmitFrames(_, _)) + .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) { + RetransmitDataPacket(3, type, ENCRYPTION_FORWARD_SECURE); + }))); + manager_.MaybeSendProbePackets(); + // Verify PTO period gets set to twice the current value and based on packet3. + QuicTime packet3_sent_time = clock_.Now(); + EXPECT_EQ(packet3_sent_time + expected_pto_delay * 2, + manager_.GetRetransmissionTime()); + + // Received ACK for packets 1 and 2. + uint64_t acked[] = {1, 2}; + ExpectAcksAndLosses(true, acked, QUICHE_ARRAYSIZE(acked), nullptr, 0); + manager_.OnAckFrameStart(QuicPacketNumber(2), QuicTime::Delta::Infinite(), + clock_.Now()); + manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(3)); + EXPECT_EQ(PACKETS_NEWLY_ACKED, + manager_.OnAckFrameEnd(clock_.Now(), QuicPacketNumber(1), + ENCRYPTION_FORWARD_SECURE)); + expected_pto_delay = + rtt_stats->SmoothedOrInitialRtt() + + std::max(4 * rtt_stats->mean_deviation(), + QuicTime::Delta::FromMilliseconds(1)) + + QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs); + + // Verify PTO is correctly re-armed based on sent time of packet 4. + EXPECT_EQ(packet3_sent_time + expected_pto_delay, + manager_.GetRetransmissionTime()); +} + +TEST_F(QuicSentPacketManagerTest, + ComputingProbeTimeoutByLeftEdgeMultiplePacketNumberSpaces) { + SetQuicReloadableFlag(quic_arm_pto_with_earliest_sent_time, true); + manager_.EnableMultiplePacketNumberSpacesSupport(); + EnablePto(k1PTO); + // Use PTOS and PLE1. + QuicConfig config; + QuicTagVector options; + options.push_back(kPTOS); + options.push_back(kPLE1); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + EXPECT_CALL(*network_change_visitor_, OnCongestionChange()); + manager_.SetFromConfig(config); + EXPECT_TRUE(manager_.skip_packet_number_for_pto()); + EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true)); + 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(); + + // Send packet 1. + SendDataPacket(1, ENCRYPTION_INITIAL); + const QuicTime packet1_sent_time = clock_.Now(); + // Verify PTO is correctly set. + QuicTime::Delta expected_pto_delay = + srtt + 4 * rtt_stats->mean_deviation() + + QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs); + EXPECT_EQ(packet1_sent_time + expected_pto_delay, + manager_.GetRetransmissionTime()); + + // Send packet 2 in handshake. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + SendDataPacket(2, ENCRYPTION_HANDSHAKE); + const QuicTime packet2_sent_time = clock_.Now(); + // Verify PTO timeout is still based on packet 1. + EXPECT_EQ(packet1_sent_time + expected_pto_delay, + manager_.GetRetransmissionTime()); + + // Discard initial keys. + EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false)); + manager_.NeuterUnencryptedPackets(); + + // Send packet 3 in 1-RTT. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + SendDataPacket(3, ENCRYPTION_FORWARD_SECURE); + // Verify PTO timeout is based on packet 2. + const QuicTime packet3_sent_time = clock_.Now(); + EXPECT_EQ(packet2_sent_time + expected_pto_delay, + manager_.GetRetransmissionTime()); + + // Send packet 4 in handshake. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + SendDataPacket(4, ENCRYPTION_HANDSHAKE); + // Verify PTO timeout is based on packet 4 as application data is ignored. + EXPECT_EQ(clock_.Now() + expected_pto_delay, + manager_.GetRetransmissionTime()); + + // Discard handshake keys. + manager_.SetHandshakeConfirmed(); + // Verify PTO timeout is now based on packet 3 as handshake is + // complete/confirmed. + EXPECT_EQ(packet3_sent_time + expected_pto_delay, + manager_.GetRetransmissionTime()); + + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + SendDataPacket(5, ENCRYPTION_FORWARD_SECURE); + // Verify PTO timeout is still based on packet 3. + EXPECT_EQ(packet3_sent_time + expected_pto_delay, + manager_.GetRetransmissionTime()); +} + TEST_F(QuicSentPacketManagerTest, SetHandshakeConfirmed) { QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT); manager_.EnableMultiplePacketNumberSpacesSupport();
diff --git a/quic/core/quic_unacked_packet_map.cc b/quic/core/quic_unacked_packet_map.cc index c856353..efb8e65 100644 --- a/quic/core/quic_unacked_packet_map.cc +++ b/quic/core/quic_unacked_packet_map.cc
@@ -477,6 +477,32 @@ return largest_sent_retransmittable_packets_[packet_number_space]; } +const QuicTransmissionInfo* +QuicUnackedPacketMap::GetFirstInFlightTransmissionInfo() const { + DCHECK(HasInFlightPackets()); + for (auto it = unacked_packets_.begin(); it != unacked_packets_.end(); ++it) { + if (it->in_flight) { + return &(*it); + } + } + DCHECK(false); + return nullptr; +} + +const QuicTransmissionInfo* +QuicUnackedPacketMap::GetFirstInFlightTransmissionInfoOfSpace( + PacketNumberSpace packet_number_space) const { + // TODO(fayang): Optimize this part if arm 1st PTO with first in flight sent + // time works. + for (auto it = unacked_packets_.begin(); it != unacked_packets_.end(); ++it) { + if (it->in_flight && + GetPacketNumberSpace(it->encryption_level) == packet_number_space) { + return &(*it); + } + } + return nullptr; +} + void QuicUnackedPacketMap::EnableMultiplePacketNumberSpacesSupport() { if (supports_multiple_packet_number_spaces_) { QUIC_BUG << "Multiple packet number spaces has already been enabled";
diff --git a/quic/core/quic_unacked_packet_map.h b/quic/core/quic_unacked_packet_map.h index 8492fb0..bcf5061 100644 --- a/quic/core/quic_unacked_packet_map.h +++ b/quic/core/quic_unacked_packet_map.h
@@ -216,6 +216,14 @@ QuicTime GetLastInFlightPacketSentTime( PacketNumberSpace packet_number_space) const; + // Returns TransmissionInfo of the first in flight packet. + const QuicTransmissionInfo* GetFirstInFlightTransmissionInfo() const; + + // Returns TransmissionInfo of first in flight packet in + // |packet_number_space|. + const QuicTransmissionInfo* GetFirstInFlightTransmissionInfoOfSpace( + PacketNumberSpace packet_number_space) const; + void SetSessionNotifier(SessionNotifierInterface* session_notifier); void EnableMultiplePacketNumberSpacesSupport();