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 }