github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/http/ratelimit/ratelimit_test.go (about)

     1  package ratelimit
     2  
     3  import (
     4  	"math"
     5  	"testing"
     6  	"time"
     7  
     8  	gc "gopkg.in/check.v1"
     9  )
    10  
    11  func TestPackage(t *testing.T) {
    12  	gc.TestingT(t)
    13  }
    14  
    15  type rateLimitSuite struct{}
    16  
    17  var _ = gc.Suite(rateLimitSuite{})
    18  
    19  type takeReq struct {
    20  	time       time.Duration
    21  	count      int64
    22  	expectWait time.Duration
    23  }
    24  
    25  var takeTests = []struct {
    26  	about        string
    27  	fillInterval time.Duration
    28  	capacity     int64
    29  	reqs         []takeReq
    30  }{{
    31  	about:        "serial requests",
    32  	fillInterval: 250 * time.Millisecond,
    33  	capacity:     10,
    34  	reqs: []takeReq{{
    35  		time:       0,
    36  		count:      0,
    37  		expectWait: 0,
    38  	}, {
    39  		time:       0,
    40  		count:      10,
    41  		expectWait: 0,
    42  	}, {
    43  		time:       0,
    44  		count:      1,
    45  		expectWait: 250 * time.Millisecond,
    46  	}, {
    47  		time:       250 * time.Millisecond,
    48  		count:      1,
    49  		expectWait: 250 * time.Millisecond,
    50  	}},
    51  }, {
    52  	about:        "concurrent requests",
    53  	fillInterval: 250 * time.Millisecond,
    54  	capacity:     10,
    55  	reqs: []takeReq{{
    56  		time:       0,
    57  		count:      10,
    58  		expectWait: 0,
    59  	}, {
    60  		time:       0,
    61  		count:      2,
    62  		expectWait: 500 * time.Millisecond,
    63  	}, {
    64  		time:       0,
    65  		count:      2,
    66  		expectWait: 1000 * time.Millisecond,
    67  	}, {
    68  		time:       0,
    69  		count:      1,
    70  		expectWait: 1250 * time.Millisecond,
    71  	}},
    72  }, {
    73  	about:        "more than capacity",
    74  	fillInterval: 1 * time.Millisecond,
    75  	capacity:     10,
    76  	reqs: []takeReq{{
    77  		time:       0,
    78  		count:      10,
    79  		expectWait: 0,
    80  	}, {
    81  		time:       20 * time.Millisecond,
    82  		count:      15,
    83  		expectWait: 5 * time.Millisecond,
    84  	}},
    85  }, {
    86  	about:        "sub-quantum time",
    87  	fillInterval: 10 * time.Millisecond,
    88  	capacity:     10,
    89  	reqs: []takeReq{{
    90  		time:       0,
    91  		count:      10,
    92  		expectWait: 0,
    93  	}, {
    94  		time:       7 * time.Millisecond,
    95  		count:      1,
    96  		expectWait: 3 * time.Millisecond,
    97  	}, {
    98  		time:       8 * time.Millisecond,
    99  		count:      1,
   100  		expectWait: 12 * time.Millisecond,
   101  	}},
   102  }, {
   103  	about:        "within capacity",
   104  	fillInterval: 10 * time.Millisecond,
   105  	capacity:     5,
   106  	reqs: []takeReq{{
   107  		time:       0,
   108  		count:      5,
   109  		expectWait: 0,
   110  	}, {
   111  		time:       60 * time.Millisecond,
   112  		count:      5,
   113  		expectWait: 0,
   114  	}, {
   115  		time:       60 * time.Millisecond,
   116  		count:      1,
   117  		expectWait: 10 * time.Millisecond,
   118  	}, {
   119  		time:       80 * time.Millisecond,
   120  		count:      2,
   121  		expectWait: 10 * time.Millisecond,
   122  	}},
   123  }}
   124  
   125  var availTests = []struct {
   126  	about        string
   127  	capacity     int64
   128  	fillInterval time.Duration
   129  	take         int64
   130  	sleep        time.Duration
   131  
   132  	expectCountAfterTake  int64
   133  	expectCountAfterSleep int64
   134  }{{
   135  	about:                 "should fill tokens after interval",
   136  	capacity:              5,
   137  	fillInterval:          time.Second,
   138  	take:                  5,
   139  	sleep:                 time.Second,
   140  	expectCountAfterTake:  0,
   141  	expectCountAfterSleep: 1,
   142  }, {
   143  	about:                 "should fill tokens plus existing count",
   144  	capacity:              2,
   145  	fillInterval:          time.Second,
   146  	take:                  1,
   147  	sleep:                 time.Second,
   148  	expectCountAfterTake:  1,
   149  	expectCountAfterSleep: 2,
   150  }, {
   151  	about:                 "shouldn't fill before interval",
   152  	capacity:              2,
   153  	fillInterval:          2 * time.Second,
   154  	take:                  1,
   155  	sleep:                 time.Second,
   156  	expectCountAfterTake:  1,
   157  	expectCountAfterSleep: 1,
   158  }, {
   159  	about:                 "should fill only once after 1*interval before 2*interval",
   160  	capacity:              2,
   161  	fillInterval:          2 * time.Second,
   162  	take:                  1,
   163  	sleep:                 3 * time.Second,
   164  	expectCountAfterTake:  1,
   165  	expectCountAfterSleep: 2,
   166  }}
   167  
   168  func (rateLimitSuite) TestTake(c *gc.C) {
   169  	for i, test := range takeTests {
   170  		tb := NewBucket(test.fillInterval, test.capacity)
   171  		for j, req := range test.reqs {
   172  			d, ok := tb.take(tb.startTime.Add(req.time), req.count, infinityDuration)
   173  			c.Assert(ok, gc.Equals, true)
   174  			if d != req.expectWait {
   175  				c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expectWait)
   176  			}
   177  		}
   178  	}
   179  }
   180  
   181  func (rateLimitSuite) TestTakeMaxDuration(c *gc.C) {
   182  	for i, test := range takeTests {
   183  		tb := NewBucket(test.fillInterval, test.capacity)
   184  		for j, req := range test.reqs {
   185  			if req.expectWait > 0 {
   186  				d, ok := tb.take(tb.startTime.Add(req.time), req.count, req.expectWait-1)
   187  				c.Assert(ok, gc.Equals, false)
   188  				c.Assert(d, gc.Equals, time.Duration(0))
   189  			}
   190  			d, ok := tb.take(tb.startTime.Add(req.time), req.count, req.expectWait)
   191  			c.Assert(ok, gc.Equals, true)
   192  			if d != req.expectWait {
   193  				c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expectWait)
   194  			}
   195  		}
   196  	}
   197  }
   198  
   199  type takeAvailableReq struct {
   200  	time   time.Duration
   201  	count  int64
   202  	expect int64
   203  }
   204  
   205  var takeAvailableTests = []struct {
   206  	about        string
   207  	fillInterval time.Duration
   208  	capacity     int64
   209  	reqs         []takeAvailableReq
   210  }{{
   211  	about:        "serial requests",
   212  	fillInterval: 250 * time.Millisecond,
   213  	capacity:     10,
   214  	reqs: []takeAvailableReq{{
   215  		time:   0,
   216  		count:  0,
   217  		expect: 0,
   218  	}, {
   219  		time:   0,
   220  		count:  10,
   221  		expect: 10,
   222  	}, {
   223  		time:   0,
   224  		count:  1,
   225  		expect: 0,
   226  	}, {
   227  		time:   250 * time.Millisecond,
   228  		count:  1,
   229  		expect: 1,
   230  	}},
   231  }, {
   232  	about:        "concurrent requests",
   233  	fillInterval: 250 * time.Millisecond,
   234  	capacity:     10,
   235  	reqs: []takeAvailableReq{{
   236  		time:   0,
   237  		count:  5,
   238  		expect: 5,
   239  	}, {
   240  		time:   0,
   241  		count:  2,
   242  		expect: 2,
   243  	}, {
   244  		time:   0,
   245  		count:  5,
   246  		expect: 3,
   247  	}, {
   248  		time:   0,
   249  		count:  1,
   250  		expect: 0,
   251  	}},
   252  }, {
   253  	about:        "more than capacity",
   254  	fillInterval: 1 * time.Millisecond,
   255  	capacity:     10,
   256  	reqs: []takeAvailableReq{{
   257  		time:   0,
   258  		count:  10,
   259  		expect: 10,
   260  	}, {
   261  		time:   20 * time.Millisecond,
   262  		count:  15,
   263  		expect: 10,
   264  	}},
   265  }, {
   266  	about:        "within capacity",
   267  	fillInterval: 10 * time.Millisecond,
   268  	capacity:     5,
   269  	reqs: []takeAvailableReq{{
   270  		time:   0,
   271  		count:  5,
   272  		expect: 5,
   273  	}, {
   274  		time:   60 * time.Millisecond,
   275  		count:  5,
   276  		expect: 5,
   277  	}, {
   278  		time:   70 * time.Millisecond,
   279  		count:  1,
   280  		expect: 1,
   281  	}},
   282  }}
   283  
   284  func (rateLimitSuite) TestTakeAvailable(c *gc.C) {
   285  	for i, test := range takeAvailableTests {
   286  		tb := NewBucket(test.fillInterval, test.capacity)
   287  		for j, req := range test.reqs {
   288  			d := tb.takeAvailable(tb.startTime.Add(req.time), req.count)
   289  			if d != req.expect {
   290  				c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expect)
   291  			}
   292  		}
   293  	}
   294  }
   295  
   296  func (rateLimitSuite) TestPanics(c *gc.C) {
   297  	c.Assert(func() { NewBucket(0, 1) }, gc.PanicMatches, "token bucket fill interval is not > 0")
   298  	c.Assert(func() { NewBucket(-2, 1) }, gc.PanicMatches, "token bucket fill interval is not > 0")
   299  	c.Assert(func() { NewBucket(1, 0) }, gc.PanicMatches, "token bucket capacity is not > 0")
   300  	c.Assert(func() { NewBucket(1, -2) }, gc.PanicMatches, "token bucket capacity is not > 0")
   301  }
   302  
   303  func isCloseTo(x, y, tolerance float64) bool {
   304  	return math.Abs(x-y)/y < tolerance
   305  }
   306  
   307  func (rateLimitSuite) TestRate(c *gc.C) {
   308  	tb := NewBucket(1, 1)
   309  	if !isCloseTo(tb.Rate(), 1e9, 0.00001) {
   310  		c.Fatalf("got %v want 1e9", tb.Rate())
   311  	}
   312  	tb = NewBucket(2*time.Second, 1)
   313  	if !isCloseTo(tb.Rate(), 0.5, 0.00001) {
   314  		c.Fatalf("got %v want 0.5", tb.Rate())
   315  	}
   316  	tb = NewBucketWithQuantum(100*time.Millisecond, 1, 5)
   317  	if !isCloseTo(tb.Rate(), 50, 0.00001) {
   318  		c.Fatalf("got %v want 50", tb.Rate())
   319  	}
   320  }
   321  
   322  func checkRate(c *gc.C, rate float64) {
   323  	tb := NewBucketWithRate(rate, 1<<62)
   324  	if !isCloseTo(tb.Rate(), rate, rateMargin) {
   325  		c.Fatalf("got %g want %v", tb.Rate(), rate)
   326  	}
   327  	d, ok := tb.take(tb.startTime, 1<<62, infinityDuration)
   328  	c.Assert(ok, gc.Equals, true)
   329  	c.Assert(d, gc.Equals, time.Duration(0))
   330  
   331  	// Check that the actual rate is as expected by
   332  	// asking for a not-quite multiple of the bucket's
   333  	// quantum and checking that the wait time
   334  	// correct.
   335  	d, ok = tb.take(tb.startTime, tb.quantum*2-tb.quantum/2, infinityDuration)
   336  	c.Assert(ok, gc.Equals, true)
   337  	expectTime := 1e9 * float64(tb.quantum) * 2 / rate
   338  	if !isCloseTo(float64(d), expectTime, rateMargin) {
   339  		c.Fatalf("rate %g: got %g want %v", rate, float64(d), expectTime)
   340  	}
   341  }
   342  
   343  func (rateLimitSuite) NewBucketWithRate(c *gc.C) {
   344  	for rate := float64(1); rate < 1e6; rate += 7 {
   345  		checkRate(c, rate)
   346  	}
   347  	for _, rate := range []float64{
   348  		1024 * 1024 * 1024,
   349  		1e-5,
   350  		0.9e-5,
   351  		0.5,
   352  		0.9,
   353  		0.9e8,
   354  		3e12,
   355  		4e18,
   356  		float64(1<<63 - 1),
   357  	} {
   358  		checkRate(c, rate)
   359  		checkRate(c, rate/3)
   360  		checkRate(c, rate*1.3)
   361  	}
   362  }
   363  
   364  func TestAvailable(t *testing.T) {
   365  	for i, tt := range availTests {
   366  		tb := NewBucket(tt.fillInterval, tt.capacity)
   367  		if c := tb.takeAvailable(tb.startTime, tt.take); c != tt.take {
   368  			t.Fatalf("#%d: %s, take = %d, want = %d", i, tt.about, c, tt.take)
   369  		}
   370  		if c := tb.available(tb.startTime); c != tt.expectCountAfterTake {
   371  			t.Fatalf("#%d: %s, after take, available = %d, want = %d", i, tt.about, c, tt.expectCountAfterTake)
   372  		}
   373  		if c := tb.available(tb.startTime.Add(tt.sleep)); c != tt.expectCountAfterSleep {
   374  			t.Fatalf("#%d: %s, after some time it should fill in new tokens, available = %d, want = %d",
   375  				i, tt.about, c, tt.expectCountAfterSleep)
   376  		}
   377  	}
   378  
   379  }
   380  
   381  func TestNoBonusTokenAfterBucketIsFull(t *testing.T) {
   382  	tb := NewBucketWithQuantum(time.Second*1, 100, 20)
   383  	curAvail := tb.Available()
   384  	if curAvail != 100 {
   385  		t.Fatalf("initially: actual available = %d, expected = %d", curAvail, 100)
   386  	}
   387  
   388  	time.Sleep(time.Second * 5)
   389  
   390  	curAvail = tb.Available()
   391  	if curAvail != 100 {
   392  		t.Fatalf("after pause: actual available = %d, expected = %d", curAvail, 100)
   393  	}
   394  
   395  	cnt := tb.TakeAvailable(100)
   396  	if cnt != 100 {
   397  		t.Fatalf("taking: actual taken count = %d, expected = %d", cnt, 100)
   398  	}
   399  
   400  	curAvail = tb.Available()
   401  	if curAvail != 0 {
   402  		t.Fatalf("after taken: actual available = %d, expected = %d", curAvail, 0)
   403  	}
   404  }
   405  
   406  func BenchmarkWait(b *testing.B) {
   407  	tb := NewBucket(1, 16*1024)
   408  	for i := b.N - 1; i >= 0; i-- {
   409  		tb.Wait(1)
   410  	}
   411  }
   412  
   413  func BenchmarkNewBucket(b *testing.B) {
   414  	for i := b.N - 1; i >= 0; i-- {
   415  		NewBucketWithRate(4e18, 1<<62)
   416  	}
   417  }