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