ackhandler: store the last four skipped packets (#5322)

This allows for more accurate packet number difference calculations,
which is especially important once we attempt to detect spurious loss
detection events.
This commit is contained in:
Marten Seemann
2025-08-30 14:05:59 +08:00
committed by GitHub
parent 0bef51ec76
commit 357f679072
2 changed files with 19 additions and 10 deletions

View File

@@ -3,10 +3,13 @@ package ackhandler
import ( import (
"fmt" "fmt"
"iter" "iter"
"slices"
"github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/protocol"
) )
const maxSkippedPackets = 4
type sentPacketHistory struct { type sentPacketHistory struct {
packets []*packet packets []*packet
pathProbePackets []packetWithPacketNumber pathProbePackets []packetWithPacketNumber
@@ -25,6 +28,7 @@ func newSentPacketHistory(isAppData bool) *sentPacketHistory {
} }
if isAppData { if isAppData {
h.packets = make([]*packet, 0, 32) h.packets = make([]*packet, 0, 32)
h.skippedPackets = make([]protocol.PacketNumber, 0, maxSkippedPackets)
} else { } else {
h.packets = make([]*packet, 0, 6) h.packets = make([]*packet, 0, 6)
} }
@@ -48,6 +52,9 @@ func (h *sentPacketHistory) SkippedPacket(pn protocol.PacketNumber) {
if len(h.packets) > 0 { if len(h.packets) > 0 {
h.packets = append(h.packets, nil) h.packets = append(h.packets, nil)
} }
if len(h.skippedPackets) == maxSkippedPackets {
h.skippedPackets = slices.Delete(h.skippedPackets, 0, 1)
}
h.skippedPackets = append(h.skippedPackets, pn) h.skippedPackets = append(h.skippedPackets, pn)
} }
@@ -163,13 +170,6 @@ func (h *sentPacketHistory) Remove(pn protocol.PacketNumber) error {
if len(h.packets) > 0 && h.packets[0] == nil { if len(h.packets) > 0 && h.packets[0] == nil {
panic("cleanup failed") panic("cleanup failed")
} }
if len(h.packets) > 0 && len(h.skippedPackets) > 0 {
for _, p := range h.skippedPackets {
if p < h.firstPacketNumber {
h.skippedPackets = h.skippedPackets[1:]
}
}
}
return nil return nil
} }

View File

@@ -98,7 +98,8 @@ func TestSentPacketHistoryRemovePackets(t *testing.T) {
require.Equal(t, []protocol.PacketNumber{2, 3, 5}, slices.Collect(hist.SkippedPackets())) require.Equal(t, []protocol.PacketNumber{2, 3, 5}, slices.Collect(hist.SkippedPackets()))
require.NoError(t, hist.Remove(1)) require.NoError(t, hist.Remove(1))
require.Equal(t, []protocol.PacketNumber{4, 6}, hist.getPacketNumbers()) require.Equal(t, []protocol.PacketNumber{4, 6}, hist.getPacketNumbers())
require.Equal(t, []protocol.PacketNumber{5}, slices.Collect(hist.SkippedPackets())) // skipped packets should be preserved
require.Equal(t, []protocol.PacketNumber{2, 3, 5}, slices.Collect(hist.SkippedPackets()))
// add one more packet // add one more packet
hist.SentAckElicitingPacket(7, &packet{}) hist.SentAckElicitingPacket(7, &packet{})
@@ -114,12 +115,20 @@ func TestSentPacketHistoryRemovePackets(t *testing.T) {
require.Error(t, err) require.Error(t, err)
require.EqualError(t, err, "packet 9 not found in sent packet history") require.EqualError(t, err, "packet 9 not found in sent packet history")
// only the last 4 skipped packets should be preserved
hist.SkippedPacket(9)
hist.SkippedPacket(10)
hist.SentAckElicitingPacket(11, &packet{})
hist.SkippedPacket(12)
require.Equal(t, []protocol.PacketNumber{5, 9, 10, 12}, slices.Collect(hist.SkippedPackets()))
// Remove all packets // Remove all packets
require.NoError(t, hist.Remove(4)) require.NoError(t, hist.Remove(4))
require.NoError(t, hist.Remove(6)) require.NoError(t, hist.Remove(6))
require.NoError(t, hist.Remove(8)) require.NoError(t, hist.Remove(8))
require.NoError(t, hist.Remove(11))
require.Empty(t, hist.getPacketNumbers()) require.Empty(t, hist.getPacketNumbers())
require.Empty(t, slices.Collect(hist.SkippedPackets())) require.Len(t, slices.Collect(hist.SkippedPackets()), 4)
require.False(t, hist.HasOutstandingPackets()) require.False(t, hist.HasOutstandingPackets())
} }
@@ -171,7 +180,7 @@ func TestSentPacketHistoryIterating(t *testing.T) {
} }
require.Equal(t, []protocol.PacketNumber{1, 2, 6}, packets) require.Equal(t, []protocol.PacketNumber{1, 2, 6}, packets)
require.Equal(t, []protocol.PacketNumber{4, 5}, slices.Collect(hist.SkippedPackets())) require.Equal(t, []protocol.PacketNumber{0, 4, 5}, slices.Collect(hist.SkippedPackets()))
} }
func TestSentPacketHistoryDeleteWhileIterating(t *testing.T) { func TestSentPacketHistoryDeleteWhileIterating(t *testing.T) {