未验证 提交 bddd47c6 编写于 作者: B bigsheeper 提交者: GitHub

Prevent limiter burst overflow (#20153)

Signed-off-by: Nbigsheeper <yihao.dai@zilliz.com>
Signed-off-by: Nbigsheeper <yihao.dai@zilliz.com>
上级 ec7a98d2
...@@ -122,14 +122,12 @@ func (rl *rateLimiter) registerLimiters() { ...@@ -122,14 +122,12 @@ func (rl *rateLimiter) registerLimiters() {
case internalpb.RateType_DQLQuery: case internalpb.RateType_DQLQuery:
r = Params.QuotaConfig.DQLMaxQueryRate r = Params.QuotaConfig.DQLMaxQueryRate
} }
log.Info("RateLimiter register for rateType",
zap.String("rateType", internalpb.RateType_name[rt]),
zap.String("rate", ratelimitutil.Limit(r).String()))
limit := ratelimitutil.Limit(r) limit := ratelimitutil.Limit(r)
if limit < 0 {
limit = ratelimitutil.Inf
}
burst := int(r) // use rate as burst, because Limiter is with punishment mechanism, burst is insignificant. burst := int(r) // use rate as burst, because Limiter is with punishment mechanism, burst is insignificant.
rl.limiters[internalpb.RateType(rt)] = ratelimitutil.NewLimiter(limit, burst) rl.limiters[internalpb.RateType(rt)] = ratelimitutil.NewLimiter(limit, burst)
log.Info("RateLimiter register for rateType",
zap.String("rateType", internalpb.RateType_name[rt]),
zap.String("rate", ratelimitutil.Limit(r).String()),
zap.String("burst", fmt.Sprintf("%v", burst)))
} }
} }
...@@ -49,11 +49,33 @@ func TestMultiRateLimiter(t *testing.T) { ...@@ -49,11 +49,33 @@ func TestMultiRateLimiter(t *testing.T) {
multiLimiter := NewMultiRateLimiter() multiLimiter := NewMultiRateLimiter()
bak := Params.QuotaConfig.QuotaAndLimitsEnabled bak := Params.QuotaConfig.QuotaAndLimitsEnabled
Params.QuotaConfig.QuotaAndLimitsEnabled = false Params.QuotaConfig.QuotaAndLimitsEnabled = false
ok, r := multiLimiter.Limit(internalpb.RateType(0), 1) for _, rt := range internalpb.RateType_value {
assert.False(t, ok) ok, r := multiLimiter.Limit(internalpb.RateType(rt), 1)
assert.NotEqual(t, float64(0), r) assert.False(t, ok)
assert.NotEqual(t, float64(0), r)
}
Params.QuotaConfig.QuotaAndLimitsEnabled = bak Params.QuotaConfig.QuotaAndLimitsEnabled = bak
}) })
t.Run("test limit", func(t *testing.T) {
run := func(insertRate float64) {
bakInsertRate := Params.QuotaConfig.DMLMaxInsertRate
Params.QuotaConfig.DMLMaxInsertRate = insertRate
multiLimiter := NewMultiRateLimiter()
bak := Params.QuotaConfig.QuotaAndLimitsEnabled
Params.QuotaConfig.QuotaAndLimitsEnabled = true
ok, r := multiLimiter.Limit(internalpb.RateType_DMLInsert, 1*1024*1024)
assert.False(t, ok)
assert.NotEqual(t, float64(0), r)
Params.QuotaConfig.QuotaAndLimitsEnabled = bak
Params.QuotaConfig.DMLMaxInsertRate = bakInsertRate
}
run(math.MaxInt)
run(math.MaxInt / 1.2)
run(math.MaxInt / 2)
run(math.MaxInt / 3)
run(math.MaxInt / 10000)
})
} }
func TestRateLimiter(t *testing.T) { func TestRateLimiter(t *testing.T) {
...@@ -90,14 +112,8 @@ func TestRateLimiter(t *testing.T) { ...@@ -90,14 +112,8 @@ func TestRateLimiter(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
for _, rt := range internalpb.RateType_value { for _, rt := range internalpb.RateType_value {
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
if i == 0 { ok, _ := limiter.limit(internalpb.RateType(rt), 1)
// Consume token initialed in bucket assert.True(t, ok)
ok, _ := limiter.limit(internalpb.RateType(rt), 1)
assert.False(t, ok)
} else {
ok, _ := limiter.limit(internalpb.RateType(rt), 1)
assert.True(t, ok)
}
} }
} }
}) })
......
...@@ -654,7 +654,11 @@ func (q *QuotaCenter) setRates() error { ...@@ -654,7 +654,11 @@ func (q *QuotaCenter) setRates() error {
} }
rates := make([]*internalpb.Rate, 0, len(q.currentRates)) rates := make([]*internalpb.Rate, 0, len(q.currentRates))
for rt, r := range q.currentRates { for rt, r := range q.currentRates {
rates = append(rates, &internalpb.Rate{Rt: rt, R: float64(r) / float64(proxyNum)}) if r == Inf {
rates = append(rates, &internalpb.Rate{Rt: rt, R: float64(r)})
} else {
rates = append(rates, &internalpb.Rate{Rt: rt, R: float64(r) / float64(proxyNum)})
}
} }
return rates return rates
} }
......
...@@ -29,7 +29,7 @@ import ( ...@@ -29,7 +29,7 @@ import (
const ( const (
// defaultMax is the default unlimited rate or threshold. // defaultMax is the default unlimited rate or threshold.
defaultMax = float64(math.MaxFloat64) defaultMax = float64(math.MaxInt)
// MBSize used to convert megabytes and bytes. // MBSize used to convert megabytes and bytes.
MBSize = 1024.0 * 1024.0 MBSize = 1024.0 * 1024.0
// defaultDiskQuotaInMB is the default disk quota in megabytes. // defaultDiskQuotaInMB is the default disk quota in megabytes.
......
...@@ -31,7 +31,7 @@ import ( ...@@ -31,7 +31,7 @@ import (
type Limit float64 type Limit float64
// Inf is the infinite rate limit; it allows all events. // Inf is the infinite rate limit; it allows all events.
const Inf = Limit(math.MaxFloat64) const Inf = Limit(math.MaxInt)
// A Limiter controls how frequently events are allowed to happen. // A Limiter controls how frequently events are allowed to happen.
// It implements a "token bucket" of size b, initially full and refilled // It implements a "token bucket" of size b, initially full and refilled
...@@ -113,6 +113,12 @@ func (lim *Limiter) SetLimit(newLimit Limit) { ...@@ -113,6 +113,12 @@ func (lim *Limiter) SetLimit(newLimit Limit) {
lim.last = now lim.last = now
lim.tokens = tokens lim.tokens = tokens
lim.limit = newLimit lim.limit = newLimit
if newLimit >= math.MaxInt {
lim.burst = math.MaxInt
} else {
// use rate as burst, because Limiter is with punishment mechanism, burst is insignificant.
lim.burst = int(newLimit)
}
} }
// advance calculates and returns an updated state for lim resulting from the passage of time. // advance calculates and returns an updated state for lim resulting from the passage of time.
......
...@@ -18,6 +18,7 @@ package ratelimitutil ...@@ -18,6 +18,7 @@ package ratelimitutil
import ( import (
"fmt" "fmt"
"math"
"sync" "sync"
"sync/atomic" "sync/atomic"
"testing" "testing"
...@@ -63,6 +64,15 @@ func run(t *testing.T, lim *Limiter, allows []allow) { ...@@ -63,6 +64,15 @@ func run(t *testing.T, lim *Limiter, allows []allow) {
} }
} }
func runWithoutCheckToken(t *testing.T, lim *Limiter, allows []allow) {
for i, a := range allows {
ok := lim.AllowN(a.t, a.n)
if ok != a.ok {
t.Errorf("step %d: lim.AllowN(%v, %v) = %v want %v", i, a.t, a.n, ok, a.ok)
}
}
}
func TestLimit(t *testing.T) { func TestLimit(t *testing.T) {
t.Run("test limit", func(t *testing.T) { t.Run("test limit", func(t *testing.T) {
// test base // test base
...@@ -119,18 +129,30 @@ func TestLimit(t *testing.T) { ...@@ -119,18 +129,30 @@ func TestLimit(t *testing.T) {
{t2, 1, false, -1}, {t2, 1, false, -1},
{t2, 1, false, -1}, {t2, 1, false, -1},
}) })
limit := Inf
burst := math.MaxInt
limiter := NewLimiter(limit, burst)
runWithoutCheckToken(t, limiter, []allow{
{t0, 1 * 1024 * 1024, true, 0},
{t1, 1 * 1024 * 1024, true, 0},
{t2, 1 * 1024 * 1024, true, 0},
{t3, 1 * 1024 * 1024, true, 0},
{t4, 1 * 1024 * 1024, true, 0},
{t5, 1 * 1024 * 1024, true, 0},
})
}) })
t.Run("test SetLimit", func(t *testing.T) { t.Run("test SetLimit", func(t *testing.T) {
lim := NewLimiter(10, 2) lim := NewLimiter(10, 10)
run(t, lim, []allow{ run(t, lim, []allow{
{t0, 5, true, -3}, {t0, 5, true, 5},
{t0, 1, false, -3}, {t0, 1, true, 4},
{t1, 1, false, -3}, {t1, 1, true, 4},
}) })
lim.SetLimit(100) lim.SetLimit(100)
run(t, lim, []allow{{t2, 10, true, -8}}) runWithoutCheckToken(t, lim, []allow{{t2, 10, true, 0}})
}) })
t.Run("test no truncation error", func(t *testing.T) { t.Run("test no truncation error", func(t *testing.T) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册