gfe-relnote: In QUIC, add an option PLE2 to arm 1st PTO at least 1.5 * srtt from latest sent packet. Protected by gfe2_reloadable_flag_quic_arm_pto_with_earliest_sent_time.
PiperOrigin-RevId: 298397693
Change-Id: Ia8d65c2f2d68ddf0e669d36e41f9898a4e6787b1
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index e0395da..eb8d75c 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -215,6 +215,12 @@
// aggressive
const QuicTag kPLE1 = TAG('P', 'L', 'E', '1'); // Arm the 1st PTO with
// earliest in flight sent time
+ // and at least 0.5*srtt from
+ // last sent packet.
+const QuicTag kPLE2 = TAG('P', 'L', 'E', '2'); // Arm the 1st PTO with
+ // earliest in flight sent time
+ // and at least 1.5*srtt from
+ // last sent packet.
// 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 df4ad4b..b6eae54 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -106,7 +106,7 @@
num_tlp_timeout_ptos_(0),
one_rtt_packet_acked_(false),
one_rtt_packet_sent_(false),
- arm_1st_pto_with_earliest_inflight_sent_time_(false) {
+ first_pto_srtt_multiplier_(0) {
SetSendAlgorithm(congestion_control_type);
}
@@ -187,10 +187,16 @@
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;
+ if (GetQuicReloadableFlag(quic_arm_pto_with_earliest_sent_time)) {
+ if (config.HasClientSentConnectionOption(kPLE1, perspective)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_arm_pto_with_earliest_sent_time, 1,
+ 2);
+ first_pto_srtt_multiplier_ = 0.5;
+ } else if (config.HasClientSentConnectionOption(kPLE2, perspective)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_arm_pto_with_earliest_sent_time, 2,
+ 2);
+ first_pto_srtt_multiplier_ = 1.5;
+ }
}
}
@@ -1003,18 +1009,20 @@
}
case PTO_MODE: {
if (!supports_multiple_packet_number_spaces()) {
- if (arm_1st_pto_with_earliest_inflight_sent_time_ &&
+ if (first_pto_srtt_multiplier_ > 0 &&
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.
+ // least first_pto_srtt_multiplier_ * RTT has been passed since last
+ // in flight packet.
return std::max(
clock_->ApproximateNow(),
std::max(unacked_packets_.GetFirstInFlightTransmissionInfo()
->sent_time +
GetProbeTimeoutDelay(),
unacked_packets_.GetLastInFlightPacketSentTime() +
- 0.5 * rtt_stats_.SmoothedOrInitialRtt()));
+ first_pto_srtt_multiplier_ *
+ rtt_stats_.SmoothedOrInitialRtt()));
}
// Ensure PTO never gets set to a time in the past.
return std::max(clock_->ApproximateNow(),
@@ -1027,7 +1035,7 @@
// packet of all packet number spaces.
const QuicTime earliest_right_edge =
GetEarliestPacketSentTimeForPto(&packet_number_space);
- if (arm_1st_pto_with_earliest_inflight_sent_time_ &&
+ if (first_pto_srtt_multiplier_ > 0 &&
packet_number_space == APPLICATION_DATA &&
consecutive_pto_count_ == 0) {
const QuicTransmissionInfo* first_application_info =
@@ -1035,14 +1043,14 @@
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.
+ // least first_pto_srtt_multiplier_ * RTT has been passed since last
+ // in flight 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()));
+ earliest_right_edge + first_pto_srtt_multiplier_ *
+ rtt_stats_.SmoothedOrInitialRtt()));
}
}
return std::max(clock_->ApproximateNow(),
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index 891ce1f..99ab378 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -649,8 +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_;
+ // If > 0, arm the 1st PTO with max of earliest in flight sent time + PTO
+ // delay and multiplier * srtt from last in flight packet.
+ float first_pto_srtt_multiplier_;
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 4ccb5ca..36d476f 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -3423,6 +3423,89 @@
manager_.GetRetransmissionTime());
}
+TEST_F(QuicSentPacketManagerTest, ComputingProbeTimeoutByLeftEdge2) {
+ SetQuicReloadableFlag(quic_arm_pto_with_earliest_sent_time, true);
+ EnablePto(k1PTO);
+ // Use PTOS and PLE2.
+ QuicConfig config;
+ QuicTagVector options;
+ options.push_back(kPTOS);
+ options.push_back(kPLE2);
+ 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());
+
+ // Sent a packet 10ms before PTO expiring.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(
+ expected_pto_delay.ToMilliseconds() - 10));
+ SendDataPacket(2, ENCRYPTION_FORWARD_SECURE);
+ // Verify PTO expands to packet 2 sent time + 1.5 * srtt.
+ expected_pto_delay = 1.5 * rtt_stats->smoothed_rtt();
+ 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);
+
+ 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 expected value and based on
+ // packet3 (right edge).
+ expected_pto_delay =
+ srtt + 4 * rtt_stats->mean_deviation() +
+ QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs);
+ 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 3 (left
+ // edge).
+ EXPECT_EQ(packet3_sent_time + expected_pto_delay,
+ manager_.GetRetransmissionTime());
+}
+
TEST_F(QuicSentPacketManagerTest,
ComputingProbeTimeoutByLeftEdgeMultiplePacketNumberSpaces) {
SetQuicReloadableFlag(quic_arm_pto_with_earliest_sent_time, true);
@@ -3499,6 +3582,84 @@
manager_.GetRetransmissionTime());
}
+TEST_F(QuicSentPacketManagerTest,
+ ComputingProbeTimeoutByLeftEdge2MultiplePacketNumberSpaces) {
+ SetQuicReloadableFlag(quic_arm_pto_with_earliest_sent_time, true);
+ manager_.EnableMultiplePacketNumberSpacesSupport();
+ EnablePto(k1PTO);
+ // Use PTOS and PLE2.
+ QuicConfig config;
+ QuicTagVector options;
+ options.push_back(kPTOS);
+ options.push_back(kPLE2);
+ 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());
+
+ // Send packet 5 10ms before PTO expiring.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(
+ expected_pto_delay.ToMilliseconds() - 10));
+ SendDataPacket(5, ENCRYPTION_FORWARD_SECURE);
+ // Verify PTO timeout expands to packet 5 sent time + 1.5 * srtt.
+ EXPECT_EQ(clock_.Now() + 1.5 * rtt_stats->smoothed_rtt(),
+ manager_.GetRetransmissionTime());
+}
+
TEST_F(QuicSentPacketManagerTest, SetHandshakeConfirmed) {
QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT);
manager_.EnableMultiplePacketNumberSpacesSupport();