You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
kratos/pkg/ratelimit/bbr/bbr_test.go

270 lines
6.9 KiB

package bbr
import (
"context"
"fmt"
"math"
"math/rand"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/bilibili/kratos/pkg/ratelimit"
"github.com/bilibili/kratos/pkg/stat/metric"
"github.com/stretchr/testify/assert"
)
func TestBBR(t *testing.T) {
cfg := &Config{
Window: time.Second * 5,
WinBucket: 50,
CPUThreshold: 100,
}
limiter := newLimiter(cfg)
var wg sync.WaitGroup
var drop int64
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for i := 0; i < 300; i++ {
f, err := limiter.Allow(context.TODO())
if err != nil {
atomic.AddInt64(&drop, 1)
} else {
count := rand.Intn(100)
time.Sleep(time.Millisecond * time.Duration(count))
f(ratelimit.DoneInfo{Op: ratelimit.Success})
}
}
}()
}
wg.Wait()
fmt.Println("drop: ", drop)
}
func TestBBRMaxPass(t *testing.T) {
bucketDuration := time.Millisecond * 100
passStat := metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration})
for i := 1; i <= 10; i++ {
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,
}
assert.Equal(t, int64(1), bbr.maxPASS())
}
func TestBBRMinRt(t *testing.T) {
bucketDuration := time.Millisecond * 100
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))
}
if i != 9 {
time.Sleep(bucketDuration)
}
}
bbr := &BBR{
rtStat: rtStat,
}
assert.Equal(t, int64(6), bbr.minRT())
// default max min rt is equal to maxFloat64.
bucketDuration = time.Millisecond * 100
rtStat = metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration})
bbr = &BBR{
rtStat: rtStat,
}
assert.Equal(t, int64(math.Ceil(math.MaxFloat64)), bbr.minRT())
}
func TestBBRMaxQps(t *testing.T) {
bucketDuration := time.Millisecond * 100
passStat := metric.NewRollingCounter(metric.RollingCounterOpts{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++ {
rtStat.Add(int64(j))
}
if i != 9 {
time.Sleep(bucketDuration)
}
}
bbr := &BBR{
passStat: passStat,
rtStat: rtStat,
winBucketPerSec: 10,
}
assert.Equal(t, int64(60), bbr.maxFlight())
}
func TestBBRShouldDrop(t *testing.T) {
var cpu int64
cpuGetter := func() int64 {
return cpu
}
bucketDuration := time.Millisecond * 100
passStat := metric.NewRollingCounter(metric.RollingCounterOpts{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++ {
rtStat.Add(int64(j))
}
if i != 9 {
time.Sleep(bucketDuration)
}
}
bbr := &BBR{
cpu: cpuGetter,
passStat: passStat,
rtStat: rtStat,
winBucketPerSec: 10,
conf: defaultConf,
}
// cpu >= 800, inflight < maxQps
cpu = 800
bbr.inFlight = 50
assert.Equal(t, false, bbr.shouldDrop())
// cpu >= 800, inflight > maxQps
cpu = 800
bbr.inFlight = 80
assert.Equal(t, true, bbr.shouldDrop())
// cpu < 800, inflight > maxQps, cold duration
cpu = 700
bbr.inFlight = 80
assert.Equal(t, true, bbr.shouldDrop())
// cpu < 800, inflight > maxQps
time.Sleep(2 * time.Second)
cpu = 700
bbr.inFlight = 80
assert.Equal(t, false, bbr.shouldDrop())
}
func TestGroup(t *testing.T) {
cfg := &Config{
Window: time.Second * 5,
WinBucket: 50,
CPUThreshold: 100,
}
group := NewGroup(cfg)
t.Run("get", func(t *testing.T) {
limiter := group.Get("test")
assert.NotNil(t, limiter)
})
}
func BenchmarkBBRAllowUnderLowLoad(b *testing.B) {
cpuGetter := func() int64 {
return 500
}
bucketDuration := time.Millisecond * 100
passStat := metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration})
rtStat := metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration})
bbr := &BBR{
cpu: cpuGetter,
passStat: passStat,
rtStat: rtStat,
winBucketPerSec: 10,
conf: defaultConf,
}
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) {
cpuGetter := func() int64 {
return 900
}
bucketDuration := time.Millisecond * 100
passStat := metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration})
rtStat := metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration})
bbr := &BBR{
cpu: cpuGetter,
passStat: passStat,
rtStat: rtStat,
winBucketPerSec: 10,
conf: defaultConf,
}
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 BenchmarkBBRShouldDropUnderLowLoad(b *testing.B) {
cpuGetter := func() int64 {
return 500
}
bucketDuration := time.Millisecond * 100
passStat := metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration})
rtStat := metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration})
bbr := &BBR{
cpu: cpuGetter,
passStat: passStat,
rtStat: rtStat,
winBucketPerSec: 10,
conf: defaultConf,
}
for i := 0; i < 10000; i++ {
done, err := bbr.Allow(context.TODO())
time.Sleep(time.Millisecond * 1)
if err == nil {
done(ratelimit.DoneInfo{Op: ratelimit.Success})
}
}
b.ResetTimer()
for i := 0; i <= b.N; i++ {
bbr.shouldDrop()
}
}
func BenchmarkBBRShouldDropUnderHighLoad(b *testing.B) {
cpuGetter := func() int64 {
return 900
}
bucketDuration := time.Millisecond * 100
passStat := metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration})
rtStat := metric.NewRollingCounter(metric.RollingCounterOpts{Size: 10, BucketDuration: bucketDuration})
bbr := &BBR{
cpu: cpuGetter,
passStat: passStat,
rtStat: rtStat,
winBucketPerSec: 10,
conf: defaultConf,
}
for i := 0; i < 10000; i++ {
done, err := bbr.Allow(context.TODO())
time.Sleep(time.Millisecond * 1)
if err == nil {
done(ratelimit.DoneInfo{Op: ratelimit.Success})
}
}
b.ResetTimer()
for i := 0; i <= b.N; i++ {
bbr.shouldDrop()
}
}