Prometheus per label ttl#18689
Conversation
abhishekrb19
left a comment
There was a problem hiding this comment.
Thanks @aho135! Did a high level review and left some thoughts
| @@ -33,7 +37,7 @@ public class DimensionsAndCollector | |||
| private final SimpleCollector collector; | |||
| private final double conversionFactor; | |||
| private final double[] histogramBuckets; | |||
| private final Stopwatch updateTimer; | |||
| private final ConcurrentHashMap<List<String>, Stopwatch> labelValuesToStopwatch; | |||
There was a problem hiding this comment.
Did you observe any memory pressure with this change? Previously there were some concerns on the memory usage with this approach, so it would be nice if you could share any observations
There was a problem hiding this comment.
I haven't done any in depth memory profiling for this. I think it largely depends on how many unique label combinations there are. For something like queryId I'd imagine the memory usage would be higher under heavy query load. But for dimensions like taskId the memory usage should be much lower. I think it roughly gets offset anyway because we are clearing the label from the underlying SimpleCollector map now
| metric.getMillisSinceLastUpdate() | ||
| ); | ||
| metric.getCollector().clear(); | ||
| Set<List<String>> labelValuesCopy = new HashSet<>(metric.getLabelValuesToStopwatch().keySet()); |
There was a problem hiding this comment.
Is a copy of the map required here if the underlying map is already concurrent safe from using ConcurrentMap? We're also doing computeIfPresent() on the map entries whihh should be safe
There was a problem hiding this comment.
Yeah good point. Felt a little dangerous to be iterating over while removing keys, but I think the consistency guarantees of the ConcurrentMap make this safe
| "Metric should be expired", | ||
| testMetric.removeIfExpired(Arrays.asList("historical", "druid.test.cn", "historical1")) | ||
| ); | ||
| Assert.assertFalse( |
There was a problem hiding this comment.
Thanks for these tests!
From a functional PoV, it would be better to verify the end state of the collector, that the metrics are indeed cleared, rather than calling the internal function removeIfExpired(); as it is, I think we don't have coverage for cleanUpStaleMetrics().
What do you think of something like:
emitter.emit(metric1 - labelset1)emitter.emit(metric1 - labelset2)emitter.emit(metric1 - labelset1)
verify that metric1 - labelset1 is not cleared from the collector, whereas metric1 - labelset2 is cleared.
I think we could leverage the SimpleCollector's samples for verification. We could also pass BlockingExecutorService from the test to the prometheus emitter to control the executions (a bunch of tests do this to have better control of async executors). Please let me know what you think.
There was a problem hiding this comment.
Yeah agreed. Ideally I'd wanted to check SimpleCollector.children and ensure that the labels were removed by cleanUpStaleMetrics(). Unfortunately the SimpleCollector doesn't expose any function to access the children Map. I did verify via the debugger that the entry is removed from the child map after the TTL though
The tests do actually run cleanUpStaleMetrics() because the executor is created in emitter.start() and manual sleep times allow the scheduled executor to run
There was a problem hiding this comment.
This test case does roughly model the behavior you suggested:
Except I update metric1 - labelset2 instead
emitter.emit(metric1 - labelset1)
emitter.emit(metric1 - labelset2)
emitter.emit(metric1 - labelset2)
There was a problem hiding this comment.
Gotcha, the SimpleCollector exposes a collect() API to get the metrics collected/samples. Perhaps something like this should work?
List<Collector.MetricFamilySamples.Sample> samples = testMetric.getCollector().collect().get(0).samples;
// assert the labels & the sample size is 1 during a valid period
After the TTL period, the sample size would be 0? I think that would give us sufficient functionality validation on cleanUpStaleMetrics()
| "Metric should be expired", | ||
| testMetric.removeIfExpired(Arrays.asList("historical", "druid.test.cn", "historical1")) | ||
| ); | ||
| Assert.assertFalse( |
There was a problem hiding this comment.
Gotcha, the SimpleCollector exposes a collect() API to get the metrics collected/samples. Perhaps something like this should work?
List<Collector.MetricFamilySamples.Sample> samples = testMetric.getCollector().collect().get(0).samples;
// assert the labels & the sample size is 1 during a valid period
After the TTL period, the sample size would be 0? I think that would give us sufficient functionality validation on cleanUpStaleMetrics()
| | `druid.emitter.prometheus.addServiceAsLabel` | Flag to include the druid service name (e.g. `druid/broker`, `druid/coordinator`, etc.) as a prometheus label. | no | false | | ||
| | `druid.emitter.prometheus.pushGatewayAddress` | Pushgateway address. Required if using `pushgateway` strategy. | no | none | | ||
| | `druid.emitter.prometheus.flushPeriod` | When using the `pushgateway` strategy metrics are emitted every `flushPeriod` seconds. <br/>When using the `exporter` strategy this configures the metric TTL such that if the metric value is not updated within `flushPeriod` seconds then it will stop being emitted. Note that unique label combinations per metric are currently not subject to TTL expiration. It is recommended to set this to at least 3 * `scrape_interval`. | Required if `pushgateway` strategy is used, optional otherwise. | 15 seconds for `pushgateway` strategy. <br/>None for `exporter` strategy. | | ||
| | `druid.emitter.prometheus.flushPeriod` | When using the `pushgateway` strategy metrics are emitted every `flushPeriod` seconds. <br/>When using the `exporter` strategy this configures the metric TTL such that if the metric value is not updated within `flushPeriod` seconds then it will stop being emitted. It is recommended to set this to at least 3 * `scrape_interval`. | Required if `pushgateway` strategy is used, optional otherwise. | 15 seconds for `pushgateway` strategy. <br/>None for `exporter` strategy. | |
There was a problem hiding this comment.
Should we perhaps amend this to say that this reset also applies to unique label combinations?
abhishekrb19
left a comment
There was a problem hiding this comment.
Lgtm, some minor comments. Thanks, @aho135!
|
@aho135 when you get a chance, could you please pull the latest master to this branch? That should fix the IT failures due to openjdk version. |
|
The failing embedded test is unrelated to this PR. It's been flakey on a few other PRs, a retry didn't help: cc: @kfaraz |
|
Thanks, @abhishekrb19 ! I think this test flakes out every now and then. |
…8718) Follow up to #18689 to not track labels if the TTL isn't set Co-authored-by: Andrew Ho <[email protected]>
Fixes apache#14638 This is an extension of apache#18598 to enhance the TTL to track metrics at the label level instead of just the metric level. This should allow operators to configure medium cardinality dimensions on the prometheus-emitter without putting pressure on the Prometheus collector/sub-system.
…ache#18718) Follow up to apache#18689 to not track labels if the TTL isn't set Co-authored-by: Andrew Ho <[email protected]>
Fixes #14638
This is an extension of #18598 to enhance the TTL to track metrics at the label level instead of just the metric level.
Description
This PR enhances the current metric TTL tracking in the prometheus-emitter so that all unique label combinations are tracked instead of just the metric name. This is done using a ConcurrentHashMap which maintains a mapping of label values to a Stopwatch. The map needs to handle concurrent updates because there is a possibility for the TTL scheduler to remove a key from a map at the same that the key is updated during metric emission. These are handled through the two atomic operations removeIfExpired and resetLastUpdateTime
Release note
Enhances the prometheus-emitter metric TTL tracking to consider all label value combinations instead of just the metric name.
Key changed/added classes in this PR
PrometheusEmitterPrometheusEmitterTestDimensionsAndCollectorThis PR has: