...

Source file src/github.com/letsencrypt/boulder/ratelimits/gcra_test.go

Documentation: github.com/letsencrypt/boulder/ratelimits

     1  package ratelimits
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	"github.com/jmhodges/clock"
     8  	"github.com/letsencrypt/boulder/config"
     9  	"github.com/letsencrypt/boulder/test"
    10  )
    11  
    12  func Test_decide(t *testing.T) {
    13  	clk := clock.NewFake()
    14  	limit := precomputeLimit(
    15  		limit{Burst: 10, Count: 1, Period: config.Duration{Duration: time.Second}},
    16  	)
    17  
    18  	// Begin by using 1 of our 10 requests.
    19  	d := maybeSpend(clk, limit, clk.Now(), 1)
    20  	test.Assert(t, d.Allowed, "should be allowed")
    21  	test.AssertEquals(t, d.Remaining, int64(9))
    22  	test.AssertEquals(t, d.RetryIn, time.Duration(0))
    23  	test.AssertEquals(t, d.ResetIn, time.Second)
    24  
    25  	// Immediately use another 9 of our remaining requests.
    26  	d = maybeSpend(clk, limit, d.newTAT, 9)
    27  	test.Assert(t, d.Allowed, "should be allowed")
    28  	test.AssertEquals(t, d.Remaining, int64(0))
    29  	// We should have to wait 1 second before we can use another request but we
    30  	// used 9 so we should have to wait 9 seconds to make an identical request.
    31  	test.AssertEquals(t, d.RetryIn, time.Second*9)
    32  	test.AssertEquals(t, d.ResetIn, time.Second*10)
    33  
    34  	// Our new TAT should be 10 seconds (limit.Burst) in the future.
    35  	test.AssertEquals(t, d.newTAT, clk.Now().Add(time.Second*10))
    36  
    37  	// Let's try using just 1 more request without waiting.
    38  	d = maybeSpend(clk, limit, d.newTAT, 1)
    39  	test.Assert(t, !d.Allowed, "should not be allowed")
    40  	test.AssertEquals(t, d.Remaining, int64(0))
    41  	test.AssertEquals(t, d.RetryIn, time.Second)
    42  	test.AssertEquals(t, d.ResetIn, time.Second*10)
    43  
    44  	// Let's try being exactly as patient as we're told to be.
    45  	clk.Add(d.RetryIn)
    46  	d = maybeSpend(clk, limit, d.newTAT, 0)
    47  	test.AssertEquals(t, d.Remaining, int64(1))
    48  
    49  	// We are 1 second in the future, we should have 1 new request.
    50  	d = maybeSpend(clk, limit, d.newTAT, 1)
    51  	test.Assert(t, d.Allowed, "should be allowed")
    52  	test.AssertEquals(t, d.Remaining, int64(0))
    53  	test.AssertEquals(t, d.RetryIn, time.Second)
    54  	test.AssertEquals(t, d.ResetIn, time.Second*10)
    55  
    56  	// Let's try waiting (10 seconds) for our whole bucket to refill.
    57  	clk.Add(d.ResetIn)
    58  
    59  	// We should have 10 new requests. If we use 1 we should have 9 remaining.
    60  	d = maybeSpend(clk, limit, d.newTAT, 1)
    61  	test.Assert(t, d.Allowed, "should be allowed")
    62  	test.AssertEquals(t, d.Remaining, int64(9))
    63  	test.AssertEquals(t, d.RetryIn, time.Duration(0))
    64  	test.AssertEquals(t, d.ResetIn, time.Second)
    65  
    66  	// Wait just shy of how long we're told to wait for refilling.
    67  	clk.Add(d.ResetIn - time.Millisecond)
    68  
    69  	// We should still have 9 remaining because we're still 1ms shy of the
    70  	// refill time.
    71  	d = maybeSpend(clk, limit, d.newTAT, 0)
    72  	test.Assert(t, d.Allowed, "should be allowed")
    73  	test.AssertEquals(t, d.Remaining, int64(9))
    74  	test.AssertEquals(t, d.RetryIn, time.Duration(0))
    75  	test.AssertEquals(t, d.ResetIn, time.Millisecond)
    76  
    77  	// Spending 0 simply informed us that we still have 9 remaining, let's see
    78  	// what we have after waiting 20 hours.
    79  	clk.Add(20 * time.Hour)
    80  
    81  	// C'mon, big money, no whammies, no whammies, STOP!
    82  	d = maybeSpend(clk, limit, d.newTAT, 0)
    83  	test.Assert(t, d.Allowed, "should be allowed")
    84  	test.AssertEquals(t, d.Remaining, int64(10))
    85  	test.AssertEquals(t, d.RetryIn, time.Duration(0))
    86  	test.AssertEquals(t, d.ResetIn, time.Duration(0))
    87  
    88  	// Turns out that the most we can accrue is 10 (limit.Burst). Let's empty
    89  	// this bucket out so we can try something else.
    90  	d = maybeSpend(clk, limit, d.newTAT, 10)
    91  	test.Assert(t, d.Allowed, "should be allowed")
    92  	test.AssertEquals(t, d.Remaining, int64(0))
    93  	// We should have to wait 1 second before we can use another request but we
    94  	// used 10 so we should have to wait 10 seconds to make an identical
    95  	// request.
    96  	test.AssertEquals(t, d.RetryIn, time.Second*10)
    97  	test.AssertEquals(t, d.ResetIn, time.Second*10)
    98  
    99  	// If you spend 0 while you have 0 you should get 0.
   100  	d = maybeSpend(clk, limit, d.newTAT, 0)
   101  	test.Assert(t, d.Allowed, "should be allowed")
   102  	test.AssertEquals(t, d.Remaining, int64(0))
   103  	test.AssertEquals(t, d.RetryIn, time.Duration(0))
   104  	test.AssertEquals(t, d.ResetIn, time.Second*10)
   105  
   106  	// We don't play by the rules, we spend 1 when we have 0.
   107  	d = maybeSpend(clk, limit, d.newTAT, 1)
   108  	test.Assert(t, !d.Allowed, "should not be allowed")
   109  	test.AssertEquals(t, d.Remaining, int64(0))
   110  	test.AssertEquals(t, d.RetryIn, time.Second)
   111  	test.AssertEquals(t, d.ResetIn, time.Second*10)
   112  
   113  	// Okay, maybe we should play by the rules if we want to get anywhere.
   114  	clk.Add(d.RetryIn)
   115  
   116  	// Our patience pays off, we should have 1 new request. Let's use it.
   117  	d = maybeSpend(clk, limit, d.newTAT, 1)
   118  	test.Assert(t, d.Allowed, "should be allowed")
   119  	test.AssertEquals(t, d.Remaining, int64(0))
   120  	test.AssertEquals(t, d.RetryIn, time.Second)
   121  	test.AssertEquals(t, d.ResetIn, time.Second*10)
   122  
   123  	// Refill from empty to 5.
   124  	clk.Add(d.ResetIn / 2)
   125  
   126  	// Attempt to spend 7 when we only have 5. We should be denied but the
   127  	// decision should reflect a retry of 2 seconds, the time it would take to
   128  	// refill from 5 to 7.
   129  	d = maybeSpend(clk, limit, d.newTAT, 7)
   130  	test.Assert(t, !d.Allowed, "should not be allowed")
   131  	test.AssertEquals(t, d.Remaining, int64(5))
   132  	test.AssertEquals(t, d.RetryIn, time.Second*2)
   133  	test.AssertEquals(t, d.ResetIn, time.Second*5)
   134  }
   135  
   136  func Test_maybeRefund(t *testing.T) {
   137  	clk := clock.NewFake()
   138  	limit := precomputeLimit(
   139  		limit{Burst: 10, Count: 1, Period: config.Duration{Duration: time.Second}},
   140  	)
   141  
   142  	// Begin by using 1 of our 10 requests.
   143  	d := maybeSpend(clk, limit, clk.Now(), 1)
   144  	test.Assert(t, d.Allowed, "should be allowed")
   145  	test.AssertEquals(t, d.Remaining, int64(9))
   146  	test.AssertEquals(t, d.RetryIn, time.Duration(0))
   147  	test.AssertEquals(t, d.ResetIn, time.Second)
   148  
   149  	// Refund back to 10.
   150  	d = maybeRefund(clk, limit, d.newTAT, 1)
   151  	test.AssertEquals(t, d.Remaining, int64(10))
   152  	test.AssertEquals(t, d.RetryIn, time.Duration(0))
   153  	test.AssertEquals(t, d.ResetIn, time.Duration(0))
   154  
   155  	// Spend 1 more of our 10 requests.
   156  	d = maybeSpend(clk, limit, d.newTAT, 1)
   157  	test.Assert(t, d.Allowed, "should be allowed")
   158  	test.AssertEquals(t, d.Remaining, int64(9))
   159  	test.AssertEquals(t, d.RetryIn, time.Duration(0))
   160  	test.AssertEquals(t, d.ResetIn, time.Second)
   161  
   162  	// Wait for our bucket to refill.
   163  	clk.Add(d.ResetIn)
   164  
   165  	// Attempt to refund from 10 to 11.
   166  	d = maybeRefund(clk, limit, d.newTAT, 1)
   167  	test.Assert(t, !d.Allowed, "should not be allowed")
   168  	test.AssertEquals(t, d.Remaining, int64(10))
   169  	test.AssertEquals(t, d.RetryIn, time.Duration(0))
   170  	test.AssertEquals(t, d.ResetIn, time.Duration(0))
   171  
   172  	// Spend 10 all 10 of our requests.
   173  	d = maybeSpend(clk, limit, d.newTAT, 10)
   174  	test.Assert(t, d.Allowed, "should be allowed")
   175  	test.AssertEquals(t, d.Remaining, int64(0))
   176  	// We should have to wait 1 second before we can use another request but we
   177  	// used 10 so we should have to wait 10 seconds to make an identical
   178  	// request.
   179  	test.AssertEquals(t, d.RetryIn, time.Second*10)
   180  	test.AssertEquals(t, d.ResetIn, time.Second*10)
   181  
   182  	// Attempt a refund of 10.
   183  	d = maybeRefund(clk, limit, d.newTAT, 10)
   184  	test.AssertEquals(t, d.Remaining, int64(10))
   185  	test.AssertEquals(t, d.RetryIn, time.Duration(0))
   186  	test.AssertEquals(t, d.ResetIn, time.Duration(0))
   187  
   188  	// Wait 11 seconds to catching up to TAT.
   189  	clk.Add(11 * time.Second)
   190  
   191  	// Attempt to refund to 11, then ensure it's still 10.
   192  	d = maybeRefund(clk, limit, d.newTAT, 1)
   193  	test.Assert(t, !d.Allowed, "should be allowed")
   194  	test.AssertEquals(t, d.Remaining, int64(10))
   195  	test.AssertEquals(t, d.RetryIn, time.Duration(0))
   196  	test.AssertEquals(t, d.ResetIn, time.Duration(0))
   197  
   198  	// Spend 5 of our 10 requests, then refund 1.
   199  	d = maybeSpend(clk, limit, d.newTAT, 5)
   200  	d = maybeRefund(clk, limit, d.newTAT, 1)
   201  	test.Assert(t, d.Allowed, "should be allowed")
   202  	test.AssertEquals(t, d.Remaining, int64(6))
   203  	test.AssertEquals(t, d.RetryIn, time.Duration(0))
   204  
   205  	// Wait, a 2.5 seconds to refill to 8.5 requests.
   206  	clk.Add(time.Millisecond * 2500)
   207  
   208  	// Ensure we have 8.5 requests.
   209  	d = maybeSpend(clk, limit, d.newTAT, 0)
   210  	test.Assert(t, d.Allowed, "should be allowed")
   211  	test.AssertEquals(t, d.Remaining, int64(8))
   212  	test.AssertEquals(t, d.RetryIn, time.Duration(0))
   213  	// Check that ResetIn represents the fractional earned request.
   214  	test.AssertEquals(t, d.ResetIn, time.Millisecond*1500)
   215  
   216  	// Refund 2 requests, we should only have 10, not 10.5.
   217  	d = maybeRefund(clk, limit, d.newTAT, 2)
   218  	test.AssertEquals(t, d.Remaining, int64(10))
   219  	test.AssertEquals(t, d.RetryIn, time.Duration(0))
   220  	test.AssertEquals(t, d.ResetIn, time.Duration(0))
   221  }
   222  

View as plain text