Skip to content

Conversation

@zwoop
Copy link
Contributor

@zwoop zwoop commented Oct 30, 2023

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
Copy link
Contributor

ywkaras commented Oct 31, 2023

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

@zwoop
Copy link
Contributor Author

zwoop commented Oct 31, 2023

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
Copy link
Contributor

ywkaras commented Oct 31, 2023

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

@zwoop
Copy link
Contributor Author

zwoop commented Oct 31, 2023

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

class AtomicType : public Metrics::AtomicType
{
};
Copy link
Contributor

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; ?

Copy link
Contributor Author

@zwoop zwoop Nov 6, 2023

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
Contributor

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
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.


public:
AtomicType() = default;
virtual ~AtomicType() = default;
Copy link
Contributor

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
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
Contributor

Choose a reason for hiding this comment

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

above what?

Copy link
Contributor Author

@zwoop zwoop Nov 7, 2023

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
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
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.

{
auto &instance = Metrics::instance();

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

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
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
Contributor

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.

}

static int64_t
load(AtomicType *metric)
Copy link
Contributor

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
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
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).

return metric->_value.load();
}

}; // class Counter
Copy link
Contributor

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
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
Contributor

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
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.

return lookup(id);
}
return std::nullopt;
}
Copy link
Contributor

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
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
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
Contributor

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
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.

{
auto &instance = Metrics::instance();

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

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
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
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));
             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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
Contributor

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
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
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

@zwoop zwoop Nov 7, 2023

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".

}

static AtomicType *
createPtr(const std::string_view name)
Copy link
Contributor

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
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
Contributor

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
Contributor

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
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
Copy link
Contributor

ywkaras commented Nov 7, 2023

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
Copy link
Contributor

bneradt commented Nov 7, 2023

[approve ci]

1 similar comment
@bneradt
Copy link
Contributor

bneradt commented Nov 7, 2023

[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
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
Copy link
Contributor

@masaori335 masaori335 left a comment

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