github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/internal/rate/rate_test.go (about)

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