-
Notifications
You must be signed in to change notification settings - Fork 334
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
Optimistically return metrics from the cache (if present) #418
base: master
Are you sure you want to change the base?
Conversation
…ilding a metric to add to the cache
@jupp0r any advice on how to address this jenkins error:
It doesn't seem related to this PR? |
Ignore it, it's a know issue that I though I had fixed. |
Hi, Could you please add a test for the expected behavior? Thanks, |
I've been thinking about adding a test but I'm struggling to come with anything. PS: I'm not super happy about the code duplication either but again I couldn't come up with a better approach. |
Is the idea that hashing the labels is less expensive than the call to |
The idea is that the vast majority of the time As an illustration, we have the following pseudo-code:
The first request for each vhost initializes the cache (ie. |
I get it. How about implementing a helper which accepts a pre-computed hash value, thereby avoiding the new first-lookup penalty and avoiding the duplication mentioned earlier? |
How about this? |
metrics_iterator iter = FindMetric(labels); | ||
if (iter->second) return *(iter->second); | ||
return Add(iter, detail::make_unique<T>(args...)); |
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.
You're acquiring the lock twice here. The helpers (e.g. FindMetric and Add overload) are private and should assume that the lock is held.
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.
Same comment as before: One double-locking is nothing in the long-run. However, grabbing the lock before computing the hash is always detrimental.
But that really opens the door to the fix I prefer: move the implementation of Add
to the header file. Much simpler but I figure less acceptable.
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.
I don't think consolidating lock acquisition is incompatible with hashing outside. As long as the private methods remain in the code file the goal of preventing instantiation outside of Counter/Gauge/... should be achieved. So,
T& Add(const std::map<std::string, std::string>& labels, Args&&... args) {
auto hash = detail::hash_labels(labels);
std::lock_guard<std::mutex> lock{mutex_};
metrics_iterator iter = FindMetric(hash);
if (iter->second) return *(iter->second);
return Add(iter, detail::make_unique<T>(args...));
where both private methods (FindMetric and Add) use a pre-computed hash.
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.
Taking the lock twice leads to a race condition where the unique ptr is null in between.
My goal when hiding the implementation was to avoid circular includes as well as limiting the exposed symbols to have at least a chance to provide patches while maintaining the SONAME.
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.
Taking the lock twice leads to a race condition where the unique ptr is null in between.
If by "race condition" you mean "there is a possibility that 2 (or more) unique ptr's are created" that is correct.
My goal when hiding the implementation was to avoid circular includes as well as limiting the exposed symbols to have at least a chance to provide patches while maintaining the SONAME.
Right. This is why this patch is structured this way: to maintain the same isolation of the implementation. The runtime cost is what we've talked about here: double-hashing and double-locking until the cache is populated. After that, no more double-hashing or double-locking, and no more extraneous new/delete (the original intent of this patch.)
I think it's a worthy trade-off.
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.
On my forgeAI-nick/optimistic-family-add
branch I moved the lock into to inlined template. That way we don't double lock.
Could you please reset your branch to "our" |
before building a metric to add to the cache.