app/vmselect/promql: remove NaNs from /api/v1/query_range output like Prometheus does

Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/153
This commit is contained in:
Aliaksandr Valialkin 2019-08-20 22:52:49 +03:00
parent a0d480fbf3
commit 7b5168adfb
2 changed files with 72 additions and 0 deletions

View file

@ -574,12 +574,47 @@ func QueryRangeHandler(w http.ResponseWriter, r *http.Request) error {
result = adjustLastPoints(result) result = adjustLastPoints(result)
} }
// Remove NaN values as Prometheus does.
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/153
removeNaNValuesInplace(result)
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
WriteQueryRangeResponse(w, result) WriteQueryRangeResponse(w, result)
queryRangeDuration.UpdateDuration(startTime) queryRangeDuration.UpdateDuration(startTime)
return nil return nil
} }
func removeNaNValuesInplace(tss []netstorage.Result) {
for i := range tss {
ts := &tss[i]
hasNaNs := false
for _, v := range ts.Values {
if math.IsNaN(v) {
hasNaNs = true
break
}
}
if !hasNaNs {
// Fast path: nothing to remove.
continue
}
// Slow path: remove NaNs.
srcTimestamps := ts.Timestamps
dstValues := ts.Values[:0]
dstTimestamps := ts.Timestamps[:0]
for j, v := range ts.Values {
if math.IsNaN(v) {
continue
}
dstValues = append(dstValues, v)
dstTimestamps = append(dstTimestamps, srcTimestamps[j])
}
ts.Values = dstValues
ts.Timestamps = dstTimestamps
}
}
var queryRangeDuration = metrics.NewSummary(`vm_request_duration_seconds{path="/api/v1/query_range"}`) var queryRangeDuration = metrics.NewSummary(`vm_request_duration_seconds{path="/api/v1/query_range"}`)
// adjustLastPoints substitutes the last point values with the previous // adjustLastPoints substitutes the last point values with the previous

View file

@ -2,11 +2,48 @@ package prometheus
import ( import (
"fmt" "fmt"
"math"
"net/http" "net/http"
"net/url" "net/url"
"reflect"
"testing" "testing"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/netstorage"
) )
func TestRemoveNaNValuesInplace(t *testing.T) {
f := func(tss []netstorage.Result, tssExpected []netstorage.Result) {
t.Helper()
removeNaNValuesInplace(tss)
if !reflect.DeepEqual(tss, tssExpected) {
t.Fatalf("unexpected result; got %v; want %v", tss, tssExpected)
}
}
nan := math.NaN()
f(nil, nil)
f([]netstorage.Result{
{
Timestamps: []int64{100, 200, 300},
Values: []float64{1, 2, 3},
},
{
Timestamps: []int64{100, 200, 300, 400},
Values: []float64{nan, nan, 3, nan},
},
}, []netstorage.Result{
{
Timestamps: []int64{100, 200, 300},
Values: []float64{1, 2, 3},
},
{
Timestamps: []int64{300},
Values: []float64{3},
},
})
}
func TestGetTimeSuccess(t *testing.T) { func TestGetTimeSuccess(t *testing.T) {
f := func(s string, timestampExpected int64) { f := func(s string, timestampExpected int64) {
t.Helper() t.Helper()