-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
util/quotapool: clean up some commentary, invert Update logic #63227
util/quotapool: clean up some commentary, invert Update logic #63227
Conversation
Have you considered an AbstractPool.Update primitive that just takes a func? That way we won't have to box things just to call a method. Well, at least if everything gets inlined, which I'm hoping (if not, we could have a Begin and End pair of primitives). |
Some motivation for that suggestion: the top level method won't look as clean, but that cleanliness comes at a high cost - you have to understand the interface that you're passing, and you have to mess around with pools. The code of the layer above is also more opaque to a reader compared to code that performs the necessary update right there. |
The `Merge` method was clunky and required type switched. Instead, the API is changed to take a closure. Amazingly none of these closures escape! Release note: None
7fcd881
to
420aac3
Compare
Done! Turns out they don't even need to be inlined (the |
Awesome! Is whatever the func binds (eg readBytes) not escaping either? |
Nope |
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.
Great!!
It would be nice to have a benchmark for one of the more used limiters. I can add that separately though.
Reviewed 5 of 5 files at r1.
Reviewable status:complete! 1 of 0 LGTMs obtained (waiting on @ajwerner)
There's benchmarks. Let me run them. |
TFTR! Seems like noise:
bors r=RaduBerinde |
Cool! Thanks! I will rebase my other PR on top of this and try to use RateLimiter instead of the abstract pool. |
Build succeeded: |
pkg/kv/kvserver/tenantrate/limiter.go, line 130 at r1 (raw file):
Random question, but don't we need to If a long time has passed since the last update, the reduction won't matter because next time we |
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.
Reviewable status:
complete! 1 of 0 LGTMs obtained
pkg/kv/kvserver/tenantrate/limiter.go, line 130 at r1 (raw file):
Previously, RaduBerinde wrote…
Random question, but don't we need to
update()
here?If a long time has passed since the last update, the reduction won't matter because next time we
update()
we'll be at the burst limit in either case. Well, maybe for this particular usage it's fine since we're accounting for something that happened at or before the last update anyway, but it seems a little strange.
If somebody was blocked waiting, they already have a timer. See the comment above the return. 🤷
pkg/kv/kvserver/tenantrate/limiter.go, line 130 at r1 (raw file): Previously, ajwerner wrote…
Consider this:
Compare with:
With the current implementation, we can get anything in-between these two depending on what update() events may have happened in-between, even if they didn't make a significant difference in terms of tokens. I realize this is a bit nitpicky - I'm not really worried about any actual practical repercussions of this difference. I'm asking because in the refactoring I proposed in #62783, I am updating before adjusting and wanted to run that by you. |
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.
this looks nice!
Reviewed 1 of 5 files at r1.
Reviewable status:complete! 1 of 0 LGTMs obtained
pkg/kv/kvserver/tenantrate/limiter.go, line 130 at r1 (raw file):
rl.metrics.readBytesAdmitted.Inc(readBytes) rl.qp.Update(func(res quotapool.Resource) (shouldNotify bool) { rb := res.(*tokenBuckets)
nit: do we even need the res
parameter? The limiter
created tokenBuckets
and the quotaPool
so it knows the underlying resource.
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.
Reviewable status:
complete! 1 of 0 LGTMs obtained
pkg/kv/kvserver/tenantrate/limiter.go, line 130 at r1 (raw file):
Previously, sumeerbhola wrote…
nit: do we even need the
res
parameter? Thelimiter
createdtokenBuckets
and thequotaPool
so it knows the underlying resource.
Interesting. We’d need to keep a reference to it on the *limiter struct and then some comment saying that it should only be accessed from underneath the quota pool. Is that better?
The pattern of having users of libraries which provide data structures type asserting to get their implementation out it pretty common in go. Consider https://pkg.go.dev/github.com/google/btree#Item
pkg/kv/kvserver/tenantrate/limiter.go, line 130 at r1 (raw file):
Previously, RaduBerinde wrote…
Consider this:
- lastUpdated is a while in the past (say twice the amount of time it takes to fully replenish the bucket)
- we get a RecordRead with a significant size (say the entire burst limit). We subtract the amount but do not update anything else.
- immediately we get a new request for quota. We update, and because enough time has passed (even if the previous step put us in debt), our bucket is full after updating. The request goes through.
Compare with:
- we get the RecordRead and we update the bucket (it is now full) and subtract the amount; bucket is now empty.
- immediately we get a new request for quota. The bucket was very recently updated so it's still empty. We have to wait.
With the current implementation, we can get anything in-between these two depending on what update() events may have happened in-between, even if they didn't make a significant difference in terms of tokens.
I realize this is a bit nitpicky - I'm not really worried about any actual practical repercussions of this difference. I'm asking because in the refactoring I proposed in #62783, I am updating before adjusting and wanted to run that by you.
Good point. Makes sense.
The
Merge
method was clunky and required type switched. Instead, thequantity being used to do the update should know the type of the resource.
Also, renames the
QuotaPool
toAbstractPool
.Release note: None