diff --git a/app/vmselect/promql/rollup.go b/app/vmselect/promql/rollup.go index af06ffbfd..c03f4ddda 100644 --- a/app/vmselect/promql/rollup.go +++ b/app/vmselect/promql/rollup.go @@ -52,6 +52,8 @@ var rollupFuncs = map[string]newRollupFunc{ "lifetime": newRollupFuncOneArg(rollupLifetime), "lag": newRollupFuncOneArg(rollupLag), "scrape_interval": newRollupFuncOneArg(rollupScrapeInterval), + "tmin_over_time": newRollupFuncOneArg(rollupTmin), + "tmax_over_time": newRollupFuncOneArg(rollupTmax), "share_le_over_time": newRollupShareLE, "share_gt_over_time": newRollupShareGT, "histogram_over_time": newRollupFuncOneArg(rollupHistogram), @@ -746,6 +748,52 @@ func rollupMax(rfa *rollupFuncArg) float64 { return maxValue } +func rollupTmin(rfa *rollupFuncArg) float64 { + // There is no need in handling NaNs here, since they must be cleaned up + // before calling rollup funcs. + minValue := rfa.prevValue + minTimestamp := rfa.prevTimestamp + values := rfa.values + timestamps := rfa.timestamps + if math.IsNaN(minValue) { + if len(values) == 0 { + return nan + } + minValue = values[0] + minTimestamp = timestamps[0] + } + for i, v := range values { + if v < minValue { + minValue = v + minTimestamp = timestamps[i] + } + } + return float64(minTimestamp) * 1e-3 +} + +func rollupTmax(rfa *rollupFuncArg) float64 { + // There is no need in handling NaNs here, since they must be cleaned up + // before calling rollup funcs. + maxValue := rfa.prevValue + maxTimestamp := rfa.prevTimestamp + values := rfa.values + timestamps := rfa.timestamps + if math.IsNaN(maxValue) { + if len(values) == 0 { + return nan + } + maxValue = values[0] + maxTimestamp = timestamps[0] + } + for i, v := range values { + if v > maxValue { + maxValue = v + maxTimestamp = timestamps[i] + } + } + return float64(maxTimestamp) * 1e-3 +} + func rollupSum(rfa *rollupFuncArg) float64 { // There is no need in handling NaNs here, since they must be cleaned up // before calling rollup funcs. diff --git a/app/vmselect/promql/rollup_test.go b/app/vmselect/promql/rollup_test.go index 899a26d05..f9fa08535 100644 --- a/app/vmselect/promql/rollup_test.go +++ b/app/vmselect/promql/rollup_test.go @@ -331,6 +331,8 @@ func TestRollupNewRollupFuncSuccess(t *testing.T) { f("avg_over_time", 47.083333333333336) f("min_over_time", 12) f("max_over_time", 123) + f("tmin_over_time", 0.08) + f("tmax_over_time", 0.005) f("sum_over_time", 565) f("sum2_over_time", 37951) f("geomean_over_time", 39.33466603189148) diff --git a/docs/ExtendedPromQL.md b/docs/ExtendedPromQL.md index 182e4398f..34022a8c1 100644 --- a/docs/ExtendedPromQL.md +++ b/docs/ExtendedPromQL.md @@ -98,3 +98,5 @@ This functionality can be tried at [an editable Grafana dashboard](http://play-g Example: `share_le_over_time(memory_usage_bytes[24h], 100*1024*1024)` returns the share of time series values for the last 24 hours when memory usage was below or equal to 100MB. - `share_gt_over_time(m[d], gt)` - returns share (in the range 0..1) of values in `m` over `d`, which are bigger than `gt`. Useful for calculating SLI and SLO. Example: `share_gt_over_time(up[24h], 0)` - returns service availability for the last 24 hours. +- `tmin_over_time(m[d])` - returns timestamp for the minimum value for `m` over `d` time range. +- `tmax_over_time(m[d])` - returns timestamp for the maximum value for `m` over `d` time range. diff --git a/lib/metricsql/rollup.go b/lib/metricsql/rollup.go index 7406e1471..1b7586716 100644 --- a/lib/metricsql/rollup.go +++ b/lib/metricsql/rollup.go @@ -42,6 +42,8 @@ var rollupFuncs = map[string]bool{ "lifetime": true, "lag": true, "scrape_interval": true, + "tmin_over_time": true, + "tmax_over_time": true, "share_le_over_time": true, "share_gt_over_time": true, "histogram_over_time": true,