github.com/shuguocloud/go-zero@v1.3.0/core/load/adaptiveshedder_test.go (about)

     1  package load
     2  
     3  import (
     4  	"math/rand"
     5  	"sync"
     6  	"sync/atomic"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/shuguocloud/go-zero/core/collection"
    12  	"github.com/shuguocloud/go-zero/core/logx"
    13  	"github.com/shuguocloud/go-zero/core/mathx"
    14  	"github.com/shuguocloud/go-zero/core/stat"
    15  	"github.com/shuguocloud/go-zero/core/syncx"
    16  )
    17  
    18  const (
    19  	buckets        = 10
    20  	bucketDuration = time.Millisecond * 50
    21  )
    22  
    23  func init() {
    24  	stat.SetReporter(nil)
    25  }
    26  
    27  func TestAdaptiveShedder(t *testing.T) {
    28  	DisableLog()
    29  	shedder := NewAdaptiveShedder(WithWindow(bucketDuration), WithBuckets(buckets), WithCpuThreshold(100))
    30  	var wg sync.WaitGroup
    31  	var drop int64
    32  	proba := mathx.NewProba()
    33  	for i := 0; i < 100; i++ {
    34  		wg.Add(1)
    35  		go func() {
    36  			defer wg.Done()
    37  			for i := 0; i < 30; i++ {
    38  				promise, err := shedder.Allow()
    39  				if err != nil {
    40  					atomic.AddInt64(&drop, 1)
    41  				} else {
    42  					count := rand.Intn(5)
    43  					time.Sleep(time.Millisecond * time.Duration(count))
    44  					if proba.TrueOnProba(0.01) {
    45  						promise.Fail()
    46  					} else {
    47  						promise.Pass()
    48  					}
    49  				}
    50  			}
    51  		}()
    52  	}
    53  	wg.Wait()
    54  }
    55  
    56  func TestAdaptiveShedderMaxPass(t *testing.T) {
    57  	passCounter := newRollingWindow()
    58  	for i := 1; i <= 10; i++ {
    59  		passCounter.Add(float64(i * 100))
    60  		time.Sleep(bucketDuration)
    61  	}
    62  	shedder := &adaptiveShedder{
    63  		passCounter:     passCounter,
    64  		droppedRecently: syncx.NewAtomicBool(),
    65  	}
    66  	assert.Equal(t, int64(1000), shedder.maxPass())
    67  
    68  	// default max pass is equal to 1.
    69  	passCounter = newRollingWindow()
    70  	shedder = &adaptiveShedder{
    71  		passCounter:     passCounter,
    72  		droppedRecently: syncx.NewAtomicBool(),
    73  	}
    74  	assert.Equal(t, int64(1), shedder.maxPass())
    75  }
    76  
    77  func TestAdaptiveShedderMinRt(t *testing.T) {
    78  	rtCounter := newRollingWindow()
    79  	for i := 0; i < 10; i++ {
    80  		if i > 0 {
    81  			time.Sleep(bucketDuration)
    82  		}
    83  		for j := i*10 + 1; j <= i*10+10; j++ {
    84  			rtCounter.Add(float64(j))
    85  		}
    86  	}
    87  	shedder := &adaptiveShedder{
    88  		rtCounter: rtCounter,
    89  	}
    90  	assert.Equal(t, float64(6), shedder.minRt())
    91  
    92  	// default max min rt is equal to maxFloat64.
    93  	rtCounter = newRollingWindow()
    94  	shedder = &adaptiveShedder{
    95  		rtCounter:       rtCounter,
    96  		droppedRecently: syncx.NewAtomicBool(),
    97  	}
    98  	assert.Equal(t, defaultMinRt, shedder.minRt())
    99  }
   100  
   101  func TestAdaptiveShedderMaxFlight(t *testing.T) {
   102  	passCounter := newRollingWindow()
   103  	rtCounter := newRollingWindow()
   104  	for i := 0; i < 10; i++ {
   105  		if i > 0 {
   106  			time.Sleep(bucketDuration)
   107  		}
   108  		passCounter.Add(float64((i + 1) * 100))
   109  		for j := i*10 + 1; j <= i*10+10; j++ {
   110  			rtCounter.Add(float64(j))
   111  		}
   112  	}
   113  	shedder := &adaptiveShedder{
   114  		passCounter:     passCounter,
   115  		rtCounter:       rtCounter,
   116  		windows:         buckets,
   117  		droppedRecently: syncx.NewAtomicBool(),
   118  	}
   119  	assert.Equal(t, int64(54), shedder.maxFlight())
   120  }
   121  
   122  func TestAdaptiveShedderShouldDrop(t *testing.T) {
   123  	logx.Disable()
   124  	passCounter := newRollingWindow()
   125  	rtCounter := newRollingWindow()
   126  	for i := 0; i < 10; i++ {
   127  		if i > 0 {
   128  			time.Sleep(bucketDuration)
   129  		}
   130  		passCounter.Add(float64((i + 1) * 100))
   131  		for j := i*10 + 1; j <= i*10+10; j++ {
   132  			rtCounter.Add(float64(j))
   133  		}
   134  	}
   135  	shedder := &adaptiveShedder{
   136  		passCounter:     passCounter,
   137  		rtCounter:       rtCounter,
   138  		windows:         buckets,
   139  		dropTime:        syncx.NewAtomicDuration(),
   140  		droppedRecently: syncx.NewAtomicBool(),
   141  	}
   142  	// cpu >=  800, inflight < maxPass
   143  	systemOverloadChecker = func(int64) bool {
   144  		return true
   145  	}
   146  	shedder.avgFlying = 50
   147  	assert.False(t, shedder.shouldDrop())
   148  
   149  	// cpu >=  800, inflight > maxPass
   150  	shedder.avgFlying = 80
   151  	shedder.flying = 50
   152  	assert.False(t, shedder.shouldDrop())
   153  
   154  	// cpu >=  800, inflight > maxPass
   155  	shedder.avgFlying = 80
   156  	shedder.flying = 80
   157  	assert.True(t, shedder.shouldDrop())
   158  
   159  	// cpu < 800, inflight > maxPass
   160  	systemOverloadChecker = func(int64) bool {
   161  		return false
   162  	}
   163  	shedder.avgFlying = 80
   164  	assert.False(t, shedder.shouldDrop())
   165  
   166  	// cpu >=  800, inflight < maxPass
   167  	systemOverloadChecker = func(int64) bool {
   168  		return true
   169  	}
   170  	shedder.avgFlying = 80
   171  	shedder.flying = 80
   172  	_, err := shedder.Allow()
   173  	assert.NotNil(t, err)
   174  }
   175  
   176  func TestAdaptiveShedderStillHot(t *testing.T) {
   177  	logx.Disable()
   178  	passCounter := newRollingWindow()
   179  	rtCounter := newRollingWindow()
   180  	for i := 0; i < 10; i++ {
   181  		if i > 0 {
   182  			time.Sleep(bucketDuration)
   183  		}
   184  		passCounter.Add(float64((i + 1) * 100))
   185  		for j := i*10 + 1; j <= i*10+10; j++ {
   186  			rtCounter.Add(float64(j))
   187  		}
   188  	}
   189  	shedder := &adaptiveShedder{
   190  		passCounter:     passCounter,
   191  		rtCounter:       rtCounter,
   192  		windows:         buckets,
   193  		dropTime:        syncx.NewAtomicDuration(),
   194  		droppedRecently: syncx.ForAtomicBool(true),
   195  	}
   196  	assert.False(t, shedder.stillHot())
   197  	shedder.dropTime.Set(-coolOffDuration * 2)
   198  	assert.False(t, shedder.stillHot())
   199  }
   200  
   201  func BenchmarkAdaptiveShedder_Allow(b *testing.B) {
   202  	logx.Disable()
   203  
   204  	bench := func(b *testing.B) {
   205  		shedder := NewAdaptiveShedder()
   206  		proba := mathx.NewProba()
   207  		for i := 0; i < 6000; i++ {
   208  			p, err := shedder.Allow()
   209  			if err == nil {
   210  				time.Sleep(time.Millisecond)
   211  				if proba.TrueOnProba(0.01) {
   212  					p.Fail()
   213  				} else {
   214  					p.Pass()
   215  				}
   216  			}
   217  		}
   218  
   219  		b.ResetTimer()
   220  		for i := 0; i < b.N; i++ {
   221  			p, err := shedder.Allow()
   222  			if err == nil {
   223  				p.Pass()
   224  			}
   225  		}
   226  	}
   227  
   228  	systemOverloadChecker = func(int64) bool {
   229  		return true
   230  	}
   231  	b.Run("high load", bench)
   232  	systemOverloadChecker = func(int64) bool {
   233  		return false
   234  	}
   235  	b.Run("low load", bench)
   236  }
   237  
   238  func newRollingWindow() *collection.RollingWindow {
   239  	return collection.NewRollingWindow(buckets, bucketDuration, collection.IgnoreCurrentBucket())
   240  }