gfe-relnote: In QUIC, when detecting loss of a packet number space, do not use no packet missing optimization if largest_newly_acked != the largest packet in packets_acked. Protected by gfe2_reloadable_flag_quic_fix_packets_acked.
PiperOrigin-RevId: 253292739
Change-Id: I64e94f8eb6b256dbf47d4677732eb6e21bb78268
diff --git a/quic/core/congestion_control/general_loss_algorithm.cc b/quic/core/congestion_control/general_loss_algorithm.cc
index 8cd5e71..cf4bc35 100644
--- a/quic/core/congestion_control/general_loss_algorithm.cc
+++ b/quic/core/congestion_control/general_loss_algorithm.cc
@@ -66,8 +66,16 @@
loss_detection_timeout_ = QuicTime::Zero();
if (!packets_acked.empty() &&
packets_acked.front().packet_number == least_in_flight_) {
- if (least_in_flight_ + packets_acked.size() - 1 == largest_newly_acked) {
- // Optimization for the case when no packet is missing.
+ if (GetQuicReloadableFlag(quic_fix_packets_acked)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_fix_packets_acked);
+ }
+ if ((!GetQuicReloadableFlag(quic_fix_packets_acked) ||
+ packets_acked.back().packet_number == largest_newly_acked) &&
+ least_in_flight_ + packets_acked.size() - 1 == largest_newly_acked) {
+ // Optimization for the case when no packet is missing. Please note,
+ // packets_acked can include packets of different packet number space, so
+ // do not use this optimization if largest_newly_acked is not the largest
+ // packet in packets_acked.
least_in_flight_ = largest_newly_acked + 1;
largest_previously_acked_ = largest_newly_acked;
return;
diff --git a/quic/core/congestion_control/uber_loss_algorithm_test.cc b/quic/core/congestion_control/uber_loss_algorithm_test.cc
index 1fe3465..307d791 100644
--- a/quic/core/congestion_control/uber_loss_algorithm_test.cc
+++ b/quic/core/congestion_control/uber_loss_algorithm_test.cc
@@ -159,6 +159,40 @@
VerifyLosses(5, packets_acked_, std::vector<uint64_t>{2, 3});
}
+// Regression test for b/133771183.
+TEST_F(UberLossAlgorithmTest, PacketInLimbo) {
+ // This test mimics a scenario: server sends 1-SHLO, 2-1RTT, 3-1RTT,
+ // 4-retransmit SHLO. Client receives and ACKs packets 1, 3 and 4.
+ QuicUnackedPacketMapPeer::SetPerspective(unacked_packets_.get(),
+ Perspective::IS_SERVER);
+
+ SendPacket(1, ENCRYPTION_ZERO_RTT);
+ SendPacket(2, ENCRYPTION_FORWARD_SECURE);
+ SendPacket(3, ENCRYPTION_FORWARD_SECURE);
+ SendPacket(4, ENCRYPTION_ZERO_RTT);
+
+ SendPacket(5, ENCRYPTION_FORWARD_SECURE);
+ AckPackets({1, 3, 4});
+ unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace(
+ APPLICATION_DATA, QuicPacketNumber(3));
+ unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace(
+ HANDSHAKE_DATA, QuicPacketNumber(4));
+ // No packet loss detected.
+ VerifyLosses(4, packets_acked_, std::vector<uint64_t>{});
+
+ SendPacket(6, ENCRYPTION_FORWARD_SECURE);
+ AckPackets({5, 6});
+ unacked_packets_->MaybeUpdateLargestAckedOfPacketNumberSpace(
+ APPLICATION_DATA, QuicPacketNumber(6));
+ if (GetQuicReloadableFlag(quic_fix_packets_acked)) {
+ // Verify packet 2 is detected lost.
+ VerifyLosses(6, packets_acked_, std::vector<uint64_t>{2});
+ } else {
+ // No losses, packet 2 is in limbo.
+ VerifyLosses(6, packets_acked_, std::vector<uint64_t>{});
+ }
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc
index 1e8302f..58d8b14 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -68,16 +68,23 @@
HANDSHAKE_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
}
- void RetransmitDataPacket(uint64_t packet_number, TransmissionType type) {
+ void RetransmitDataPacket(uint64_t packet_number,
+ TransmissionType type,
+ EncryptionLevel level) {
EXPECT_CALL(
*send_algorithm_,
OnPacketSent(_, BytesInFlight(), QuicPacketNumber(packet_number),
kDefaultLength, HAS_RETRANSMITTABLE_DATA));
SerializedPacket packet(CreatePacket(packet_number, true));
+ packet.encryption_level = level;
manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(), type,
HAS_RETRANSMITTABLE_DATA);
}
+ void RetransmitDataPacket(uint64_t packet_number, TransmissionType type) {
+ RetransmitDataPacket(packet_number, type, ENCRYPTION_INITIAL);
+ }
+
protected:
QuicSentPacketManagerTest()
: manager_(Perspective::IS_SERVER,
@@ -319,12 +326,19 @@
}
void SendAckPacket(uint64_t packet_number, uint64_t largest_acked) {
+ SendAckPacket(packet_number, largest_acked, ENCRYPTION_INITIAL);
+ }
+
+ void SendAckPacket(uint64_t packet_number,
+ uint64_t largest_acked,
+ EncryptionLevel level) {
EXPECT_CALL(
*send_algorithm_,
OnPacketSent(_, BytesInFlight(), QuicPacketNumber(packet_number),
kDefaultLength, NO_RETRANSMITTABLE_DATA));
SerializedPacket packet(CreatePacket(packet_number, false));
packet.largest_acked = QuicPacketNumber(largest_acked);
+ packet.encryption_level = level;
manager_.OnPacketSent(&packet, QuicPacketNumber(), clock_.Now(),
NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA);
}
@@ -2830,6 +2844,65 @@
manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_HANDSHAKE));
}
+// Regression test for b/133771183.
+TEST_P(QuicSentPacketManagerTest, PacketInLimbo) {
+ if (!manager_.session_decides_what_to_write()) {
+ return;
+ }
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+ // Send SHLO.
+ SendCryptoPacket(1);
+ // Send data packet.
+ SendDataPacket(2, ENCRYPTION_FORWARD_SECURE);
+ // Send Ack Packet.
+ SendAckPacket(3, 1, ENCRYPTION_FORWARD_SECURE);
+ // Retransmit SHLO.
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(InvokeWithoutArgs([this]() { RetransmitCryptoPacket(4); }));
+ manager_.OnRetransmissionTimeout();
+
+ // Successfully decrypt a forward secure packet.
+ manager_.SetHandshakeConfirmed();
+ EXPECT_CALL(notifier_, HasUnackedCryptoData()).WillRepeatedly(Return(false));
+ // Send Ack packet.
+ SendAckPacket(5, 2, ENCRYPTION_FORWARD_SECURE);
+
+ // Retransmission alarm fires.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+ RetransmitDataPacket(6, type, ENCRYPTION_FORWARD_SECURE);
+ })));
+ manager_.MaybeRetransmitTailLossProbe();
+
+ // Received Ack of packets 1, 3 and 4.
+ uint64_t acked[] = {1, 3, 4};
+ ExpectAcksAndLosses(true, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
+ manager_.OnAckFrameStart(QuicPacketNumber(4), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(5));
+ manager_.OnAckRange(QuicPacketNumber(1), QuicPacketNumber(2));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+
+ uint64_t acked2[] = {5, 6};
+ uint64_t loss[] = {2};
+ if (GetQuicReloadableFlag(quic_fix_packets_acked)) {
+ // Verify packet 2 is detected lost.
+ EXPECT_CALL(notifier_, OnFrameLost(_)).Times(1);
+ ExpectAcksAndLosses(true, acked2, QUIC_ARRAYSIZE(acked2), loss,
+ QUIC_ARRAYSIZE(loss));
+ } else {
+ // Packet 2 in limbo, bummer.
+ ExpectAcksAndLosses(true, acked2, QUIC_ARRAYSIZE(acked2), nullptr, 0);
+ }
+ manager_.OnAckFrameStart(QuicPacketNumber(6), QuicTime::Delta::Infinite(),
+ clock_.Now());
+ manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(7));
+ EXPECT_EQ(PACKETS_NEWLY_ACKED,
+ manager_.OnAckFrameEnd(clock_.Now(), ENCRYPTION_INITIAL));
+}
+
} // namespace
} // namespace test
} // namespace quic