From c7c6d7dc2448a89ea3671dd61efe471187398ef1 Mon Sep 17 00:00:00 2001 From: Arve Knudsen Date: Tue, 6 Feb 2024 17:32:50 +0400 Subject: [PATCH] model/labels: Add Label.Identifying field Signed-off-by: Arve Knudsen --- model/labels/labels.go | 8 +++- model/labels/labels_common.go | 8 ++++ model/labels/labels_stringlabels.go | 8 +++- model/labels/labels_test.go | 40 +++++++++---------- storage/remote/codec.go | 4 +- .../prometheusremotewrite/helper.go | 4 +- storage/remote/write_handler.go | 5 ++- 7 files changed, 48 insertions(+), 29 deletions(-) diff --git a/model/labels/labels.go b/model/labels/labels.go index b6663dad24..74e5a57648 100644 --- a/model/labels/labels.go +++ b/model/labels/labels.go @@ -412,8 +412,12 @@ func (b *ScratchBuilder) Reset() { // Add a name/value pair. // Note if you Add the same name twice you will get a duplicate label, which is invalid. -func (b *ScratchBuilder) Add(name, value string) { - b.add = append(b.add, Label{Name: name, Value: value}) +func (b *ScratchBuilder) Add(name, value string, identifyingLabels ...string) { + identifying := false + if len(identifyingLabels) > 0 && slices.Contains(identifyingLabels, name) { + identifying = true + } + b.add = append(b.add, Label{Name: name, Value: value, Identifying: identifying}) } // Add a name/value pair, using []byte instead of string. diff --git a/model/labels/labels_common.go b/model/labels/labels_common.go index 2a722b84cc..cf9d1a61e0 100644 --- a/model/labels/labels_common.go +++ b/model/labels/labels_common.go @@ -16,6 +16,7 @@ package labels import ( "bytes" "encoding/json" + "fmt" "strconv" "github.com/prometheus/common/model" @@ -36,6 +37,13 @@ var seps = []byte{'\xff'} // Label is a key/value pair of strings. type Label struct { Name, Value string + // Identifying signifies whether this is an identifying label for an info metric. + Identifying bool +} + +// String returns l's string representation. +func (l Label) String() string { + return fmt.Sprintf("%s=%s", l.Name, l.Value) } func (ls Labels) String() string { diff --git a/model/labels/labels_stringlabels.go b/model/labels/labels_stringlabels.go index f53c3b2d05..54c25b8b5b 100644 --- a/model/labels/labels_stringlabels.go +++ b/model/labels/labels_stringlabels.go @@ -620,8 +620,12 @@ func (b *ScratchBuilder) Reset() { // Add a name/value pair. // Note if you Add the same name twice you will get a duplicate label, which is invalid. -func (b *ScratchBuilder) Add(name, value string) { - b.add = append(b.add, Label{Name: name, Value: value}) +func (b *ScratchBuilder) Add(name, value string, identifyingLabels ...string) { + identifying := false + if len(identifyingLabels) > 0 && slices.Contains(identifyingLabels, name) { + identifying = true + } + b.add = append(b.add, Label{Name: name, Value: value, Identifying: identifying}) } // Add a name/value pair, using []byte instead of string to reduce memory allocations. diff --git a/model/labels/labels_test.go b/model/labels/labels_test.go index c497851d1b..8d6088419b 100644 --- a/model/labels/labels_test.go +++ b/model/labels/labels_test.go @@ -582,7 +582,7 @@ func TestBuilder(t *testing.T) { }, { base: FromStrings("aaa", "111", "bbb", "222", "ccc", "333"), - set: []Label{{"aaa", "444"}, {"bbb", "555"}, {"ccc", "666"}}, + set: []Label{{Name: "aaa", Value: "444"}, {Name: "bbb", Value: "555"}, {Name: "ccc", Value: "666"}}, want: FromStrings("aaa", "444", "bbb", "555", "ccc", "666"), }, { @@ -591,24 +591,24 @@ func TestBuilder(t *testing.T) { want: FromStrings("aaa", "111", "ccc", "333"), }, { - set: []Label{{"aaa", "111"}, {"bbb", "222"}, {"ccc", "333"}}, + set: []Label{{Name: "aaa", Value: "111"}, {Name: "bbb", Value: "222"}, {Name: "ccc", Value: "333"}}, del: []string{"bbb"}, want: FromStrings("aaa", "111", "ccc", "333"), }, { base: FromStrings("aaa", "111"), - set: []Label{{"bbb", "222"}}, + set: []Label{{Name: "bbb", Value: "222"}}, want: FromStrings("aaa", "111", "bbb", "222"), }, { base: FromStrings("aaa", "111"), - set: []Label{{"bbb", "222"}, {"bbb", "333"}}, + set: []Label{{Name: "bbb", Value: "222"}, {Name: "bbb", Value: "333"}}, want: FromStrings("aaa", "111", "bbb", "333"), }, { base: FromStrings("aaa", "111", "bbb", "222", "ccc", "333"), del: []string{"bbb"}, - set: []Label{{"ddd", "444"}}, + set: []Label{{Name: "ddd", Value: "444"}}, want: FromStrings("aaa", "111", "ccc", "333", "ddd", "444"), }, { // Blank value is interpreted as delete. @@ -617,7 +617,7 @@ func TestBuilder(t *testing.T) { }, { base: FromStrings("aaa", "111", "bbb", "222", "ccc", "333"), - set: []Label{{"bbb", ""}}, + set: []Label{{Name: "bbb", Value: ""}}, want: FromStrings("aaa", "111", "ccc", "333"), }, { @@ -633,7 +633,7 @@ func TestBuilder(t *testing.T) { { base: FromStrings("aaa", "111", "bbb", "222", "ccc", "333"), del: []string{"bbb"}, - set: []Label{{"ddd", "444"}}, + set: []Label{{Name: "ddd", Value: "444"}}, keep: []string{"aaa", "ddd"}, want: FromStrings("aaa", "111", "ddd", "444"), }, @@ -677,19 +677,19 @@ func TestScratchBuilder(t *testing.T) { want: EmptyLabels(), }, { - add: []Label{{"aaa", "111"}}, + add: []Label{{Name: "aaa", Value: "111"}}, want: FromStrings("aaa", "111"), }, { - add: []Label{{"aaa", "111"}, {"bbb", "222"}, {"ccc", "333"}}, + add: []Label{{Name: "aaa", Value: "111"}, {Name: "bbb", Value: "222"}, {Name: "ccc", Value: "333"}}, want: FromStrings("aaa", "111", "bbb", "222", "ccc", "333"), }, { - add: []Label{{"bbb", "222"}, {"aaa", "111"}, {"ccc", "333"}}, + add: []Label{{Name: "bbb", Value: "222"}, {Name: "aaa", Value: "111"}, {Name: "ccc", Value: "333"}}, want: FromStrings("aaa", "111", "bbb", "222", "ccc", "333"), }, { - add: []Label{{"ddd", "444"}}, + add: []Label{{Name: "ddd", Value: "444"}}, want: FromStrings("ddd", "444"), }, } { @@ -769,15 +769,15 @@ func BenchmarkLabels_Hash(b *testing.B) { func BenchmarkBuilder(b *testing.B) { m := []Label{ - {"job", "node"}, - {"instance", "123.123.1.211:9090"}, - {"path", "/api/v1/namespaces//deployments/"}, - {"method", "GET"}, - {"namespace", "system"}, - {"status", "500"}, - {"prometheus", "prometheus-core-1"}, - {"datacenter", "eu-west-1"}, - {"pod_name", "abcdef-99999-defee"}, + {Name: "job", Value: "node"}, + {Name: "instance", Value: "123.123.1.211:9090"}, + {Name: "path", Value: "/api/v1/namespaces//deployments/"}, + {Name: "method", Value: "GET"}, + {Name: "namespace", Value: "system"}, + {Name: "status", Value: "500"}, + {Name: "prometheus", Value: "prometheus-core-1"}, + {Name: "datacenter", Value: "eu-west-1"}, + {Name: "pod_name", Value: "abcdef-99999-defee"}, } var l Labels diff --git a/storage/remote/codec.go b/storage/remote/codec.go index 9cf1ed8712..47f8e3ef55 100644 --- a/storage/remote/codec.go +++ b/storage/remote/codec.go @@ -760,10 +760,10 @@ func LabelProtosToMetric(labelPairs []*prompb.Label) model.Metric { return metric } -func labelProtosToLabels(labelPairs []prompb.Label) labels.Labels { +func labelProtosToLabels(labelPairs []prompb.Label, identifyingLabels ...string) labels.Labels { b := labels.ScratchBuilder{} for _, l := range labelPairs { - b.Add(l.Name, l.Value) + b.Add(l.Name, l.Value, identifyingLabels...) } b.Sort() return b.Labels() diff --git a/storage/remote/otlptranslator/prometheusremotewrite/helper.go b/storage/remote/otlptranslator/prometheusremotewrite/helper.go index 51bd055e9c..19bba9d4ac 100644 --- a/storage/remote/otlptranslator/prometheusremotewrite/helper.go +++ b/storage/remote/otlptranslator/prometheusremotewrite/helper.go @@ -10,6 +10,7 @@ import ( "fmt" "log" "math" + "slices" "sort" "strconv" "strings" @@ -86,9 +87,10 @@ func addSample(tsMap map[string]*prompb.TimeSeries, sample *prompb.Sample, label var identifyingLabels []string if isTargetMetric { identifyingLabels = []string{ - model.JobLabel, model.InstanceLabel, + model.JobLabel, } + slices.Sort(identifyingLabels) } newTs := &prompb.TimeSeries{ Labels: labels, diff --git a/storage/remote/write_handler.go b/storage/remote/write_handler.go index 43a821ddff..718f0c635a 100644 --- a/storage/remote/write_handler.go +++ b/storage/remote/write_handler.go @@ -114,7 +114,7 @@ func (h *writeHandler) write(ctx context.Context, req *prompb.WriteRequest) (err var exemplarErr error for _, ts := range req.Timeseries { - labels := labelProtosToLabels(ts.Labels) + labels := labelProtosToLabels(ts.Labels, ts.IdentifyingLabels...) if !labels.IsValid() { level.Warn(h.logger).Log("msg", "Invalid metric names or labels", "got", labels.String()) samplesWithInvalidLabels++ @@ -122,6 +122,8 @@ func (h *writeHandler) write(ctx context.Context, req *prompb.WriteRequest) (err } var ref storage.SeriesRef for _, s := range ts.Samples { + // When recording a sample for an info type metric with identifying labels, + // make sure also to persist which of the labels are identifying ref, err = app.Append(ref, labels, s.Timestamp, s.Value) if err != nil { unwrappedErr := errors.Unwrap(err) @@ -225,7 +227,6 @@ func (h *otlpWriteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } prwMetrics := make([]prompb.TimeSeries, 0, len(prwMetricsMap)) - for _, ts := range prwMetricsMap { prwMetrics = append(prwMetrics, *ts) }