Aliaksandr Valialkin 2020-07-17 18:28:19 +03:00
parent 6b5ad535ae
commit 1c641037e8
8 changed files with 67 additions and 4 deletions

View File

@ -4508,6 +4508,17 @@ func TestExecSuccess(t *testing.T) {
resultExpected := []netstorage.Result{r} resultExpected := []netstorage.Result{r}
f(q, resultExpected) f(q, resultExpected)
}) })
t.Run(`mode_over_time()`, func(t *testing.T) {
t.Parallel()
q := `mode_over_time(round(time()/500)[100s:1s])`
r := netstorage.Result{
MetricName: metricNameExpected,
Values: []float64{2, 2, 3, 3, 3, 4},
Timestamps: timestampsExpected,
}
resultExpected := []netstorage.Result{r}
f(q, resultExpected)
})
t.Run(`integrate(1)`, func(t *testing.T) { t.Run(`integrate(1)`, func(t *testing.T) {
t.Parallel() t.Parallel()
q := `integrate(1)` q := `integrate(1)`
@ -5719,6 +5730,7 @@ func TestExecError(t *testing.T) {
f(`hoeffding_bound_upper(0.99, foo, 1)`) f(`hoeffding_bound_upper(0.99, foo, 1)`)
f(`outliersk()`) f(`outliersk()`)
f(`outliersk(1)`) f(`outliersk(1)`)
f(`mode_over_time()`)
// Invalid argument type // Invalid argument type
f(`median_over_time({}, 2)`) f(`median_over_time({}, 2)`)

View File

@ -4,6 +4,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"math" "math"
"sort"
"strings" "strings"
"sync" "sync"
@ -79,6 +80,9 @@ var rollupFuncs = map[string]newRollupFunc{
// in order to properly handle offset and timestamps unaligned to the current step. // in order to properly handle offset and timestamps unaligned to the current step.
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/415 for details. // See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/415 for details.
"timestamp": newRollupFuncOneArg(rollupTimestamp), "timestamp": newRollupFuncOneArg(rollupTimestamp),
// See https://en.wikipedia.org/wiki/Mode_(statistics)
"mode_over_time": newRollupFuncOneArg(rollupModeOverTime),
} }
// rollupAggrFuncs are functions that can be passed to `aggr_over_time()` // rollupAggrFuncs are functions that can be passed to `aggr_over_time()`
@ -121,6 +125,7 @@ var rollupAggrFuncs = map[string]rollupFunc{
"ascent_over_time": rollupAscentOverTime, "ascent_over_time": rollupAscentOverTime,
"descent_over_time": rollupDescentOverTime, "descent_over_time": rollupDescentOverTime,
"timestamp": rollupTimestamp, "timestamp": rollupTimestamp,
"mode_over_time": rollupModeOverTime,
} }
var rollupFuncsCannotAdjustWindow = map[string]bool{ var rollupFuncsCannotAdjustWindow = map[string]bool{
@ -167,6 +172,7 @@ var rollupFuncsKeepMetricGroup = map[string]bool{
"hoeffding_bound_upper": true, "hoeffding_bound_upper": true,
"first_over_time": true, "first_over_time": true,
"last_over_time": true, "last_over_time": true,
"mode_over_time": true,
} }
func getRollupAggrFuncNames(expr metricsql.Expr) ([]string, error) { func getRollupAggrFuncNames(expr metricsql.Expr) ([]string, error) {
@ -1534,6 +1540,32 @@ func rollupTimestamp(rfa *rollupFuncArg) float64 {
return float64(timestamps[len(timestamps)-1]) / 1e3 return float64(timestamps[len(timestamps)-1]) / 1e3
} }
func rollupModeOverTime(rfa *rollupFuncArg) float64 {
// There is no need in handling NaNs here, since they must be cleaned up
// before calling rollup funcs.
values := rfa.values
prevValue := rfa.prevValue
if len(values) == 0 {
return prevValue
}
sort.Float64s(values)
j := -1
dMax := 0
mode := prevValue
for i, v := range values {
if prevValue == v {
continue
}
if d := i - j; d > dMax {
dMax = d
mode = prevValue
}
j = i
prevValue = v
}
return mode
}
func rollupAscentOverTime(rfa *rollupFuncArg) float64 { func rollupAscentOverTime(rfa *rollupFuncArg) float64 {
// There is no need in handling NaNs here, since they must be cleaned up // There is no need in handling NaNs here, since they must be cleaned up
// before calling rollup funcs. // before calling rollup funcs.

View File

@ -392,6 +392,7 @@ func TestRollupNewRollupFuncSuccess(t *testing.T) {
f("ascent_over_time", 142) f("ascent_over_time", 142)
f("descent_over_time", 231) f("descent_over_time", 231)
f("timestamp", 0.13) f("timestamp", 0.13)
f("mode_over_time", 34)
} }
func TestRollupNewRollupFuncError(t *testing.T) { func TestRollupNewRollupFuncError(t *testing.T) {
@ -952,6 +953,20 @@ func TestRollupFuncsNoWindow(t *testing.T) {
timestampsExpected := []int64{0, 40, 80, 120, 160} timestampsExpected := []int64{0, 40, 80, 120, 160}
testRowsEqual(t, values, rc.Timestamps, valuesExpected, timestampsExpected) testRowsEqual(t, values, rc.Timestamps, valuesExpected, timestampsExpected)
}) })
t.Run("mode_over_time", func(t *testing.T) {
rc := rollupConfig{
Func: rollupModeOverTime,
Start: 0,
End: 160,
Step: 40,
Window: 80,
}
rc.Timestamps = getTimestamps(rc.Start, rc.End, rc.Step)
values := rc.Do(nil, testValues, testTimestamps)
valuesExpected := []float64{nan, nan, 34, 44, 44}
timestampsExpected := []int64{0, 40, 80, 120, 160}
testRowsEqual(t, values, rc.Timestamps, valuesExpected, timestampsExpected)
})
} }
func TestRollupBigNumberOfValues(t *testing.T) { func TestRollupBigNumberOfValues(t *testing.T) {

View File

@ -123,3 +123,4 @@ This functionality can be tried at [an editable Grafana dashboard](http://play-g
This aggregate function is useful to detect anomalies across groups of similar time series. This aggregate function is useful to detect anomalies across groups of similar time series.
- `ascent_over_time(m[d])` - returns the sum of positive deltas between adjancent data points in `m` over `d`. Useful for tracking height gains in GPS track. - `ascent_over_time(m[d])` - returns the sum of positive deltas between adjancent data points in `m` over `d`. Useful for tracking height gains in GPS track.
- `descent_over_time(m[d])` - returns the absolute sum of negative deltas between adjancent data points in `m` over `d`. Useful for tracking height loss in GPS track. - `descent_over_time(m[d])` - returns the absolute sum of negative deltas between adjancent data points in `m` over `d`. Useful for tracking height loss in GPS track.
- `mode_over_time(m[d])` - returns [mode](https://en.wikipedia.org/wiki/Mode_(statistics)) for `m` values over `d`. It is expected that `m` values are discrete.

2
go.mod
View File

@ -9,7 +9,7 @@ require (
// like https://github.com/valyala/fasthttp/commit/996610f021ff45fdc98c2ce7884d5fa4e7f9199b // like https://github.com/valyala/fasthttp/commit/996610f021ff45fdc98c2ce7884d5fa4e7f9199b
github.com/VictoriaMetrics/fasthttp v1.0.1 github.com/VictoriaMetrics/fasthttp v1.0.1
github.com/VictoriaMetrics/metrics v1.11.3 github.com/VictoriaMetrics/metrics v1.11.3
github.com/VictoriaMetrics/metricsql v0.2.5 github.com/VictoriaMetrics/metricsql v0.2.6
github.com/aws/aws-sdk-go v1.33.5 github.com/aws/aws-sdk-go v1.33.5
github.com/cespare/xxhash/v2 v2.1.1 github.com/cespare/xxhash/v2 v2.1.1
github.com/golang/snappy v0.0.1 github.com/golang/snappy v0.0.1

4
go.sum
View File

@ -53,8 +53,8 @@ github.com/VictoriaMetrics/metrics v1.11.2 h1:t/ceLP6SvagUqypCKU7cI7+tQn54+TIV/t
github.com/VictoriaMetrics/metrics v1.11.2/go.mod h1:LU2j9qq7xqZYXz8tF3/RQnB2z2MbZms5TDiIg9/NHiQ= github.com/VictoriaMetrics/metrics v1.11.2/go.mod h1:LU2j9qq7xqZYXz8tF3/RQnB2z2MbZms5TDiIg9/NHiQ=
github.com/VictoriaMetrics/metrics v1.11.3 h1:eSfXc0CrquKa1VTNUvhP+dhNjLUZHQGTFfp19mYCQWE= github.com/VictoriaMetrics/metrics v1.11.3 h1:eSfXc0CrquKa1VTNUvhP+dhNjLUZHQGTFfp19mYCQWE=
github.com/VictoriaMetrics/metrics v1.11.3/go.mod h1:LU2j9qq7xqZYXz8tF3/RQnB2z2MbZms5TDiIg9/NHiQ= github.com/VictoriaMetrics/metrics v1.11.3/go.mod h1:LU2j9qq7xqZYXz8tF3/RQnB2z2MbZms5TDiIg9/NHiQ=
github.com/VictoriaMetrics/metricsql v0.2.5 h1:9kL+RA2yuPfMpYdqycRbKOJ9WKdDmPmV6hAju5L6ti0= github.com/VictoriaMetrics/metricsql v0.2.6 h1:hJxeGeyP++fWuW41URWcl2PTNTMTTeqm7UcT1BEZOOg=
github.com/VictoriaMetrics/metricsql v0.2.5/go.mod h1:UIjd9S0W1UnTWlJdM0wLS+2pfuPqjwqKoK8yTos+WyE= github.com/VictoriaMetrics/metricsql v0.2.6/go.mod h1:UIjd9S0W1UnTWlJdM0wLS+2pfuPqjwqKoK8yTos+WyE=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/aws/aws-sdk-go v1.33.5 h1:p2fr1ryvNTU6avUWLI+/H7FGv0TBIjzVM5WDgXBBv4U= github.com/aws/aws-sdk-go v1.33.5 h1:p2fr1ryvNTU6avUWLI+/H7FGv0TBIjzVM5WDgXBBv4U=

View File

@ -63,6 +63,9 @@ var rollupFuncs = map[string]bool{
// `timestamp` func has been moved here because it must work properly with offsets and samples unaligned to the current step. // `timestamp` func has been moved here because it must work properly with offsets and samples unaligned to the current step.
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/415 for details. // See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/415 for details.
"timestamp": true, "timestamp": true,
// See https://en.wikipedia.org/wiki/Mode_(statistics)
"mode_over_time": true,
} }
// IsRollupFunc returns whether funcName is known rollup function. // IsRollupFunc returns whether funcName is known rollup function.

2
vendor/modules.txt vendored
View File

@ -16,7 +16,7 @@ github.com/VictoriaMetrics/fasthttp/fasthttputil
github.com/VictoriaMetrics/fasthttp/stackless github.com/VictoriaMetrics/fasthttp/stackless
# github.com/VictoriaMetrics/metrics v1.11.3 # github.com/VictoriaMetrics/metrics v1.11.3
github.com/VictoriaMetrics/metrics github.com/VictoriaMetrics/metrics
# github.com/VictoriaMetrics/metricsql v0.2.5 # github.com/VictoriaMetrics/metricsql v0.2.6
github.com/VictoriaMetrics/metricsql github.com/VictoriaMetrics/metricsql
github.com/VictoriaMetrics/metricsql/binaryop github.com/VictoriaMetrics/metricsql/binaryop
# github.com/aws/aws-sdk-go v1.33.5 # github.com/aws/aws-sdk-go v1.33.5