Skip to content
This repository was archived by the owner on Apr 19, 2024. It is now read-only.

Commit 8ab97c3

Browse files
committed
Added Behavior RESET_REMAINING to reset any hits recorded in the cache for the specified rate limit
* Behavior is now a flag, this should be a backward compatible change for anyone using GLOBAL or NO_BATCHING but will break anyone using DURATION_IS_GREGORIAN. Use `HasBehavior()` function to check for behavior flags.
1 parent e6ca0e7 commit 8ab97c3

File tree

6 files changed

+172
-64
lines changed

6 files changed

+172
-64
lines changed

CHANGELOG

+6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
### Added
99
* Allow cache users to invalidate a ratelimit after a specific time
1010
* Changing limit and duration before expire should now work correctly
11+
* Added Behavior RESET_REMAINING to reset any hits recorded in the cache for the
12+
specified rate limit
1113

1214
## Changes
1315
* TokenBucketItem is now provided when `OnChange()` is called instead of `RateLimitResp`
@@ -17,6 +19,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1719
* Always include reset_time in leaky bucket responses
1820
* Fixed subtle bug during shutdown where PeerClient passed into goroutine could
1921
be out of scope/changed when routine runs
22+
* Behavior is now a flag, this should be a backward compatible change for
23+
anyone using GLOBAL or NO_BATCHING but will break anyone using
24+
DURATION_IS_GREGORIAN. Use `HasBehavior()` function to check for behavior
25+
flags.
2026

2127
## [0.7.1] - 2019-12-10
2228
### Added

algorithms.go

+17-1
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,24 @@ func tokenBucket(s Store, c Cache, r *RateLimitReq) (resp *RateLimitResp, err er
3333
}
3434

3535
if ok {
36+
if HasBehavior(r.Behavior, Behavior_RESET_REMAINING) {
37+
c.Remove(r.HashKey())
38+
if s != nil {
39+
s.Remove(r.HashKey())
40+
}
41+
return &RateLimitResp{
42+
Status: Status_UNDER_LIMIT,
43+
Limit: r.Limit,
44+
Remaining: r.Limit,
45+
ResetTime: 0,
46+
}, nil
47+
}
48+
3649
// The following semantic allows for requests of more than the limit to be rejected, but subsequent
3750
// requests within the same duration that are under the limit to succeed. IE: client attempts to
3851
// send 1000 emails but 100 is their limit. The request is rejected as over the limit, but since we
3952
// don't store OVER_LIMIT in the cache the client can retry within the same rate limit duration with
4053
// 100 emails and the request will succeed.
41-
4254
t, ok := item.Value.(*TokenBucketItem)
4355
if !ok {
4456
// Client switched algorithms; perhaps due to a migration?
@@ -190,6 +202,10 @@ func leakyBucket(s Store, c Cache, r *RateLimitReq) (resp *RateLimitResp, err er
190202
return leakyBucket(s, c, r)
191203
}
192204

205+
if HasBehavior(r.Behavior, Behavior_RESET_REMAINING) {
206+
b.Remaining = r.Limit
207+
}
208+
193209
// Update limit and duration if they changed
194210
b.Limit = r.Limit
195211
b.Duration = r.Duration

functional_test.go

+72
Original file line numberDiff line numberDiff line change
@@ -422,4 +422,76 @@ func TestChangeLimit(t *testing.T) {
422422
}
423423
}
424424

425+
func TestResetRemaining(t *testing.T) {
426+
client, errs := guber.DialV1Server(cluster.GetPeer())
427+
require.Nil(t, errs)
428+
429+
tests := []struct {
430+
Remaining int64
431+
Algorithm guber.Algorithm
432+
Behavior guber.Behavior
433+
Status guber.Status
434+
Name string
435+
Limit int64
436+
}{
437+
{
438+
Name: "Should subtract 1 from remaining",
439+
Algorithm: guber.Algorithm_TOKEN_BUCKET,
440+
Status: guber.Status_UNDER_LIMIT,
441+
Behavior: guber.Behavior_BATCHING,
442+
Remaining: 99,
443+
Limit: 100,
444+
},
445+
{
446+
Name: "Should subtract 2 from remaining",
447+
Algorithm: guber.Algorithm_TOKEN_BUCKET,
448+
Status: guber.Status_UNDER_LIMIT,
449+
Behavior: guber.Behavior_BATCHING,
450+
Remaining: 98,
451+
Limit: 100,
452+
},
453+
{
454+
Name: "Should reset the remaining",
455+
Algorithm: guber.Algorithm_TOKEN_BUCKET,
456+
Status: guber.Status_UNDER_LIMIT,
457+
Behavior: guber.Behavior_RESET_REMAINING,
458+
Remaining: 100,
459+
Limit: 100,
460+
},
461+
{
462+
Name: "Should subtract 1 from remaining after reset",
463+
Algorithm: guber.Algorithm_TOKEN_BUCKET,
464+
Status: guber.Status_UNDER_LIMIT,
465+
Behavior: guber.Behavior_BATCHING,
466+
Remaining: 99,
467+
Limit: 100,
468+
},
469+
}
470+
471+
for _, tt := range tests {
472+
t.Run(tt.Name, func(t *testing.T) {
473+
resp, err := client.GetRateLimits(context.Background(), &guber.GetRateLimitsReq{
474+
Requests: []*guber.RateLimitReq{
475+
{
476+
Name: "test_reset_remaining",
477+
UniqueKey: "account:1234",
478+
Algorithm: tt.Algorithm,
479+
Duration: guber.Millisecond * 100,
480+
Behavior: tt.Behavior,
481+
Limit: tt.Limit,
482+
Hits: 1,
483+
},
484+
},
485+
})
486+
require.Nil(t, err)
487+
488+
rl := resp.Responses[0]
489+
490+
assert.Equal(t, tt.Status, rl.Status)
491+
assert.Equal(t, tt.Remaining, rl.Remaining)
492+
assert.Equal(t, tt.Limit, rl.Limit)
493+
})
494+
}
495+
}
496+
425497
// TODO: Add a test for sending no rate limits RateLimitReqList.RateLimits = nil

gubernator.pb.go

+63-54
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

proto/gubernator.proto

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ enum Behavior {
120120
// ignore any `Hit` values provided in the current request. The effect this has is dependent on
121121
// algorithm chosen. For instance, if used with `TOKEN_BUCKET` it will immediately expire the
122122
// cache value. For `LEAKY_BUCKET` it sets the `Remaining` to `Limit`.
123-
RESET_HITS = 8;
123+
RESET_REMAINING = 8;
124124

125125
// TODO: Add support for LOCAL. Which would force the rate limit to be handled by the local instance
126126
}

0 commit comments

Comments
 (0)