Return NaN when summaries have no observations yet.

Also, properly handle decay upon Write().

This fixes https://github.com/prometheus/client_golang/issues/85 .
This commit is contained in:
beorn7 2015-03-15 13:58:14 +01:00
parent 7af92a21b3
commit 715be73ac9
2 changed files with 21 additions and 5 deletions

View File

@ -16,6 +16,7 @@ package prometheus
import ( import (
"fmt" "fmt"
"hash/fnv" "hash/fnv"
"math"
"sort" "sort"
"sync" "sync"
"time" "time"
@ -277,10 +278,8 @@ func (s *summary) Write(out *dto.Metric) error {
s.bufMtx.Lock() s.bufMtx.Lock()
s.mtx.Lock() s.mtx.Lock()
// Swap bufs even if hotBuf is empty to set new hotBufExpTime.
if len(s.hotBuf) != 0 { s.swapBufs(time.Now())
s.swapBufs(time.Now())
}
s.bufMtx.Unlock() s.bufMtx.Unlock()
s.flushColdBuf() s.flushColdBuf()
@ -288,9 +287,15 @@ func (s *summary) Write(out *dto.Metric) error {
sum.SampleSum = proto.Float64(s.sum) sum.SampleSum = proto.Float64(s.sum)
for _, rank := range s.sortedObjectives { for _, rank := range s.sortedObjectives {
var q float64
if s.headStream.Count() == 0 {
q = math.NaN()
} else {
q = s.headStream.Query(rank)
}
qs = append(qs, &dto.Quantile{ qs = append(qs, &dto.Quantile{
Quantile: proto.Float64(rank), Quantile: proto.Float64(rank),
Value: proto.Float64(s.headStream.Query(rank)), Value: proto.Float64(q),
}) })
} }

View File

@ -289,6 +289,11 @@ func TestSummaryVecConcurrency(t *testing.T) {
} }
func TestSummaryDecay(t *testing.T) { func TestSummaryDecay(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test in short mode.")
// More because it depends on timing than because it is particularly long...
}
sum := NewSummary(SummaryOpts{ sum := NewSummary(SummaryOpts{
Name: "test_summary", Name: "test_summary",
Help: "helpless", Help: "helpless",
@ -315,6 +320,12 @@ func TestSummaryDecay(t *testing.T) {
} }
} }
tick.Stop() tick.Stop()
// Wait for MaxAge without observations and make sure quantiles are NaN.
time.Sleep(100 * time.Millisecond)
sum.Write(m)
if got := *m.Summary.Quantile[0].Value; !math.IsNaN(got) {
t.Errorf("got %f, want NaN after expiration", got)
}
} }
func getBounds(vars []float64, q, ε float64) (min, max float64) { func getBounds(vars []float64, q, ε float64) (min, max float64) {