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 })