|
|
|
@ -17,6 +17,7 @@ import (
|
|
|
|
|
"context"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"math"
|
|
|
|
|
"sort"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
@ -29,11 +30,13 @@ import (
|
|
|
|
|
"github.com/prometheus/prometheus/model/histogram"
|
|
|
|
|
"github.com/prometheus/prometheus/model/labels"
|
|
|
|
|
"github.com/prometheus/prometheus/model/timestamp"
|
|
|
|
|
"github.com/prometheus/prometheus/model/value"
|
|
|
|
|
"github.com/prometheus/prometheus/promql"
|
|
|
|
|
"github.com/prometheus/prometheus/promql/parser"
|
|
|
|
|
"github.com/prometheus/prometheus/promql/parser/posrange"
|
|
|
|
|
"github.com/prometheus/prometheus/promql/promqltest"
|
|
|
|
|
"github.com/prometheus/prometheus/storage"
|
|
|
|
|
"github.com/prometheus/prometheus/tsdb/chunkenc"
|
|
|
|
|
"github.com/prometheus/prometheus/util/annotations"
|
|
|
|
|
"github.com/prometheus/prometheus/util/stats"
|
|
|
|
|
"github.com/prometheus/prometheus/util/teststorage"
|
|
|
|
@ -3781,3 +3784,115 @@ func TestRateAnnotations(t *testing.T) {
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestHistogramRateWithFloatStaleness(t *testing.T) {
|
|
|
|
|
// Make a chunk with two normal histograms of the same value.
|
|
|
|
|
h1 := histogram.Histogram{
|
|
|
|
|
Schema: 2,
|
|
|
|
|
Count: 10,
|
|
|
|
|
Sum: 100,
|
|
|
|
|
PositiveSpans: []histogram.Span{{Offset: 0, Length: 1}},
|
|
|
|
|
PositiveBuckets: []int64{100},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c1 := chunkenc.NewHistogramChunk()
|
|
|
|
|
app, err := c1.Appender()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
var (
|
|
|
|
|
newc chunkenc.Chunk
|
|
|
|
|
recoded bool
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
newc, recoded, app, err = app.AppendHistogram(nil, 0, h1.Copy(), false)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.False(t, recoded)
|
|
|
|
|
require.Nil(t, newc)
|
|
|
|
|
|
|
|
|
|
newc, recoded, _, err = app.AppendHistogram(nil, 10, h1.Copy(), false)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.False(t, recoded)
|
|
|
|
|
require.Nil(t, newc)
|
|
|
|
|
|
|
|
|
|
// Make a chunk with a single float stale marker.
|
|
|
|
|
c2 := chunkenc.NewXORChunk()
|
|
|
|
|
app, err = c2.Appender()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
app.Append(20, math.Float64frombits(value.StaleNaN))
|
|
|
|
|
|
|
|
|
|
// Make a chunk with two normal histograms that have zero value.
|
|
|
|
|
h2 := histogram.Histogram{
|
|
|
|
|
Schema: 2,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c3 := chunkenc.NewHistogramChunk()
|
|
|
|
|
app, err = c3.Appender()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
newc, recoded, app, err = app.AppendHistogram(nil, 30, h2.Copy(), false)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.False(t, recoded)
|
|
|
|
|
require.Nil(t, newc)
|
|
|
|
|
|
|
|
|
|
newc, recoded, _, err = app.AppendHistogram(nil, 40, h2.Copy(), false)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.False(t, recoded)
|
|
|
|
|
require.Nil(t, newc)
|
|
|
|
|
|
|
|
|
|
querier := storage.MockQuerier{
|
|
|
|
|
SelectMockFunction: func(_ bool, _ *storage.SelectHints, _ ...*labels.Matcher) storage.SeriesSet {
|
|
|
|
|
return &singleSeriesSet{
|
|
|
|
|
series: mockSeries{chunks: []chunkenc.Chunk{c1, c2, c3}, labelSet: []string{"__name__", "foo"}},
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
queriable := storage.MockQueryable{MockQuerier: &querier}
|
|
|
|
|
|
|
|
|
|
engine := promqltest.NewTestEngine(t, false, 0, promqltest.DefaultMaxSamplesPerQuery)
|
|
|
|
|
|
|
|
|
|
q, err := engine.NewInstantQuery(context.Background(), &queriable, nil, "rate(foo[40s])", timestamp.Time(45))
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
defer q.Close()
|
|
|
|
|
|
|
|
|
|
res := q.Exec(context.Background())
|
|
|
|
|
require.NoError(t, res.Err)
|
|
|
|
|
|
|
|
|
|
vec, err := res.Vector()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
// Single sample result.
|
|
|
|
|
require.Len(t, vec, 1)
|
|
|
|
|
// The result is a histogram.
|
|
|
|
|
require.NotNil(t, vec[0].H)
|
|
|
|
|
// The result should be zero as the histogram has not increased, so the rate is zero.
|
|
|
|
|
require.Equal(t, 0.0, vec[0].H.Count)
|
|
|
|
|
require.Equal(t, 0.0, vec[0].H.Sum)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type singleSeriesSet struct {
|
|
|
|
|
series storage.Series
|
|
|
|
|
consumed bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *singleSeriesSet) Next() bool { c := s.consumed; s.consumed = true; return !c }
|
|
|
|
|
func (s singleSeriesSet) At() storage.Series { return s.series }
|
|
|
|
|
func (s singleSeriesSet) Err() error { return nil }
|
|
|
|
|
func (s singleSeriesSet) Warnings() annotations.Annotations { return nil }
|
|
|
|
|
|
|
|
|
|
type mockSeries struct {
|
|
|
|
|
chunks []chunkenc.Chunk
|
|
|
|
|
labelSet []string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s mockSeries) Labels() labels.Labels {
|
|
|
|
|
return labels.FromStrings(s.labelSet...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s mockSeries) Iterator(it chunkenc.Iterator) chunkenc.Iterator {
|
|
|
|
|
iterables := []chunkenc.Iterator{}
|
|
|
|
|
for _, c := range s.chunks {
|
|
|
|
|
iterables = append(iterables, c.Iterator(nil))
|
|
|
|
|
}
|
|
|
|
|
return storage.ChainSampleIteratorFromIterators(it, iterables)
|
|
|
|
|
}
|
|
|
|
|