diff --git a/model/histogram/float_histogram.go b/model/histogram/float_histogram.go index 64c7b797a..bf89f2a47 100644 --- a/model/histogram/float_histogram.go +++ b/model/histogram/float_histogram.go @@ -469,8 +469,8 @@ func (h *FloatHistogram) DetectReset(previous *FloatHistogram) bool { // is a counter reset or not. // We do the same if the CounterResetHint is GaugeType, which should not happen, but PromQL still // allows the user to apply functions to gauge histograms that are only meant for counter histograms. - // In this case, we treat the gauge histograms as a counter histograms - // (and we plan to return a warning about it to the user). + // In this case, we treat the gauge histograms as counter histograms. A warning should be returned + // to the user in this case. if h.Count < previous.Count { return true } diff --git a/promql/functions.go b/promql/functions.go index cb3be0aef..9ce577de9 100644 --- a/promql/functions.go +++ b/promql/functions.go @@ -187,6 +187,8 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra minSchema = last.Schema } + var annos annotations.Annotations + // First iteration to find out two things: // - What's the smallest relevant schema? // - Are all data points histograms? @@ -197,10 +199,12 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra if curr == nil { return nil, annotations.New().Add(annotations.NewMixedFloatsHistogramsWarning(metricName, pos)) } - // TODO(trevorwhitney): Check if isCounter is consistent with curr.CounterResetHint. if !isCounter { continue } + if curr.CounterResetHint == histogram.GaugeType { + annos.Add(annotations.NewNativeHistogramNotCounterWarning(metricName, pos)) + } if curr.Schema < minSchema { minSchema = curr.Schema } @@ -218,6 +222,8 @@ 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)) } h.CounterResetHint = histogram.GaugeType diff --git a/util/annotations/annotations.go b/util/annotations/annotations.go index 180d40843..29dafeb2e 100644 --- a/util/annotations/annotations.go +++ b/util/annotations/annotations.go @@ -107,6 +107,8 @@ var ( BadBucketLabelWarning = fmt.Errorf("%w: bucket label %q is missing or has a malformed value", PromQLWarning, model.BucketLabel) MixedFloatsHistogramsWarning = fmt.Errorf("%w: encountered a mix of histograms and floats for metric name", PromQLWarning) MixedClassicNativeHistogramsWarning = fmt.Errorf("%w: vector contains a mix of classic and native histograms for metric name", PromQLWarning) + NativeHistogramNotCounterWarning = fmt.Errorf("%w: this native histogram metric is not a counter:", PromQLWarning) + NativeHistogramNotGaugeWarning = fmt.Errorf("%w: this native histogram metric is not a gauge:", PromQLWarning) PossibleNonCounterInfo = fmt.Errorf("%w: metric might not be a counter, name does not end in _total/_sum/_count/_bucket:", PromQLInfo) HistogramQuantileForcedMonotonicityInfo = fmt.Errorf("%w: input to histogram_quantile needed to be fixed for monotonicity (see https://prometheus.io/docs/prometheus/latest/querying/functions/#histogram_quantile) for metric name", PromQLInfo) @@ -166,6 +168,24 @@ func NewMixedClassicNativeHistogramsWarning(metricName string, pos posrange.Posi } } +// NewNativeHistogramNotCounterWarning is used when histogramRate is called +// with isCounter set to true on a gauge histogram. +func NewNativeHistogramNotCounterWarning(metricName string, pos posrange.PositionRange) error { + return annoErr{ + PositionRange: pos, + Err: fmt.Errorf("%w %q", NativeHistogramNotCounterWarning, metricName), + } +} + +// NewNativeHistogramNotGaugeWarning is used when histogramRate is called +// with isCounter set to false on a counter histogram. +func NewNativeHistogramNotGaugeWarning(metricName string, pos posrange.PositionRange) error { + return annoErr{ + PositionRange: pos, + Err: fmt.Errorf("%w %q", NativeHistogramNotGaugeWarning, metricName), + } +} + // NewPossibleNonCounterInfo is used when a named counter metric with only float samples does not // have the suffixes _total, _sum, _count, or _bucket. func NewPossibleNonCounterInfo(metricName string, pos posrange.PositionRange) error {