From e615bcced7f52b1eb50d911de739a3fec5c54db9 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Thu, 18 Apr 2024 01:10:07 +0200 Subject: [PATCH] lib/storage: improve performance for /api/v1/label/labelName/values when match[] contains only a single filter on labelName This speeds up auto-suggestion for metric names in VMUI and Grafana, which use the following query in this case: /api/v1/label/__name__/values?match[]={__name__=~"*.some_value.*"} When the user types `some_value` in the query input field. --- docs/CHANGELOG.md | 1 + lib/storage/storage.go | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index f7dcf0279..58a4847a2 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -35,6 +35,7 @@ See also [LTS releases](https://docs.victoriametrics.com/lts-releases/). * FEATURE: [vmauth](https://docs.victoriametrics.com/vmauth/): support automatic discovering and load balancing for TCP addresses behind DNS SRV addresses. These addresses can be put inside `url_prefix` urls in the form `http://srv+addr/path`, where the `addr` is the [DNS SRV](https://en.wikipedia.org/wiki/SRV_record) address, which is automatically resolved to hostnames with TCP ports. See [these docs](https://docs.victoriametrics.com/vmauth/#srv-urls) for details. * FEATURE: [vmauth](https://docs.victoriametrics.com/vmauth/): support specifying client TLS certificates and TLS ServerName for requests to HTTPS backends. See [these docs](https://docs.victoriametrics.com/vmauth/#backend-tls-setup). * FEATURE: [vmauth](https://docs.victoriametrics.com/vmauth/): support regex matching when routing incoming requests based on HTTP [query args](https://en.wikipedia.org/wiki/Query_string) via `src_query_args` option at `url_map`. See [these docs](https://docs.victoriametrics.com/vmauth/#generic-http-proxy-for-different-backends) and [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6070). +* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): optimize auto-suggestion performance for metric names when the database contains big number of unique time series. * FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): in the Select component, user-entered values are now preserved on blur if they match options in the list. * BUGFIX: [vmalert](https://docs.victoriametrics.com/vmalert.html): supported any status codes from the range 200-299 from alertmanager. Previously, only 200 status code considered a successful action. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6110). diff --git a/lib/storage/storage.go b/lib/storage/storage.go index cee64beb6..973babe1b 100644 --- a/lib/storage/storage.go +++ b/lib/storage/storage.go @@ -1350,7 +1350,42 @@ func (s *Storage) SearchLabelNamesWithFiltersOnTimeRange(qt *querytracer.Tracer, func (s *Storage) SearchLabelValuesWithFiltersOnTimeRange(qt *querytracer.Tracer, accountID, projectID uint32, labelName string, tfss []*TagFilters, tr TimeRange, maxLabelValues, maxMetrics int, deadline uint64, ) ([]string, error) { - return s.idb().SearchLabelValuesWithFiltersOnTimeRange(qt, accountID, projectID, labelName, tfss, tr, maxLabelValues, maxMetrics, deadline) + idb := s.idb() + + key := labelName + if key == "__name__" { + key = "" + } + if len(tfss) == 1 && len(tfss[0].tfs) == 1 && string(tfss[0].tfs[0].key) == key { + // tfss contains only a single filter on labelName. It is faster searching for label values + // without any filters and then later applying the filter to the found label values. + lvs, err := idb.SearchLabelValuesWithFiltersOnTimeRange(qt, accountID, projectID, labelName, nil, tr, maxLabelValues, maxMetrics, deadline) + if err != nil { + return nil, err + } + lvs = filterLabelValues(accountID, projectID, lvs, &tfss[0].tfs[0], key) + return lvs, nil + } + + return idb.SearchLabelValuesWithFiltersOnTimeRange(qt, accountID, projectID, labelName, tfss, tr, maxLabelValues, maxMetrics, deadline) +} + +func filterLabelValues(accountID, projectID uint32, lvs []string, tf *tagFilter, key string) []string { + var b []byte + result := lvs[:0] + for _, lv := range lvs { + b = marshalCommonPrefix(b[:0], nsPrefixTagToMetricIDs, accountID, projectID) + b = marshalTagValue(b, bytesutil.ToUnsafeBytes(key)) + b = marshalTagValue(b, bytesutil.ToUnsafeBytes(lv)) + ok, err := tf.match(b) + if err != nil { + logger.Panicf("BUG: cannot match label %q=%q with tagFilter %s: %w", key, lv, tf.String(), err) + } + if ok { + result = append(result, lv) + } + } + return result } // SearchTagValueSuffixes returns all the tag value suffixes for the given tagKey and tagValuePrefix on the given tr.