diff --git a/internal/observabilityproxy/limits.go b/internal/observabilityproxy/limits.go index f0392f25b..eaebe3104 100644 --- a/internal/observabilityproxy/limits.go +++ b/internal/observabilityproxy/limits.go @@ -17,6 +17,7 @@ limitations under the License. package observabilityproxy import ( + "math" "sync" "time" ) @@ -79,7 +80,11 @@ func (l *limiter) allowRate(user string) (time.Duration, bool) { b.tokens = min(burst, b.tokens+now.Sub(b.last).Seconds()*l.cfg.RatePerUser) b.last = now if b.tokens < 1 { - return time.Duration((1 - b.tokens) / l.cfg.RatePerUser * float64(time.Second)), false + wait, ok := scaleDuration((1-b.tokens)/l.cfg.RatePerUser, time.Second) + if !ok { + wait = math.MaxInt64 + } + return wait, false } b.tokens-- return 0, true diff --git a/internal/observabilityproxy/limits_test.go b/internal/observabilityproxy/limits_test.go index 5c3af772d..f7ff45d3a 100644 --- a/internal/observabilityproxy/limits_test.go +++ b/internal/observabilityproxy/limits_test.go @@ -108,3 +108,11 @@ func allowed(l *limiter, user string) bool { _, ok := l.allowRate(user) return ok } + +func TestLimiterRateWaitAtTinyRate(t *testing.T) { + l := newLimiter(LimiterConfig{RatePerUser: 1e-12, RateBurst: 1}) + allowed(l, "alice") + if wait, ok := l.allowRate("alice"); ok || wait <= 0 { + t.Fatalf("empty bucket at a tiny rate: admitted %v, wait %s, want refused with a positive wait", ok, wait) + } +}