diff --git a/pkg/streamingpromql/engine_test.go b/pkg/streamingpromql/engine_test.go index b1c87f495fc..74d857d82e1 100644 --- a/pkg/streamingpromql/engine_test.go +++ b/pkg/streamingpromql/engine_test.go @@ -2824,6 +2824,8 @@ func TestQueryStats(t *testing.T) { start_series 0 1 _ _ _ _ _ _ _ _ _ end_series _ _ _ _ _ 5 6 7 8 9 10 sparse_series 0 _ _ _ _ _ _ 7 _ _ _ + nan_series NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN + native_histogram_series {{schema:0 sum:2 count:4 buckets:[1 2 1]}} {{sum:2 count:4 buckets:[1 2 1]}} `) runQueryAndGetTotalSamples := func(t *testing.T, engine promql.QueryEngine, expr string, isInstantQuery bool) int64 { @@ -2904,6 +2906,23 @@ func TestQueryStats(t *testing.T) { expr: `dense_series{} + end_series{}`, expectedTotalSamples: 11 + 6, }, + "instant vector selector with NaNs": { + expr: `nan_series{}`, + isInstantQuery: true, + expectedTotalSamples: 1, + }, + "range vector selector with NaNs": { + expr: `sum_over_time(nan_series{}[1m])`, + expectedTotalSamples: 11, + }, + "instant vector selector with native histograms": { + expr: `native_histogram_series{}`, + expectedTotalSamples: 78, + }, + "range vector selector with native histograms": { + expr: `sum_over_time(native_histogram_series{}[1m])`, + expectedTotalSamples: 26, + }, } for name, testCase := range testCases { diff --git a/pkg/streamingpromql/operators/selectors/instant_vector_selector.go b/pkg/streamingpromql/operators/selectors/instant_vector_selector.go index ca7b10f5b17..8acb397cf20 100644 --- a/pkg/streamingpromql/operators/selectors/instant_vector_selector.go +++ b/pkg/streamingpromql/operators/selectors/instant_vector_selector.go @@ -120,8 +120,6 @@ func (v *InstantVectorSelector) NextSeries(ctx context.Context) (types.InstantVe continue } - v.Stats.TotalSamples++ - // if (f, h) have been set by PeekPrev, we do not know if f is 0 because that's the actual value, or because // the previous value had a histogram. // PeekPrev will set the histogram to nil, or the value to 0 if the other type exists. @@ -146,6 +144,12 @@ func (v *InstantVectorSelector) NextSeries(ctx context.Context) (types.InstantVe data.Histograms = append(data.Histograms, promql.HPoint{T: stepT, H: h}) lastHistogramT = t lastHistogram = h + + // For consistency with PromQL engine: + // h.Size() returns the size of the histogram in bytes, + // add 8 bytes to account for the timestamp, + // and divide by 16 to get the number of samples. + v.Stats.TotalSamples += int64((h.Size() + 8) / 16) } else { // Only create the slice once we know the series is a histogram or not. if len(data.Floats) == 0 { @@ -157,6 +161,7 @@ func (v *InstantVectorSelector) NextSeries(ctx context.Context) (types.InstantVe } } data.Floats = append(data.Floats, promql.FPoint{T: stepT, F: f}) + v.Stats.TotalSamples++ } } diff --git a/pkg/streamingpromql/operators/selectors/range_vector_selector.go b/pkg/streamingpromql/operators/selectors/range_vector_selector.go index 9f410f2fd46..1a272728a51 100644 --- a/pkg/streamingpromql/operators/selectors/range_vector_selector.go +++ b/pkg/streamingpromql/operators/selectors/range_vector_selector.go @@ -104,7 +104,7 @@ func (m *RangeVectorSelector) NextStepSamples() (*types.RangeVectorStepData, err m.stepData.RangeStart = rangeStart m.stepData.RangeEnd = rangeEnd - m.Stats.TotalSamples += int64(m.stepData.Floats.Count() + m.stepData.Histograms.Count()) + m.Stats.TotalSamples += int64(m.stepData.Floats.Count() + m.stepData.Histograms.CountSamples()) return m.stepData, nil } diff --git a/pkg/streamingpromql/types/hpoint_ring_buffer.go b/pkg/streamingpromql/types/hpoint_ring_buffer.go index e7c7293ff29..eddb2c797ba 100644 --- a/pkg/streamingpromql/types/hpoint_ring_buffer.go +++ b/pkg/streamingpromql/types/hpoint_ring_buffer.go @@ -301,6 +301,21 @@ func (v HPointRingBufferView) Count() int { return v.size } +// CountSamples returns the size of the HPointRingBufferView compared to the size of an FPointRingBufferView. +// For consistency with PromQL engine: +// H.Size() returns the size of the histogram in bytes, +// add 8 bytes to account for the timestamp, +// and divide by 16 to get the number of samples. +func (v HPointRingBufferView) CountSamples() int { + var totalSize int + for i := 0; i < v.size; i++ { + totalSize += v.buffer.pointAt(i).H.Size() + 8 + } + totalSize = totalSize / 16 + + return totalSize +} + // Any returns true if this ring buffer view contains any points. func (v HPointRingBufferView) Any() bool { return v.size != 0