trace congestion state changes

This commit is contained in:
Marten Seemann
2020-07-22 14:59:32 +07:00
parent 741dc28d74
commit 0b7efe10d1
12 changed files with 99 additions and 24 deletions

View File

@@ -103,6 +103,7 @@ func newSentPacketHandler(
congestion.DefaultClock{},
rttStats,
true, // use Reno
tracer,
)
return &sentPacketHandler{

View File

@@ -5,6 +5,7 @@ import (
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/logging"
)
const (
@@ -57,17 +58,20 @@ type cubicSender struct {
initialCongestionWindow protocol.ByteCount
initialMaxCongestionWindow protocol.ByteCount
lastState logging.CongestionState
tracer logging.ConnectionTracer
}
var _ SendAlgorithm = &cubicSender{}
var _ SendAlgorithmWithDebugInfos = &cubicSender{}
// NewCubicSender makes a new cubic sender
func NewCubicSender(clock Clock, rttStats *utils.RTTStats, reno bool) *cubicSender {
return newCubicSender(clock, rttStats, reno, initialCongestionWindow, maxCongestionWindow)
func NewCubicSender(clock Clock, rttStats *utils.RTTStats, reno bool, tracer logging.ConnectionTracer) *cubicSender {
return newCubicSender(clock, rttStats, reno, initialCongestionWindow, maxCongestionWindow, tracer)
}
func newCubicSender(clock Clock, rttStats *utils.RTTStats, reno bool, initialCongestionWindow, initialMaxCongestionWindow protocol.ByteCount) *cubicSender {
func newCubicSender(clock Clock, rttStats *utils.RTTStats, reno bool, initialCongestionWindow, initialMaxCongestionWindow protocol.ByteCount, tracer logging.ConnectionTracer) *cubicSender {
c := &cubicSender{
rttStats: rttStats,
largestSentPacketNumber: protocol.InvalidPacketNumber,
@@ -82,8 +86,13 @@ func newCubicSender(clock Clock, rttStats *utils.RTTStats, reno bool, initialCon
cubic: NewCubic(clock),
clock: clock,
reno: reno,
tracer: tracer,
}
c.pacer = newPacer(c.BandwidthEstimate)
if c.tracer != nil {
c.lastState = logging.CongestionStateSlowStart
c.tracer.UpdatedCongestionState(logging.CongestionStateSlowStart)
}
return c
}
@@ -131,6 +140,7 @@ func (c *cubicSender) MaybeExitSlowStart() {
if c.InSlowStart() && c.hybridSlowStart.ShouldExitSlowStart(c.rttStats.LatestRTT(), c.rttStats.MinRTT(), c.GetCongestionWindow()/maxDatagramSize) {
// exit slow start
c.slowStartThreshold = c.congestionWindow
c.maybeTraceStateChange(logging.CongestionStateCongestionAvoidance)
}
}
@@ -161,6 +171,7 @@ func (c *cubicSender) OnPacketLost(
return
}
c.lastCutbackExitedSlowstart = c.InSlowStart()
c.maybeTraceStateChange(logging.CongestionStateRecovery)
if c.reno {
c.congestionWindow = protocol.ByteCount(float64(c.congestionWindow) * renoBeta)
@@ -189,6 +200,7 @@ func (c *cubicSender) maybeIncreaseCwnd(
// the current window.
if !c.isCwndLimited(priorInFlight) {
c.cubic.OnApplicationLimited()
c.maybeTraceStateChange(logging.CongestionStateApplicationLimited)
return
}
if c.congestionWindow >= c.maxCongestionWindow {
@@ -197,9 +209,11 @@ func (c *cubicSender) maybeIncreaseCwnd(
if c.InSlowStart() {
// TCP slow start, exponential growth, increase by one for each ACK.
c.congestionWindow += maxDatagramSize
c.maybeTraceStateChange(logging.CongestionStateSlowStart)
return
}
// Congestion avoidance
c.maybeTraceStateChange(logging.CongestionStateCongestionAvoidance)
if c.reno {
// Classic Reno congestion avoidance.
c.numAckedPackets++
@@ -257,3 +271,10 @@ func (c *cubicSender) OnConnectionMigration() {
c.slowStartThreshold = c.initialMaxCongestionWindow
c.maxCongestionWindow = c.initialMaxCongestionWindow
}
func (c *cubicSender) maybeTraceStateChange(new logging.CongestionState) {
if c.tracer == nil || new == c.lastState {
return
}
c.tracer.UpdatedCongestionState(new)
}

View File

@@ -40,7 +40,7 @@ var _ = Describe("Cubic Sender", func() {
ackedPacketNumber = 0
clock = mockClock{}
rttStats = utils.NewRTTStats()
sender = newCubicSender(&clock, rttStats, true /*reno*/, initialCongestionWindowPackets*maxDatagramSize, MaxCongestionWindow)
sender = newCubicSender(&clock, rttStats, true /*reno*/, initialCongestionWindowPackets*maxDatagramSize, MaxCongestionWindow, nil)
})
SendAvailableSendWindowLen := func(packetLength protocol.ByteCount) int {
@@ -308,7 +308,7 @@ var _ = Describe("Cubic Sender", func() {
It("tcp cubic reset epoch on quiescence", func() {
const maxCongestionWindow = 50
const maxCongestionWindowBytes = maxCongestionWindow * maxDatagramSize
sender = newCubicSender(&clock, rttStats, false, initialCongestionWindowPackets*maxDatagramSize, maxCongestionWindowBytes)
sender = newCubicSender(&clock, rttStats, false, initialCongestionWindowPackets*maxDatagramSize, maxCongestionWindowBytes, nil)
numSent := SendAvailableSendWindow()
@@ -448,7 +448,7 @@ var _ = Describe("Cubic Sender", func() {
})
It("default max cwnd", func() {
sender = newCubicSender(&clock, rttStats, true /*reno*/, initialCongestionWindowPackets*maxDatagramSize, maxCongestionWindow)
sender = newCubicSender(&clock, rttStats, true /*reno*/, initialCongestionWindowPackets*maxDatagramSize, maxCongestionWindow, nil)
defaultMaxCongestionWindowPackets := maxCongestionWindow / maxDatagramSize
for i := 1; i < int(defaultMaxCongestionWindowPackets); i++ {
@@ -460,7 +460,7 @@ var _ = Describe("Cubic Sender", func() {
It("limit cwnd increase in congestion avoidance", func() {
// Enable Cubic.
sender = newCubicSender(&clock, rttStats, false, initialCongestionWindowPackets*maxDatagramSize, MaxCongestionWindow)
sender = newCubicSender(&clock, rttStats, false, initialCongestionWindowPackets*maxDatagramSize, MaxCongestionWindow, nil)
numSent := SendAvailableSendWindow()
// Make sure we fall out of slow start.

View File

@@ -5,16 +5,15 @@
package mocks
import (
"net"
"reflect"
"time"
net "net"
reflect "reflect"
time "time"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/golang/mock/gomock"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/wire"
"github.com/lucas-clemente/quic-go/logging"
gomock "github.com/golang/mock/gomock"
protocol "github.com/lucas-clemente/quic-go/internal/protocol"
utils "github.com/lucas-clemente/quic-go/internal/utils"
wire "github.com/lucas-clemente/quic-go/internal/wire"
logging "github.com/lucas-clemente/quic-go/logging"
)
// MockConnectionTracer is a mock of ConnectionTracer interface
@@ -232,6 +231,18 @@ func (mr *MockConnectionTracerMockRecorder) StartedConnection(arg0, arg1, arg2,
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartedConnection", reflect.TypeOf((*MockConnectionTracer)(nil).StartedConnection), arg0, arg1, arg2, arg3, arg4)
}
// UpdatedCongestionState mocks base method
func (m *MockConnectionTracer) UpdatedCongestionState(arg0 logging.CongestionState) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "UpdatedCongestionState", arg0)
}
// UpdatedCongestionState indicates an expected call of UpdatedCongestionState
func (mr *MockConnectionTracerMockRecorder) UpdatedCongestionState(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedCongestionState", reflect.TypeOf((*MockConnectionTracer)(nil).UpdatedCongestionState), arg0)
}
// UpdatedKey mocks base method
func (m *MockConnectionTracer) UpdatedKey(arg0 protocol.KeyPhase, arg1 bool) {
m.ctrl.T.Helper()