Skip to content

Commit

Permalink
Add tests and cleanup code from benchmarking
Browse files Browse the repository at this point in the history
  • Loading branch information
kgeckhart committed Sep 3, 2024
1 parent 2db25ab commit 1ae7ece
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 73 deletions.
27 changes: 6 additions & 21 deletions pkg/promutil/prometheus.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,25 +123,6 @@ func (p *PrometheusCollector) Collect(metrics chan<- prometheus.Metric) {
}
}

func toMetrics(metrics []*PrometheusMetric) []prometheus.Metric {
result := make([]prometheus.Metric, 0, len(metrics))
for _, metric := range metrics {
gauge := prometheus.NewGauge(prometheus.GaugeOpts{
Name: metric.Name,
Help: "Help is not implemented yet.",
ConstLabels: metric.Labels,
})
gauge.Set(metric.Value)

if !metric.IncludeTimestamp {
result = append(result, gauge)
} else {
result = append(result, prometheus.NewMetricWithTimestamp(metric.Timestamp, gauge))
}
}
return result
}

func toConstMetrics(metrics []*PrometheusMetric) []prometheus.Metric {
// We keep two fast lookup maps here one for the prometheus.Desc of a metric which can be reused for each metric with
// the same name and the expected label key order of a particular metric name.
Expand All @@ -167,8 +148,12 @@ func toConstMetrics(metrics []*PrometheusMetric) []prometheus.Metric {
labelValues = append(labelValues, metric.Labels[labelKey])
}

promMetric := prometheus.MustNewConstMetric(metricsDesc, prometheus.GaugeValue, metric.Value, labelValues...)
if metric.IncludeTimestamp {
promMetric, err := prometheus.NewConstMetric(metricsDesc, prometheus.GaugeValue, metric.Value, labelValues...)
if err != nil {
// If for whatever reason the metric or metricsDesc is considered invalid this will ensure the error is
// reported through the collector
promMetric = prometheus.NewInvalidMetric(metricsDesc, err)
} else if metric.IncludeTimestamp {
promMetric = prometheus.NewMetricWithTimestamp(metric.Timestamp, promMetric)
}

Expand Down
156 changes: 104 additions & 52 deletions pkg/promutil/prometheus_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package promutil

import (
"fmt"
"math/rand/v2"
"testing"
"time"

"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestSplitString(t *testing.T) {
Expand Down Expand Up @@ -119,63 +121,113 @@ func TestPromStringTag(t *testing.T) {
}
}

func Benchmark_CreateMetrics(b *testing.B) {
tests := []struct {
uniqueMetrics int
numberOfLabels int
metricsToMigrate int
}{
{10, 1, 1000},
{10, 1, 10000},
{10, 1, 100000},
{10, 5, 1000},
{10, 5, 10000},
{10, 5, 100000},
{10, 10, 1000},
{10, 10, 10000},
{10, 10, 100000},
{10, 20, 1000},
{10, 20, 10000},
{10, 20, 100000},
{20, 20, 1000},
{20, 20, 10000},
{20, 20, 100000},
func TestNewPrometheusCollector_CanReportMetricsAndErrors(t *testing.T) {
metrics := []*PrometheusMetric{
{
Name: "this*is*not*valid",
Labels: map[string]string{},
Value: 0,
IncludeTimestamp: false,
},
{
Name: "this_is_valid",
Labels: map[string]string{"key": "value1"},
Value: 0,
IncludeTimestamp: false,
},
}
collector := NewPrometheusCollector(metrics)
registry := prometheus.NewRegistry()
require.NoError(t, registry.Register(collector))
families, err := registry.Gather()
assert.Error(t, err)
assert.Len(t, families, 1)
family := families[0]
assert.Equal(t, "this_is_valid", family.GetName())
}

for _, tc := range tests {
metricsToMigrate := createTestData(tc.uniqueMetrics, tc.numberOfLabels, tc.metricsToMigrate)
func TestNewPrometheusCollector_CanReportMetrics(t *testing.T) {
ts := time.Now()

b.Run(fmt.Sprintf("current: unique metrics %d, number of labels %d, metrics to migrate %d", tc.uniqueMetrics, tc.numberOfLabels, tc.metricsToMigrate), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
toMetrics(metricsToMigrate)
}
})
labelSet1 := map[string]string{"key1": "value", "key2": "value", "key3": "value"}
labelSet2 := map[string]string{"key2": "out", "key3": "of", "key1": "order"}
labelSet3 := map[string]string{"key2": "out", "key1": "of", "key3": "order"}
metrics := []*PrometheusMetric{
{
Name: "metric_with_labels",
Labels: labelSet1,
Value: 1,
IncludeTimestamp: false,
},
{
Name: "metric_with_labels",
Labels: labelSet2,
Value: 2,
IncludeTimestamp: false,
},
{
Name: "metric_with_labels",
Labels: labelSet3,
Value: 3,
IncludeTimestamp: false,
},
{
Name: "metric_with_timestamp",
Labels: map[string]string{},
Value: 1,
IncludeTimestamp: true,
Timestamp: ts,
},
}

b.Run(fmt.Sprintf("new: unique metrics %d, number of labels %d, metrics to migrate %d", tc.uniqueMetrics, tc.numberOfLabels, tc.metricsToMigrate), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
toConstMetrics(metricsToMigrate)
}
})
collector := NewPrometheusCollector(metrics)
registry := prometheus.NewRegistry()
require.NoError(t, registry.Register(collector))
families, err := registry.Gather()
assert.NoError(t, err)
assert.Len(t, families, 2)

var metricWithLabels *dto.MetricFamily
var metricWithTs *dto.MetricFamily

for _, metricFamily := range families {
assert.Equal(t, dto.MetricType_GAUGE, metricFamily.GetType())

switch {
case metricFamily.GetName() == "metric_with_labels":
metricWithLabels = metricFamily
case metricFamily.GetName() == "metric_with_timestamp":
metricWithTs = metricFamily
default:
require.Failf(t, "Encountered an unexpected metric family %s", metricFamily.GetName())
}
}
}
require.NotNil(t, metricWithLabels)
require.NotNil(t, metricWithTs)

func createTestData(uniqueMetrics int, numberOfLabels int, totalToMigrate int) []*PrometheusMetric {
result := make([]*PrometheusMetric, 0, totalToMigrate)
for i := 0; i < totalToMigrate; i++ {
metricName := fmt.Sprintf("metric_%d", rand.IntN(uniqueMetrics-1))
labels := make(map[string]string, numberOfLabels)
for j := 0; j < numberOfLabels; j++ {
labels[fmt.Sprintf("label_%d", j)] = fmt.Sprintf("label-value-%d", j)
assert.Len(t, metricWithLabels.Metric, 3)
for _, metric := range metricWithLabels.Metric {
assert.Len(t, metric.Label, 3)
var labelSetToMatch map[string]string
switch *metric.Gauge.Value {
case 1.0:
labelSetToMatch = labelSet1
case 2.0:
labelSetToMatch = labelSet2
case 3.0:
labelSetToMatch = labelSet3
default:
require.Fail(t, "Encountered an metric value value %v", *metric.Gauge.Value)
}

for _, labelPairs := range metric.Label {
require.Contains(t, labelSetToMatch, *labelPairs.Name)
require.Equal(t, labelSetToMatch[*labelPairs.Name], *labelPairs.Value)
}
result = append(result, &PrometheusMetric{
Name: metricName,
Labels: labels,
Value: 0,
IncludeTimestamp: false,
})
}

return result
require.Len(t, metricWithTs.Metric, 1)
tsMetric := metricWithTs.Metric[0]
assert.Equal(t, ts.UnixMilli(), *tsMetric.TimestampMs)
assert.Equal(t, 1.0, *tsMetric.Gauge.Value)
}

0 comments on commit 1ae7ece

Please sign in to comment.