Skip to content

Commit a0ab828

Browse files
authored
Handle spiky load pattern by limiting based on static rate (#862)
1 parent 8e50e97 commit a0ab828

File tree

3 files changed

+22
-13
lines changed

3 files changed

+22
-13
lines changed

processor/ratelimitprocessor/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ type DynamicRateLimiting struct {
8383
// WindowConfigurator is the component ID of the extension to dynamically
8484
// determine the window multiplier. The extension is expected to implement
8585
// the `WindowConfigurator` interface. The window configurator is used in
86-
// the hot path so it should respond fast.If the configurator returns a
86+
// the hot path so it should respond fast. If the configurator returns a
8787
// negative multiplier then the default multiplier will be used.
8888
WindowConfigurator component.ID `mapstructure:"window_configurator"`
8989
}

processor/ratelimitprocessor/gubernator.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -411,17 +411,24 @@ func (r *gubernatorRateLimiter) getDynamicLimit(ctx context.Context,
411411
// Only record the incoming hits when the current rate is within the allowed
412412
// range, otherwise, do not record the hits and return the calculated rate.
413413
// MaxAllowed sets a ceiling on the rate with the window duration. If the
414-
// previous period had hits and the window multiplier is suggesting lowering
415-
// the ingestion rate then the MaxAllowed will be allowed to go below the
416-
// static rate (to as low as `1`). As soon as the window multiplier suggests
417-
// increasing the ingestion rate, the MaxAllowed will jump to a minimum of
418-
// static rate.
414+
// the window multiplier is suggesting lowering the ingestion rate then the
415+
// MaxAllowed will be allowed to go below the static rate (to as low as `1`).
416+
// As soon as the window multiplier suggests increasing the ingestion rate,
417+
// the MaxAllowed will jump to a minimum of static rate.
419418
//
420419
// NOTE(marclop) We may want to add a follow-up static ceiling to avoid
421420
// unbounded growth.
422421
var maxAllowed float64
423-
if previous > 0 && windowMultiplier <= 1 {
424-
maxAllowed = max(1, previous*windowMultiplier)
422+
if windowMultiplier <= 1 {
423+
// multiplier indicates scale down. If we have hits in the previous
424+
// multiplier then scale that down, otherwise scale the static rate
425+
// as per the multiplier instead of returning the default static rate.
426+
// This should protect against deployments with spiky load patterns.
427+
if previous > 0 {
428+
maxAllowed = max(1, previous*windowMultiplier)
429+
} else {
430+
maxAllowed = max(1, staticRate*windowMultiplier)
431+
}
425432
} else {
426433
maxAllowed = max(staticRate, previous*windowMultiplier)
427434
}

processor/ratelimitprocessor/gubernator_test.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -850,13 +850,15 @@ func TestGubernatorRateLimiter_WindowConfigurator(t *testing.T) {
850850
actual, expectedLimit, expectedLimit-actual, gubernator.Status_UNDER_LIMIT,
851851
)
852852

853-
// wait for a few periods to assert rate that the rate goes to be equal to the
854-
// static rate as data is NOT sent in the first waiting window causing the
855-
// previous rate to go to zero in the next window which we will assert.
853+
// wait for a few periods to assert that the rate goes to be calculated
854+
// based on static rate and the multiplier as data is NOT sent in the previous
855+
// waiting window causing the previous rate to go to zero. As the window
856+
// multiplier is still suggesting lowering the rate, we apply the multiplier
857+
// to the static limit.
856858
waitUntilNextPeriod(windowDuration)
857859
waitUntilNextPeriod(windowDuration)
858-
expectedLimit = int64(staticRatePerSec)
859-
actual = int64(100) * int64(windowDuration) / int64(time.Second) // send 100 hits
860+
expectedLimit = int64(float64(staticRatePerSec) * 0.01)
861+
actual = int64(5) * int64(windowDuration) / int64(time.Second)
860862
require.NoError(t, rateLimiter.RateLimit(ctx, int(actual)))
861863
assertRequestRateLimitEvent(
862864
t, uniqueKey,

0 commit comments

Comments
 (0)