From f19363ec6250feef6c06508910bc79613f026b6f Mon Sep 17 00:00:00 2001 From: ZverGuy Date: Wed, 4 Nov 2026 05:31:06 +0700 Subject: [PATCH] fix(observability-proxy): keep Retry-After positive at tiny rates The wait until the next token was converted from float to Duration without a range check, so a rate limit below about 1e-10 requests per second produced a negative Retry-After on amd64. The wait now saturates at the largest Duration. The earlier 429 change said range, step, bucket and limit violations keep 422; on the logs root they are clamped or answer 400, and nothing answers 422. Assisted-by: LLM Signed-off-by: ZverGuy --- internal/observabilityproxy/limits.go | 7 ++++++- internal/observabilityproxy/limits_test.go | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) 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) + } +}