55package resolve
66
77import (
8+ "context"
9+ "math"
810 "strings"
911 "sync"
1012 "time"
1113
1214 "github.com/miekg/dns"
13- "go.uber.org/ratelimit"
1415 "golang.org/x/net/publicsuffix"
16+ "golang.org/x/time/rate"
1517)
1618
1719const (
18- startQPSPerNameserver = 10
19- maxQPSPerNameserver = 100
20- numIntervalSeconds = 2
21- rateUpdateInterval = numIntervalSeconds * time .Second
22- maxTimeoutPercentage = 0.5
20+ maxQPSPerNameserver = 100
21+ numIntervalSeconds = 5
22+ rateUpdateInterval = numIntervalSeconds * time .Second
23+ minUpdateSampleSize = 10
2324)
2425
2526type rateTrack struct {
2627 sync.Mutex
27- qps int
28- rate ratelimit. Limiter
29- success int
30- timeout int
28+ rate * rate. Limiter
29+ avg time. Duration
30+ count int
31+ first bool
3132}
3233
3334type RateTracker struct {
@@ -52,9 +53,11 @@ func NewRateTracker() *RateTracker {
5253}
5354
5455func newRateTrack () * rateTrack {
56+ limit := rate .Every (100 * time .Millisecond )
57+
5558 return & rateTrack {
56- qps : startQPSPerNameserver ,
57- rate : ratelimit . New ( startQPSPerNameserver ) ,
59+ rate : rate . NewLimiter ( limit , 1 ) ,
60+ first : true ,
5861 }
5962}
6063
@@ -75,24 +78,24 @@ func (r *RateTracker) Take(sub string) {
7578 rate := tracker .rate
7679 tracker .Unlock ()
7780
78- rate .Take ()
79- }
80-
81- // Success signals to the RateTracker that a request for the provided subdomain name was successful.
82- func (r * RateTracker ) Success (sub string ) {
83- tracker := r .getDomainRateTracker (sub )
84-
85- tracker .Lock ()
86- tracker .success ++
87- tracker .Unlock ()
81+ _ = rate .Wait (context .TODO ())
8882}
8983
90- // Timeout signals to the RateTracker that a request for the provided subdomain name timed out.
91- func (r * RateTracker ) Timeout (sub string ) {
84+ // ReportResponseTime provides the response time for a request for the domain name provided in the sub parameter.
85+ func (r * RateTracker ) ReportResponseTime (sub string , delta time.Duration ) {
86+ var average , count float64
9287 tracker := r .getDomainRateTracker (sub )
9388
9489 tracker .Lock ()
95- tracker .timeout ++
90+ tracker .count ++
91+ count = float64 (tracker .count )
92+ average = float64 (tracker .avg .Milliseconds ())
93+ average = ((average * (count - 1 )) + float64 (delta .Milliseconds ())) / count
94+ tracker .avg = time .Duration (math .Round (average )) * time .Millisecond
95+
96+ if tracker .first {
97+ defer tracker .update ()
98+ }
9699 tracker .Unlock ()
97100}
98101
@@ -124,26 +127,18 @@ func (r *RateTracker) updateAllRateLimiters() {
124127func (rt * rateTrack ) update () {
125128 rt .Lock ()
126129 defer rt .Unlock ()
127- // check if this rate tracker has already been updated
128- if rt .success == 0 && rt .timeout == 0 {
130+
131+ if rt .first {
132+ rt .first = false
133+ } else if rt .count < minUpdateSampleSize {
129134 return
130135 }
131- // timeouts in excess of maxTimeoutPercentage indicate a need to slow down
132- if float64 (rt .timeout )/ float64 (rt .success + rt .timeout ) > maxTimeoutPercentage {
133- rt .qps -= 1
134- if rt .qps <= 0 {
135- rt .qps = 1
136- }
137- } else {
138- rt .qps += 1
139- if rt .qps > maxQPSPerNameserver {
140- rt .qps = maxQPSPerNameserver
141- }
142- }
136+
137+ limit := rate .Every (rt .avg )
143138 // update the QPS rate limiter and reset counters
144- rt .rate = ratelimit . New ( rt . qps )
145- rt .success = 0
146- rt .timeout = 0
139+ rt .rate = rate . NewLimiter ( limit , 1 )
140+ rt .avg = 0
141+ rt .count = 0
147142}
148143
149144func (r * RateTracker ) getDomainRateTracker (sub string ) * rateTrack {
0 commit comments