Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add within limit counter #232

Merged
merged 3 commits into from
Mar 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions examples/prom-statsd-exporter/conf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ mappings: # Requires statsd exporter >= v0.6.0 since it uses the "drop" action.
labels:
domain: "$1"
key1: "$2"
- match:
"ratelimit.service.rate_limit.*.*.within_limit"
name: "ratelimit_service_rate_limit_within_limit"
timer_type: "histogram"
labels:
domain: "$1"
key1: "$2"

- match:
"ratelimit.service.rate_limit.*.*.*.near_limit"
Expand All @@ -45,6 +52,14 @@ mappings: # Requires statsd exporter >= v0.6.0 since it uses the "drop" action.
domain: "$1"
key1: "$2"
key2: "$3"
- match:
"ratelimit.service.rate_limit.*.*.*.within_limit"
name: "ratelimit_service_rate_limit_within_limit"
timer_type: "histogram"
labels:
domain: "$1"
key1: "$2"
key2: "$3"

- match: "ratelimit.service.call.should_rate_limit.*"
name: "ratelimit_service_should_rate_limit_error"
Expand Down
1 change: 1 addition & 0 deletions src/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type RateLimitStats struct {
OverLimit stats.Counter
NearLimit stats.Counter
OverLimitWithLocalCache stats.Counter
WithinLimit stats.Counter
}

// Wrapper for an individual rate limit config entry which includes the defined limit and stats.
Expand Down
1 change: 1 addition & 0 deletions src/config/config_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func newRateLimitStats(statsScope stats.Scope, key string) RateLimitStats {
ret.OverLimit = statsScope.NewCounter(key + ".over_limit")
ret.NearLimit = statsScope.NewCounter(key + ".near_limit")
ret.OverLimitWithLocalCache = statsScope.NewCounter(key + ".over_limit_with_local_cache")
ret.WithinLimit = statsScope.NewCounter(key + ".within_limit")
return ret
}

Expand Down
1 change: 1 addition & 0 deletions src/limiter/base_limiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ func (this *BaseRateLimiter) GetResponseDescriptorStatus(key string, limitInfo *

// The limit is OK but we additionally want to know if we are near the limit.
checkNearLimitThreshold(limitInfo, hitsAddend)
limitInfo.limit.Stats.WithinLimit.Add(uint64(hitsAddend))
}
return responseDescriptorStatus
}
Expand Down
19 changes: 19 additions & 0 deletions test/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,13 @@ func TestBasicConfig(t *testing.T) {
rl.Stats.TotalHits.Inc()
rl.Stats.OverLimit.Inc()
rl.Stats.NearLimit.Inc()
rl.Stats.WithinLimit.Inc()
assert.EqualValues(5, rl.Limit.RequestsPerUnit)
assert.Equal(pb.RateLimitResponse_RateLimit_SECOND, rl.Limit.Unit)
assert.EqualValues(1, stats.NewCounter("test-domain.key1_value1.subkey1.total_hits").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key1_value1.subkey1.over_limit").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key1_value1.subkey1.near_limit").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key1_value1.subkey1.within_limit").Value())

rl = rlConfig.GetLimit(
nil, "test-domain",
Expand All @@ -79,6 +81,7 @@ func TestBasicConfig(t *testing.T) {
rl.Stats.TotalHits.Inc()
rl.Stats.OverLimit.Inc()
rl.Stats.NearLimit.Inc()
rl.Stats.WithinLimit.Inc()
assert.EqualValues(10, rl.Limit.RequestsPerUnit)
assert.Equal(pb.RateLimitResponse_RateLimit_SECOND, rl.Limit.Unit)
assert.EqualValues(
Expand All @@ -87,6 +90,8 @@ func TestBasicConfig(t *testing.T) {
1, stats.NewCounter("test-domain.key1_value1.subkey1_subvalue1.over_limit").Value())
assert.EqualValues(
1, stats.NewCounter("test-domain.key1_value1.subkey1_subvalue1.near_limit").Value())
assert.EqualValues(
1, stats.NewCounter("test-domain.key1_value1.subkey1_subvalue1.within_limit").Value())

rl = rlConfig.GetLimit(
nil, "test-domain",
Expand All @@ -96,11 +101,13 @@ func TestBasicConfig(t *testing.T) {
rl.Stats.TotalHits.Inc()
rl.Stats.OverLimit.Inc()
rl.Stats.NearLimit.Inc()
rl.Stats.WithinLimit.Inc()
assert.EqualValues(20, rl.Limit.RequestsPerUnit)
assert.Equal(pb.RateLimitResponse_RateLimit_MINUTE, rl.Limit.Unit)
assert.EqualValues(1, stats.NewCounter("test-domain.key2.total_hits").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key2.over_limit").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key2.near_limit").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key2.within_limit").Value())

rl = rlConfig.GetLimit(
nil, "test-domain",
Expand All @@ -110,11 +117,13 @@ func TestBasicConfig(t *testing.T) {
rl.Stats.TotalHits.Inc()
rl.Stats.OverLimit.Inc()
rl.Stats.NearLimit.Inc()
rl.Stats.WithinLimit.Inc()
assert.EqualValues(30, rl.Limit.RequestsPerUnit)
assert.Equal(pb.RateLimitResponse_RateLimit_MINUTE, rl.Limit.Unit)
assert.EqualValues(1, stats.NewCounter("test-domain.key2_value2.total_hits").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key2_value2.over_limit").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key2_value2.near_limit").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key2_value2.within_limit").Value())

rl = rlConfig.GetLimit(
nil, "test-domain",
Expand All @@ -131,11 +140,13 @@ func TestBasicConfig(t *testing.T) {
rl.Stats.TotalHits.Inc()
rl.Stats.OverLimit.Inc()
rl.Stats.NearLimit.Inc()
rl.Stats.WithinLimit.Inc()
assert.EqualValues(1, rl.Limit.RequestsPerUnit)
assert.Equal(pb.RateLimitResponse_RateLimit_HOUR, rl.Limit.Unit)
assert.EqualValues(1, stats.NewCounter("test-domain.key3.total_hits").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key3.over_limit").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key3.near_limit").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key3.within_limit").Value())

rl = rlConfig.GetLimit(
nil, "test-domain",
Expand All @@ -145,11 +156,13 @@ func TestBasicConfig(t *testing.T) {
rl.Stats.TotalHits.Inc()
rl.Stats.OverLimit.Inc()
rl.Stats.NearLimit.Inc()
rl.Stats.WithinLimit.Inc()
assert.EqualValues(1, rl.Limit.RequestsPerUnit)
assert.Equal(pb.RateLimitResponse_RateLimit_DAY, rl.Limit.Unit)
assert.EqualValues(1, stats.NewCounter("test-domain.key4.total_hits").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key4.over_limit").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key4.near_limit").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key4.within_limit").Value())
}

func TestConfigLimitOverride(t *testing.T) {
Expand Down Expand Up @@ -179,9 +192,11 @@ func TestConfigLimitOverride(t *testing.T) {
rl.Stats.TotalHits.Inc()
rl.Stats.OverLimit.Inc()
rl.Stats.NearLimit.Inc()
rl.Stats.WithinLimit.Inc()
assert.EqualValues(1, stats.NewCounter("test-domain.key1_value1.subkey1_something.total_hits").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key1_value1.subkey1_something.over_limit").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key1_value1.subkey1_something.near_limit").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key1_value1.subkey1_something.within_limit").Value())

// Change in override value doesn't erase stats
rl = rlConfig.GetLimit(
Expand All @@ -196,13 +211,15 @@ func TestConfigLimitOverride(t *testing.T) {
rl.Stats.TotalHits.Inc()
rl.Stats.OverLimit.Inc()
rl.Stats.NearLimit.Inc()
rl.Stats.WithinLimit.Inc()
common.AssertProtoEqual(assert, &pb.RateLimitResponse_RateLimit{
RequestsPerUnit: 42,
Unit: pb.RateLimitResponse_RateLimit_HOUR,
}, rl.Limit)
assert.EqualValues(2, stats.NewCounter("test-domain.key1_value1.subkey1_something.total_hits").Value())
assert.EqualValues(2, stats.NewCounter("test-domain.key1_value1.subkey1_something.over_limit").Value())
assert.EqualValues(2, stats.NewCounter("test-domain.key1_value1.subkey1_something.near_limit").Value())
assert.EqualValues(2, stats.NewCounter("test-domain.key1_value1.subkey1_something.within_limit").Value())

// Different value creates a different counter
rl = rlConfig.GetLimit(
Expand All @@ -221,9 +238,11 @@ func TestConfigLimitOverride(t *testing.T) {
rl.Stats.TotalHits.Inc()
rl.Stats.OverLimit.Inc()
rl.Stats.NearLimit.Inc()
rl.Stats.WithinLimit.Inc()
assert.EqualValues(1, stats.NewCounter("test-domain.key1_value1.subkey1_something_else.total_hits").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key1_value1.subkey1_something_else.over_limit").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key1_value1.subkey1_something_else.near_limit").Value())
assert.EqualValues(1, stats.NewCounter("test-domain.key1_value1.subkey1_something_else.within_limit").Value())
}

func expectConfigPanic(t *testing.T, call func(), expectedError string) {
Expand Down
1 change: 1 addition & 0 deletions test/limiter/base_limiter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,5 @@ func TestGetResponseStatusBelowLimit(t *testing.T) {
assert.Equal(uint32(4), responseStatus.GetLimitRemaining())
assert.Equal(uint64(0), limits[0].Stats.NearLimit.Value())
assert.Equal(limits[0].Limit, responseStatus.GetCurrentLimit())
assert.Equal(uint64(1), limits[0].Stats.WithinLimit.Value())
}
22 changes: 22 additions & 0 deletions test/memcached/cache_impl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func TestMemcached(t *testing.T) {
assert.Equal(uint64(1), limits[0].Stats.TotalHits.Value())
assert.Equal(uint64(0), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(1), limits[0].Stats.WithinLimit.Value())

timeSource.EXPECT().UnixNow().Return(int64(1234)).MaxTimes(3)
client.EXPECT().GetMulti([]string{"domain_key2_value2_subkey2_subvalue2_1200"}).Return(
Expand All @@ -74,6 +75,7 @@ func TestMemcached(t *testing.T) {
assert.Equal(uint64(1), limits[1].Stats.TotalHits.Value())
assert.Equal(uint64(1), limits[1].Stats.OverLimit.Value())
assert.Equal(uint64(0), limits[1].Stats.NearLimit.Value())
assert.Equal(uint64(0), limits[1].Stats.WithinLimit.Value())

timeSource.EXPECT().UnixNow().Return(int64(1000000)).MaxTimes(5)
client.EXPECT().GetMulti([]string{
Expand Down Expand Up @@ -105,9 +107,11 @@ func TestMemcached(t *testing.T) {
assert.Equal(uint64(1), limits[0].Stats.TotalHits.Value())
assert.Equal(uint64(1), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.WithinLimit.Value())
assert.Equal(uint64(1), limits[0].Stats.TotalHits.Value())
assert.Equal(uint64(1), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.WithinLimit.Value())

cache.Flush()
}
Expand Down Expand Up @@ -137,6 +141,7 @@ func TestMemcachedGetError(t *testing.T) {
assert.Equal(uint64(1), limits[0].Stats.TotalHits.Value())
assert.Equal(uint64(0), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(1), limits[0].Stats.WithinLimit.Value())

// No error, but the key is missing
timeSource.EXPECT().UnixNow().Return(int64(1234)).MaxTimes(3)
Expand All @@ -154,6 +159,7 @@ func TestMemcachedGetError(t *testing.T) {
assert.Equal(uint64(1), limits[0].Stats.TotalHits.Value())
assert.Equal(uint64(0), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(1), limits[0].Stats.WithinLimit.Value())

cache.Flush()
}
Expand Down Expand Up @@ -227,6 +233,7 @@ func TestOverLimitWithLocalCache(t *testing.T) {
assert.Equal(uint64(0), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.OverLimitWithLocalCache.Value())
assert.Equal(uint64(0), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(1), limits[0].Stats.WithinLimit.Value())

// Check the local cache stats.
testLocalCacheStats(localCacheStats, statsStore, sink, 0, 1, 1, 0, 0)
Expand All @@ -246,6 +253,7 @@ func TestOverLimitWithLocalCache(t *testing.T) {
assert.Equal(uint64(0), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.OverLimitWithLocalCache.Value())
assert.Equal(uint64(1), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(2), limits[0].Stats.WithinLimit.Value())

// Check the local cache stats.
testLocalCacheStats(localCacheStats, statsStore, sink, 0, 2, 2, 0, 0)
Expand All @@ -265,6 +273,7 @@ func TestOverLimitWithLocalCache(t *testing.T) {
assert.Equal(uint64(1), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.OverLimitWithLocalCache.Value())
assert.Equal(uint64(1), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(2), limits[0].Stats.WithinLimit.Value())

// Check the local cache stats.
testLocalCacheStats(localCacheStats, statsStore, sink, 0, 2, 3, 0, 1)
Expand All @@ -281,6 +290,7 @@ func TestOverLimitWithLocalCache(t *testing.T) {
assert.Equal(uint64(2), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(1), limits[0].Stats.OverLimitWithLocalCache.Value())
assert.Equal(uint64(1), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(2), limits[0].Stats.WithinLimit.Value())

// Check the local cache stats.
testLocalCacheStats(localCacheStats, statsStore, sink, 1, 3, 4, 0, 1)
Expand Down Expand Up @@ -317,6 +327,7 @@ func TestNearLimit(t *testing.T) {
assert.Equal(uint64(1), limits[0].Stats.TotalHits.Value())
assert.Equal(uint64(0), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(1), limits[0].Stats.WithinLimit.Value())

// Test Near Limit Stats. At Near Limit Ratio, still OK
timeSource.EXPECT().UnixNow().Return(int64(1000000)).MaxTimes(3)
Expand All @@ -332,6 +343,7 @@ func TestNearLimit(t *testing.T) {
assert.Equal(uint64(2), limits[0].Stats.TotalHits.Value())
assert.Equal(uint64(0), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(1), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(2), limits[0].Stats.WithinLimit.Value())

// Test Near Limit Stats. We went OVER_LIMIT, but the near_limit counter only increases
// when we are near limit, not after we have passed the limit.
Expand All @@ -348,6 +360,7 @@ func TestNearLimit(t *testing.T) {
assert.Equal(uint64(3), limits[0].Stats.TotalHits.Value())
assert.Equal(uint64(1), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(1), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(2), limits[0].Stats.WithinLimit.Value())

// Now test hitsAddend that is greater than 1
// All of it under limit, under near limit
Expand All @@ -366,6 +379,7 @@ func TestNearLimit(t *testing.T) {
assert.Equal(uint64(3), limits[0].Stats.TotalHits.Value())
assert.Equal(uint64(0), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(3), limits[0].Stats.WithinLimit.Value())

// All of it under limit, some over near limit
timeSource.EXPECT().UnixNow().Return(int64(1234)).MaxTimes(3)
Expand All @@ -383,6 +397,7 @@ func TestNearLimit(t *testing.T) {
assert.Equal(uint64(2), limits[0].Stats.TotalHits.Value())
assert.Equal(uint64(0), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(1), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(2), limits[0].Stats.WithinLimit.Value())

// All of it under limit, all of it over near limit
timeSource.EXPECT().UnixNow().Return(int64(1234)).MaxTimes(3)
Expand All @@ -400,6 +415,7 @@ func TestNearLimit(t *testing.T) {
assert.Equal(uint64(3), limits[0].Stats.TotalHits.Value())
assert.Equal(uint64(0), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(3), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(3), limits[0].Stats.WithinLimit.Value())

// Some of it over limit, all of it over near limit
timeSource.EXPECT().UnixNow().Return(int64(1234)).MaxTimes(3)
Expand All @@ -417,6 +433,7 @@ func TestNearLimit(t *testing.T) {
assert.Equal(uint64(3), limits[0].Stats.TotalHits.Value())
assert.Equal(uint64(2), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(1), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.WithinLimit.Value())

// Some of it in all three places
timeSource.EXPECT().UnixNow().Return(int64(1234)).MaxTimes(3)
Expand All @@ -434,6 +451,7 @@ func TestNearLimit(t *testing.T) {
assert.Equal(uint64(7), limits[0].Stats.TotalHits.Value())
assert.Equal(uint64(2), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(4), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.WithinLimit.Value())

// all of it over limit
timeSource.EXPECT().UnixNow().Return(int64(1234)).MaxTimes(3)
Expand All @@ -451,6 +469,7 @@ func TestNearLimit(t *testing.T) {
assert.Equal(uint64(3), limits[0].Stats.TotalHits.Value())
assert.Equal(uint64(3), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.WithinLimit.Value())

cache.Flush()
}
Expand Down Expand Up @@ -493,6 +512,7 @@ func TestMemcacheWithJitter(t *testing.T) {
assert.Equal(uint64(1), limits[0].Stats.TotalHits.Value())
assert.Equal(uint64(0), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(1), limits[0].Stats.WithinLimit.Value())

cache.Flush()
}
Expand Down Expand Up @@ -534,6 +554,7 @@ func TestMemcacheAdd(t *testing.T) {
assert.Equal(uint64(1), limits[0].Stats.TotalHits.Value())
assert.Equal(uint64(0), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(1), limits[0].Stats.WithinLimit.Value())

// A rate limit with 1-minute window
timeSource.EXPECT().UnixNow().Return(int64(1234)).MaxTimes(3)
Expand All @@ -557,6 +578,7 @@ func TestMemcacheAdd(t *testing.T) {
assert.Equal(uint64(1), limits[0].Stats.TotalHits.Value())
assert.Equal(uint64(0), limits[0].Stats.OverLimit.Value())
assert.Equal(uint64(0), limits[0].Stats.NearLimit.Value())
assert.Equal(uint64(1), limits[0].Stats.WithinLimit.Value())

cache.Flush()
}
Expand Down
Loading