Repository: VictoriaMetrics/VictoriaMetrics
Pull request #1282 (2021-05-10 14:51:57)

Adds tsdb match filters

codecov[bot]: # [Codecov](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics) Report
> Merging [#1282](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics) (e542325) into [master](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/commit/377ef9f39938fafa12fb6db3a90a396baf05f3bb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics) (377ef9f) will **increase** coverage by `1.26%`.
> The diff coverage is `54.72%`.

[![Impacted file tree graph](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282/graphs/tree.svg?width=650&height=150&src=pr&token=YND8dYVcXn&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics)](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics)

```diff
@@            Coverage Diff             @@
##           master    #1282      +/-   ##
==========================================
+ Coverage   59.36%   60.62%   +1.26%     
==========================================
  Files         215      215              
  Lines       25570    25748     +178     
==========================================
+ Hits        15179    15610     +431     
+ Misses       8984     8710     -274     
- Partials     1407     1428      +21     
```


| [Impacted Files](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics) | Coverage Δ | |
|---|---|---|
| [app/victoria-metrics/main.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-YXBwL3ZpY3RvcmlhLW1ldHJpY3MvbWFpbi5nbw==) | `6.55% <0.00%> (+0.20%)` | :arrow_up: |
| [app/vmalert/datasource/datasource.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-YXBwL3ZtYWxlcnQvZGF0YXNvdXJjZS9kYXRhc291cmNlLmdv) | `12.50% <ø> (ø)` | |
| [app/vmalert/datasource/init.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-YXBwL3ZtYWxlcnQvZGF0YXNvdXJjZS9pbml0Lmdv) | `0.00% <ø> (ø)` | |
| [app/vmalert/main.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-YXBwL3ZtYWxlcnQvbWFpbi5nbw==) | `22.01% <0.00%> (ø)` | |
| [app/vmalert/remotewrite/init.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-YXBwL3ZtYWxlcnQvcmVtb3Rld3JpdGUvaW5pdC5nbw==) | `0.00% <ø> (ø)` | |
| [app/vmauth/main.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-YXBwL3ZtYXV0aC9tYWluLmdv) | `11.11% <0.00%> (+0.58%)` | :arrow_up: |
| [app/vmctl/vm/vm.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-YXBwL3ZtY3RsL3ZtL3ZtLmdv) | `4.70% <ø> (ø)` | |
| [app/vmselect/prometheus/prometheus.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-YXBwL3Ztc2VsZWN0L3Byb21ldGhldXMvcHJvbWV0aGV1cy5nbw==) | `4.87% <0.00%> (-0.14%)` | :arrow_down: |
| [lib/fs/dir\_remover.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-bGliL2ZzL2Rpcl9yZW1vdmVyLmdv) | `30.50% <0.00%> (ø)` | |
| [lib/fs/fs.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-bGliL2ZzL2ZzLmdv) | `4.54% <0.00%> (-0.32%)` | :arrow_down: |
| ... and [42 more](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics). Last update [d9e3872...e542325](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1282?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics).


Patch:
diff --git a/app/vmselect/netstorage/netstorage.go b/app/vmselect/netstorage/netstorage.go
index c28a8b946..2ab9bbec5 100644
--- a/app/vmselect/netstorage/netstorage.go
+++ b/app/vmselect/netstorage/netstorage.go
@@ -695,6 +695,26 @@ func GetTSDBStatusForDate(deadline searchutils.Deadline, date uint64, topN int)
 	return status, nil
 }
 
+// GetTSDBStatusWithFilters returns tsdb status according to https://prometheus.io/docs/prometheus/latest/querying/api/#tsdb-stats
+func GetTSDBStatusWithFilters(deadline searchutils.Deadline, sq *storage.SearchQuery, topN int) (*storage.TSDBStatus, error) {
+	if deadline.Exceeded() {
+		return nil, fmt.Errorf("timeout exceeded before starting the query processing: %s", deadline.String())
+	}
+	tr := storage.TimeRange{
+		MaxTimestamp: sq.MaxTimestamp,
+		MinTimestamp: sq.MinTimestamp,
+	}
+	tfss, err := setupTfss(tr, sq.TagFilterss, deadline)
+	if err != nil {
+		return nil, err
+	}
+	status, err := vmstorage.GetTSDBStatusWithFilters(tr, tfss, topN, *maxMetricsPerSearch, deadline.Deadline())
+	if err != nil {
+		return nil, fmt.Errorf("error during tsdb status request: %w", err)
+	}
+	return status, nil
+}
+
 // GetSeriesCount returns the number of unique series.
 func GetSeriesCount(deadline searchutils.Deadline) (uint64, error) {
 	if deadline.Exceeded() {
diff --git a/app/vmselect/prometheus/prometheus.go b/app/vmselect/prometheus/prometheus.go
index 137b625f1..8847dd077 100644
--- a/app/vmselect/prometheus/prometheus.go
+++ b/app/vmselect/prometheus/prometheus.go
@@ -638,6 +638,12 @@ func TSDBStatusHandler(startTime time.Time, w http.ResponseWriter, r *http.Reque
 	if err := r.ParseForm(); err != nil {
 		return fmt.Errorf("cannot parse form values: %w", err)
 	}
+	etf, err := searchutils.GetEnforcedTagFiltersFromRequest(r)
+	if err != nil {
+		return err
+	}
+	matches := getMatchesFromRequest(r)
+
 	date := fasttime.UnixDate()
 	dateStr := r.FormValue("date")
 	if len(dateStr) > 0 {
@@ -662,9 +668,17 @@ func TSDBStatusHandler(startTime time.Time, w http.ResponseWriter, r *http.Reque
 		}
 		topN = n
 	}
-	status, err := netstorage.GetTSDBStatusForDate(deadline, date, topN)
-	if err != nil {
-		return fmt.Errorf(`cannot obtain tsdb status for date=%d, topN=%d: %w`, date, topN, err)
+	var status *storage.TSDBStatus
+	if len(matches) == 0 && len(etf) == 0 {
+		status, err = netstorage.GetTSDBStatusForDate(deadline, date, topN)
+		if err != nil {
+			return fmt.Errorf(`cannot obtain tsdb status for date=%d, topN=%d: %w`, date, topN, err)
+		}
+	} else {
+		status, err = tsdbStatusWithMatches(matches, etf, date, topN, deadline)
+		if err != nil {
+			return fmt.Errorf("cannot tsdb status with matches for date=%d, topN=%d: %w", date, topN, err)
+		}
 	}
 	w.Header().Set("Content-Type", "application/json; charset=utf-8")
 	bw := bufferedwriter.Get(w)
@@ -677,6 +691,26 @@ func TSDBStatusHandler(startTime time.Time, w http.ResponseWriter, r *http.Reque
 	return nil
 }
 
+func tsdbStatusWithMatches(matches []string, etf []storage.TagFilter, date uint64, topN int, deadline searchutils.Deadline) (*storage.TSDBStatus, error) {
+	tagFilterss, err := getTagFilterssFromMatches(matches)
+	if err != nil {
+		return nil, err
+	}
+
+	tagFilterss = addEnforcedFiltersToTagFilterss(tagFilterss, etf)
+	if len(tagFilterss) == 0 {
+		logger.Panicf("BUG: tagFilterss must be non-empty")
+	}
+	start := int64(date*secsPerDay) * 1000
+	end := int64(date*secsPerDay+secsPerDay) * 1000
+	sq := storage.NewSearchQuery(start, end, tagFilterss)
+	status, err := netstorage.GetTSDBStatusWithFilters(deadline, sq, topN)
+	if err != nil {
+		return nil, err
+	}
+	return status, nil
+}
+
 var tsdbStatusDuration = metrics.NewSummary(`vm_request_duration_seconds{path="/api/v1/status/tsdb"}`)
 
 // LabelsHandler processes /api/v1/labels request.
diff --git a/app/vmstorage/main.go b/app/vmstorage/main.go
index 731fc0002..485857f33 100644
--- a/app/vmstorage/main.go
+++ b/app/vmstorage/main.go
@@ -213,6 +213,14 @@ func GetTSDBStatusForDate(date uint64, topN int, deadline uint64) (*storage.TSDB
 	return status, err
 }
 
+// GetTSDBStatusWithFilters returns TSDB status for given filters.
+func GetTSDBStatusWithFilters(tr storage.TimeRange, tfss []*storage.TagFilters, topN, maxMetrics int, deadline uint64) (*storage.TSDBStatus, error) {
+	WG.Add(1)
+	status, err := Storage.GetTSDBStatusForDateWithFilters(tfss, tr, maxMetrics, deadline, topN)
+	WG.Done()
+	return status, err
+}
+
 // GetSeriesCount returns the number of time series in the storage.
 func GetSeriesCount(deadline uint64) (uint64, error) {
 	WG.Add(1)
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 806e77dcb..32ea4b5dc 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -7,6 +7,7 @@ sort: 15
 * FEATURE: vminsert: add support for data ingestion via other `vminsert` nodes. This allows building multi-level data ingestion paths in VictoriaMetrics cluster by writing data from one level of `vminsert` nodes to another level of `vminsert` nodes. See [these docs](https://docs.victoriametrics.com/Cluster-VictoriaMetrics.html#multi-level-cluster-setup) and [this comment](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/541#issuecomment-835487858) for details.
 * FEATURE: vmalert: add flag to control behaviour on startup for state restore errors. Such errors were returned and logged before as well. But now user can specify whether to just log these errors (`-remoteRead.ignoreRestoreErrors=true`) or to stop the process (`-remoteRead.ignoreRestoreErrors=false`). The latter is important when VM isn't ready yet to serve queries from vmalert and it needs to wait. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1252).
 * FEATURE: vmalert: add ability to pass `round_digits` query arg to datasource via `-datasource.roundDigits` command-line flag. This can be used for limiting the number of decimal digits after the point in recording rule results. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/525).
+* FEATURE: return `X-Server-Hostname` header in http responses of all the VictoriaMetrics components. This should simplify tracing the origin server behind a load balancer or behind auth proxy during troubleshooting.
 
 * BUGFIX: vmagent: fix possible race when refreshing `role: endpoints` and `role: endpointslices` scrape targets in `kubernetes_sd_config`. Prevoiusly `pod` objects could be updated after the related `endpoints` object update. This could lead to missing scrape targets. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1240).
 * BUGFIX: properly remove stale parts outside the configured retention if `-retentionPeriod` is smaller than one month. Previously stale parts could remain active for up to a month after they go outside the retention.
diff --git a/lib/httpserver/httpserver.go b/lib/httpserver/httpserver.go
index e2892ca99..d3f4260fc 100644
--- a/lib/httpserver/httpserver.go
+++ b/lib/httpserver/httpserver.go
@@ -8,6 +8,7 @@ import (
 	"flag"
 	"fmt"
 	"io"
+	"log"
 	"net"
 	"net/http"
 	"net/http/pprof"
@@ -194,6 +195,17 @@ func gzipHandler(s *server, rh RequestHandler) http.HandlerFunc {
 var metricsHandlerDuration = metrics.NewHistogram(`vm_http_request_duration_seconds{path="/metrics"}`)
 var connTimeoutClosedConns = metrics.NewCounter(`vm_http_conn_timeout_closed_conns_total`)
 
+var hostname = func() string {
+	h, err := os.Hostname()
+	if err != nil {
+		// Cannot use logger.Errorf, since it isn't initialized yet.
+		// So use log.Printf instead.
+		log.Printf("ERROR: cannot determine hostname: %s", err)
+		return "unknown"
+	}
+	return h
+}()
+
 func handlerWrapper(s *server, w http.ResponseWriter, r *http.Request, rh RequestHandler) {
 	// All the VictoriaMetrics code assumes that panic stops the process.
 	// Unfortunately, the standard net/http.Server recovers from panics in request handlers,
@@ -209,6 +221,7 @@ func handlerWrapper(s *server, w http.ResponseWriter, r *http.Request, rh Reques
 		}
 	}()
 
+	w.Header().Add("X-Server-Hostname", hostname)
 	requestsTotal.Inc()
 	if whetherToCloseConn(r) {
 		connTimeoutClosedConns.Inc()
diff --git a/lib/promscrape/discovery/kubernetes/api_watcher.go b/lib/promscrape/discovery/kubernetes/api_watcher.go
index 854a510fe..1dbbbc1cc 100644
--- a/lib/promscrape/discovery/kubernetes/api_watcher.go
+++ b/lib/promscrape/discovery/kubernetes/api_watcher.go
@@ -75,11 +75,6 @@ func newAPIWatcher(apiServer string, ac *promauth.Config, sdc *SDConfig, swcFunc
 }
 
 func (aw *apiWatcher) mustStart() {
-	if aw.role == "endpoints" || aw.role == "endpointslices" {
-		// endpoints and endpointslices watchers query pod and service objects. So start watchers for these roles as well.
-		aw.gw.startWatchersForRole("pod", nil)
-		aw.gw.startWatchersForRole("service", nil)
-	}
 	aw.gw.startWatchersForRole(aw.role, aw)
 }
 
@@ -255,37 +250,12 @@ func (gw *groupWatcher) getObjectByRoleLocked(role, namespace, name string) obje
 	return nil
 }
 
-func (gw *groupWatcher) refreshEndpointsLabelsLocked(namespace, key string) {
-	// Refresh endpoints and endpointslices labels for the corresponding service as Prometheus does.
-	// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1240
-	gw.refreshObjectLabelsLocked("endpoints", namespace, key)
-	gw.refreshObjectLabelsLocked("endpointslices", namespace, key)
-}
-
-func (gw *groupWatcher) refreshObjectLabelsLocked(role, namespace, key string) {
-	for _, uw := range gw.m {
-		if len(uw.aws) == 0 {
-			// No apiWatchers to notify
-			continue
-		}
-		if uw.role != role {
-			// Role mismatch
-			continue
-		}
-		if uw.namespace != "" && uw.namespace != namespace {
-			// Namespace mismatch
-			continue
-		}
-		if o := uw.objectsByKey[key]; o != nil {
-			labels := o.getTargetLabels(gw)
-			for aw := range uw.aws {
-				aw.setScrapeWorks(namespace, key, labels)
-			}
-		}
-	}
-}
-
 func (gw *groupWatcher) startWatchersForRole(role string, aw *apiWatcher) {
+	if role == "endpoints" || role == "endpointslices" {
+		// endpoints and endpointslices watchers query pod and service objects. So start watchers for these roles as well.
+		gw.startWatchersForRole("pod", nil)
+		gw.startWatchersForRole("service", nil)
+	}
 	paths, namespaces := getAPIPathsWithNamespaces(role, gw.namespaces, gw.selectors)
 	for i, path := range paths {
 		apiURL := gw.apiServer + path
@@ -303,6 +273,18 @@ func (gw *groupWatcher) startWatchersForRole(role string, aw *apiWatcher) {
 		if needStart {
 			uw.reloadObjects()
 			go uw.watchForUpdates()
+			if role == "endpoints" || role == "endpointslices" {
+				// Refresh endpoints and enpointslices targets in background, since they depend on other object types such as pod and service.
+				// This should fix https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1240 .
+				go func() {
+					for {
+						time.Sleep(5 * time.Second)
+						gw.mu.Lock()
+						uw.reloadScrapeWorksForAPIWatchersLocked(uw.aws)
+						gw.mu.Unlock()
+					}
+				}()
+			}
 		}
 	}
 }
@@ -488,13 +470,6 @@ func (uw *urlWatcher) reloadObjects() string {
 	uw.objectsCount.Add(added - removed)
 	uw.reloadScrapeWorksForAPIWatchersLocked(uw.aws)
 	uw.resourceVersion = metadata.ResourceVersion
-	if uw.role == "service" {
-		// Refresh endpoints labels for the corresponding services as Prometheus does.
-		// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1240
-		for key := range objectsByKey {
-			uw.gw.refreshEndpointsLabelsLocked(uw.namespace, key)
-		}
-	}
 	uw.gw.mu.Unlock()
 
 	logger.Infof("reloaded %d objects from %q", len(objectsByKey), requestURL)
@@ -616,11 +591,6 @@ func (uw *urlWatcher) readObjectUpdateStream(r io.Reader) error {
 					aw.setScrapeWorks(uw.namespace, key, labels)
 				}
 			}
-			if uw.role == "service" {
-				// Refresh endpoints labels for the corresponding service as Prometheus does.
-				// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1240
-				uw.gw.refreshEndpointsLabelsLocked(uw.namespace, key)
-			}
 			uw.gw.mu.Unlock()
 		case "DELETED":
 			o, err := uw.parseObject(we.Object)
@@ -637,11 +607,6 @@ func (uw *urlWatcher) readObjectUpdateStream(r io.Reader) error {
 			for aw := range uw.aws {
 				aw.removeScrapeWorks(uw.namespace, key)
 			}
-			if uw.role == "service" {
-				// Refresh endpoints labels for the corresponding service as Prometheus does.
-				// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1240
-				uw.gw.refreshEndpointsLabelsLocked(uw.namespace, key)
-			}
 			uw.gw.mu.Unlock()
 		case "BOOKMARK":
 			// See https://kubernetes.io/docs/reference/using-api/api-concepts/#watch-bookmarks
diff --git a/lib/storage/index_db.go b/lib/storage/index_db.go
index fc7365dcf..393dca868 100644
--- a/lib/storage/index_db.go
+++ b/lib/storage/index_db.go
@@ -1309,6 +1309,105 @@ func (is *indexSearch) getSeriesCount() (uint64, error) {
 	return metricIDsLen, nil
 }
 
+// GetTSDBStatusWithFiltersOnTimeRange returns topN entries for tsdb status for given TSIDs.
+func (db *indexDB) GetTSDBStatusWithFiltersOnTimeRange(tfss []*TagFilters, tr TimeRange, maxMetrics, topN int, deadline uint64) (*TSDBStatus, error) {
+	is := db.getIndexSearch(deadline)
+	status, err := is.GetTSDBStatusWithFiltersOnTimeRange(tfss, tr, maxMetrics, topN, deadline)
+	db.putIndexSearch(is)
+	if err != nil {
+		return nil, err
+	}
+	if status.hasEntries() {
+		return status, nil
+	}
+	ok := db.doExtDB(func(extDB *indexDB) {
+		is := extDB.getIndexSearch(deadline)
+		status, err = is.GetTSDBStatusWithFiltersOnTimeRange(tfss, tr, maxMetrics, topN, deadline)
+		extDB.putIndexSearch(is)
+	})
+	if ok && err != nil {
+		return nil, fmt.Errorf("error when obtaining TSDB status from extDB: %w", err)
+	}
+	return status, nil
+}
+
+// GetTSDBStatusWithFiltersOnTimeRange returns topN entries for tsdb status for given TSIDs.
+func (is *indexSearch) GetTSDBStatusWithFiltersOnTimeRange(tfss []*TagFilters, tr TimeRange, maxMetrics, topN int, deadline uint64) (*TSDBStatus, error) {
+	metricIDs, err := is.searchMetricIDs(tfss, tr, maxMetrics)
+	if err != nil {
+		return nil, err
+	}
+
+	thLabelValueCountByLabelName := newTopHeap(topN)
+	thSeriesCountByLabelValuePair := newTopHeap(topN)
+	thSeriesCountByMetricName := newTopHeap(topN)
+
+	var metricName, tmpMetricName, labelPairs []byte
+	var mn MetricName
+	var metricNameCnt uint64
+	metricNameLabel := "__name__"
+	tmpPairs := []byte(metricNameLabel)
+
+	// holds uniq count values per label name.
+	cntByUniqLabelValues := make(map[string]uint64, len(metricIDs))
+	// holds count for label=value uniq pairs.
+	cntLabelPairs := make(map[string]uint64, len(metricIDs))
+	for i := range metricIDs {
+		if i&paceLimiterSlowIterationsMask == 0 {
+			if err := checkSearchDeadlineAndPace(deadline); err != nil {
+				return nil, err
+			}
+		}
+		mID := metricIDs[i]
+		metricName, err = is.searchMetricName(metricName[:0], mID)
+		if err == io.EOF {
+			continue
+		}
+		if err != nil {
+			return nil, err
+		}
+		if err = mn.Unmarshal(metricName); err != nil {
+			return nil, fmt.Errorf("cannot unmarshal metricName=%q: %w", metricName, err)
+		}
+		if !bytes.Equal(tmpMetricName, mn.MetricGroup) {
+			tmpMetricName = mn.MetricGroup
+			cntByUniqLabelValues[metricNameLabel]++
+			tmpPairs = append(tmpPairs[:len(metricNameLabel)], '=')
+			tmpPairs = append(tmpPairs, mn.MetricGroup...)
+			thSeriesCountByMetricName.pushIfNonEmpty(tmpMetricName, metricNameCnt)
+			metricNameCnt = 0
+		}
+		cntLabelPairs[string(tmpPairs)]++
+		metricNameCnt++
+		for j := range mn.Tags {
+			tag := mn.Tags[j]
+			labelPairs = append(labelPairs[:0], tag.Key...)
+			labelPairs = append(labelPairs, '=')
+			labelPairs = append(labelPairs, tag.Value...)
+			// if label pairs not seen, its uniq value for given label.
+			if _, ok := cntLabelPairs[string(labelPairs)]; ok {
+				cntLabelPairs[string(labelPairs)]++
+			} else {
+				cntLabelPairs[string(labelPairs)]++
+				cntByUniqLabelValues[string(tag.Key)]++
+			}
+		}
+	}
+	thSeriesCountByMetricName.pushIfNonEmpty(tmpMetricName, metricNameCnt)
+	for k, v := range cntLabelPairs {
+		thSeriesCountByLabelValuePair.pushIfNonEmpty([]byte(k), v)
+	}
+	for k, v := range cntByUniqLabelValues {
+		thLabelValueCountByLabelName.pushIfNonEmpty([]byte(k), v)
+	}
+	status := TSDBStatus{
+		SeriesCountByMetricName:     thSeriesCountByMetricName.getSortedResult(),
+		LabelValueCountByLabelName:  thLabelValueCountByLabelName.getSortedResult(),
+		SeriesCountByLabelValuePair: thSeriesCountByLabelValuePair.getSortedResult(),
+	}
+	return &status, nil
+}
+
 // GetTSDBStatusForDate returns topN entries for tsdb status for the given date.
 func (db *indexDB) GetTSDBStatusForDate(date uint64, topN int, deadline uint64) (*TSDBStatus, error) {
 	is := db.getIndexSearch(deadline)
@@ -2293,15 +2392,11 @@ func (is *indexSearch) searchMetricIDs(tfss []*TagFilters, tr TimeRange, maxMetr
 	metricIDs := &uint64set.Set{}
 	for _, tfs := range tfss {
 		if len(tfs.tfs) == 0 {
-			// Return all the metric ids
-			if err := is.updateMetricIDsAll(metricIDs, maxMetrics+1); err != nil {
-				return nil, err
-			}
-			if metricIDs.Len() > maxMetrics {
-				return nil, fmt.Errorf("the number of unique timeseries exceeds %d; either narrow down the search or increase -search.maxUniqueTimeseries", maxMetrics)
+			// An empty filters must be equivalent to `{__name__!=""}`
+			tfs = NewTagFilters()
+			if err := tfs.Add(nil, nil, true, false); err != nil {
+				logger.Panicf(`BUG: cannot add {__name__!=""} filter: %s`, err)
 			}
-			// Stop the iteration, since we cannot find more metric ids with the remaining tfss.
-			break
 		}
 		if err := is.updateMetricIDsForTagFilters(metricIDs, tfs, tr, maxMetrics+1); err != nil {
 			return nil, err
diff --git a/lib/storage/storage.go b/lib/storage/storage.go
index 81bd3b18e..f24f7af76 100644
--- a/lib/storage/storage.go
+++ b/lib/storage/storage.go
@@ -1248,6 +1248,11 @@ func (s *Storage) GetTSDBStatusForDate(date uint64, topN int, deadline uint64) (
 	return s.idb().GetTSDBStatusForDate(date, topN, deadline)
 }
 
+// GetTSDBStatusForDateWithFilters special function for /api/v1/status/tsdb with match[] filters.
+func (s *Storage) GetTSDBStatusForDateWithFilters(tfss []*TagFilters, tr TimeRange, maxMetrics int, deadline uint64, topN int) (*TSDBStatus, error) {
+	return s.idb().GetTSDBStatusWithFiltersOnTimeRange(tfss, tr, maxMetrics, topN, deadline)
+}
+
 // MetricRow is a metric to insert into storage.
 type MetricRow struct {
 	// MetricNameRaw contains raw metric name, which must be decoded


Test patch:
diff --git a/lib/storage/index_db_test.go b/lib/storage/index_db_test.go
index 98a4943d5..a879f5529 100644
--- a/lib/storage/index_db_test.go
+++ b/lib/storage/index_db_test.go
@@ -1691,6 +1691,33 @@ func TestSearchTSIDWithTimeRange(t *testing.T) {
 	if !reflect.DeepEqual(status.SeriesCountByLabelValuePair, expectedSeriesCountByLabelValuePair) {
 		t.Fatalf("unexpected SeriesCountByLabelValuePair;\ngot\n%v\nwant\n%v", status.SeriesCountByLabelValuePair, expectedSeriesCountByLabelValuePair)
 	}
+
+	// Perform a search across all the days, should match all metrics
+	tr = TimeRange{
+		MinTimestamp: int64(now),
+		MaxTimestamp: int64(now - msecPerDay*days),
+	}
+
+	tfs = NewTagFilters()
+	if err := tfs.Add([]byte("day"), []byte("3"), false, false); err != nil {
+		t.Fatalf("cannot add filter: %s", err)
+	}
+	status, err = db.GetTSDBStatusWithFiltersOnTimeRange([]*TagFilters{tfs}, tr, 10000, 5, noDeadline)
+	if err != nil {
+		t.Fatalf("error in GetTSDBStatusForDate: %s", err)
+	}
+	if !status.hasEntries() {
+		t.Fatalf("expecting non-empty TSDB status")
+	}
+	expectedSeriesCountByMetricName = []TopHeapEntry{
+		{
+			Name:  "testMetric",
+			Count: 1000,
+		},
+	}
+	if !reflect.DeepEqual(status.SeriesCountByMetricName, expectedSeriesCountByMetricName) {
+		t.Fatalf("unexpected SeriesCountByMetricName;\ngot\n%v\nwant\n%v", status.SeriesCountByMetricName, expectedSeriesCountByMetricName)
+	}
 }
 
 func toTFPointers(tfs []tagFilter) []*tagFilter {