forked from hyperledger-archives/burrow
-
Notifications
You must be signed in to change notification settings - Fork 0
/
throttler.go
70 lines (60 loc) · 1.96 KB
/
throttler.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
package ethereum
import (
"math/big"
"time"
"github.com/hyperledger/burrow/logging"
)
type Throttler struct {
// Request timestamps as unix nanos (avoid space overhead of time.Time)
requests []int64
maxRequestsPerNanosecond *big.Float
// Window over which to accumulate request times
window time.Duration
logger *logging.Logger
}
func NewThrottler(maxRequests int, timeBase time.Duration, window time.Duration, logger *logging.Logger) *Throttler {
maxRequestsPerNanosecond := new(big.Float).SetInt64(int64(maxRequests))
maxRequestsPerNanosecond.Quo(maxRequestsPerNanosecond, new(big.Float).SetInt64(int64(timeBase)))
return &Throttler{
maxRequestsPerNanosecond: maxRequestsPerNanosecond,
window: window,
logger: logger,
}
}
func (t *Throttler) Throttle() {
time.Sleep(t.calculateWait())
}
func (t *Throttler) calculateWait() time.Duration {
requests := len(t.requests)
if requests < 2 {
return 0
}
delta := t.requests[requests-1] - t.requests[0]
deltaNanoseconds := new(big.Float).SetInt64(delta)
allowedRequestsInDelta := new(big.Float).Mul(deltaNanoseconds, t.maxRequestsPerNanosecond)
overage := allowedRequestsInDelta.Sub(new(big.Float).SetInt64(int64(requests)), allowedRequestsInDelta)
if overage.Sign() > 0 {
// Wait just long enough to eat our overage at max request rate
nanos, _ := new(big.Float).Quo(overage, t.maxRequestsPerNanosecond).Int64()
wait := time.Duration(nanos)
t.logger.InfoMsg("throttling connection",
"num_requests", requests,
"over_period", time.Duration(delta).String(),
"requests_overage", overage.String(),
"wait", wait.String(),
)
return wait
}
return 0
}
func (t *Throttler) addNow() {
t.add(time.Now())
}
func (t *Throttler) add(now time.Time) {
cutoff := now.Add(-t.window)
// Remove expired requests
for len(t.requests) > 0 && t.requests[0] < cutoff.UnixNano() {
t.requests = t.requests[1:]
}
t.requests = append(t.requests, now.UnixNano())
}