mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-01 19:27:35 +03:00
152 lines
4.9 KiB
Go
152 lines
4.9 KiB
Go
package congestion
|
|
|
|
import (
|
|
"math/rand"
|
|
"time"
|
|
|
|
"github.com/refraction-networking/uquic/internal/protocol"
|
|
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
var _ = Describe("Pacer", func() {
|
|
var p *pacer
|
|
|
|
const packetsPerSecond = 50
|
|
var bandwidth uint64 // in bytes/s
|
|
|
|
BeforeEach(func() {
|
|
bandwidth = uint64(packetsPerSecond * initialMaxDatagramSize) // 50 full-size packets per second
|
|
// The pacer will multiply the bandwidth with 1.25 to achieve a slightly higher pacing speed.
|
|
// For the tests, cancel out this factor, so we can do the math using the exact bandwidth.
|
|
p = newPacer(func() Bandwidth { return Bandwidth(bandwidth) * BytesPerSecond * 4 / 5 })
|
|
})
|
|
|
|
It("allows a burst at the beginning", func() {
|
|
t := time.Now()
|
|
Expect(p.TimeUntilSend()).To(BeZero())
|
|
Expect(p.Budget(t)).To(BeEquivalentTo(maxBurstSizePackets * initialMaxDatagramSize))
|
|
})
|
|
|
|
It("allows a big burst for high pacing rates", func() {
|
|
t := time.Now()
|
|
bandwidth = uint64(10000 * packetsPerSecond * initialMaxDatagramSize)
|
|
Expect(p.TimeUntilSend()).To(BeZero())
|
|
Expect(p.Budget(t)).To(BeNumerically(">", maxBurstSizePackets*initialMaxDatagramSize))
|
|
})
|
|
|
|
It("reduces the budget when sending packets", func() {
|
|
t := time.Now()
|
|
budget := p.Budget(t)
|
|
for budget > 0 {
|
|
Expect(p.TimeUntilSend()).To(BeZero())
|
|
Expect(p.Budget(t)).To(Equal(budget))
|
|
p.SentPacket(t, initialMaxDatagramSize)
|
|
budget -= initialMaxDatagramSize
|
|
}
|
|
Expect(p.Budget(t)).To(BeZero())
|
|
Expect(p.TimeUntilSend()).ToNot(BeZero())
|
|
})
|
|
|
|
sendBurst := func(t time.Time) {
|
|
for p.Budget(t) > 0 {
|
|
p.SentPacket(t, initialMaxDatagramSize)
|
|
}
|
|
}
|
|
|
|
It("paces packets after a burst", func() {
|
|
t := time.Now()
|
|
sendBurst(t)
|
|
// send 100 exactly paced packets
|
|
for i := 0; i < 100; i++ {
|
|
t2 := p.TimeUntilSend()
|
|
Expect(t2.Sub(t)).To(BeNumerically("~", time.Second/packetsPerSecond, time.Nanosecond))
|
|
Expect(p.Budget(t2)).To(BeEquivalentTo(initialMaxDatagramSize))
|
|
p.SentPacket(t2, initialMaxDatagramSize)
|
|
t = t2
|
|
}
|
|
})
|
|
|
|
It("accounts for non-full-size packets", func() {
|
|
t := time.Now()
|
|
sendBurst(t)
|
|
t2 := p.TimeUntilSend()
|
|
Expect(t2.Sub(t)).To(BeNumerically("~", time.Second/packetsPerSecond, time.Nanosecond))
|
|
// send a half-full packet
|
|
Expect(p.Budget(t2)).To(BeEquivalentTo(initialMaxDatagramSize))
|
|
size := initialMaxDatagramSize / 2
|
|
p.SentPacket(t2, size)
|
|
Expect(p.Budget(t2)).To(Equal(initialMaxDatagramSize - size))
|
|
Expect(p.TimeUntilSend()).To(BeTemporally("~", t2.Add(time.Second/packetsPerSecond/2), time.Nanosecond))
|
|
})
|
|
|
|
It("accumulates budget, if no packets are sent", func() {
|
|
t := time.Now()
|
|
sendBurst(t)
|
|
t2 := p.TimeUntilSend()
|
|
Expect(t2).To(BeTemporally(">", t))
|
|
// wait for 5 times the duration
|
|
Expect(p.Budget(t.Add(5 * t2.Sub(t)))).To(BeEquivalentTo(5 * initialMaxDatagramSize))
|
|
})
|
|
|
|
It("accumulates budget, if no packets are sent, for larger packet sizes", func() {
|
|
t := time.Now()
|
|
sendBurst(t)
|
|
const packetSize = initialMaxDatagramSize + 200
|
|
p.SetMaxDatagramSize(packetSize)
|
|
t2 := p.TimeUntilSend()
|
|
Expect(t2).To(BeTemporally(">", t))
|
|
// wait for 5 times the duration
|
|
Expect(p.Budget(t.Add(5 * t2.Sub(t)))).To(BeEquivalentTo(5 * packetSize))
|
|
})
|
|
|
|
It("has enough budget for at least one packet when the timer expires", func() {
|
|
t := time.Now()
|
|
sendBurst(t)
|
|
for bw := uint64(100); bw < uint64(5*initialMaxDatagramSize); bw++ {
|
|
bandwidth = bw // reduce the bandwidth to 5 packet per second
|
|
t2 := p.TimeUntilSend()
|
|
Expect(t2).To(BeTemporally(">", t))
|
|
Expect(p.Budget(t2)).To(BeNumerically(">=", initialMaxDatagramSize))
|
|
}
|
|
})
|
|
|
|
It("never allows bursts larger than the maximum burst size", func() {
|
|
t := time.Now()
|
|
sendBurst(t)
|
|
Expect(p.Budget(t.Add(time.Hour))).To(BeEquivalentTo(maxBurstSizePackets * initialMaxDatagramSize))
|
|
})
|
|
|
|
It("never allows bursts larger than the maximum burst size, for larger packets", func() {
|
|
t := time.Now()
|
|
const packetSize = initialMaxDatagramSize + 200
|
|
p.SetMaxDatagramSize(packetSize)
|
|
sendBurst(t)
|
|
Expect(p.Budget(t.Add(time.Hour))).To(BeEquivalentTo(maxBurstSizePackets * packetSize))
|
|
})
|
|
|
|
It("changes the bandwidth", func() {
|
|
t := time.Now()
|
|
sendBurst(t)
|
|
bandwidth = uint64(5 * initialMaxDatagramSize) // reduce the bandwidth to 5 packet per second
|
|
Expect(p.TimeUntilSend()).To(Equal(t.Add(time.Second / 5)))
|
|
})
|
|
|
|
It("doesn't pace faster than the minimum pacing duration", func() {
|
|
t := time.Now()
|
|
sendBurst(t)
|
|
bandwidth = uint64(1e6 * initialMaxDatagramSize)
|
|
Expect(p.TimeUntilSend()).To(Equal(t.Add(protocol.MinPacingDelay)))
|
|
Expect(p.Budget(t.Add(protocol.MinPacingDelay))).To(Equal(protocol.ByteCount(protocol.MinPacingDelay) * initialMaxDatagramSize * 1e6 / 1e9))
|
|
})
|
|
|
|
It("protects against overflows", func() {
|
|
p = newPacer(func() Bandwidth { return infBandwidth })
|
|
t := time.Now()
|
|
p.SentPacket(t, initialMaxDatagramSize)
|
|
for i := 0; i < 1e5; i++ {
|
|
Expect(p.Budget(t.Add(time.Duration(rand.Int63())))).To(BeNumerically(">=", 0))
|
|
}
|
|
})
|
|
})
|