github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/util/metrics_helper_test.go (about)

     1  package util
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"math/rand"
     7  	"sort"
     8  	"strconv"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/gogo/protobuf/proto"
    13  	"github.com/prometheus/client_golang/prometheus"
    14  	"github.com/prometheus/client_golang/prometheus/promauto"
    15  	"github.com/prometheus/client_golang/prometheus/testutil"
    16  	dto "github.com/prometheus/client_model/go"
    17  	"github.com/prometheus/prometheus/model/labels"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  func TestSum(t *testing.T) {
    22  	require.Equal(t, float64(0), sum(nil, counterValue))
    23  	require.Equal(t, float64(0), sum(&dto.MetricFamily{Metric: nil}, counterValue))
    24  	require.Equal(t, float64(0), sum(&dto.MetricFamily{Metric: []*dto.Metric{{Counter: &dto.Counter{}}}}, counterValue))
    25  	require.Equal(t, 12345.6789, sum(&dto.MetricFamily{Metric: []*dto.Metric{{Counter: &dto.Counter{Value: proto.Float64(12345.6789)}}}}, counterValue))
    26  	require.Equal(t, 20235.80235, sum(&dto.MetricFamily{Metric: []*dto.Metric{
    27  		{Counter: &dto.Counter{Value: proto.Float64(12345.6789)}},
    28  		{Counter: &dto.Counter{Value: proto.Float64(7890.12345)}},
    29  	}}, counterValue))
    30  	// using 'counterValue' as function only sums counters
    31  	require.Equal(t, float64(0), sum(&dto.MetricFamily{Metric: []*dto.Metric{
    32  		{Gauge: &dto.Gauge{Value: proto.Float64(12345.6789)}},
    33  		{Gauge: &dto.Gauge{Value: proto.Float64(7890.12345)}},
    34  	}}, counterValue))
    35  }
    36  
    37  func TestMax(t *testing.T) {
    38  	require.Equal(t, float64(0), max(nil, counterValue))
    39  	require.Equal(t, float64(0), max(&dto.MetricFamily{Metric: nil}, counterValue))
    40  	require.Equal(t, float64(0), max(&dto.MetricFamily{Metric: []*dto.Metric{{Counter: &dto.Counter{}}}}, counterValue))
    41  	require.Equal(t, 12345.6789, max(&dto.MetricFamily{Metric: []*dto.Metric{{Counter: &dto.Counter{Value: proto.Float64(12345.6789)}}}}, counterValue))
    42  	require.Equal(t, 7890.12345, max(&dto.MetricFamily{Metric: []*dto.Metric{
    43  		{Counter: &dto.Counter{Value: proto.Float64(1234.56789)}},
    44  		{Counter: &dto.Counter{Value: proto.Float64(7890.12345)}},
    45  	}}, counterValue))
    46  	// using 'counterValue' as function only works on counters
    47  	require.Equal(t, float64(0), max(&dto.MetricFamily{Metric: []*dto.Metric{
    48  		{Gauge: &dto.Gauge{Value: proto.Float64(12345.6789)}},
    49  		{Gauge: &dto.Gauge{Value: proto.Float64(7890.12345)}},
    50  	}}, counterValue))
    51  }
    52  
    53  func TestCounterValue(t *testing.T) {
    54  	require.Equal(t, float64(0), counterValue(nil))
    55  	require.Equal(t, float64(0), counterValue(&dto.Metric{}))
    56  	require.Equal(t, float64(0), counterValue(&dto.Metric{Counter: &dto.Counter{}}))
    57  	require.Equal(t, float64(543857.12837), counterValue(&dto.Metric{Counter: &dto.Counter{Value: proto.Float64(543857.12837)}}))
    58  }
    59  
    60  func TestGetMetricsWithLabelNames(t *testing.T) {
    61  	labels := []string{"a", "b"}
    62  
    63  	require.Equal(t, map[string]metricsWithLabels{}, getMetricsWithLabelNames(nil, labels))
    64  	require.Equal(t, map[string]metricsWithLabels{}, getMetricsWithLabelNames(&dto.MetricFamily{}, labels))
    65  
    66  	m1 := &dto.Metric{Label: makeLabels("a", "5"), Counter: &dto.Counter{Value: proto.Float64(1)}}
    67  	m2 := &dto.Metric{Label: makeLabels("a", "10", "b", "20"), Counter: &dto.Counter{Value: proto.Float64(1.5)}}
    68  	m3 := &dto.Metric{Label: makeLabels("a", "10", "b", "20", "c", "1"), Counter: &dto.Counter{Value: proto.Float64(2)}}
    69  	m4 := &dto.Metric{Label: makeLabels("a", "10", "b", "20", "c", "2"), Counter: &dto.Counter{Value: proto.Float64(3)}}
    70  	m5 := &dto.Metric{Label: makeLabels("a", "11", "b", "21"), Counter: &dto.Counter{Value: proto.Float64(4)}}
    71  	m6 := &dto.Metric{Label: makeLabels("ignored", "123", "a", "12", "b", "22", "c", "30"), Counter: &dto.Counter{Value: proto.Float64(4)}}
    72  
    73  	out := getMetricsWithLabelNames(&dto.MetricFamily{Metric: []*dto.Metric{m1, m2, m3, m4, m5, m6}}, labels)
    74  
    75  	// m1 is not returned at all, as it doesn't have both required labels.
    76  	require.Equal(t, map[string]metricsWithLabels{
    77  		getLabelsString([]string{"10", "20"}): {
    78  			labelValues: []string{"10", "20"},
    79  			metrics:     []*dto.Metric{m2, m3, m4}},
    80  		getLabelsString([]string{"11", "21"}): {
    81  			labelValues: []string{"11", "21"},
    82  			metrics:     []*dto.Metric{m5}},
    83  		getLabelsString([]string{"12", "22"}): {
    84  			labelValues: []string{"12", "22"},
    85  			metrics:     []*dto.Metric{m6}},
    86  	}, out)
    87  
    88  	// no labels -- returns all metrics in single key. this isn't very efficient, and there are other functions
    89  	// (without labels) to handle this better, but it still works.
    90  	out2 := getMetricsWithLabelNames(&dto.MetricFamily{Metric: []*dto.Metric{m1, m2, m3, m4, m5, m6}}, nil)
    91  	require.Equal(t, map[string]metricsWithLabels{
    92  		getLabelsString(nil): {
    93  			labelValues: []string{},
    94  			metrics:     []*dto.Metric{m1, m2, m3, m4, m5, m6}},
    95  	}, out2)
    96  }
    97  
    98  func BenchmarkGetMetricsWithLabelNames(b *testing.B) {
    99  	const (
   100  		numMetrics         = 1000
   101  		numLabelsPerMetric = 10
   102  	)
   103  
   104  	// Generate metrics and add them to a metric family.
   105  	mf := &dto.MetricFamily{Metric: make([]*dto.Metric, 0, numMetrics)}
   106  	for i := 0; i < numMetrics; i++ {
   107  		labels := []*dto.LabelPair{{
   108  			Name:  proto.String("unique"),
   109  			Value: proto.String(strconv.Itoa(i)),
   110  		}}
   111  
   112  		for l := 1; l < numLabelsPerMetric; l++ {
   113  			labels = append(labels, &dto.LabelPair{
   114  				Name:  proto.String(fmt.Sprintf("label_%d", l)),
   115  				Value: proto.String(fmt.Sprintf("value_%d", l)),
   116  			})
   117  		}
   118  
   119  		mf.Metric = append(mf.Metric, &dto.Metric{
   120  			Label:   labels,
   121  			Counter: &dto.Counter{Value: proto.Float64(1.5)},
   122  		})
   123  	}
   124  
   125  	b.ResetTimer()
   126  	b.ReportAllocs()
   127  
   128  	for n := 0; n < b.N; n++ {
   129  		out := getMetricsWithLabelNames(mf, []string{"label_1", "label_2", "label_3"})
   130  
   131  		if expected := 1; len(out) != expected {
   132  			b.Fatalf("unexpected number of output groups: expected = %d got = %d", expected, len(out))
   133  		}
   134  	}
   135  }
   136  
   137  func makeLabels(namesAndValues ...string) []*dto.LabelPair {
   138  	out := []*dto.LabelPair(nil)
   139  
   140  	for i := 0; i+1 < len(namesAndValues); i = i + 2 {
   141  		out = append(out, &dto.LabelPair{
   142  			Name:  proto.String(namesAndValues[i]),
   143  			Value: proto.String(namesAndValues[i+1]),
   144  		})
   145  	}
   146  
   147  	return out
   148  }
   149  
   150  // TestSendSumOfGaugesPerUserWithLabels tests to ensure multiple metrics for the same user with a matching label are
   151  // summed correctly
   152  func TestSendSumOfGaugesPerUserWithLabels(t *testing.T) {
   153  	user1Metric := prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: "test_metric"}, []string{"label_one", "label_two"})
   154  	user2Metric := prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: "test_metric"}, []string{"label_one", "label_two"})
   155  	user1Metric.WithLabelValues("a", "b").Set(100)
   156  	user1Metric.WithLabelValues("a", "c").Set(80)
   157  	user2Metric.WithLabelValues("a", "b").Set(60)
   158  	user2Metric.WithLabelValues("a", "c").Set(40)
   159  
   160  	user1Reg := prometheus.NewRegistry()
   161  	user2Reg := prometheus.NewRegistry()
   162  	user1Reg.MustRegister(user1Metric)
   163  	user2Reg.MustRegister(user2Metric)
   164  
   165  	regs := NewUserRegistries()
   166  	regs.AddUserRegistry("user-1", user1Reg)
   167  	regs.AddUserRegistry("user-2", user2Reg)
   168  	mf := regs.BuildMetricFamiliesPerUser()
   169  
   170  	{
   171  		desc := prometheus.NewDesc("test_metric", "", []string{"user", "label_one"}, nil)
   172  		actual := collectMetrics(t, func(out chan prometheus.Metric) {
   173  			mf.SendSumOfGaugesPerUserWithLabels(out, desc, "test_metric", "label_one")
   174  		})
   175  		expected := []*dto.Metric{
   176  			{Label: makeLabels("label_one", "a", "user", "user-1"), Gauge: &dto.Gauge{Value: proto.Float64(180)}},
   177  			{Label: makeLabels("label_one", "a", "user", "user-2"), Gauge: &dto.Gauge{Value: proto.Float64(100)}},
   178  		}
   179  		require.ElementsMatch(t, expected, actual)
   180  	}
   181  
   182  	{
   183  		desc := prometheus.NewDesc("test_metric", "", []string{"user", "label_two"}, nil)
   184  		actual := collectMetrics(t, func(out chan prometheus.Metric) {
   185  			mf.SendSumOfGaugesPerUserWithLabels(out, desc, "test_metric", "label_two")
   186  		})
   187  		expected := []*dto.Metric{
   188  			{Label: makeLabels("label_two", "b", "user", "user-1"), Gauge: &dto.Gauge{Value: proto.Float64(100)}},
   189  			{Label: makeLabels("label_two", "c", "user", "user-1"), Gauge: &dto.Gauge{Value: proto.Float64(80)}},
   190  			{Label: makeLabels("label_two", "b", "user", "user-2"), Gauge: &dto.Gauge{Value: proto.Float64(60)}},
   191  			{Label: makeLabels("label_two", "c", "user", "user-2"), Gauge: &dto.Gauge{Value: proto.Float64(40)}},
   192  		}
   193  		require.ElementsMatch(t, expected, actual)
   194  	}
   195  
   196  	{
   197  		desc := prometheus.NewDesc("test_metric", "", []string{"user", "label_one", "label_two"}, nil)
   198  		actual := collectMetrics(t, func(out chan prometheus.Metric) {
   199  			mf.SendSumOfGaugesPerUserWithLabels(out, desc, "test_metric", "label_one", "label_two")
   200  		})
   201  		expected := []*dto.Metric{
   202  			{Label: makeLabels("label_one", "a", "label_two", "b", "user", "user-1"), Gauge: &dto.Gauge{Value: proto.Float64(100)}},
   203  			{Label: makeLabels("label_one", "a", "label_two", "c", "user", "user-1"), Gauge: &dto.Gauge{Value: proto.Float64(80)}},
   204  			{Label: makeLabels("label_one", "a", "label_two", "b", "user", "user-2"), Gauge: &dto.Gauge{Value: proto.Float64(60)}},
   205  			{Label: makeLabels("label_one", "a", "label_two", "c", "user", "user-2"), Gauge: &dto.Gauge{Value: proto.Float64(40)}},
   206  		}
   207  		require.ElementsMatch(t, expected, actual)
   208  	}
   209  }
   210  
   211  func TestSendMaxOfGauges(t *testing.T) {
   212  	user1Reg := prometheus.NewRegistry()
   213  	user2Reg := prometheus.NewRegistry()
   214  	desc := prometheus.NewDesc("test_metric", "", nil, nil)
   215  	regs := NewUserRegistries()
   216  	regs.AddUserRegistry("user-1", user1Reg)
   217  	regs.AddUserRegistry("user-2", user2Reg)
   218  
   219  	// No matching metric.
   220  	mf := regs.BuildMetricFamiliesPerUser()
   221  	actual := collectMetrics(t, func(out chan prometheus.Metric) {
   222  		mf.SendMaxOfGauges(out, desc, "test_metric")
   223  	})
   224  	expected := []*dto.Metric{
   225  		{Label: nil, Gauge: &dto.Gauge{Value: proto.Float64(0)}},
   226  	}
   227  	require.ElementsMatch(t, expected, actual)
   228  
   229  	// Register a metric for each user.
   230  	user1Metric := promauto.With(user1Reg).NewGauge(prometheus.GaugeOpts{Name: "test_metric"})
   231  	user2Metric := promauto.With(user2Reg).NewGauge(prometheus.GaugeOpts{Name: "test_metric"})
   232  	user1Metric.Set(100)
   233  	user2Metric.Set(80)
   234  	mf = regs.BuildMetricFamiliesPerUser()
   235  
   236  	actual = collectMetrics(t, func(out chan prometheus.Metric) {
   237  		mf.SendMaxOfGauges(out, desc, "test_metric")
   238  	})
   239  	expected = []*dto.Metric{
   240  		{Label: nil, Gauge: &dto.Gauge{Value: proto.Float64(100)}},
   241  	}
   242  	require.ElementsMatch(t, expected, actual)
   243  }
   244  
   245  func TestSendSumOfHistogramsWithLabels(t *testing.T) {
   246  	buckets := []float64{1, 2, 3}
   247  	user1Metric := prometheus.NewHistogramVec(prometheus.HistogramOpts{Name: "test_metric", Buckets: buckets}, []string{"label_one", "label_two"})
   248  	user2Metric := prometheus.NewHistogramVec(prometheus.HistogramOpts{Name: "test_metric", Buckets: buckets}, []string{"label_one", "label_two"})
   249  	user1Metric.WithLabelValues("a", "b").Observe(1)
   250  	user1Metric.WithLabelValues("a", "c").Observe(2)
   251  	user2Metric.WithLabelValues("a", "b").Observe(3)
   252  	user2Metric.WithLabelValues("a", "c").Observe(4)
   253  
   254  	user1Reg := prometheus.NewRegistry()
   255  	user2Reg := prometheus.NewRegistry()
   256  	user1Reg.MustRegister(user1Metric)
   257  	user2Reg.MustRegister(user2Metric)
   258  
   259  	regs := NewUserRegistries()
   260  	regs.AddUserRegistry("user-1", user1Reg)
   261  	regs.AddUserRegistry("user-2", user2Reg)
   262  	mf := regs.BuildMetricFamiliesPerUser()
   263  
   264  	{
   265  		desc := prometheus.NewDesc("test_metric", "", []string{"label_one"}, nil)
   266  		actual := collectMetrics(t, func(out chan prometheus.Metric) {
   267  			mf.SendSumOfHistogramsWithLabels(out, desc, "test_metric", "label_one")
   268  		})
   269  		expected := []*dto.Metric{
   270  			{Label: makeLabels("label_one", "a"), Histogram: &dto.Histogram{SampleCount: uint64p(4), SampleSum: float64p(10), Bucket: []*dto.Bucket{
   271  				{UpperBound: float64p(1), CumulativeCount: uint64p(1)},
   272  				{UpperBound: float64p(2), CumulativeCount: uint64p(2)},
   273  				{UpperBound: float64p(3), CumulativeCount: uint64p(3)},
   274  			}}},
   275  		}
   276  		require.ElementsMatch(t, expected, actual)
   277  	}
   278  
   279  	{
   280  		desc := prometheus.NewDesc("test_metric", "", []string{"label_two"}, nil)
   281  		actual := collectMetrics(t, func(out chan prometheus.Metric) {
   282  			mf.SendSumOfHistogramsWithLabels(out, desc, "test_metric", "label_two")
   283  		})
   284  		expected := []*dto.Metric{
   285  			{Label: makeLabels("label_two", "b"), Histogram: &dto.Histogram{SampleCount: uint64p(2), SampleSum: float64p(4), Bucket: []*dto.Bucket{
   286  				{UpperBound: float64p(1), CumulativeCount: uint64p(1)},
   287  				{UpperBound: float64p(2), CumulativeCount: uint64p(1)},
   288  				{UpperBound: float64p(3), CumulativeCount: uint64p(2)},
   289  			}}},
   290  			{Label: makeLabels("label_two", "c"), Histogram: &dto.Histogram{SampleCount: uint64p(2), SampleSum: float64p(6), Bucket: []*dto.Bucket{
   291  				{UpperBound: float64p(1), CumulativeCount: uint64p(0)},
   292  				{UpperBound: float64p(2), CumulativeCount: uint64p(1)},
   293  				{UpperBound: float64p(3), CumulativeCount: uint64p(1)},
   294  			}}},
   295  		}
   296  		require.ElementsMatch(t, expected, actual)
   297  	}
   298  
   299  	{
   300  		desc := prometheus.NewDesc("test_metric", "", []string{"label_one", "label_two"}, nil)
   301  		actual := collectMetrics(t, func(out chan prometheus.Metric) {
   302  			mf.SendSumOfHistogramsWithLabels(out, desc, "test_metric", "label_one", "label_two")
   303  		})
   304  		expected := []*dto.Metric{
   305  			{Label: makeLabels("label_one", "a", "label_two", "b"), Histogram: &dto.Histogram{SampleCount: uint64p(2), SampleSum: float64p(4), Bucket: []*dto.Bucket{
   306  				{UpperBound: float64p(1), CumulativeCount: uint64p(1)},
   307  				{UpperBound: float64p(2), CumulativeCount: uint64p(1)},
   308  				{UpperBound: float64p(3), CumulativeCount: uint64p(2)},
   309  			}}},
   310  			{Label: makeLabels("label_one", "a", "label_two", "c"), Histogram: &dto.Histogram{SampleCount: uint64p(2), SampleSum: float64p(6), Bucket: []*dto.Bucket{
   311  				{UpperBound: float64p(1), CumulativeCount: uint64p(0)},
   312  				{UpperBound: float64p(2), CumulativeCount: uint64p(1)},
   313  				{UpperBound: float64p(3), CumulativeCount: uint64p(1)},
   314  			}}},
   315  		}
   316  		require.ElementsMatch(t, expected, actual)
   317  	}
   318  }
   319  
   320  // TestSumOfCounterPerUserWithLabels tests to ensure multiple metrics for the same user with a matching label are
   321  // summed correctly
   322  func TestSumOfCounterPerUserWithLabels(t *testing.T) {
   323  	user1Metric := prometheus.NewCounterVec(prometheus.CounterOpts{Name: "test_metric"}, []string{"label_one", "label_two"})
   324  	user2Metric := prometheus.NewCounterVec(prometheus.CounterOpts{Name: "test_metric"}, []string{"label_one", "label_two"})
   325  	user1Metric.WithLabelValues("a", "b").Add(100)
   326  	user1Metric.WithLabelValues("a", "c").Add(80)
   327  	user2Metric.WithLabelValues("a", "b").Add(60)
   328  	user2Metric.WithLabelValues("a", "c").Add(40)
   329  
   330  	user1Reg := prometheus.NewRegistry()
   331  	user2Reg := prometheus.NewRegistry()
   332  	user1Reg.MustRegister(user1Metric)
   333  	user2Reg.MustRegister(user2Metric)
   334  
   335  	regs := NewUserRegistries()
   336  	regs.AddUserRegistry("user-1", user1Reg)
   337  	regs.AddUserRegistry("user-2", user2Reg)
   338  	mf := regs.BuildMetricFamiliesPerUser()
   339  
   340  	{
   341  		desc := prometheus.NewDesc("test_metric", "", []string{"user", "label_one"}, nil)
   342  		actual := collectMetrics(t, func(out chan prometheus.Metric) {
   343  			mf.SendSumOfCountersPerUserWithLabels(out, desc, "test_metric", "label_one")
   344  		})
   345  		expected := []*dto.Metric{
   346  			{Label: makeLabels("label_one", "a", "user", "user-1"), Counter: &dto.Counter{Value: proto.Float64(180)}},
   347  			{Label: makeLabels("label_one", "a", "user", "user-2"), Counter: &dto.Counter{Value: proto.Float64(100)}},
   348  		}
   349  		require.ElementsMatch(t, expected, actual)
   350  	}
   351  
   352  	{
   353  		desc := prometheus.NewDesc("test_metric", "", []string{"user", "label_two"}, nil)
   354  		actual := collectMetrics(t, func(out chan prometheus.Metric) {
   355  			mf.SendSumOfCountersPerUserWithLabels(out, desc, "test_metric", "label_two")
   356  		})
   357  		expected := []*dto.Metric{
   358  			{Label: makeLabels("label_two", "b", "user", "user-1"), Counter: &dto.Counter{Value: proto.Float64(100)}},
   359  			{Label: makeLabels("label_two", "c", "user", "user-1"), Counter: &dto.Counter{Value: proto.Float64(80)}},
   360  			{Label: makeLabels("label_two", "b", "user", "user-2"), Counter: &dto.Counter{Value: proto.Float64(60)}},
   361  			{Label: makeLabels("label_two", "c", "user", "user-2"), Counter: &dto.Counter{Value: proto.Float64(40)}},
   362  		}
   363  		require.ElementsMatch(t, expected, actual)
   364  	}
   365  
   366  	{
   367  		desc := prometheus.NewDesc("test_metric", "", []string{"user", "label_one", "label_two"}, nil)
   368  		actual := collectMetrics(t, func(out chan prometheus.Metric) {
   369  			mf.SendSumOfCountersPerUserWithLabels(out, desc, "test_metric", "label_one", "label_two")
   370  		})
   371  		expected := []*dto.Metric{
   372  			{Label: makeLabels("label_one", "a", "label_two", "b", "user", "user-1"), Counter: &dto.Counter{Value: proto.Float64(100)}},
   373  			{Label: makeLabels("label_one", "a", "label_two", "c", "user", "user-1"), Counter: &dto.Counter{Value: proto.Float64(80)}},
   374  			{Label: makeLabels("label_one", "a", "label_two", "b", "user", "user-2"), Counter: &dto.Counter{Value: proto.Float64(60)}},
   375  			{Label: makeLabels("label_one", "a", "label_two", "c", "user", "user-2"), Counter: &dto.Counter{Value: proto.Float64(40)}},
   376  		}
   377  		require.ElementsMatch(t, expected, actual)
   378  	}
   379  }
   380  
   381  func TestSendSumOfSummariesPerUser(t *testing.T) {
   382  	objectives := map[float64]float64{0.25: 25, 0.5: 50, 0.75: 75}
   383  	user1Metric := prometheus.NewSummary(prometheus.SummaryOpts{Name: "test_metric", Objectives: objectives})
   384  	user2Metric := prometheus.NewSummary(prometheus.SummaryOpts{Name: "test_metric", Objectives: objectives})
   385  	user1Metric.Observe(25)
   386  	user1Metric.Observe(50)
   387  	user1Metric.Observe(75)
   388  	user2Metric.Observe(25)
   389  	user2Metric.Observe(50)
   390  	user2Metric.Observe(76)
   391  
   392  	user1Reg := prometheus.NewRegistry()
   393  	user2Reg := prometheus.NewRegistry()
   394  	user1Reg.MustRegister(user1Metric)
   395  	user2Reg.MustRegister(user2Metric)
   396  
   397  	regs := NewUserRegistries()
   398  	regs.AddUserRegistry("user-1", user1Reg)
   399  	regs.AddUserRegistry("user-2", user2Reg)
   400  	mf := regs.BuildMetricFamiliesPerUser()
   401  
   402  	{
   403  		desc := prometheus.NewDesc("test_metric", "", []string{"user"}, nil)
   404  		actual := collectMetrics(t, func(out chan prometheus.Metric) {
   405  			mf.SendSumOfSummariesPerUser(out, desc, "test_metric")
   406  		})
   407  		expected := []*dto.Metric{
   408  			{
   409  				Label: makeLabels("user", "user-1"),
   410  				Summary: &dto.Summary{
   411  					SampleCount: uint64p(3),
   412  					SampleSum:   float64p(150),
   413  					Quantile: []*dto.Quantile{
   414  						{
   415  							Quantile: proto.Float64(.25),
   416  							Value:    proto.Float64(25),
   417  						},
   418  						{
   419  							Quantile: proto.Float64(.5),
   420  							Value:    proto.Float64(50),
   421  						},
   422  						{
   423  							Quantile: proto.Float64(.75),
   424  							Value:    proto.Float64(75),
   425  						},
   426  					},
   427  				},
   428  			},
   429  			{
   430  				Label: makeLabels("user", "user-2"),
   431  				Summary: &dto.Summary{
   432  					SampleCount: uint64p(3),
   433  					SampleSum:   float64p(151),
   434  					Quantile: []*dto.Quantile{
   435  						{
   436  							Quantile: proto.Float64(.25),
   437  							Value:    proto.Float64(25),
   438  						},
   439  						{
   440  							Quantile: proto.Float64(.5),
   441  							Value:    proto.Float64(50),
   442  						},
   443  						{
   444  							Quantile: proto.Float64(.75),
   445  							Value:    proto.Float64(76),
   446  						},
   447  					},
   448  				},
   449  			},
   450  		}
   451  		require.ElementsMatch(t, expected, actual)
   452  	}
   453  }
   454  
   455  func TestFloat64PrecisionStability(t *testing.T) {
   456  	const (
   457  		numRuns       = 100
   458  		numRegistries = 100
   459  		cardinality   = 20
   460  	)
   461  
   462  	// Randomise the seed but log it in case we need to reproduce the test on failure.
   463  	seed := time.Now().UnixNano()
   464  	rand.Seed(seed)
   465  	t.Log("random generator seed:", seed)
   466  
   467  	// Generate a large number of registries with different metrics each.
   468  	registries := NewUserRegistries()
   469  	for userID := 1; userID <= numRegistries; userID++ {
   470  		reg := prometheus.NewRegistry()
   471  		labelNames := []string{"label_one", "label_two"}
   472  
   473  		g := promauto.With(reg).NewGaugeVec(prometheus.GaugeOpts{Name: "test_gauge"}, labelNames)
   474  		for i := 0; i < cardinality; i++ {
   475  			g.WithLabelValues("a", strconv.Itoa(i)).Set(rand.Float64())
   476  		}
   477  
   478  		c := promauto.With(reg).NewCounterVec(prometheus.CounterOpts{Name: "test_counter"}, labelNames)
   479  		for i := 0; i < cardinality; i++ {
   480  			c.WithLabelValues("a", strconv.Itoa(i)).Add(rand.Float64())
   481  		}
   482  
   483  		h := promauto.With(reg).NewHistogramVec(prometheus.HistogramOpts{Name: "test_histogram", Buckets: []float64{0.1, 0.5, 1}}, labelNames)
   484  		for i := 0; i < cardinality; i++ {
   485  			h.WithLabelValues("a", strconv.Itoa(i)).Observe(rand.Float64())
   486  		}
   487  
   488  		s := promauto.With(reg).NewSummaryVec(prometheus.SummaryOpts{Name: "test_summary"}, labelNames)
   489  		for i := 0; i < cardinality; i++ {
   490  			s.WithLabelValues("a", strconv.Itoa(i)).Observe(rand.Float64())
   491  		}
   492  
   493  		registries.AddUserRegistry(strconv.Itoa(userID), reg)
   494  	}
   495  
   496  	// Ensure multiple runs always return the same exact results.
   497  	expected := map[string][]*dto.Metric{}
   498  
   499  	for run := 0; run < numRuns; run++ {
   500  		mf := registries.BuildMetricFamiliesPerUser()
   501  
   502  		gauge := collectMetrics(t, func(out chan prometheus.Metric) {
   503  			mf.SendSumOfGauges(out, prometheus.NewDesc("test_gauge", "", nil, nil), "test_gauge")
   504  		})
   505  		gaugeWithLabels := collectMetrics(t, func(out chan prometheus.Metric) {
   506  			mf.SendSumOfGaugesWithLabels(out, prometheus.NewDesc("test_gauge", "", []string{"label_one"}, nil), "test_gauge", "label_one")
   507  		})
   508  
   509  		counter := collectMetrics(t, func(out chan prometheus.Metric) {
   510  			mf.SendSumOfCounters(out, prometheus.NewDesc("test_counter", "", nil, nil), "test_counter")
   511  		})
   512  		counterWithLabels := collectMetrics(t, func(out chan prometheus.Metric) {
   513  			mf.SendSumOfCountersWithLabels(out, prometheus.NewDesc("test_counter", "", []string{"label_one"}, nil), "test_counter", "label_one")
   514  		})
   515  
   516  		histogram := collectMetrics(t, func(out chan prometheus.Metric) {
   517  			mf.SendSumOfHistograms(out, prometheus.NewDesc("test_histogram", "", nil, nil), "test_histogram")
   518  		})
   519  		histogramWithLabels := collectMetrics(t, func(out chan prometheus.Metric) {
   520  			mf.SendSumOfHistogramsWithLabels(out, prometheus.NewDesc("test_histogram", "", []string{"label_one"}, nil), "test_histogram", "label_one")
   521  		})
   522  
   523  		summary := collectMetrics(t, func(out chan prometheus.Metric) {
   524  			mf.SendSumOfSummaries(out, prometheus.NewDesc("test_summary", "", nil, nil), "test_summary")
   525  		})
   526  		summaryWithLabels := collectMetrics(t, func(out chan prometheus.Metric) {
   527  			mf.SendSumOfSummariesWithLabels(out, prometheus.NewDesc("test_summary", "", []string{"label_one"}, nil), "test_summary", "label_one")
   528  		})
   529  
   530  		// The first run we just store the expected value.
   531  		if run == 0 {
   532  			expected["gauge"] = gauge
   533  			expected["gauge_with_labels"] = gaugeWithLabels
   534  			expected["counter"] = counter
   535  			expected["counter_with_labels"] = counterWithLabels
   536  			expected["histogram"] = histogram
   537  			expected["histogram_with_labels"] = histogramWithLabels
   538  			expected["summary"] = summary
   539  			expected["summary_with_labels"] = summaryWithLabels
   540  			continue
   541  		}
   542  
   543  		// All subsequent runs we assert the actual metric with the expected one.
   544  		require.Equal(t, expected["gauge"], gauge)
   545  		require.Equal(t, expected["gauge_with_labels"], gaugeWithLabels)
   546  		require.Equal(t, expected["counter"], counter)
   547  		require.Equal(t, expected["counter_with_labels"], counterWithLabels)
   548  		require.Equal(t, expected["histogram"], histogram)
   549  		require.Equal(t, expected["histogram_with_labels"], histogramWithLabels)
   550  		require.Equal(t, expected["summary"], summary)
   551  		require.Equal(t, expected["summary_with_labels"], summaryWithLabels)
   552  	}
   553  }
   554  
   555  // This test is a baseline for following tests, that remove or replace a registry.
   556  // It shows output for metrics from setupTestMetrics before doing any modifications.
   557  func TestUserRegistries_RemoveBaseline(t *testing.T) {
   558  	mainRegistry := prometheus.NewPedanticRegistry()
   559  	mainRegistry.MustRegister(setupTestMetrics())
   560  
   561  	require.NoError(t, testutil.GatherAndCompare(mainRegistry, bytes.NewBufferString(`
   562  		# HELP counter help
   563  		# TYPE counter counter
   564  		counter 75
   565  
   566  		# HELP counter_labels help
   567  		# TYPE counter_labels counter
   568  		counter_labels{label_one="a"} 75
   569  
   570  		# HELP counter_user help
   571  		# TYPE counter_user counter
   572  		counter_user{user="1"} 5
   573  		counter_user{user="2"} 10
   574  		counter_user{user="3"} 15
   575  		counter_user{user="4"} 20
   576  		counter_user{user="5"} 25
   577  
   578  		# HELP gauge help
   579  		# TYPE gauge gauge
   580  		gauge 75
   581  
   582  		# HELP gauge_labels help
   583  		# TYPE gauge_labels gauge
   584  		gauge_labels{label_one="a"} 75
   585  
   586  		# HELP gauge_user help
   587  		# TYPE gauge_user gauge
   588  		gauge_user{user="1"} 5
   589  		gauge_user{user="2"} 10
   590  		gauge_user{user="3"} 15
   591  		gauge_user{user="4"} 20
   592  		gauge_user{user="5"} 25
   593  
   594  		# HELP histogram help
   595  		# TYPE histogram histogram
   596  		histogram_bucket{le="1"} 5
   597  		histogram_bucket{le="3"} 15
   598  		histogram_bucket{le="5"} 25
   599  		histogram_bucket{le="+Inf"} 25
   600  		histogram_sum 75
   601  		histogram_count 25
   602  
   603  		# HELP histogram_labels help
   604  		# TYPE histogram_labels histogram
   605  		histogram_labels_bucket{label_one="a",le="1"} 5
   606  		histogram_labels_bucket{label_one="a",le="3"} 15
   607  		histogram_labels_bucket{label_one="a",le="5"} 25
   608  		histogram_labels_bucket{label_one="a",le="+Inf"} 25
   609  		histogram_labels_sum{label_one="a"} 75
   610  		histogram_labels_count{label_one="a"} 25
   611  
   612  		# HELP summary help
   613  		# TYPE summary summary
   614  		summary_sum 75
   615  		summary_count 25
   616  
   617  		# HELP summary_labels help
   618  		# TYPE summary_labels summary
   619  		summary_labels_sum{label_one="a"} 75
   620  		summary_labels_count{label_one="a"} 25
   621  
   622  		# HELP summary_user help
   623  		# TYPE summary_user summary
   624  		summary_user_sum{user="1"} 5
   625  		summary_user_count{user="1"} 5
   626  		summary_user_sum{user="2"} 10
   627  		summary_user_count{user="2"} 5
   628  		summary_user_sum{user="3"} 15
   629  		summary_user_count{user="3"} 5
   630  		summary_user_sum{user="4"} 20
   631  		summary_user_count{user="4"} 5
   632  		summary_user_sum{user="5"} 25
   633  		summary_user_count{user="5"} 5
   634  	`)))
   635  }
   636  
   637  func TestUserRegistries_RemoveUserRegistry_SoftRemoval(t *testing.T) {
   638  	tm := setupTestMetrics()
   639  
   640  	mainRegistry := prometheus.NewPedanticRegistry()
   641  	mainRegistry.MustRegister(tm)
   642  
   643  	// Soft-remove single registry.
   644  	tm.regs.RemoveUserRegistry(strconv.Itoa(3), false)
   645  
   646  	require.NoError(t, testutil.GatherAndCompare(mainRegistry, bytes.NewBufferString(`
   647  			# HELP counter help
   648  			# TYPE counter counter
   649  	# No change in counter
   650  			counter 75
   651  	
   652  			# HELP counter_labels help
   653  			# TYPE counter_labels counter
   654  	# No change in counter per label.
   655  			counter_labels{label_one="a"} 75
   656  	
   657  			# HELP counter_user help
   658  			# TYPE counter_user counter
   659  	# User 3 is now missing.
   660  			counter_user{user="1"} 5
   661  			counter_user{user="2"} 10
   662  			counter_user{user="4"} 20
   663  			counter_user{user="5"} 25
   664  	
   665  			# HELP gauge help
   666  			# TYPE gauge gauge
   667  	# Drop in the gauge (value 3, counted 5 times)
   668  			gauge 60
   669  	
   670  			# HELP gauge_labels help
   671  			# TYPE gauge_labels gauge
   672  	# Drop in the gauge (value 3, counted 5 times)
   673  			gauge_labels{label_one="a"} 60
   674  	
   675  			# HELP gauge_user help
   676  			# TYPE gauge_user gauge
   677  	# User 3 is now missing.
   678  			gauge_user{user="1"} 5
   679  			gauge_user{user="2"} 10
   680  			gauge_user{user="4"} 20
   681  			gauge_user{user="5"} 25
   682  	
   683  			# HELP histogram help
   684  			# TYPE histogram histogram
   685  	# No change in the histogram
   686  			histogram_bucket{le="1"} 5
   687  			histogram_bucket{le="3"} 15
   688  			histogram_bucket{le="5"} 25
   689  			histogram_bucket{le="+Inf"} 25
   690  			histogram_sum 75
   691  			histogram_count 25
   692  	
   693  			# HELP histogram_labels help
   694  			# TYPE histogram_labels histogram
   695  	# No change in the histogram per label
   696  			histogram_labels_bucket{label_one="a",le="1"} 5
   697  			histogram_labels_bucket{label_one="a",le="3"} 15
   698  			histogram_labels_bucket{label_one="a",le="5"} 25
   699  			histogram_labels_bucket{label_one="a",le="+Inf"} 25
   700  			histogram_labels_sum{label_one="a"} 75
   701  			histogram_labels_count{label_one="a"} 25
   702  	
   703  			# HELP summary help
   704  			# TYPE summary summary
   705  	# No change in the summary
   706  			summary_sum 75
   707  			summary_count 25
   708  	
   709  			# HELP summary_labels help
   710  			# TYPE summary_labels summary
   711  	# No change in the summary per label
   712  			summary_labels_sum{label_one="a"} 75
   713  			summary_labels_count{label_one="a"} 25
   714  	
   715  			# HELP summary_user help
   716  			# TYPE summary_user summary
   717  	# Summary for user 3 is now missing.
   718  			summary_user_sum{user="1"} 5
   719  			summary_user_count{user="1"} 5
   720  			summary_user_sum{user="2"} 10
   721  			summary_user_count{user="2"} 5
   722  			summary_user_sum{user="4"} 20
   723  			summary_user_count{user="4"} 5
   724  			summary_user_sum{user="5"} 25
   725  			summary_user_count{user="5"} 5
   726  	`)))
   727  }
   728  func TestUserRegistries_RemoveUserRegistry_HardRemoval(t *testing.T) {
   729  	tm := setupTestMetrics()
   730  
   731  	mainRegistry := prometheus.NewPedanticRegistry()
   732  	mainRegistry.MustRegister(tm)
   733  
   734  	// Soft-remove single registry.
   735  	tm.regs.RemoveUserRegistry(strconv.Itoa(3), true)
   736  
   737  	require.NoError(t, testutil.GatherAndCompare(mainRegistry, bytes.NewBufferString(`
   738  			# HELP counter help
   739  			# TYPE counter counter
   740  	# Counter drop (reset!)
   741  			counter 60
   742  	
   743  			# HELP counter_labels help
   744  			# TYPE counter_labels counter
   745  	# Counter drop (reset!)
   746  			counter_labels{label_one="a"} 60
   747  	
   748  			# HELP counter_user help
   749  			# TYPE counter_user counter
   750  	# User 3 is now missing.
   751  			counter_user{user="1"} 5
   752  			counter_user{user="2"} 10
   753  			counter_user{user="4"} 20
   754  			counter_user{user="5"} 25
   755  	
   756  			# HELP gauge help
   757  			# TYPE gauge gauge
   758  	# Drop in the gauge (value 3, counted 5 times)
   759  			gauge 60
   760  	
   761  			# HELP gauge_labels help
   762  			# TYPE gauge_labels gauge
   763  	# Drop in the gauge (value 3, counted 5 times)
   764  			gauge_labels{label_one="a"} 60
   765  	
   766  			# HELP gauge_user help
   767  			# TYPE gauge_user gauge
   768  	# User 3 is now missing.
   769  			gauge_user{user="1"} 5
   770  			gauge_user{user="2"} 10
   771  			gauge_user{user="4"} 20
   772  			gauge_user{user="5"} 25
   773  	
   774  			# HELP histogram help
   775  			# TYPE histogram histogram
   776  	# Histogram drop (reset for sum and count!)
   777  			histogram_bucket{le="1"} 5
   778  			histogram_bucket{le="3"} 10
   779  			histogram_bucket{le="5"} 20
   780  			histogram_bucket{le="+Inf"} 20
   781  			histogram_sum 60
   782  			histogram_count 20
   783  	
   784  			# HELP histogram_labels help
   785  			# TYPE histogram_labels histogram
   786  	# No change in the histogram per label
   787  			histogram_labels_bucket{label_one="a",le="1"} 5
   788  			histogram_labels_bucket{label_one="a",le="3"} 10
   789  			histogram_labels_bucket{label_one="a",le="5"} 20
   790  			histogram_labels_bucket{label_one="a",le="+Inf"} 20
   791  			histogram_labels_sum{label_one="a"} 60
   792  			histogram_labels_count{label_one="a"} 20
   793  	
   794  			# HELP summary help
   795  			# TYPE summary summary
   796  	# Summary drop!
   797  			summary_sum 60
   798  			summary_count 20
   799  	
   800  			# HELP summary_labels help
   801  			# TYPE summary_labels summary
   802  	# Summary drop!
   803  			summary_labels_sum{label_one="a"} 60
   804  			summary_labels_count{label_one="a"} 20
   805  	
   806  			# HELP summary_user help
   807  			# TYPE summary_user summary
   808  	# Summary for user 3 is now missing.
   809  			summary_user_sum{user="1"} 5
   810  			summary_user_count{user="1"} 5
   811  			summary_user_sum{user="2"} 10
   812  			summary_user_count{user="2"} 5
   813  			summary_user_sum{user="4"} 20
   814  			summary_user_count{user="4"} 5
   815  			summary_user_sum{user="5"} 25
   816  			summary_user_count{user="5"} 5
   817  	`)))
   818  }
   819  
   820  func TestUserRegistries_AddUserRegistry_ReplaceRegistry(t *testing.T) {
   821  	tm := setupTestMetrics()
   822  
   823  	mainRegistry := prometheus.NewPedanticRegistry()
   824  	mainRegistry.MustRegister(tm)
   825  
   826  	// Replace registry for user 5 with empty registry. Replacement does soft-delete previous registry.
   827  	tm.regs.AddUserRegistry(strconv.Itoa(5), prometheus.NewRegistry())
   828  
   829  	require.NoError(t, testutil.GatherAndCompare(mainRegistry, bytes.NewBufferString(`
   830  			# HELP counter help
   831  			# TYPE counter counter
   832  	# No change in counter
   833  			counter 75
   834  	
   835  			# HELP counter_labels help
   836  			# TYPE counter_labels counter
   837  	# No change in counter per label
   838  			counter_labels{label_one="a"} 75
   839  	
   840  			# HELP counter_user help
   841  			# TYPE counter_user counter
   842  	# Per-user counter now missing.
   843  			counter_user{user="1"} 5
   844  			counter_user{user="2"} 10
   845  			counter_user{user="3"} 15
   846  			counter_user{user="4"} 20
   847  	
   848  			# HELP gauge help
   849  			# TYPE gauge gauge
   850  	# Gauge drops by 25 (value for user 5, times 5)
   851  			gauge 50
   852  	
   853  			# HELP gauge_labels help
   854  			# TYPE gauge_labels gauge
   855  	# Gauge drops by 25 (value for user 5, times 5)
   856  			gauge_labels{label_one="a"} 50
   857  	
   858  			# HELP gauge_user help
   859  			# TYPE gauge_user gauge
   860  	# Gauge for user 5 is missing
   861  			gauge_user{user="1"} 5
   862  			gauge_user{user="2"} 10
   863  			gauge_user{user="3"} 15
   864  			gauge_user{user="4"} 20
   865  	
   866  			# HELP histogram help
   867  			# TYPE histogram histogram
   868  	# No change in histogram
   869  			histogram_bucket{le="1"} 5
   870  			histogram_bucket{le="3"} 15
   871  			histogram_bucket{le="5"} 25
   872  			histogram_bucket{le="+Inf"} 25
   873  			histogram_sum 75
   874  			histogram_count 25
   875  	
   876  			# HELP histogram_labels help
   877  			# TYPE histogram_labels histogram
   878  	# No change in histogram per label.
   879  			histogram_labels_bucket{label_one="a",le="1"} 5
   880  			histogram_labels_bucket{label_one="a",le="3"} 15
   881  			histogram_labels_bucket{label_one="a",le="5"} 25
   882  			histogram_labels_bucket{label_one="a",le="+Inf"} 25
   883  			histogram_labels_sum{label_one="a"} 75
   884  			histogram_labels_count{label_one="a"} 25
   885  	
   886  			# HELP summary help
   887  			# TYPE summary summary
   888  	# No change in summary
   889  			summary_sum 75
   890  			summary_count 25
   891  	
   892  			# HELP summary_labels help
   893  			# TYPE summary_labels summary
   894  	# No change in summary per label
   895  			summary_labels_sum{label_one="a"} 75
   896  			summary_labels_count{label_one="a"} 25
   897  	
   898  			# HELP summary_user help
   899  			# TYPE summary_user summary
   900  	# Summary for user 5 now zero (reset)
   901  			summary_user_sum{user="1"} 5
   902  			summary_user_count{user="1"} 5
   903  			summary_user_sum{user="2"} 10
   904  			summary_user_count{user="2"} 5
   905  			summary_user_sum{user="3"} 15
   906  			summary_user_count{user="3"} 5
   907  			summary_user_sum{user="4"} 20
   908  			summary_user_count{user="4"} 5
   909  			summary_user_sum{user="5"} 0
   910  			summary_user_count{user="5"} 0
   911  	`)))
   912  }
   913  
   914  func setupTestMetrics() *testMetrics {
   915  	const numUsers = 5
   916  	const cardinality = 5
   917  
   918  	tm := newTestMetrics()
   919  
   920  	for userID := 1; userID <= numUsers; userID++ {
   921  		reg := prometheus.NewRegistry()
   922  
   923  		labelNames := []string{"label_one", "label_two"}
   924  
   925  		g := promauto.With(reg).NewGaugeVec(prometheus.GaugeOpts{Name: "test_gauge"}, labelNames)
   926  		for i := 0; i < cardinality; i++ {
   927  			g.WithLabelValues("a", strconv.Itoa(i)).Set(float64(userID))
   928  		}
   929  
   930  		c := promauto.With(reg).NewCounterVec(prometheus.CounterOpts{Name: "test_counter"}, labelNames)
   931  		for i := 0; i < cardinality; i++ {
   932  			c.WithLabelValues("a", strconv.Itoa(i)).Add(float64(userID))
   933  		}
   934  
   935  		h := promauto.With(reg).NewHistogramVec(prometheus.HistogramOpts{Name: "test_histogram", Buckets: []float64{1, 3, 5}}, labelNames)
   936  		for i := 0; i < cardinality; i++ {
   937  			h.WithLabelValues("a", strconv.Itoa(i)).Observe(float64(userID))
   938  		}
   939  
   940  		s := promauto.With(reg).NewSummaryVec(prometheus.SummaryOpts{Name: "test_summary"}, labelNames)
   941  		for i := 0; i < cardinality; i++ {
   942  			s.WithLabelValues("a", strconv.Itoa(i)).Observe(float64(userID))
   943  		}
   944  
   945  		tm.regs.AddUserRegistry(strconv.Itoa(userID), reg)
   946  	}
   947  	return tm
   948  }
   949  
   950  type testMetrics struct {
   951  	regs *UserRegistries
   952  
   953  	gauge             *prometheus.Desc
   954  	gaugePerUser      *prometheus.Desc
   955  	gaugeWithLabels   *prometheus.Desc
   956  	counter           *prometheus.Desc
   957  	counterPerUser    *prometheus.Desc
   958  	counterWithLabels *prometheus.Desc
   959  	histogram         *prometheus.Desc
   960  	histogramLabels   *prometheus.Desc
   961  	summary           *prometheus.Desc
   962  	summaryPerUser    *prometheus.Desc
   963  	summaryLabels     *prometheus.Desc
   964  }
   965  
   966  func newTestMetrics() *testMetrics {
   967  	return &testMetrics{
   968  		regs: NewUserRegistries(),
   969  
   970  		gauge:             prometheus.NewDesc("gauge", "help", nil, nil),
   971  		gaugePerUser:      prometheus.NewDesc("gauge_user", "help", []string{"user"}, nil),
   972  		gaugeWithLabels:   prometheus.NewDesc("gauge_labels", "help", []string{"label_one"}, nil),
   973  		counter:           prometheus.NewDesc("counter", "help", nil, nil),
   974  		counterPerUser:    prometheus.NewDesc("counter_user", "help", []string{"user"}, nil),
   975  		counterWithLabels: prometheus.NewDesc("counter_labels", "help", []string{"label_one"}, nil),
   976  		histogram:         prometheus.NewDesc("histogram", "help", nil, nil),
   977  		histogramLabels:   prometheus.NewDesc("histogram_labels", "help", []string{"label_one"}, nil),
   978  		summary:           prometheus.NewDesc("summary", "help", nil, nil),
   979  		summaryPerUser:    prometheus.NewDesc("summary_user", "help", []string{"user"}, nil),
   980  		summaryLabels:     prometheus.NewDesc("summary_labels", "help", []string{"label_one"}, nil),
   981  	}
   982  }
   983  
   984  func (tm *testMetrics) Describe(out chan<- *prometheus.Desc) {
   985  	out <- tm.gauge
   986  	out <- tm.gaugePerUser
   987  	out <- tm.gaugeWithLabels
   988  	out <- tm.counter
   989  	out <- tm.counterPerUser
   990  	out <- tm.counterWithLabels
   991  	out <- tm.histogram
   992  	out <- tm.histogramLabels
   993  	out <- tm.summary
   994  	out <- tm.summaryPerUser
   995  	out <- tm.summaryLabels
   996  }
   997  
   998  func (tm *testMetrics) Collect(out chan<- prometheus.Metric) {
   999  	data := tm.regs.BuildMetricFamiliesPerUser()
  1000  
  1001  	data.SendSumOfGauges(out, tm.gauge, "test_gauge")
  1002  	data.SendSumOfGaugesPerUser(out, tm.gaugePerUser, "test_gauge")
  1003  	data.SendSumOfGaugesWithLabels(out, tm.gaugeWithLabels, "test_gauge", "label_one")
  1004  	data.SendSumOfCounters(out, tm.counter, "test_counter")
  1005  	data.SendSumOfCountersPerUser(out, tm.counterPerUser, "test_counter")
  1006  	data.SendSumOfCountersWithLabels(out, tm.counterWithLabels, "test_counter", "label_one")
  1007  	data.SendSumOfHistograms(out, tm.histogram, "test_histogram")
  1008  	data.SendSumOfHistogramsWithLabels(out, tm.histogramLabels, "test_histogram", "label_one")
  1009  	data.SendSumOfSummaries(out, tm.summary, "test_summary")
  1010  	data.SendSumOfSummariesPerUser(out, tm.summaryPerUser, "test_summary")
  1011  	data.SendSumOfSummariesWithLabels(out, tm.summaryLabels, "test_summary", "label_one")
  1012  }
  1013  
  1014  func collectMetrics(t *testing.T, send func(out chan prometheus.Metric)) []*dto.Metric {
  1015  	out := make(chan prometheus.Metric)
  1016  
  1017  	go func() {
  1018  		send(out)
  1019  		close(out)
  1020  	}()
  1021  
  1022  	var metrics []*dto.Metric
  1023  	for m := range out {
  1024  		collected := &dto.Metric{}
  1025  		err := m.Write(collected)
  1026  		require.NoError(t, err)
  1027  
  1028  		metrics = append(metrics, collected)
  1029  	}
  1030  
  1031  	return metrics
  1032  }
  1033  
  1034  func float64p(v float64) *float64 {
  1035  	return &v
  1036  }
  1037  
  1038  func uint64p(v uint64) *uint64 {
  1039  	return &v
  1040  }
  1041  
  1042  func BenchmarkGetLabels_SmallSet(b *testing.B) {
  1043  	m := prometheus.NewCounterVec(prometheus.CounterOpts{
  1044  		Name: "test",
  1045  		ConstLabels: map[string]string{
  1046  			"cluster": "abc",
  1047  		},
  1048  	}, []string{"reason", "user"})
  1049  
  1050  	m.WithLabelValues("bad", "user1").Inc()
  1051  	m.WithLabelValues("worse", "user1").Inc()
  1052  	m.WithLabelValues("worst", "user1").Inc()
  1053  
  1054  	m.WithLabelValues("bad", "user2").Inc()
  1055  	m.WithLabelValues("worst", "user2").Inc()
  1056  
  1057  	m.WithLabelValues("worst", "user3").Inc()
  1058  
  1059  	b.ResetTimer()
  1060  	for i := 0; i < b.N; i++ {
  1061  		if _, err := GetLabels(m, map[string]string{"user": "user1", "reason": "worse"}); err != nil {
  1062  			b.Fatal(err)
  1063  		}
  1064  	}
  1065  }
  1066  
  1067  func BenchmarkGetLabels_MediumSet(b *testing.B) {
  1068  	m := prometheus.NewCounterVec(prometheus.CounterOpts{
  1069  		Name: "test",
  1070  		ConstLabels: map[string]string{
  1071  			"cluster": "abc",
  1072  		},
  1073  	}, []string{"reason", "user"})
  1074  
  1075  	for i := 1; i <= 1000; i++ {
  1076  		m.WithLabelValues("bad", fmt.Sprintf("user%d", i)).Inc()
  1077  		m.WithLabelValues("worse", fmt.Sprintf("user%d", i)).Inc()
  1078  		m.WithLabelValues("worst", fmt.Sprintf("user%d", i)).Inc()
  1079  
  1080  		if i%2 == 0 {
  1081  			m.WithLabelValues("bad", fmt.Sprintf("user%d", i)).Inc()
  1082  			m.WithLabelValues("worst", fmt.Sprintf("user%d", i)).Inc()
  1083  		} else {
  1084  			m.WithLabelValues("worst", fmt.Sprintf("user%d", i)).Inc()
  1085  		}
  1086  	}
  1087  	b.ResetTimer()
  1088  
  1089  	for i := 0; i < b.N; i++ {
  1090  		if _, err := GetLabels(m, map[string]string{"user": "user1", "reason": "worse"}); err != nil {
  1091  			b.Fatal(err)
  1092  		}
  1093  	}
  1094  }
  1095  
  1096  func TestGetLabels(t *testing.T) {
  1097  	m := prometheus.NewCounterVec(prometheus.CounterOpts{
  1098  		Name: "test",
  1099  		ConstLabels: map[string]string{
  1100  			"cluster": "abc",
  1101  		},
  1102  	}, []string{"reason", "user"})
  1103  
  1104  	m.WithLabelValues("bad", "user1").Inc()
  1105  	m.WithLabelValues("worse", "user1").Inc()
  1106  	m.WithLabelValues("worst", "user1").Inc()
  1107  
  1108  	m.WithLabelValues("bad", "user2").Inc()
  1109  	m.WithLabelValues("worst", "user2").Inc()
  1110  
  1111  	m.WithLabelValues("worst", "user3").Inc()
  1112  
  1113  	verifyLabels(t, m, nil, []labels.Labels{
  1114  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "bad", "user": "user1"}),
  1115  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "worse", "user": "user1"}),
  1116  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "worst", "user": "user1"}),
  1117  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "bad", "user": "user2"}),
  1118  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "worst", "user": "user2"}),
  1119  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "worst", "user": "user3"}),
  1120  	})
  1121  
  1122  	verifyLabels(t, m, map[string]string{"cluster": "abc"}, []labels.Labels{
  1123  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "bad", "user": "user1"}),
  1124  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "worse", "user": "user1"}),
  1125  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "worst", "user": "user1"}),
  1126  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "bad", "user": "user2"}),
  1127  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "worst", "user": "user2"}),
  1128  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "worst", "user": "user3"}),
  1129  	})
  1130  
  1131  	verifyLabels(t, m, map[string]string{"reason": "bad"}, []labels.Labels{
  1132  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "bad", "user": "user1"}),
  1133  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "bad", "user": "user2"}),
  1134  	})
  1135  
  1136  	verifyLabels(t, m, map[string]string{"user": "user1"}, []labels.Labels{
  1137  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "bad", "user": "user1"}),
  1138  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "worse", "user": "user1"}),
  1139  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "worst", "user": "user1"}),
  1140  	})
  1141  
  1142  	verifyLabels(t, m, map[string]string{"user": "user1", "reason": "worse"}, []labels.Labels{
  1143  		labels.FromMap(map[string]string{"cluster": "abc", "reason": "worse", "user": "user1"}),
  1144  	})
  1145  }
  1146  
  1147  func verifyLabels(t *testing.T, m prometheus.Collector, filter map[string]string, expectedLabels []labels.Labels) {
  1148  	result, err := GetLabels(m, filter)
  1149  	require.NoError(t, err)
  1150  
  1151  	sort.Slice(result, func(i, j int) bool {
  1152  		return labels.Compare(result[i], result[j]) < 0
  1153  	})
  1154  
  1155  	sort.Slice(expectedLabels, func(i, j int) bool {
  1156  		return labels.Compare(expectedLabels[i], expectedLabels[j]) < 0
  1157  	})
  1158  
  1159  	require.Equal(t, expectedLabels, result)
  1160  }