|
|
|
@ -3,19 +3,45 @@ package bbr |
|
|
|
|
import ( |
|
|
|
|
"context" |
|
|
|
|
"fmt" |
|
|
|
|
"math" |
|
|
|
|
"math/rand" |
|
|
|
|
"sync" |
|
|
|
|
"sync/atomic" |
|
|
|
|
"testing" |
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
limit "github.com/bilibili/kratos/pkg/ratelimit" |
|
|
|
|
"github.com/bilibili/kratos/pkg/ratelimit" |
|
|
|
|
"github.com/bilibili/kratos/pkg/stat/metric" |
|
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
func confForTest() *Config { |
|
|
|
|
return &Config{ |
|
|
|
|
Window: time.Second, |
|
|
|
|
WinBucket: 10, |
|
|
|
|
CPUThreshold: 800, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func warmup(bbr *BBR, count int) { |
|
|
|
|
for i := 0; i < count; i++ { |
|
|
|
|
done, err := bbr.Allow(context.TODO()) |
|
|
|
|
time.Sleep(time.Millisecond * 1) |
|
|
|
|
if err == nil { |
|
|
|
|
done(ratelimit.DoneInfo{Op: ratelimit.Success}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func forceAllow(bbr *BBR) { |
|
|
|
|
inflight := bbr.inFlight |
|
|
|
|
bbr.inFlight = bbr.maxPASS() - 1 |
|
|
|
|
done, err := bbr.Allow(context.TODO()) |
|
|
|
|
if err == nil { |
|
|
|
|
done(ratelimit.DoneInfo{Op: ratelimit.Success}) |
|
|
|
|
} |
|
|
|
|
bbr.inFlight = inflight |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func TestBBR(t *testing.T) { |
|
|
|
|
cfg := &Config{ |
|
|
|
|
Window: time.Second * 5, |
|
|
|
@ -36,7 +62,7 @@ func TestBBR(t *testing.T) { |
|
|
|
|
} else { |
|
|
|
|
count := rand.Intn(100) |
|
|
|
|
time.Sleep(time.Millisecond * time.Duration(count)) |
|
|
|
|
f(limit.DoneInfo{Op: limit.Success}) |
|
|
|
|
f(ratelimit.DoneInfo{Op: ratelimit.Success}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}() |
|
|
|
@ -47,27 +73,22 @@ func TestBBR(t *testing.T) { |
|
|
|
|
|
|
|
|
|
func TestBBRMaxPass(t *testing.T) { |
|
|
|
|
bucketDuration := time.Millisecond * 100 |
|
|
|
|
passStat := metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration}) |
|
|
|
|
bbr := newLimiter(confForTest()).(*BBR) |
|
|
|
|
for i := 1; i <= 10; i++ { |
|
|
|
|
passStat.Add(int64(i * 100)) |
|
|
|
|
bbr.passStat.Add(int64(i * 100)) |
|
|
|
|
time.Sleep(bucketDuration) |
|
|
|
|
} |
|
|
|
|
bbr := &BBR{ |
|
|
|
|
passStat: passStat, |
|
|
|
|
} |
|
|
|
|
assert.Equal(t, int64(1000), bbr.maxPASS()) |
|
|
|
|
|
|
|
|
|
// default max pass is equal to 1.
|
|
|
|
|
passStat = metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration}) |
|
|
|
|
bbr = &BBR{ |
|
|
|
|
passStat: passStat, |
|
|
|
|
} |
|
|
|
|
bbr = newLimiter(confForTest()).(*BBR) |
|
|
|
|
assert.Equal(t, int64(1), bbr.maxPASS()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func TestBBRMinRt(t *testing.T) { |
|
|
|
|
bucketDuration := time.Millisecond * 100 |
|
|
|
|
rtStat := metric.NewRollingGauge(metric.RollingGaugeOpts{Size: 10, BucketDuration: bucketDuration}) |
|
|
|
|
bbr := newLimiter(confForTest()).(*BBR) |
|
|
|
|
rtStat := metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration}) |
|
|
|
|
for i := 0; i < 10; i++ { |
|
|
|
|
for j := i*10 + 1; j <= i*10+10; j++ { |
|
|
|
|
rtStat.Add(int64(j)) |
|
|
|
@ -76,26 +97,23 @@ func TestBBRMinRt(t *testing.T) { |
|
|
|
|
time.Sleep(bucketDuration) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
bbr := &BBR{ |
|
|
|
|
rtStat: rtStat, |
|
|
|
|
} |
|
|
|
|
bbr.rtStat = rtStat |
|
|
|
|
assert.Equal(t, int64(6), bbr.minRT()) |
|
|
|
|
|
|
|
|
|
// default max min rt is equal to maxFloat64.
|
|
|
|
|
bucketDuration = time.Millisecond * 100 |
|
|
|
|
rtStat = metric.NewRollingGauge(metric.RollingGaugeOpts{Size: 10, BucketDuration: bucketDuration}) |
|
|
|
|
bbr = &BBR{ |
|
|
|
|
rtStat: rtStat, |
|
|
|
|
} |
|
|
|
|
assert.Equal(t, int64(math.Ceil(math.MaxFloat64)), bbr.minRT()) |
|
|
|
|
bbr = newLimiter(confForTest()).(*BBR) |
|
|
|
|
bbr.rtStat = metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration}) |
|
|
|
|
assert.Equal(t, int64(1), bbr.minRT()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func TestBBRMaxInflight(t *testing.T) { |
|
|
|
|
func TestBBRMaxQps(t *testing.T) { |
|
|
|
|
bbr := newLimiter(confForTest()).(*BBR) |
|
|
|
|
bucketDuration := time.Millisecond * 100 |
|
|
|
|
passStat := metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration}) |
|
|
|
|
rtStat := metric.NewRollingGauge(metric.RollingGaugeOpts{Size: 10, BucketDuration: bucketDuration}) |
|
|
|
|
rtStat := metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration}) |
|
|
|
|
for i := 0; i < 10; i++ { |
|
|
|
|
passStat.Add(int64((i + 1) * 100)) |
|
|
|
|
passStat.Add(int64((i + 2) * 100)) |
|
|
|
|
for j := i*10 + 1; j <= i*10+10; j++ { |
|
|
|
|
rtStat.Add(int64(j)) |
|
|
|
|
} |
|
|
|
@ -103,22 +121,20 @@ func TestBBRMaxInflight(t *testing.T) { |
|
|
|
|
time.Sleep(bucketDuration) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
bbr := &BBR{ |
|
|
|
|
passStat: passStat, |
|
|
|
|
rtStat: rtStat, |
|
|
|
|
winBucketPerSec: 10, |
|
|
|
|
} |
|
|
|
|
bbr.passStat = passStat |
|
|
|
|
bbr.rtStat = rtStat |
|
|
|
|
assert.Equal(t, int64(60), bbr.maxFlight()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func TestBBRShouldDrop(t *testing.T) { |
|
|
|
|
var cpu int64 |
|
|
|
|
cpuGetter := func() int64 { |
|
|
|
|
bbr := newLimiter(confForTest()).(*BBR) |
|
|
|
|
bbr.cpu = func() int64 { |
|
|
|
|
return cpu |
|
|
|
|
} |
|
|
|
|
bucketDuration := time.Millisecond * 100 |
|
|
|
|
passStat := metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration}) |
|
|
|
|
rtStat := metric.NewRollingGauge(metric.RollingGaugeOpts{Size: 10, BucketDuration: bucketDuration}) |
|
|
|
|
rtStat := metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration}) |
|
|
|
|
for i := 0; i < 10; i++ { |
|
|
|
|
passStat.Add(int64((i + 1) * 100)) |
|
|
|
|
for j := i*10 + 1; j <= i*10+10; j++ { |
|
|
|
@ -128,13 +144,8 @@ func TestBBRShouldDrop(t *testing.T) { |
|
|
|
|
time.Sleep(bucketDuration) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
bbr := &BBR{ |
|
|
|
|
cpu: cpuGetter, |
|
|
|
|
passStat: passStat, |
|
|
|
|
rtStat: rtStat, |
|
|
|
|
winBucketPerSec: 10, |
|
|
|
|
conf: defaultConf, |
|
|
|
|
} |
|
|
|
|
bbr.passStat = passStat |
|
|
|
|
bbr.rtStat = rtStat |
|
|
|
|
// cpu >= 800, inflight < maxQps
|
|
|
|
|
cpu = 800 |
|
|
|
|
bbr.inFlight = 50 |
|
|
|
@ -169,3 +180,83 @@ func TestGroup(t *testing.T) { |
|
|
|
|
assert.NotNil(t, limiter) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func BenchmarkBBRAllowUnderLowLoad(b *testing.B) { |
|
|
|
|
bbr := newLimiter(confForTest()).(*BBR) |
|
|
|
|
bbr.cpu = func() int64 { |
|
|
|
|
return 500 |
|
|
|
|
} |
|
|
|
|
b.ResetTimer() |
|
|
|
|
for i := 0; i <= b.N; i++ { |
|
|
|
|
done, err := bbr.Allow(context.TODO()) |
|
|
|
|
if err == nil { |
|
|
|
|
done(ratelimit.DoneInfo{Op: ratelimit.Success}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func BenchmarkBBRAllowUnderHighLoad(b *testing.B) { |
|
|
|
|
bbr := newLimiter(confForTest()).(*BBR) |
|
|
|
|
bbr.cpu = func() int64 { |
|
|
|
|
return 900 |
|
|
|
|
} |
|
|
|
|
bbr.inFlight = 1 |
|
|
|
|
b.ResetTimer() |
|
|
|
|
for i := 0; i <= b.N; i++ { |
|
|
|
|
if i%10000 == 0 { |
|
|
|
|
maxFlight := bbr.maxFlight() |
|
|
|
|
if maxFlight != 0 { |
|
|
|
|
bbr.inFlight = rand.Int63n(bbr.maxFlight() * 2) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
done, err := bbr.Allow(context.TODO()) |
|
|
|
|
if err == nil { |
|
|
|
|
done(ratelimit.DoneInfo{Op: ratelimit.Success}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func BenchmarkBBRShouldDropUnderLowLoad(b *testing.B) { |
|
|
|
|
bbr := newLimiter(confForTest()).(*BBR) |
|
|
|
|
bbr.cpu = func() int64 { |
|
|
|
|
return 500 |
|
|
|
|
} |
|
|
|
|
warmup(bbr, 10000) |
|
|
|
|
b.ResetTimer() |
|
|
|
|
for i := 0; i <= b.N; i++ { |
|
|
|
|
bbr.shouldDrop() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func BenchmarkBBRShouldDropUnderHighLoad(b *testing.B) { |
|
|
|
|
bbr := newLimiter(confForTest()).(*BBR) |
|
|
|
|
bbr.cpu = func() int64 { |
|
|
|
|
return 900 |
|
|
|
|
} |
|
|
|
|
warmup(bbr, 10000) |
|
|
|
|
bbr.inFlight = 1000 |
|
|
|
|
b.ResetTimer() |
|
|
|
|
for i := 0; i <= b.N; i++ { |
|
|
|
|
bbr.shouldDrop() |
|
|
|
|
if i%10000 == 0 { |
|
|
|
|
forceAllow(bbr) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func BenchmarkBBRShouldDropUnderUnstableLoad(b *testing.B) { |
|
|
|
|
bbr := newLimiter(confForTest()).(*BBR) |
|
|
|
|
bbr.cpu = func() int64 { |
|
|
|
|
return 500 |
|
|
|
|
} |
|
|
|
|
warmup(bbr, 10000) |
|
|
|
|
bbr.prevDrop.Store(time.Since(initTime)) |
|
|
|
|
bbr.inFlight = 1000 |
|
|
|
|
b.ResetTimer() |
|
|
|
|
for i := 0; i <= b.N; i++ { |
|
|
|
|
bbr.shouldDrop() |
|
|
|
|
if i%100000 == 0 { |
|
|
|
|
forceAllow(bbr) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|