gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/balancer/rls/internal/adaptive/adaptive_test.go (about) 1 /* 2 * 3 * Copyright 2020 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package adaptive 20 21 import ( 22 "sync" 23 "testing" 24 "time" 25 ) 26 27 // stats returns a tuple with accepts, throttles for the current time. 28 func (th *Throttler) stats() (int64, int64) { 29 now := timeNowFunc() 30 31 th.mu.Lock() 32 a, t := th.accepts.sum(now), th.throttles.sum(now) 33 th.mu.Unlock() 34 return a, t 35 } 36 37 // Enums for responses. 38 const ( 39 E = iota // No response 40 A // Accepted 41 T // Throttled 42 ) 43 44 func TestRegisterBackendResponse(t *testing.T) { 45 testcases := []struct { 46 desc string 47 bins int64 48 ticks []int64 49 responses []int64 50 wantAccepts []int64 51 wantThrottled []int64 52 }{ 53 { 54 "Accumulate", 55 3, 56 []int64{0, 1, 2}, // Ticks 57 []int64{A, T, E}, // Responses 58 []int64{1, 1, 1}, // Accepts 59 []int64{0, 1, 1}, // Throttled 60 }, 61 { 62 "LightTimeTravel", 63 3, 64 []int64{1, 0, 2}, // Ticks 65 []int64{A, T, E}, // Response 66 []int64{1, 1, 1}, // Accepts 67 []int64{0, 1, 1}, // Throttled 68 }, 69 { 70 "HeavyTimeTravel", 71 3, 72 []int64{8, 0, 9}, // Ticks 73 []int64{A, A, A}, // Response 74 []int64{1, 1, 2}, // Accepts 75 []int64{0, 0, 0}, // Throttled 76 }, 77 { 78 "Rollover", 79 1, 80 []int64{0, 1, 2}, // Ticks 81 []int64{A, T, E}, // Responses 82 []int64{1, 0, 0}, // Accepts 83 []int64{0, 1, 0}, // Throttled 84 }, 85 } 86 87 m := mockClock{} 88 oldTimeNowFunc := timeNowFunc 89 timeNowFunc = m.Now 90 defer func() { timeNowFunc = oldTimeNowFunc }() 91 92 for _, test := range testcases { 93 t.Run(test.desc, func(t *testing.T) { 94 th := newWithArgs(time.Duration(test.bins), test.bins, 2.0, 8) 95 for i, tick := range test.ticks { 96 m.SetNanos(tick) 97 98 if test.responses[i] != E { 99 th.RegisterBackendResponse(test.responses[i] == T) 100 } 101 102 if gotAccepts, gotThrottled := th.stats(); gotAccepts != test.wantAccepts[i] || gotThrottled != test.wantThrottled[i] { 103 t.Errorf("th.stats() = {%d, %d} for index %d, want {%d, %d}", i, gotAccepts, gotThrottled, test.wantAccepts[i], test.wantThrottled[i]) 104 } 105 } 106 }) 107 } 108 } 109 110 func TestShouldThrottleOptions(t *testing.T) { 111 // ShouldThrottle should return true iff 112 // (requests - RatioForAccepts * accepts) / (requests + RequestsPadding) <= p 113 // where p is a random number. For the purposes of this test it's fixed 114 // to 0.5. 115 responses := []int64{T, T, T, T, T, T, T, T, T, A, A, A, A, A, A, T, T, T, T} 116 117 n := false 118 y := true 119 120 testcases := []struct { 121 desc string 122 ratioForAccepts float64 123 requestsPadding float64 124 want []bool 125 }{ 126 { 127 "Baseline", 128 1.1, 129 8, 130 []bool{n, n, n, n, n, n, n, n, y, y, y, y, y, n, n, n, y, y, y}, 131 }, 132 { 133 "ChangePadding", 134 1.1, 135 7, 136 []bool{n, n, n, n, n, n, n, y, y, y, y, y, y, y, y, y, y, y, y}, 137 }, 138 { 139 "ChangeRatioForAccepts", 140 1.4, 141 8, 142 []bool{n, n, n, n, n, n, n, n, y, y, n, n, n, n, n, n, n, n, n}, 143 }, 144 } 145 146 m := mockClock{} 147 oldTimeNowFunc := timeNowFunc 148 timeNowFunc = m.Now 149 oldRandFunc := randFunc 150 randFunc = func() float64 { return 0.5 } 151 defer func() { 152 timeNowFunc = oldTimeNowFunc 153 randFunc = oldRandFunc 154 }() 155 156 for _, test := range testcases { 157 t.Run(test.desc, func(t *testing.T) { 158 m.SetNanos(0) 159 th := newWithArgs(time.Nanosecond, 1, test.ratioForAccepts, test.requestsPadding) 160 for i, response := range responses { 161 if response != E { 162 th.RegisterBackendResponse(response == T) 163 } 164 if got := th.ShouldThrottle(); got != test.want[i] { 165 t.Errorf("ShouldThrottle for index %d: got %v, want %v", i, got, test.want[i]) 166 } 167 } 168 }) 169 } 170 } 171 172 func TestParallel(t *testing.T) { 173 // Uses all the defaults which comes with a 30 second duration. 174 th := New() 175 176 testDuration := 2 * time.Second 177 numRoutines := 10 178 accepts := make([]int64, numRoutines) 179 throttles := make([]int64, numRoutines) 180 var wg sync.WaitGroup 181 for i := 0; i < numRoutines; i++ { 182 wg.Add(1) 183 go func(num int) { 184 defer wg.Done() 185 186 ticker := time.NewTicker(testDuration) 187 var accept int64 188 var throttle int64 189 for i := 0; ; i++ { 190 select { 191 case <-ticker.C: 192 ticker.Stop() 193 accepts[num] = accept 194 throttles[num] = throttle 195 return 196 default: 197 if i%2 == 0 { 198 th.RegisterBackendResponse(true) 199 throttle++ 200 } else { 201 th.RegisterBackendResponse(false) 202 accept++ 203 } 204 } 205 } 206 }(i) 207 } 208 wg.Wait() 209 210 var wantAccepts, wantThrottles int64 211 for i := 0; i < numRoutines; i++ { 212 wantAccepts += accepts[i] 213 wantThrottles += throttles[i] 214 } 215 216 if gotAccepts, gotThrottles := th.stats(); gotAccepts != wantAccepts || gotThrottles != wantThrottles { 217 t.Errorf("th.stats() = {%d, %d}, want {%d, %d}", gotAccepts, gotThrottles, wantAccepts, wantThrottles) 218 } 219 } 220 221 type mockClock struct { 222 t time.Time 223 } 224 225 func (m *mockClock) Now() time.Time { 226 return m.t 227 } 228 229 func (m *mockClock) SetNanos(n int64) { 230 m.t = time.Unix(0, n) 231 }