-
Notifications
You must be signed in to change notification settings - Fork 593
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Fireperf] Fix RateLimiter
by avoiding manually setting time
#4027
Conversation
…uble for tokenCount
Timer
constructor call from RateLimiter and use double for tokenCount
RateLimiter
by avoiding manually setting time
Coverage Report 1Affected Products
Test Logs
Notes |
Size Report 1Affected Products
Test Logs
Notes |
new Timer( | ||
lastTimeTokenReplenished.getMicros() | ||
+ (long) (newTokens * MICROS_IN_A_SECOND / rate.getTokensPerSeconds())); | ||
double newTokens = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A similar issue would exist in iOS as well. We might need to followup on that to fix it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does iOS also have our own rate limiter implementation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for fixing this!
Only failing GHA is
|
Blocking #4024.
Context and why this is bad
b/243686627
TL;DR: don't use
@VisibleForTesting
method for real code, they break encapsulation. In this case,Timer(long time)
is breaking logic too because it's mixing 2 different timebases together (time-since-epoch and time-since-VM-start). It's a bad method that should not exist even for tests.Leftover problem
However if we remove the above, there's a leftover problem from #2662:
Before #2662, say there are 0 tokens at first. We can call
rateLimiter.check()
after any amount of time, so something like 1.33 tokens can be generated, but we only uselong
for ourtokenCount
, so it will automatically round to 1, and record current time aslastTimeTokenReplenished
, then use that token so we are back down to 0. However if next time we callcheck()
, 0.67 tokens are generated, then we round to 0, logic before #2662 would say we have no tokens to use, which is wrong because within the time described in above scenario, 2 tokens should have been generated, and we only used 2 tokens, so there should be enough.#2662 tried to fix this by manually setting
lastTimeTokenReplenished
to the exact time at which a whole-number of tokens are added. In the same example, if 1.33 tokens are generated, then we still round-down and only add 1 totokenCount
, but we advancelastTimeTokenReplenished
bythe time it took to generate 1 token
. This is like time only increment in ticks, with each tick guaranteed to be a multiple of the time it takes to generate a single token. This works, but manually setting time is error-prone (this PR/bug is literal proof).Solution
Simply change tokens to
double
. Guava also uses double for their rate limiter's token counter https://github.com/google/guava/blob/9c1e5dea4b980202ba003b90fcb64183d42031b3/android/guava/src/com/google/common/util/concurrent/SmoothRateLimiter.java#L313