diff --git a/quic/api/QuicTransportBase.cpp b/quic/api/QuicTransportBase.cpp index 920f2b8ba..3e48f6496 100644 --- a/quic/api/QuicTransportBase.cpp +++ b/quic/api/QuicTransportBase.cpp @@ -2770,6 +2770,11 @@ void QuicTransportBase::scheduleAckTimeout() { if (!ackTimeout_.isScheduled()) { auto factoredRtt = std::chrono::duration_cast( kAckTimerFactor * conn_->lossState.srtt); + // If we are using ACK_FREQUENCY, disable the factored RTT heuristic + // and only use the update max ACK delay. + if (conn_->ackStates.appDataAckState.ackFrequencySequenceNumber) { + factoredRtt = conn_->ackStates.maxAckDelay; + } auto& wheelTimer = getEventBase()->timer(); auto timeout = timeMax( std::chrono::duration_cast( diff --git a/quic/api/test/QuicTransportTest.cpp b/quic/api/test/QuicTransportTest.cpp index 75ee3b6d0..cae4c729a 100644 --- a/quic/api/test/QuicTransportTest.cpp +++ b/quic/api/test/QuicTransportTest.cpp @@ -4343,6 +4343,36 @@ TEST_F(QuicTransportTest, ScheduleAckTimeout) { EXPECT_NEAR(transport_->getAckTimeout()->getTimeRemaining().count(), 25, 5); } +TEST_F(QuicTransportTest, ScheduleAckTimeoutSRTTFactor) { + transport_->getConnectionState().lossState.srtt = 50ms; + EXPECT_FALSE(transport_->getAckTimeout()->isScheduled()); + transport_->getConnectionState().pendingEvents.scheduleAckTimeout = true; + transport_->onNetworkData( + SocketAddress("::1", 10003), + NetworkData( + IOBuf::copyBuffer("Never on time, always timeout"), Clock::now())); + EXPECT_TRUE(transport_->getAckTimeout()->isScheduled()); + EXPECT_NEAR( + transport_->getAckTimeout()->getTimeRemaining().count(), 50 / 4, 2); +} + +TEST_F(QuicTransportTest, ScheduleAckTimeoutAckFreq) { + transport_->getConnectionState().lossState.srtt = 50ms; + transport_->getConnectionState().transportSettings.minAckDelay = 1ms; + transport_->getConnectionState() + .ackStates.appDataAckState.ackFrequencySequenceNumber = 1; + transport_->getConnectionState().ackStates.maxAckDelay = 50ms / 3; + EXPECT_FALSE(transport_->getAckTimeout()->isScheduled()); + transport_->getConnectionState().pendingEvents.scheduleAckTimeout = true; + transport_->onNetworkData( + SocketAddress("::1", 10003), + NetworkData( + IOBuf::copyBuffer("Never on time, always timeout"), Clock::now())); + EXPECT_TRUE(transport_->getAckTimeout()->isScheduled()); + EXPECT_NEAR( + transport_->getAckTimeout()->getTimeRemaining().count(), 50 / 3, 2); +} + TEST_F(QuicTransportTest, ScheduleAckTimeoutFromMaxAckDelay) { // Make srtt large so we will use maxAckDelay transport_->getConnectionState().lossState.srtt = 25000000us;