Skip to content
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

stats: refactor to enable non-hot-restart stats to have distinct representation from hot-restart stats. #3606

Merged
merged 15 commits into from
Jun 20, 2018

Conversation

jmarantz
Copy link
Contributor

@jmarantz jmarantz commented Jun 12, 2018

Description:

Currently the presence of hot_restart as an option requires that all stats to be allocated in block of memory pre-allocated at runtime. This may present scalability challenges in some environments for a couple of reasons:

  1. Committing to the maximum number & size of stats on startup
  2. Difficulty of employing complex structures such as reference-counted symbol tables to share common substrings
    This PR does some refactoring to make it easier to create an alternate stats representation for the non-hot-restart case. Note that it does not actually do that: just makes it easier by virtualizing makeCounter and makeGauge.

See #3585 for more discussion.

Risk Level: Medium

Testing: //test/...

jmarantz added 7 commits June 12, 2018 14:43
…network issues when running under docker.

Signed-off-by: Joshua Marantz <jmarantz@google.com>
…istinct representations ot be created for non-hot-restart case.

Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
…-alloc case.

Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
Signed-off-by: Joshua Marantz <jmarantz@google.com>
@jmarantz jmarantz changed the title WIP: stats: refactor to enable non-hot-restart stats to have distinct representation from hot-restart stats. stats: refactor to enable non-hot-restart stats to have distinct representation from hot-restart stats. Jun 13, 2018
@jmarantz
Copy link
Contributor Author

Ready for a look I think.

@jmarantz
Copy link
Contributor Author

@ambuc

…alStore::counter() and gauge().

Signed-off-by: Joshua Marantz <jmarantz@google.com>
@jmarantz
Copy link
Contributor Author

@ggreenway do you have time to take a look?

Copy link
Member

@mrice32 mrice32 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good on first pass! A few comments.

*/
virtual RawStatData* alloc(const std::string& name) PURE;
virtual Counter* makeCounter(const std::string& name, std::string& tag_extracted_name,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that we removed the free(), but we're still returning raw pointers. Can you document the ownership model? How is the StatDataAllocator informed of when the stat is no longer needed by the caller? Is the user allowed to just delete the raw pointer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good point here, and I think my design here needs a bit of rethinking in the context of how a caller would free stuff. The other comments I'll address later, and I'm flipping back to WIP-mode while I think about this :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this was easy to deal with by just returning a shared_ptr at this level.

*/
class RawStatDataAllocator {
class StatDataAllocator {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like this is a general stat factory class that determines the impl for each type of stat (and how it is allocated). If it is able to choose the representation for gauges and counters, why not also allow it to make histograms?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added TODO -- making a parallel path for histograms would require moving the histogram impl out of thread_local_store and into stats_impl, which seems OK to me, but not in this PR. @ramaraochavali @mattklein123 WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmarantz originally I have added it to Stats_impl only. But later moved to thread_local_store based on @mattklein123 suggestion. I think it is ok to bring it back to stats impl.

// result, creating it with the heap allocator.
template <class StatType>
StatType&
safeMakeStat(const std::string& name,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add more documentation here about each argument. I'm only asking because the signature is reasonably complicated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I left some. I'm not sure it's super-illuminating as the semantics around tls_are rather complex :)


// If we have a valid cache entry, return it.
if (tls_ref && *tls_ref) {
return **tls_ref;
}

// We must now look in the central store so we must be locked. We grab a reference to the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we remove this comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch; replaced.

@@ -408,25 +408,33 @@ typedef std::unique_ptr<StoreRoot> StoreRootPtr;
struct RawStatData;

/**
* Abstract interface for allocating a RawStatData.
* Abstract interface for allocating statistics. Alternate
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: remove Alternate since the interface doesn't impose a default.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@jmarantz jmarantz changed the title stats: refactor to enable non-hot-restart stats to have distinct representation from hot-restart stats. WIP stats: refactor to enable non-hot-restart stats to have distinct representation from hot-restart stats. Jun 15, 2018
Signed-off-by: Joshua Marantz <jmarantz@google.com>
@jmarantz jmarantz changed the title WIP stats: refactor to enable non-hot-restart stats to have distinct representation from hot-restart stats. stats: refactor to enable non-hot-restart stats to have distinct representation from hot-restart stats. Jun 15, 2018
@ggreenway
Copy link
Contributor

I am swamped right now; not sure that I'll have time to review.

mrice32
mrice32 previously approved these changes Jun 18, 2018
Copy link
Member

@mrice32 mrice32 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM other than one minor comment about the interface.

*/
virtual RawStatData* alloc(const std::string& name) PURE;
virtual CounterSharedPtr makeCounter(const std::string& name, std::string& tag_extracted_name,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tag_extracted_name and tags are passed as references not so they can be modified and read by the caller, but so that they can be moved. I think a better way to communicate this in the interface is by making them rvalue references (for example: std::string&& tag_extracted_name), which will force all callers to wrap them in std::move() or pass an actual rvalue.

Also, the interface should document that if the returned CounterSharedPtr is nullptr, then these objects will not be moved/modified (we assume this at the callsite).

Similar comments for makeGauge().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Signed-off-by: Joshua Marantz <jmarantz@google.com>
*/
virtual void free(RawStatData& data) PURE;
virtual GaugeSharedPtr makeGauge(const std::string& name, std::string&& tag_extracted_name,
Copy link
Member

@mrice32 mrice32 Jun 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also document when these these rvalue references will be moved and when they will not (if allocation fails, they will not be moved, so that they may be used again by the caller).

Signed-off-by: Joshua Marantz <jmarantz@google.com>
Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One tiny nit. Sorry I thought I could power through this but this is a morning review. Will do first thing.

* @param name the full name of the stat.
* @param tag_extracted_name the name of the stat with tag-values stripped out.
* @param tags the extracted tag values.
* @return Counter* a counter, or nullptr if allocation failed, in which case tag_extracted_name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: doc comment out of date, same below

@mattklein123 mattklein123 self-assigned this Jun 19, 2018
Signed-off-by: Joshua Marantz <jmarantz@google.com>
mrice32
mrice32 previously approved these changes Jun 19, 2018
Copy link
Member

@mrice32 mrice32 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sweet! LGTM

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great. Nice cleanup. One question on readability.

@@ -225,6 +225,27 @@ class ThreadLocalStoreImpl : Logger::Loggable<Logger::Id::stats>, public StoreRo
Histogram& histogram(const std::string& name) override;
Histogram& tlsHistogram(const std::string& name, ParentHistogramImpl& parent) override;

template <class StatType>
using MakeStatFn = std::shared_ptr<StatType> (StatDataAllocator::*)(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This type of function pointer syntax took me by surprise and I suspect most people are not familiar with it. Is there any more readable way of doing this? Potentially a normal lambda that is passed an instance of StatDataAllocator? This is not a big deal, just throwing it out there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can use std::function but I would have to make static method intermediates which I think is less readable.

In the past I have added macros for defining and calling pointer to member functions, which helps a bit I think. That's my preferred option. WDYT?

There is also std::invoke, which I had never used before. But I wasn't about to get that to with and I didn't think it was all that readable either.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can use std::function but I would have to make static method intermediates which I think is less readable.

In the past I have added macros for defining and calling pointer to member functions, which helps a bit I think. That's my preferred option. WDYT?

There is also std::invoke, which I had never used before. But I wasn't about to get that to with and I didn't think it was all that readable either.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're going to use pointer to member function, I think macros are the preferred method for readability.

Why would you need static intermediates to use std::function? Can't you capture 'this' in the closure?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right; I don't need a static intermediary, but I think the way the code is structured I do need an intermediate closure, because I'm not starting with 'this', I'm starting with two alternate allocators. I do have that working -- it's a little messy IMO -- and I'll push that for reference, and then I'll push the version where I use pointer to member functions, but via macros, and I'm fine with either of those.

…ction.

Signed-off-by: Joshua Marantz <jmarantz@google.com>
…ros to aid readability.

Signed-off-by: Joshua Marantz <jmarantz@google.com>
@jmarantz
Copy link
Contributor Author

OK ptal at the two options:

Use std::function (requires an inline intermediary): 3c75f26

Use pointer-to-member function, with macros to aid readability (current state of PR).

I'm happy to go with either.

@mattklein123
Copy link
Member

I have a slight preference for the std::function version, but I think either is a big improvement. @ggreenway any preference?

@ggreenway
Copy link
Contributor

I also have a slight preference for the std::function version. I think fewer people will be confused by it; pointer to member function is an infrequently used language feature.

…with macros to aid readability."

This reverts commit b9eee85.

Signed-off-by: Joshua Marantz <jmarantz@google.com>
@jmarantz
Copy link
Contributor Author

std::function version is fine with me, that is now the state of this PR.

@mattklein123 mattklein123 merged commit b34adb5 into envoyproxy:master Jun 20, 2018
@jmarantz jmarantz deleted the virtualize-make-stats branch June 20, 2018 18:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants