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