github.com/searKing/golang/go@v1.2.117/time/rate/rate_test.go (about) 1 // Copyright 2020 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package rate 6 7 import ( 8 "context" 9 "runtime" 10 "runtime/debug" 11 "sync" 12 "sync/atomic" 13 "testing" 14 "time" 15 16 time_ "github.com/searKing/golang/go/time" 17 ) 18 19 const ( 20 d = 100 * time.Millisecond 21 ) 22 23 type allow struct { 24 n int 25 ok bool 26 } 27 28 func runAllow(t *testing.T, lim *BurstLimiter, allows []allow) { 29 for i, allow := range allows { 30 ok := lim.AllowN(allow.n) 31 if ok { 32 lim.PutTokenN(allow.n) 33 } 34 if ok != allow.ok { 35 t.Errorf("step %d: lim.AllowN(%v) = %v want %v", i, allow.n, ok, allow.ok) 36 } 37 } 38 } 39 40 func TestLimiterBurst1(t *testing.T) { 41 runAllow(t, NewFullBurstLimiter(1), []allow{ 42 {1, true}, 43 {2, false}, // burst size is 1, so n=2 always fails 44 }) 45 } 46 47 func TestLimiterBurst3(t *testing.T) { 48 runAllow(t, NewFullBurstLimiter(3), []allow{ 49 {1, true}, 50 {2, true}, 51 {3, true}, 52 {4, false}, // burst size is 1, so n=3 always fails 53 }) 54 } 55 56 func TestSimultaneousRequests(t *testing.T) { 57 const ( 58 burst = 5 59 numRequests = 15 60 ) 61 var ( 62 wg sync.WaitGroup 63 numOK = uint32(0) 64 ) 65 66 // Very slow replenishing bucket. 67 lim := NewFullBurstLimiter(burst) 68 69 // Tries to take a token, atomically updates the counter and decreases the wait 70 // group counter. 71 f := func() { 72 defer wg.Done() 73 if ok := lim.Allow(); ok { 74 atomic.AddUint32(&numOK, 1) 75 } 76 } 77 78 wg.Add(numRequests) 79 for i := 0; i < numRequests; i++ { 80 go f() 81 } 82 wg.Wait() 83 if numOK != burst { 84 t.Errorf("numOK = %d, want %d", numOK, burst) 85 } 86 } 87 88 func TestLongRunningQPS(t *testing.T) { 89 if testing.Short() { 90 t.Skip("skipping in short mode") 91 } 92 if runtime.GOOS == "openbsd" { 93 t.Skip("low resolution time.Sleep invalidates test (golang.org/issue/14183)") 94 return 95 } 96 97 // The test runs for a few seconds executing many requests and then checks 98 // that overall number of requests is reasonable. 99 const ( 100 burst = 100 101 ) 102 var numOK = int32(0) 103 104 lim := NewFullBurstLimiter(burst) 105 106 var wg sync.WaitGroup 107 f := func() { 108 if ok := lim.Allow(); ok { 109 atomic.AddInt32(&numOK, 1) 110 } 111 wg.Done() 112 } 113 114 start := time.Now() 115 end := start.Add(5 * time.Second) 116 for time.Now().Before(end) { 117 wg.Add(1) 118 go f() 119 120 // This will still offer ~500 requests per second, but won't consume 121 // outrageous amount of CPU. 122 time.Sleep(2 * time.Millisecond) 123 } 124 wg.Wait() 125 ideal := burst 126 127 // We should never get more requests than allowed. 128 if want := int32(ideal + 1); numOK > want { 129 t.Errorf("numOK = %d, want %d (ideal %d)", numOK, want, ideal) 130 } 131 // We should get very close to the number of requests allowed. 132 if want := int32(0.999 * float64(ideal)); numOK < want { 133 t.Errorf("numOK = %d, want %d (ideal %d)", numOK, want, ideal) 134 } 135 } 136 137 type reserve struct { 138 n int 139 ready bool 140 } 141 142 func runReserve(t *testing.T, lim *BurstLimiter, req reserve) *Reservation { 143 return runReserveMax(t, lim, req, time_.InfDuration) 144 } 145 146 func runReserveMax(t *testing.T, lim *BurstLimiter, req reserve, maxReserve time.Duration) *Reservation { 147 ctx, cancel := context.WithTimeout(context.Background(), maxReserve) 148 defer cancel() 149 r := lim.reserveN(ctx, req.n, false, false) 150 if r.Ready() != req.ready { 151 t.Errorf("lim.reserveN(%v, %v) = (%v) want (%v)", req.n, maxReserve, r.Ready(), req.ready) 152 } 153 return r 154 } 155 156 func TestSimpleReserve(t *testing.T) { 157 lim := NewFullBurstLimiter(2) 158 159 runReserve(t, lim, reserve{2, true}).PutToken() 160 runReserve(t, lim, reserve{2, true}).PutToken() 161 runReserve(t, lim, reserve{2, true}).PutToken() 162 } 163 164 func TestSimpleReserveGC(t *testing.T) { 165 // disable GC so we can control when it happens. 166 defer debug.SetGCPercent(debug.SetGCPercent(-1)) 167 var rc bool 168 { 169 lim := NewEmptyBurstLimiter(1) 170 r := lim.Reserve(context.Background()) 171 r.canceled = func() { 172 rc = true 173 } 174 // After one GC, the BurstLimiter should keep the reservation alive. 175 runtime.GC() 176 if rc { 177 t.Errorf("reservation should be alive after gc") 178 } 179 runtime.KeepAlive(r) 180 181 // A second GC should drop the reservation. 182 runtime.GC() 183 if !rc { 184 t.Errorf("reservation should be dropped after gc") 185 } 186 } 187 } 188 189 func TestMix(t *testing.T) { 190 lim := NewFullBurstLimiter(2) 191 192 runReserve(t, lim, reserve{3, false}).PutToken() // should return false because n > Burst 193 runReserve(t, lim, reserve{2, true}).PutToken() 194 runAllow(t, lim, []allow{{3, false}}) // not enough tokens - don't allow 195 runReserve(t, lim, reserve{2, true}).PutToken() 196 runAllow(t, lim, []allow{{1, true}}) 197 } 198 199 func TestCancelInvalid(t *testing.T) { 200 lim := NewFullBurstLimiter(2) 201 202 r := runReserve(t, lim, reserve{2, true}) 203 defer r.PutToken() 204 runReserve(t, lim, reserve{3, false}) // should have no effect 205 runReserve(t, lim, reserve{2, false}) // did not get extra tokens 206 } 207 208 func TestCancelLast(t *testing.T) { 209 lim := NewFullBurstLimiter(2) 210 211 runReserve(t, lim, reserve{2, true}).PutToken() 212 r := runReserve(t, lim, reserve{2, true}) 213 r.PutToken() // got 2 tokens back 214 runReserve(t, lim, reserve{2, true}).PutToken() 215 } 216 217 func TestCancelTooLate(t *testing.T) { 218 lim := NewFullBurstLimiter(2) 219 220 runReserve(t, lim, reserve{2, true}).PutToken() 221 r := runReserve(t, lim, reserve{2, true}) 222 runReserve(t, lim, reserve{2, false}) 223 r.PutToken() // too late to cancel - should have no effect 224 } 225 226 func TestCancel1Tokens(t *testing.T) { 227 lim := NewFullBurstLimiter(2) 228 229 runReserve(t, lim, reserve{2, true}).PutToken() 230 r := runReserve(t, lim, reserve{1, true}) 231 runReserve(t, lim, reserve{1, true}) 232 runReserve(t, lim, reserve{1, false}) 233 r.PutToken() // got 1 tokens back 234 runReserve(t, lim, reserve{1, true}) 235 } 236 237 func TestCancel2Token(t *testing.T) { 238 lim := NewFullBurstLimiter(2) 239 240 runReserve(t, lim, reserve{2, true}).PutToken() 241 r := runReserve(t, lim, reserve{2, true}) 242 runReserve(t, lim, reserve{1, false}) 243 r.PutToken() // got 2 token back 244 runReserve(t, lim, reserve{2, true}) 245 } 246 247 func TestCancelMulti(t *testing.T) { 248 lim := NewFullBurstLimiter(4) 249 250 runReserve(t, lim, reserve{4, true}).PutToken() 251 rA := runReserve(t, lim, reserve{3, true}) 252 runReserve(t, lim, reserve{1, true}).PutToken() 253 rC := runReserve(t, lim, reserve{1, true}) 254 rC.PutToken() // get 1 token back 255 rA.PutToken() // get 3+1 tokens back, as if C was never reserved 256 runReserve(t, lim, reserve{4, true}) 257 } 258 259 func TestReserveJumpBack(t *testing.T) { 260 lim := NewFullBurstLimiter(2) 261 262 runReserve(t, lim, reserve{2, true}).PutToken() // start at t1 263 runReserve(t, lim, reserve{1, true}).PutToken() // should violate Limit,Burst 264 runReserve(t, lim, reserve{2, true}).PutToken() 265 } 266 267 func TestReserveJumpBackCancel(t *testing.T) { 268 lim := NewFullBurstLimiter(2) 269 270 runReserve(t, lim, reserve{2, true}).PutToken() // start at t1 271 r := runReserve(t, lim, reserve{2, true}) 272 runReserve(t, lim, reserve{0, true}) 273 runReserve(t, lim, reserve{1, false}) 274 r.PutToken() // cancel at get 1 token back 275 runReserve(t, lim, reserve{2, true}) // should violate Limit,Burst 276 } 277 278 func TestReserveMax(t *testing.T) { 279 lim := NewFullBurstLimiter(2) 280 maxT := d 281 282 runReserveMax(t, lim, reserve{2, true}, maxT).PutToken() 283 runReserveMax(t, lim, reserve{1, true}, maxT).PutToken() // reserve for close future 284 } 285 286 type wait struct { 287 name string 288 ctx context.Context 289 n int 290 nilErr bool 291 } 292 293 func runWait(t *testing.T, lim *BurstLimiter, w wait) { 294 err := lim.WaitN(w.ctx, w.n) 295 if (w.nilErr && err != nil) || (!w.nilErr && err == nil) { 296 errString := "<nil>" 297 if !w.nilErr { 298 errString = "<non-nil error>" 299 } 300 t.Errorf("lim.WaitN(%v, lim, %v) = %v; want %v", 301 w.name, w.n, err, errString) 302 } 303 } 304 305 func TestWaitSimple(t *testing.T) { 306 lim := NewFullBurstLimiter(3) 307 308 ctx, cancel := context.WithCancel(context.Background()) 309 cancel() 310 runWait(t, lim, wait{"already-cancelled", ctx, 1, false}) 311 312 runWait(t, lim, wait{"exceed-burst-error", context.Background(), 4, false}) 313 314 ctx, cancel = context.WithTimeout(context.Background(), time.Second) 315 runWait(t, lim, wait{"act-now", ctx, 2, true}) 316 lim.PutTokenN(2) 317 runWait(t, lim, wait{"act-later", ctx, 3, true}) 318 } 319 320 func TestWaitTimeout(t *testing.T) { 321 lim := NewFullBurstLimiter(3) 322 323 ctx, cancel := context.WithTimeout(context.Background(), d) 324 defer cancel() 325 runWait(t, lim, wait{"act-now", ctx, 2, true}) 326 runWait(t, lim, wait{"w-timeout-err", ctx, 3, false}) 327 } 328 329 func TestWaitBlock(t *testing.T) { 330 lim := NewFullBurstLimiter(1) 331 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 332 defer cancel() 333 runWait(t, lim, wait{"act-now", context.Background(), 1, true}) 334 runtime.GC() 335 runWait(t, lim, wait{"w-timeout-err", ctx, 1, false}) 336 // timeout 337 lim.PutToken() 338 { 339 ctx, cancel = context.WithTimeout(context.Background(), time.Second) 340 defer cancel() 341 runWait(t, lim, wait{"act-later", ctx, 1, true}) 342 runtime.GC() 343 runWait(t, lim, wait{"w-timeout-err", ctx, 1, false}) 344 } 345 346 } 347 348 func BenchmarkAllowN(b *testing.B) { 349 lim := NewFullBurstLimiter(1) 350 b.ReportAllocs() 351 b.ResetTimer() 352 b.RunParallel(func(pb *testing.PB) { 353 for pb.Next() { 354 lim.AllowN(1) 355 } 356 }) 357 } 358 359 func BenchmarkWaitNNoDelay(b *testing.B) { 360 lim := NewFullBurstLimiter(b.N) 361 ctx := context.Background() 362 b.ReportAllocs() 363 b.ResetTimer() 364 for i := 0; i < b.N; i++ { 365 lim.WaitN(ctx, 1) 366 } 367 } 368 369 func TestSimultaneousLongRequests(t *testing.T) { 370 const ( 371 burst = 5 372 numRequests = 15 373 ) 374 var ( 375 timeout = 1 * time.Millisecond 376 ) 377 var ( 378 wg sync.WaitGroup 379 numOK = uint32(0) 380 ) 381 382 // Very slow replenishing bucket. 383 lim := NewFullBurstLimiter(burst) 384 385 // Tries to take a token, atomically updates the counter and decreases the wait 386 // group counter. 387 f := func(i int) { 388 if i < numRequests { 389 defer wg.Done() 390 } 391 var limiterCtx = context.Background() 392 var cancel context.CancelFunc 393 if timeout > 0 { 394 limiterCtx, cancel = context.WithTimeout(context.Background(), timeout) 395 defer cancel() 396 } 397 if err := lim.Wait(limiterCtx); err != nil { 398 t.Logf("#%d wait expect ready, got err %s", i, err) 399 } else { 400 atomic.AddUint32(&numOK, 1) 401 defer lim.PutToken() 402 t.Logf("#%d got token", i) 403 time.Sleep(time.Second) 404 t.Logf("#%d put token", i) 405 } 406 } 407 408 wg.Add(numRequests) 409 for i := 0; i < numRequests; i++ { 410 go f(i) 411 } 412 wg.Wait() 413 f(numRequests) 414 if numOK != burst+1 { 415 t.Errorf("numOK = %d, want %d", numOK, numRequests) 416 } 417 }