github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/internal/rate/rate_test.go (about) 1 // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //go:build go1.7 16 // +build go1.7 17 18 package rate 19 20 import ( 21 "context" 22 "math" 23 "runtime" 24 "sync" 25 "sync/atomic" 26 "testing" 27 "time" 28 ) 29 30 func TestLimit(t *testing.T) { 31 if Limit(10) == Inf { 32 t.Errorf("Limit(10) == Inf should be false") 33 } 34 } 35 36 func closeEnough(a, b Limit) bool { 37 return (math.Abs(float64(a)/float64(b)) - 1.0) < 1e-9 38 } 39 40 func TestEvery(t *testing.T) { 41 cases := []struct { 42 interval time.Duration 43 lim Limit 44 }{ 45 {0, Inf}, 46 {-1, Inf}, 47 {1 * time.Nanosecond, Limit(1e9)}, 48 {1 * time.Microsecond, Limit(1e6)}, 49 {1 * time.Millisecond, Limit(1e3)}, 50 {10 * time.Millisecond, Limit(100)}, 51 {100 * time.Millisecond, Limit(10)}, 52 {1 * time.Second, Limit(1)}, 53 {2 * time.Second, Limit(0.5)}, 54 {time.Duration(2.5 * float64(time.Second)), Limit(0.4)}, 55 {4 * time.Second, Limit(0.25)}, 56 {10 * time.Second, Limit(0.1)}, 57 {time.Duration(math.MaxInt64), Limit(1e9 / float64(math.MaxInt64))}, 58 } 59 for _, tc := range cases { 60 lim := Every(tc.interval) 61 if !closeEnough(lim, tc.lim) { 62 t.Errorf("Every(%v) = %v want %v", tc.interval, lim, tc.lim) 63 } 64 } 65 } 66 67 const ( 68 d = 100 * time.Millisecond 69 ) 70 71 var ( 72 t0 = time.Now() 73 t1 = t0.Add(time.Duration(1) * d) 74 t2 = t0.Add(time.Duration(2) * d) 75 t3 = t0.Add(time.Duration(3) * d) 76 t4 = t0.Add(time.Duration(4) * d) 77 t5 = t0.Add(time.Duration(5) * d) 78 t9 = t0.Add(time.Duration(9) * d) 79 ) 80 81 type allow struct { 82 t time.Time 83 n int 84 ok bool 85 } 86 87 func run(t *testing.T, lim *Limiter, allows []allow) { 88 for i, allow := range allows { 89 ok := lim.AllowN(allow.t, allow.n) 90 if ok != allow.ok { 91 t.Errorf("step %d: lim.AllowN(%v, %v) = %v want %v", 92 i, allow.t, allow.n, ok, allow.ok) 93 } 94 } 95 } 96 97 func TestLimiterBurst1(t *testing.T) { 98 run(t, NewLimiter(10, 1), []allow{ 99 {t0, 1, true}, 100 {t0, 1, false}, 101 {t0, 1, false}, 102 {t1, 1, true}, 103 {t1, 1, false}, 104 {t1, 1, false}, 105 {t2, 2, false}, // burst size is 1, so n=2 always fails 106 {t2, 1, true}, 107 {t2, 1, false}, 108 }) 109 } 110 111 func TestLimiterBurst3(t *testing.T) { 112 run(t, NewLimiter(10, 3), []allow{ 113 {t0, 2, true}, 114 {t0, 2, false}, 115 {t0, 1, true}, 116 {t0, 1, false}, 117 {t1, 4, false}, 118 {t2, 1, true}, 119 {t3, 1, true}, 120 {t4, 1, true}, 121 {t4, 1, true}, 122 {t4, 1, false}, 123 {t4, 1, false}, 124 {t9, 3, true}, 125 {t9, 0, true}, 126 }) 127 } 128 129 func TestLimiterJumpBackwards(t *testing.T) { 130 run(t, NewLimiter(10, 3), []allow{ 131 {t1, 1, true}, // start at t1 132 {t0, 1, true}, // jump back to t0, two tokens remain 133 {t0, 1, true}, 134 {t0, 1, false}, 135 {t0, 1, false}, 136 {t1, 1, true}, // got a token 137 {t1, 1, false}, 138 {t1, 1, false}, 139 {t2, 1, true}, // got another token 140 {t2, 1, false}, 141 {t2, 1, false}, 142 }) 143 } 144 145 func TestSimultaneousRequests(t *testing.T) { 146 const ( 147 limit = 1 148 burst = 5 149 numRequests = 15 150 ) 151 var ( 152 wg sync.WaitGroup 153 numOK = uint32(0) 154 ) 155 156 // Very slow replenishing bucket. 157 lim := NewLimiter(limit, burst) 158 159 // Tries to take a token, atomically updates the counter and decreases the wait 160 // group counter. 161 f := func() { 162 defer wg.Done() 163 if ok := lim.Allow(); ok { 164 atomic.AddUint32(&numOK, 1) 165 } 166 } 167 168 wg.Add(numRequests) 169 for i := 0; i < numRequests; i++ { 170 go f() 171 } 172 wg.Wait() 173 if numOK != burst { 174 t.Errorf("numOK = %d, want %d", numOK, burst) 175 } 176 } 177 178 func TestLongRunningQPS(t *testing.T) { 179 if testing.Short() { 180 t.Skip("skipping in short mode") 181 } 182 183 // The test runs for a few seconds executing many requests and then checks 184 // that overall number of requests is reasonable. 185 const ( 186 limit = 100 187 burst = 100 188 ) 189 var numOK = int32(0) 190 191 lim := NewLimiter(limit, burst) 192 193 // We simulate time advancing below to remove any timing related dependency 194 // that can cause the test to flake. 195 start := time.Now() 196 elapsed := 5 * time.Second 197 increment := 2 * time.Millisecond 198 count := int(elapsed / increment) 199 200 for i := 0; i <= count; i++ { 201 if ok := lim.AllowN(start.Add(time.Duration(i)*2*time.Millisecond), 1); ok { 202 atomic.AddInt32(&numOK, 1) 203 } 204 } 205 ideal := burst + (limit * float64(elapsed) / float64(time.Second)) 206 207 // We should never get more requests than allowed. 208 if want := int32(ideal + 1); numOK > want { 209 t.Errorf("numOK = %d, want %d (ideal %f)", numOK, want, ideal) 210 } 211 // We should get very close to the number of requests allowed. 212 if want := int32(0.99 * ideal); numOK < want { 213 t.Errorf("numOK = %d, want %d (ideal %f)", numOK, want, ideal) 214 } 215 } 216 217 type request struct { 218 t time.Time 219 n int 220 act time.Time 221 ok bool 222 } 223 224 // dFromDuration converts a duration to a multiple of the global constant d 225 func dFromDuration(dur time.Duration) int { 226 // Adding a millisecond to be swallowed by the integer division 227 // because we don't care about small inaccuracies 228 return int((dur + time.Millisecond) / d) 229 } 230 231 // dSince returns multiples of d since t0 232 func dSince(t time.Time) int { 233 return dFromDuration(t.Sub(t0)) 234 } 235 236 func runReserve(t *testing.T, lim *Limiter, req request) *Reservation { 237 return runReserveMax(t, lim, req, InfDuration) 238 } 239 240 func runReserveMax(t *testing.T, lim *Limiter, req request, maxReserve time.Duration) *Reservation { 241 r := lim.reserveN(req.t, req.n, maxReserve) 242 if r.ok && (dSince(r.timeToAct) != dSince(req.act)) || r.ok != req.ok { 243 t.Errorf("lim.reserveN(t%d, %v, %v) = (t%d, %v) want (t%d, %v)", 244 dSince(req.t), req.n, maxReserve, dSince(r.timeToAct), r.ok, dSince(req.act), req.ok) 245 } 246 return &r 247 } 248 249 func TestSimpleReserve(t *testing.T) { 250 lim := NewLimiter(10, 2) 251 252 runReserve(t, lim, request{t0, 2, t0, true}) 253 runReserve(t, lim, request{t0, 2, t2, true}) 254 runReserve(t, lim, request{t3, 2, t4, true}) 255 } 256 257 func TestMix(t *testing.T) { 258 lim := NewLimiter(10, 2) 259 260 runReserve(t, lim, request{t0, 3, t1, false}) // should return false because n > Burst 261 runReserve(t, lim, request{t0, 2, t0, true}) 262 run(t, lim, []allow{{t1, 2, false}}) // not enought tokens - don't allow 263 runReserve(t, lim, request{t1, 2, t2, true}) 264 run(t, lim, []allow{{t1, 1, false}}) // negative tokens - don't allow 265 run(t, lim, []allow{{t3, 1, true}}) 266 } 267 268 func TestCancelInvalid(t *testing.T) { 269 lim := NewLimiter(10, 2) 270 271 runReserve(t, lim, request{t0, 2, t0, true}) 272 r := runReserve(t, lim, request{t0, 3, t3, false}) 273 r.CancelAt(t0) // should have no effect 274 runReserve(t, lim, request{t0, 2, t2, true}) // did not get extra tokens 275 } 276 277 func TestCancelLast(t *testing.T) { 278 lim := NewLimiter(10, 2) 279 280 runReserve(t, lim, request{t0, 2, t0, true}) 281 r := runReserve(t, lim, request{t0, 2, t2, true}) 282 r.CancelAt(t1) // got 2 tokens back 283 runReserve(t, lim, request{t1, 2, t2, true}) 284 } 285 286 func TestCancelTooLate(t *testing.T) { 287 lim := NewLimiter(10, 2) 288 289 runReserve(t, lim, request{t0, 2, t0, true}) 290 r := runReserve(t, lim, request{t0, 2, t2, true}) 291 r.CancelAt(t3) // too late to cancel - should have no effect 292 runReserve(t, lim, request{t3, 2, t4, true}) 293 } 294 295 func TestCancel0Tokens(t *testing.T) { 296 lim := NewLimiter(10, 2) 297 298 runReserve(t, lim, request{t0, 2, t0, true}) 299 r := runReserve(t, lim, request{t0, 1, t1, true}) 300 runReserve(t, lim, request{t0, 1, t2, true}) 301 r.CancelAt(t0) // got 0 tokens back 302 runReserve(t, lim, request{t0, 1, t3, true}) 303 } 304 305 func TestCancel1Token(t *testing.T) { 306 lim := NewLimiter(10, 2) 307 308 runReserve(t, lim, request{t0, 2, t0, true}) 309 r := runReserve(t, lim, request{t0, 2, t2, true}) 310 runReserve(t, lim, request{t0, 1, t3, true}) 311 r.CancelAt(t2) // got 1 token back 312 runReserve(t, lim, request{t2, 2, t4, true}) 313 } 314 315 func TestCancelMulti(t *testing.T) { 316 lim := NewLimiter(10, 4) 317 318 runReserve(t, lim, request{t0, 4, t0, true}) 319 rA := runReserve(t, lim, request{t0, 3, t3, true}) 320 runReserve(t, lim, request{t0, 1, t4, true}) 321 rC := runReserve(t, lim, request{t0, 1, t5, true}) 322 rC.CancelAt(t1) // get 1 token back 323 rA.CancelAt(t1) // get 2 tokens back, as if C was never reserved 324 runReserve(t, lim, request{t1, 3, t5, true}) 325 } 326 327 func TestReserveJumpBack(t *testing.T) { 328 lim := NewLimiter(10, 2) 329 330 runReserve(t, lim, request{t1, 2, t1, true}) // start at t1 331 runReserve(t, lim, request{t0, 1, t1, true}) // should violate Limit,Burst 332 runReserve(t, lim, request{t2, 2, t3, true}) 333 } 334 335 func TestReserveJumpBackCancel(t *testing.T) { 336 lim := NewLimiter(10, 2) 337 338 runReserve(t, lim, request{t1, 2, t1, true}) // start at t1 339 r := runReserve(t, lim, request{t1, 2, t3, true}) 340 runReserve(t, lim, request{t1, 1, t4, true}) 341 r.CancelAt(t0) // cancel at t0, get 1 token back 342 runReserve(t, lim, request{t1, 2, t4, true}) // should violate Limit,Burst 343 } 344 345 func TestReserveSetLimit(t *testing.T) { 346 lim := NewLimiter(5, 2) 347 348 runReserve(t, lim, request{t0, 2, t0, true}) 349 runReserve(t, lim, request{t0, 2, t4, true}) 350 lim.SetLimitAt(t2, 10) 351 runReserve(t, lim, request{t2, 1, t4, true}) // violates Limit and Burst 352 } 353 354 func TestReserveSetLimitCancel(t *testing.T) { 355 lim := NewLimiter(5, 2) 356 357 runReserve(t, lim, request{t0, 2, t0, true}) 358 r := runReserve(t, lim, request{t0, 2, t4, true}) 359 lim.SetLimitAt(t2, 10) 360 r.CancelAt(t2) // 2 tokens back 361 runReserve(t, lim, request{t2, 2, t3, true}) 362 } 363 364 func TestReserveMax(t *testing.T) { 365 lim := NewLimiter(10, 2) 366 maxT := d 367 368 runReserveMax(t, lim, request{t0, 2, t0, true}, maxT) 369 runReserveMax(t, lim, request{t0, 1, t1, true}, maxT) // reserve for close future 370 runReserveMax(t, lim, request{t0, 1, t2, false}, maxT) // time to act too far in the future 371 } 372 373 type wait struct { 374 name string 375 ctx context.Context 376 n int 377 delay int // in multiples of d 378 nilErr bool 379 } 380 381 func runWait(t *testing.T, lim *Limiter, w wait) { 382 start := time.Now() 383 err := lim.WaitN(w.ctx, w.n) 384 delay := time.Since(start) 385 if (w.nilErr && err != nil) || (!w.nilErr && err == nil) || w.delay != dFromDuration(delay) { 386 errString := "<nil>" 387 if !w.nilErr { 388 errString = "<non-nil error>" 389 } 390 t.Errorf("lim.WaitN(%v, lim, %v) = %v with delay %v ; want %v with delay %v", 391 w.name, w.n, err, delay, errString, d*time.Duration(w.delay)) 392 } 393 } 394 395 func TestWaitSimple(t *testing.T) { 396 switch runtime.GOOS { 397 case "windows", "darwin": 398 t.Skip("flaky; see #1256") 399 } 400 401 lim := NewLimiter(10, 3) 402 403 ctx, cancel := context.WithCancel(context.Background()) 404 cancel() 405 runWait(t, lim, wait{"already-cancelled", ctx, 1, 0, false}) 406 407 runWait(t, lim, wait{"exceed-burst-error", context.Background(), 4, 0, false}) 408 409 runWait(t, lim, wait{"act-now", context.Background(), 2, 0, true}) 410 runWait(t, lim, wait{"act-later", context.Background(), 3, 2, true}) 411 } 412 413 func TestWaitCancel(t *testing.T) { 414 switch runtime.GOOS { 415 case "windows", "darwin": 416 t.Skip("flaky; see #1187") 417 } 418 419 lim := NewLimiter(10, 3) 420 421 ctx, cancel := context.WithCancel(context.Background()) 422 runWait(t, lim, wait{"act-now", ctx, 2, 0, true}) // after this lim.tokens = 1 423 go func() { 424 time.Sleep(d) 425 cancel() 426 }() 427 runWait(t, lim, wait{"will-cancel", ctx, 3, 1, false}) 428 // should get 3 tokens back, and have lim.tokens = 2 429 t.Logf("tokens:%v last:%v lastEvent:%v", lim.tokens, lim.last, lim.lastEvent) 430 runWait(t, lim, wait{"act-now-after-cancel", context.Background(), 2, 0, true}) 431 } 432 433 func TestWaitTimeout(t *testing.T) { 434 if runtime.GOOS == "windows" { 435 t.Skip("flaky on Windows") 436 } 437 438 lim := NewLimiter(10, 3) 439 440 ctx, cancel := context.WithTimeout(context.Background(), d) 441 defer cancel() 442 runWait(t, lim, wait{"act-now", ctx, 2, 0, true}) 443 runWait(t, lim, wait{"w-timeout-err", ctx, 3, 0, false}) 444 } 445 446 func TestWaitInf(t *testing.T) { 447 lim := NewLimiter(Inf, 0) 448 449 runWait(t, lim, wait{"exceed-burst-no-error", context.Background(), 3, 0, true}) 450 } 451 452 func BenchmarkAllowN(b *testing.B) { 453 lim := NewLimiter(Every(1*time.Second), 1) 454 now := time.Now() 455 b.ReportAllocs() 456 b.ResetTimer() 457 b.RunParallel(func(pb *testing.PB) { 458 for pb.Next() { 459 lim.AllowN(now, 1) 460 } 461 }) 462 }