Skip to content
This repository was archived by the owner on Aug 23, 2023. It is now read-only.

Add absolute function #1300

Merged
merged 3 commits into from
May 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/graphite.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ See also:

| Function name and signature | Alias | Metrictank |
| -------------------------------------------------------------- | ------------ | ---------- |
| absolute | | No |
| absolute | | Stable |
| aggregate | | No |
| aggregateLine | | No |
| aggregateWithWildcards | | No |
Expand Down
57 changes: 57 additions & 0 deletions expr/func_absolute.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package expr

import (
"fmt"
"math"

"github.com/grafana/metrictank/api/models"
"github.com/raintank/schema"
)

type FuncAbsolute struct {
in GraphiteFunc
}

func NewAbsolute() GraphiteFunc {
return &FuncAbsolute{}
}

func (s *FuncAbsolute) Signature() ([]Arg, []Arg) {
return []Arg{
ArgSeriesList{val: &s.in}}, []Arg{ArgSeriesList{}}
}

func (s *FuncAbsolute) Context(context Context) Context {
return context
}

func (s *FuncAbsolute) Exec(cache map[Req][]models.Series) ([]models.Series, error) {
series, err := s.in.Exec(cache)
if err != nil {
return nil, err
}

out := make([]models.Series, len(series))
for i, serie := range series {
transformed := &out[i]
transformed.Target = fmt.Sprintf("absolute(%s)", serie.Target)
transformed.QueryPatt = fmt.Sprintf("absolute(%s)", serie.QueryPatt)
transformed.Tags = make(map[string]string, len(serie.Tags)+1)
transformed.Datapoints = pointSlicePool.Get().([]schema.Point)
transformed.Interval = serie.Interval
transformed.Consolidator = serie.Consolidator
transformed.QueryCons = serie.QueryCons

for k, v := range serie.Tags {
transformed.Tags[k] = v
}
transformed.Tags["absolute"] = "1"
for _, p := range serie.Datapoints {
p.Val = math.Abs(p.Val)
transformed.Datapoints = append(transformed.Datapoints, p)
}
cache[Req{}] = append(cache[Req{}], *transformed)
}

return out, nil
}
124 changes: 124 additions & 0 deletions expr/func_absolute_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package expr

import (
"math"
"strconv"
"testing"

"github.com/grafana/metrictank/api/models"
"github.com/grafana/metrictank/test"
"github.com/raintank/schema"
)

func getNewAbsolute(in []models.Series) *FuncAbsolute {
f := NewAbsolute()
ps := f.(*FuncAbsolute)
ps.in = NewMock(in)
return ps
}

var random = []schema.Point{
{Val: 0, Ts: 10},
{Val: -10, Ts: 20},
{Val: 5.5, Ts: 30},
{Val: math.NaN(), Ts: 40},
{Val: -math.MaxFloat64, Ts: 50},
{Val: -1234567890, Ts: 60},
{Val: math.MaxFloat64, Ts: 70},
}

var randomAbsolute = []schema.Point{
{Val: 0, Ts: 10},
{Val: 10, Ts: 20},
{Val: 5.5, Ts: 30},
{Val: math.NaN(), Ts: 40},
{Val: math.MaxFloat64, Ts: 50},
{Val: 1234567890, Ts: 60},
{Val: math.MaxFloat64, Ts: 70},
}

func TestAbsoluteRandom(t *testing.T) {
f := getNewAbsolute(
[]models.Series{
{
Interval: 10,
QueryPatt: "random",
Target: "rand",
Datapoints: getCopy(random),
},
},
)
out := []models.Series{
{
Interval: 10,
QueryPatt: "absolute(random)",
Target: "absolute(rand)",
Datapoints: getCopy(randomAbsolute),
},
}

got, err := f.Exec(make(map[Req][]models.Series))
if err := equalOutput(out, got, nil, err); err != nil {
t.Fatal(err)
}
}
func BenchmarkAbsolute10k_1NoNulls(b *testing.B) {
benchmarkAbsolute(b, 1, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkAbsolute10k_10NoNulls(b *testing.B) {
benchmarkAbsolute(b, 10, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkAbsolute10k_100NoNulls(b *testing.B) {
benchmarkAbsolute(b, 100, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkAbsolute10k_1000NoNulls(b *testing.B) {
benchmarkAbsolute(b, 1000, test.RandFloats10k, test.RandFloats10k)
}
func BenchmarkAbsolute10k_1SomeSeriesHalfNulls(b *testing.B) {
benchmarkAbsolute(b, 1, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkAbsolute10k_10SomeSeriesHalfNulls(b *testing.B) {
benchmarkAbsolute(b, 10, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkAbsolute10k_100SomeSeriesHalfNulls(b *testing.B) {
benchmarkAbsolute(b, 100, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkAbsolute10k_1000SomeSeriesHalfNulls(b *testing.B) {
benchmarkAbsolute(b, 1000, test.RandFloats10k, test.RandFloatsWithNulls10k)
}
func BenchmarkAbsolute10k_1AllSeriesHalfNulls(b *testing.B) {
benchmarkAbsolute(b, 1, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}
func BenchmarkAbsolute10k_10AllSeriesHalfNulls(b *testing.B) {
benchmarkAbsolute(b, 10, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}
func BenchmarkAbsolute10k_100AllSeriesHalfNulls(b *testing.B) {
benchmarkAbsolute(b, 100, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}
func BenchmarkAbsolute10k_1000AllSeriesHalfNulls(b *testing.B) {
benchmarkAbsolute(b, 1000, test.RandFloatsWithNulls10k, test.RandFloatsWithNulls10k)
}
func benchmarkAbsolute(b *testing.B, numSeries int, fn0, fn1 func() []schema.Point) {
var input []models.Series
for i := 0; i < numSeries; i++ {
series := models.Series{
QueryPatt: strconv.Itoa(i),
}
if i%2 == 0 {
series.Datapoints = fn0()
} else {
series.Datapoints = fn1()
}
input = append(input, series)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
f := NewAbsolute()
f.(*FuncAbsolute).in = NewMock(input)
got, err := f.Exec(make(map[Req][]models.Series))
if err != nil {
b.Fatalf("%s", err)
}
results = got
}
}
1 change: 1 addition & 0 deletions expr/funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ var funcs map[string]funcDef
func init() {
// keys must be sorted alphabetically. but functions with aliases can go together, in which case they are sorted by the first of their aliases
funcs = map[string]funcDef{
"absolute": {NewAbsolute, true},
"alias": {NewAlias, true},
"aliasByTags": {NewAliasByNode, true},
"aliasByNode": {NewAliasByNode, true},
Expand Down