From 81b70a964cff02d23aa6bdcd1b79c14843d996dd Mon Sep 17 00:00:00 2001 From: Eric Lin Date: Fri, 4 Jun 2021 21:55:54 +0000 Subject: [PATCH] rate: Avoid precision loss with edge cases When burst is 1, there would be edge cases we get tokens of 0.9999999999997222 due to float64 multiplication/division rounding out value. So avoid those calculation when 'last' is old enough Fixes golang/go#46579 --- rate/rate.go | 4 ++-- rate/rate_test.go | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/rate/rate.go b/rate/rate.go index a98fe77..649cc72 100644 --- a/rate/rate.go +++ b/rate/rate.go @@ -370,8 +370,8 @@ func (lim *Limiter) advance(now time.Time) (newNow time.Time, newLast time.Time, // Avoid making delta overflow below when last is very old. maxElapsed := lim.limit.durationFromTokens(float64(lim.burst) - lim.tokens) elapsed := now.Sub(last) - if elapsed > maxElapsed { - elapsed = maxElapsed + if elapsed >= maxElapsed { + return now, last, float64(lim.burst) } // Calculate the new number of tokens, due to time that passed. diff --git a/rate/rate_test.go b/rate/rate_test.go index 866347c..2f97664 100644 --- a/rate/rate_test.go +++ b/rate/rate_test.go @@ -476,3 +476,11 @@ func BenchmarkWaitNNoDelay(b *testing.B) { lim.WaitN(ctx, 1) } } + +func TestPreciseAllow(t *testing.T) { + lim := NewLimiter(0.00027777777777777778, 1) + lim.tokens = 0.54990492004805558 + if !lim.Allow() { + t.Errorf("want ok, got false") + } +}