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