commit | 1f47c861a9ac5a6e7645609f91b895398ff31642 | [log] [tgz] |
---|---|---|

author | Josh Bleecher Snyder <josharian@gmail.com> | Wed Jul 21 16:47:54 2021 -0700 |

committer | Josh Bleecher Snyder <josharian@gmail.com> | Fri Jul 23 03:22:27 2021 +0000 |

tree | 29a20f66ce49ba5be461d41be241d8d1ee561e02 | |

parent | 38a9dc6acbc6efd7758b7376c52f89090df822bd [diff] |

rate: simplify code When a long time has passed since a previous call to the rate limiter, we can overflow when calculating how many new tokens are available. To prevent that, we calculated a maximum elapsed amount. When we hit maxElapsed, we ended up converting a duration into tokens and then back. This operation was lossy, so we took pains to increase the precision of the conversion in tokensFromDuration. An easier fix is to avoid the round trip entirely. We know the max number of new tokens we should ever have, so we can cap it directly. And in fact we do cap it already. Delete the extraneous code. This lets us revert to the original, simpler tokensFromDuration. While we're here, tweak durationFromTokens for clarity. Updates #34861 Change-Id: I9a3b0fd848d772b314aecc43fa675631b8be7f2f Reviewed-on: https://go-review.googlesource.com/c/time/+/336469 Trust: Josh Bleecher Snyder <josharian@gmail.com> Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Eric Lagergren <ericscottlagergren@gmail.com> Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com>

diff --git a/rate/rate.go b/rate/rate.go index c0f7a57..0cfcc84 100644 --- a/rate/rate.go +++ b/rate/rate.go

@@ -364,20 +364,13 @@ last = now } - // 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 - } - // Calculate the new number of tokens, due to time that passed. + elapsed := now.Sub(last) delta := lim.limit.tokensFromDuration(elapsed) tokens := lim.tokens + delta if burst := float64(lim.burst); tokens > burst { tokens = burst } - return now, last, tokens } @@ -385,15 +378,11 @@ // of time it takes to accumulate them at a rate of limit tokens per second. func (limit Limit) durationFromTokens(tokens float64) time.Duration { seconds := tokens / float64(limit) - return time.Nanosecond * time.Duration(1e9*seconds) + return time.Duration(float64(time.Second) * seconds) } // tokensFromDuration is a unit conversion function from a time duration to the number of tokens // which could be accumulated during that duration at a rate of limit tokens per second. func (limit Limit) tokensFromDuration(d time.Duration) float64 { - // Split the integer and fractional parts ourself to minimize rounding errors. - // See golang.org/issues/34861. - sec := float64(d/time.Second) * float64(limit) - nsec := float64(d%time.Second) * float64(limit) - return sec + nsec/1e9 + return d.Seconds() * float64(limit) }