github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/vent/chain/ethereum/throttler.go (about) 1 package ethereum 2 3 import ( 4 "math/big" 5 "time" 6 7 "github.com/hyperledger/burrow/logging" 8 ) 9 10 type Throttler struct { 11 // Request timestamps as unix nanos (avoid space overhead of time.Time) 12 requests []int64 13 maxRequestsPerNanosecond *big.Float 14 // Window over which to accumulate request times 15 window time.Duration 16 logger *logging.Logger 17 } 18 19 func NewThrottler(maxRequests int, timeBase time.Duration, window time.Duration, logger *logging.Logger) *Throttler { 20 maxRequestsPerNanosecond := new(big.Float).SetInt64(int64(maxRequests)) 21 maxRequestsPerNanosecond.Quo(maxRequestsPerNanosecond, new(big.Float).SetInt64(int64(timeBase))) 22 return &Throttler{ 23 maxRequestsPerNanosecond: maxRequestsPerNanosecond, 24 window: window, 25 logger: logger, 26 } 27 } 28 29 func (t *Throttler) Throttle() { 30 time.Sleep(t.calculateWait()) 31 } 32 33 func (t *Throttler) calculateWait() time.Duration { 34 requests := len(t.requests) 35 if requests < 2 { 36 return 0 37 } 38 delta := t.requests[requests-1] - t.requests[0] 39 deltaNanoseconds := new(big.Float).SetInt64(delta) 40 41 allowedRequestsInDelta := new(big.Float).Mul(deltaNanoseconds, t.maxRequestsPerNanosecond) 42 43 overage := allowedRequestsInDelta.Sub(new(big.Float).SetInt64(int64(requests)), allowedRequestsInDelta) 44 if overage.Sign() > 0 { 45 // Wait just long enough to eat our overage at max request rate 46 nanos, _ := new(big.Float).Quo(overage, t.maxRequestsPerNanosecond).Int64() 47 wait := time.Duration(nanos) 48 t.logger.InfoMsg("throttling connection", 49 "num_requests", requests, 50 "over_period", time.Duration(delta).String(), 51 "requests_overage", overage.String(), 52 "wait", wait.String(), 53 ) 54 return wait 55 } 56 return 0 57 } 58 59 func (t *Throttler) addNow() { 60 t.add(time.Now()) 61 } 62 63 func (t *Throttler) add(now time.Time) { 64 cutoff := now.Add(-t.window) 65 // Remove expired requests 66 for len(t.requests) > 0 && t.requests[0] < cutoff.UnixNano() { 67 t.requests = t.requests[1:] 68 } 69 t.requests = append(t.requests, now.UnixNano()) 70 }