gfe-relnote: In QUIC, send a PING when skipping packet number PTO fires and there is no data to send. Protected by gfe2_reloadable_flag_quic_send_ping_when_pto_skips_packet_number.
PiperOrigin-RevId: 300369878
Change-Id: I395da0107a24c218d9b96fc87df056a6213604f3
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 4a92ab7..f3d7bca 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -2634,7 +2634,7 @@
}
#endif
- const QuicPacketNumber previous_created_packet_number =
+ QuicPacketNumber previous_created_packet_number =
packet_creator_.packet_number();
if (close_connection_after_five_rtos_ &&
sent_packet_manager_.GetConsecutiveRtoCount() >= 4) {
@@ -2663,6 +2663,10 @@
packet_creator_.SkipNPacketNumbers(
num_packet_numbers_to_skip, sent_packet_manager_.GetLeastUnacked(),
sent_packet_manager_.EstimateMaxPacketsInFlight(max_packet_length()));
+ if (GetQuicReloadableFlag(quic_send_ping_when_pto_skips_packet_number)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_send_ping_when_pto_skips_packet_number);
+ previous_created_packet_number += num_packet_numbers_to_skip;
+ }
if (debug_visitor_ != nullptr) {
debug_visitor_->OnNPacketNumbersSkipped(num_packet_numbers_to_skip);
}
@@ -2689,17 +2693,11 @@
retransmission_mode == QuicSentPacketManager::RTO_MODE ||
retransmission_mode == QuicSentPacketManager::PTO_MODE) &&
!visitor_->WillingAndAbleToWrite()) {
- // Send PING if timer fires in RTO or PTO mode but there is no data to
+ // Send PING if timer fires in TLP/RTO/PTO mode but there is no data to
// send.
- // When TLP fires, either new data or tail loss probe should be sent.
- // There is corner case where TLP fires after RTO because packets get
- // acked. Two packets are marked RTO_RETRANSMITTED, but the first packet
- // is retransmitted as two packets because of packet number length
- // increases (please see QuicConnectionTest.RtoPacketAsTwo).
- QUIC_DLOG_IF(WARNING,
- retransmission_mode == QuicSentPacketManager::TLP_MODE &&
- stats_.rto_count == 0)
- << "No packet gets sent when timer fires in TLP mode, sending PING";
+ QUIC_DLOG(INFO) << ENDPOINT
+ << "No packet gets sent when timer fires in mode "
+ << retransmission_mode << ", send PING";
DCHECK_LT(0u, sent_packet_manager_.pending_timer_transmission_count());
visitor_->SendPing();
}
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index ce0829f..0940048 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -1380,6 +1380,16 @@
void SendPing() { notifier_.WriteOrBufferPing(); }
+ MessageStatus SendMessage(quiche::QuicheStringPiece message) {
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
+ return connection_.SendMessage(
+ 1,
+ MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(), message,
+ &storage),
+ false);
+ }
+
void ProcessAckPacket(uint64_t packet_number, QuicAckFrame* frame) {
if (packet_number > 1) {
QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, packet_number - 1);
@@ -10158,6 +10168,44 @@
ASSERT_TRUE(writer_->coalesced_packet() == nullptr);
}
+// Regression test for b/151220135.
+TEST_P(QuicConnectionTest, SendPingWhenSkipPacketNumberForPto) {
+ if (!VersionSupportsMessageFrames(connection_.transport_version())) {
+ return;
+ }
+ QuicConfig config;
+ QuicTagVector connection_options;
+ connection_options.push_back(kPTOS);
+ connection_options.push_back(k1PTO);
+ config.SetConnectionOptionsToSend(connection_options);
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ connection_.SetFromConfig(config);
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+
+ EXPECT_EQ(MESSAGE_STATUS_SUCCESS, SendMessage("message"));
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+
+ // Although there are bytes in flight, no packet gets sent on PTO firing.
+ if (GetQuicReloadableFlag(quic_send_ping_when_pto_skips_packet_number)) {
+ // PTO fires, verify a PING packet gets sent because there is no data to
+ // send.
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, QuicPacketNumber(3), _, _));
+ EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+ SendPing();
+ }));
+ } else {
+ // No packet gets sent.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ }
+ connection_.GetRetransmissionAlarm()->Fire();
+ if (GetQuicReloadableFlag(quic_send_ping_when_pto_skips_packet_number)) {
+ EXPECT_EQ(1u, connection_.GetStats().pto_count);
+ EXPECT_EQ(0u, connection_.GetStats().crypto_retransmit_count);
+ EXPECT_EQ(1u, writer_->ping_frames().size());
+ }
+}
+
} // namespace
} // namespace test
} // namespace quic