promql: avoid unnecessary Metric.Get() calls in functions.go (#17676)
Some checks are pending
buf.build / lint and publish (push) Waiting to run
CI / Go tests (push) Waiting to run
CI / More Go tests (push) Waiting to run
CI / Go tests with previous Go version (push) Waiting to run
CI / UI tests (push) Waiting to run
CI / Go tests on Windows (push) Waiting to run
CI / Mixins tests (push) Waiting to run
CI / Build Prometheus for common architectures (push) Waiting to run
CI / Build Prometheus for all architectures (push) Waiting to run
CI / Report status of build Prometheus for all architectures (push) Blocked by required conditions
CI / Check generated parser (push) Waiting to run
CI / golangci-lint (push) Waiting to run
CI / fuzzing (push) Waiting to run
CI / codeql (push) Waiting to run
CI / Publish main branch artifacts (push) Blocked by required conditions
CI / Publish release artefacts (push) Blocked by required conditions
CI / Publish UI on npm Registry (push) Blocked by required conditions
Scorecards supply-chain security / Scorecards analysis (push) Waiting to run

Moved some Metric.Get() calls in PromQL functions to avoid unnecessary label extraction.
In many cases, this work was done to extract metric name, and was only used if annotations were emitted.

In the same go I also replaced labels.MetricName with model.MetricNameLabel, since the former was deprecated.

Signed-off-by: Vilius Pranckaitis <vpranckaitis@gmail.com>
This commit is contained in:
Vilius Pranckaitis 2026-01-08 13:58:05 +02:00 committed by GitHub
parent da254fd680
commit 6a81e4441e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 45 additions and 51 deletions

View file

@ -200,9 +200,8 @@ func extrapolatedRate(vals Matrix, args parser.Expressions, enh *EvalNodeHelper,
// We need either at least two Histograms and no Floats, or at least two
// Floats and no Histograms to calculate a rate. Otherwise, drop this
// Vector element.
metricName := samples.Metric.Get(labels.MetricName)
if len(samples.Histograms) > 0 && len(samples.Floats) > 0 {
return enh.Out, annos.Add(annotations.NewMixedFloatsHistogramsWarning(metricName, args[0].PositionRange()))
return enh.Out, annos.Add(annotations.NewMixedFloatsHistogramsWarning(getMetricName(samples.Metric), args[0].PositionRange()))
}
switch {
@ -211,7 +210,7 @@ func extrapolatedRate(vals Matrix, args parser.Expressions, enh *EvalNodeHelper,
firstT = samples.Histograms[0].T
lastT = samples.Histograms[numSamplesMinusOne].T
var newAnnos annotations.Annotations
resultHistogram, newAnnos = histogramRate(samples.Histograms, isCounter, metricName, args[0].PositionRange())
resultHistogram, newAnnos = histogramRate(samples.Histograms, isCounter, samples.Metric, args[0].PositionRange())
annos.Merge(newAnnos)
if resultHistogram == nil {
// The histograms are not compatible with each other.
@ -305,7 +304,7 @@ func extrapolatedRate(vals Matrix, args parser.Expressions, enh *EvalNodeHelper,
// points[0] to be a histogram. It returns nil if any other Point in points is
// not a histogram, and a warning wrapped in an annotation in that case.
// Otherwise, it returns the calculated histogram and an empty annotation.
func histogramRate(points []HPoint, isCounter bool, metricName string, pos posrange.PositionRange) (*histogram.FloatHistogram, annotations.Annotations) {
func histogramRate(points []HPoint, isCounter bool, labels labels.Labels, pos posrange.PositionRange) (*histogram.FloatHistogram, annotations.Annotations) {
var (
prev = points[0].H
usingCustomBuckets = prev.UsesCustomBuckets()
@ -314,14 +313,14 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra
)
if last == nil {
return nil, annos.Add(annotations.NewMixedFloatsHistogramsWarning(metricName, pos))
return nil, annos.Add(annotations.NewMixedFloatsHistogramsWarning(getMetricName(labels), pos))
}
// We check for gauge type histograms in the loop below, but the loop
// below does not run on the first and last point, so check the first
// and last point now.
if isCounter && (prev.CounterResetHint == histogram.GaugeType || last.CounterResetHint == histogram.GaugeType) {
annos.Add(annotations.NewNativeHistogramNotCounterWarning(metricName, pos))
annos.Add(annotations.NewNativeHistogramNotCounterWarning(getMetricName(labels), pos))
}
// Null out the 1st sample if there is a counter reset between the 1st
@ -338,7 +337,7 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra
}
if last.UsesCustomBuckets() != usingCustomBuckets {
return nil, annos.Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, pos))
return nil, annos.Add(annotations.NewMixedExponentialCustomHistogramsWarning(getMetricName(labels), pos))
}
// First iteration to find out two things:
@ -348,19 +347,19 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra
for _, currPoint := range points[1 : len(points)-1] {
curr := currPoint.H
if curr == nil {
return nil, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(metricName, pos))
return nil, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(getMetricName(labels), pos))
}
if !isCounter {
continue
}
if curr.CounterResetHint == histogram.GaugeType {
annos.Add(annotations.NewNativeHistogramNotCounterWarning(metricName, pos))
annos.Add(annotations.NewNativeHistogramNotCounterWarning(getMetricName(labels), pos))
}
if curr.Schema < minSchema {
minSchema = curr.Schema
}
if curr.UsesCustomBuckets() != usingCustomBuckets {
return nil, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, pos))
return nil, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(getMetricName(labels), pos))
}
}
@ -371,7 +370,7 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra
_, _, nhcbBoundsReconciled, err := h.Sub(prev)
if err != nil {
if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) {
return nil, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, pos))
return nil, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(getMetricName(labels), pos))
}
}
if nhcbBoundsReconciled {
@ -387,7 +386,7 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra
_, _, nhcbBoundsReconciled, err := h.Add(prev)
if err != nil {
if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) {
return nil, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, pos))
return nil, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(getMetricName(labels), pos))
}
}
if nhcbBoundsReconciled {
@ -397,7 +396,7 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra
prev = curr
}
} else if points[0].H.CounterResetHint != histogram.GaugeType || points[len(points)-1].H.CounterResetHint != histogram.GaugeType {
annos.Add(annotations.NewNativeHistogramNotGaugeWarning(metricName, pos))
annos.Add(annotations.NewNativeHistogramNotGaugeWarning(getMetricName(labels), pos))
}
h.CounterResetHint = histogram.GaugeType
@ -431,10 +430,9 @@ func funcIdelta(_ []Vector, matrixVals Matrix, args parser.Expressions, enh *Eva
func instantValue(vals Matrix, args parser.Expressions, out Vector, isRate bool) (Vector, annotations.Annotations) {
var (
samples = vals[0]
metricName = samples.Metric.Get(labels.MetricName)
ss = make([]Sample, 0, 2)
annos annotations.Annotations
samples = vals[0]
ss = make([]Sample, 0, 2)
annos annotations.Annotations
)
// No sense in trying to compute a rate without at least two points. Drop
@ -500,11 +498,11 @@ func instantValue(vals Matrix, args parser.Expressions, out Vector, isRate bool)
resultSample.H = ss[1].H.Copy()
// irate should only be applied to counters.
if isRate && (ss[1].H.CounterResetHint == histogram.GaugeType || ss[0].H.CounterResetHint == histogram.GaugeType) {
annos.Add(annotations.NewNativeHistogramNotCounterWarning(metricName, args.PositionRange()))
annos.Add(annotations.NewNativeHistogramNotCounterWarning(getMetricName(samples.Metric), args.PositionRange()))
}
// idelta should only be applied to gauges.
if !isRate && (ss[1].H.CounterResetHint != histogram.GaugeType || ss[0].H.CounterResetHint != histogram.GaugeType) {
annos.Add(annotations.NewNativeHistogramNotGaugeWarning(metricName, args.PositionRange()))
annos.Add(annotations.NewNativeHistogramNotGaugeWarning(getMetricName(samples.Metric), args.PositionRange()))
}
if !isRate || !ss[1].H.DetectReset(ss[0].H) {
// This subtraction may deliberately include conflicting
@ -513,7 +511,7 @@ func instantValue(vals Matrix, args parser.Expressions, out Vector, isRate bool)
// conflicting counter resets is ignored here.
_, _, nhcbBoundsReconciled, err := resultSample.H.Sub(ss[0].H)
if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) {
return out, annos.Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, args.PositionRange()))
return out, annos.Add(annotations.NewMixedExponentialCustomHistogramsWarning(getMetricName(samples.Metric), args.PositionRange()))
}
if nhcbBoundsReconciled {
annos.Add(annotations.NewMismatchedCustomBucketsHistogramsInfo(args.PositionRange(), annotations.HistogramSub))
@ -523,7 +521,7 @@ func instantValue(vals Matrix, args parser.Expressions, out Vector, isRate bool)
resultSample.H.Compact(0)
default:
// Mix of a float and a histogram.
return out, annos.Add(annotations.NewMixedFloatsHistogramsWarning(metricName, args.PositionRange()))
return out, annos.Add(annotations.NewMixedFloatsHistogramsWarning(getMetricName(samples.Metric), args.PositionRange()))
}
if isRate {
@ -565,7 +563,6 @@ func calcTrendValue(i int, tf, s0, s1, b float64) float64 {
// https://en.wikipedia.org/wiki/Exponential_smoothing .
func funcDoubleExponentialSmoothing(vectorVals []Vector, matrixVal Matrix, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
samples := matrixVal[0]
metricName := samples.Metric.Get(labels.MetricName)
// The smoothing factor argument.
sf := vectorVals[0][0].F
@ -586,7 +583,7 @@ func funcDoubleExponentialSmoothing(vectorVals []Vector, matrixVal Matrix, args
if l < 2 {
// Annotate mix of float and histogram.
if l == 1 && len(samples.Histograms) > 0 {
return enh.Out, annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange()))
return enh.Out, annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange()))
}
return enh.Out, nil
}
@ -609,7 +606,7 @@ func funcDoubleExponentialSmoothing(vectorVals []Vector, matrixVal Matrix, args
s0, s1 = s1, x+y
}
if len(samples.Histograms) > 0 {
return append(enh.Out, Sample{F: s1}), annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange()))
return append(enh.Out, Sample{F: s1}), annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange()))
}
return append(enh.Out, Sample{F: s1}), nil
}
@ -795,8 +792,7 @@ func aggrHistOverTime(matrixVal Matrix, enh *EvalNodeHelper, aggrFn func(Series)
func funcAvgOverTime(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
firstSeries := matrixVal[0]
if len(firstSeries.Floats) > 0 && len(firstSeries.Histograms) > 0 {
metricName := firstSeries.Metric.Get(labels.MetricName)
return enh.Out, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(metricName, args[0].PositionRange()))
return enh.Out, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(getMetricName(firstSeries.Metric), args[0].PositionRange()))
}
// For the average calculation of histograms, we use incremental mean
// calculation without the help of Kahan summation (but this should
@ -871,9 +867,8 @@ func funcAvgOverTime(_ []Vector, matrixVal Matrix, args parser.Expressions, enh
return mean, nil
})
if err != nil {
metricName := firstSeries.Metric.Get(labels.MetricName)
if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) {
return enh.Out, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, args[0].PositionRange()))
return enh.Out, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(getMetricName(firstSeries.Metric), args[0].PositionRange()))
}
}
return vec, annos
@ -980,8 +975,7 @@ func funcMadOverTime(_ []Vector, matrixVal Matrix, args parser.Expressions, enh
return enh.Out, nil
}
if len(samples.Histograms) > 0 {
metricName := samples.Metric.Get(labels.MetricName)
annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange()))
annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange()))
}
return aggrOverTime(matrixVal, enh, func(s Series) float64 {
values := make(vectorByValueHeap, 0, len(s.Floats))
@ -1059,8 +1053,7 @@ func compareOverTime(matrixVal Matrix, args parser.Expressions, enh *EvalNodeHel
return enh.Out, nil
}
if len(samples.Histograms) > 0 {
metricName := samples.Metric.Get(labels.MetricName)
annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange()))
annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange()))
}
return aggrOverTime(matrixVal, enh, func(s Series) float64 {
maxVal := s.Floats[0].F
@ -1096,8 +1089,7 @@ func funcMinOverTime(_ []Vector, matrixVals Matrix, args parser.Expressions, enh
func funcSumOverTime(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
firstSeries := matrixVal[0]
if len(firstSeries.Floats) > 0 && len(firstSeries.Histograms) > 0 {
metricName := firstSeries.Metric.Get(labels.MetricName)
return enh.Out, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(metricName, args[0].PositionRange()))
return enh.Out, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(getMetricName(firstSeries.Metric), args[0].PositionRange()))
}
if len(firstSeries.Floats) == 0 {
// The passed values only contain histograms.
@ -1138,9 +1130,8 @@ func funcSumOverTime(_ []Vector, matrixVal Matrix, args parser.Expressions, enh
return sum, nil
})
if err != nil {
metricName := firstSeries.Metric.Get(labels.MetricName)
if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) {
return enh.Out, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, args[0].PositionRange()))
return enh.Out, annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(getMetricName(firstSeries.Metric), args[0].PositionRange()))
}
}
return vec, annos
@ -1170,8 +1161,7 @@ func funcQuantileOverTime(vectorVals []Vector, matrixVal Matrix, args parser.Exp
annos.Add(annotations.NewInvalidQuantileWarning(q, args[0].PositionRange()))
}
if len(el.Histograms) > 0 {
metricName := el.Metric.Get(labels.MetricName)
annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange()))
annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(el.Metric), args[0].PositionRange()))
}
values := make(vectorByValueHeap, 0, len(el.Floats))
for _, f := range el.Floats {
@ -1187,8 +1177,7 @@ func varianceOverTime(matrixVal Matrix, args parser.Expressions, enh *EvalNodeHe
return enh.Out, nil
}
if len(samples.Histograms) > 0 {
metricName := samples.Metric.Get(labels.MetricName)
annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange()))
annos.Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange()))
}
return aggrOverTime(matrixVal, enh, func(s Series) float64 {
var count float64
@ -1478,14 +1467,13 @@ func linearRegression(samples []FPoint, interceptTime int64) (slope, intercept f
// === deriv(node parser.ValueTypeMatrix) (Vector, Annotations) ===
func funcDeriv(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
samples := matrixVal[0]
metricName := samples.Metric.Get(labels.MetricName)
// No sense in trying to compute a derivative without at least two float points.
// Drop this Vector element.
if len(samples.Floats) < 2 {
// Annotate mix of float and histogram.
if len(samples.Floats) == 1 && len(samples.Histograms) > 0 {
return enh.Out, annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange()))
return enh.Out, annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange()))
}
return enh.Out, nil
}
@ -1495,7 +1483,7 @@ func funcDeriv(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *EvalN
// https://github.com/prometheus/prometheus/issues/2674
slope, _ := linearRegression(samples.Floats, samples.Floats[0].T)
if len(samples.Histograms) > 0 {
return append(enh.Out, Sample{F: slope}), annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange()))
return append(enh.Out, Sample{F: slope}), annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange()))
}
return append(enh.Out, Sample{F: slope}), nil
}
@ -1504,21 +1492,20 @@ func funcDeriv(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *EvalN
func funcPredictLinear(vectorVals []Vector, matrixVal Matrix, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
samples := matrixVal[0]
duration := vectorVals[0][0].F
metricName := samples.Metric.Get(labels.MetricName)
// No sense in trying to predict anything without at least two float points.
// Drop this Vector element.
if len(samples.Floats) < 2 {
// Annotate mix of float and histogram.
if len(samples.Floats) == 1 && len(samples.Histograms) > 0 {
return enh.Out, annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange()))
return enh.Out, annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange()))
}
return enh.Out, nil
}
slope, intercept := linearRegression(samples.Floats, enh.Ts)
if len(samples.Histograms) > 0 {
return append(enh.Out, Sample{F: slope*duration + intercept}), annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(metricName, args[0].PositionRange()))
return append(enh.Out, Sample{F: slope*duration + intercept}), annotations.New().Add(annotations.NewHistogramIgnoredInMixedRangeInfo(getMetricName(samples.Metric), args[0].PositionRange()))
}
return append(enh.Out, Sample{F: slope*duration + intercept}), nil
}
@ -1624,7 +1611,7 @@ func funcHistogramFraction(vectorVals []Vector, _ Matrix, args parser.Expression
if !enh.enableDelayedNameRemoval {
sample.Metric = sample.Metric.DropReserved(schema.IsMetadataLabel)
}
hf, hfAnnos := HistogramFraction(lower, upper, sample.H, sample.Metric.Get(model.MetricNameLabel), args[0].PositionRange())
hf, hfAnnos := HistogramFraction(lower, upper, sample.H, getMetricName(sample.Metric), args[0].PositionRange())
annos.Merge(hfAnnos)
enh.Out = append(enh.Out, Sample{
Metric: sample.Metric,
@ -1672,7 +1659,7 @@ func funcHistogramQuantile(vectorVals []Vector, _ Matrix, args parser.Expression
if !enh.enableDelayedNameRemoval {
sample.Metric = sample.Metric.DropReserved(schema.IsMetadataLabel)
}
hq, hqAnnos := HistogramQuantile(q, sample.H, sample.Metric.Get(model.MetricNameLabel), args[0].PositionRange())
hq, hqAnnos := HistogramQuantile(q, sample.H, getMetricName(sample.Metric), args[0].PositionRange())
annos.Merge(hqAnnos)
enh.Out = append(enh.Out, Sample{
Metric: sample.Metric,
@ -1687,7 +1674,7 @@ func funcHistogramQuantile(vectorVals []Vector, _ Matrix, args parser.Expression
res, forcedMonotonicity, _ := BucketQuantile(q, mb.buckets)
if forcedMonotonicity {
if enh.enableDelayedNameRemoval {
annos.Add(annotations.NewHistogramQuantileForcedMonotonicityInfo(mb.metric.Get(labels.MetricName), args[1].PositionRange()))
annos.Add(annotations.NewHistogramQuantileForcedMonotonicityInfo(getMetricName(mb.metric), args[1].PositionRange()))
} else {
annos.Add(annotations.NewHistogramQuantileForcedMonotonicityInfo("", args[1].PositionRange()))
}
@ -2224,3 +2211,7 @@ func stringSliceFromArgs(args parser.Expressions) []string {
}
return tmp
}
func getMetricName(metric labels.Labels) string {
return metric.Get(model.MetricNameLabel)
}

View file

@ -18,9 +18,11 @@ import (
"math"
"testing"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"github.com/prometheus/prometheus/model/histogram"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/promql/parser/posrange"
)
@ -29,10 +31,11 @@ func TestHistogramRateCounterResetHint(t *testing.T) {
{T: 0, H: &histogram.FloatHistogram{CounterResetHint: histogram.CounterReset, Count: 5, Sum: 5}},
{T: 1, H: &histogram.FloatHistogram{CounterResetHint: histogram.UnknownCounterReset, Count: 10, Sum: 10}},
}
fh, _ := histogramRate(points, false, "foo", posrange.PositionRange{})
labels := labels.FromMap(map[string]string{model.MetricNameLabel: "foo"})
fh, _ := histogramRate(points, false, labels, posrange.PositionRange{})
require.Equal(t, histogram.GaugeType, fh.CounterResetHint)
fh, _ = histogramRate(points, true, "foo", posrange.PositionRange{})
fh, _ = histogramRate(points, true, labels, posrange.PositionRange{})
require.Equal(t, histogram.GaugeType, fh.CounterResetHint)
}