Skip to content

receiver/prometheus: wire up direct pdata conversion mechanism#6421

Merged
bogdandrutu merged 37 commits into
open-telemetry:mainfrom
Aneurysm9:receiver-prometheus-wireUp-roundTrip
Dec 8, 2021
Merged

receiver/prometheus: wire up direct pdata conversion mechanism#6421
bogdandrutu merged 37 commits into
open-telemetry:mainfrom
Aneurysm9:receiver-prometheus-wireUp-roundTrip

Conversation

@Aneurysm9
Copy link
Copy Markdown
Member

Description: Adds the remaining pieces of a direct-to-pdata mechanism for the Prometheus receiver and a feature gate that allows selecting whether to use the legacy OpenCensus translations or the new direct route.

Link to tracking Issue: #4892

Testing: Unit tests exist for new code in the internal package. End-to-end tests for the receiver now run against configurations that use both the OpenCensus and pdata builders.

odeke-em and others added 16 commits September 20, 2021 23:35
…thout OpenCensus

Wire up and use the direct Prometheus->Pdata conversion end to end.
With this change the receiver will no longer need OpenCensus.

This change will involve more follow-ups that just migrate over the tests,
because we don't want a super bloated/massive PR.

Fixes open-telemetry#4892
Depends on PR open-telemetry/opentelemetry-collector#3694
Depends on PR open-telemetry/opentelemetry-collector#3695
Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
…ut-of-order exposition

Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
…ensus or pdata directly

Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
… issues with out-of-order exposition data

Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
…port paths

Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
… copy of opencensus translator during development

Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
@Aneurysm9 Aneurysm9 requested review from a team and dmitryax November 23, 2021 23:24
@Aneurysm9
Copy link
Copy Markdown
Member Author

@dashpole can you take a look at this?

Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
@Aneurysm9 Aneurysm9 force-pushed the receiver-prometheus-wireUp-roundTrip branch from 8382077 to 598f4a2 Compare November 24, 2021 00:19
Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
@Aneurysm9
Copy link
Copy Markdown
Member Author

Changes outside of receiver/prometheusreceiver are due to #6305 adding a CI check using porto but not ensuring that it was up-to-date with HEAD. If another PR that has to make the same changes to pass the lint check lands first then they should disappear from this PR.

… suite runtime

Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
BufferCount int `mapstructure:"buffer_count"`
UseStartTimeMetric bool `mapstructure:"use_start_time_metric"`
StartTimeMetricRegex string `mapstructure:"start_time_metric_regex"`
pdataDirect bool
Copy link
Copy Markdown
Contributor

@dashpole dashpole Nov 24, 2021

Choose a reason for hiding this comment

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

It is probably simpler just to use featuregate.IsEnabled(pdataPipelineGate.ID) where it is needed, rather than store it here. Is there a way to set feature gates during a test?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

There is only a global feature gate registry, so it can't necessarily be used in tests. The feature gate registry also has a lock, so we recommend that the feature status is checked once and stored if it is needed repeatedly. There is no mechanism for operators to change feature gate statuses during program runtime, so it should be safe to read this once and re-use it.

Comment thread receiver/prometheusreceiver/internal/metricsutil_pdata.go
Comment thread receiver/prometheusreceiver/internal/otlp_metricfamily.go Outdated
Comment thread receiver/prometheusreceiver/internal/otlp_metricfamily.go
Comment thread receiver/prometheusreceiver/internal/otlp_metrics_adjuster.go
Comment thread receiver/prometheusreceiver/internal/otlp_metrics_adjuster.go Outdated
Comment thread receiver/prometheusreceiver/internal/otlp_metricsbuilder.go Outdated
Comment thread receiver/prometheusreceiver/internal/otlp_transaction.go Outdated
Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
…tor-contrib into receiver-prometheus-wireUp-roundTrip

Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
Copy link
Copy Markdown
Contributor

@dashpole dashpole left a comment

Choose a reason for hiding this comment

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

looks great!

@dashpole
Copy link
Copy Markdown
Contributor

Huh, i'm sure why my approval checkmark isn't green. I'm in the code owners entry for the prometheus receiver

@Aneurysm9
Copy link
Copy Markdown
Member Author

Huh, i'm sure why my approval checkmark isn't green. I'm in the code owners entry for the prometheus receiver

Not sure what's going on there. It doesn't look like there are any files outside /receiver/prometheus/ so you should be an owner for all files in the review.

In any event, this should have more eyes on it. @anuraaga and @codeboten can I get a review?

@Aneurysm9
Copy link
Copy Markdown
Member Author

@dashpole apparently to be an effective code owner for review approval purposes you must have write access to the repository:

The people you choose as code owners must have write permissions for the repository.

Though that seems to be contradicted later in the same document:

A CODEOWNERS file uses a pattern that follows most of the same rules used in gitignore files, with some exceptions. The pattern is followed by one or more GitHub usernames or team names using the standard @username or @org/team-name format. Users must have read access to the repository and teams must have explicit write access, even if the team's members already have access.

v0v

Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
Copy link
Copy Markdown
Contributor

@anuraaga anuraaga left a comment

Choose a reason for hiding this comment

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

Intense PR did a first lightweight pass. I definitely wasn't expecting this much logic to convert between two metrics formats 😮

High level question, is this embedding functionality from cumulativetodelta? And if so, should it be?

https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/cumulativetodeltaprocessor/internal/tracking/metric.go

Also, things like metrics GC feel like they'd already be in the OTel SDK. Curious if there's no way to leverage the OTel SDK to do this conversion instead of what may be a lot of similar concepts. I wouldn't imagine there to be a huge penalty in efficiency, but not sure. And also just a wild idea that came to mind, it may not be applicable at all.

histogram.SetAggregationTemporality(pdata.MetricAggregationTemporalityCumulative)

destPointL := histogram.DataPoints()
// By default the AggregationTemporality is Cumulative until it'll be changed by the caller.
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.

From what I can tell this is referring to line 53

Copy link
Copy Markdown
Member Author

@Aneurysm9 Aneurysm9 Nov 30, 2021

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 either a vestige of a prior pdata API or a misunderstanding by the original author. The default aggregation temporality appears to be unspecified. See https://github.com/open-telemetry/opentelemetry-collector/blob/main/model/pdata/metrics.go#L256-L261. I am removing this comment.


func gaugeDistMetricPdata(name string, kvp []*kv, startTs pdata.Timestamp, points ...*pdata.HistogramDataPoint) *pdata.Metric {
hMetric := cumulativeDistMetricPdata(name, kvp, startTs, points...)
hMetric.Histogram().SetAggregationTemporality(pdata.MetricAggregationTemporalityDelta)
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.

From what I understand, all prometheus data is cumulative including gauge, but am likely wrong.

Wondering if there is a reference for the mapping, I searched around and found this doc with a broken link to a design doc. Would help reviewing this PR if there's information about the mapping somewhere

https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/openmetrics-guidelines.md

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm not actually sure. This is inherited test helper code that seems to be used to test adjustment of gauge histograms, which should never be encountered since the appender won't build them and the metric adjuster will short circuit when encountering a distribution with other than cumulative temporality. I think this can be removed without impacting functionality or test coverage.

Comment thread receiver/prometheusreceiver/internal/otlp_metricfamily.go
// Get the timeseriesinfo for the timeseries associated with the metric and label values.
func (tsm *timeseriesMapPdata) get(metric *pdata.Metric, kv pdata.AttributeMap) *timeseriesinfoPdata {
name := metric.Name()
sig := getTimeseriesSignaturePdata(name, kv)
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.

Maybe better to use a hashed object instead of string for map key

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 the cloudwatch exporter has a pattern using a Sorted object that could work

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm not sure I follow. The pdata.AttributeMap isn't comparable and can't be used as a map key. Since the labels are user-provided and arbitrary I'm not sure we could create a comparable object other than a string from them.

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.

There's this attribute.Distinct type used here for something similar I think recommended by jmacd a while back

I figure it has at least somewhat better performance than strings

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

That is from the Go SDK's attribute package, which is not in use here. These are pdata.AttributeMaps which would require similar conversion to utilize attribute.Distinct.

tsi = &timeseriesinfoPdata{}
tsm.tsiMap[sig] = tsi
}
tsm.mark = true
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.

Since gc() uses a lock to access tsi, I'd expect a lock here too, or otherwise atomic write. If it's already locked at a higher level, then a comment could help

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The lock is taken in *MetricsAdjusterPdata.AdjustMetrics(), which is the first exported method encountered when walking up the call stack from all invocations of this method. This is the same as the OpenCensus-based implementation in metrics_adjuster.go that has been in use by this receiver for a long time. I expect that to be the case for most of the comments on this implementation as most of them are in repeated code.

A reworking of this implementation is probably in order, particularly given the issue noted by @dashpole regarding targets that are scraped less frequently than the fixed GC interval. That should probably be handled in a separate issue.

jm.RLock()
defer jm.RUnlock()
if time.Since(jm.lastGC) > jm.gcInterval {
go jm.gc()
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.

If get can be called from multiple threads, I think this can cause gc to be called multiple times concurrently, I suspect that's not intended. Can use a comment if this case can't happen since a simple method like get we'd generally expect to not have any threading guarantees

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The time since the last GC is checked again inside gc() once a lock is obtained. It may be triggered multiple times, but it will still only run once per interval. That's what the comments in this method and in gc() are trying to convey.

}

func (jm *JobsMapPdata) get(job, instance string) *timeseriesMapPdata {
sig := job + ":" + instance
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.

Notice above using printf and here using concatenation, I think most code in collector would use printf

// Returns the total number of timeseries that had reset start times.
func (ma *MetricsAdjusterPdata) AdjustMetrics(metricL *pdata.MetricSlice) int {
resets := 0
ma.tsm.Lock()
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's somewhat mysterious we're locking on tsm here but not accessing it (presumably we are accessing through adjustMetric). Maybe a comment to ensure it was intended


default:
// this shouldn't happen
ma.logger.Info("Adjust - skipping unexpected point", zap.String("type", dataType.String()))
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.

Debug level probably

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is currently info in the OC metrics adjuster. If anything, since this is a situation that should never occur I might want to make this a warning.

Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
@Aneurysm9
Copy link
Copy Markdown
Member Author

@open-telemetry/collector-contrib-approvers can I get some more eyes on this? We have further work that is held up behind getting these changes landed. Thanks!

@alolita alolita added the ready to merge Code review completed; ready to merge by maintainers label Dec 3, 2021
@alolita
Copy link
Copy Markdown
Member

alolita commented Dec 3, 2021

Given @Aneurysm9 and @dashpole are codeowners for this component, can we get this PR merged please? Can the permissions be fixed @bogdandrutu @codeboten

There are other PRs (14 PRs - 9 tests and 5 bug fixes) that are blocked on this merge.

@bogdandrutu
Copy link
Copy Markdown
Member

@alolita I am not OK to merge this since a very good engineer and approver reviewed this (@anuraaga) and comments are not addressed.

@bogdandrutu bogdandrutu removed the ready to merge Code review completed; ready to merge by maintainers label Dec 3, 2021
Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
Copy link
Copy Markdown
Contributor

@anuraaga anuraaga left a comment

Choose a reason for hiding this comment

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

I hadn't realized at first that there isn't that much new code here, a lot is copied from the old translation path to allow a feature flag deprecation. So generally good with the code as it seems to be a relatively simple change on top of that - would appreciate some more code comments to verify lock expectations though

…djuster.

Signed-off-by: Anthony J Mirabella <a9@aneurysm9.com>
@Aneurysm9 Aneurysm9 added the ready to merge Code review completed; ready to merge by maintainers label Dec 8, 2021
@bogdandrutu bogdandrutu merged commit 22d15bb into open-telemetry:main Dec 8, 2021
@Aneurysm9 Aneurysm9 deleted the receiver-prometheus-wireUp-roundTrip branch December 8, 2021 18:52
@CatherineF-dev
Copy link
Copy Markdown

CatherineF-dev commented Aug 3, 2022

Hi, thanks for the great work which possibly reduces open-telemetry memory to 50% in a testing cluster. (v0.41 v.s. v0.40)

Want to cherry-pick this magic into previous versions. Do we know which small change in this PR is related to reduce agent memory?

@dashpole
Copy link
Copy Markdown
Contributor

dashpole commented Aug 3, 2022

@CatherineF-dev I'm afraid this isn't an easily cherry-pickable change, as it involved refactoring the entire receiver.

povilasv referenced this pull request in coralogix/opentelemetry-collector-contrib-old-fork Dec 19, 2022
…ess (#6421)

Unrevert #6267 and make it so that we never error out.

Co-authored-by: Alex Boten <alex@boten.ca>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready to merge Code review completed; ready to merge by maintainers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants