les/utils: UDP rate limiter (#21930)

* les/utils: Limiter

* les/utils: dropped prior weight vs variable cost logic, using fixed weights

* les/utils: always create node selector in addressGroup

* les/utils: renamed request weight to request cost

* les/utils: simplified and improved the DoS penalty mechanism

* les/utils: minor fixes

* les/utils: made selection weight calculation nicer

* les/utils: fixed linter warning

* les/utils: more precise and reliable probabilistic test

* les/utils: fixed linter warning
This commit is contained in:
Felföldi Zsolt
2021-01-28 22:47:15 +01:00
committed by GitHub
parent eb21c652c0
commit 7a800f98f6
3 changed files with 625 additions and 14 deletions

View File

@ -52,17 +52,17 @@ func (w *WeightedRandomSelect) Remove(item WrsItem) {
// IsEmpty returns true if the set is empty
func (w *WeightedRandomSelect) IsEmpty() bool {
return w.root.sumWeight == 0
return w.root.sumCost == 0
}
// setWeight sets an item's weight to a specific value (removes it if zero)
func (w *WeightedRandomSelect) setWeight(item WrsItem, weight uint64) {
if weight > math.MaxInt64-w.root.sumWeight {
// old weight is still included in sumWeight, remove and check again
if weight > math.MaxInt64-w.root.sumCost {
// old weight is still included in sumCost, remove and check again
w.setWeight(item, 0)
if weight > math.MaxInt64-w.root.sumWeight {
log.Error("WeightedRandomSelect overflow", "sumWeight", w.root.sumWeight, "new weight", weight)
weight = math.MaxInt64 - w.root.sumWeight
if weight > math.MaxInt64-w.root.sumCost {
log.Error("WeightedRandomSelect overflow", "sumCost", w.root.sumCost, "new weight", weight)
weight = math.MaxInt64 - w.root.sumCost
}
}
idx, ok := w.idx[item]
@ -75,9 +75,9 @@ func (w *WeightedRandomSelect) setWeight(item WrsItem, weight uint64) {
if weight != 0 {
if w.root.itemCnt == w.root.maxItems {
// add a new level
newRoot := &wrsNode{sumWeight: w.root.sumWeight, itemCnt: w.root.itemCnt, level: w.root.level + 1, maxItems: w.root.maxItems * wrsBranches}
newRoot := &wrsNode{sumCost: w.root.sumCost, itemCnt: w.root.itemCnt, level: w.root.level + 1, maxItems: w.root.maxItems * wrsBranches}
newRoot.items[0] = w.root
newRoot.weights[0] = w.root.sumWeight
newRoot.weights[0] = w.root.sumCost
w.root = newRoot
}
w.idx[item] = w.root.insert(item, weight)
@ -91,10 +91,10 @@ func (w *WeightedRandomSelect) setWeight(item WrsItem, weight uint64) {
// updates its weight and selects another one
func (w *WeightedRandomSelect) Choose() WrsItem {
for {
if w.root.sumWeight == 0 {
if w.root.sumCost == 0 {
return nil
}
val := uint64(rand.Int63n(int64(w.root.sumWeight)))
val := uint64(rand.Int63n(int64(w.root.sumCost)))
choice, lastWeight := w.root.choose(val)
weight := w.wfn(choice)
if weight != lastWeight {
@ -112,7 +112,7 @@ const wrsBranches = 8 // max number of branches in the wrsNode tree
type wrsNode struct {
items [wrsBranches]interface{}
weights [wrsBranches]uint64
sumWeight uint64
sumCost uint64
level, itemCnt, maxItems int
}
@ -126,7 +126,7 @@ func (n *wrsNode) insert(item WrsItem, weight uint64) int {
}
}
n.itemCnt++
n.sumWeight += weight
n.sumCost += weight
n.weights[branch] += weight
if n.level == 0 {
n.items[branch] = item
@ -150,7 +150,7 @@ func (n *wrsNode) setWeight(idx int, weight uint64) uint64 {
oldWeight := n.weights[idx]
n.weights[idx] = weight
diff := weight - oldWeight
n.sumWeight += diff
n.sumCost += diff
if weight == 0 {
n.items[idx] = nil
n.itemCnt--
@ -161,7 +161,7 @@ func (n *wrsNode) setWeight(idx int, weight uint64) uint64 {
branch := idx / branchItems
diff := n.items[branch].(*wrsNode).setWeight(idx-branch*branchItems, weight)
n.weights[branch] += diff
n.sumWeight += diff
n.sumCost += diff
if weight == 0 {
n.itemCnt--
}