In quic, coalesce packet of higher encryption level with initial or handshake retransmission. protected by gfe2_reloadable_flag_quic_coalesced_packet_of_higher_space.
The goal of this change is to mitigate inflated RTT sample.
PiperOrigin-RevId: 321556627
Change-Id: I7fc8675bb0f05a48b6e81ed4aa9c04b42fb0690a
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 62aa83f..de7e450 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -10399,7 +10399,12 @@
_, _));
EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(1);
connection_.GetRetransmissionAlarm()->Fire();
- EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet());
+ if (GetQuicReloadableFlag(quic_coalesced_packet_of_higher_space)) {
+ // Verify 1-RTT packet gets coalesced with handshake retransmission.
+ EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet());
+ } else {
+ EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet());
+ }
// Send application data.
connection_.SendApplicationDataAtLevel(ENCRYPTION_FORWARD_SECURE, 5, "data",
@@ -10410,15 +10415,24 @@
// Retransmit handshake data again.
clock_.AdvanceTime(retransmission_time - clock_.Now());
+ QuicPacketNumber handshake_retransmission =
+ GetQuicReloadableFlag(quic_default_on_pto) ? QuicPacketNumber(5)
+ : QuicPacketNumber(7);
+ if (GetQuicReloadableFlag(quic_coalesced_packet_of_higher_space)) {
+ handshake_retransmission += 1;
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, handshake_retransmission + 1, _, _));
+ }
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _,
- GetQuicReloadableFlag(quic_default_on_pto)
- ? QuicPacketNumber(5)
- : QuicPacketNumber(7),
- _, _));
+ OnPacketSent(_, _, handshake_retransmission, _, _));
EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(1);
connection_.GetRetransmissionAlarm()->Fire();
- EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet());
+ if (GetQuicReloadableFlag(quic_coalesced_packet_of_higher_space)) {
+ // Verify 1-RTT packet gets coalesced with handshake retransmission.
+ EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet());
+ } else {
+ EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet());
+ }
// Discard handshake key.
connection_.OnHandshakeComplete();
@@ -10427,12 +10441,14 @@
// Retransmit application data.
clock_.AdvanceTime(retransmission_time - clock_.Now());
+ QuicPacketNumber application_retransmission =
+ GetQuicReloadableFlag(quic_default_on_pto) ? QuicPacketNumber(6)
+ : QuicPacketNumber(9);
+ if (GetQuicReloadableFlag(quic_coalesced_packet_of_higher_space)) {
+ application_retransmission += 2;
+ }
EXPECT_CALL(*send_algorithm_,
- OnPacketSent(_, _,
- GetQuicReloadableFlag(quic_default_on_pto)
- ? QuicPacketNumber(6)
- : QuicPacketNumber(9),
- _, _));
+ OnPacketSent(_, _, application_retransmission, _, _));
connection_.GetRetransmissionAlarm()->Fire();
EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet());
}
@@ -11217,7 +11233,11 @@
connection_.SetEncrypter(ENCRYPTION_HANDSHAKE,
std::make_unique<TaggingEncrypter>(0x02));
connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE);
- EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(1);
+ if (GetQuicReloadableFlag(quic_coalesced_packet_of_higher_space)) {
+ EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(2);
+ } else {
+ EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(1);
+ }
connection_.SendCryptoDataWithString("foo", 0, ENCRYPTION_HANDSHAKE);
// Verify PTO time does not change.
EXPECT_EQ(expected_pto_time,
@@ -11356,6 +11376,89 @@
}
}
+// Regression test for b/161228202
+TEST_P(QuicConnectionTest, InflatedRttSample) {
+ if (!connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ // 30ms RTT.
+ const QuicTime::Delta kTestRTT = QuicTime::Delta::FromMilliseconds(30);
+ set_perspective(Perspective::IS_SERVER);
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats());
+ use_tagging_decrypter();
+ // Receives packet 1000 in initial data.
+ if (QuicVersionUsesCryptoFrames(connection_.transport_version())) {
+ EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber());
+ }
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
+ ProcessCryptoPacketAtLevel(1000, ENCRYPTION_INITIAL);
+ EXPECT_TRUE(connection_.HasPendingAcks());
+
+ connection_.SetEncrypter(ENCRYPTION_INITIAL,
+ std::make_unique<TaggingEncrypter>(0x01));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+ // Send INITIAL 1.
+ connection_.SendCryptoDataWithString(std::string(512, 'a'), 0,
+ ENCRYPTION_INITIAL);
+ ASSERT_TRUE(connection_.sent_packet_manager()
+ .GetRetransmissionTime()
+ .IsInitialized());
+ QuicTime::Delta pto_timeout =
+ connection_.sent_packet_manager().GetRetransmissionTime() - clock_.Now();
+ // Send Handshake 2.
+ connection_.SetEncrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<TaggingEncrypter>(0x02));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE);
+ if (GetQuicReloadableFlag(quic_coalesced_packet_of_higher_space)) {
+ // Verify HANDSHAKE packet is coalesced with INITIAL retransmission.
+ EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(2);
+ } else {
+ EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(1);
+ }
+ connection_.SendCryptoDataWithString(std::string(1024, 'a'), 0,
+ ENCRYPTION_HANDSHAKE);
+
+ // INITIAL 1 gets lost and PTO fires.
+ clock_.AdvanceTime(pto_timeout);
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ clock_.AdvanceTime(kTestRTT);
+ // Assume retransmitted INITIAL gets received.
+ QuicFrames frames;
+ QuicPacketNumber initial_retransmission =
+ GetQuicReloadableFlag(quic_default_on_pto) ? QuicPacketNumber(3)
+ : QuicPacketNumber(4);
+ auto ack_frame =
+ InitAckFrame({{initial_retransmission, initial_retransmission + 1}});
+ frames.push_back(QuicFrame(&ack_frame));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _))
+ .Times(AnyNumber());
+ ProcessFramesPacketAtLevel(1001, frames, ENCRYPTION_INITIAL);
+ EXPECT_EQ(kTestRTT, rtt_stats->latest_rtt());
+ // Because retransmitted INITIAL gets received so HANDSHAKE 2 gets processed.
+ frames.clear();
+ QuicAckFrame ack_frame2;
+ if (GetQuicReloadableFlag(quic_coalesced_packet_of_higher_space)) {
+ // HANDSHAKE 5 is also processed.
+ ack_frame2 = InitAckFrame(
+ {{QuicPacketNumber(2), QuicPacketNumber(3)},
+ {initial_retransmission + 1, initial_retransmission + 2}});
+ } else {
+ ack_frame2 = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}});
+ }
+ ack_frame2.ack_delay_time = QuicTime::Delta::Zero();
+ frames.push_back(QuicFrame(&ack_frame2));
+ ProcessFramesPacketAtLevel(1, frames, ENCRYPTION_HANDSHAKE);
+ if (GetQuicReloadableFlag(quic_coalesced_packet_of_higher_space)) {
+ // Verify RTT inflation gets mitigated.
+ EXPECT_EQ(rtt_stats->latest_rtt(), kTestRTT);
+ } else {
+ // Verify this RTT sample gets inflated as it includes the PTO timeout and
+ // the actual RTT.
+ EXPECT_GE(rtt_stats->latest_rtt(), pto_timeout + kTestRTT);
+ }
+}
+
} // namespace
} // namespace test
} // namespace quic