-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat(stats): split off transmission to RawSender, implement batching and queueing support, add streamlined prefix and suffix support
#6267
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
Conversation
1b1c768 to
1b87c07
Compare
4faf35f chore: add `stats` as a pull request header scope (Kittywhiskers Van Gogh) Pull request description: ## Additional Information Would allow tagging #5167, #6267 and #6237 as `feat(stats)`. ## Breaking Changes None. ## Checklist: - [x] I have performed a self-review of my own code **(note: N/A)** - [x] I have commented my code, particularly in hard-to-understand areas **(note: N/A)** - [x] I have added or updated relevant unit/integration/functional/e2e tests **(note: N/A)** - [x] I have made corresponding changes to the documentation **(note: N/A)** - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ **(note: N/A)** ACKs for top commit: PastaPastaPasta: utACK 4faf35f UdjinM6: utACK 4faf35f thephez: utACK 4faf35f Tree-SHA512: 25cc28792351852b9e920d980b8814d93274bc53d22ce63fb1a5bf32821b10902d22b384a408e1c4a7b97239e53e235c45ac008d0f82e1afe5d6071b392acb47
|
This pull request has conflicts, please rebase. |
|
Should update diff --git a/test/util/data/non-backported.txt b/test/util/data/non-backported.txt
index 150bbb70c1..7684074874 100644
--- a/test/util/data/non-backported.txt
+++ b/test/util/data/non-backported.txt
@@ -29,7 +29,8 @@ src/rpc/quorums.cpp
src/saltedhasher.*
src/spork.*
src/stacktraces.*
-src/statsd_client.*
+src/stats/*.cpp
+src/stats/*.h
src/test/block_reward_reallocation_tests.cpp
src/test/bls_tests.cpp
src/test/dip0020opcodes_tests.cpp |
|
not sure if can actually do d42c88f develop: this PR: |
old ideas
diff --git a/src/stats/client.cpp b/src/stats/client.cpp
index cdcf36d281..b536a44db3 100644
--- a/src/stats/client.cpp
+++ b/src/stats/client.cpp
@@ -157,6 +157,8 @@ bool StatsdClient::send(const std::string& key, T1 value, const std::string& typ
return true;
}
+ if (value < EPSILON) value = 0;
+
// Construct the message and if our message isn't always-send, report the sample rate
std::string buf{strprintf("%s%s%s:%s|%s", m_prefix, key, m_suffix, ToString(value), type)};
if (frequency < 1.f) {
diff --git a/src/stats/client.cpp b/src/stats/client.cpp
index cdcf36d281..832085c8ef 100644
--- a/src/stats/client.cpp
+++ b/src/stats/client.cpp
@@ -6,7 +6,6 @@
#include <stats/client.h>
-#include <util/string.h>
#include <util/system.h>
#include <cmath>
@@ -25,6 +24,7 @@ static constexpr char STATSD_NS_DELIMITER{'.'};
static constexpr char STATSD_METRIC_COUNT[]{"c"};
/** Character used to denote Statsd message type as gauge */
static constexpr char STATSD_METRIC_GAUGE[]{"g"};
+static constexpr char STATSD_METRIC_GAUGE_DOUBLE[]{"g"};
/** Characters used to denote Statsd message type as timing */
static constexpr char STATSD_METRIC_TIMING[]{"ms"};
} // anonymous namespace
@@ -133,7 +133,7 @@ bool StatsdClient::gauge(const std::string& key, int64_t value, float frequency)
bool StatsdClient::gaugeDouble(const std::string& key, double value, float frequency)
{
- return send(key, value, STATSD_METRIC_GAUGE, frequency);
+ return send(key, value, STATSD_METRIC_GAUGE_DOUBLE, frequency);
}
bool StatsdClient::timing(const std::string& key, uint64_t ms, float frequency)
@@ -158,7 +158,12 @@ bool StatsdClient::send(const std::string& key, T1 value, const std::string& typ
}
// Construct the message and if our message isn't always-send, report the sample rate
- std::string buf{strprintf("%s%s%s:%s|%s", m_prefix, key, m_suffix, ToString(value), type)};
+ std::string buf;
+ if (type == STATSD_METRIC_GAUGE_DOUBLE) {
+ buf = strprintf("%s%s%s:%f|%s", m_prefix, key, m_suffix, value, type);
+ } else {
+ buf = strprintf("%s%s%s:%d|%s", m_prefix, key, m_suffix, value, type);
+ }
if (frequency < 1.f) {
buf += strprintf("|@%.2f", frequency);
}actually, diff --git a/src/stats/client.cpp b/src/stats/client.cpp
index cdcf36d281..55f5f458c0 100644
--- a/src/stats/client.cpp
+++ b/src/stats/client.cpp
@@ -6,7 +6,6 @@
#include <stats/client.h>
-#include <util/string.h>
#include <util/system.h>
#include <cmath>
@@ -158,7 +157,7 @@ bool StatsdClient::send(const std::string& key, T1 value, const std::string& typ
}
// Construct the message and if our message isn't always-send, report the sample rate
- std::string buf{strprintf("%s%s%s:%s|%s", m_prefix, key, m_suffix, ToString(value), type)};
+ std::string buf{strprintf("%s%s%s:%.6f|%s", m_prefix, key, m_suffix, value, type)};
if (frequency < 1.f) {
buf += strprintf("|@%.2f", frequency);
}
|
|
This pull request has conflicts, please rebase. |
In upcoming commits, message sending will be split off into a separate file and stats capabilities will be fleshed out. Prepare for that by giving it its own directory. Also get rid of `statsd` namespace, it is entirely unnecessary. Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
RawSender, implement batching and queueing support, add streamlined suffix and prefix supportRawSender, implement batching and queueing support, add streamlined prefix and suffix support
knst
left a comment
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.
UB due to noexcept should be fixed before merging it
src/stats/rawsender.cpp
Outdated
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.
if you can't open socker or create m_sock, the RawSender is useless object.
Instead, just throw exception from constructor and after that you don't need to check anything like if (m_sock) because object is always valid.
see relevant comment for StatsdClient
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.
Would like to avoid exceptions where possible, StatsdClient already prints errors generated and reaps the inactive object immediately (source), think it's fine as-is.
src/stats/client.cpp
Outdated
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.
stats: move message sending logic to RawSender
instead having here std::optional just throw exception from RawSender constructor;
If m_sender is created - it's always valid. If m_sender is not created - it's nullptr and code is simpler
src/stats/rawsender.cpp
Outdated
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.
if socket is not created, no need to count failures of sending data: all of the will be failed;
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.
The success and failure counters also serve as a way to count the number of requests sent for processing, even if they all eventually fail. Think it's fine, as-is.
src/stats/rawsender.h
Outdated
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.
Consider using tl:unexpected instead from util/expected.h
using RawSenderError = tl::expected<void, std::error>;
...
return {}; // success
return std::string{"ERROR"}; // failure
src/stats/rawsender.h
Outdated
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.
here's only one possible m_server can be, use instead:
-std::pair<struct sockaddr_storage, socklen_t> m_server{{}, sizeof(struct sockaddr_storage)};
+struct sockaddr_storagem_server{};
+socklen_t server_len{sizeof(struct sockaddr_storage)};
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 would rather they be one variable.
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 would rather they be one variable.
why? The only purpose for all class Sock is to be a wrapper around this m_server
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.
m_server has nothing to do with the creation of the SOCKET or the subsequent m_socks wrapper, considering that IPv4 UDP params are hardcoded (source). It is populated when resolving the address (source) and then used by ::sendto (source). The former requires taking size information as a pointer, requiring us to store it as a variable.
It's fine as-is.
src/init.cpp
Outdated
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.
add TODO to remove it in v23
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.
Already have a PR that gets rid of legacy code, can open it and mark as draft until we're in the merging window for that version, that okay?
src/stats/rawsender.h
Outdated
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.
RawSender is not a good name for this class, because it's just std::vector and has nothing to do with 'RawSender'.
Move this one to client.h and maybe rename it to 'RawStat' or StatRawMessage or something similar.
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.
src/stats/rawsender.h
Outdated
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.
RawSender is a global namespace is not a good name, when it has nothing to do without stats, it's not in utils.
let's re-introduce back namespace stats maybe? also you can move RawSender completely inside client.cpp, it's no need to be in it's own module.
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.
RawSender(and by extension,RawMessage) strive to remain as unopinionated as possible, moving the responsibility to construct valid Statsd messages ontoStatsdClient. This is to ensure thatRawSendercan be reused down the line or independently extended without impacting theStatsdclient.
Please refer to PR description for reasoning behind it being split out.
src/stats/rawsender.cpp
Outdated
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.
try to use a helper CreateSockTCP from netbase.h instead re-writing low-level code
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.
CreateSockTCP, as the name suggests, is used for TCP connections. Currently Statsd messages are transmitted over UDP. One of the earlier drafts of #5167 did create a CreateSockUDP but it would not only make future backports affecting netbase.{cpp,h} painful, it was clunky from the get-go.
Furthermore, Sock only envisions the usage of TCP, providing only a wrapper for ::send() (and not ::sendto(), which is generally an applicable way to send messages when using UDP) (source)
`RawSender` is inspired by `UDPSender` from `vthiery/cpp-statsd-client` and separating it out of `StatsdClient` is needed to implement queueing and batching support in upcoming commits. This is the start of migrating our Statsd codebase to `cpp-statsd-client`.
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
Additionally, let's remove the key sanitation logic since keys are hardcoded. Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
- `timing` cannot contain a negative value, change to `uint64_t` - remove `sendDouble`, use template specialization of `Send` instead, that can be reused as `send` - rename `value` in `count()` to `delta` - add more comments `gaugeDouble` has been kept around because utilizing templates for it would require us to either write template specializations for every kind of `gauge` value (implicit conversions will not save us from this) or move all logic to the header, neither option seems desirable.
This is to avoid the odd circumstance where stats don't work because you specified everything but didn't explicitly enable it. Using `-statsenabled` has been deprecated and will be removed in the next major release.
`statshostname` implies that alongside specifying the host, the user needs to specify a separate "hostname" field in order to connect when in fact, it is an entirely optional suffix field applied to stats keys. Rename it to `statssuffix` and deprecate `statshostname`, the latter will be removed in a subsequent major release.
`statsns`, aside from being an unclear name, in its default behaviour, doesn't use the namespace delimiter contaminates the root namespace of the key. Let's take care of that by deprecating `statsns` with its replacement, `statsprefix`, that behaves properly.
This marks the completion of our transition from code based on `talebook/statsd-client-cpp` to code based on `vthiery/cpp-statsd-client`. Also, we long stopped using `snprintf`, remove it from exclusions list.
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.
utACK cc1a75a
but I'd probably provide later extra PR to address my comments. I don't want to block this PR by nits and refactorings
UdjinM6
left a comment
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.
utACK cc1a75a
what does this mean? Do you approve or not? |
edited message to make it more clear. |
Motivation
This pull request achieves the goal originally set out in dash#5167, to migrate the base of our Statsd client implementation to one that is actively maintained. Statoshi (source) utilizes talebook/statsd-client-cpp, which in turn is inherited by Dash.
As Statsd is the only cross-platform reporting mechanism available (USDT requires a Linux host with superuser privileges and RPCs don't provide as much flexibility as desired), emphasis is placed on using Statsd as the primary way for the Dash daemon to report metrics related to node and network health.
As part of maintaining our Statsd client, this PR aims to migrate the base of our implementation to vthiery/cpp-statsd-client, a streamlined implementation that embraces C++11 with a thread-safe implementation of queueing and batching, which reduces the number of packets used to transmit stats.
These capabilities are optional and users can opt not to use them by setting
-statsduration=0to disable queueing (which will also disable batching) or-statsbatchsize=0, which will disable batching (but will not disable queueing unless requested explicitly).Additional Information
statsd::StatsdClient, make globalunique_ptr#5167RawSender(and by extension,RawMessage) strive to remain as unopinionated as possible, moving the responsibility to construct valid Statsd messages ontoStatsdClient. This is to ensure thatRawSendercan be reused down the line or independently extended without impacting the Statsd client.RawMessageexists to provide extensions tostd::vector<uint8_t>that make it easier to abstract away strings while also implementing some of its semantics likeappend()(and its alias,+=).InitStatsClient()was introduced to keepStatsdClientindifferent to how arguments are obtained and sanitized before they're supplied to the constructor. This is to keep it indifferent to all the backwards-compatibility code for deprecated arguments still work.%fwithout having to specify a precision as tinyformat automatically assumes a precision of 6 (source) and problems don't seem to be observed when using%fwith integers (source).static_assertto ensure that a specialization ofsend()involving a non-arithmetic type will raise alarm when compiling (source).Breaking changes
-statsenabled(replaced with specifying-statshost),-statshostname(replaced by-statssuffix) and-statsns(replaced by-statsprefix) have been deprecated and will be removed in a future release.Checklist: