-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpacer.go
131 lines (121 loc) · 4.45 KB
/
pacer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// 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{}
}