815 lines
31 KiB
Go
815 lines
31 KiB
Go
package congestion
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/lucas-clemente/quic-go/protocol"
|
|
. "github.com/onsi/ginkgo"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
const initialCongestionWindowPackets protocol.PacketNumber = 10
|
|
const defaultWindowTCP = protocol.ByteCount(initialCongestionWindowPackets) * protocol.DefaultTCPMSS
|
|
|
|
type mockClock time.Time
|
|
|
|
func (c *mockClock) Now() time.Time {
|
|
return time.Time(*c)
|
|
}
|
|
|
|
func (c *mockClock) Advance(d time.Duration) {
|
|
*c = mockClock(time.Time(*c).Add(d))
|
|
}
|
|
|
|
const MaxCongestionWindow = protocol.PacketNumber(200)
|
|
|
|
var _ = Describe("Cubic Sender", func() {
|
|
var (
|
|
sender SendAlgorithmWithDebugInfo
|
|
clock mockClock
|
|
bytesInFlight protocol.ByteCount
|
|
packetNumber protocol.PacketNumber
|
|
ackedPacketNumber protocol.PacketNumber
|
|
rttStats *RTTStats
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
bytesInFlight = 0
|
|
packetNumber = 1
|
|
ackedPacketNumber = 0
|
|
clock = mockClock{}
|
|
rttStats = NewRTTStats()
|
|
sender = NewCubicSender(&clock, rttStats, true /*reno*/, initialCongestionWindowPackets, MaxCongestionWindow)
|
|
})
|
|
|
|
SendAvailableSendWindowLen := func(packetLength protocol.ByteCount) int {
|
|
// Send as long as TimeUntilSend returns Zero.
|
|
packets_sent := 0
|
|
can_send := sender.TimeUntilSend(clock.Now(), bytesInFlight) == 0
|
|
for can_send {
|
|
sender.OnPacketSent(clock.Now(), bytesInFlight, packetNumber, packetLength, true)
|
|
packetNumber++
|
|
packets_sent++
|
|
bytesInFlight += packetLength
|
|
can_send = sender.TimeUntilSend(clock.Now(), bytesInFlight) == 0
|
|
}
|
|
return packets_sent
|
|
}
|
|
|
|
// Normal is that TCP acks every other segment.
|
|
AckNPacketsLen := func(n int, packetLength protocol.ByteCount) {
|
|
rttStats.UpdateRTT(60*time.Millisecond, 0, clock.Now())
|
|
sender.MaybeExitSlowStart()
|
|
for i := 0; i < n; i++ {
|
|
ackedPacketNumber++
|
|
sender.OnPacketAcked(ackedPacketNumber, packetLength, bytesInFlight)
|
|
}
|
|
bytesInFlight -= protocol.ByteCount(n) * packetLength
|
|
clock.Advance(time.Millisecond)
|
|
}
|
|
|
|
LoseNPacketsLen := func(n int, packetLength protocol.ByteCount) {
|
|
for i := 0; i < n; i++ {
|
|
ackedPacketNumber++
|
|
sender.OnPacketLost(ackedPacketNumber, packetLength, bytesInFlight)
|
|
}
|
|
bytesInFlight -= protocol.ByteCount(n) * packetLength
|
|
}
|
|
|
|
// Does not increment acked_packet_number_.
|
|
LosePacket := func(number protocol.PacketNumber) {
|
|
sender.OnPacketLost(number, protocol.DefaultTCPMSS, bytesInFlight)
|
|
bytesInFlight -= protocol.DefaultTCPMSS
|
|
}
|
|
|
|
SendAvailableSendWindow := func() int { return SendAvailableSendWindowLen(protocol.DefaultTCPMSS) }
|
|
AckNPackets := func(n int) { AckNPacketsLen(n, protocol.DefaultTCPMSS) }
|
|
LoseNPackets := func(n int) { LoseNPacketsLen(n, protocol.DefaultTCPMSS) }
|
|
|
|
It("simpler sender", func() {
|
|
// At startup make sure we are at the default.
|
|
Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP))
|
|
// At startup make sure we can send.
|
|
Expect(sender.TimeUntilSend(clock.Now(), 0)).To(BeZero())
|
|
// Make sure we can send.
|
|
Expect(sender.TimeUntilSend(clock.Now(), 0)).To(BeZero())
|
|
// And that window is un-affected.
|
|
Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP))
|
|
|
|
// Fill the send window with data, then verify that we can't send.
|
|
SendAvailableSendWindow()
|
|
Expect(sender.TimeUntilSend(clock.Now(), sender.GetCongestionWindow())).ToNot(BeZero())
|
|
})
|
|
|
|
It("application limited slow start", func() {
|
|
// Send exactly 10 packets and ensure the CWND ends at 14 packets.
|
|
const kNumberOfAcks = 5
|
|
// At startup make sure we can send.
|
|
Expect(sender.TimeUntilSend(clock.Now(), 0)).To(BeZero())
|
|
// Make sure we can send.
|
|
Expect(sender.TimeUntilSend(clock.Now(), 0)).To(BeZero())
|
|
|
|
SendAvailableSendWindow()
|
|
for i := 0; i < kNumberOfAcks; i++ {
|
|
AckNPackets(2)
|
|
}
|
|
bytesToSend := sender.GetCongestionWindow()
|
|
// It's expected 2 acks will arrive when the bytes_in_flight are greater than
|
|
// half the CWND.
|
|
Expect(bytesToSend).To(Equal(defaultWindowTCP + protocol.DefaultTCPMSS*2*2))
|
|
})
|
|
|
|
It("exponential slow start", func() {
|
|
const kNumberOfAcks = 20
|
|
// At startup make sure we can send.
|
|
Expect(sender.TimeUntilSend(clock.Now(), 0)).To(BeZero())
|
|
Expect(sender.BandwidthEstimate()).To(BeZero())
|
|
// Make sure we can send.
|
|
Expect(sender.TimeUntilSend(clock.Now(), 0)).To(BeZero())
|
|
|
|
for i := 0; i < kNumberOfAcks; i++ {
|
|
// Send our full send window.
|
|
SendAvailableSendWindow()
|
|
AckNPackets(2)
|
|
}
|
|
cwnd := sender.GetCongestionWindow()
|
|
Expect(cwnd).To(Equal(defaultWindowTCP + protocol.DefaultTCPMSS*2*kNumberOfAcks))
|
|
Expect(sender.BandwidthEstimate()).To(Equal(BandwidthFromDelta(cwnd, rttStats.SmoothedRTT())))
|
|
})
|
|
|
|
It("slow start packet loss", func() {
|
|
sender.SetNumEmulatedConnections(1)
|
|
const kNumberOfAcks = 10
|
|
for i := 0; i < kNumberOfAcks; i++ {
|
|
// Send our full send window.
|
|
SendAvailableSendWindow()
|
|
AckNPackets(2)
|
|
}
|
|
SendAvailableSendWindow()
|
|
expected_send_window := defaultWindowTCP + (protocol.DefaultTCPMSS * 2 * kNumberOfAcks)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// Lose a packet to exit slow start.
|
|
LoseNPackets(1)
|
|
packets_in_recovery_window := expected_send_window / protocol.DefaultTCPMSS
|
|
|
|
// We should now have fallen out of slow start with a reduced window.
|
|
expected_send_window = protocol.ByteCount(float32(expected_send_window) * renoBeta)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// Recovery phase. We need to ack every packet in the recovery window before
|
|
// we exit recovery.
|
|
number_of_packets_in_window := expected_send_window / protocol.DefaultTCPMSS
|
|
AckNPackets(int(packets_in_recovery_window))
|
|
SendAvailableSendWindow()
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// We need to ack an entire window before we increase CWND by 1.
|
|
AckNPackets(int(number_of_packets_in_window) - 2)
|
|
SendAvailableSendWindow()
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// Next ack should increase cwnd by 1.
|
|
AckNPackets(1)
|
|
expected_send_window += protocol.DefaultTCPMSS
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// Now RTO and ensure slow start gets reset.
|
|
Expect(sender.HybridSlowStart().Started()).To(BeTrue())
|
|
sender.OnRetransmissionTimeout(true)
|
|
Expect(sender.HybridSlowStart().Started()).To(BeFalse())
|
|
})
|
|
|
|
It("slow start packet loss with large reduction", func() {
|
|
sender.SetSlowStartLargeReduction(true)
|
|
|
|
sender.SetNumEmulatedConnections(1)
|
|
const kNumberOfAcks = 10
|
|
for i := 0; i < kNumberOfAcks; i++ {
|
|
// Send our full send window.
|
|
SendAvailableSendWindow()
|
|
AckNPackets(2)
|
|
}
|
|
SendAvailableSendWindow()
|
|
expected_send_window := defaultWindowTCP + (protocol.DefaultTCPMSS * 2 * kNumberOfAcks)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// Lose a packet to exit slow start. We should now have fallen out of
|
|
// slow start with a window reduced by 1.
|
|
LoseNPackets(1)
|
|
expected_send_window -= protocol.DefaultTCPMSS
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// Lose 5 packets in recovery and verify that congestion window is reduced
|
|
// further.
|
|
LoseNPackets(5)
|
|
expected_send_window -= 5 * protocol.DefaultTCPMSS
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
packets_in_recovery_window := expected_send_window / protocol.DefaultTCPMSS
|
|
|
|
// Recovery phase. We need to ack every packet in the recovery window before
|
|
// we exit recovery.
|
|
number_of_packets_in_window := expected_send_window / protocol.DefaultTCPMSS
|
|
AckNPackets(int(packets_in_recovery_window))
|
|
SendAvailableSendWindow()
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// We need to ack the rest of the window before cwnd increases by 1.
|
|
AckNPackets(int(number_of_packets_in_window - 1))
|
|
SendAvailableSendWindow()
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// Next ack should increase cwnd by 1.
|
|
AckNPackets(1)
|
|
expected_send_window += protocol.DefaultTCPMSS
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// Now RTO and ensure slow start gets reset.
|
|
Expect(sender.HybridSlowStart().Started()).To(BeTrue())
|
|
sender.OnRetransmissionTimeout(true)
|
|
Expect(sender.HybridSlowStart().Started()).To(BeFalse())
|
|
})
|
|
|
|
It("slow start half packet loss with large reduction", func() {
|
|
sender.SetSlowStartLargeReduction(true)
|
|
|
|
sender.SetNumEmulatedConnections(1)
|
|
const kNumberOfAcks = 10
|
|
for i := 0; i < kNumberOfAcks; i++ {
|
|
// Send our full send window in half sized packets.
|
|
SendAvailableSendWindowLen(protocol.DefaultTCPMSS / 2)
|
|
AckNPackets(2)
|
|
}
|
|
SendAvailableSendWindowLen(protocol.DefaultTCPMSS / 2)
|
|
expected_send_window := defaultWindowTCP + (protocol.DefaultTCPMSS * 2 * kNumberOfAcks)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// Lose a packet to exit slow start. We should now have fallen out of
|
|
// slow start with a window reduced by 1.
|
|
LoseNPackets(1)
|
|
expected_send_window -= protocol.DefaultTCPMSS
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// Lose 10 packets in recovery and verify that congestion window is reduced
|
|
// by 5 packets.
|
|
LoseNPacketsLen(10, protocol.DefaultTCPMSS/2)
|
|
expected_send_window -= 5 * protocol.DefaultTCPMSS
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
})
|
|
|
|
It("no PRR when less than one packet in flight", func() {
|
|
SendAvailableSendWindow()
|
|
LoseNPackets(int(initialCongestionWindowPackets) - 1)
|
|
AckNPackets(1)
|
|
// PRR will allow 2 packets for every ack during recovery.
|
|
Expect(SendAvailableSendWindow()).To(Equal(2))
|
|
// Simulate abandoning all packets by supplying a bytes_in_flight of 0.
|
|
// PRR should now allow a packet to be sent, even though prr's state
|
|
// variables believe it has sent enough packets.
|
|
Expect(sender.TimeUntilSend(clock.Now(), 0)).To(BeZero())
|
|
})
|
|
|
|
It("slow start packet loss PRR", func() {
|
|
sender.SetNumEmulatedConnections(1)
|
|
// Test based on the first example in RFC6937.
|
|
// Ack 10 packets in 5 acks to raise the CWND to 20, as in the example.
|
|
const kNumberOfAcks = 5
|
|
for i := 0; i < kNumberOfAcks; i++ {
|
|
// Send our full send window.
|
|
SendAvailableSendWindow()
|
|
AckNPackets(2)
|
|
}
|
|
SendAvailableSendWindow()
|
|
expected_send_window := defaultWindowTCP + (protocol.DefaultTCPMSS * 2 * kNumberOfAcks)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
LoseNPackets(1)
|
|
|
|
// We should now have fallen out of slow start with a reduced window.
|
|
send_window_before_loss := expected_send_window
|
|
expected_send_window = protocol.ByteCount(float32(expected_send_window) * renoBeta)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// Testing TCP proportional rate reduction.
|
|
// We should send packets paced over the received acks for the remaining
|
|
// outstanding packets. The number of packets before we exit recovery is the
|
|
// original CWND minus the packet that has been lost and the one which
|
|
// triggered the loss.
|
|
remaining_packets_in_recovery := send_window_before_loss/protocol.DefaultTCPMSS - 2
|
|
|
|
for i := protocol.ByteCount(0); i < remaining_packets_in_recovery; i++ {
|
|
AckNPackets(1)
|
|
SendAvailableSendWindow()
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
}
|
|
|
|
// We need to ack another window before we increase CWND by 1.
|
|
number_of_packets_in_window := expected_send_window / protocol.DefaultTCPMSS
|
|
for i := protocol.ByteCount(0); i < number_of_packets_in_window; i++ {
|
|
AckNPackets(1)
|
|
Expect(SendAvailableSendWindow()).To(Equal(1))
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
}
|
|
|
|
AckNPackets(1)
|
|
expected_send_window += protocol.DefaultTCPMSS
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
})
|
|
|
|
It("slow start burst packet loss PRR", func() {
|
|
sender.SetNumEmulatedConnections(1)
|
|
// Test based on the second example in RFC6937, though we also implement
|
|
// forward acknowledgements, so the first two incoming acks will trigger
|
|
// PRR immediately.
|
|
// Ack 20 packets in 10 acks to raise the CWND to 30.
|
|
const kNumberOfAcks = 10
|
|
for i := 0; i < kNumberOfAcks; i++ {
|
|
// Send our full send window.
|
|
SendAvailableSendWindow()
|
|
AckNPackets(2)
|
|
}
|
|
SendAvailableSendWindow()
|
|
expected_send_window := defaultWindowTCP + (protocol.DefaultTCPMSS * 2 * kNumberOfAcks)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// Lose one more than the congestion window reduction, so that after loss,
|
|
// bytes_in_flight is lesser than the congestion window.
|
|
send_window_after_loss := protocol.ByteCount(renoBeta * float32(expected_send_window))
|
|
num_packets_to_lose := (expected_send_window-send_window_after_loss)/protocol.DefaultTCPMSS + 1
|
|
LoseNPackets(int(num_packets_to_lose))
|
|
// Immediately after the loss, ensure at least one packet can be sent.
|
|
// Losses without subsequent acks can occur with timer based loss detection.
|
|
Expect(sender.TimeUntilSend(clock.Now(), bytesInFlight)).To(BeZero())
|
|
AckNPackets(1)
|
|
|
|
// We should now have fallen out of slow start with a reduced window.
|
|
expected_send_window = protocol.ByteCount(float32(expected_send_window) * renoBeta)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// Only 2 packets should be allowed to be sent, per PRR-SSRB
|
|
Expect(SendAvailableSendWindow()).To(Equal(2))
|
|
|
|
// Ack the next packet, which triggers another loss.
|
|
LoseNPackets(1)
|
|
AckNPackets(1)
|
|
|
|
// Send 2 packets to simulate PRR-SSRB.
|
|
Expect(SendAvailableSendWindow()).To(Equal(2))
|
|
|
|
// Ack the next packet, which triggers another loss.
|
|
LoseNPackets(1)
|
|
AckNPackets(1)
|
|
|
|
// Send 2 packets to simulate PRR-SSRB.
|
|
Expect(SendAvailableSendWindow()).To(Equal(2))
|
|
|
|
// Exit recovery and return to sending at the new rate.
|
|
for i := 0; i < kNumberOfAcks; i++ {
|
|
AckNPackets(1)
|
|
Expect(SendAvailableSendWindow()).To(Equal(1))
|
|
}
|
|
})
|
|
|
|
It("RTO congestion window", func() {
|
|
Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP))
|
|
Expect(sender.SlowstartThreshold()).To(Equal(MaxCongestionWindow))
|
|
|
|
// Expect the window to decrease to the minimum once the RTO fires
|
|
// and slow start threshold to be set to 1/2 of the CWND.
|
|
sender.OnRetransmissionTimeout(true)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(protocol.ByteCount(2 * protocol.DefaultTCPMSS)))
|
|
Expect(sender.SlowstartThreshold()).To(Equal(protocol.PacketNumber(5)))
|
|
})
|
|
|
|
It("RTO congestion window no retransmission", func() {
|
|
Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP))
|
|
|
|
// Expect the window to remain unchanged if the RTO fires but no
|
|
// packets are retransmitted.
|
|
sender.OnRetransmissionTimeout(false)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP))
|
|
})
|
|
|
|
It("retransmission delay", func() {
|
|
const kRttMs = 10 * time.Millisecond
|
|
const kDeviationMs = 3 * time.Millisecond
|
|
Expect(sender.RetransmissionDelay()).To(BeZero())
|
|
|
|
rttStats.UpdateRTT(kRttMs, 0, clock.Now())
|
|
|
|
// Initial value is to set the median deviation to half of the initial
|
|
// rtt, the median in then multiplied by a factor of 4 and finally the
|
|
// smoothed rtt is added which is the initial rtt.
|
|
expected_delay := kRttMs + kRttMs/2*4
|
|
Expect(sender.RetransmissionDelay()).To(Equal(expected_delay))
|
|
|
|
for i := 0; i < 100; i++ {
|
|
// run to make sure that we converge.
|
|
rttStats.UpdateRTT(kRttMs+kDeviationMs, 0, clock.Now())
|
|
rttStats.UpdateRTT(kRttMs-kDeviationMs, 0, clock.Now())
|
|
}
|
|
expected_delay = kRttMs + kDeviationMs*4
|
|
|
|
Expect(rttStats.SmoothedRTT()).To(BeNumerically("~", kRttMs, time.Millisecond))
|
|
Expect(sender.RetransmissionDelay()).To(BeNumerically("~", expected_delay, time.Millisecond))
|
|
Expect(sender.BandwidthEstimate() / BytesPerSecond).To(Equal(Bandwidth(
|
|
sender.GetCongestionWindow() * protocol.ByteCount(time.Second) / protocol.ByteCount(rttStats.SmoothedRTT()),
|
|
)))
|
|
})
|
|
|
|
It("slow start max send window", func() {
|
|
const kMaxCongestionWindowTCP = 50
|
|
const kNumberOfAcks = 100
|
|
sender = NewCubicSender(&clock, rttStats, false, initialCongestionWindowPackets, kMaxCongestionWindowTCP)
|
|
|
|
for i := 0; i < kNumberOfAcks; i++ {
|
|
// Send our full send window.
|
|
SendAvailableSendWindow()
|
|
AckNPackets(2)
|
|
}
|
|
expected_send_window := kMaxCongestionWindowTCP * protocol.DefaultTCPMSS
|
|
Expect(sender.GetCongestionWindow()).To(Equal(protocol.ByteCount(expected_send_window)))
|
|
})
|
|
|
|
It("tcp reno max congestion window", func() {
|
|
const kMaxCongestionWindowTCP = 50
|
|
const kNumberOfAcks = 1000
|
|
sender = NewCubicSender(&clock, rttStats, false, initialCongestionWindowPackets, kMaxCongestionWindowTCP)
|
|
|
|
SendAvailableSendWindow()
|
|
AckNPackets(2)
|
|
// Make sure we fall out of slow start.
|
|
LoseNPackets(1)
|
|
|
|
for i := 0; i < kNumberOfAcks; i++ {
|
|
// Send our full send window.
|
|
SendAvailableSendWindow()
|
|
AckNPackets(2)
|
|
}
|
|
|
|
expected_send_window := kMaxCongestionWindowTCP * protocol.DefaultTCPMSS
|
|
Expect(sender.GetCongestionWindow()).To(Equal(protocol.ByteCount(expected_send_window)))
|
|
})
|
|
|
|
It("tcp cubic max congestion window", func() {
|
|
const kMaxCongestionWindowTCP = 50
|
|
// Set to 10000 to compensate for small cubic alpha.
|
|
const kNumberOfAcks = 10000
|
|
|
|
sender = NewCubicSender(&clock, rttStats, false, initialCongestionWindowPackets, kMaxCongestionWindowTCP)
|
|
|
|
SendAvailableSendWindow()
|
|
AckNPackets(2)
|
|
// Make sure we fall out of slow start.
|
|
LoseNPackets(1)
|
|
|
|
for i := 0; i < kNumberOfAcks; i++ {
|
|
// Send our full send window.
|
|
SendAvailableSendWindow()
|
|
AckNPackets(2)
|
|
}
|
|
|
|
expected_send_window := kMaxCongestionWindowTCP * protocol.DefaultTCPMSS
|
|
Expect(sender.GetCongestionWindow()).To(Equal(protocol.ByteCount(expected_send_window)))
|
|
})
|
|
|
|
It("tcp cubic reset epoch on quiescence", func() {
|
|
const kMaxCongestionWindow = 50
|
|
const kMaxCongestionWindowBytes = kMaxCongestionWindow * protocol.DefaultTCPMSS
|
|
sender = NewCubicSender(&clock, rttStats, false, initialCongestionWindowPackets, kMaxCongestionWindow)
|
|
|
|
num_sent := SendAvailableSendWindow()
|
|
|
|
// Make sure we fall out of slow start.
|
|
saved_cwnd := sender.GetCongestionWindow()
|
|
LoseNPackets(1)
|
|
Expect(saved_cwnd).To(BeNumerically(">", sender.GetCongestionWindow()))
|
|
|
|
// Ack the rest of the outstanding packets to get out of recovery.
|
|
for i := 1; i < num_sent; i++ {
|
|
AckNPackets(1)
|
|
}
|
|
Expect(bytesInFlight).To(BeZero())
|
|
|
|
// Send a new window of data and ack all; cubic growth should occur.
|
|
saved_cwnd = sender.GetCongestionWindow()
|
|
num_sent = SendAvailableSendWindow()
|
|
for i := 0; i < num_sent; i++ {
|
|
AckNPackets(1)
|
|
}
|
|
Expect(saved_cwnd).To(BeNumerically("<", sender.GetCongestionWindow()))
|
|
Expect(kMaxCongestionWindowBytes).To(BeNumerically(">", sender.GetCongestionWindow()))
|
|
Expect(bytesInFlight).To(BeZero())
|
|
|
|
// Quiescent time of 100 seconds
|
|
clock.Advance(100 * time.Second)
|
|
|
|
// Send new window of data and ack one packet. Cubic epoch should have
|
|
// been reset; ensure cwnd increase is not dramatic.
|
|
saved_cwnd = sender.GetCongestionWindow()
|
|
SendAvailableSendWindow()
|
|
AckNPackets(1)
|
|
Expect(saved_cwnd).To(BeNumerically("~", sender.GetCongestionWindow(), protocol.DefaultTCPMSS))
|
|
Expect(kMaxCongestionWindowBytes).To(BeNumerically(">", sender.GetCongestionWindow()))
|
|
})
|
|
|
|
It("tcp cubic shifted epoch on quiescence", func() {
|
|
const kMaxCongestionWindow = 50
|
|
const kMaxCongestionWindowBytes = kMaxCongestionWindow * protocol.DefaultTCPMSS
|
|
sender = NewCubicSender(&clock, rttStats, false, initialCongestionWindowPackets, kMaxCongestionWindow)
|
|
|
|
num_sent := SendAvailableSendWindow()
|
|
|
|
// Make sure we fall out of slow start.
|
|
saved_cwnd := sender.GetCongestionWindow()
|
|
LoseNPackets(1)
|
|
Expect(saved_cwnd).To(BeNumerically(">", sender.GetCongestionWindow()))
|
|
|
|
// Ack the rest of the outstanding packets to get out of recovery.
|
|
for i := 1; i < num_sent; i++ {
|
|
AckNPackets(1)
|
|
}
|
|
Expect(bytesInFlight).To(BeZero())
|
|
|
|
// Send a new window of data and ack all; cubic growth should occur.
|
|
saved_cwnd = sender.GetCongestionWindow()
|
|
num_sent = SendAvailableSendWindow()
|
|
for i := 0; i < num_sent; i++ {
|
|
AckNPackets(1)
|
|
}
|
|
Expect(saved_cwnd).To(BeNumerically("<", sender.GetCongestionWindow()))
|
|
Expect(kMaxCongestionWindowBytes).To(BeNumerically(">", sender.GetCongestionWindow()))
|
|
Expect(bytesInFlight).To(BeZero())
|
|
|
|
// Quiescent time of 100 seconds
|
|
clock.Advance(100 * time.Second)
|
|
|
|
// Send new window of data and ack one packet. Cubic epoch should have
|
|
// been reset; ensure cwnd increase is not dramatic.
|
|
saved_cwnd = sender.GetCongestionWindow()
|
|
SendAvailableSendWindow()
|
|
AckNPackets(1)
|
|
Expect(saved_cwnd).To(BeNumerically("~", sender.GetCongestionWindow(), protocol.DefaultTCPMSS))
|
|
Expect(kMaxCongestionWindowBytes).To(BeNumerically(">", sender.GetCongestionWindow()))
|
|
})
|
|
|
|
It("multiple losses in one window", func() {
|
|
SendAvailableSendWindow()
|
|
initial_window := sender.GetCongestionWindow()
|
|
LosePacket(ackedPacketNumber + 1)
|
|
post_loss_window := sender.GetCongestionWindow()
|
|
Expect(initial_window).To(BeNumerically(">", post_loss_window))
|
|
LosePacket(ackedPacketNumber + 3)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(post_loss_window))
|
|
LosePacket(packetNumber - 1)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(post_loss_window))
|
|
|
|
// Lose a later packet and ensure the window decreases.
|
|
LosePacket(packetNumber)
|
|
Expect(post_loss_window).To(BeNumerically(">", sender.GetCongestionWindow()))
|
|
})
|
|
|
|
It("don't track ack packets", func() {
|
|
// Send a packet with no retransmittable data, and ensure it's not tracked.
|
|
Expect(sender.OnPacketSent(clock.Now(), bytesInFlight, packetNumber, protocol.DefaultTCPMSS, false)).To(BeFalse())
|
|
packetNumber++
|
|
|
|
// Send a data packet with retransmittable data, and ensure it is tracked.
|
|
Expect(sender.OnPacketSent(clock.Now(), bytesInFlight, packetNumber, protocol.DefaultTCPMSS, true)).To(BeTrue())
|
|
})
|
|
|
|
// TEST_F(TcpCubicSenderPacketsTest, ConfigureInitialWindow) {
|
|
// QuicConfig config;
|
|
//
|
|
// QuicTagVector options;
|
|
// options.push_back(kIW03);
|
|
// QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
|
|
// sender.SetFromConfig(config, Perspective::IS_SERVER);
|
|
// Expect( sender.congestion_window()).To(Equal(3u))
|
|
//
|
|
// options.clear();
|
|
// options.push_back(kIW10);
|
|
// QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
|
|
// sender.SetFromConfig(config, Perspective::IS_SERVER);
|
|
// Expect( sender.congestion_window()).To(Equal(10u))
|
|
//
|
|
// options.clear();
|
|
// options.push_back(kIW20);
|
|
// QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
|
|
// sender.SetFromConfig(config, Perspective::IS_SERVER);
|
|
// Expect( sender.congestion_window()).To(Equal(20u))
|
|
//
|
|
// options.clear();
|
|
// options.push_back(kIW50);
|
|
// QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
|
|
// sender.SetFromConfig(config, Perspective::IS_SERVER);
|
|
// Expect( sender.congestion_window()).To(Equal(50u))
|
|
// }
|
|
//
|
|
// TEST_F(TcpCubicSenderPacketsTest, ConfigureMinimumWindow) {
|
|
// QuicConfig config;
|
|
//
|
|
// // Verify that kCOPT: kMIN1 forces the min CWND to 1 packet.
|
|
// QuicTagVector options;
|
|
// options.push_back(kMIN1);
|
|
// QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
|
|
// sender.SetFromConfig(config, Perspective::IS_SERVER);
|
|
// sender.OnRetransmissionTimeout(true);
|
|
// Expect( sender.congestion_window()).To(Equal(1u))
|
|
// }
|
|
|
|
It("2 connection congestion avoidance at end of recovery", func() {
|
|
sender.SetNumEmulatedConnections(2)
|
|
// Ack 10 packets in 5 acks to raise the CWND to 20.
|
|
const kNumberOfAcks = 5
|
|
for i := 0; i < kNumberOfAcks; i++ {
|
|
// Send our full send window.
|
|
SendAvailableSendWindow()
|
|
AckNPackets(2)
|
|
}
|
|
SendAvailableSendWindow()
|
|
expected_send_window := defaultWindowTCP + (protocol.DefaultTCPMSS * 2 * kNumberOfAcks)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
LoseNPackets(1)
|
|
|
|
// We should now have fallen out of slow start with a reduced window.
|
|
expected_send_window = protocol.ByteCount(float32(expected_send_window) * sender.RenoBeta())
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// No congestion window growth should occur in recovery phase, i.e., until the
|
|
// currently outstanding 20 packets are acked.
|
|
for i := 0; i < 10; i++ {
|
|
// Send our full send window.
|
|
SendAvailableSendWindow()
|
|
Expect(sender.InRecovery()).To(BeTrue())
|
|
AckNPackets(2)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
}
|
|
Expect(sender.InRecovery()).To(BeFalse())
|
|
|
|
// Out of recovery now. Congestion window should not grow for half an RTT.
|
|
packets_in_send_window := expected_send_window / protocol.DefaultTCPMSS
|
|
SendAvailableSendWindow()
|
|
AckNPackets(int(packets_in_send_window/2 - 2))
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// Next ack should increase congestion window by 1MSS.
|
|
SendAvailableSendWindow()
|
|
AckNPackets(2)
|
|
expected_send_window += protocol.DefaultTCPMSS
|
|
packets_in_send_window += 1
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// Congestion window should remain steady again for half an RTT.
|
|
SendAvailableSendWindow()
|
|
AckNPackets(int(packets_in_send_window/2 - 1))
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// Next ack should cause congestion window to grow by 1MSS.
|
|
SendAvailableSendWindow()
|
|
AckNPackets(2)
|
|
expected_send_window += protocol.DefaultTCPMSS
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
})
|
|
|
|
It("1 connection congestion avoidance at end of recovery", func() {
|
|
sender.SetNumEmulatedConnections(1)
|
|
// Ack 10 packets in 5 acks to raise the CWND to 20.
|
|
const kNumberOfAcks = 5
|
|
for i := 0; i < kNumberOfAcks; i++ {
|
|
// Send our full send window.
|
|
SendAvailableSendWindow()
|
|
AckNPackets(2)
|
|
}
|
|
SendAvailableSendWindow()
|
|
expected_send_window := defaultWindowTCP + (protocol.DefaultTCPMSS * 2 * kNumberOfAcks)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
LoseNPackets(1)
|
|
|
|
// We should now have fallen out of slow start with a reduced window.
|
|
expected_send_window = protocol.ByteCount(float32(expected_send_window) * renoBeta)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// No congestion window growth should occur in recovery phase, i.e., until the
|
|
// currently outstanding 20 packets are acked.
|
|
for i := 0; i < 10; i++ {
|
|
// Send our full send window.
|
|
SendAvailableSendWindow()
|
|
Expect(sender.InRecovery()).To(BeTrue())
|
|
AckNPackets(2)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
}
|
|
Expect(sender.InRecovery()).To(BeFalse())
|
|
|
|
// Out of recovery now. Congestion window should not grow during RTT.
|
|
for i := protocol.ByteCount(0); i < expected_send_window/protocol.DefaultTCPMSS-2; i += 2 {
|
|
// Send our full send window.
|
|
SendAvailableSendWindow()
|
|
AckNPackets(2)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
}
|
|
|
|
// Next ack should cause congestion window to grow by 1MSS.
|
|
SendAvailableSendWindow()
|
|
AckNPackets(2)
|
|
expected_send_window += protocol.DefaultTCPMSS
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
})
|
|
|
|
// TEST_F(TcpCubicSenderPacketsTest, BandwidthResumption) {
|
|
// // Test that when provided with CachedNetworkParameters and opted in to the
|
|
// // bandwidth resumption experiment, that the TcpCubicSenderPackets sets
|
|
// // initial CWND appropriately.
|
|
//
|
|
// // Set some common values.
|
|
// CachedNetworkParameters cached_network_params;
|
|
// const QuicPacketCount kNumberOfPackets = 123;
|
|
// const int kBandwidthEstimateBytesPerSecond =
|
|
// kNumberOfPackets * protocol.DefaultTCPMSS;
|
|
// cached_network_params.set_bandwidth_estimate_bytes_per_second(
|
|
// kBandwidthEstimateBytesPerSecond);
|
|
// cached_network_params.set_min_rtt_ms(1000);
|
|
//
|
|
// // Make sure that a bandwidth estimate results in a changed CWND.
|
|
// cached_network_params.set_timestamp(clock.WallNow().ToUNIXSeconds() -
|
|
// (kNumSecondsPerHour - 1));
|
|
// sender.ResumeConnectionState(cached_network_params, false);
|
|
// Expect( sender.congestion_window()).To(Equal(kNumberOfPackets))
|
|
//
|
|
// // Resumed CWND is limited to be in a sensible range.
|
|
// cached_network_params.set_bandwidth_estimate_bytes_per_second(
|
|
// (kMaxCongestionWindow + 1) * protocol.DefaultTCPMSS);
|
|
// sender.ResumeConnectionState(cached_network_params, false);
|
|
// Expect( sender.congestion_window()).To(Equal(kMaxCongestionWindow))
|
|
//
|
|
// cached_network_params.set_bandwidth_estimate_bytes_per_second(
|
|
// (kMinCongestionWindowForBandwidthResumption - 1) * protocol.DefaultTCPMSS);
|
|
// sender.ResumeConnectionState(cached_network_params, false);
|
|
// EXPECT_EQ(kMinCongestionWindowForBandwidthResumption,
|
|
// sender.congestion_window());
|
|
//
|
|
// // Resume to the max value.
|
|
// cached_network_params.set_max_bandwidth_estimate_bytes_per_second(
|
|
// (kMinCongestionWindowForBandwidthResumption + 10) * protocol.DefaultTCPMSS);
|
|
// sender.ResumeConnectionState(cached_network_params, true);
|
|
// EXPECT_EQ((kMinCongestionWindowForBandwidthResumption + 10) * protocol.DefaultTCPMSS,
|
|
// sender.GetCongestionWindow());
|
|
// }
|
|
//
|
|
// TEST_F(TcpCubicSenderPacketsTest, PaceBelowCWND) {
|
|
// QuicConfig config;
|
|
//
|
|
// // Verify that kCOPT: kMIN4 forces the min CWND to 1 packet, but allows up
|
|
// // to 4 to be sent.
|
|
// QuicTagVector options;
|
|
// options.push_back(kMIN4);
|
|
// QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
|
|
// sender.SetFromConfig(config, Perspective::IS_SERVER);
|
|
// sender.OnRetransmissionTimeout(true);
|
|
// Expect( sender.congestion_window()).To(Equal(1u))
|
|
// EXPECT_TRUE(
|
|
// sender.TimeUntilSend(QuicTime::Zero(), protocol.DefaultTCPMSS).IsZero());
|
|
// EXPECT_TRUE(
|
|
// sender.TimeUntilSend(QuicTime::Zero(), 2 * protocol.DefaultTCPMSS).IsZero());
|
|
// EXPECT_TRUE(
|
|
// sender.TimeUntilSend(QuicTime::Zero(), 3 * protocol.DefaultTCPMSS).IsZero());
|
|
// EXPECT_FALSE(
|
|
// sender.TimeUntilSend(QuicTime::Zero(), 4 * protocol.DefaultTCPMSS).IsZero());
|
|
// }
|
|
|
|
It("reset after connection migration", func() {
|
|
Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP))
|
|
Expect(sender.SlowstartThreshold()).To(Equal(MaxCongestionWindow))
|
|
|
|
// Starts with slow start.
|
|
sender.SetNumEmulatedConnections(1)
|
|
const kNumberOfAcks = 10
|
|
for i := 0; i < kNumberOfAcks; i++ {
|
|
// Send our full send window.
|
|
SendAvailableSendWindow()
|
|
AckNPackets(2)
|
|
}
|
|
SendAvailableSendWindow()
|
|
expected_send_window := defaultWindowTCP + (protocol.DefaultTCPMSS * 2 * kNumberOfAcks)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
|
|
// Loses a packet to exit slow start.
|
|
LoseNPackets(1)
|
|
|
|
// We should now have fallen out of slow start with a reduced window. Slow
|
|
// start threshold is also updated.
|
|
expected_send_window = protocol.ByteCount(float32(expected_send_window) * renoBeta)
|
|
Expect(sender.GetCongestionWindow()).To(Equal(expected_send_window))
|
|
Expect(sender.SlowstartThreshold()).To(Equal(protocol.PacketNumber(expected_send_window / protocol.DefaultTCPMSS)))
|
|
|
|
// Resets cwnd and slow start threshold on connection migrations.
|
|
sender.OnConnectionMigration()
|
|
Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP))
|
|
Expect(sender.SlowstartThreshold()).To(Equal(MaxCongestionWindow))
|
|
Expect(sender.HybridSlowStart().Started()).To(BeFalse())
|
|
})
|
|
})
|