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 }