Skip to content

Implement metricTtl for prometheus-emitter#18598

Merged
abhishekrb19 merged 23 commits into
apache:masterfrom
aho135:prometheus-emitter-ttl
Oct 12, 2025
Merged

Implement metricTtl for prometheus-emitter#18598
abhishekrb19 merged 23 commits into
apache:masterfrom
aho135:prometheus-emitter-ttl

Conversation

@aho135

@aho135 aho135 commented Oct 4, 2025

Copy link
Copy Markdown
Contributor

Partially addresses #14638. This implementation will stop emitting the metric if it has not been updated within the TTL. This does not account for scenarios where the metric is being emitted but with different label values. This PR can be enhanced in the future to track all label combinations

Description

This PR adds flushPeriod for the exporter strategy which defines the amount of time a metric can exist without being updated before the value is cleared. Prometheus is unique amongst emitters as it utilizes a pull model instead of a push model. This leads to an issue with stale metrics. For example, if the active Coordinator leader is emitting ingest/kafka/lag but then a leader election change occurs, the Coordinator would continue to emit that stale metric value until it is restarted.

An alternative approach is to reset the metric values within DruidCoordinator. There we have access to the ServiceEmitter. We could update the Emitter interface to have a clear() method that is called in stopBeingLeader. For most emitters though, this method would do nothing since they use a push model. Instead of muddying the interface I thought it better to implement this in prometheus-emitter itself

Release note

Adds flushPeriod to the exporter strategy in the prometheus-emitter which allows the operator to specify the TTL for a metric. If a metric value has not been updated within the TTL period then the value will stop being emitted.


Key changed/added classes in this PR
  • PrometheusEmitter
  • PrometheusEmitterConfig
  • DimensionsAndCollector
  • PrometheusEmitterConfigTest
  • PrometheusEmitterTest

This PR has:

  • been self-reviewed.
  • added documentation for new or modified features or behaviors.
  • a release note entry in the PR description.
  • added Javadocs for most classes and all non-trivial methods. Linked related entities via Javadoc links.
  • added comments explaining the "why" and the intent of the code wherever would not be obvious for an unfamiliar reader.
  • added unit tests or modified existing tests to cover new code paths, ensuring the threshold for code coverage is met.
  • added integration tests.
  • been tested in a test Druid cluster.

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

Thanks for the fix, @aho135 ! I have left some minor suggestions.

Could you share some screenshots where we can see stale metrics being reported?

@aho135

aho135 commented Oct 7, 2025

Copy link
Copy Markdown
Contributor Author

Thanks for the fix, @aho135 ! I have left some minor suggestions.

Could you share some screenshots where we can see stale metrics being reported?

Thanks for the review @kfaraz!

This is the ingest/kafka/partitionlag metric being emitted by 2 Coordinators. The active one is emitting the proper metric (0) but the previous Coordinator is emitting a stale metric that doesn't get reset until we manually restart it. The scenario we run into is that if a leader change occurs while there is lag on a topic then the old Coordinator continues to emit that stale lag metric. We have some alerting set up for lag, so the stale value ends up triggering false alarms.

Screenshot 2025-10-06 at 1 59 05 PM

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

Looks good, left some final suggestions.

Comment thread docs/development/extensions-contrib/prometheus.md Outdated

public boolean isExpired(long ttlSeconds)
{
return updateTimer.hasElapsed(new Duration(TimeUnit.SECONDS.toMillis(ttlSeconds)));

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.

Suggested change
return updateTimer.hasElapsed(new Duration(TimeUnit.SECONDS.toMillis(ttlSeconds)));
return updateTimer.hasElapsed(Duration.standardSeconds(ttlSeconds));

// Start TTL scheduler if TTL is configured
if (config.getFlushPeriod() != null) {
exec = ScheduledExecutors.fixed(1, "PrometheusTTLExecutor-%s");
// Check TTL every minute

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.

I don't think this comment is valid anymore, please remove it.

config.getFlushPeriod(),
TimeUnit.SECONDS
);
log.info("Started TTL scheduler with TTL of %d seconds", config.getFlushPeriod());

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.

Suggested change
log.info("Started TTL scheduler with TTL of %d seconds", config.getFlushPeriod());
log.info("Started TTL scheduler with TTL of [%d] seconds.", config.getFlushPeriod());

updateTimer.start();
}

public long getTimeSinceLastUpdate()

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.

Suggested change
public long getTimeSinceLastUpdate()
public long getMillisSinceLastUpdate()

Comment on lines +453 to +454
Assert.assertFalse("Metric should not be expired initially",
testMetric.isExpired(flushPeriod));

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.

Style: cleaner to put the first arg in a new line. Please use the same style in other places too.

Suggested change
Assert.assertFalse("Metric should not be expired initially",
testMetric.isExpired(flushPeriod));
Assert.assertFalse(
"Metric should not be expired initially",
testMetric.isExpired(flushPeriod)
);

Comment on lines +292 to +294
log.debug("Metric [%s] has expired (last updated %d ms ago)",
entry.getKey(),
entry.getValue().getTimeSinceLastUpdate());

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.

Style:

Suggested change
log.debug("Metric [%s] has expired (last updated %d ms ago)",
entry.getKey(),
entry.getValue().getTimeSinceLastUpdate());
log.debug(
"Metric[%s] has expired as it was last updated [%d] ms ago.",
entry.getKey(),
entry.getValue().getTimeSinceLastUpdate()
);

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

Thanks @aho135 for the feature, this is very useful! I've left some questions on the TTL-based tracking and some misc suggestions.

Comment on lines +100 to +102
if (Objects.nonNull(flushPeriod)) {
Preconditions.checkArgument(flushPeriod > 0, "flushPeriod must be greater than 0.");
}

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.

  • Would be good to include the invalid flushPeriod in the error message
  • Looks like the other validation uses this to, could use DruidException for newer ones. Something like:
throw DruidException.forPersona(DruidException.Persona.OPERATOR)
                          .ofCategory(DruidException.Category.INVALID_INPUT)
                          .build(
                                  "Invalid value for 'druid.emitter.prometheus.flushPeriod'[%s] specified, flushPeriod must be > 0 if specified.",
                                  waitForShutdownDelay
                              )
                          );

| `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` | Emit metrics to Pushgateway every `flushPeriod` seconds. Required if `pushgateway` strategy is used. | no | 15 |
| `druid.emitter.prometheus.flushPeriod` | If strategy is `pushgateway`, emits metrics every `flushPeriod` seconds. Required if `pushgateway` strategy is used. If strategy is `exporter`, configures the metric ttl such that if the metric value is not updated within the `flushPeriod` then it will stop being emitted. Optional if `exporter` strategy is used. | no | 15 seconds for `pushgateway` strategy. None for `exporter` strategy |

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.

Thanks for the docs! Minor suggestion:

Suggested change
| `druid.emitter.prometheus.flushPeriod` | If strategy is `pushgateway`, emits metrics every `flushPeriod` seconds. Required if `pushgateway` strategy is used. If strategy is `exporter`, configures the metric ttl such that if the metric value is not updated within the `flushPeriod` then it will stop being emitted. Optional if `exporter` strategy is used. | no | 15 seconds for `pushgateway` strategy. None for `exporter` strategy |
| `druid.emitter.prometheus.flushPeriod` | If strategy is `pushgateway`, metrics are emitted every `flushPeriod` seconds. Required if `pushgateway` strategy is used. If strategy is `exporter`, this configures the metric TTL such that if the metric value is not updated within the `flushPeriod` seconds, then it will stop being emitted. Optional if `exporter` strategy is used. | no | 15 seconds for `pushgateway` strategy. None for `exporter` strategy |

Also, do you have guidance on what an operator should set this value to for TTL eviction (perhaps something as a function of emission period)?

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.

Yeah, good point. I guess it would have to account for the scraping frequency too.
So, a good value would be something like 3 * scrape period or 3 * emission period, whichever is larger.

// Start TTL scheduler if TTL is configured
if (config.getFlushPeriod() != null) {
exec = ScheduledExecutors.fixed(1, "PrometheusTTLExecutor-%s");
// Check TTL every minute

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.

I think this comment is stale?


Map<String, DimensionsAndCollector> map = metrics.getRegisteredMetrics();
for (Map.Entry<String, DimensionsAndCollector> entry : map.entrySet()) {
if (entry.getValue().isExpired(config.getFlushPeriod())) {

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.

Extract entry.getValue() to a variable and reuse since it's used a few times in this loop :)


Map<String, DimensionsAndCollector> map = metrics.getRegisteredMetrics();
for (Map.Entry<String, DimensionsAndCollector> entry : map.entrySet()) {
if (entry.getValue().isExpired(config.getFlushPeriod())) {

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.

DimensionsAndCollector could accept the flush period directly, initialized from the Prometheus config that’s plumbed through Metrics. Then this code could become if (entry.getValue().isExpired()) {} where this fixed config isn't redundantly passed in

log.debug("Metric [%s] has expired (last updated %d ms ago)",
entry.getKey(),
entry.getValue().getTimeSinceLastUpdate());
entry.getValue().getCollector().clear();

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.

Is this clear() operation on SimpleCollector thread-safe? Doing a quick scan of the docs suggest that the client libraries must be thread-safe, but wanted to confirm that clearing can concurrently happen with the metric updates in emitMetric()

Side note: I think it would be better to move some of these methods inside DimensionsAndCollector to avoid abstraction leaks. But that refactor can be done later since it'd also require moving the metric updates from emitMetric() into this class.

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.

Yup, confirmed that the Map getting cleared is a ConcurrentHashMap

Comment on lines +67 to +68
updateTimer.reset();
updateTimer.start();

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.

nit: can be simplified to updateTimer.restart();

config.getFlushPeriod(),
TimeUnit.SECONDS
);
log.info("Started TTL scheduler with TTL of %d seconds", config.getFlushPeriod());

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.

Suggested change
log.info("Started TTL scheduler with TTL of %d seconds", config.getFlushPeriod());
log.info("Started TTL scheduler with TTL of [%d] seconds", config.getFlushPeriod());

log.debug("Metric [%s] has expired (last updated %d ms ago)",
entry.getKey(),
entry.getValue().getTimeSinceLastUpdate());
entry.getValue().getCollector().clear();

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.

It looks like clear() removes all children for the metric. Do we instead want to use remove() to target a specific time series by specifying the corresponding set of label values?

Without that, metrics that track things like task ID or query ID, for example, task/run/time or query/wait/time mentioned in #14638 wouldn’t get evicted if they’re being updated frequently.

When we call metric.resetLastUpdateTime(), should we pass the appropriate label set and track it? Then in cleanUpStaleMetrics(), we could check the label sets and clear only the stale time series rather than clearing all of them or none at all. What do you think?

@kfaraz kfaraz Oct 7, 2025

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.

Hmm, I suppose removing the children for specific labels would be a more complete solution.
But it could blow up the memory usage pretty fast since we would now be tracking the last updated time for every unique set of label values for every metric.

I think the issue this PR is trying to address is that if a certain metric stops being reported by a service
altogether (irrespective of label values), it should not remain in the collector anymore. e.g. in leadership changes.

Also, the staleness concern should really only apply to gauge metrics. So if timer (histogram) metrics like task/run/time and query/wait/time are still being updated by the current service (for any label), we should continue emitting them. Getting rid of the expired labels becomes more of a mem usage concern than anything else.

But with gauge metrics like say segment/size (which represents bytes of segment available on a historical for a given datasource), I agree that the story is different.
If all segments of a datasource are removed from a historical, we should definitely stop reporting segment/size for that datasource.

But I would suggest we add a comment and postpone the full solution for gauge metrics for a later PR.
Ideally, we would want to write up an embedded test (using Prometheus testcontainer) and verify the behaviour of gauge metrics in such cases and then proceed with a solution.

What are your thoughts, @abhishekrb19 , @aho135 ?

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.

Hmm, I suppose removing the children for specific labels would be a more complete solution.
But it could blow up the memory usage pretty fast since we would now be tracking the last updated time for every unique set of label values for every metric.

Yeah, good callout on the memory usage when tracking every unique combination of label sets per metric. Prometheus isn’t designed for very high cardinality labels, and the Prometheus SimpleCollector in the java library already tracks the unique label sets (children) internally. On the extension side, we’d only be dealing with a subset of those children, since we’d remove the ephemeral “stale” entries anyway (if there's a way to leverage the collector’s internal state somehow, that would be even better). So I’m not too concerned about the additional memory this would require, though it might still be worth validating in a cluster with some labels with reasonable cardinality and load.

I’m also okay with the suggestion to start without labels for now and revisit this (or another solution) once we have a better understanding of the additional memory overhead it might introduce. The current implementation should remain extensible enough to support label tracking in the future if needed. We can keep #14638
open and call out in the documentation that the TTL expiration only applies to certain metric types and not to individual timeseries'.
@aho135 @kfaraz what do you 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.

Yeah, I was also concerned about memory usage. Especially where folks are adding queryId as a dimension like in #14638.

Sounds good to me to tackle the label problem in a separate PR. We can say that this PR specifically targets resetting the metric value in the leader election scenario

Comment on lines +477 to +480
ServiceMetricEvent event = ServiceMetricEvent.builder()
.setMetric("segment/loadQueue/count", 10)
.build(ImmutableMap.of("service", "historical", "host", "druid.test.cn"));
emitter.emit(event);

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.

Could you add the same metric with different label values so that it actrs as a different timeseries and then verify that one exists while the other one has expired?

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 guess we can defer this test since we're not considering labels when clearing in this PR

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

LGTM, some minor comments. Thanks, @aho135!

private final Integer ttlSeconds;

DimensionsAndCollector(String[] dimensions, SimpleCollector collector, double conversionFactor, double[] histogramBuckets)
DimensionsAndCollector(String[] dimensions, SimpleCollector collector, double conversionFactor, double[] histogramBuckets, Integer ttlSeconds)

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.

Suggested change
DimensionsAndCollector(String[] dimensions, SimpleCollector collector, double conversionFactor, double[] histogramBuckets, Integer ttlSeconds)
DimensionsAndCollector(String[] dimensions, SimpleCollector collector, double conversionFactor, double[] histogramBuckets, @Nullable Integer ttlSeconds)

}

public Metrics(String namespace, String path, boolean isAddHostAsLabel, boolean isAddServiceAsLabel, Map<String, String> extraLabels)
public Metrics(String namespace, String path, boolean isAddHostAsLabel, boolean isAddServiceAsLabel, Map<String, String> extraLabels, Integer ttlSeconds)

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.

Suggested change
public Metrics(String namespace, String path, boolean isAddHostAsLabel, boolean isAddServiceAsLabel, Map<String, String> extraLabels, Integer ttlSeconds)
public Metrics(String namespace, String path, boolean isAddHostAsLabel, boolean isAddServiceAsLabel, Map<String, String> extraLabels, @Nullable Integer ttlSeconds)

this.conversionFactor = conversionFactor;
this.histogramBuckets = histogramBuckets;
this.updateTimer = Stopwatch.createStarted();
this.ttlSeconds = ttlSeconds;

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.

nit: you could assign Duration.standardSeconds(ttlSeconds) in the constructor itself

Comment thread website/.spelling
tableSpec
TableSpec

- ../docs/development/extensions-contrib/prometheus.md

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.

I think this isn't needed?

Suggested change
- ../docs/development/extensions-contrib/prometheus.md

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 was just copying the current format. Ex: https://github.com/apache/druid/blob/master/website/.spelling#L2455

I can remove this if it's not needed though

| `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` | Emit metrics to Pushgateway every `flushPeriod` seconds. Required if `pushgateway` strategy is used. | no | 15 |
| `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. |

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.

An additional note on the behavior for clarity:

Suggested change
| `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. |
| `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. |

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

LGTM

Left some nitpicks which are suggestions for later clean up, not needed in this PR.

private final double conversionFactor;
private final double[] histogramBuckets;
private final Stopwatch updateTimer;
private final Integer ttlSeconds;

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.

Nit: This can be a Duration to avoid repeated created of a Duration object in isExpired.

Comment on lines +73 to +78
@@ -74,7 +74,8 @@ public PrometheusEmitter(PrometheusEmitterConfig config)
config.getDimensionMapPath(),
config.isAddHostAsLabel(),
config.isAddServiceAsLabel(),
config.getExtraLabels()
config.getExtraLabels(),
config.getFlushPeriod()

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.

Nit: we could pass the entire config object to the constructor rather than these 6 args.

Comment on lines +100 to +111
if (Objects.nonNull(flushPeriod)) {
if (flushPeriod <= 0) {
throw DruidException.forPersona(DruidException.Persona.OPERATOR)
.ofCategory(DruidException.Category.INVALID_INPUT)
.build(
StringUtils.format(
"Invalid value for flushPeriod[%s] specified, flushPeriod must be > 0.",
flushPeriod
)
);
}
}

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.

Nit: seems like this part can be commoned out.

@aho135

aho135 commented Oct 11, 2025

Copy link
Copy Markdown
Contributor Author

Thanks for the review @abhishekrb19 @kfaraz I went ahead and made those updates

Comment on lines +81 to +83
if (ttlSeconds == null) {
log.error("Invalid usage of isExpired(), TTL has not been set");
return false;

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 condition should never trigger in production. if it does, it’s a bug, so we could throw DruidException.defensive() to indicate invalid usage for developers. Given it’s configured correctly now, this should be fine and we can update this later.

@abhishekrb19
abhishekrb19 merged commit 13b5492 into apache:master Oct 12, 2025
60 checks passed
@abhishekrb19 abhishekrb19 added this to the 35.0.0 milestone Oct 14, 2025
cecemei pushed a commit to cecemei/druid that referenced this pull request Oct 17, 2025
…er (apache#18598)

Partially addresses apache#14638. This implementation adds flushPeriod to the exporter strategy in the prometheus-emitter, which when configured, will stop emitting the metric if it has not been updated within the TTL. This does not account for scenarios where the metric is being emitted but with different label values. This PR can be enhanced in the future to track all label combinations.
cecemei pushed a commit that referenced this pull request Oct 21, 2025
…er (#18598)

Partially addresses #14638. This implementation adds flushPeriod to the exporter strategy in the prometheus-emitter, which when configured, will stop emitting the metric if it has not been updated within the TTL. This does not account for scenarios where the metric is being emitted but with different label values. This PR can be enhanced in the future to track all label combinations.
@aho135 aho135 mentioned this pull request Oct 23, 2025
10 tasks
abhishekrb19 pushed a commit that referenced this pull request Nov 4, 2025
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. This should allow operators to configure medium cardinality dimensions on the prometheus-emitter without putting pressure on the Prometheus collector/sub-system.
riovic918data pushed a commit to riovic918data/druid that referenced this pull request Jun 12, 2026
…er (apache#18598)

Partially addresses apache#14638. This implementation adds flushPeriod to the exporter strategy in the prometheus-emitter, which when configured, will stop emitting the metric if it has not been updated within the TTL. This does not account for scenarios where the metric is being emitted but with different label values. This PR can be enhanced in the future to track all label combinations.
riovic918data pushed a commit to riovic918data/druid that referenced this pull request Jun 12, 2026
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.
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