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 }