Skip to content

Refactor new metrics with Counter vs Gauge types - #10694

Merged
zwoop merged 6 commits into
apache:masterfrom
zwoop:RefactorNewMetrics
Nov 9, 2023
Merged

Refactor new metrics with Counter vs Gauge types#10694
zwoop merged 6 commits into
apache:masterfrom
zwoop:RefactorNewMetrics

Conversation

@zwoop

@zwoop zwoop commented Oct 30, 2023

Copy link
Copy Markdown
Contributor

This adds some more tooling around Counter vs Gauge type of metrics, assuring that Counters for example can only increment. There's no change in functionality here, other than the naming and creation details. But it will assure that the underlying APIs are used correctly.

Performance wise this should make no difference, at least in a release build. The compiler will optimize this down to just the fetch_add() just as before.

@zwoop zwoop added this to the 10.0.0 milestone Oct 30, 2023
@zwoop
zwoop requested review from bneradt and bryancall October 30, 2023 19:42
@zwoop zwoop self-assigned this Oct 30, 2023
@zwoop
zwoop force-pushed the RefactorNewMetrics branch from ae5feaa to a502f4c Compare October 30, 2023 21:41
@bryancall
bryancall requested a review from ywkaras October 30, 2023 22:13
@ywkaras

ywkaras commented Oct 31, 2023

Copy link
Copy Markdown

Maybe you can fix the Clang Analyzer defect by inserting ink_assert(d != nullptr); after line 1269.

@zwoop

zwoop commented Oct 31, 2023

Copy link
Copy Markdown
Contributor Author

Maybe you can fix the Clang Analyzer defect by inserting ink_assert(d != nullptr); after line 1269.

I could, but the thing is, this is not my fault :). I asked @masaori335 to look at this, I'm suspecting that something in my PR makes clang-analyzer either confused, or brilliant, in detecting this old issue.

@zwoop
zwoop force-pushed the RefactorNewMetrics branch from a502f4c to eaa3ff6 Compare October 31, 2023 16:49
@ywkaras

ywkaras commented Oct 31, 2023

Copy link
Copy Markdown

Clang Analyzer punishes for unconfessed sins and those from previous lifetimes.

@zwoop

zwoop commented Oct 31, 2023

Copy link
Copy Markdown
Contributor Author

Clang Analyzer punishes for unconfessed sins and those from previous lifetimes.

True dat.

@zwoop
zwoop force-pushed the RefactorNewMetrics branch 2 times, most recently from 8fc2b43 to 7688644 Compare November 1, 2023 16:18
Comment thread include/api/Metrics.h

class AtomicType : public Metrics::AtomicType
{
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why not just using AtomicType = Metrics::AtomicType; ?

@zwoop zwoop Nov 6, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Because then the compiler won't give errors or warnings about wrong types. Initially I did this all with "using", and then it became too easy to do the wrong things with the wrong types of metrics. Meaning, if you do

using Foo == Bar;

Foo and Bar are identical types, and can be used interchangeable. They truly are aliases. And I wanted to avoid people trying to do dangerous stuff such as sucking out a Counter out of the metrics storage, and then treat it as a Gauge.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why don't you give it the constructor explicit AtomicType(Metrics::AtomicType a) : Metrics::AtomicType(a) {} so you won't need the reinterpret_cast.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ok, let me look tomorrow morning.

Comment thread include/api/Metrics.h Outdated

public:
AtomicType() = default;
virtual ~AtomicType() = default;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Making this virtual doubles the size of instances of this type. Why is it necessary?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same as above :/

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

above what?

@zwoop zwoop Nov 7, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It had to be virtual, otherwise I couldn't subclass it later as I did. And I had to sub class it, rather than aliases, to make the compiler detect bad usage. It's not considered polymorphic otherwise, e.g.

/Users/leif/apache/trafficserver/src/proxy/http/PreWarmManager.cc:1181:18: error: 'ts::Metrics::AtomicType' is not polymorphic
        metric = dynamic_cast<Metrics::Counter::AtomicType *>(metrics.lookup(stats_id));

Maybe we can fix the usage that's causing trouble here, I didn't write this code in the PreWarmManager.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Maybe the right solution here is to have

Metrics::Counter::lookup(stats_id);
Metrics::Gauge::lookup(stats_id);

?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@ywkaras I added those static's, which allows us to eliminate the virtual. Let me know what you think, it's in the last commit that I lovingly made just for you.

Comment thread include/api/Metrics.h
{
auto &instance = Metrics::instance();

return reinterpret_cast<AtomicType *>(instance.lookup(instance._create(name)));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This looks like the overload of lookup() that returns IdType/uint32_t. How can that work, to cast it to a64-bit pointer?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's a different lookup no? I'd have to look again, but remember there are two lookups, one to that returns the IdType, and one that returns a pointer directly to the atomic types.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

OK I see but it shouldn't need the case then.

Comment thread include/api/Metrics.h Outdated
}

static int64_t
load(AtomicType *metric)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This can't be const?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Maybe? I can try.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ok, fixed, and uploaded with a new commit for your review suggestions (will add more as if we identify more things that needs fixing).

Comment thread include/api/Metrics.h
return metric->_value.load();
}

}; // class Counter

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I would have expected Counter to have load, increment and clear, and Gauge to have load, increment and decrement.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

clear? Why clear? All counters starts at zero, and can always go up. You should not set it back to 0 IMO.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yes there are pros and cons to having clear(). I was thinking we had set() to do a clear, so just have clear().

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Well, I'll probably argue that if you need to do clear(), you are using this wrong, and it should be a gauge. Counters are supposed to always increase, randomly setting it to 0 (or any value) is wrong, and why I even bothered adding this difference of Counters vs Gauges.

Comment thread include/api/Metrics.h Outdated
@@ -103,16 +125,9 @@ class Metrics
return std::nullopt;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This function is not currently used.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's used in the tests :). But it could be used, maybe even should be used. Certainly in plugins.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed.

increment_current_active_connections_stat() override
{
Metrics::increment(http2_rsb.current_active_server_connection_count);
Metrics::Gauge::increment(http2_rsb.current_active_server_connection_count);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Since you had to change all of these anyway, why didn't you make increment and decrement non-static member functions:
http2_rsb.current_active_server_connection_count->increment(). It's shorter, and you would get a compiler error if you tried to decrement a counter rather than a gauge.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I thought of that, and even asked @cmcfarlen , we both preferred the static. I can change it if we think it's better. It just reads better with the static IMO.

Comment thread include/api/Metrics.h
{
auto &instance = Metrics::instance();

return reinterpret_cast<AtomicType *>(instance.lookup(instance._create(name)));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Another superfluous cast.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No, I don't think it's superfluous, it won't compile otherwise I'm fairly certain.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

/Users/leif/apache/trafficserver/include/api/Metrics.h:312:14: error: cannot initialize return object of type 'AtomicType *' (aka 'ts::Metrics::Gauge::AtomicType *') with an rvalue of type 'AtomicType *' (aka 'ts::Metrics::AtomicType *')
      return instance.lookup(instance._create(name));
             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Comment thread src/iocore/aio/test_AIO.cc Outdated
Metrics::IntType *completed = m.lookup(m.lookup("proxy.process.io_uring.completed"));
Metrics::IntType *submitted = m.lookup(m.lookup("proxy.process.io_uring.submitted"));
Metrics::Counter::AtomicType *completed = m.lookup(m.lookup("proxy.process.io_uring.completed"));
Metrics::Counter::AtomicType *submitted = m.lookup(m.lookup("proxy.process.io_uring.submitted"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You could have had two more useless casts here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You implying we should have another lookup function, that avoids both lookups here/ This is an unusual use case honestly, the the IdType lookups are kept mostly because of the old APIs and use of "ids" instead of pointers.

We can probably eliminate some of this in v11 once we clean out more old stuff completely. Or, if we could convince Strostrup to allow overloading to take the return type into the resolution.


printf("submissions: %lu\n", Metrics::read(submitted));
printf("completions: %lu\n", Metrics::read(completed));
printf("submissions: %lu\n", Metrics::Gauge::load(submitted));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@zwoop zwoop Nov 7, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeh, not my code. I'm fairly certain we've agreed that you don't have to rewrite everyones code to make something "better".

Comment thread include/api/Metrics.h
}

static AtomicType *
createPtr(const std::string_view name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

const std::string_view is overkill, all the member functions are const I think.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

does it hurt? Other than your eyes ?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Alan will sentence you to hard labor on his pumpkin farm for this.

this->rsb.refcountcache_last_sync_time = intm.newMetricPtr((metrics_prefix + "last_sync.time").c_str());
this->rsb.refcountcache_last_total_items = intm.newMetricPtr((metrics_prefix + "last_sync.total_items").c_str());
this->rsb.refcountcache_last_total_size = intm.newMetricPtr((metrics_prefix + "last_sync.total_size").c_str());
this->rsb.refcountcache_current_items = Metrics::Gauge::createPtr((metrics_prefix + "current_items").c_str());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why are you doing string -> c string -> string_view instead of just string -> string_view directly?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Probably a remnant from the code as it was prior to changing to new metrics.

ywkaras
ywkaras previously approved these changes Nov 7, 2023
@zwoop
zwoop force-pushed the RefactorNewMetrics branch from afa6c52 to acdb96c Compare November 7, 2023 05:20
@ywkaras

ywkaras commented Nov 7, 2023

Copy link
Copy Markdown

How about: https://godbolt.org/z/7Wv444EfE

@zwoop
zwoop force-pushed the RefactorNewMetrics branch 4 times, most recently from 774727b to e287f76 Compare November 7, 2023 21:04
@bneradt

bneradt commented Nov 7, 2023

Copy link
Copy Markdown
Contributor

[approve ci]

1 similar comment
@bneradt

bneradt commented Nov 7, 2023

Copy link
Copy Markdown
Contributor

[approve ci]

@zwoop
zwoop force-pushed the RefactorNewMetrics branch 4 times, most recently from e56c764 to 10ea86c Compare November 8, 2023 00:01
ywkaras
ywkaras previously approved these changes Nov 8, 2023
std::string_view alpn_name = alpn_name_for_stat(dst->alpn_index);
_makeName(dst, COUNTER_STAT_ENTRIES[j], name, sizeof(name));

auto metric = Metrics::Counter::createPtr(name); // This will do a lookup if it already exists

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This looks much convenient 👍

masaori335
masaori335 previously approved these changes Nov 8, 2023

@masaori335 masaori335 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This makes metrics type explicit. Looks good to me.

@zwoop
zwoop dismissed stale reviews from masaori335 and ywkaras via 8e6333d November 8, 2023 17:20
@zwoop
zwoop force-pushed the RefactorNewMetrics branch from 10ea86c to 8e6333d Compare November 8, 2023 17:20
@zwoop
zwoop merged commit 954cd74 into apache:master Nov 9, 2023
@zwoop
zwoop deleted the RefactorNewMetrics branch November 9, 2023 16:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants