diff --git a/pkg/services/alerting/conditions/reducer.go b/pkg/services/alerting/conditions/reducer.go index bf57110ea1c..9a5097b8dde 100644 --- a/pkg/services/alerting/conditions/reducer.go +++ b/pkg/services/alerting/conditions/reducer.go @@ -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 } diff --git a/pkg/services/alerting/conditions/reducer_test.go b/pkg/services/alerting/conditions/reducer_test.go index eac71378f3d..7f522f24f6b 100644 --- a/pkg/services/alerting/conditions/reducer_test.go +++ b/pkg/services/alerting/conditions/reducer_test.go @@ -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) + }) }) }