Conversation
4aef084 to
2ab18af
Compare
--stats-sinks has been documented (with a statsd example) but no NighthawkStatsSinkFactory implementation was ever linked into the binaries, so any --stats-sinks value aborted at startup with "Didn't find a registered implementation". This adds a sink and registers it under Envoy's names so the documented configs work as written: - envoy.stat_sinks.statsd (envoy.config.metrics.v3.StatsdSink, UDP address form only) and envoy.stat_sinks.dog_statsd (envoy.config.metrics.v3.DogStatsdSink: tags, optional max_bytes_per_datagram batching). - Counters are sent as deltas (|c) on every flush, gauges as values (|g). Nighthawk's sinkable latency statistics record nanoseconds, so each sample is converted and sent as a millisecond timing with microsecond precision (|ms); Envoy histograms are converted per unit. - Per-worker metrics are named worker.<n>.<rest> (both cluster.<n>. and worker.<n>. store scopes), or carry a worker:<n> tag with DogStatsD. - One UDP socket is shared by all threads; with batching, latency samples are packed per recording thread and sent when the batch fills, on the next flush and at shutdown. Envoy's thread-local slots are not usable here because Nighthawk's flush worker thread registers after sinks are created. - New --stats-sink-tag key:value (repeatable, CommandLineOptions stats_sink_tags) adds tags to every message of tag-capable sinks. NighthawkStatsSinkFactory::createStatsSink now receives the translated typed config, a ThreadLocal::SlotAllocator and the configured tags. Signed-off-by: Bruno Palermo <bruno.palermo@superbid.net>
2ab18af to
4d6b364
Compare
Brings the branch up to date with the Envoy bump to 82c182e, the envoy_package additions, envoyproxy#1598 and envoyproxy#1599. Both conflicts were "each side appends its own thing in the same place": a test case in options_test.cc and a bullet in the version history changelist. Both sides kept. Signed-off-by: Bruno Palermo <bruno.palermo@superbid.net>
eric846
left a comment
There was a problem hiding this comment.
What do you think of the following proposal?
Could Nighthawk be enhanced to directly support Envoy stats sink plugin factories? I think this would help us reuse existing sinks with less effort.
The existing --stats-sinks support uses Nighthawk-specific NighthawkStatsSinkFactory factories, which then create standard Envoy Envoy::Stats::Sink objects.
My proposal is for Nighthawk to directly support Envoy::Server::Configuration::StatsSinkFactory. Then anytime someone wanted to start using a particular Envoy sink plugin, they could just link it into their Nighthawk binary without hvaing to write a NighthawkStatsSinkFactory for it.
If there's an existing Envoy UDP statsd sink, we coulde use it instead of creating a new one just for Nighthawk. We could just link any official Envoy sink into the official Nighthawk binary, also including the DataDog one.
The cleanest way to introduce support for Envoy::Server::Configuration::StatsSinkFactory might be to introduce a new Nighthawk plugin type under a new command line flag --envoy-stats-sinks, and mark --stats-sinks as deprecated.
Envoy::Server::Configuration::StatsSinkFactory has the method virtual absl::StatusOr<Stats::SinkPtr> createStatsSink(const Protobuf::Message& config, Server::Configuration::ServerFactoryContext& server).
Fortunately Nighthawk implements NighthawkServerFactoryContext and NighthawkServerInstance which includes thread-local storage.
About tags: If we were using an Envoy sink plugin that already supported tags, could we just configure the tags in the sink plugin config proto as processed by the Envoy::Server::Configuration::StatsSinkFactory? Then we wouldn't need Nighthawk to have any special awareness of tags.
|
I just saw your notes about the
|
|
Thanks, I like the direction and I think the factory part should happen. Before committing to the "delete ours and link Envoy's" half, I went and checked what Nighthawk's stats actually look like to a sink, and found something that changes the shape of the work. Supporting The blocker for reuse isn't the factory type, it's that Nighthawk's latency histograms aren't store histograms. That matters because most Envoy sinks implement The statsd family does receive them, but reports them in the wrong unit. On tags: yes in principle, not yet in practice. So I'd suggest splitting it:
One thing I still need to pin down: I hit a thread-local assert when a TLS-based sink ran under our flush worker and worked around it with a process-wide socket. Does that split sound reasonable, and would you prefer step 1 as its own PR with this one rebased on top? |
|
I would definitely do any other experiments in a separate PR on the side, because the current PR may end up being the only viable solution. I left some review comments on the current PR in case we use it. On tags, agreed. Maybe Nighthawk's stats can be improved later, but for now a new flag is simpler. My overall motivation here is to try to find a way to avoid adding a new sink, which is a lot of code to review and maintain in Nighthawk. I think avoiding this new sink is even worth it even if it means updating Nighthawk's stats to be more like Envoy's, and/or workarounds like a compatibility mode where we have to divide all values by 1,000,000. For the other PR:
|
|
Your motivation makes sense to me, and I'd rather land less Nighthawk code than more — so let me answer the
The fix is to record each sample into a store histogram as well. On backward compatibility with existing Nighthawk sink plugins, which was your sharpest question. There is one real exception, a sink that recovers the worker id via On the That only helps once Envoy's statsd sinks actually honour the unit, which today they do not: On the thread-local writer, from your earlier comment: the sink here avoids it by keeping one process-wide UDP socket guarded by a mutex, with no thread-local writer at all. Your suggestion about changing when Nighthawk creates the sinks, or when it creates the flush worker thread, is the more interesting direction and I have not pinned down the root cause yet — On the panics: good to know stubbing them is routine. For the UDP sinks it may not even arise — they need only On cost, since you raised the risk of an expensive stats change: the mirror adds a cached thread-local lookup and a circllhist insert per sample, and exactly one mirrored record per request in HTTP mode (the latency-by-status statistics are mutually exclusive). I measured it on loopback at 5k rps and it sits below the noise floor — two runs of the identical build differed by more than the change did — which bounds it at well under a microsecond per sample against roughly 140 us of client CPU per request. Happy to keep this PR open as the fallback while the other path is explored, as you suggest. If the mirror plus |
|
Reading your earlier comment more closely, I think you answered some of my questions. I agree with the way you split it. I would try that in a separate PR in case the current PR ends up being the better option. |
|
Opened #1616 with the split you suggested. It's the store histogram change only — recording each sample into an Envoy store histogram so Nighthawk's latency statistics appear in It also carries a test answering the threading question from your earlier comment, and I owe you a correction there: I said the thread-local issue was unresolved and that I'd get to the bottom of it before proposing anything. I have, and it isn't a blocker — Envoy's
|
Description
This PR is related to #1607
--stats-sinksis documented in the README with a statsd example, but noNighthawkStatsSinkFactoryimplementation is linked into the binaries, so any--stats-sinksvalue aborts at startup withDidn't find a registered implementation for 'envoy.stat_sinks.statsd'. This adds a UDP statsd sink and registers it under Envoy's sink names so the documented configs work as written.Behavior:
envoy.stat_sinks.statsd(envoy.config.metrics.v3.StatsdSink, UDPaddressform, IP literal or host name resolved at startup;tcp_cluster_nameis rejected) andenvoy.stat_sinks.dog_statsd(envoy.config.metrics.v3.DogStatsdSink: DogStatsD tags, optionalmax_bytes_per_datagrambatching).|c) on every flush, gauges as values (|g). Nighthawk's sinkable latency statistics record nanoseconds, so every sample is sent as a millisecond timing with microsecond precision (|ms); Envoy histograms are converted per their unit.worker.<n>.<rest>(covering both thecluster.<n>.andworker.<n>.store scopes), or carry aworker:<n>tag with DogStatsD.--stats-sink-tag key:value(repeatable;CommandLineOptions.stats_sink_tags) adds tags to every message of tag-capable sinks, e.g. to identify a run or pod.nighthawk(override withprefix).Notes for Reviewers
UdpStatsdSinkwas not reusable as-is: it keeps a thread-local writer, and Nighthawk's flush worker thread registers with TLS after sinks are created, which trips Envoy'scurrentThreadRegisteredWorkerassert; it also forwards histogram values verbatim as|ms, wrong for Nighthawk's nanosecond statistics. The sink here shares one UDP socket across threads and batches latency samples per recording thread (sent when the batch fills, on the next flush, and at shutdown).NighthawkStatsSinkFactory::createStatsSinknow receives the translated typed config (it previously had no access to it), aThreadLocal::SlotAllocatorand the configured tags;include/is documented as not a public API, and the only in-tree implementation (the test fake) is updated.//test:statsd_sink_test(naming with/without tags, ns to ms conversion, counter deltas and gauges, batching and end-of-run flush against a real loopback UDP receiver, factory registration, host name resolution and config validation);test/integration/test_stats_sinks.py(fullnighthawk_clientruns with each sink, capturing the datagrams);//test:options_test,//test:process_test,//test:factories_test. README usage regenerated;docs/root/statistics.mdand version history updated.