github.com/daeuniverse/quic-go@v0.0.0-20240413031024-943f218e0810/internal/congestion/pacer_test.go (about)

     1  package congestion
     2  
     3  import (
     4  	"math/rand"
     5  	"time"
     6  
     7  	"github.com/daeuniverse/quic-go/internal/protocol"
     8  
     9  	. "github.com/onsi/ginkgo/v2"
    10  	. "github.com/onsi/gomega"
    11  )
    12  
    13  var _ = Describe("Pacer", func() {
    14  	var p *pacer
    15  
    16  	const packetsPerSecond = 50
    17  	var bandwidth uint64 // in bytes/s
    18  
    19  	BeforeEach(func() {
    20  		bandwidth = uint64(packetsPerSecond * initialMaxDatagramSize) // 50 full-size packets per second
    21  		// The pacer will multiply the bandwidth with 1.25 to achieve a slightly higher pacing speed.
    22  		// For the tests, cancel out this factor, so we can do the math using the exact bandwidth.
    23  		p = newPacer(func() Bandwidth { return Bandwidth(bandwidth) * BytesPerSecond * 4 / 5 })
    24  	})
    25  
    26  	It("allows a burst at the beginning", func() {
    27  		t := time.Now()
    28  		Expect(p.TimeUntilSend()).To(BeZero())
    29  		Expect(p.Budget(t)).To(BeEquivalentTo(maxBurstSizePackets * initialMaxDatagramSize))
    30  	})
    31  
    32  	It("allows a big burst for high pacing rates", func() {
    33  		t := time.Now()
    34  		bandwidth = uint64(10000 * packetsPerSecond * initialMaxDatagramSize)
    35  		Expect(p.TimeUntilSend()).To(BeZero())
    36  		Expect(p.Budget(t)).To(BeNumerically(">", maxBurstSizePackets*initialMaxDatagramSize))
    37  	})
    38  
    39  	It("reduces the budget when sending packets", func() {
    40  		t := time.Now()
    41  		budget := p.Budget(t)
    42  		for budget > 0 {
    43  			Expect(p.TimeUntilSend()).To(BeZero())
    44  			Expect(p.Budget(t)).To(Equal(budget))
    45  			p.SentPacket(t, initialMaxDatagramSize)
    46  			budget -= initialMaxDatagramSize
    47  		}
    48  		Expect(p.Budget(t)).To(BeZero())
    49  		Expect(p.TimeUntilSend()).ToNot(BeZero())
    50  	})
    51  
    52  	sendBurst := func(t time.Time) {
    53  		for p.Budget(t) > 0 {
    54  			p.SentPacket(t, initialMaxDatagramSize)
    55  		}
    56  	}
    57  
    58  	It("paces packets after a burst", func() {
    59  		t := time.Now()
    60  		sendBurst(t)
    61  		// send 100 exactly paced packets
    62  		for i := 0; i < 100; i++ {
    63  			t2 := p.TimeUntilSend()
    64  			Expect(t2.Sub(t)).To(BeNumerically("~", time.Second/packetsPerSecond, time.Nanosecond))
    65  			Expect(p.Budget(t2)).To(BeEquivalentTo(initialMaxDatagramSize))
    66  			p.SentPacket(t2, initialMaxDatagramSize)
    67  			t = t2
    68  		}
    69  	})
    70  
    71  	It("accounts for non-full-size packets", func() {
    72  		t := time.Now()
    73  		sendBurst(t)
    74  		t2 := p.TimeUntilSend()
    75  		Expect(t2.Sub(t)).To(BeNumerically("~", time.Second/packetsPerSecond, time.Nanosecond))
    76  		// send a half-full packet
    77  		Expect(p.Budget(t2)).To(BeEquivalentTo(initialMaxDatagramSize))
    78  		size := initialMaxDatagramSize / 2
    79  		p.SentPacket(t2, size)
    80  		Expect(p.Budget(t2)).To(Equal(initialMaxDatagramSize - size))
    81  		Expect(p.TimeUntilSend()).To(BeTemporally("~", t2.Add(time.Second/packetsPerSecond/2), time.Nanosecond))
    82  	})
    83  
    84  	It("accumulates budget, if no packets are sent", func() {
    85  		t := time.Now()
    86  		sendBurst(t)
    87  		t2 := p.TimeUntilSend()
    88  		Expect(t2).To(BeTemporally(">", t))
    89  		// wait for 5 times the duration
    90  		Expect(p.Budget(t.Add(5 * t2.Sub(t)))).To(BeEquivalentTo(5 * initialMaxDatagramSize))
    91  	})
    92  
    93  	It("accumulates budget, if no packets are sent, for larger packet sizes", func() {
    94  		t := time.Now()
    95  		sendBurst(t)
    96  		const packetSize = initialMaxDatagramSize + 200
    97  		p.SetMaxDatagramSize(packetSize)
    98  		t2 := p.TimeUntilSend()
    99  		Expect(t2).To(BeTemporally(">", t))
   100  		// wait for 5 times the duration
   101  		Expect(p.Budget(t.Add(5 * t2.Sub(t)))).To(BeEquivalentTo(5 * packetSize))
   102  	})
   103  
   104  	It("has enough budget for at least one packet when the timer expires", func() {
   105  		t := time.Now()
   106  		sendBurst(t)
   107  		for bw := uint64(100); bw < uint64(5*initialMaxDatagramSize); bw++ {
   108  			bandwidth = bw // reduce the bandwidth to 5 packet per second
   109  			t2 := p.TimeUntilSend()
   110  			Expect(t2).To(BeTemporally(">", t))
   111  			Expect(p.Budget(t2)).To(BeNumerically(">=", initialMaxDatagramSize))
   112  		}
   113  	})
   114  
   115  	It("never allows bursts larger than the maximum burst size", func() {
   116  		t := time.Now()
   117  		sendBurst(t)
   118  		Expect(p.Budget(t.Add(time.Hour))).To(BeEquivalentTo(maxBurstSizePackets * initialMaxDatagramSize))
   119  	})
   120  
   121  	It("never allows bursts larger than the maximum burst size, for larger packets", func() {
   122  		t := time.Now()
   123  		const packetSize = initialMaxDatagramSize + 200
   124  		p.SetMaxDatagramSize(packetSize)
   125  		sendBurst(t)
   126  		Expect(p.Budget(t.Add(time.Hour))).To(BeEquivalentTo(maxBurstSizePackets * packetSize))
   127  	})
   128  
   129  	It("changes the bandwidth", func() {
   130  		t := time.Now()
   131  		sendBurst(t)
   132  		bandwidth = uint64(5 * initialMaxDatagramSize) // reduce the bandwidth to 5 packet per second
   133  		Expect(p.TimeUntilSend()).To(Equal(t.Add(time.Second / 5)))
   134  	})
   135  
   136  	It("doesn't pace faster than the minimum pacing duration", func() {
   137  		t := time.Now()
   138  		sendBurst(t)
   139  		bandwidth = uint64(1e6 * initialMaxDatagramSize)
   140  		Expect(p.TimeUntilSend()).To(Equal(t.Add(protocol.MinPacingDelay)))
   141  		Expect(p.Budget(t.Add(protocol.MinPacingDelay))).To(Equal(protocol.ByteCount(protocol.MinPacingDelay) * initialMaxDatagramSize * 1e6 / 1e9))
   142  	})
   143  
   144  	It("protects against overflows", func() {
   145  		p = newPacer(func() Bandwidth { return infBandwidth })
   146  		t := time.Now()
   147  		p.SentPacket(t, initialMaxDatagramSize)
   148  		for i := 0; i < 1e5; i++ {
   149  			Expect(p.Budget(t.Add(time.Duration(rand.Int63())))).To(BeNumerically(">=", 0))
   150  		}
   151  	})
   152  })