From 5f46f8a11de37fe9c99cc991ed9f20852331ce4e Mon Sep 17 00:00:00 2001 From: Roman Khavronenko Date: Fri, 7 Jun 2024 15:44:48 +0200 Subject: [PATCH] lib/promrelabel: speedup label match by `__name__` (#6432) The change adds a fastpath for `equalValue` comparisons against `__name__` label by avoiding calls to `toCanonicalLabelName` func. This speedups matches by metric name like `'foo'`. See bench stats below: ``` benchcmp old.txt new.txt benchmark old ns/op new ns/op delta BenchmarkIfExpression/equal_label:_last-10 35.6 35.1 -1.18% BenchmarkIfExpression/equal_label:_middle-10 18.3 17.3 -5.41% BenchmarkIfExpression/equal_label:_first-10 1.20 1.24 +2.74% BenchmarkIfExpression/equal___name__:_last-10 10.1 4.96 -50.75% BenchmarkIfExpression/equal___name__:_middle-10 5.79 3.16 -45.41% BenchmarkIfExpression/equal___name__:_first-10 1.17 1.05 -9.76% ``` Signed-off-by: hagen1778 --- docs/CHANGELOG.md | 1 + lib/promrelabel/if_expression.go | 14 +++- lib/promrelabel/if_expression_timing_test.go | 78 ++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 lib/promrelabel/if_expression_timing_test.go diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 257493fd7..e41464b3a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -60,6 +60,7 @@ See also [LTS releases](https://docs.victoriametrics.com/lts-releases/). * FEATURE: expose metric `vm_indexdb_items_dropped_total` to track the number of IndexDB records that had to be dropped during ingestion. The reason of dropping the record will be annotated in `reason` label of the exposed metric. This change also comes with a new [alerting rule](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/alerts-health.yml) to track changes of this metric. * FEATURE: [alerts-health](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/alerts-health.yml): add new alerting rules `TooLongLabelValues` and `TooLongLabelNames` to notify about truncation of label values or names respectively. * FEATURE: [stream aggregation](https://docs.victoriametrics.com/stream-aggregation/): expose `vm_streamaggr_ignored_samples_total` [counters](https://docs.victoriametrics.com/keyconcepts/#counter) at [`/metrics` page](https://docs.victoriametrics.com/#monitoring), which can be used for detecting amount of too old or NaN valued ignored samples. Expose also `vm_streamaggr_samples_lag_seconds` [histogram](https://docs.victoriametrics.com/keyconcepts/#histogram) to monitor aggregated samples lag. +* FEATURE: [stream aggregation](https://docs.victoriametrics.com/stream-aggregation/): improve filtering speed of the received data samples if [match](https://docs.victoriametrics.com/stream-aggregation/#stream-aggregation-config) field is matching only [metric name](https://docs.victoriametrics.com/keyconcepts/#structure-of-a-metric). * BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix bug that prevents the first query trace from expanding on click event. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6186). The issue was introduced in [v1.100.0](https://docs.victoriametrics.com/changelog/#v11000) release. * BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix calendar display when `UTC+00:00` timezone is set. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6239). diff --git a/lib/promrelabel/if_expression.go b/lib/promrelabel/if_expression.go index 1f3506dfb..7d102a1f6 100644 --- a/lib/promrelabel/if_expression.go +++ b/lib/promrelabel/if_expression.go @@ -287,10 +287,22 @@ func (lf *labelFilter) match(labels []prompbmarshal.Label) bool { return false } +func (lf *labelFilter) equalNameValue(labels []prompbmarshal.Label) bool { + for _, label := range labels { + if label.Name == "__name__" { + return label.Value == lf.value + } + } + return false +} + func (lf *labelFilter) equalValue(labels []prompbmarshal.Label) bool { + if lf.label == "" { + return lf.equalNameValue(labels) + } labelNameMatches := 0 for _, label := range labels { - if toCanonicalLabelName(label.Name) != lf.label { + if label.Name != lf.label { continue } labelNameMatches++ diff --git a/lib/promrelabel/if_expression_timing_test.go b/lib/promrelabel/if_expression_timing_test.go new file mode 100644 index 000000000..a407d8e86 --- /dev/null +++ b/lib/promrelabel/if_expression_timing_test.go @@ -0,0 +1,78 @@ +package promrelabel + +import ( + "fmt" + "gopkg.in/yaml.v2" + "testing" + + "github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal" +) + +func BenchmarkIfExpression(b *testing.B) { + const maxLabels = 100 + labels := make([]prompbmarshal.Label, maxLabels) + for i := 0; i < maxLabels; i++ { + label := prompbmarshal.Label{ + Name: fmt.Sprintf("foo%d", i), + Value: fmt.Sprintf("bar%d", i), + } + labels[i] = label + } + + b.Run("equal label: last", func(b *testing.B) { + n := maxLabels - 1 + ifExpr := fmt.Sprintf(`'{foo%d="bar%d"}'`, n, n) + benchIfExpr(b, ifExpr, labels) + }) + b.Run("equal label: middle", func(b *testing.B) { + n := maxLabels / 2 + ifExpr := fmt.Sprintf(`'{foo%d="bar%d"}'`, n, n) + benchIfExpr(b, ifExpr, labels) + }) + b.Run("equal label: first", func(b *testing.B) { + ifExpr := fmt.Sprintf(`'{foo%d="bar%d"}'`, 0, 0) + benchIfExpr(b, ifExpr, labels) + }) + + labels[maxLabels-1] = prompbmarshal.Label{ + Name: "__name__", + Value: "foo", + } + b.Run("equal __name__: last", func(b *testing.B) { + ifExpr := `foo` + benchIfExpr(b, ifExpr, labels) + }) + + labels[maxLabels/2] = prompbmarshal.Label{ + Name: "__name__", + Value: "foo", + } + b.Run("equal __name__: middle", func(b *testing.B) { + ifExpr := `foo` + benchIfExpr(b, ifExpr, labels) + }) + + labels[0] = prompbmarshal.Label{ + Name: "__name__", + Value: "foo", + } + b.Run("equal __name__: first", func(b *testing.B) { + ifExpr := `foo` + benchIfExpr(b, ifExpr, labels) + }) +} + +func benchIfExpr(b *testing.B, expr string, labels []prompbmarshal.Label) { + b.Helper() + var ie IfExpression + if err := yaml.UnmarshalStrict([]byte(expr), &ie); err != nil { + b.Fatalf("unexpected error during unmarshal: %s", err) + } + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if !ie.Match(labels) { + panic(fmt.Sprintf("expected to have a match for %q", expr)) + } + } + }) +}