192 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Go
		
	
	
	
// Copyright 2015 The Prometheus Authors
 | 
						|
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
// you may not use this file except in compliance with the License.
 | 
						|
// You may obtain a copy of the License at
 | 
						|
//
 | 
						|
// http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
//
 | 
						|
// Unless required by applicable law or agreed to in writing, software
 | 
						|
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
// See the License for the specific language governing permissions and
 | 
						|
// limitations under the License.
 | 
						|
 | 
						|
package promql
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"math"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	"github.com/stretchr/testify/require"
 | 
						|
 | 
						|
	"github.com/prometheus/prometheus/model/histogram"
 | 
						|
	"github.com/prometheus/prometheus/model/labels"
 | 
						|
	"github.com/prometheus/prometheus/model/value"
 | 
						|
	"github.com/prometheus/prometheus/tsdb/chunkenc"
 | 
						|
	"github.com/prometheus/prometheus/tsdb/tsdbutil"
 | 
						|
)
 | 
						|
 | 
						|
func TestHistogramStatsDecoding(t *testing.T) {
 | 
						|
	cases := []struct {
 | 
						|
		name          string
 | 
						|
		histograms    []*histogram.Histogram
 | 
						|
		expectedHints []histogram.CounterResetHint
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name: "unknown counter reset triggers detection",
 | 
						|
			histograms: []*histogram.Histogram{
 | 
						|
				tsdbutil.GenerateTestHistogramWithHint(0, histogram.NotCounterReset),
 | 
						|
				tsdbutil.GenerateTestHistogramWithHint(1, histogram.UnknownCounterReset),
 | 
						|
				tsdbutil.GenerateTestHistogramWithHint(2, histogram.CounterReset),
 | 
						|
				tsdbutil.GenerateTestHistogramWithHint(2, histogram.UnknownCounterReset),
 | 
						|
			},
 | 
						|
			expectedHints: []histogram.CounterResetHint{
 | 
						|
				histogram.NotCounterReset,
 | 
						|
				histogram.NotCounterReset,
 | 
						|
				histogram.CounterReset,
 | 
						|
				histogram.NotCounterReset,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "stale sample before unknown reset hint",
 | 
						|
			histograms: []*histogram.Histogram{
 | 
						|
				tsdbutil.GenerateTestHistogramWithHint(0, histogram.NotCounterReset),
 | 
						|
				tsdbutil.GenerateTestHistogramWithHint(1, histogram.UnknownCounterReset),
 | 
						|
				{Sum: math.Float64frombits(value.StaleNaN)},
 | 
						|
				tsdbutil.GenerateTestHistogramWithHint(1, histogram.UnknownCounterReset),
 | 
						|
			},
 | 
						|
			expectedHints: []histogram.CounterResetHint{
 | 
						|
				histogram.NotCounterReset,
 | 
						|
				histogram.NotCounterReset,
 | 
						|
				histogram.UnknownCounterReset,
 | 
						|
				histogram.NotCounterReset,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "unknown counter reset at the beginning",
 | 
						|
			histograms: []*histogram.Histogram{
 | 
						|
				tsdbutil.GenerateTestHistogramWithHint(1, histogram.UnknownCounterReset),
 | 
						|
			},
 | 
						|
			expectedHints: []histogram.CounterResetHint{
 | 
						|
				histogram.NotCounterReset,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "detect real counter reset",
 | 
						|
			histograms: []*histogram.Histogram{
 | 
						|
				tsdbutil.GenerateTestHistogramWithHint(2, histogram.UnknownCounterReset),
 | 
						|
				tsdbutil.GenerateTestHistogramWithHint(1, histogram.UnknownCounterReset),
 | 
						|
			},
 | 
						|
			expectedHints: []histogram.CounterResetHint{
 | 
						|
				histogram.NotCounterReset,
 | 
						|
				histogram.CounterReset,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "detect real counter reset after stale NaN",
 | 
						|
			histograms: []*histogram.Histogram{
 | 
						|
				tsdbutil.GenerateTestHistogramWithHint(2, histogram.UnknownCounterReset),
 | 
						|
				{Sum: math.Float64frombits(value.StaleNaN)},
 | 
						|
				tsdbutil.GenerateTestHistogramWithHint(1, histogram.UnknownCounterReset),
 | 
						|
			},
 | 
						|
			expectedHints: []histogram.CounterResetHint{
 | 
						|
				histogram.NotCounterReset,
 | 
						|
				histogram.UnknownCounterReset,
 | 
						|
				histogram.CounterReset,
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, tc := range cases {
 | 
						|
		t.Run(tc.name, func(t *testing.T) {
 | 
						|
			t.Run("histogram_stats", func(t *testing.T) {
 | 
						|
				decodedStats := make([]*histogram.Histogram, 0)
 | 
						|
				statsIterator := NewHistogramStatsIterator(newHistogramSeries(tc.histograms).Iterator(nil))
 | 
						|
				for statsIterator.Next() != chunkenc.ValNone {
 | 
						|
					_, h := statsIterator.AtHistogram(nil)
 | 
						|
					decodedStats = append(decodedStats, h)
 | 
						|
				}
 | 
						|
				for i := 0; i < len(tc.histograms); i++ {
 | 
						|
					require.Equal(t, tc.expectedHints[i], decodedStats[i].CounterResetHint, fmt.Sprintf("mismatch in counter reset hint for histogram %d", i))
 | 
						|
					h := tc.histograms[i]
 | 
						|
					if value.IsStaleNaN(h.Sum) {
 | 
						|
						require.True(t, value.IsStaleNaN(decodedStats[i].Sum))
 | 
						|
						require.Equal(t, uint64(0), decodedStats[i].Count)
 | 
						|
					} else {
 | 
						|
						require.Equal(t, tc.histograms[i].Count, decodedStats[i].Count)
 | 
						|
						require.Equal(t, tc.histograms[i].Sum, decodedStats[i].Sum)
 | 
						|
					}
 | 
						|
				}
 | 
						|
			})
 | 
						|
			t.Run("float_histogram_stats", func(t *testing.T) {
 | 
						|
				decodedStats := make([]*histogram.FloatHistogram, 0)
 | 
						|
				statsIterator := NewHistogramStatsIterator(newHistogramSeries(tc.histograms).Iterator(nil))
 | 
						|
				for statsIterator.Next() != chunkenc.ValNone {
 | 
						|
					_, h := statsIterator.AtFloatHistogram(nil)
 | 
						|
					decodedStats = append(decodedStats, h)
 | 
						|
				}
 | 
						|
				for i := 0; i < len(tc.histograms); i++ {
 | 
						|
					require.Equal(t, tc.expectedHints[i], decodedStats[i].CounterResetHint)
 | 
						|
					fh := tc.histograms[i].ToFloat(nil)
 | 
						|
					if value.IsStaleNaN(fh.Sum) {
 | 
						|
						require.True(t, value.IsStaleNaN(decodedStats[i].Sum))
 | 
						|
						require.Equal(t, float64(0), decodedStats[i].Count)
 | 
						|
					} else {
 | 
						|
						require.Equal(t, fh.Count, decodedStats[i].Count)
 | 
						|
						require.Equal(t, fh.Sum, decodedStats[i].Sum)
 | 
						|
					}
 | 
						|
				}
 | 
						|
			})
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type histogramSeries struct {
 | 
						|
	histograms []*histogram.Histogram
 | 
						|
}
 | 
						|
 | 
						|
func newHistogramSeries(histograms []*histogram.Histogram) *histogramSeries {
 | 
						|
	return &histogramSeries{
 | 
						|
		histograms: histograms,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (m histogramSeries) Labels() labels.Labels { return labels.EmptyLabels() }
 | 
						|
 | 
						|
func (m histogramSeries) Iterator(_ chunkenc.Iterator) chunkenc.Iterator {
 | 
						|
	return &histogramIterator{
 | 
						|
		i:          -1,
 | 
						|
		histograms: m.histograms,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type histogramIterator struct {
 | 
						|
	i          int
 | 
						|
	histograms []*histogram.Histogram
 | 
						|
}
 | 
						|
 | 
						|
func (h *histogramIterator) Next() chunkenc.ValueType {
 | 
						|
	h.i++
 | 
						|
	if h.i < len(h.histograms) {
 | 
						|
		return chunkenc.ValHistogram
 | 
						|
	}
 | 
						|
	return chunkenc.ValNone
 | 
						|
}
 | 
						|
 | 
						|
func (h *histogramIterator) Seek(t int64) chunkenc.ValueType { panic("not implemented") }
 | 
						|
 | 
						|
func (h *histogramIterator) At() (int64, float64) { panic("not implemented") }
 | 
						|
 | 
						|
func (h *histogramIterator) AtHistogram(_ *histogram.Histogram) (int64, *histogram.Histogram) {
 | 
						|
	return 0, h.histograms[h.i]
 | 
						|
}
 | 
						|
 | 
						|
func (h *histogramIterator) AtFloatHistogram(_ *histogram.FloatHistogram) (int64, *histogram.FloatHistogram) {
 | 
						|
	return 0, h.histograms[h.i].ToFloat(nil)
 | 
						|
}
 | 
						|
 | 
						|
func (h *histogramIterator) AtT() int64 { return 0 }
 | 
						|
 | 
						|
func (h *histogramIterator) Err() error { return nil }
 |