Repository: prometheus/prometheus
File: promql/functions.go (Go)

Code before:
	case ss[1].H == nil && ss[0].H == nil:
		if isRate && ss[1].F < ss[0].F {
			// Counter reset.
		} else {
			resultSample.F = ss[1].F - ss[0].F
		}
	case ss[1].H != nil && ss[0].H != nil:
		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()))
		}
		// 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()))
		}
		_, err := resultSample.H.Sub(ss[0].H)
		if !isRate && err != nil {
			if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) {
				return out, annos.Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, args.PositionRange()))
			} else if errors.Is(err, histogram.ErrHistogramsIncompatibleBounds) {
				return out, annos.Add(annotations.NewIncompatibleCustomBucketsHistogramsWarning(metricName, args.PositionRange()))
			}
		}
		// For counter resets.
		if isRate && ss[1].H.DetectReset(ss[0].H) {
			_, err := resultSample.H.Add(ss[0].H)
			if err != nil {
				if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) {
					annos.Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, args.PositionRange()))
				} else if errors.Is(err, histogram.ErrHistogramsIncompatibleBounds) {
					annos.Add(annotations.NewIncompatibleCustomBucketsHistogramsWarning(metricName, args.PositionRange()))
				}
			}
		}
		resultSample.H.CounterResetHint = histogram.GaugeType
		resultSample.H.Compact(0)
	default:
		// Mix of a float and a histogram.
		return out, annos.Add(annotations.NewMixedFloatsHistogramsWarning(metricName, args.PositionRange()))
	}

	if isRate {
		// Convert to per-second.
		if resultSample.H == nil {
			resultSample.F /= float64(sampledInterval) / 1000
		} else {
			resultSample.H.Div(float64(sampledInterval) / 1000)
		}
	}


Review comment (suggestion):
After some thinking, I think we should allow the calculation in case of counters and a detected reset in any case, including those of incompatible buckets (as they are considered a reset, too).

A normal `rate` with just two histograms should do the same then. I'll check if that's already the case.

Code-wise, this simplifies things, because you can just put the inverted condition (`!isRate || !ss[1].H.DetectReset(ss[0].H)`) in front of the `_, err := resultSample.H.Sub(ss[0].H)` above.

Code after:
	sampledInterval := ss[1].T - ss[0].T
	if sampledInterval == 0 {
		// Avoid dividing by 0.
		return out, nil
	}
	switch {
	case ss[1].H == nil && ss[0].H == nil:
		if !isRate || ss[1].F >= ss[0].F {
			// Gauge or counter without reset.
			resultSample.F = ss[1].F - ss[0].F
		}
		// In case of a counter reset, we leave resultSample at
		// its current value, which is already ss[1].
	case ss[1].H != nil && ss[0].H != nil:
		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()))
		}
		// 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()))
		}
		if !isRate || !ss[1].H.DetectReset(ss[0].H) {
			_, err := resultSample.H.Sub(ss[0].H)
			if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) {
				return out, annos.Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, args.PositionRange()))
			} else if errors.Is(err, histogram.ErrHistogramsIncompatibleBounds) {
				return out, annos.Add(annotations.NewIncompatibleCustomBucketsHistogramsWarning(metricName, args.PositionRange()))
			}
		}
		resultSample.H.CounterResetHint = histogram.GaugeType
		resultSample.H.Compact(0)
	default:
		// Mix of a float and a histogram.
		return out, annos.Add(annotations.NewMixedFloatsHistogramsWarning(metricName, args.PositionRange()))
	}

	if isRate {
		// Convert to per-second.
		if resultSample.H == nil {
			resultSample.F /= float64(sampledInterval) / 1000
		} else {
			resultSample.H.Div(float64(sampledInterval) / 1000)
		}
	}

	return append(out, resultSample), annos
}

// Calculate the trend value at the given index i in raw data d.