diff --git a/promql/functions.go b/promql/functions.go index 9252cb71fe..bba77487fc 100644 --- a/promql/functions.go +++ b/promql/functions.go @@ -474,6 +474,43 @@ func funcDeriv(ev *evaluator, args Expressions) Value { return resultVector } +// === predict_linear(node ExprMatrix, k ExprScalar) Vector === +func funcPredictLinear(ev *evaluator, args Expressions) Value { + vector := funcDeriv(ev, args[0:1]).(Vector) + duration := clientmodel.SampleValue(clientmodel.SampleValue(ev.evalFloat(args[1]))) + + excludedLabels := map[clientmodel.LabelName]struct{}{ + clientmodel.MetricNameLabel: {}, + } + + // Calculate predicted delta over the duration. + signatureToDelta := map[uint64]clientmodel.SampleValue{} + for _, el := range vector { + signature := clientmodel.SignatureWithoutLabels(el.Metric.Metric, excludedLabels) + signatureToDelta[signature] = el.Value * duration + } + + // add predicted delta to last value. + matrixBounds := ev.evalMatrixBounds(args[0]) + outVec := make(Vector, 0, len(signatureToDelta)) + for _, samples := range matrixBounds { + if len(samples.Values) < 2 { + continue + } + signature := clientmodel.SignatureWithoutLabels(samples.Metric.Metric, excludedLabels) + delta, ok := signatureToDelta[signature] + if ok { + samples.Metric.Delete(clientmodel.MetricNameLabel) + outVec = append(outVec, &Sample{ + Metric: samples.Metric, + Value: delta + samples.Values[1].Value, + Timestamp: ev.Timestamp, + }) + } + } + return outVec +} + // === histogram_quantile(k ExprScalar, vector ExprVector) Vector === func funcHistogramQuantile(ev *evaluator, args Expressions) Value { q := clientmodel.SampleValue(ev.evalFloat(args[0])) @@ -688,6 +725,12 @@ var functions = map[string]*Function{ ReturnType: ExprVector, Call: funcMinOverTime, }, + "predict_linear": { + Name: "predict_linear", + ArgTypes: []ExprType{ExprMatrix, ExprScalar}, + ReturnType: ExprVector, + Call: funcPredictLinear, + }, "rate": { Name: "rate", ArgTypes: []ExprType{ExprMatrix}, diff --git a/promql/testdata/functions.test b/promql/testdata/functions.test index 6d246e2b0b..423c0b26e3 100644 --- a/promql/testdata/functions.test +++ b/promql/testdata/functions.test @@ -62,3 +62,33 @@ load 5m eval instant at 50m increase(http_requests[50m]) {path="/foo"} 100 {path="/bar"} 90 + + +clear + +# Tests for deriv() and predict_linear(). +load 5m + testcounter_reset_middle 0+10x4 0+10x5 + http_requests{job="app-server", instance="1", group="canary"} 0+80x10 + +# Deriv should return the same as rate in simple cases. +eval instant at 50m rate(http_requests{group="canary", instance="1", job="app-server"}[60m]) + {group="canary", instance="1", job="app-server"} 0.26666666666666666 + +eval instant at 50m deriv(http_requests{group="canary", instance="1", job="app-server"}[60m]) + {group="canary", instance="1", job="app-server"} 0.26666666666666666 + +# Deriv should return correct result. +eval instant at 50m deriv(testcounter_reset_middle[100m]) + {} 0.010606060606060607 + +# Predict_linear should return correct result. +eval instant at 50m predict_linear(testcounter_reset_middle[100m], 3600) + {} 88.181818181818185200 + +# Predict_linear is syntactic sugar around deriv. +eval instant at 50m predict_linear(http_requests[50m], 3600) - (http_requests + deriv(http_requests[50m]) * 3600) + {group="canary", instance="1", job="app-server"} 0 + +eval instant at 50m predict_linear(testcounter_reset_middle[100m], 3600) - (testcounter_reset_middle + deriv(testcounter_reset_middle[100m]) * 3600) + {} 0 diff --git a/promql/testdata/legacy.test b/promql/testdata/legacy.test index 86861ab3c8..43c0da5fed 100644 --- a/promql/testdata/legacy.test +++ b/promql/testdata/legacy.test @@ -238,10 +238,6 @@ eval instant at 50m delta(http_requests{group="canary", instance="1", job="app-s eval instant at 50m rate(http_requests{group="canary", instance="1", job="app-server"}[60m]) {group="canary", instance="1", job="app-server"} 0.26666666666666666 -# Deriv should return the same as rate in simple cases. -eval instant at 50m deriv(http_requests{group="canary", instance="1", job="app-server"}[60m]) - {group="canary", instance="1", job="app-server"} 0.26666666666666666 - # Counter resets at in the middle of range are handled correctly by rate(). eval instant at 50m rate(testcounter_reset_middle[60m]) {} 0.03 @@ -251,10 +247,6 @@ eval instant at 50m rate(testcounter_reset_middle[60m]) eval instant at 50m rate(testcounter_reset_end[5m]) {} 0 -# Deriv should return correct result. -eval instant at 50m deriv(testcounter_reset_middle[100m]) - {} 0.010606060606060607 - # count_scalar for a non-empty vector should return scalar element count. eval instant at 50m count_scalar(http_requests) 8