2018-01-03 19:19:49 +00:00
package flowcontrol
import (
2018-01-20 18:07:01 +00:00
"os"
"strconv"
2018-01-03 19:19:49 +00:00
"time"
"github.com/lucas-clemente/quic-go/congestion"
"github.com/lucas-clemente/quic-go/internal/protocol"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
2018-01-20 18:07:01 +00:00
// on the CIs, the timing is a lot less precise, so scale every duration by this factor
func scaleDuration ( t time . Duration ) time . Duration {
scaleFactor := 1
if f , err := strconv . Atoi ( os . Getenv ( "TIMESCALE_FACTOR" ) ) ; err == nil { // parsing "" errors, so this works fine if the env is not set
scaleFactor = f
}
Expect ( scaleFactor ) . ToNot ( BeZero ( ) )
return time . Duration ( scaleFactor ) * t
}
2018-01-03 19:19:49 +00:00
var _ = Describe ( "Base Flow controller" , func ( ) {
var controller * baseFlowController
BeforeEach ( func ( ) {
controller = & baseFlowController { }
controller . rttStats = & congestion . RTTStats { }
} )
Context ( "send flow control" , func ( ) {
It ( "adds bytes sent" , func ( ) {
controller . bytesSent = 5
controller . AddBytesSent ( 6 )
Expect ( controller . bytesSent ) . To ( Equal ( protocol . ByteCount ( 5 + 6 ) ) )
} )
It ( "gets the size of the remaining flow control window" , func ( ) {
controller . bytesSent = 5
controller . sendWindow = 12
Expect ( controller . sendWindowSize ( ) ) . To ( Equal ( protocol . ByteCount ( 12 - 5 ) ) )
} )
It ( "updates the size of the flow control window" , func ( ) {
controller . AddBytesSent ( 5 )
controller . UpdateSendWindow ( 15 )
Expect ( controller . sendWindow ) . To ( Equal ( protocol . ByteCount ( 15 ) ) )
Expect ( controller . sendWindowSize ( ) ) . To ( Equal ( protocol . ByteCount ( 15 - 5 ) ) )
} )
It ( "says that the window size is 0 if we sent more than we were allowed to" , func ( ) {
controller . AddBytesSent ( 15 )
controller . UpdateSendWindow ( 10 )
Expect ( controller . sendWindowSize ( ) ) . To ( BeZero ( ) )
} )
It ( "does not decrease the flow control window" , func ( ) {
controller . UpdateSendWindow ( 20 )
Expect ( controller . sendWindowSize ( ) ) . To ( Equal ( protocol . ByteCount ( 20 ) ) )
controller . UpdateSendWindow ( 10 )
Expect ( controller . sendWindowSize ( ) ) . To ( Equal ( protocol . ByteCount ( 20 ) ) )
} )
} )
Context ( "receive flow control" , func ( ) {
2018-01-20 18:07:01 +00:00
var (
receiveWindow protocol . ByteCount = 10000
receiveWindowSize protocol . ByteCount = 1000
)
2018-01-03 19:19:49 +00:00
BeforeEach ( func ( ) {
2018-01-20 18:07:01 +00:00
controller . bytesRead = receiveWindow - receiveWindowSize
2018-01-03 19:19:49 +00:00
controller . receiveWindow = receiveWindow
2018-01-20 18:07:01 +00:00
controller . receiveWindowSize = receiveWindowSize
2018-01-03 19:19:49 +00:00
} )
It ( "adds bytes read" , func ( ) {
controller . bytesRead = 5
controller . AddBytesRead ( 6 )
Expect ( controller . bytesRead ) . To ( Equal ( protocol . ByteCount ( 5 + 6 ) ) )
} )
It ( "triggers a window update when necessary" , func ( ) {
2018-01-20 18:07:01 +00:00
bytesConsumed := float64 ( receiveWindowSize ) * protocol . WindowUpdateThreshold + 1 // consumed 1 byte more than the threshold
bytesRemaining := receiveWindowSize - protocol . ByteCount ( bytesConsumed )
readPosition := receiveWindow - bytesRemaining
2018-01-03 19:19:49 +00:00
controller . bytesRead = readPosition
offset := controller . getWindowUpdate ( )
2018-01-20 18:07:01 +00:00
Expect ( offset ) . To ( Equal ( readPosition + receiveWindowSize ) )
Expect ( controller . receiveWindow ) . To ( Equal ( readPosition + receiveWindowSize ) )
2018-01-03 19:19:49 +00:00
} )
It ( "doesn't trigger a window update when not necessary" , func ( ) {
2018-01-20 18:07:01 +00:00
bytesConsumed := float64 ( receiveWindowSize ) * protocol . WindowUpdateThreshold - 1 // consumed 1 byte less than the threshold
bytesRemaining := receiveWindowSize - protocol . ByteCount ( bytesConsumed )
readPosition := receiveWindow - bytesRemaining
2018-01-03 19:19:49 +00:00
controller . bytesRead = readPosition
offset := controller . getWindowUpdate ( )
Expect ( offset ) . To ( BeZero ( ) )
} )
2018-01-20 18:07:01 +00:00
Context ( "receive window size auto-tuning" , func ( ) {
var oldWindowSize protocol . ByteCount
2018-01-03 19:19:49 +00:00
BeforeEach ( func ( ) {
2018-01-20 18:07:01 +00:00
oldWindowSize = controller . receiveWindowSize
controller . maxReceiveWindowSize = 5000
2018-01-03 19:19:49 +00:00
} )
// update the congestion such that it returns a given value for the smoothed RTT
setRtt := func ( t time . Duration ) {
controller . rttStats . UpdateRTT ( t , 0 , time . Now ( ) )
Expect ( controller . rttStats . SmoothedRTT ( ) ) . To ( Equal ( t ) ) // make sure it worked
}
2018-01-20 18:07:01 +00:00
It ( "doesn't increase the window size for a new stream" , func ( ) {
controller . maybeAdjustWindowSize ( )
Expect ( controller . receiveWindowSize ) . To ( Equal ( oldWindowSize ) )
2018-01-03 19:19:49 +00:00
} )
2018-01-20 18:07:01 +00:00
It ( "doesn't increase the window size when no RTT estimate is available" , func ( ) {
2018-01-03 19:19:49 +00:00
setRtt ( 0 )
2018-01-20 18:07:01 +00:00
controller . startNewAutoTuningEpoch ( )
controller . AddBytesRead ( 400 )
offset := controller . getWindowUpdate ( )
Expect ( offset ) . ToNot ( BeZero ( ) ) // make sure a window update is sent
Expect ( controller . receiveWindowSize ) . To ( Equal ( oldWindowSize ) )
2018-01-03 19:19:49 +00:00
} )
2018-01-20 18:07:01 +00:00
It ( "increases the window size if read so fast that the window would be consumed in less than 4 RTTs" , func ( ) {
bytesRead := controller . bytesRead
rtt := scaleDuration ( 20 * time . Millisecond )
setRtt ( rtt )
// consume more than 2/3 of the window...
dataRead := receiveWindowSize * 2 / 3 + 1
// ... in 4*2/3 of the RTT
controller . epochStartOffset = controller . bytesRead
controller . epochStartTime = time . Now ( ) . Add ( - rtt * 4 * 2 / 3 )
controller . AddBytesRead ( dataRead )
2018-01-03 19:19:49 +00:00
offset := controller . getWindowUpdate ( )
Expect ( offset ) . ToNot ( BeZero ( ) )
2018-01-20 18:07:01 +00:00
// check that the window size was increased
newWindowSize := controller . receiveWindowSize
Expect ( newWindowSize ) . To ( Equal ( 2 * oldWindowSize ) )
// check that the new window size was used to increase the offset
Expect ( offset ) . To ( Equal ( protocol . ByteCount ( bytesRead + dataRead + newWindowSize ) ) )
2018-01-03 19:19:49 +00:00
} )
2018-01-20 18:07:01 +00:00
It ( "doesn't increase the window size if data is read so fast that the window would be consumed in less than 4 RTTs, but less than half the window has been read" , func ( ) {
// this test only makes sense if a window update is triggered before half of the window has been consumed
Expect ( protocol . WindowUpdateThreshold ) . To ( BeNumerically ( ">" , 1 / 3 ) )
bytesRead := controller . bytesRead
rtt := scaleDuration ( 20 * time . Millisecond )
setRtt ( rtt )
// consume more than 2/3 of the window...
dataRead := receiveWindowSize * 1 / 3 + 1
// ... in 4*2/3 of the RTT
controller . epochStartOffset = controller . bytesRead
controller . epochStartTime = time . Now ( ) . Add ( - rtt * 4 * 1 / 3 )
controller . AddBytesRead ( dataRead )
2018-01-03 19:19:49 +00:00
offset := controller . getWindowUpdate ( )
Expect ( offset ) . ToNot ( BeZero ( ) )
2018-01-20 18:07:01 +00:00
// check that the window size was not increased
newWindowSize := controller . receiveWindowSize
Expect ( newWindowSize ) . To ( Equal ( oldWindowSize ) )
// check that the new window size was used to increase the offset
Expect ( offset ) . To ( Equal ( protocol . ByteCount ( bytesRead + dataRead + newWindowSize ) ) )
2018-01-03 19:19:49 +00:00
} )
2018-01-20 18:07:01 +00:00
It ( "doesn't increase the window size if read too slowly" , func ( ) {
bytesRead := controller . bytesRead
rtt := scaleDuration ( 20 * time . Millisecond )
setRtt ( rtt )
// consume less than 2/3 of the window...
dataRead := receiveWindowSize * 2 / 3 - 1
// ... in 4*2/3 of the RTT
controller . epochStartOffset = controller . bytesRead
controller . epochStartTime = time . Now ( ) . Add ( - rtt * 4 * 2 / 3 )
controller . AddBytesRead ( dataRead )
2018-01-03 19:19:49 +00:00
offset := controller . getWindowUpdate ( )
Expect ( offset ) . ToNot ( BeZero ( ) )
2018-01-20 18:07:01 +00:00
// check that the window size was not increased
Expect ( controller . receiveWindowSize ) . To ( Equal ( oldWindowSize ) )
// check that the new window size was used to increase the offset
Expect ( offset ) . To ( Equal ( protocol . ByteCount ( bytesRead + dataRead + oldWindowSize ) ) )
} )
It ( "doesn't increase the window size to a value higher than the maxReceiveWindowSize" , func ( ) {
resetEpoch := func ( ) {
// make sure the next call to maybeAdjustWindowSize will increase the window
controller . epochStartTime = time . Now ( ) . Add ( - time . Millisecond )
controller . epochStartOffset = controller . bytesRead
controller . AddBytesRead ( controller . receiveWindowSize / 2 + 1 )
}
setRtt ( scaleDuration ( 20 * time . Millisecond ) )
resetEpoch ( )
controller . maybeAdjustWindowSize ( )
Expect ( controller . receiveWindowSize ) . To ( Equal ( 2 * oldWindowSize ) ) // 2000
// because the lastWindowUpdateTime is updated by MaybeTriggerWindowUpdate(), we can just call maybeAdjustWindowSize() multiple times and get an increase of the window size every time
resetEpoch ( )
controller . maybeAdjustWindowSize ( )
Expect ( controller . receiveWindowSize ) . To ( Equal ( 2 * 2 * oldWindowSize ) ) // 4000
resetEpoch ( )
controller . maybeAdjustWindowSize ( )
Expect ( controller . receiveWindowSize ) . To ( Equal ( controller . maxReceiveWindowSize ) ) // 5000
controller . maybeAdjustWindowSize ( )
Expect ( controller . receiveWindowSize ) . To ( Equal ( controller . maxReceiveWindowSize ) ) // 5000
2018-01-03 19:19:49 +00:00
} )
} )
} )
} )