gfe-relnote: In QUIC, add adaptive reordering thresholds. Deprecate SpuriousRetransmitDetected and call SpuriousLossDetected instead. Protected by gfe2_reloadable_flag_quic_detect_spurious_loss.

PiperOrigin-RevId: 268541613
Change-Id: I861b74d6cd261c30affc3e4bc1874fee4c4f3ab4
diff --git a/quic/core/congestion_control/general_loss_algorithm.cc b/quic/core/congestion_control/general_loss_algorithm.cc
index 02b2129..805ee83 100644
--- a/quic/core/congestion_control/general_loss_algorithm.cc
+++ b/quic/core/congestion_control/general_loss_algorithm.cc
@@ -31,6 +31,8 @@
 
 GeneralLossAlgorithm::GeneralLossAlgorithm(LossDetectionType loss_type)
     : loss_detection_timeout_(QuicTime::Zero()),
+      reordering_threshold_(kNumberOfNacksBeforeRetransmission),
+      use_adaptive_reordering_threshold_(false),
       least_in_flight_(1),
       packet_number_space_(NUM_PACKET_NUMBER_SPACES) {
   SetLossDetectionType(loss_type);
@@ -113,8 +115,7 @@
 
     if (loss_type_ == kNack) {
       // FACK based loss detection.
-      if (largest_newly_acked - packet_number >=
-          kNumberOfNacksBeforeRetransmission) {
+      if (largest_newly_acked - packet_number >= reordering_threshold_) {
         packets_lost->push_back(LostPacket(packet_number, it->bytes_sent));
         continue;
       }
@@ -207,6 +208,35 @@
   } while (proposed_extra_time < extra_time_needed && reordering_shift_ > 0);
 }
 
+void GeneralLossAlgorithm::SpuriousLossDetected(
+    const QuicUnackedPacketMap& unacked_packets,
+    const RttStats& rtt_stats,
+    QuicTime ack_receive_time,
+    QuicPacketNumber packet_number,
+    QuicPacketNumber previous_largest_acked) {
+  if (loss_type_ == kAdaptiveTime && reordering_shift_ > 0) {
+    // Increase reordering fraction such that the packet would not have been
+    // declared lost.
+    QuicTime::Delta time_needed =
+        ack_receive_time -
+        unacked_packets.GetTransmissionInfo(packet_number).sent_time;
+    QuicTime::Delta max_rtt =
+        std::max(rtt_stats.previous_srtt(), rtt_stats.latest_rtt());
+    while (max_rtt + (max_rtt >> reordering_shift_) < time_needed &&
+           reordering_shift_ > 0) {
+      --reordering_shift_;
+    }
+  }
+
+  if (use_adaptive_reordering_threshold_) {
+    DCHECK_LT(packet_number, previous_largest_acked);
+    // Increase reordering_threshold_ such that packet_number would not have
+    // been declared lost.
+    reordering_threshold_ = std::max(
+        reordering_threshold_, previous_largest_acked - packet_number + 1);
+  }
+}
+
 void GeneralLossAlgorithm::SetPacketNumberSpace(
     PacketNumberSpace packet_number_space) {
   if (packet_number_space_ < NUM_PACKET_NUMBER_SPACES) {
diff --git a/quic/core/congestion_control/general_loss_algorithm.h b/quic/core/congestion_control/general_loss_algorithm.h
index 089387f..3bde91b 100644
--- a/quic/core/congestion_control/general_loss_algorithm.h
+++ b/quic/core/congestion_control/general_loss_algorithm.h
@@ -54,10 +54,21 @@
       const RttStats& rtt_stats,
       QuicPacketNumber spurious_retransmission) override;
 
+  // Called to increases time and/or packet threshold.
+  void SpuriousLossDetected(const QuicUnackedPacketMap& unacked_packets,
+                            const RttStats& rtt_stats,
+                            QuicTime ack_receive_time,
+                            QuicPacketNumber packet_number,
+                            QuicPacketNumber previous_largest_acked) override;
+
   void SetPacketNumberSpace(PacketNumberSpace packet_number_space);
 
   int reordering_shift() const { return reordering_shift_; }
 
+  void enable_adaptive_reordering_threshold() {
+    use_adaptive_reordering_threshold_ = true;
+  }
+
  private:
   QuicTime loss_detection_timeout_;
   // Largest sent packet when a spurious retransmit is detected.
@@ -68,6 +79,10 @@
   // loss.  Fraction calculated by shifting max(SRTT, latest_rtt) to the right
   // by reordering_shift.
   int reordering_shift_;
+  // Reordering threshold for loss detection.
+  QuicPacketCount reordering_threshold_;
+  // If true, uses adaptive reordering threshold for loss detection.
+  bool use_adaptive_reordering_threshold_;
   // The largest newly acked from the previous call to DetectLosses.
   QuicPacketNumber largest_previously_acked_;
   // The least in flight packet. Loss detection should start from this. Please
diff --git a/quic/core/congestion_control/general_loss_algorithm_test.cc b/quic/core/congestion_control/general_loss_algorithm_test.cc
index c03ad90..8c200cc 100644
--- a/quic/core/congestion_control/general_loss_algorithm_test.cc
+++ b/quic/core/congestion_control/general_loss_algorithm_test.cc
@@ -514,6 +514,13 @@
   // Advance the time 1/4 RTT and indicate the loss was spurious.
   // The new threshold should be 1/2 RTT.
   clock_.AdvanceTime(rtt_stats_.smoothed_rtt() * (1.0f / 4));
+  if (GetQuicReloadableFlag(quic_detect_spurious_loss)) {
+    loss_algorithm_.SpuriousLossDetected(unacked_packets_, rtt_stats_,
+                                         clock_.Now(), QuicPacketNumber(1),
+                                         QuicPacketNumber(2));
+    EXPECT_EQ(1, loss_algorithm_.reordering_shift());
+    return;
+  }
   loss_algorithm_.SpuriousRetransmitDetected(unacked_packets_, clock_.Now(),
                                              rtt_stats_, QuicPacketNumber(11));
   EXPECT_EQ(1, loss_algorithm_.reordering_shift());
@@ -525,6 +532,69 @@
   EXPECT_EQ(1, loss_algorithm_.reordering_shift());
 }
 
+TEST_F(GeneralLossAlgorithmTest, IncreaseReorderingThresholdUponSpuriousLoss) {
+  loss_algorithm_.enable_adaptive_reordering_threshold();
+  for (size_t i = 1; i <= 4; ++i) {
+    SendDataPacket(i);
+  }
+  // Acking 4 causes 1 detected lost.
+  AckedPacketVector packets_acked;
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(4));
+  packets_acked.push_back(AckedPacket(
+      QuicPacketNumber(4), kMaxOutgoingPacketSize, QuicTime::Zero()));
+  VerifyLosses(4, packets_acked, std::vector<uint64_t>{1});
+  packets_acked.clear();
+
+  // Retransmit 1 as 5.
+  SendDataPacket(5);
+
+  // Acking 1 such that it was detected lost spuriously.
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(1));
+  packets_acked.push_back(AckedPacket(
+      QuicPacketNumber(1), kMaxOutgoingPacketSize, QuicTime::Zero()));
+  loss_algorithm_.SpuriousLossDetected(unacked_packets_, rtt_stats_,
+                                       clock_.Now(), QuicPacketNumber(1),
+                                       QuicPacketNumber(4));
+  VerifyLosses(4, packets_acked, std::vector<uint64_t>{});
+  packets_acked.clear();
+
+  // Verify acking 5 does not cause 2 detected lost.
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(5));
+  packets_acked.push_back(AckedPacket(
+      QuicPacketNumber(5), kMaxOutgoingPacketSize, QuicTime::Zero()));
+  VerifyLosses(5, packets_acked, std::vector<uint64_t>{});
+  packets_acked.clear();
+
+  SendDataPacket(6);
+
+  // Acking 6 will causes 2 detected lost.
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(6));
+  packets_acked.push_back(AckedPacket(
+      QuicPacketNumber(6), kMaxOutgoingPacketSize, QuicTime::Zero()));
+  VerifyLosses(6, packets_acked, std::vector<uint64_t>{2});
+  packets_acked.clear();
+
+  // Retransmit 2 as 7.
+  SendDataPacket(7);
+
+  // Acking 2 such that it was detected lost spuriously.
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(2));
+  packets_acked.push_back(AckedPacket(
+      QuicPacketNumber(2), kMaxOutgoingPacketSize, QuicTime::Zero()));
+  loss_algorithm_.SpuriousLossDetected(unacked_packets_, rtt_stats_,
+                                       clock_.Now(), QuicPacketNumber(2),
+                                       QuicPacketNumber(6));
+  VerifyLosses(6, packets_acked, std::vector<uint64_t>{});
+  packets_acked.clear();
+
+  // Acking 7 will not cause 3 as detected lost.
+  unacked_packets_.RemoveFromInFlight(QuicPacketNumber(7));
+  packets_acked.push_back(AckedPacket(
+      QuicPacketNumber(7), kMaxOutgoingPacketSize, QuicTime::Zero()));
+  VerifyLosses(7, packets_acked, std::vector<uint64_t>{});
+  packets_acked.clear();
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/congestion_control/loss_detection_interface.h b/quic/core/congestion_control/loss_detection_interface.h
index 7439d3f..f686785 100644
--- a/quic/core/congestion_control/loss_detection_interface.h
+++ b/quic/core/congestion_control/loss_detection_interface.h
@@ -37,11 +37,21 @@
 
   // Called when a |spurious_retransmission| is detected.  The original
   // transmission must have been caused by DetectLosses.
+  // TODO(fayang): Remove this method when deprecating
+  // quic_detect_spurious_loss.
   virtual void SpuriousRetransmitDetected(
       const QuicUnackedPacketMap& unacked_packets,
       QuicTime time,
       const RttStats& rtt_stats,
       QuicPacketNumber spurious_retransmission) = 0;
+
+  // Called when |packet_number| was detected lost but gets acked later.
+  virtual void SpuriousLossDetected(
+      const QuicUnackedPacketMap& unacked_packets,
+      const RttStats& rtt_stats,
+      QuicTime ack_receive_time,
+      QuicPacketNumber packet_number,
+      QuicPacketNumber previous_largest_acked) = 0;
 };
 
 }  // namespace quic
diff --git a/quic/core/congestion_control/uber_loss_algorithm.cc b/quic/core/congestion_control/uber_loss_algorithm.cc
index e0dff98..e3b526a 100644
--- a/quic/core/congestion_control/uber_loss_algorithm.cc
+++ b/quic/core/congestion_control/uber_loss_algorithm.cc
@@ -81,4 +81,15 @@
                                   spurious_retransmission);
 }
 
+void UberLossAlgorithm::SpuriousLossDetected(
+    const QuicUnackedPacketMap& unacked_packets,
+    const RttStats& rtt_stats,
+    QuicTime ack_receive_time,
+    QuicPacketNumber packet_number,
+    QuicPacketNumber previous_largest_acked) {
+  general_loss_algorithms_[unacked_packets.GetPacketNumberSpace(packet_number)]
+      .SpuriousLossDetected(unacked_packets, rtt_stats, ack_receive_time,
+                            packet_number, previous_largest_acked);
+}
+
 }  // namespace quic
diff --git a/quic/core/congestion_control/uber_loss_algorithm.h b/quic/core/congestion_control/uber_loss_algorithm.h
index dddbcb3..558aaea 100644
--- a/quic/core/congestion_control/uber_loss_algorithm.h
+++ b/quic/core/congestion_control/uber_loss_algorithm.h
@@ -42,6 +42,13 @@
       const RttStats& rtt_stats,
       QuicPacketNumber spurious_retransmission) override;
 
+  // Called to increases time or packet threshold.
+  void SpuriousLossDetected(const QuicUnackedPacketMap& unacked_packets,
+                            const RttStats& rtt_stats,
+                            QuicTime ack_receive_time,
+                            QuicPacketNumber packet_number,
+                            QuicPacketNumber previous_largest_acked) override;
+
  private:
   LossDetectionType loss_type_;
   // One loss algorithm per packet number space.
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index e167c09..7a413d0 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -109,7 +109,9 @@
       max_probe_packets_per_pto_(2),
       consecutive_pto_count_(0),
       fix_rto_retransmission_(false),
-      handshake_mode_disabled_(false) {
+      handshake_mode_disabled_(false),
+      detect_spurious_losses_(
+          GetQuicReloadableFlag(quic_detect_spurious_loss)) {
   SetSendAlgorithm(congestion_control_type);
 }
 
@@ -535,7 +537,8 @@
     QuicPacketNumber acked_packet_number) {
   if (session_decides_what_to_write()) {
     RecordOneSpuriousRetransmission(info);
-    if (info.transmission_type == LOSS_RETRANSMISSION) {
+    if (!detect_spurious_losses_ &&
+        info.transmission_type == LOSS_RETRANSMISSION) {
       // Only inform the loss detection of spurious retransmits it caused.
       loss_algorithm_->SpuriousRetransmitDetected(
           unacked_packets_, clock_->Now(), rtt_stats_, acked_packet_number);
@@ -603,6 +606,7 @@
 
 void QuicSentPacketManager::MarkPacketHandled(QuicPacketNumber packet_number,
                                               QuicTransmissionInfo* info,
+                                              QuicTime ack_receive_time,
                                               QuicTime::Delta ack_delay_time,
                                               QuicTime receive_timestamp) {
   QuicPacketNumber newest_transmission =
@@ -635,6 +639,26 @@
         RecordSpuriousRetransmissions(*info, packet_number);
       }
     }
+    if (detect_spurious_losses_ && session_decides_what_to_write() &&
+        info->state == LOST) {
+      // Record as a spurious loss as a packet previously declared lost gets
+      // acked.
+      QUIC_RELOADABLE_FLAG_COUNT(quic_detect_spurious_loss);
+      const PacketNumberSpace packet_number_space =
+          unacked_packets_.GetPacketNumberSpace(info->encryption_level);
+      const QuicPacketNumber previous_largest_acked =
+          supports_multiple_packet_number_spaces()
+              ? unacked_packets_.GetLargestAckedOfPacketNumberSpace(
+                    packet_number_space)
+              : unacked_packets_.largest_acked();
+      QUIC_DVLOG(1) << "Packet " << packet_number
+                    << " was detected lost spuriously, "
+                       "previous_largest_acked: "
+                    << previous_largest_acked;
+      loss_algorithm_->SpuriousLossDetected(unacked_packets_, rtt_stats_,
+                                            ack_receive_time, packet_number,
+                                            previous_largest_acked);
+    }
   } else {
     DCHECK(!session_decides_what_to_write());
     RecordSpuriousRetransmissions(*info, packet_number);
@@ -1306,7 +1330,9 @@
     }
     QUIC_DVLOG(1) << ENDPOINT << "Got an "
                   << QuicUtils::EncryptionLevelToString(ack_decrypted_level)
-                  << " ack for packet " << acked_packet.packet_number;
+                  << " ack for packet " << acked_packet.packet_number
+                  << " , state: "
+                  << QuicUtils::SentPacketStateToString(info->state);
     const PacketNumberSpace packet_number_space =
         unacked_packets_.GetPacketNumberSpace(info->encryption_level);
     if (supports_multiple_packet_number_spaces() &&
@@ -1330,7 +1356,7 @@
     }
     unacked_packets_.MaybeUpdateLargestAckedOfPacketNumberSpace(
         packet_number_space, acked_packet.packet_number);
-    MarkPacketHandled(acked_packet.packet_number, info,
+    MarkPacketHandled(acked_packet.packet_number, info, ack_receive_time,
                       last_ack_frame_.ack_delay_time,
                       acked_packet.receive_timestamp);
   }
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index e50a181..cbb00a9 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -491,6 +491,7 @@
   // |info| due to receipt by the peer.
   void MarkPacketHandled(QuicPacketNumber packet_number,
                          QuicTransmissionInfo* info,
+                         QuicTime ack_receive_time,
                          QuicTime::Delta ack_delay_time,
                          QuicTime receive_timestamp);
 
@@ -656,6 +657,9 @@
 
   // True if HANDSHAKE mode has been disabled.
   bool handshake_mode_disabled_;
+
+  // Latched value of quic_detect_spurious_loss.
+  const bool detect_spurious_losses_;
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc
index be22dee..b0806e5 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -774,8 +774,15 @@
     uint64_t acked[] = {3};
     ExpectAcksAndLosses(false, acked, QUIC_ARRAYSIZE(acked), nullptr, 0);
     EXPECT_CALL(*loss_algorithm, DetectLosses(_, _, _, _, _, _));
-    EXPECT_CALL(*loss_algorithm,
-                SpuriousRetransmitDetected(_, _, _, QuicPacketNumber(5)));
+    if (GetQuicReloadableFlag(quic_detect_spurious_loss) &&
+        manager_.session_decides_what_to_write()) {
+      EXPECT_CALL(*loss_algorithm,
+                  SpuriousLossDetected(_, _, _, QuicPacketNumber(3),
+                                       QuicPacketNumber(4)));
+    } else {
+      EXPECT_CALL(*loss_algorithm,
+                  SpuriousRetransmitDetected(_, _, _, QuicPacketNumber(5)));
+    }
     manager_.OnAckFrameStart(QuicPacketNumber(4), QuicTime::Delta::Infinite(),
                              clock_.Now());
     manager_.OnAckRange(QuicPacketNumber(3), QuicPacketNumber(5));