Repository: VictoriaMetrics/VictoriaMetrics
Pull request #2180 (2022-02-11 23:20:28)

build(deps): bump follow-redirects from 1.14.6 to 1.14.8 in /app/vmui/packages/vmui
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.6 to 1.14.8.
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/follow-redirects/follow-redirects/commit/3d81dc3237b4ffe8b722bb3d1c70a7866657166e"><code>3d81dc3</code></a> Release version 1.14.8 of the npm package.</li>
<li><a href="https://github.com/follow-redirects/follow-redirects/commit/62e546a99c07c3ee5e4e0718c84a6ca127c5c445"><code>62e546a</code></a> Drop confidential headers across schemes.</li>
<li><a href="https://github.com/follow-redirects/follow-redirects/commit/2ede36d7c60d3acdcd324dcd99a9dbd52e4fb3a6"><code>2ede36d</code></a> Release version 1.14.7 of the npm package.</li>
<li><a href="https://github.com/follow-redirects/follow-redirects/commit/8b347cbcef7c7b72a6e9be20f5710c17d6163c22"><code>8b347cb</code></a> Drop Cookie header across domains.</li>
<li>See full diff in <a href="https://github.com/follow-redirects/follow-redirects/compare/v1.14.6...v1.14.8">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=follow-redirects&package-manager=npm_and_yarn&previous-version=1.14.6&new-version=1.14.8)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/VictoriaMetrics/VictoriaMetrics/network/alerts).

</details>

codecov[bot]: # [Codecov](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/2180?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics) Report
> Merging [#2180](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/2180?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics) (9daf536) into [master](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/commit/e08b74fcd6bd49e52772c3f276e02517b9652540?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics) (e08b74f) will **increase** coverage by `0.03%`.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/2180/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/2180?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics)

```diff
@@            Coverage Diff             @@
##           master    #2180      +/-   ##
==========================================
+ Coverage   60.56%   60.59%   +0.03%     
==========================================
  Files         240      240              
  Lines       40631    40631              
==========================================
+ Hits        24607    24621      +14     
+ Misses      14390    14379      -11     
+ Partials     1634     1631       -3     
```


| [Impacted Files](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/2180?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics) | Coverage Δ | |
|---|---|---|
| [lib/mergeset/table.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/2180/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-bGliL21lcmdlc2V0L3RhYmxlLmdv) | `65.68% <0.00%> (+0.48%)` | :arrow_up: |
| [lib/bloomfilter/filter.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/2180/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-bGliL2Jsb29tZmlsdGVyL2ZpbHRlci5nbw==) | `100.00% <0.00%> (+2.00%)` | :arrow_up: |
| [app/vmselect/promql/aggr\_incremental.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/2180/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-YXBwL3Ztc2VsZWN0L3Byb21xbC9hZ2dyX2luY3JlbWVudGFsLmdv) | `87.65% <0.00%> (+2.40%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/2180?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/2180?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics). Last update [e08b74f...9daf536](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/2180?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/promql/eval.go b/app/vmselect/promql/eval.go
index f13a9b7d5..535bd1dab 100644
--- a/app/vmselect/promql/eval.go
+++ b/app/vmselect/promql/eval.go
@@ -633,6 +633,9 @@ func evalRollupFuncWithoutAt(ec *EvalConfig, funcName string, rf rollupFunc, exp
 	if err != nil {
 		return nil, err
 	}
+	if funcName == "absent_over_time" {
+		rvs = aggregateAbsentOverTime(ec, re.Expr, rvs)
+	}
 	if offset != 0 && len(rvs) > 0 {
 		// Make a copy of timestamps, since they may be used in other values.
 		srcTimestamps := rvs[0].Timestamps
@@ -647,6 +650,27 @@ func evalRollupFuncWithoutAt(ec *EvalConfig, funcName string, rf rollupFunc, exp
 	return rvs, nil
 }
 
+// aggregateAbsentOverTime collapses tss to a single time series with 1 and nan values.
+//
+// Values for returned series are set to nan if at least a single tss series contains nan at that point.
+// This means that tss contains a series with non-empty results at that point.
+// This follows Prometheus logic - see https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2130
+func aggregateAbsentOverTime(ec *EvalConfig, expr metricsql.Expr, tss []*timeseries) []*timeseries {
+	rvs := getAbsentTimeseries(ec, expr)
+	if len(tss) == 0 {
+		return rvs
+	}
+	for i := range tss[0].Values {
+		for _, ts := range tss {
+			if math.IsNaN(ts.Values[i]) {
+				rvs[0].Values[i] = nan
+				break
+			}
+		}
+	}
+	return rvs
+}
+
 func evalRollupFuncWithSubquery(ec *EvalConfig, funcName string, rf rollupFunc, expr metricsql.Expr, re *metricsql.RollupExpr) ([]*timeseries, error) {
 	// TODO: determine whether to use rollupResultCacheV here.
 	step := re.Step.Duration(ec.Step)
@@ -669,10 +693,6 @@ func evalRollupFuncWithSubquery(ec *EvalConfig, funcName string, rf rollupFunc,
 		return nil, err
 	}
 	if len(tssSQ) == 0 {
-		if funcName == "absent_over_time" {
-			tss := evalNumber(ec, 1)
-			return tss, nil
-		}
 		return nil, nil
 	}
 	sharedTimestamps := getTimestamps(ec.Start, ec.End, ec.Step)
@@ -822,14 +842,7 @@ func evalRollupFuncWithMetricExpr(ec *EvalConfig, funcName string, rf rollupFunc
 	rssLen := rss.Len()
 	if rssLen == 0 {
 		rss.Cancel()
-		var tss []*timeseries
-		if funcName == "absent_over_time" {
-			tss = getAbsentTimeseries(ec, me)
-		}
-		// Add missing points until ec.End.
-		// Do not cache the result, since missing points
-		// may be backfilled in the future.
-		tss = mergeTimeseries(tssCached, tss, start, ec)
+		tss := mergeTimeseries(tssCached, nil, start, ec)
 		return tss, nil
 	}
 
diff --git a/app/vmui/packages/vmui/package-lock.json b/app/vmui/packages/vmui/package-lock.json
index a33fef861..921f89680 100644
--- a/app/vmui/packages/vmui/package-lock.json
+++ b/app/vmui/packages/vmui/package-lock.json
@@ -9356,9 +9356,9 @@
       "peer": true
     },
     "node_modules/follow-redirects": {
-      "version": "1.14.6",
-      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz",
-      "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==",
+      "version": "1.14.8",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
+      "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
       "dev": true,
       "funding": [
         {
@@ -26422,9 +26422,9 @@
       "peer": true
     },
     "follow-redirects": {
-      "version": "1.14.6",
-      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz",
-      "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==",
+      "version": "1.14.8",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
+      "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
       "dev": true,
       "peer": true
     },
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index b045e5d7b..02bdf7830 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -31,9 +31,11 @@ The following tip changes can be tested by building VictoriaMetrics components f
 * FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): provide the ability to fetch target responses on behalf of `vmagent` by clicking the `response` link for the needed target at `/targets` page. This feature may be useful for debugging responses from targets located in isolated environments.
 * FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): show the total number of scrapes and the total number of scrape errors per target at `/targets` page. This information may be useful when debugging unreliable scrape targets.
 * FEATURE: vmagent and single-node VictoriaMetrics: disallow unknown fields at `-promscrape.config` file. Previously unknown fields were allowed. This could lead to long-living silent config errors. The previous behaviour can be returned by passing `-promscrape.config.strictParse=false` command-line flag.
-* FEATURE: add `__meta_kubernetes_endpointslice_label*` and `__meta_kubernetes_endpointslice_annotation*` labels for `role: endpointslice` targets in [kubernetes_sd_config](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config) to be consistent with other `role` values. See [this issue](https://github.com/prometheus/prometheus/issues/10284).
+* FEATURE: vmagent: add `__meta_kubernetes_endpointslice_label*` and `__meta_kubernetes_endpointslice_annotation*` labels for `role: endpointslice` targets in [kubernetes_sd_config](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config) to be consistent with other `role` values. See [this issue](https://github.com/prometheus/prometheus/issues/10284).
+* FEATURE: vmagent: add `collapse all` and `expand all` buttons to `http://vmagent:8429/targets` page. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2021).
 * FEATURE: vmagent: support Prometheus-like durations in `-promscrape.config`. See [this comment](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/817#issuecomment-1033384766).
 
+* BUGFIX: calculate [absent_over_time()](https://docs.victoriametrics.com/MetricsQL.html#absent_over_time) in the same way as Prometheus does. Previously it could return multiple time series instead of at most one time series like Prometheus does. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2130).
 * BUGFIX: return proper results from `highestMax()` function at [Graphite render API](https://docs.victoriametrics.com/#graphite-render-api-usage). Previously it was incorrectly returning timeseries with min peaks instead of max peaks.
 * BUGFIX: properly limit indexdb cache sizes. Previously they could exceed values set via `-memory.allowedPercent` and/or `-memory.allowedBytes` when `indexdb` contained many data parts. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2007).
 * BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix a bug, which could break time range picker when editing `From` or `To` input fields. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2080).
diff --git a/lib/promscrape/targets_response.qtpl.go b/lib/promscrape/targets_response.qtpl.go
deleted file mode 100644
index c5abd4e9a..000000000
--- a/lib/promscrape/targets_response.qtpl.go
+++ /dev/null
@@ -1,466 +0,0 @@
-// Code generated by qtc from "targets_response.qtpl". DO NOT EDIT.
-// See https://github.com/valyala/quicktemplate for details.
-
-//line lib/promscrape/targets_response.qtpl:1
-package promscrape
-
-//line lib/promscrape/targets_response.qtpl:1
-import (
-	"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
-	"github.com/VictoriaMetrics/VictoriaMetrics/lib/promrelabel"
-	"time"
-)
-
-//line lib/promscrape/targets_response.qtpl:9
-import (
-	qtio422016 "io"
-
-	qt422016 "github.com/valyala/quicktemplate"
-)
-
-//line lib/promscrape/targets_response.qtpl:9
-var (
-	_ = qtio422016.Copy
-	_ = qt422016.AcquireByteBuffer
-)
-
-//line lib/promscrape/targets_response.qtpl:9
-func StreamTargetsResponsePlain(qw422016 *qt422016.Writer, jts []jobTargetsStatuses, emptyJobs []string, showOriginLabels bool) {
-//line lib/promscrape/targets_response.qtpl:11
-	for _, js := range jts {
-//line lib/promscrape/targets_response.qtpl:11
-		qw422016.N().S(`job=`)
-//line lib/promscrape/targets_response.qtpl:12
-		qw422016.N().Q(js.job)
-//line lib/promscrape/targets_response.qtpl:12
-		qw422016.N().S(`(`)
-//line lib/promscrape/targets_response.qtpl:12
-		qw422016.N().D(js.upCount)
-//line lib/promscrape/targets_response.qtpl:12
-		qw422016.N().S(`/`)
-//line lib/promscrape/targets_response.qtpl:12
-		qw422016.N().D(js.targetsTotal)
-//line lib/promscrape/targets_response.qtpl:12
-		qw422016.N().S(` `)
-//line lib/promscrape/targets_response.qtpl:12
-		qw422016.N().S(`up)`)
-//line lib/promscrape/targets_response.qtpl:13
-		qw422016.N().S(`
-`)
-//line lib/promscrape/targets_response.qtpl:14
-		for _, ts := range js.targetsStatus {
-//line lib/promscrape/targets_response.qtpl:15
-			qw422016.N().S("\t")
-//line lib/promscrape/targets_response.qtpl:15
-			qw422016.N().S(`state=`)
-//line lib/promscrape/targets_response.qtpl:16
-			if ts.up {
-//line lib/promscrape/targets_response.qtpl:16
-				qw422016.N().S(`up`)
-//line lib/promscrape/targets_response.qtpl:16
-			} else {
-//line lib/promscrape/targets_response.qtpl:16
-				qw422016.N().S(`down`)
-//line lib/promscrape/targets_response.qtpl:16
-			}
-//line lib/promscrape/targets_response.qtpl:16
-			qw422016.N().S(`,`)
-//line lib/promscrape/targets_response.qtpl:16
-			qw422016.N().S(` `)
-//line lib/promscrape/targets_response.qtpl:16
-			qw422016.N().S(`endpoint=`)
-//line lib/promscrape/targets_response.qtpl:17
-			qw422016.N().S(ts.sw.Config.ScrapeURL)
-//line lib/promscrape/targets_response.qtpl:17
-			qw422016.N().S(`,`)
-//line lib/promscrape/targets_response.qtpl:17
-			qw422016.N().S(` `)
-//line lib/promscrape/targets_response.qtpl:17
-			qw422016.N().S(`labels=`)
-//line lib/promscrape/targets_response.qtpl:18
-			qw422016.N().S(promLabelsString(promrelabel.FinalizeLabels(nil, ts.sw.Config.Labels)))
-//line lib/promscrape/targets_response.qtpl:18
-			qw422016.N().S(`,`)
-//line lib/promscrape/targets_response.qtpl:18
-			qw422016.N().S(` `)
-//line lib/promscrape/targets_response.qtpl:19
-			if showOriginLabels {
-//line lib/promscrape/targets_response.qtpl:19
-				qw422016.N().S(`originalLabels=`)
-//line lib/promscrape/targets_response.qtpl:19
-				qw422016.N().S(promLabelsString(ts.sw.Config.OriginalLabels))
-//line lib/promscrape/targets_response.qtpl:19
-				qw422016.N().S(`,`)
-//line lib/promscrape/targets_response.qtpl:19
-				qw422016.N().S(` `)
-//line lib/promscrape/targets_response.qtpl:19
-			}
-//line lib/promscrape/targets_response.qtpl:19
-			qw422016.N().S(`scrapes_total=`)
-//line lib/promscrape/targets_response.qtpl:20
-			qw422016.N().D(ts.scrapesTotal)
-//line lib/promscrape/targets_response.qtpl:20
-			qw422016.N().S(`,`)
-//line lib/promscrape/targets_response.qtpl:20
-			qw422016.N().S(` `)
-//line lib/promscrape/targets_response.qtpl:20
-			qw422016.N().S(`scrapes_failed=`)
-//line lib/promscrape/targets_response.qtpl:21
-			qw422016.N().D(ts.scrapesFailed)
-//line lib/promscrape/targets_response.qtpl:21
-			qw422016.N().S(`,`)
-//line lib/promscrape/targets_response.qtpl:21
-			qw422016.N().S(` `)
-//line lib/promscrape/targets_response.qtpl:21
-			qw422016.N().S(`last_scrape=`)
-//line lib/promscrape/targets_response.qtpl:22
-			qw422016.N().FPrec(ts.getDurationFromLastScrape().Seconds(), 3)
-//line lib/promscrape/targets_response.qtpl:22
-			qw422016.N().S(`s ago,`)
-//line lib/promscrape/targets_response.qtpl:22
-			qw422016.N().S(` `)
-//line lib/promscrape/targets_response.qtpl:22
-			qw422016.N().S(`scrape_duration=`)
-//line lib/promscrape/targets_response.qtpl:23
-			qw422016.N().D(int(ts.scrapeDuration))
-//line lib/promscrape/targets_response.qtpl:23
-			qw422016.N().S(`ms,`)
-//line lib/promscrape/targets_response.qtpl:23
-			qw422016.N().S(` `)
-//line lib/promscrape/targets_response.qtpl:23
-			qw422016.N().S(`samples_scraped=`)
-//line lib/promscrape/targets_response.qtpl:24
-			qw422016.N().D(ts.samplesScraped)
-//line lib/promscrape/targets_response.qtpl:24
-			qw422016.N().S(`,`)
-//line lib/promscrape/targets_response.qtpl:24
-			qw422016.N().S(` `)
-//line lib/promscrape/targets_response.qtpl:24
-			qw422016.N().S(`error=`)
-//line lib/promscrape/targets_response.qtpl:25
-			if ts.err != nil {
-//line lib/promscrape/targets_response.qtpl:25
-				qw422016.N().S(ts.err.Error())
-//line lib/promscrape/targets_response.qtpl:25
-			}
-//line lib/promscrape/targets_response.qtpl:26
-			qw422016.N().S(`
-`)
-//line lib/promscrape/targets_response.qtpl:27
-		}
-//line lib/promscrape/targets_response.qtpl:28
-	}
-//line lib/promscrape/targets_response.qtpl:30
-	for _, jobName := range emptyJobs {
-//line lib/promscrape/targets_response.qtpl:30
-		qw422016.N().S(`job=`)
-//line lib/promscrape/targets_response.qtpl:31
-		qw422016.N().Q(jobName)
-//line lib/promscrape/targets_response.qtpl:31
-		qw422016.N().S(`(0/0 up)`)
-//line lib/promscrape/targets_response.qtpl:32
-		qw422016.N().S(`
-`)
-//line lib/promscrape/targets_response.qtpl:33
-	}
-//line lib/promscrape/targets_response.qtpl:35
-}
-
-//line lib/promscrape/targets_response.qtpl:35
-func WriteTargetsResponsePlain(qq422016 qtio422016.Writer, jts []jobTargetsStatuses, emptyJobs []string, showOriginLabels bool) {
-//line lib/promscrape/targets_response.qtpl:35
-	qw422016 := qt422016.AcquireWriter(qq422016)
-//line lib/promscrape/targets_response.qtpl:35
-	StreamTargetsResponsePlain(qw422016, jts, emptyJobs, showOriginLabels)
-//line lib/promscrape/targets_response.qtpl:35
-	qt422016.ReleaseWriter(qw422016)
-//line lib/promscrape/targets_response.qtpl:35
-}
-
-//line lib/promscrape/targets_response.qtpl:35
-func TargetsResponsePlain(jts []jobTargetsStatuses, emptyJobs []string, showOriginLabels bool) string {
-//line lib/promscrape/targets_response.qtpl:35
-	qb422016 := qt422016.AcquireByteBuffer()
-//line lib/promscrape/targets_response.qtpl:35
-	WriteTargetsResponsePlain(qb422016, jts, emptyJobs, showOriginLabels)
-//line lib/promscrape/targets_response.qtpl:35
-	qs422016 := string(qb422016.B)
-//line lib/promscrape/targets_response.qtpl:35
-	qt422016.ReleaseByteBuffer(qb422016)
-//line lib/promscrape/targets_response.qtpl:35
-	return qs422016
-//line lib/promscrape/targets_response.qtpl:35
-}
-
-//line lib/promscrape/targets_response.qtpl:37
-func StreamTargetsResponseHTML(qw422016 *qt422016.Writer, jts []jobTargetsStatuses, emptyJobs []string, onlyUnhealthy bool) {
-//line lib/promscrape/targets_response.qtpl:37
-	qw422016.N().S(`<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"><title>Scrape targets</title></head><body class="m-3"><h1>Scrape targets</h1><div><button type="button" class="btn`)
-//line lib/promscrape/targets_response.qtpl:49
-	qw422016.N().S(` `)
-//line lib/promscrape/targets_response.qtpl:49
-	if !onlyUnhealthy {
-//line lib/promscrape/targets_response.qtpl:49
-		qw422016.N().S(`btn-primary`)
-//line lib/promscrape/targets_response.qtpl:49
-	} else {
-//line lib/promscrape/targets_response.qtpl:49
-		qw422016.N().S(`btn-secondary`)
-//line lib/promscrape/targets_response.qtpl:49
-	}
-//line lib/promscrape/targets_response.qtpl:49
-	qw422016.N().S(`" onclick="location.href='targets'">All</button><button type="button" class="btn`)
-//line lib/promscrape/targets_response.qtpl:52
-	qw422016.N().S(` `)
-//line lib/promscrape/targets_response.qtpl:52
-	if onlyUnhealthy {
-//line lib/promscrape/targets_response.qtpl:52
-		qw422016.N().S(`btn-primary`)
-//line lib/promscrape/targets_response.qtpl:52
-	} else {
-//line lib/promscrape/targets_response.qtpl:52
-		qw422016.N().S(`btn-secondary`)
-//line lib/promscrape/targets_response.qtpl:52
-	}
-//line lib/promscrape/targets_response.qtpl:52
-	qw422016.N().S(`" onclick="location.href='targets?show_only_unhealthy=true'">Unhealthy</button></div>`)
-//line lib/promscrape/targets_response.qtpl:56
-	for i, js := range jts {
-//line lib/promscrape/targets_response.qtpl:57
-		if onlyUnhealthy && js.upCount == js.targetsTotal {
-//line lib/promscrape/targets_response.qtpl:57
-			continue
-//line lib/promscrape/targets_response.qtpl:57
-		}
-//line lib/promscrape/targets_response.qtpl:57
-		qw422016.N().S(`<div><h4>`)
-//line lib/promscrape/targets_response.qtpl:60
-		qw422016.E().S(js.job)
-//line lib/promscrape/targets_response.qtpl:60
-		qw422016.N().S(` `)
-//line lib/promscrape/targets_response.qtpl:60
-		qw422016.N().S(`(`)
-//line lib/promscrape/targets_response.qtpl:60
-		qw422016.N().D(js.upCount)
-//line lib/promscrape/targets_response.qtpl:60
-		qw422016.N().S(`/`)
-//line lib/promscrape/targets_response.qtpl:60
-		qw422016.N().D(js.targetsTotal)
-//line lib/promscrape/targets_response.qtpl:60
-		qw422016.N().S(` `)
-//line lib/promscrape/targets_response.qtpl:60
-		qw422016.N().S(`up)<button type="button" class="btn btn-primary" onclick="document.getElementById('table-`)
-//line lib/promscrape/targets_response.qtpl:61
-		qw422016.N().D(i)
-//line lib/promscrape/targets_response.qtpl:61
-		qw422016.N().S(`').style.display='none'">collapse</button><button type="button" class="btn btn-secondary" onclick="document.getElementById('table-`)
-//line lib/promscrape/targets_response.qtpl:62
-		qw422016.N().D(i)
-//line lib/promscrape/targets_response.qtpl:62
-		qw422016.N().S(`').style.display='block'">expand</button></h4><div id="table-`)
-//line lib/promscrape/targets_response.qtpl:64
-		qw422016.N().D(i)
-//line lib/promscrape/targets_response.qtpl:64
-		qw422016.N().S(`"><table class="table table-striped table-hover table-bordered table-sm"><thead><tr><th scope="col">Endpoint</th><th scope="col">State</th><th scope="col" title="scrape target labels">Labels</th><th scope="col" title="total scrapes">Scrapes</th><th scope="col" title="total scrape errors">Errors</th><th scope="col" title="the time of the last scrape">Last Scrape</th><th scope="col" title="the duration of the last scrape">Duration</th><th scope="col" title="the number of metrics scraped during the last scrape">Samples</th><th scope="col" title="error from the last scrape (if any)">Last error</th></tr></thead><tbody>`)
-//line lib/promscrape/targets_response.qtpl:80
-		for _, ts := range js.targetsStatus {
-//line lib/promscrape/targets_response.qtpl:82
-			endpoint := ts.sw.Config.ScrapeURL
-			targetID := getTargetID(ts.sw)
-			lastScrapeTime := ts.getDurationFromLastScrape()
-
-//line lib/promscrape/targets_response.qtpl:86
-			if onlyUnhealthy && ts.up {
-//line lib/promscrape/targets_response.qtpl:86
-				continue
-//line lib/promscrape/targets_response.qtpl:86
-			}
-//line lib/promscrape/targets_response.qtpl:86
-			qw422016.N().S(`<tr`)
-//line lib/promscrape/targets_response.qtpl:87
-			if !ts.up {
-//line lib/promscrape/targets_response.qtpl:87
-				qw422016.N().S(` `)
-//line lib/promscrape/targets_response.qtpl:87
-				qw422016.N().S(`class="alert alert-danger" role="alert"`)
-//line lib/promscrape/targets_response.qtpl:87
-			}
-//line lib/promscrape/targets_response.qtpl:87
-			qw422016.N().S(`><td><a href="`)
-//line lib/promscrape/targets_response.qtpl:88
-			qw422016.E().S(endpoint)
-//line lib/promscrape/targets_response.qtpl:88
-			qw422016.N().S(`" target="_blank">`)
-//line lib/promscrape/targets_response.qtpl:88
-			qw422016.E().S(endpoint)
-//line lib/promscrape/targets_response.qtpl:88
-			qw422016.N().S(`</a> (<a href="target_response?id=`)
-//line lib/promscrape/targets_response.qtpl:89
-			qw422016.E().S(targetID)
-//line lib/promscrape/targets_response.qtpl:89
-			qw422016.N().S(`" target="_blank" title="click to fetch target response on behalf of the scraper">response</a>)</td><td>`)
-//line lib/promscrape/targets_response.qtpl:91
-			if ts.up {
-//line lib/promscrape/targets_response.qtpl:91
-				qw422016.N().S(`UP`)
-//line lib/promscrape/targets_response.qtpl:91
-			} else {
-//line lib/promscrape/targets_response.qtpl:91
-				qw422016.N().S(`DOWN`)
-//line lib/promscrape/targets_response.qtpl:91
-			}
-//line lib/promscrape/targets_response.qtpl:91
-			qw422016.N().S(`</td><td><div title="click to show original labels" onclick="document.getElementById('original_labels_`)
-//line lib/promscrape/targets_response.qtpl:93
-			qw422016.E().S(targetID)
-//line lib/promscrape/targets_response.qtpl:93
-			qw422016.N().S(`').style.display='block'">`)
-//line lib/promscrape/targets_response.qtpl:94
-			streamformatLabel(qw422016, promrelabel.FinalizeLabels(nil, ts.sw.Config.Labels))
-//line lib/promscrape/targets_response.qtpl:94
-			qw422016.N().S(`</div><div style="display:none" id="original_labels_`)
-//line lib/promscrape/targets_response.qtpl:96
-			qw422016.E().S(targetID)
-//line lib/promscrape/targets_response.qtpl:96
-			qw422016.N().S(`">`)
-//line lib/promscrape/targets_response.qtpl:97
-			streamformatLabel(qw422016, ts.sw.Config.OriginalLabels)
-//line lib/promscrape/targets_response.qtpl:97
-			qw422016.N().S(`</div></td><td>`)
-//line lib/promscrape/targets_response.qtpl:100
-			qw422016.N().D(ts.scrapesTotal)
-//line lib/promscrape/targets_response.qtpl:100
-			qw422016.N().S(`</td><td>`)
-//line lib/promscrape/targets_response.qtpl:101
-			qw422016.N().D(ts.scrapesFailed)
-//line lib/promscrape/targets_response.qtpl:101
-			qw422016.N().S(`</td><td>`)
-//line lib/promscrape/targets_response.qtpl:103
-			if lastScrapeTime < 365*24*time.Hour {
-//line lib/promscrape/targets_response.qtpl:104
-				qw422016.N().FPrec(lastScrapeTime.Seconds(), 3)
-//line lib/promscrape/targets_response.qtpl:104
-				qw422016.N().S(`s ago`)
-//line lib/promscrape/targets_response.qtpl:105
-			} else {
-//line lib/promscrape/targets_response.qtpl:105
-				qw422016.N().S(`none`)
-//line lib/promscrape/targets_response.qtpl:107
-			}
-//line lib/promscrape/targets_response.qtpl:107
-			qw422016.N().S(`<td>`)
-//line lib/promscrape/targets_response.qtpl:108
-			qw422016.N().D(int(ts.scrapeDuration))
-//line lib/promscrape/targets_response.qtpl:108
-			qw422016.N().S(`ms</td><td>`)
-//line lib/promscrape/targets_response.qtpl:109
-			qw422016.N().D(ts.samplesScraped)
-//line lib/promscrape/targets_response.qtpl:109
-			qw422016.N().S(`</td><td>`)
-//line lib/promscrape/targets_response.qtpl:110
-			if ts.err != nil {
-//line lib/promscrape/targets_response.qtpl:110
-				qw422016.E().S(ts.err.Error())
-//line lib/promscrape/targets_response.qtpl:110
-			}
-//line lib/promscrape/targets_response.qtpl:110
-			qw422016.N().S(`</td></tr>`)
-//line lib/promscrape/targets_response.qtpl:112
-		}
-//line lib/promscrape/targets_response.qtpl:112
-		qw422016.N().S(`</tbody></table></div></div>`)
-//line lib/promscrape/targets_response.qtpl:117
-	}
-//line lib/promscrape/targets_response.qtpl:119
-	for _, jobName := range emptyJobs {
-//line lib/promscrape/targets_response.qtpl:119
-		qw422016.N().S(`<div><h4><a>`)
-//line lib/promscrape/targets_response.qtpl:122
-		qw422016.E().S(jobName)
-//line lib/promscrape/targets_response.qtpl:122
-		qw422016.N().S(`(0/0 up)</a></h4><table class="table table-striped table-hover table-bordered table-sm"><thead><tr><th scope="col">Endpoint</th><th scope="col">State</th><th scope="col">Labels</th><th scope="col">Last Scrape</th><th scope="col">Scrape Duration</th><th scope="col">Samples Scraped</th><th scope="col">Error</th></tr></thead></table></div>`)
-//line lib/promscrape/targets_response.qtpl:138
-	}
-//line lib/promscrape/targets_response.qtpl:138
-	qw422016.N().S(`</body></html>`)
-//line lib/promscrape/targets_response.qtpl:141
-}
-
-//line lib/promscrape/targets_response.qtpl:141
-func WriteTargetsResponseHTML(qq422016 qtio422016.Writer, jts []jobTargetsStatuses, emptyJobs []string, onlyUnhealthy bool) {
-//line lib/promscrape/targets_response.qtpl:141
-	qw422016 := qt422016.AcquireWriter(qq422016)
-//line lib/promscrape/targets_response.qtpl:141
-	StreamTargetsResponseHTML(qw422016, jts, emptyJobs, onlyUnhealthy)
-//line lib/promscrape/targets_response.qtpl:141
-	qt422016.ReleaseWriter(qw422016)
-//line lib/promscrape/targets_response.qtpl:141
-}
-
-//line lib/promscrape/targets_response.qtpl:141
-func TargetsResponseHTML(jts []jobTargetsStatuses, emptyJobs []string, onlyUnhealthy bool) string {
-//line lib/promscrape/targets_response.qtpl:141
-	qb422016 := qt422016.AcquireByteBuffer()
-//line lib/promscrape/targets_response.qtpl:141
-	WriteTargetsResponseHTML(qb422016, jts, emptyJobs, onlyUnhealthy)
-//line lib/promscrape/targets_response.qtpl:141
-	qs422016 := string(qb422016.B)
-//line lib/promscrape/targets_response.qtpl:141
-	qt422016.ReleaseByteBuffer(qb422016)
-//line lib/promscrape/targets_response.qtpl:141
-	return qs422016
-//line lib/promscrape/targets_response.qtpl:141
-}
-
-//line lib/promscrape/targets_response.qtpl:143
-func streamformatLabel(qw422016 *qt422016.Writer, labels []prompbmarshal.Label) {
-//line lib/promscrape/targets_response.qtpl:143
-	qw422016.N().S(`{`)
-//line lib/promscrape/targets_response.qtpl:145
-	for i, label := range labels {
-//line lib/promscrape/targets_response.qtpl:146
-		qw422016.E().S(label.Name)
-//line lib/promscrape/targets_response.qtpl:146
-		qw422016.N().S(`=`)
-//line lib/promscrape/targets_response.qtpl:146
-		qw422016.E().Q(label.Value)
-//line lib/promscrape/targets_response.qtpl:147
-		if i+1 < len(labels) {
-//line lib/promscrape/targets_response.qtpl:147
-			qw422016.N().S(`,`)
-//line lib/promscrape/targets_response.qtpl:147
-			qw422016.N().S(` `)
-//line lib/promscrape/targets_response.qtpl:147
-		}
-//line lib/promscrape/targets_response.qtpl:148
-	}
-//line lib/promscrape/targets_response.qtpl:148
-	qw422016.N().S(`}`)
-//line lib/promscrape/targets_response.qtpl:150
-}
-
-//line lib/promscrape/targets_response.qtpl:150
-func writeformatLabel(qq422016 qtio422016.Writer, labels []prompbmarshal.Label) {
-//line lib/promscrape/targets_response.qtpl:150
-	qw422016 := qt422016.AcquireWriter(qq422016)
-//line lib/promscrape/targets_response.qtpl:150
-	streamformatLabel(qw422016, labels)
-//line lib/promscrape/targets_response.qtpl:150
-	qt422016.ReleaseWriter(qw422016)
-//line lib/promscrape/targets_response.qtpl:150
-}
-
-//line lib/promscrape/targets_response.qtpl:150
-func formatLabel(labels []prompbmarshal.Label) string {
-//line lib/promscrape/targets_response.qtpl:150
-	qb422016 := qt422016.AcquireByteBuffer()
-//line lib/promscrape/targets_response.qtpl:150
-	writeformatLabel(qb422016, labels)
-//line lib/promscrape/targets_response.qtpl:150
-	qs422016 := string(qb422016.B)
-//line lib/promscrape/targets_response.qtpl:150
-	qt422016.ReleaseByteBuffer(qb422016)
-//line lib/promscrape/targets_response.qtpl:150
-	return qs422016
-//line lib/promscrape/targets_response.qtpl:150
-}
diff --git a/lib/promscrape/targets_response.qtpl b/lib/promscrape/targetstatus.qtpl
similarity index 91%
rename from lib/promscrape/targets_response.qtpl
rename to lib/promscrape/targetstatus.qtpl
index 1d50802ee..292a34551 100644
--- a/lib/promscrape/targets_response.qtpl
+++ b/lib/promscrape/targetstatus.qtpl
@@ -42,16 +42,36 @@ job={%q= jobName %} (0/0 up)
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
     <title>Scrape targets</title>
+    <script>
+function collapse_all() {
+  for (var i = 0; i < {%d len(jts) %}; i++) {
+    var id = "table-" + i;
+    document.getElementById(id).style.display = 'none';
+  }
+}
+function expand_all() {
+  for (var i = 0; i < {%d len(jts) %}; i++) {
+    var id = "table-" + i;
+    document.getElementById(id).style.display = 'block';
+  }
+}
+    </script>
 </head>
 <body class="m-3">
   <h1>Scrape targets</h1>
-  <div>
+  <div style="padding: 3px">
     <button type="button" class="btn{% space %}{% if !onlyUnhealthy %}btn-primary{% else %}btn-secondary{% endif %}" onclick="location.href='targets'">
         All
     </button>
     <button type="button" class="btn{% space %}{% if onlyUnhealthy %}btn-primary{% else %}btn-secondary{% endif %}" onclick="location.href='targets?show_only_unhealthy=true'">
         Unhealthy
     </button>
+    <button type="button" class="btn btn-primary" onclick="collapse_all()">
+        Collapse all
+    </button>
+    <button type="button" class="btn btn-secondary" onclick="expand_all()">
+        Expand all
+    </button>
   </div>
   {% for i, js := range jts %}
     {% if onlyUnhealthy && js.upCount == js.targetsTotal %}{% continue %}{% endif %}
diff --git a/lib/promscrape/targetstatus.qtpl.go b/lib/promscrape/targetstatus.qtpl.go
new file mode 100644
index 000000000..534e2d4a7
--- /dev/null
+++ b/lib/promscrape/targetstatus.qtpl.go
@@ -0,0 +1,474 @@
+// Code generated by qtc from "targetstatus.qtpl". DO NOT EDIT.
+// See https://github.com/valyala/quicktemplate for details.
+
+//line lib/promscrape/targetstatus.qtpl:1
+package promscrape
+
+//line lib/promscrape/targetstatus.qtpl:1
+import (
+	"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
+	"github.com/VictoriaMetrics/VictoriaMetrics/lib/promrelabel"
+	"time"
+)
+
+//line lib/promscrape/targetstatus.qtpl:9
+import (
+	qtio422016 "io"
+
+	qt422016 "github.com/valyala/quicktemplate"
+)
+
+//line lib/promscrape/targetstatus.qtpl:9
+var (
+	_ = qtio422016.Copy
+	_ = qt422016.AcquireByteBuffer
+)
+
+//line lib/promscrape/targetstatus.qtpl:9
+func StreamTargetsResponsePlain(qw422016 *qt422016.Writer, jts []jobTargetsStatuses, emptyJobs []string, showOriginLabels bool) {
+//line lib/promscrape/targetstatus.qtpl:11
+	for _, js := range jts {
+//line lib/promscrape/targetstatus.qtpl:11
+		qw422016.N().S(`job=`)
+//line lib/promscrape/targetstatus.qtpl:12
+		qw422016.N().Q(js.job)
+//line lib/promscrape/targetstatus.qtpl:12
+		qw422016.N().S(`(`)
+//line lib/promscrape/targetstatus.qtpl:12
+		qw422016.N().D(js.upCount)
+//line lib/promscrape/targetstatus.qtpl:12
+		qw422016.N().S(`/`)
+//line lib/promscrape/targetstatus.qtpl:12
+		qw422016.N().D(js.targetsTotal)
+//line lib/promscrape/targetstatus.qtpl:12
+		qw422016.N().S(` `)
+//line lib/promscrape/targetstatus.qtpl:12
+		qw422016.N().S(`up)`)
+//line lib/promscrape/targetstatus.qtpl:13
+		qw422016.N().S(`
+`)
+//line lib/promscrape/targetstatus.qtpl:14
+		for _, ts := range js.targetsStatus {
+//line lib/promscrape/targetstatus.qtpl:15
+			qw422016.N().S("\t")
+//line lib/promscrape/targetstatus.qtpl:15
+			qw422016.N().S(`state=`)
+//line lib/promscrape/targetstatus.qtpl:16
+			if ts.up {
+//line lib/promscrape/targetstatus.qtpl:16
+				qw422016.N().S(`up`)
+//line lib/promscrape/targetstatus.qtpl:16
+			} else {
+//line lib/promscrape/targetstatus.qtpl:16
+				qw422016.N().S(`down`)
+//line lib/promscrape/targetstatus.qtpl:16
+			}
+//line lib/promscrape/targetstatus.qtpl:16
+			qw422016.N().S(`,`)
+//line lib/promscrape/targetstatus.qtpl:16
+			qw422016.N().S(` `)
+//line lib/promscrape/targetstatus.qtpl:16
+			qw422016.N().S(`endpoint=`)
+//line lib/promscrape/targetstatus.qtpl:17
+			qw422016.N().S(ts.sw.Config.ScrapeURL)
+//line lib/promscrape/targetstatus.qtpl:17
+			qw422016.N().S(`,`)
+//line lib/promscrape/targetstatus.qtpl:17
+			qw422016.N().S(` `)
+//line lib/promscrape/targetstatus.qtpl:17
+			qw422016.N().S(`labels=`)
+//line lib/promscrape/targetstatus.qtpl:18
+			qw422016.N().S(promLabelsString(promrelabel.FinalizeLabels(nil, ts.sw.Config.Labels)))
+//line lib/promscrape/targetstatus.qtpl:18
+			qw422016.N().S(`,`)
+//line lib/promscrape/targetstatus.qtpl:18
+			qw422016.N().S(` `)
+//line lib/promscrape/targetstatus.qtpl:19
+			if showOriginLabels {
+//line lib/promscrape/targetstatus.qtpl:19
+				qw422016.N().S(`originalLabels=`)
+//line lib/promscrape/targetstatus.qtpl:19
+				qw422016.N().S(promLabelsString(ts.sw.Config.OriginalLabels))
+//line lib/promscrape/targetstatus.qtpl:19
+				qw422016.N().S(`,`)
+//line lib/promscrape/targetstatus.qtpl:19
+				qw422016.N().S(` `)
+//line lib/promscrape/targetstatus.qtpl:19
+			}
+//line lib/promscrape/targetstatus.qtpl:19
+			qw422016.N().S(`scrapes_total=`)
+//line lib/promscrape/targetstatus.qtpl:20
+			qw422016.N().D(ts.scrapesTotal)
+//line lib/promscrape/targetstatus.qtpl:20
+			qw422016.N().S(`,`)
+//line lib/promscrape/targetstatus.qtpl:20
+			qw422016.N().S(` `)
+//line lib/promscrape/targetstatus.qtpl:20
+			qw422016.N().S(`scrapes_failed=`)
+//line lib/promscrape/targetstatus.qtpl:21
+			qw422016.N().D(ts.scrapesFailed)
+//line lib/promscrape/targetstatus.qtpl:21
+			qw422016.N().S(`,`)
+//line lib/promscrape/targetstatus.qtpl:21
+			qw422016.N().S(` `)
+//line lib/promscrape/targetstatus.qtpl:21
+			qw422016.N().S(`last_scrape=`)
+//line lib/promscrape/targetstatus.qtpl:22
+			qw422016.N().FPrec(ts.getDurationFromLastScrape().Seconds(), 3)
+//line lib/promscrape/targetstatus.qtpl:22
+			qw422016.N().S(`s ago,`)
+//line lib/promscrape/targetstatus.qtpl:22
+			qw422016.N().S(` `)
+//line lib/promscrape/targetstatus.qtpl:22
+			qw422016.N().S(`scrape_duration=`)
+//line lib/promscrape/targetstatus.qtpl:23
+			qw422016.N().D(int(ts.scrapeDuration))
+//line lib/promscrape/targetstatus.qtpl:23
+			qw422016.N().S(`ms,`)
+//line lib/promscrape/targetstatus.qtpl:23
+			qw422016.N().S(` `)
+//line lib/promscrape/targetstatus.qtpl:23
+			qw422016.N().S(`samples_scraped=`)
+//line lib/promscrape/targetstatus.qtpl:24
+			qw422016.N().D(ts.samplesScraped)
+//line lib/promscrape/targetstatus.qtpl:24
+			qw422016.N().S(`,`)
+//line lib/promscrape/targetstatus.qtpl:24
+			qw422016.N().S(` `)
+//line lib/promscrape/targetstatus.qtpl:24
+			qw422016.N().S(`error=`)
+//line lib/promscrape/targetstatus.qtpl:25
+			if ts.err != nil {
+//line lib/promscrape/targetstatus.qtpl:25
+				qw422016.N().S(ts.err.Error())
+//line lib/promscrape/targetstatus.qtpl:25
+			}
+//line lib/promscrape/targetstatus.qtpl:26
+			qw422016.N().S(`
+`)
+//line lib/promscrape/targetstatus.qtpl:27
+		}
+//line lib/promscrape/targetstatus.qtpl:28
+	}
+//line lib/promscrape/targetstatus.qtpl:30
+	for _, jobName := range emptyJobs {
+//line lib/promscrape/targetstatus.qtpl:30
+		qw422016.N().S(`job=`)
+//line lib/promscrape/targetstatus.qtpl:31
+		qw422016.N().Q(jobName)
+//line lib/promscrape/targetstatus.qtpl:31
+		qw422016.N().S(`(0/0 up)`)
+//line lib/promscrape/targetstatus.qtpl:32
+		qw422016.N().S(`
+`)
+//line lib/promscrape/targetstatus.qtpl:33
+	}
+//line lib/promscrape/targetstatus.qtpl:35
+}
+
+//line lib/promscrape/targetstatus.qtpl:35
+func WriteTargetsResponsePlain(qq422016 qtio422016.Writer, jts []jobTargetsStatuses, emptyJobs []string, showOriginLabels bool) {
+//line lib/promscrape/targetstatus.qtpl:35
+	qw422016 := qt422016.AcquireWriter(qq422016)
+//line lib/promscrape/targetstatus.qtpl:35
+	StreamTargetsResponsePlain(qw422016, jts, emptyJobs, showOriginLabels)
+//line lib/promscrape/targetstatus.qtpl:35
+	qt422016.ReleaseWriter(qw422016)
+//line lib/promscrape/targetstatus.qtpl:35
+}
+
+//line lib/promscrape/targetstatus.qtpl:35
+func TargetsResponsePlain(jts []jobTargetsStatuses, emptyJobs []string, showOriginLabels bool) string {
+//line lib/promscrape/targetstatus.qtpl:35
+	qb422016 := qt422016.AcquireByteBuffer()
+//line lib/promscrape/targetstatus.qtpl:35
+	WriteTargetsResponsePlain(qb422016, jts, emptyJobs, showOriginLabels)
+//line lib/promscrape/targetstatus.qtpl:35
+	qs422016 := string(qb422016.B)
+//line lib/promscrape/targetstatus.qtpl:35
+	qt422016.ReleaseByteBuffer(qb422016)
+//line lib/promscrape/targetstatus.qtpl:35
+	return qs422016
+//line lib/promscrape/targetstatus.qtpl:35
+}
+
+//line lib/promscrape/targetstatus.qtpl:37
+func StreamTargetsResponseHTML(qw422016 *qt422016.Writer, jts []jobTargetsStatuses, emptyJobs []string, onlyUnhealthy bool) {
+//line lib/promscrape/targetstatus.qtpl:37
+	qw422016.N().S(`<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"><title>Scrape targets</title><script>function collapse_all() {for (var i = 0; i <`)
+//line lib/promscrape/targetstatus.qtpl:47
+	qw422016.N().D(len(jts))
+//line lib/promscrape/targetstatus.qtpl:47
+	qw422016.N().S(`; i++) {var id = "table-" + i;document.getElementById(id).style.display = 'none';}}function expand_all() {for (var i = 0; i <`)
+//line lib/promscrape/targetstatus.qtpl:53
+	qw422016.N().D(len(jts))
+//line lib/promscrape/targetstatus.qtpl:53
+	qw422016.N().S(`; i++) {var id = "table-" + i;document.getElementById(id).style.display = 'block';}}</script></head><body class="m-3"><h1>Scrape targets</h1><div style="padding: 3px"><button type="button" class="btn`)
+//line lib/promscrape/targetstatus.qtpl:63
+	qw422016.N().S(` `)
+//line lib/promscrape/targetstatus.qtpl:63
+	if !onlyUnhealthy {
+//line lib/promscrape/targetstatus.qtpl:63
+		qw422016.N().S(`btn-primary`)
+//line lib/promscrape/targetstatus.qtpl:63
+	} else {
+//line lib/promscrape/targetstatus.qtpl:63
+		qw422016.N().S(`btn-secondary`)
+//line lib/promscrape/targetstatus.qtpl:63
+	}
+//line lib/promscrape/targetstatus.qtpl:63
+	qw422016.N().S(`" onclick="location.href='targets'">All</button><button type="button" class="btn`)
+//line lib/promscrape/targetstatus.qtpl:66
+	qw422016.N().S(` `)
+//line lib/promscrape/targetstatus.qtpl:66
+	if onlyUnhealthy {
+//line lib/promscrape/targetstatus.qtpl:66
+		qw422016.N().S(`btn-primary`)
+//line lib/promscrape/targetstatus.qtpl:66
+	} else {
+//line lib/promscrape/targetstatus.qtpl:66
+		qw422016.N().S(`btn-secondary`)
+//line lib/promscrape/targetstatus.qtpl:66
+	}
+//line lib/promscrape/targetstatus.qtpl:66
+	qw422016.N().S(`" onclick="location.href='targets?show_only_unhealthy=true'">Unhealthy</button><button type="button" class="btn btn-primary" onclick="collapse_all()">Collapse all</button><button type="button" class="btn btn-secondary" onclick="expand_all()">Expand all</button></div>`)
+//line lib/promscrape/targetstatus.qtpl:76
+	for i, js := range jts {
+//line lib/promscrape/targetstatus.qtpl:77
+		if onlyUnhealthy && js.upCount == js.targetsTotal {
+//line lib/promscrape/targetstatus.qtpl:77
+			continue
+//line lib/promscrape/targetstatus.qtpl:77
+		}
+//line lib/promscrape/targetstatus.qtpl:77
+		qw422016.N().S(`<div><h4>`)
+//line lib/promscrape/targetstatus.qtpl:80
+		qw422016.E().S(js.job)
+//line lib/promscrape/targetstatus.qtpl:80
+		qw422016.N().S(` `)
+//line lib/promscrape/targetstatus.qtpl:80
+		qw422016.N().S(`(`)
+//line lib/promscrape/targetstatus.qtpl:80
+		qw422016.N().D(js.upCount)
+//line lib/promscrape/targetstatus.qtpl:80
+		qw422016.N().S(`/`)
+//line lib/promscrape/targetstatus.qtpl:80
+		qw422016.N().D(js.targetsTotal)
+//line lib/promscrape/targetstatus.qtpl:80
+		qw422016.N().S(` `)
+//line lib/promscrape/targetstatus.qtpl:80
+		qw422016.N().S(`up)<button type="button" class="btn btn-primary" onclick="document.getElementById('table-`)
+//line lib/promscrape/targetstatus.qtpl:81
+		qw422016.N().D(i)
+//line lib/promscrape/targetstatus.qtpl:81
+		qw422016.N().S(`').style.display='none'">collapse</button><button type="button" class="btn btn-secondary" onclick="document.getElementById('table-`)
+//line lib/promscrape/targetstatus.qtpl:82
+		qw422016.N().D(i)
+//line lib/promscrape/targetstatus.qtpl:82
+		qw422016.N().S(`').style.display='block'">expand</button></h4><div id="table-`)
+//line lib/promscrape/targetstatus.qtpl:84
+		qw422016.N().D(i)
+//line lib/promscrape/targetstatus.qtpl:84
+		qw422016.N().S(`"><table class="table table-striped table-hover table-bordered table-sm"><thead><tr><th scope="col">Endpoint</th><th scope="col">State</th><th scope="col" title="scrape target labels">Labels</th><th scope="col" title="total scrapes">Scrapes</th><th scope="col" title="total scrape errors">Errors</th><th scope="col" title="the time of the last scrape">Last Scrape</th><th scope="col" title="the duration of the last scrape">Duration</th><th scope="col" title="the number of metrics scraped during the last scrape">Samples</th><th scope="col" title="error from the last scrape (if any)">Last error</th></tr></thead><tbody>`)
+//line lib/promscrape/targetstatus.qtpl:100
+		for _, ts := range js.targetsStatus {
+//line lib/promscrape/targetstatus.qtpl:102
+			endpoint := ts.sw.Config.ScrapeURL
+			targetID := getTargetID(ts.sw)
+			lastScrapeTime := ts.getDurationFromLastScrape()
+
+//line lib/promscrape/targetstatus.qtpl:106
+			if onlyUnhealthy && ts.up {
+//line lib/promscrape/targetstatus.qtpl:106
+				continue
+//line lib/promscrape/targetstatus.qtpl:106
+			}
+//line lib/promscrape/targetstatus.qtpl:106
+			qw422016.N().S(`<tr`)
+//line lib/promscrape/targetstatus.qtpl:107
+			if !ts.up {
+//line lib/promscrape/targetstatus.qtpl:107
+				qw422016.N().S(` `)
+//line lib/promscrape/targetstatus.qtpl:107
+				qw422016.N().S(`class="alert alert-danger" role="alert"`)
+//line lib/promscrape/targetstatus.qtpl:107
+			}
+//line lib/promscrape/targetstatus.qtpl:107
+			qw422016.N().S(`><td><a href="`)
+//line lib/promscrape/targetstatus.qtpl:108
+			qw422016.E().S(endpoint)
+//line lib/promscrape/targetstatus.qtpl:108
+			qw422016.N().S(`" target="_blank">`)
+//line lib/promscrape/targetstatus.qtpl:108
+			qw422016.E().S(endpoint)
+//line lib/promscrape/targetstatus.qtpl:108
+			qw422016.N().S(`</a> (<a href="target_response?id=`)
+//line lib/promscrape/targetstatus.qtpl:109
+			qw422016.E().S(targetID)
+//line lib/promscrape/targetstatus.qtpl:109
+			qw422016.N().S(`" target="_blank" title="click to fetch target response on behalf of the scraper">response</a>)</td><td>`)
+//line lib/promscrape/targetstatus.qtpl:111
+			if ts.up {
+//line lib/promscrape/targetstatus.qtpl:111
+				qw422016.N().S(`UP`)
+//line lib/promscrape/targetstatus.qtpl:111
+			} else {
+//line lib/promscrape/targetstatus.qtpl:111
+				qw422016.N().S(`DOWN`)
+//line lib/promscrape/targetstatus.qtpl:111
+			}
+//line lib/promscrape/targetstatus.qtpl:111
+			qw422016.N().S(`</td><td><div title="click to show original labels" onclick="document.getElementById('original_labels_`)
+//line lib/promscrape/targetstatus.qtpl:113
+			qw422016.E().S(targetID)
+//line lib/promscrape/targetstatus.qtpl:113
+			qw422016.N().S(`').style.display='block'">`)
+//line lib/promscrape/targetstatus.qtpl:114
+			streamformatLabel(qw422016, promrelabel.FinalizeLabels(nil, ts.sw.Config.Labels))
+//line lib/promscrape/targetstatus.qtpl:114
+			qw422016.N().S(`</div><div style="display:none" id="original_labels_`)
+//line lib/promscrape/targetstatus.qtpl:116
+			qw422016.E().S(targetID)
+//line lib/promscrape/targetstatus.qtpl:116
+			qw422016.N().S(`">`)
+//line lib/promscrape/targetstatus.qtpl:117
+			streamformatLabel(qw422016, ts.sw.Config.OriginalLabels)
+//line lib/promscrape/targetstatus.qtpl:117
+			qw422016.N().S(`</div></td><td>`)
+//line lib/promscrape/targetstatus.qtpl:120
+			qw422016.N().D(ts.scrapesTotal)
+//line lib/promscrape/targetstatus.qtpl:120
+			qw422016.N().S(`</td><td>`)
+//line lib/promscrape/targetstatus.qtpl:121
+			qw422016.N().D(ts.scrapesFailed)
+//line lib/promscrape/targetstatus.qtpl:121
+			qw422016.N().S(`</td><td>`)
+//line lib/promscrape/targetstatus.qtpl:123
+			if lastScrapeTime < 365*24*time.Hour {
+//line lib/promscrape/targetstatus.qtpl:124
+				qw422016.N().FPrec(lastScrapeTime.Seconds(), 3)
+//line lib/promscrape/targetstatus.qtpl:124
+				qw422016.N().S(`s ago`)
+//line lib/promscrape/targetstatus.qtpl:125
+			} else {
+//line lib/promscrape/targetstatus.qtpl:125
+				qw422016.N().S(`none`)
+//line lib/promscrape/targetstatus.qtpl:127
+			}
+//line lib/promscrape/targetstatus.qtpl:127
+			qw422016.N().S(`<td>`)
+//line lib/promscrape/targetstatus.qtpl:128
+			qw422016.N().D(int(ts.scrapeDuration))
+//line lib/promscrape/targetstatus.qtpl:128
+			qw422016.N().S(`ms</td><td>`)
+//line lib/promscrape/targetstatus.qtpl:129
+			qw422016.N().D(ts.samplesScraped)
+//line lib/promscrape/targetstatus.qtpl:129
+			qw422016.N().S(`</td><td>`)
+//line lib/promscrape/targetstatus.qtpl:130
+			if ts.err != nil {
+//line lib/promscrape/targetstatus.qtpl:130
+				qw422016.E().S(ts.err.Error())
+//line lib/promscrape/targetstatus.qtpl:130
+			}
+//line lib/promscrape/targetstatus.qtpl:130
+			qw422016.N().S(`</td></tr>`)
+//line lib/promscrape/targetstatus.qtpl:132
+		}
+//line lib/promscrape/targetstatus.qtpl:132
+		qw422016.N().S(`</tbody></table></div></div>`)
+//line lib/promscrape/targetstatus.qtpl:137
+	}
+//line lib/promscrape/targetstatus.qtpl:139
+	for _, jobName := range emptyJobs {
+//line lib/promscrape/targetstatus.qtpl:139
+		qw422016.N().S(`<div><h4><a>`)
+//line lib/promscrape/targetstatus.qtpl:142
+		qw422016.E().S(jobName)
+//line lib/promscrape/targetstatus.qtpl:142
+		qw422016.N().S(`(0/0 up)</a></h4><table class="table table-striped table-hover table-bordered table-sm"><thead><tr><th scope="col">Endpoint</th><th scope="col">State</th><th scope="col">Labels</th><th scope="col">Last Scrape</th><th scope="col">Scrape Duration</th><th scope="col">Samples Scraped</th><th scope="col">Error</th></tr></thead></table></div>`)
+//line lib/promscrape/targetstatus.qtpl:158
+	}
+//line lib/promscrape/targetstatus.qtpl:158
+	qw422016.N().S(`</body></html>`)
+//line lib/promscrape/targetstatus.qtpl:161
+}
+
+//line lib/promscrape/targetstatus.qtpl:161
+func WriteTargetsResponseHTML(qq422016 qtio422016.Writer, jts []jobTargetsStatuses, emptyJobs []string, onlyUnhealthy bool) {
+//line lib/promscrape/targetstatus.qtpl:161
+	qw422016 := qt422016.AcquireWriter(qq422016)
+//line lib/promscrape/targetstatus.qtpl:161
+	StreamTargetsResponseHTML(qw422016, jts, emptyJobs, onlyUnhealthy)
+//line lib/promscrape/targetstatus.qtpl:161
+	qt422016.ReleaseWriter(qw422016)
+//line lib/promscrape/targetstatus.qtpl:161
+}
+
+//line lib/promscrape/targetstatus.qtpl:161
+func TargetsResponseHTML(jts []jobTargetsStatuses, emptyJobs []string, onlyUnhealthy bool) string {
+//line lib/promscrape/targetstatus.qtpl:161
+	qb422016 := qt422016.AcquireByteBuffer()
+//line lib/promscrape/targetstatus.qtpl:161
+	WriteTargetsResponseHTML(qb422016, jts, emptyJobs, onlyUnhealthy)
+//line lib/promscrape/targetstatus.qtpl:161
+	qs422016 := string(qb422016.B)
+//line lib/promscrape/targetstatus.qtpl:161
+	qt422016.ReleaseByteBuffer(qb422016)
+//line lib/promscrape/targetstatus.qtpl:161
+	return qs422016
+//line lib/promscrape/targetstatus.qtpl:161
+}
+
+//line lib/promscrape/targetstatus.qtpl:163
+func streamformatLabel(qw422016 *qt422016.Writer, labels []prompbmarshal.Label) {
+//line lib/promscrape/targetstatus.qtpl:163
+	qw422016.N().S(`{`)
+//line lib/promscrape/targetstatus.qtpl:165
+	for i, label := range labels {
+//line lib/promscrape/targetstatus.qtpl:166
+		qw422016.E().S(label.Name)
+//line lib/promscrape/targetstatus.qtpl:166
+		qw422016.N().S(`=`)
+//line lib/promscrape/targetstatus.qtpl:166
+		qw422016.E().Q(label.Value)
+//line lib/promscrape/targetstatus.qtpl:167
+		if i+1 < len(labels) {
+//line lib/promscrape/targetstatus.qtpl:167
+			qw422016.N().S(`,`)
+//line lib/promscrape/targetstatus.qtpl:167
+			qw422016.N().S(` `)
+//line lib/promscrape/targetstatus.qtpl:167
+		}
+//line lib/promscrape/targetstatus.qtpl:168
+	}
+//line lib/promscrape/targetstatus.qtpl:168
+	qw422016.N().S(`}`)
+//line lib/promscrape/targetstatus.qtpl:170
+}
+
+//line lib/promscrape/targetstatus.qtpl:170
+func writeformatLabel(qq422016 qtio422016.Writer, labels []prompbmarshal.Label) {
+//line lib/promscrape/targetstatus.qtpl:170
+	qw422016 := qt422016.AcquireWriter(qq422016)
+//line lib/promscrape/targetstatus.qtpl:170
+	streamformatLabel(qw422016, labels)
+//line lib/promscrape/targetstatus.qtpl:170
+	qt422016.ReleaseWriter(qw422016)
+//line lib/promscrape/targetstatus.qtpl:170
+}
+
+//line lib/promscrape/targetstatus.qtpl:170
+func formatLabel(labels []prompbmarshal.Label) string {
+//line lib/promscrape/targetstatus.qtpl:170
+	qb422016 := qt422016.AcquireByteBuffer()
+//line lib/promscrape/targetstatus.qtpl:170
+	writeformatLabel(qb422016, labels)
+//line lib/promscrape/targetstatus.qtpl:170
+	qs422016 := string(qb422016.B)
+//line lib/promscrape/targetstatus.qtpl:170
+	qt422016.ReleaseByteBuffer(qb422016)
+//line lib/promscrape/targetstatus.qtpl:170
+	return qs422016
+//line lib/promscrape/targetstatus.qtpl:170
+}
diff --git a/lib/storage/index_db.go b/lib/storage/index_db.go
index 2eaa7af65..e303db036 100644
--- a/lib/storage/index_db.go
+++ b/lib/storage/index_db.go
@@ -370,12 +370,13 @@ func (db *indexDB) putMetricNameToCache(metricID uint64, metricName []byte) {
 //
 // It returns true if new index entry was created, and false if it was skipped.
 func (db *indexDB) maybeCreateIndexes(tsid *TSID, metricNameRaw []byte) (bool, error) {
-	h := xxhash.Sum64(metricNameRaw)
-	p := float64(uint32(h)) / (1 << 32)
 	pMin := float64(fasttime.UnixTimestamp()-db.rotationTimestamp) / 3600
-	if p > pMin {
-		// Fast path: there is no need creating indexes for metricNameRaw yet.
-		return false, nil
+	if pMin < 1 {
+		p := float64(uint32(fastHashUint64(tsid.MetricID))) / (1 << 32)
+		if p > pMin {
+			// Fast path: there is no need creating indexes for metricNameRaw yet.
+			return false, nil
+		}
 	}
 	// Slow path: create indexes for (tsid, metricNameRaw) at db.
 	mn := GetMetricName()
diff --git a/lib/storage/storage.go b/lib/storage/storage.go
index 75c131ffa..c5b61f674 100644
--- a/lib/storage/storage.go
+++ b/lib/storage/storage.go
@@ -1983,7 +1983,10 @@ func (s *Storage) updatePerDateData(rows []rawRow, mrs []*MetricRow) error {
 	hmPrev := s.prevHourMetricIDs.Load().(*hourMetricIDs)
 	hmPrevDate := hmPrev.hour / 24
 	nextDayMetricIDs := &s.nextDayMetricIDs.Load().(*byDateMetricIDEntry).v
-	todayShare16bit := uint64((float64(fasttime.UnixTimestamp()%(3600*24)) / (3600 * 24)) * (1 << 16))
+	ts := fasttime.UnixTimestamp()
+	// Start pre-populating the next per-day inverted index during the last hour of the current day.
+	// pMin linearly increases from 0 to 1 during the last hour of the day.
+	pMin := (float64(ts%(3600*24)) / 3600) - 23
 	type pendingDateMetricID struct {
 		date     uint64
 		metricID uint64
@@ -2012,18 +2015,20 @@ func (s *Storage) updatePerDateData(rows []rawRow, mrs []*MetricRow) error {
 				// Fast path: the metricID is in the current hour cache.
 				// This means the metricID has been already added to per-day inverted index.
 
-				// Gradually pre-populate per-day inverted index for the next day
-				// during the current day.
+				// Gradually pre-populate per-day inverted index for the next day during the last hour of the current day.
 				// This should reduce CPU usage spike and slowdown at the beginning of the next day
 				// when entries for all the active time series must be added to the index.
 				// This should address https://github.com/VictoriaMetrics/VictoriaMetrics/issues/430 .
-				if todayShare16bit > (metricID&(1<<16-1)) && !nextDayMetricIDs.Has(metricID) {
-					pendingDateMetricIDs = append(pendingDateMetricIDs, pendingDateMetricID{
-						date:     date + 1,
-						metricID: metricID,
-						mr:       mrs[i],
-					})
-					pendingNextDayMetricIDs = append(pendingNextDayMetricIDs, metricID)
+				if pMin > 0 {
+					p := float64(uint32(fastHashUint64(metricID))) / (1 << 32)
+					if p < pMin && !nextDayMetricIDs.Has(metricID) {
+						pendingDateMetricIDs = append(pendingDateMetricIDs, pendingDateMetricID{
+							date:     date + 1,
+							metricID: metricID,
+							mr:       mrs[i],
+						})
+						pendingNextDayMetricIDs = append(pendingNextDayMetricIDs, metricID)
+					}
 				}
 				continue
 			}
@@ -2117,6 +2122,13 @@ func (s *Storage) updatePerDateData(rows []rawRow, mrs []*MetricRow) error {
 	return firstError
 }
 
+func fastHashUint64(x uint64) uint64 {
+	x ^= x >> 12 // a
+	x ^= x << 25 // b
+	x ^= x >> 27 // c
+	return x * 2685821657736338717
+}
+
 // dateMetricIDCache is fast cache for holding (date, metricID) entries.
 //
 // It should be faster than map[date]*uint64set.Set on multicore systems.


Test patch:
diff --git a/app/vmselect/promql/exec_test.go b/app/vmselect/promql/exec_test.go
index 18f6f775b..508b11d37 100644
--- a/app/vmselect/promql/exec_test.go
+++ b/app/vmselect/promql/exec_test.go
@@ -867,19 +867,37 @@ func TestExecSuccess(t *testing.T) {
 		resultExpected := []netstorage.Result{r}
 		f(q, resultExpected)
 	})
-	t.Run(`absent_over_time(scalar(multi-timeseries))`, func(t *testing.T) {
+	t.Run(`absent_over_time(non-nan)`, func(t *testing.T) {
 		t.Parallel()
 		q := `
-		absent_over_time(label_set(scalar(1 or label_set(2, "xx", "foo")), "yy", "foo"))`
+		absent_over_time(time())`
+		resultExpected := []netstorage.Result{}
+		f(q, resultExpected)
+	})
+	t.Run(`absent_over_time(nan)`, func(t *testing.T) {
+		t.Parallel()
+		q := `
+		absent_over_time((time() < 1500)[300s:])`
 		r := netstorage.Result{
 			MetricName: metricNameExpected,
-			Values:     []float64{1, 1, 1, 1, 1, 1},
+			Values:     []float64{nan, nan, nan, nan, 1, 1},
+			Timestamps: timestampsExpected,
+		}
+		resultExpected := []netstorage.Result{r}
+		f(q, resultExpected)
+	})
+	t.Run(`absent_over_time(multi-ts)`, func(t *testing.T) {
+		t.Parallel()
+		q := `
+		absent_over_time((
+			alias((time() < 1400)[200s:], "one"),
+			alias((time() > 1600)[200s:], "two"),
+		))`
+		r := netstorage.Result{
+			MetricName: metricNameExpected,
+			Values:     []float64{nan, nan, nan, 1, nan, nan},
 			Timestamps: timestampsExpected,
 		}
-		r.MetricName.Tags = []storage.Tag{{
-			Key:   []byte("yy"),
-			Value: []byte("foo"),
-		}}
 		resultExpected := []netstorage.Result{r}
 		f(q, resultExpected)
 	})