Add quantile aggregator plugin (#8594)
This commit is contained in:
parent
a5385a2557
commit
b6b5d34060
8
go.mod
8
go.mod
|
|
@ -36,7 +36,7 @@ require (
|
||||||
github.com/bitly/go-hostpool v0.1.0 // indirect
|
github.com/bitly/go-hostpool v0.1.0 // indirect
|
||||||
github.com/bmatcuk/doublestar/v3 v3.0.0
|
github.com/bmatcuk/doublestar/v3 v3.0.0
|
||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869
|
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869
|
||||||
github.com/caio/go-tdigest v2.3.0+incompatible // indirect
|
github.com/caio/go-tdigest v3.1.0+incompatible
|
||||||
github.com/cenkalti/backoff v2.0.0+incompatible // indirect
|
github.com/cenkalti/backoff v2.0.0+incompatible // indirect
|
||||||
github.com/cisco-ie/nx-telemetry-proto v0.0.0-20190531143454-82441e232cf6
|
github.com/cisco-ie/nx-telemetry-proto v0.0.0-20190531143454-82441e232cf6
|
||||||
github.com/cockroachdb/apd v1.1.0 // indirect
|
github.com/cockroachdb/apd v1.1.0 // indirect
|
||||||
|
|
@ -92,7 +92,6 @@ require (
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||||
github.com/kubernetes/apimachinery v0.0.0-20190119020841-d41becfba9ee
|
github.com/kubernetes/apimachinery v0.0.0-20190119020841-d41becfba9ee
|
||||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353 // indirect
|
|
||||||
github.com/lib/pq v1.3.0 // indirect
|
github.com/lib/pq v1.3.0 // indirect
|
||||||
github.com/mailru/easyjson v0.0.0-20180717111219-efc7eb8984d6 // indirect
|
github.com/mailru/easyjson v0.0.0-20180717111219-efc7eb8984d6 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1
|
github.com/matttproud/golang_protobuf_extensions v1.0.1
|
||||||
|
|
@ -135,7 +134,7 @@ require (
|
||||||
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect
|
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect
|
||||||
github.com/vjeantet/grok v1.0.1
|
github.com/vjeantet/grok v1.0.1
|
||||||
github.com/vmware/govmomi v0.19.0
|
github.com/vmware/govmomi v0.19.0
|
||||||
github.com/wavefronthq/wavefront-sdk-go v0.9.2
|
github.com/wavefronthq/wavefront-sdk-go v0.9.7
|
||||||
github.com/wvanbergen/kafka v0.0.0-20171203153745-e2edea948ddf
|
github.com/wvanbergen/kafka v0.0.0-20171203153745-e2edea948ddf
|
||||||
github.com/wvanbergen/kazoo-go v0.0.0-20180202103751-f72d8611297a // indirect
|
github.com/wvanbergen/kazoo-go v0.0.0-20180202103751-f72d8611297a // indirect
|
||||||
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c
|
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c
|
||||||
|
|
@ -150,7 +149,6 @@ require (
|
||||||
golang.org/x/text v0.3.3
|
golang.org/x/text v0.3.3
|
||||||
golang.org/x/tools v0.0.0-20200317043434-63da46f3035e // indirect
|
golang.org/x/tools v0.0.0-20200317043434-63da46f3035e // indirect
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200205215550-e35592f146e4
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200205215550-e35592f146e4
|
||||||
gonum.org/v1/gonum v0.6.2 // indirect
|
|
||||||
google.golang.org/api v0.20.0
|
google.golang.org/api v0.20.0
|
||||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884
|
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884
|
||||||
google.golang.org/grpc v1.33.1
|
google.golang.org/grpc v1.33.1
|
||||||
|
|
@ -159,7 +157,7 @@ require (
|
||||||
gopkg.in/ldap.v3 v3.1.0
|
gopkg.in/ldap.v3 v3.1.0
|
||||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce
|
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce
|
||||||
gopkg.in/olivere/elastic.v5 v5.0.70
|
gopkg.in/olivere/elastic.v5 v5.0.70
|
||||||
gopkg.in/yaml.v2 v2.2.8
|
gopkg.in/yaml.v2 v2.3.0
|
||||||
gotest.tools v2.2.0+incompatible
|
gotest.tools v2.2.0+incompatible
|
||||||
honnef.co/go/tools v0.0.1-2020.1.3 // indirect
|
honnef.co/go/tools v0.0.1-2020.1.3 // indirect
|
||||||
k8s.io/apimachinery v0.17.1 // indirect
|
k8s.io/apimachinery v0.17.1 // indirect
|
||||||
|
|
|
||||||
15
go.sum
15
go.sum
|
|
@ -146,8 +146,8 @@ github.com/bmatcuk/doublestar/v3 v3.0.0 h1:TQtVPlDnAYwcrVNB2JiGuMc++H5qzWZd9PhkN
|
||||||
github.com/bmatcuk/doublestar/v3 v3.0.0/go.mod h1:6PcTVMw80pCY1RVuoqu3V++99uQB3vsSYKPTd8AWA0k=
|
github.com/bmatcuk/doublestar/v3 v3.0.0/go.mod h1:6PcTVMw80pCY1RVuoqu3V++99uQB3vsSYKPTd8AWA0k=
|
||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||||
github.com/caio/go-tdigest v2.3.0+incompatible h1:zP6nR0nTSUzlSqqr7F/LhslPlSZX/fZeGmgmwj2cxxY=
|
github.com/caio/go-tdigest v3.1.0+incompatible h1:uoVMJ3Q5lXmVLCCqaMGHLBWnbGoN6Lpu7OAUPR60cds=
|
||||||
github.com/caio/go-tdigest v2.3.0+incompatible/go.mod h1:sHQM/ubZStBUmF1WbB8FAm8q9GjDajLC5T7ydxE3JHI=
|
github.com/caio/go-tdigest v3.1.0+incompatible/go.mod h1:sHQM/ubZStBUmF1WbB8FAm8q9GjDajLC5T7ydxE3JHI=
|
||||||
github.com/cenkalti/backoff v2.0.0+incompatible h1:5IIPUHhlnUZbcHQsQou5k1Tn58nJkeJL9U+ig5CHJbY=
|
github.com/cenkalti/backoff v2.0.0+incompatible h1:5IIPUHhlnUZbcHQsQou5k1Tn58nJkeJL9U+ig5CHJbY=
|
||||||
github.com/cenkalti/backoff v2.0.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
github.com/cenkalti/backoff v2.0.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
|
@ -607,6 +607,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/tbrandon/mbserver v0.0.0-20170611213546-993e1772cc62 h1:Oj2e7Sae4XrOsk3ij21QjjEgAcVSeo9nkp0dI//cD2o=
|
github.com/tbrandon/mbserver v0.0.0-20170611213546-993e1772cc62 h1:Oj2e7Sae4XrOsk3ij21QjjEgAcVSeo9nkp0dI//cD2o=
|
||||||
|
|
@ -628,8 +629,8 @@ github.com/vjeantet/grok v1.0.1 h1:2rhIR7J4gThTgcZ1m2JY4TrJZNgjn985U28kT2wQrJ4=
|
||||||
github.com/vjeantet/grok v1.0.1/go.mod h1:ax1aAchzC6/QMXMcyzHQGZWaW1l195+uMYIkCWPCNIo=
|
github.com/vjeantet/grok v1.0.1/go.mod h1:ax1aAchzC6/QMXMcyzHQGZWaW1l195+uMYIkCWPCNIo=
|
||||||
github.com/vmware/govmomi v0.19.0 h1:CR6tEByWCPOnRoRyhLzuHaU+6o2ybF3qufNRWS/MGrY=
|
github.com/vmware/govmomi v0.19.0 h1:CR6tEByWCPOnRoRyhLzuHaU+6o2ybF3qufNRWS/MGrY=
|
||||||
github.com/vmware/govmomi v0.19.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
|
github.com/vmware/govmomi v0.19.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
|
||||||
github.com/wavefronthq/wavefront-sdk-go v0.9.2 h1:/LvWgZYNjHFUg+ZUX+qv+7e+M8sEMi0lM15zPp681Gk=
|
github.com/wavefronthq/wavefront-sdk-go v0.9.7 h1:SrtABcXXeKCW5SerQYsnCzHo15GeggjZmL+DjtTy6CI=
|
||||||
github.com/wavefronthq/wavefront-sdk-go v0.9.2/go.mod h1:hQI6y8M9OtTCtc0xdwh+dCER4osxXdEAeCpacjpDZEU=
|
github.com/wavefronthq/wavefront-sdk-go v0.9.7/go.mod h1:JTGsu+KKgxx+GitC65VVdftN2iep1nVpQi/8EGR6v4Y=
|
||||||
github.com/wvanbergen/kafka v0.0.0-20171203153745-e2edea948ddf h1:TOV5PC6fIWwFOFra9xJfRXZcL2pLhMI8oNuDugNxg9Q=
|
github.com/wvanbergen/kafka v0.0.0-20171203153745-e2edea948ddf h1:TOV5PC6fIWwFOFra9xJfRXZcL2pLhMI8oNuDugNxg9Q=
|
||||||
github.com/wvanbergen/kafka v0.0.0-20171203153745-e2edea948ddf/go.mod h1:nxx7XRXbR9ykhnC8lXqQyJS0rfvJGxKyKw/sT1YOttg=
|
github.com/wvanbergen/kafka v0.0.0-20171203153745-e2edea948ddf/go.mod h1:nxx7XRXbR9ykhnC8lXqQyJS0rfvJGxKyKw/sT1YOttg=
|
||||||
github.com/wvanbergen/kazoo-go v0.0.0-20180202103751-f72d8611297a h1:ILoU84rj4AQ3q6cjQvtb9jBjx4xzR/Riq/zYhmDQiOk=
|
github.com/wvanbergen/kazoo-go v0.0.0-20180202103751-f72d8611297a h1:ILoU84rj4AQ3q6cjQvtb9jBjx4xzR/Riq/zYhmDQiOk=
|
||||||
|
|
@ -851,8 +852,8 @@ golang.zx2c4.com/wireguard v0.0.20200121/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200205215550-e35592f146e4 h1:KTi97NIQGgSMaN0v/oxniJV0MEzfzmrDUOAWxombQVc=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200205215550-e35592f146e4 h1:KTi97NIQGgSMaN0v/oxniJV0MEzfzmrDUOAWxombQVc=
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200205215550-e35592f146e4/go.mod h1:UdS9frhv65KTfwxME1xE8+rHYoFpbm36gOud1GhBe9c=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200205215550-e35592f146e4/go.mod h1:UdS9frhv65KTfwxME1xE8+rHYoFpbm36gOud1GhBe9c=
|
||||||
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
|
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
|
||||||
gonum.org/v1/gonum v0.6.2 h1:4r+yNT0+8SWcOkXP+63H2zQbN+USnC73cjGUxnDF94Q=
|
gonum.org/v1/gonum v0.7.0 h1:Hdks0L0hgznZLG9nzXb8vZ0rRvqNvAcgAp84y7Mwkgw=
|
||||||
gonum.org/v1/gonum v0.6.2/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=
|
gonum.org/v1/gonum v0.7.0/go.mod h1:L02bwd0sqlsvRv41G7wGWFCsVNZFv/k1xzGIxeANHGM=
|
||||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc=
|
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc=
|
||||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
||||||
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
|
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
|
||||||
|
|
@ -958,6 +959,8 @@ gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
|
||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,6 @@ import (
|
||||||
_ "github.com/influxdata/telegraf/plugins/aggregators/histogram"
|
_ "github.com/influxdata/telegraf/plugins/aggregators/histogram"
|
||||||
_ "github.com/influxdata/telegraf/plugins/aggregators/merge"
|
_ "github.com/influxdata/telegraf/plugins/aggregators/merge"
|
||||||
_ "github.com/influxdata/telegraf/plugins/aggregators/minmax"
|
_ "github.com/influxdata/telegraf/plugins/aggregators/minmax"
|
||||||
|
_ "github.com/influxdata/telegraf/plugins/aggregators/quantile"
|
||||||
_ "github.com/influxdata/telegraf/plugins/aggregators/valuecounter"
|
_ "github.com/influxdata/telegraf/plugins/aggregators/valuecounter"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
# Quantile Aggregator Plugin
|
||||||
|
|
||||||
|
The quantile aggregator plugin aggregates specified quantiles for each numeric field
|
||||||
|
per metric it sees and emits the quantiles every `period`.
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[aggregators.quantile]]
|
||||||
|
## General Aggregator Arguments:
|
||||||
|
## The period on which to flush & clear the aggregator.
|
||||||
|
period = "30s"
|
||||||
|
|
||||||
|
## If true, the original metric will be dropped by the
|
||||||
|
## aggregator and will not get sent to the output plugins.
|
||||||
|
drop_original = false
|
||||||
|
|
||||||
|
## Quantiles to output in the range [0,1]
|
||||||
|
# quantiles = [0.25, 0.5, 0.75]
|
||||||
|
|
||||||
|
## Type of aggregation algorithm
|
||||||
|
## Supported are:
|
||||||
|
## "t-digest" -- approximation using centroids, can cope with large number of samples
|
||||||
|
## "exact R7" -- exact computation also used by Excel or NumPy (Hyndman & Fan 1996 R7)
|
||||||
|
## "exact R8" -- exact computation (Hyndman & Fan 1996 R8)
|
||||||
|
## NOTE: Do not use "exact" algorithms with large number of samples
|
||||||
|
## to not impair performance or memory consumption!
|
||||||
|
# algorithm = "t-digest"
|
||||||
|
|
||||||
|
## Compression for approximation (t-digest). The value needs to be
|
||||||
|
## greater or equal to 1.0. Smaller values will result in more
|
||||||
|
## performance but less accuracy.
|
||||||
|
# compression = 100.0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Algorithm types
|
||||||
|
##### t-digest
|
||||||
|
Proposed by [Dunning & Ertl (2019)][tdigest_paper] this type uses a
|
||||||
|
special data-structure to cluster data. These clusters are later used
|
||||||
|
to approximate the requested quantiles. The bounds of the approximation
|
||||||
|
can be controlled by the `compression` setting where smaller values
|
||||||
|
result in higher performance but less accuracy.
|
||||||
|
|
||||||
|
Due to its incremental nature, this algorithm can handle large
|
||||||
|
numbers of samples efficiently. It is recommended for applications
|
||||||
|
where exact quantile calculation isn't required.
|
||||||
|
|
||||||
|
For implementation details see the underlying [golang library][tdigest_lib].
|
||||||
|
|
||||||
|
##### exact R7 and R8
|
||||||
|
These algorithms compute quantiles as described in [Hyndman & Fan (1996)][hyndman_fan].
|
||||||
|
The R7 variant is used in Excel and NumPy. The R8 variant is recommended
|
||||||
|
by Hyndman & Fan due to its independence of the underlying sample distribution.
|
||||||
|
|
||||||
|
These algorithms save all data for the aggregation `period`. They require
|
||||||
|
a lot of memory when used with a large number of series or a
|
||||||
|
large number of samples. They are slower than the `t-digest`
|
||||||
|
algorithm and are recommended only to be used with a small number of samples and series.
|
||||||
|
|
||||||
|
|
||||||
|
#### Benchmark (linux/amd64)
|
||||||
|
The benchmark was performed by adding 100 metrics with six numeric
|
||||||
|
(and two non-numeric) fields to the aggregator and the derive the aggregation
|
||||||
|
result.
|
||||||
|
|
||||||
|
| algorithm | # quantiles | avg. runtime |
|
||||||
|
| :------------ | -------------:| -------------:|
|
||||||
|
| t-digest | 3 | 376372 ns/op |
|
||||||
|
| exact R7 | 3 | 9782946 ns/op |
|
||||||
|
| exact R8 | 3 | 9158205 ns/op |
|
||||||
|
| t-digest | 100 | 899204 ns/op |
|
||||||
|
| exact R7 | 100 | 7868816 ns/op |
|
||||||
|
| exact R8 | 100 | 8099612 ns/op |
|
||||||
|
|
||||||
|
### Measurements
|
||||||
|
Measurement names are passed trough this aggregator.
|
||||||
|
|
||||||
|
### Fields
|
||||||
|
|
||||||
|
For all numeric fields (int32/64, uint32/64 and float32/64) new *quantile*
|
||||||
|
fields are aggregated in the form `<fieldname>_<quantile*100>`. Other field
|
||||||
|
types (e.g. boolean, string) are ignored and dropped from the output.
|
||||||
|
|
||||||
|
For example passing in the following metric as *input*:
|
||||||
|
- somemetric
|
||||||
|
- average_response_ms (float64)
|
||||||
|
- minimum_response_ms (float64)
|
||||||
|
- maximum_response_ms (float64)
|
||||||
|
- status (string)
|
||||||
|
- ok (boolean)
|
||||||
|
|
||||||
|
and the default setting for `quantiles ` you get the following *output*
|
||||||
|
- somemetric
|
||||||
|
- average_response_ms_025 (float64)
|
||||||
|
- average_response_ms_050 (float64)
|
||||||
|
- average_response_ms_075 (float64)
|
||||||
|
- minimum_response_ms_025 (float64)
|
||||||
|
- minimum_response_ms_050 (float64)
|
||||||
|
- minimum_response_ms_075 (float64)
|
||||||
|
- maximum_response_ms_025 (float64)
|
||||||
|
- maximum_response_ms_050 (float64)
|
||||||
|
- maximum_response_ms_075 (float64)
|
||||||
|
|
||||||
|
The `status` and `ok` fields are dropped because they are not numeric. Note that the
|
||||||
|
number of resulting fields scales with the number of `quantiles` specified.
|
||||||
|
|
||||||
|
### Tags
|
||||||
|
|
||||||
|
Tags are passed through to the output by this aggregator.
|
||||||
|
|
||||||
|
### Example Output
|
||||||
|
|
||||||
|
```
|
||||||
|
cpu,cpu=cpu-total,host=Hugin usage_user=10.814851731872487,usage_system=2.1679541490155687,usage_irq=1.046598554697342,usage_steal=0,usage_guest_nice=0,usage_idle=85.79616247197244,usage_nice=0,usage_iowait=0,usage_softirq=0.1744330924495688,usage_guest=0 1608288360000000000
|
||||||
|
cpu,cpu=cpu-total,host=Hugin usage_guest=0,usage_system=2.1601016518428664,usage_iowait=0.02541296060990694,usage_irq=1.0165184243964942,usage_softirq=0.1778907242693666,usage_steal=0,usage_guest_nice=0,usage_user=9.275730622616953,usage_idle=87.34434561626493,usage_nice=0 1608288370000000000
|
||||||
|
cpu,cpu=cpu-total,host=Hugin usage_idle=85.78199052131747,usage_nice=0,usage_irq=1.0476428036915637,usage_guest=0,usage_guest_nice=0,usage_system=1.995510102269591,usage_iowait=0,usage_softirq=0.1995510102269662,usage_steal=0,usage_user=10.975305562484735 1608288380000000000
|
||||||
|
cpu,cpu=cpu-total,host=Hugin usage_guest_nice_075=0,usage_user_050=10.814851731872487,usage_guest_075=0,usage_steal_025=0,usage_irq_025=1.031558489546918,usage_irq_075=1.0471206791944527,usage_iowait_025=0,usage_guest_050=0,usage_guest_nice_050=0,usage_nice_075=0,usage_iowait_050=0,usage_system_050=2.1601016518428664,usage_irq_050=1.046598554697342,usage_guest_nice_025=0,usage_idle_050=85.79616247197244,usage_softirq_075=0.1887208672481664,usage_steal_075=0,usage_system_025=2.0778058770562287,usage_system_075=2.1640279004292173,usage_softirq_050=0.1778907242693666,usage_nice_050=0,usage_iowait_075=0.01270648030495347,usage_user_075=10.895078647178611,usage_nice_025=0,usage_steal_050=0,usage_user_025=10.04529117724472,usage_idle_025=85.78907649664495,usage_idle_075=86.57025404411868,usage_softirq_025=0.1761619083594677,usage_guest_025=0 1608288390000000000
|
||||||
|
```
|
||||||
|
|
||||||
|
# References
|
||||||
|
- Dunning & Ertl: "Computing Extremely Accurate Quantiles Using t-Digests", arXiv:1902.04023 (2019) [pdf][tdigest_paper]
|
||||||
|
- Hyndman & Fan: "Sample Quantiles in Statistical Packages", The American Statistician, vol. 50, pp. 361-365 (1996) [pdf][hyndman_fan]
|
||||||
|
|
||||||
|
|
||||||
|
[tdigest_paper]: https://arxiv.org/abs/1902.04023
|
||||||
|
[tdigest_lib]: https://github.com/caio/go-tdigest
|
||||||
|
[hyndman_fan]: http://www.maths.usyd.edu.au/u/UG/SM/STAT3022/r/current/Misc/Sample%20Quantiles%20in%20Statistical%20Packages.pdf
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
package quantile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/caio/go-tdigest"
|
||||||
|
)
|
||||||
|
|
||||||
|
type algorithm interface {
|
||||||
|
Add(value float64) error
|
||||||
|
Quantile(q float64) float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTDigest(compression float64) (algorithm, error) {
|
||||||
|
return tdigest.New(tdigest.Compression(compression))
|
||||||
|
}
|
||||||
|
|
||||||
|
type exactAlgorithmR7 struct {
|
||||||
|
xs []float64
|
||||||
|
sorted bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newExactR7(compression float64) (algorithm, error) {
|
||||||
|
return &exactAlgorithmR7{xs: make([]float64, 0, 100), sorted: false}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *exactAlgorithmR7) Add(value float64) error {
|
||||||
|
e.xs = append(e.xs, value)
|
||||||
|
e.sorted = false
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *exactAlgorithmR7) Quantile(q float64) float64 {
|
||||||
|
size := len(e.xs)
|
||||||
|
|
||||||
|
// No information
|
||||||
|
if len(e.xs) == 0 {
|
||||||
|
return math.NaN()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the array if necessary
|
||||||
|
if !e.sorted {
|
||||||
|
sort.Float64s(e.xs)
|
||||||
|
e.sorted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the quantile index and the fraction to the neighbor
|
||||||
|
// Hyndman & Fan; Sample Quantiles in Statistical Packages; The American Statistician vol 50; pp 361-365; 1996 -- R7
|
||||||
|
// Same as Excel and Numpy.
|
||||||
|
N := float64(size)
|
||||||
|
n := q * (N - 1)
|
||||||
|
i, gamma := math.Modf(n)
|
||||||
|
j := int(i)
|
||||||
|
if j < 0 {
|
||||||
|
return e.xs[0]
|
||||||
|
}
|
||||||
|
if j >= size {
|
||||||
|
return e.xs[size-1]
|
||||||
|
}
|
||||||
|
// Linear interpolation
|
||||||
|
return e.xs[j] + gamma*(e.xs[j+1]-e.xs[j])
|
||||||
|
}
|
||||||
|
|
||||||
|
type exactAlgorithmR8 struct {
|
||||||
|
xs []float64
|
||||||
|
sorted bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newExactR8(compression float64) (algorithm, error) {
|
||||||
|
return &exactAlgorithmR8{xs: make([]float64, 0, 100), sorted: false}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *exactAlgorithmR8) Add(value float64) error {
|
||||||
|
e.xs = append(e.xs, value)
|
||||||
|
e.sorted = false
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *exactAlgorithmR8) Quantile(q float64) float64 {
|
||||||
|
size := len(e.xs)
|
||||||
|
|
||||||
|
// No information
|
||||||
|
if size == 0 {
|
||||||
|
return math.NaN()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the array if necessary
|
||||||
|
if !e.sorted {
|
||||||
|
sort.Float64s(e.xs)
|
||||||
|
e.sorted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the quantile index and the fraction to the neighbor
|
||||||
|
// Hyndman & Fan; Sample Quantiles in Statistical Packages; The American Statistician vol 50; pp 361-365; 1996 -- R8
|
||||||
|
N := float64(size)
|
||||||
|
n := q*(N+1.0/3.0) - (2.0 / 3.0) // Indices are zero-base here but one-based in the paper
|
||||||
|
i, gamma := math.Modf(n)
|
||||||
|
j := int(i)
|
||||||
|
if j < 0 {
|
||||||
|
return e.xs[0]
|
||||||
|
}
|
||||||
|
if j >= size {
|
||||||
|
return e.xs[size-1]
|
||||||
|
}
|
||||||
|
// Linear interpolation
|
||||||
|
return e.xs[j] + gamma*(e.xs[j+1]-e.xs[j])
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,165 @@
|
||||||
|
package quantile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/plugins/aggregators"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Quantile struct {
|
||||||
|
Quantiles []float64 `toml:"quantiles"`
|
||||||
|
Compression float64 `toml:"compression"`
|
||||||
|
AlgorithmType string `toml:"algorithm"`
|
||||||
|
|
||||||
|
newAlgorithm newAlgorithmFunc
|
||||||
|
|
||||||
|
cache map[uint64]aggregate
|
||||||
|
suffixes []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type aggregate struct {
|
||||||
|
name string
|
||||||
|
fields map[string]algorithm
|
||||||
|
tags map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type newAlgorithmFunc func(compression float64) (algorithm, error)
|
||||||
|
|
||||||
|
var sampleConfig = `
|
||||||
|
## General Aggregator Arguments:
|
||||||
|
## The period on which to flush & clear the aggregator.
|
||||||
|
period = "30s"
|
||||||
|
|
||||||
|
## If true, the original metric will be dropped by the
|
||||||
|
## aggregator and will not get sent to the output plugins.
|
||||||
|
drop_original = false
|
||||||
|
|
||||||
|
## Quantiles to output in the range [0,1]
|
||||||
|
# quantiles = [0.25, 0.5, 0.75]
|
||||||
|
|
||||||
|
## Type of aggregation algorithm
|
||||||
|
## Supported are:
|
||||||
|
## "t-digest" -- approximation using centroids, can cope with large number of samples
|
||||||
|
## "exact R7" -- exact computation also used by Excel or NumPy (Hyndman & Fan 1996 R7)
|
||||||
|
## "exact R8" -- exact computation (Hyndman & Fan 1996 R8)
|
||||||
|
## NOTE: Do not use "exact" algorithms with large number of samples
|
||||||
|
## to not impair performance or memory consumption!
|
||||||
|
# algorithm = "t-digest"
|
||||||
|
|
||||||
|
## Compression for approximation (t-digest). The value needs to be
|
||||||
|
## greater or equal to 1.0. Smaller values will result in more
|
||||||
|
## performance but less accuracy.
|
||||||
|
# compression = 100.0
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Quantile) SampleConfig() string {
|
||||||
|
return sampleConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Quantile) Description() string {
|
||||||
|
return "Keep the aggregate quantiles of each metric passing through."
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Quantile) Add(in telegraf.Metric) {
|
||||||
|
id := in.HashID()
|
||||||
|
if cached, ok := q.cache[id]; ok {
|
||||||
|
fields := in.Fields()
|
||||||
|
for k, algo := range cached.fields {
|
||||||
|
if field, ok := fields[k]; ok {
|
||||||
|
if v, isconvertible := convert(field); isconvertible {
|
||||||
|
algo.Add(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// New metric, setup cache and init algorithm
|
||||||
|
a := aggregate{
|
||||||
|
name: in.Name(),
|
||||||
|
tags: in.Tags(),
|
||||||
|
fields: make(map[string]algorithm),
|
||||||
|
}
|
||||||
|
for k, field := range in.Fields() {
|
||||||
|
if v, isconvertible := convert(field); isconvertible {
|
||||||
|
// This should never error out as we tested it in Init()
|
||||||
|
algo, _ := q.newAlgorithm(q.Compression)
|
||||||
|
algo.Add(v)
|
||||||
|
a.fields[k] = algo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
q.cache[id] = a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Quantile) Push(acc telegraf.Accumulator) {
|
||||||
|
for _, aggregate := range q.cache {
|
||||||
|
fields := map[string]interface{}{}
|
||||||
|
for k, algo := range aggregate.fields {
|
||||||
|
for i, qtl := range q.Quantiles {
|
||||||
|
fields[k+q.suffixes[i]] = algo.Quantile(qtl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
acc.AddFields(aggregate.name, fields, aggregate.tags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Quantile) Reset() {
|
||||||
|
q.cache = make(map[uint64]aggregate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func convert(in interface{}) (float64, bool) {
|
||||||
|
switch v := in.(type) {
|
||||||
|
case float64:
|
||||||
|
return v, true
|
||||||
|
case int64:
|
||||||
|
return float64(v), true
|
||||||
|
case uint64:
|
||||||
|
return float64(v), true
|
||||||
|
default:
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Quantile) Init() error {
|
||||||
|
switch q.AlgorithmType {
|
||||||
|
case "t-digest", "":
|
||||||
|
q.newAlgorithm = newTDigest
|
||||||
|
case "exact R7":
|
||||||
|
q.newAlgorithm = newExactR7
|
||||||
|
case "exact R8":
|
||||||
|
q.newAlgorithm = newExactR8
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown algorithm type %q", q.AlgorithmType)
|
||||||
|
}
|
||||||
|
if _, err := q.newAlgorithm(q.Compression); err != nil {
|
||||||
|
return fmt.Errorf("cannot create %q algorithm: %v", q.AlgorithmType, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(q.Quantiles) == 0 {
|
||||||
|
q.Quantiles = []float64{0.25, 0.5, 0.75}
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicates := make(map[float64]bool)
|
||||||
|
q.suffixes = make([]string, len(q.Quantiles))
|
||||||
|
for i, qtl := range q.Quantiles {
|
||||||
|
if qtl < 0.0 || qtl > 1.0 {
|
||||||
|
return fmt.Errorf("quantile %v out of range", qtl)
|
||||||
|
}
|
||||||
|
if _, found := duplicates[qtl]; found {
|
||||||
|
return fmt.Errorf("duplicate quantile %v", qtl)
|
||||||
|
}
|
||||||
|
duplicates[qtl] = true
|
||||||
|
q.suffixes[i] = fmt.Sprintf("_%03d", int(qtl*100.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
q.Reset()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
aggregators.Add("quantile", func() telegraf.Aggregator {
|
||||||
|
return &Quantile{Compression: 100}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,635 @@
|
||||||
|
package quantile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfigInvalidAlgorithm(t *testing.T) {
|
||||||
|
q := Quantile{AlgorithmType: "a strange one"}
|
||||||
|
err := q.Init()
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "unknown algorithm type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigInvalidCompression(t *testing.T) {
|
||||||
|
q := Quantile{Compression: 0, AlgorithmType: "t-digest"}
|
||||||
|
err := q.Init()
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "cannot create \"t-digest\" algorithm")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigInvalidQuantiles(t *testing.T) {
|
||||||
|
q := Quantile{Compression: 100, Quantiles: []float64{-0.5}}
|
||||||
|
err := q.Init()
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "quantile -0.5 out of range")
|
||||||
|
|
||||||
|
q = Quantile{Compression: 100, Quantiles: []float64{1.5}}
|
||||||
|
err = q.Init()
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "quantile 1.5 out of range")
|
||||||
|
|
||||||
|
q = Quantile{Compression: 100, Quantiles: []float64{0.1, 0.2, 0.3, 0.1}}
|
||||||
|
err = q.Init()
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "duplicate quantile")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSingleMetricTDigest(t *testing.T) {
|
||||||
|
acc := testutil.Accumulator{}
|
||||||
|
|
||||||
|
q := Quantile{Compression: 100}
|
||||||
|
err := q.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"foo": "bar"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a_025": 24.75,
|
||||||
|
"a_050": 49.50,
|
||||||
|
"a_075": 74.25,
|
||||||
|
"b_025": 24.75,
|
||||||
|
"b_050": 49.50,
|
||||||
|
"b_075": 74.25,
|
||||||
|
"c_025": 24.75,
|
||||||
|
"c_050": 49.50,
|
||||||
|
"c_075": 74.25,
|
||||||
|
"d_025": 24.75,
|
||||||
|
"d_050": 49.50,
|
||||||
|
"d_075": 74.25,
|
||||||
|
"e_025": 24.75,
|
||||||
|
"e_050": 49.50,
|
||||||
|
"e_075": 74.25,
|
||||||
|
"f_025": 24.75,
|
||||||
|
"f_050": 49.50,
|
||||||
|
"f_075": 74.25,
|
||||||
|
"g_025": 0.2475,
|
||||||
|
"g_050": 0.4950,
|
||||||
|
"g_075": 0.7425,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
metrics := make([]telegraf.Metric, 100)
|
||||||
|
for i := range metrics {
|
||||||
|
metrics[i] = testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"foo": "bar"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a": int32(i),
|
||||||
|
"b": int64(i),
|
||||||
|
"c": uint32(i),
|
||||||
|
"d": uint64(i),
|
||||||
|
"e": float32(i),
|
||||||
|
"f": float64(i),
|
||||||
|
"g": float64(i) / 100.0,
|
||||||
|
"x1": "string",
|
||||||
|
"x2": true,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range metrics {
|
||||||
|
q.Add(m)
|
||||||
|
}
|
||||||
|
q.Push(&acc)
|
||||||
|
|
||||||
|
epsilon := cmpopts.EquateApprox(0, 1e-3)
|
||||||
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), epsilon)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultipleMetricsTDigest(t *testing.T) {
|
||||||
|
acc := testutil.Accumulator{}
|
||||||
|
|
||||||
|
q := Quantile{Compression: 100}
|
||||||
|
err := q.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"series": "foo"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a_025": 24.75, "a_050": 49.50, "a_075": 74.25,
|
||||||
|
"b_025": 24.75, "b_050": 49.50, "b_075": 74.25,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
),
|
||||||
|
testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"series": "bar"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a_025": 49.50, "a_050": 99.00, "a_075": 148.50,
|
||||||
|
"b_025": 49.50, "b_050": 99.00, "b_075": 148.50,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
metricsA := make([]telegraf.Metric, 100)
|
||||||
|
metricsB := make([]telegraf.Metric, 100)
|
||||||
|
for i := range metricsA {
|
||||||
|
metricsA[i] = testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"series": "foo"},
|
||||||
|
map[string]interface{}{"a": int64(i), "b": float64(i), "x1": "string", "x2": true},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for i := range metricsB {
|
||||||
|
metricsB[i] = testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"series": "bar"},
|
||||||
|
map[string]interface{}{"a": int64(2 * i), "b": float64(2 * i), "x1": "string", "x2": true},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range metricsA {
|
||||||
|
q.Add(m)
|
||||||
|
}
|
||||||
|
for _, m := range metricsB {
|
||||||
|
q.Add(m)
|
||||||
|
}
|
||||||
|
q.Push(&acc)
|
||||||
|
|
||||||
|
epsilon := cmpopts.EquateApprox(0, 1e-3)
|
||||||
|
sort := testutil.SortMetrics()
|
||||||
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), epsilon, sort)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSingleMetricExactR7(t *testing.T) {
|
||||||
|
acc := testutil.Accumulator{}
|
||||||
|
|
||||||
|
q := Quantile{AlgorithmType: "exact R7"}
|
||||||
|
err := q.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"foo": "bar"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a_025": 24.75,
|
||||||
|
"a_050": 49.50,
|
||||||
|
"a_075": 74.25,
|
||||||
|
"b_025": 24.75,
|
||||||
|
"b_050": 49.50,
|
||||||
|
"b_075": 74.25,
|
||||||
|
"c_025": 24.75,
|
||||||
|
"c_050": 49.50,
|
||||||
|
"c_075": 74.25,
|
||||||
|
"d_025": 24.75,
|
||||||
|
"d_050": 49.50,
|
||||||
|
"d_075": 74.25,
|
||||||
|
"e_025": 24.75,
|
||||||
|
"e_050": 49.50,
|
||||||
|
"e_075": 74.25,
|
||||||
|
"f_025": 24.75,
|
||||||
|
"f_050": 49.50,
|
||||||
|
"f_075": 74.25,
|
||||||
|
"g_025": 0.2475,
|
||||||
|
"g_050": 0.4950,
|
||||||
|
"g_075": 0.7425,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
metrics := make([]telegraf.Metric, 100)
|
||||||
|
for i := range metrics {
|
||||||
|
metrics[i] = testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"foo": "bar"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a": int32(i),
|
||||||
|
"b": int64(i),
|
||||||
|
"c": uint32(i),
|
||||||
|
"d": uint64(i),
|
||||||
|
"e": float32(i),
|
||||||
|
"f": float64(i),
|
||||||
|
"g": float64(i) / 100.0,
|
||||||
|
"x1": "string",
|
||||||
|
"x2": true,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range metrics {
|
||||||
|
q.Add(m)
|
||||||
|
}
|
||||||
|
q.Push(&acc)
|
||||||
|
|
||||||
|
epsilon := cmpopts.EquateApprox(0, 1e-3)
|
||||||
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), epsilon)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultipleMetricsExactR7(t *testing.T) {
|
||||||
|
acc := testutil.Accumulator{}
|
||||||
|
|
||||||
|
q := Quantile{AlgorithmType: "exact R7"}
|
||||||
|
err := q.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"series": "foo"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a_025": 24.75, "a_050": 49.50, "a_075": 74.25,
|
||||||
|
"b_025": 24.75, "b_050": 49.50, "b_075": 74.25,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
),
|
||||||
|
testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"series": "bar"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a_025": 49.50, "a_050": 99.00, "a_075": 148.50,
|
||||||
|
"b_025": 49.50, "b_050": 99.00, "b_075": 148.50,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
metricsA := make([]telegraf.Metric, 100)
|
||||||
|
metricsB := make([]telegraf.Metric, 100)
|
||||||
|
for i := range metricsA {
|
||||||
|
metricsA[i] = testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"series": "foo"},
|
||||||
|
map[string]interface{}{"a": int64(i), "b": float64(i), "x1": "string", "x2": true},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for i := range metricsB {
|
||||||
|
metricsB[i] = testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"series": "bar"},
|
||||||
|
map[string]interface{}{"a": int64(2 * i), "b": float64(2 * i), "x1": "string", "x2": true},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range metricsA {
|
||||||
|
q.Add(m)
|
||||||
|
}
|
||||||
|
for _, m := range metricsB {
|
||||||
|
q.Add(m)
|
||||||
|
}
|
||||||
|
q.Push(&acc)
|
||||||
|
|
||||||
|
epsilon := cmpopts.EquateApprox(0, 1e-3)
|
||||||
|
sort := testutil.SortMetrics()
|
||||||
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), epsilon, sort)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSingleMetricExactR8(t *testing.T) {
|
||||||
|
acc := testutil.Accumulator{}
|
||||||
|
|
||||||
|
q := Quantile{AlgorithmType: "exact R8"}
|
||||||
|
err := q.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"foo": "bar"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a_025": 24.417,
|
||||||
|
"a_050": 49.500,
|
||||||
|
"a_075": 74.583,
|
||||||
|
"b_025": 24.417,
|
||||||
|
"b_050": 49.500,
|
||||||
|
"b_075": 74.583,
|
||||||
|
"c_025": 24.417,
|
||||||
|
"c_050": 49.500,
|
||||||
|
"c_075": 74.583,
|
||||||
|
"d_025": 24.417,
|
||||||
|
"d_050": 49.500,
|
||||||
|
"d_075": 74.583,
|
||||||
|
"e_025": 24.417,
|
||||||
|
"e_050": 49.500,
|
||||||
|
"e_075": 74.583,
|
||||||
|
"f_025": 24.417,
|
||||||
|
"f_050": 49.500,
|
||||||
|
"f_075": 74.583,
|
||||||
|
"g_025": 0.24417,
|
||||||
|
"g_050": 0.49500,
|
||||||
|
"g_075": 0.74583,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
metrics := make([]telegraf.Metric, 100)
|
||||||
|
for i := range metrics {
|
||||||
|
metrics[i] = testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"foo": "bar"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a": int32(i),
|
||||||
|
"b": int64(i),
|
||||||
|
"c": uint32(i),
|
||||||
|
"d": uint64(i),
|
||||||
|
"e": float32(i),
|
||||||
|
"f": float64(i),
|
||||||
|
"g": float64(i) / 100.0,
|
||||||
|
"x1": "string",
|
||||||
|
"x2": true,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range metrics {
|
||||||
|
q.Add(m)
|
||||||
|
}
|
||||||
|
q.Push(&acc)
|
||||||
|
|
||||||
|
epsilon := cmpopts.EquateApprox(0, 1e-3)
|
||||||
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), epsilon)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultipleMetricsExactR8(t *testing.T) {
|
||||||
|
acc := testutil.Accumulator{}
|
||||||
|
|
||||||
|
q := Quantile{AlgorithmType: "exact R8"}
|
||||||
|
err := q.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"series": "foo"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a_025": 24.417, "a_050": 49.500, "a_075": 74.583,
|
||||||
|
"b_025": 24.417, "b_050": 49.500, "b_075": 74.583,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
),
|
||||||
|
testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"series": "bar"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a_025": 48.833, "a_050": 99.000, "a_075": 149.167,
|
||||||
|
"b_025": 48.833, "b_050": 99.000, "b_075": 149.167,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
metricsA := make([]telegraf.Metric, 100)
|
||||||
|
metricsB := make([]telegraf.Metric, 100)
|
||||||
|
for i := range metricsA {
|
||||||
|
metricsA[i] = testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"series": "foo"},
|
||||||
|
map[string]interface{}{"a": int64(i), "b": float64(i), "x1": "string", "x2": true},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for i := range metricsB {
|
||||||
|
metricsB[i] = testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"series": "bar"},
|
||||||
|
map[string]interface{}{"a": int64(2 * i), "b": float64(2 * i), "x1": "string", "x2": true},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range metricsA {
|
||||||
|
q.Add(m)
|
||||||
|
}
|
||||||
|
for _, m := range metricsB {
|
||||||
|
q.Add(m)
|
||||||
|
}
|
||||||
|
q.Push(&acc)
|
||||||
|
|
||||||
|
epsilon := cmpopts.EquateApprox(0, 1e-3)
|
||||||
|
sort := testutil.SortMetrics()
|
||||||
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime(), epsilon, sort)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDefaultTDigest(b *testing.B) {
|
||||||
|
metrics := make([]telegraf.Metric, 100)
|
||||||
|
for i := range metrics {
|
||||||
|
metrics[i] = testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"foo": "bar"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a": rand.Int31(),
|
||||||
|
"b": rand.Int63(),
|
||||||
|
"c": rand.Uint32(),
|
||||||
|
"d": rand.Uint64(),
|
||||||
|
"e": rand.Float32(),
|
||||||
|
"f": rand.Float64(),
|
||||||
|
"x1": "string",
|
||||||
|
"x2": true,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
q := Quantile{Compression: 100}
|
||||||
|
err := q.Init()
|
||||||
|
require.NoError(b, err)
|
||||||
|
|
||||||
|
acc := testutil.Accumulator{}
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
for _, m := range metrics {
|
||||||
|
q.Add(m)
|
||||||
|
}
|
||||||
|
q.Push(&acc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDefaultTDigest100Q(b *testing.B) {
|
||||||
|
metrics := make([]telegraf.Metric, 100)
|
||||||
|
for i := range metrics {
|
||||||
|
metrics[i] = testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"foo": "bar"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a": rand.Int31(),
|
||||||
|
"b": rand.Int63(),
|
||||||
|
"c": rand.Uint32(),
|
||||||
|
"d": rand.Uint64(),
|
||||||
|
"e": rand.Float32(),
|
||||||
|
"f": rand.Float64(),
|
||||||
|
"x1": "string",
|
||||||
|
"x2": true,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
quantiles := make([]float64, 100)
|
||||||
|
for i := range quantiles {
|
||||||
|
quantiles[i] = 0.01 * float64(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
q := Quantile{Compression: 100, Quantiles: quantiles}
|
||||||
|
err := q.Init()
|
||||||
|
require.NoError(b, err)
|
||||||
|
|
||||||
|
acc := testutil.Accumulator{}
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
for _, m := range metrics {
|
||||||
|
q.Add(m)
|
||||||
|
}
|
||||||
|
q.Push(&acc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDefaultExactR7(b *testing.B) {
|
||||||
|
metrics := make([]telegraf.Metric, 100)
|
||||||
|
for i := range metrics {
|
||||||
|
metrics[i] = testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"foo": "bar"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a": rand.Int31(),
|
||||||
|
"b": rand.Int63(),
|
||||||
|
"c": rand.Uint32(),
|
||||||
|
"d": rand.Uint64(),
|
||||||
|
"e": rand.Float32(),
|
||||||
|
"f": rand.Float64(),
|
||||||
|
"x1": "string",
|
||||||
|
"x2": true,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
q := Quantile{AlgorithmType: "exact R7"}
|
||||||
|
err := q.Init()
|
||||||
|
require.NoError(b, err)
|
||||||
|
|
||||||
|
acc := testutil.Accumulator{}
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
for _, m := range metrics {
|
||||||
|
q.Add(m)
|
||||||
|
}
|
||||||
|
q.Push(&acc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDefaultExactR7100Q(b *testing.B) {
|
||||||
|
metrics := make([]telegraf.Metric, 100)
|
||||||
|
for i := range metrics {
|
||||||
|
metrics[i] = testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"foo": "bar"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a": rand.Int31(),
|
||||||
|
"b": rand.Int63(),
|
||||||
|
"c": rand.Uint32(),
|
||||||
|
"d": rand.Uint64(),
|
||||||
|
"e": rand.Float32(),
|
||||||
|
"f": rand.Float64(),
|
||||||
|
"x1": "string",
|
||||||
|
"x2": true,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
quantiles := make([]float64, 100)
|
||||||
|
for i := range quantiles {
|
||||||
|
quantiles[i] = 0.01 * float64(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
q := Quantile{AlgorithmType: "exact R7", Quantiles: quantiles}
|
||||||
|
err := q.Init()
|
||||||
|
require.NoError(b, err)
|
||||||
|
|
||||||
|
acc := testutil.Accumulator{}
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
for _, m := range metrics {
|
||||||
|
q.Add(m)
|
||||||
|
}
|
||||||
|
q.Push(&acc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDefaultExactR8(b *testing.B) {
|
||||||
|
metrics := make([]telegraf.Metric, 100)
|
||||||
|
for i := range metrics {
|
||||||
|
metrics[i] = testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"foo": "bar"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a": rand.Int31(),
|
||||||
|
"b": rand.Int63(),
|
||||||
|
"c": rand.Uint32(),
|
||||||
|
"d": rand.Uint64(),
|
||||||
|
"e": rand.Float32(),
|
||||||
|
"f": rand.Float64(),
|
||||||
|
"x1": "string",
|
||||||
|
"x2": true,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
q := Quantile{AlgorithmType: "exact R8"}
|
||||||
|
err := q.Init()
|
||||||
|
require.NoError(b, err)
|
||||||
|
|
||||||
|
acc := testutil.Accumulator{}
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
for _, m := range metrics {
|
||||||
|
q.Add(m)
|
||||||
|
}
|
||||||
|
q.Push(&acc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDefaultExactR8100Q(b *testing.B) {
|
||||||
|
metrics := make([]telegraf.Metric, 100)
|
||||||
|
for i := range metrics {
|
||||||
|
metrics[i] = testutil.MustMetric(
|
||||||
|
"test",
|
||||||
|
map[string]string{"foo": "bar"},
|
||||||
|
map[string]interface{}{
|
||||||
|
"a": rand.Int31(),
|
||||||
|
"b": rand.Int63(),
|
||||||
|
"c": rand.Uint32(),
|
||||||
|
"d": rand.Uint64(),
|
||||||
|
"e": rand.Float32(),
|
||||||
|
"f": rand.Float64(),
|
||||||
|
"x1": "string",
|
||||||
|
"x2": true,
|
||||||
|
},
|
||||||
|
time.Now(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
quantiles := make([]float64, 100)
|
||||||
|
for i := range quantiles {
|
||||||
|
quantiles[i] = 0.01 * float64(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
q := Quantile{AlgorithmType: "exact R8", Quantiles: quantiles}
|
||||||
|
err := q.Init()
|
||||||
|
require.NoError(b, err)
|
||||||
|
|
||||||
|
acc := testutil.Accumulator{}
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
for _, m := range metrics {
|
||||||
|
q.Add(m)
|
||||||
|
}
|
||||||
|
q.Push(&acc)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue