Skip to content

Commit

Permalink
Merge pull request #343 from atlassian/jdu/coerce-non-numeric-values
Browse files Browse the repository at this point in the history
Coerce non-numeric values to numeric in the datadog backend
  • Loading branch information
digitalpoetry authored Sep 10, 2020
2 parents 3ea558e + fe43bd2 commit 9ca2a2e
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 5 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
28.3.0
------
- Rather than dropping data points, the Datadog backend will coerce non-numeric values resulting from aggregation to numeric.
`NaN` is converted to -1, `+Inf` to float64 maximum, and `-Inf` to float64 minimum.

28.2.1
------
- Document how semver is used in the codebase.
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a h1:GmsqmapfzSJkm28dhRoHz2tLRbJmqhU86IPgBtN3mmk=
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s=
Expand Down
20 changes: 15 additions & 5 deletions pkg/backends/datadog/flush.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,31 @@ func (f *flush) addMetricf(metricType metricType, value float64, source gostatsd
}

// addMetric adds a metric to the series.
// If the value is non-numeric (in the case of NaN and Inf values), the value is coerced into a numeric value.
func (f *flush) addMetric(metricType metricType, value float64, source gostatsd.Source, tags gostatsd.Tags, name string) {
if math.IsInf(value, 1) || math.IsInf(value, -1) || math.IsNaN(value) {
// The value can not be represented within the JSON payload so it is to be discarded.
return
}
f.ts.Series = append(f.ts.Series, metric{
Host: string(source),
Interval: f.flushIntervalSec,
Metric: name,
Points: [1]point{{f.timestamp, value}},
Points: [1]point{{f.timestamp, coerceToNumeric(value)}},
Tags: tags,
Type: metricType,
})
}

// coerceToNumeric will convert non-numeric NaN and Inf values to a numeric value.
// If v is a numeric, the same value is returned.
func coerceToNumeric(v float64) float64 {
if math.IsNaN(v) {
return -1
} else if math.IsInf(v, 1) {
return math.MaxFloat64
} else if math.IsInf(v, -1) {
return -math.MaxFloat64
}
return v
}

func (f *flush) maybeFlush() {
if uint(len(f.ts.Series))+20 >= f.metricsPerBatch { // flush before it reaches max size and grows the slice
f.cb(f.ts)
Expand Down
31 changes: 31 additions & 0 deletions pkg/backends/datadog/flush_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package datadog

import (
"math"
"testing"
)

func TestCoerceToNumeric(t *testing.T) {
t.Parallel()
tests := []struct {
name string
arg float64
want float64
}{
{"NaN should return 0", math.NaN(), -1},
{"+Inf should return maximum float value", math.Inf(+1), math.MaxFloat64},
{"-Inf should return minimum float value", math.Inf(-1), -math.MaxFloat64},
{"Numeric value should return unchanged", 0, 0},
{"Numeric value should return unchanged", 12_345, 12_345},
{"Numeric value should return unchanged", -12_345, -12_345},
{"Numeric value should return unchanged", math.MaxFloat64, math.MaxFloat64},
{"-Inf should return minimum float value", -math.MaxFloat64, -math.MaxFloat64},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := coerceToNumeric(tt.arg); got != tt.want {
t.Errorf("coerceToNumeric() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit 9ca2a2e

Please sign in to comment.