Alerting: Handle NaN in reducers (#22053)

- Fix bug with NaN in alerting - Closes #21953
- Alert reducers (avg/max/etc) drop null values from their calculation. This change makes it so NaN values are handled in the same way as null values.
This commit is contained in:
Andrii Melnyk 2020-02-11 01:28:07 +01:00 committed by GitHub
parent 26e294c29b
commit 47314d0f13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 29 additions and 9 deletions

View File

@ -29,7 +29,7 @@ func (s *queryReducer) Reduce(series *tsdb.TimeSeries) null.Float {
case "avg":
validPointsCount := 0
for _, point := range series.Points {
if point[0].Valid {
if isValid(point[0]) {
value += point[0].Float64
validPointsCount++
allNull = false
@ -40,7 +40,7 @@ func (s *queryReducer) Reduce(series *tsdb.TimeSeries) null.Float {
}
case "sum":
for _, point := range series.Points {
if point[0].Valid {
if isValid(point[0]) {
value += point[0].Float64
allNull = false
}
@ -48,7 +48,7 @@ func (s *queryReducer) Reduce(series *tsdb.TimeSeries) null.Float {
case "min":
value = math.MaxFloat64
for _, point := range series.Points {
if point[0].Valid {
if isValid(point[0]) {
allNull = false
if value > point[0].Float64 {
value = point[0].Float64
@ -58,7 +58,7 @@ func (s *queryReducer) Reduce(series *tsdb.TimeSeries) null.Float {
case "max":
value = -math.MaxFloat64
for _, point := range series.Points {
if point[0].Valid {
if isValid(point[0]) {
allNull = false
if value < point[0].Float64 {
value = point[0].Float64
@ -71,7 +71,7 @@ func (s *queryReducer) Reduce(series *tsdb.TimeSeries) null.Float {
case "last":
points := series.Points
for i := len(points) - 1; i >= 0; i-- {
if points[i][0].Valid {
if isValid(points[i][0]) {
value = points[i][0].Float64
allNull = false
break
@ -80,7 +80,7 @@ func (s *queryReducer) Reduce(series *tsdb.TimeSeries) null.Float {
case "median":
var values []float64
for _, v := range series.Points {
if v[0].Valid {
if isValid(v[0]) {
allNull = false
values = append(values, v[0].Float64)
}
@ -100,7 +100,7 @@ func (s *queryReducer) Reduce(series *tsdb.TimeSeries) null.Float {
allNull, value = calculateDiff(series, allNull, value, percentDiff)
case "count_non_null":
for _, v := range series.Points {
if v[0].Valid {
if isValid(v[0]) {
value++
}
}
@ -129,7 +129,7 @@ func calculateDiff(series *tsdb.TimeSeries, allNull bool, value float64, fn func
)
// get the newest point
for i = len(points) - 1; i >= 0; i-- {
if points[i][0].Valid {
if isValid(points[i][0]) {
allNull = false
first = points[i][0].Float64
break
@ -139,7 +139,7 @@ func calculateDiff(series *tsdb.TimeSeries, allNull bool, value float64, fn func
// get the oldest point
points = points[0:i]
for i := 0; i < len(points); i++ {
if points[i][0].Valid {
if isValid(points[i][0]) {
allNull = false
val := fn(first, points[i][0].Float64)
value = math.Abs(val)
@ -150,6 +150,10 @@ func calculateDiff(series *tsdb.TimeSeries, allNull bool, value float64, fn func
return allNull, value
}
func isValid(f null.Float) bool {
return f.Valid && !math.IsNaN(f.Float64)
}
var diff = func(newest, oldest float64) float64 {
return newest - oldest
}

View File

@ -1,6 +1,7 @@
package conditions
import (
"math"
"testing"
. "github.com/smartystreets/goconvey/convey"
@ -181,6 +182,21 @@ func TestSimpleReducer(t *testing.T) {
So(reducer.Reduce(series).Valid, ShouldEqual, false)
})
Convey("min should work with NaNs", func() {
result := testReducer("min", math.NaN(), math.NaN(), math.NaN())
So(result, ShouldEqual, float64(0))
})
Convey("isValid should treat NaN as invalid", func() {
result := isValid(null.FloatFrom(math.NaN()))
So(result, ShouldBeFalse)
})
Convey("isValid should treat invalid null.Float as invalid", func() {
result := isValid(null.FloatFromPtr(nil))
So(result, ShouldBeFalse)
})
})
}