// Copyright 2023 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build go1.21 package quic import ( "time" ) // A pacerState controls the rate at which packets are sent using a leaky-bucket rate limiter. // // The pacer limits the maximum size of a burst of packets. // When a burst exceeds this limit, it spreads subsequent packets // over time. // // The bucket is initialized to the maximum burst size (ten packets by default), // and fills at the rate: // // 1.25 * congestion_window / smoothed_rtt // // A sender can send one congestion window of packets per RTT, // since the congestion window consumed by each packet is returned // one round-trip later by the responding ack. // The pacer permits sending at slightly faster than this rate to // avoid underutilizing the congestion window. // // The pacer permits the bucket to become negative, and permits // sending when non-negative. This biases slightly in favor of // sending packets over limiting them, and permits bursts one // packet greater than the configured maximum, but permits the pacer // to be ignorant of the maximum packet size. // // https://www.rfc-editor.org/rfc/rfc9002.html#section-7.7 type pacerState struct { bucket int // measured in bytes maxBucket int timerGranularity time.Duration lastUpdate time.Time nextSend time.Time } func (p *pacerState) init(now time.Time, maxBurst int, timerGranularity time.Duration) { // Bucket is limited to maximum burst size, which is the initial congestion window. // https://www.rfc-editor.org/rfc/rfc9002#section-7.7-2 p.maxBucket = maxBurst p.bucket = p.maxBucket p.timerGranularity = timerGranularity p.lastUpdate = now p.nextSend = now } // pacerBytesForInterval returns the number of bytes permitted over an interval. // // rate = 1.25 * congestion_window / smoothed_rtt // bytes = interval * rate // // https://www.rfc-editor.org/rfc/rfc9002#section-7.7-6 func pacerBytesForInterval(interval time.Duration, congestionWindow int, rtt time.Duration) int { bytes := (int64(interval) * int64(congestionWindow)) / int64(rtt) bytes = (bytes * 5) / 4 // bytes *= 1.25 return int(bytes) } // pacerIntervalForBytes returns the amount of time required for a number of bytes. // // time_per_byte = (smoothed_rtt / congestion_window) / 1.25 // interval = time_per_byte * bytes // // https://www.rfc-editor.org/rfc/rfc9002#section-7.7-8 func pacerIntervalForBytes(bytes int, congestionWindow int, rtt time.Duration) time.Duration { interval := (int64(rtt) * int64(bytes)) / int64(congestionWindow) interval = (interval * 4) / 5 // interval /= 1.25 return time.Duration(interval) } // advance is called when time passes. func (p *pacerState) advance(now time.Time, congestionWindow int, rtt time.Duration) { elapsed := now.Sub(p.lastUpdate) if elapsed < 0 { // Time has gone backward? elapsed = 0 p.nextSend = now // allow a packet through to get back on track if p.bucket < 0 { p.bucket = 0 } } p.lastUpdate = now if rtt == 0 { // Avoid divide by zero in the implausible case that we measure no RTT. p.bucket = p.maxBucket return } // Refill the bucket. delta := pacerBytesForInterval(elapsed, congestionWindow, rtt) p.bucket = min(p.bucket+delta, p.maxBucket) } // packetSent is called to record transmission of a packet. func (p *pacerState) packetSent(now time.Time, size, congestionWindow int, rtt time.Duration) { p.bucket -= size if p.bucket < -congestionWindow { // Never allow the bucket to fall more than one congestion window in arrears. // We can only fall this far behind if the sender is sending unpaced packets, // the congestion window has been exceeded, or the RTT is less than the // timer granularity. // // Limiting the minimum bucket size limits the maximum pacer delay // to RTT/1.25. p.bucket = -congestionWindow } if p.bucket >= 0 { p.nextSend = now return } // Next send occurs when the bucket has refilled to 0. delay := pacerIntervalForBytes(-p.bucket, congestionWindow, rtt) p.nextSend = now.Add(delay) } // canSend reports whether a packet can be sent now. // If it returns false, next is the time when the next packet can be sent. func (p *pacerState) canSend(now time.Time) (canSend bool, next time.Time) { // If the next send time is within the timer granularity, send immediately. if p.nextSend.After(now.Add(p.timerGranularity)) { return false, p.nextSend } return true, time.Time{} }