Skip to content

Commit c2c2103

Browse files
authored
Merge branch 'prometheus:main' into feature/add-metered-appender
2 parents 011ad4f + 8be416a commit c2c2103

File tree

4 files changed

+157
-21
lines changed

4 files changed

+157
-21
lines changed

promql/engine.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3475,15 +3475,14 @@ func handleVectorBinopError(err error, e *parser.BinaryExpr) annotations.Annotat
34753475
if err == nil {
34763476
return nil
34773477
}
3478-
metricName := ""
3478+
op := parser.ItemTypeStr[e.Op]
34793479
pos := e.PositionRange()
34803480
if errors.Is(err, annotations.PromQLInfo) || errors.Is(err, annotations.PromQLWarning) {
34813481
return annotations.New().Add(err)
34823482
}
3483-
if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) {
3484-
return annotations.New().Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, pos))
3485-
} else if errors.Is(err, histogram.ErrHistogramsIncompatibleBounds) {
3486-
return annotations.New().Add(annotations.NewIncompatibleCustomBucketsHistogramsWarning(metricName, pos))
3483+
// TODO(NeerajGartia21): Test the exact annotation output once the testing framework can do so.
3484+
if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) || errors.Is(err, histogram.ErrHistogramsIncompatibleBounds) {
3485+
return annotations.New().Add(annotations.NewIncompatibleBucketLayoutInBinOpWarning(op, pos))
34873486
}
34883487
return nil
34893488
}

promql/functions.go

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -286,46 +286,115 @@ func funcIncrease(vals []parser.Value, args parser.Expressions, enh *EvalNodeHel
286286

287287
// === irate(node parser.ValueTypeMatrix) (Vector, Annotations) ===
288288
func funcIrate(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
289-
return instantValue(vals, enh.Out, true)
289+
return instantValue(vals, args, enh.Out, true)
290290
}
291291

292292
// === idelta(node model.ValMatrix) (Vector, Annotations) ===
293293
func funcIdelta(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {
294-
return instantValue(vals, enh.Out, false)
294+
return instantValue(vals, args, enh.Out, false)
295295
}
296296

297-
func instantValue(vals []parser.Value, out Vector, isRate bool) (Vector, annotations.Annotations) {
298-
samples := vals[0].(Matrix)[0]
297+
func instantValue(vals []parser.Value, args parser.Expressions, out Vector, isRate bool) (Vector, annotations.Annotations) {
298+
var (
299+
samples = vals[0].(Matrix)[0]
300+
metricName = samples.Metric.Get(labels.MetricName)
301+
ss = make([]Sample, 0, 2)
302+
annos annotations.Annotations
303+
)
304+
299305
// No sense in trying to compute a rate without at least two points. Drop
300306
// this Vector element.
301307
// TODO: add RangeTooShortWarning
302-
if len(samples.Floats) < 2 {
308+
if len(samples.Floats)+len(samples.Histograms) < 2 {
303309
return out, nil
304310
}
305311

306-
lastSample := samples.Floats[len(samples.Floats)-1]
307-
previousSample := samples.Floats[len(samples.Floats)-2]
312+
// Add the last 2 float samples if they exist.
313+
for i := max(0, len(samples.Floats)-2); i < len(samples.Floats); i++ {
314+
ss = append(ss, Sample{
315+
F: samples.Floats[i].F,
316+
T: samples.Floats[i].T,
317+
})
318+
}
308319

309-
var resultValue float64
310-
if isRate && lastSample.F < previousSample.F {
311-
// Counter reset.
312-
resultValue = lastSample.F
313-
} else {
314-
resultValue = lastSample.F - previousSample.F
320+
// Add the last 2 histogram samples into their correct position if they exist.
321+
for i := max(0, len(samples.Histograms)-2); i < len(samples.Histograms); i++ {
322+
s := Sample{
323+
H: samples.Histograms[i].H,
324+
T: samples.Histograms[i].T,
325+
}
326+
switch {
327+
case len(ss) == 0:
328+
ss = append(ss, s)
329+
case len(ss) == 1:
330+
if s.T < ss[0].T {
331+
ss = append([]Sample{s}, ss...)
332+
} else {
333+
ss = append(ss, s)
334+
}
335+
case s.T < ss[0].T:
336+
// s is older than 1st, so discard it.
337+
case s.T > ss[1].T:
338+
// s is newest, so add it as 2nd and make the old 2nd the new 1st.
339+
ss[0] = ss[1]
340+
ss[1] = s
341+
default:
342+
// In all other cases, we just make s the new 1st.
343+
// This establishes a correct order, even in the (irregular)
344+
// case of equal timestamps.
345+
ss[0] = s
346+
}
315347
}
316348

317-
sampledInterval := lastSample.T - previousSample.T
349+
resultSample := ss[1]
350+
sampledInterval := ss[1].T - ss[0].T
318351
if sampledInterval == 0 {
319352
// Avoid dividing by 0.
320353
return out, nil
321354
}
355+
switch {
356+
case ss[1].H == nil && ss[0].H == nil:
357+
if !isRate || ss[1].F >= ss[0].F {
358+
// Gauge or counter without reset.
359+
resultSample.F = ss[1].F - ss[0].F
360+
}
361+
// In case of a counter reset, we leave resultSample at
362+
// its current value, which is already ss[1].
363+
case ss[1].H != nil && ss[0].H != nil:
364+
resultSample.H = ss[1].H.Copy()
365+
// irate should only be applied to counters.
366+
if isRate && (ss[1].H.CounterResetHint == histogram.GaugeType || ss[0].H.CounterResetHint == histogram.GaugeType) {
367+
annos.Add(annotations.NewNativeHistogramNotCounterWarning(metricName, args.PositionRange()))
368+
}
369+
// idelta should only be applied to gauges.
370+
if !isRate && (ss[1].H.CounterResetHint != histogram.GaugeType || ss[0].H.CounterResetHint != histogram.GaugeType) {
371+
annos.Add(annotations.NewNativeHistogramNotGaugeWarning(metricName, args.PositionRange()))
372+
}
373+
if !isRate || !ss[1].H.DetectReset(ss[0].H) {
374+
_, err := resultSample.H.Sub(ss[0].H)
375+
if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) {
376+
return out, annos.Add(annotations.NewMixedExponentialCustomHistogramsWarning(metricName, args.PositionRange()))
377+
} else if errors.Is(err, histogram.ErrHistogramsIncompatibleBounds) {
378+
return out, annos.Add(annotations.NewIncompatibleCustomBucketsHistogramsWarning(metricName, args.PositionRange()))
379+
}
380+
}
381+
resultSample.H.CounterResetHint = histogram.GaugeType
382+
resultSample.H.Compact(0)
383+
default:
384+
// Mix of a float and a histogram.
385+
return out, annos.Add(annotations.NewMixedFloatsHistogramsWarning(metricName, args.PositionRange()))
386+
}
322387

323388
if isRate {
324389
// Convert to per-second.
325-
resultValue /= float64(sampledInterval) / 1000
390+
if resultSample.H == nil {
391+
resultSample.F /= float64(sampledInterval) / 1000
392+
} else {
393+
resultSample.H.Div(float64(sampledInterval) / 1000)
394+
}
326395
}
327396

328-
return append(out, Sample{F: resultValue}), nil
397+
return append(out, resultSample), annos
329398
}
330399

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

promql/promqltest/testdata/functions.test

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,13 @@ clear
218218
load 5m
219219
http_requests_total{path="/foo"} 0+10x10
220220
http_requests_total{path="/bar"} 0+10x5 0+10x5
221+
http_requests_histogram{path="/a"} {{sum:2 count:2}}+{{sum:3 count:3}}x5
222+
http_requests_histogram{path="/b"} 0 0 {{sum:1 count:1}} {{sum:4 count:4}}
223+
http_requests_histogram{path="/c"} 0 0 {{sum:1 count:1}} {{sum:4 count:4 counter_reset_hint:gauge}}
224+
http_requests_histogram{path="/d"} 0 0 {{sum:1 count:1 counter_reset_hint:gauge}} {{sum:4 count:4}}
225+
http_requests_histogram{path="/e"} 0 1 2 {{sum:4 count:4}}
226+
http_requests_histogram{path="/f"} 0 0 {{sum:1 count:1}} {{schema:-53 sum:3 count:3 custom_values:[5 10] buckets:[3]}}
227+
http_requests_histogram{path="/g"} 0 0 {{schema:-53 sum:3 count:3 custom_values:[1] buckets:[3]}} {{schema:-53 sum:3 count:3 custom_values:[5 10] buckets:[3]}}
221228

222229
eval instant at 50m irate(http_requests_total[50m])
223230
{path="/foo"} .03333333333333333333
@@ -228,6 +235,28 @@ eval instant at 30m irate(http_requests_total[50m])
228235
{path="/foo"} .03333333333333333333
229236
{path="/bar"} 0
230237

238+
eval instant at 20m irate(http_requests_histogram{path="/a"}[20m])
239+
{path="/a"} {{sum:0.01 count:0.01 counter_reset_hint:gauge}}
240+
241+
eval instant at 20m irate(http_requests_histogram{path="/b"}[20m])
242+
{path="/b"} {{sum:0.01 count:0.01 counter_reset_hint:gauge}}
243+
244+
eval instant at 20m irate(http_requests_histogram{path="/b"}[6m])
245+
246+
eval_warn instant at 20m irate(http_requests_histogram{path="/c"}[20m])
247+
{path="/c"} {{sum:0.01 count:0.01 counter_reset_hint:gauge}}
248+
249+
eval_warn instant at 20m irate(http_requests_histogram{path="/d"}[20m])
250+
{path="/d"} {{sum:0.01 count:0.01 counter_reset_hint:gauge}}
251+
252+
eval_warn instant at 20m irate(http_requests_histogram{path="/e"}[20m])
253+
254+
eval instant at 20m irate(http_requests_histogram{path="/f"}[20m])
255+
{path="/f"} {{schema:-53 sum:0.01 count:0.01 custom_values:[5 10] buckets:[0.01]}}
256+
257+
eval instant at 20m irate(http_requests_histogram{path="/g"}[20m])
258+
{path="/g"} {{schema:-53 sum:0.01 count:0.01 custom_values:[5 10] buckets:[0.01]}}
259+
231260
clear
232261

233262
# Tests for delta().
@@ -259,11 +288,38 @@ clear
259288
load 5m
260289
http_requests{path="/foo"} 0 50 100 150
261290
http_requests{path="/bar"} 0 50 100 50
291+
http_requests_histogram{path="/a"} {{sum:2 count:2 counter_reset_hint:gauge}}+{{sum:1 count:3 counter_reset_hint:gauge}}x5
292+
http_requests_histogram{path="/b"} 0 0 {{sum:1 count:1 counter_reset_hint:gauge}} {{sum:2 count:2 counter_reset_hint:gauge}}
293+
http_requests_histogram{path="/c"} 0 0 {{sum:1 count:1}} {{sum:2 count:2 counter_reset_hint:gauge}}
294+
http_requests_histogram{path="/d"} 0 0 {{sum:1 count:1 counter_reset_hint:gauge}} {{sum:2 count:2}}
295+
http_requests_histogram{path="/e"} 0 1 2 {{sum:1 count:2 counter_reset_hint:gauge}}
296+
http_requests_histogram{path="/f"} 0 0 {{sum:1 count:1 counter_reset_hint:gauge}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1] counter_reset_hint:gauge}}
297+
http_requests_histogram{path="/g"} 0 0 {{schema:-53 sum:1 count:1 custom_values:[1] buckets:[2] counter_reset_hint:gauge}} {{schema:-53 sum:1 count:1 custom_values:[5 10] buckets:[1] counter_reset_hint:gauge}}
262298

263299
eval instant at 20m idelta(http_requests[20m])
264300
{path="/foo"} 50
265301
{path="/bar"} -50
266302

303+
eval instant at 20m idelta(http_requests_histogram{path="/a"}[20m])
304+
{path="/a"} {{sum:1 count:3 counter_reset_hint:gauge}}
305+
306+
eval instant at 20m idelta(http_requests_histogram{path="/b"}[20m])
307+
{path="/b"} {{sum:1 count:1 counter_reset_hint:gauge}}
308+
309+
eval instant at 20m idelta(http_requests_histogram{path="/b"}[6m])
310+
311+
eval_warn instant at 20m idelta(http_requests_histogram{path="/c"}[20m])
312+
{path="/c"} {{sum:1 count:1 counter_reset_hint:gauge}}
313+
314+
eval_warn instant at 20m idelta(http_requests_histogram{path="/d"}[20m])
315+
{path="/d"} {{sum:1 count:1 counter_reset_hint:gauge}}
316+
317+
eval_warn instant at 20m idelta(http_requests_histogram{path="/e"}[20m])
318+
319+
eval_warn instant at 20m idelta(http_requests_histogram{path="/f"}[20m])
320+
321+
eval_warn instant at 20m idelta(http_requests_histogram{path="/g"}[20m])
322+
267323
clear
268324

269325
# Tests for deriv() and predict_linear().

util/annotations/annotations.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ var (
143143
NativeHistogramNotGaugeWarning = fmt.Errorf("%w: this native histogram metric is not a gauge:", PromQLWarning)
144144
MixedExponentialCustomHistogramsWarning = fmt.Errorf("%w: vector contains a mix of histograms with exponential and custom buckets schemas for metric name", PromQLWarning)
145145
IncompatibleCustomBucketsHistogramsWarning = fmt.Errorf("%w: vector contains histograms with incompatible custom buckets for metric name", PromQLWarning)
146+
IncompatibleBucketLayoutInBinOpWarning = fmt.Errorf("%w: incompatible bucket layout encountered for binary operator", PromQLWarning)
146147

147148
PossibleNonCounterInfo = fmt.Errorf("%w: metric might not be a counter, name does not end in _total/_sum/_count/_bucket:", PromQLInfo)
148149
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)
@@ -295,9 +296,20 @@ func NewHistogramIgnoredInAggregationInfo(aggregation string, pos posrange.Posit
295296
}
296297
}
297298

299+
// NewHistogramIgnoredInMixedRangeInfo is used when a histogram is ignored
300+
// in a range vector which contains mix of floats and histograms.
298301
func NewHistogramIgnoredInMixedRangeInfo(metricName string, pos posrange.PositionRange) error {
299302
return annoErr{
300303
PositionRange: pos,
301304
Err: fmt.Errorf("%w %q", HistogramIgnoredInMixedRangeInfo, metricName),
302305
}
303306
}
307+
308+
// NewIncompatibleBucketLayoutInBinOpWarning is used if binary operators act on a
309+
// combination of two incompatible histograms.
310+
func NewIncompatibleBucketLayoutInBinOpWarning(operator string, pos posrange.PositionRange) error {
311+
return annoErr{
312+
PositionRange: pos,
313+
Err: fmt.Errorf("%w %s", IncompatibleBucketLayoutInBinOpWarning, operator),
314+
}
315+
}

0 commit comments

Comments
 (0)