Repository: VictoriaMetrics/VictoriaMetrics
Pull request #1310 (2021-05-19 18:50:25)

vmctl: explicitly set `::tag` type for labels selector in `influx` mode
The `::tag` type is needed in cases when field and tag names are equal, which
results into unexpected results in InfluxQL. Setting the type explicitly helps
InfluxDB to understand which exact column we apply filter to.

https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1299

codecov[bot]: # [Codecov](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1310?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics) Report
> Merging [#1310](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1310?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics) (64176a2) into [master](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/commit/55ed77760b26c32c51b5f50d625b6a4d847506c9?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics) (55ed777) will **decrease** coverage by `0.00%`.
> The diff coverage is `100.00%`.

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

```diff
@@            Coverage Diff             @@
##           master    #1310      +/-   ##
==========================================
- Coverage   60.67%   60.67%   -0.01%     
==========================================
  Files         215      215              
  Lines       25755    25755              
==========================================
- Hits        15628    15626       -2     
  Misses       8687     8687              
- Partials     1440     1442       +2     
```


| [Impacted Files](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1310?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics) | Coverage Δ | |
|---|---|---|
| [app/vmctl/influx/influx.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1310/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-YXBwL3ZtY3RsL2luZmx1eC9pbmZsdXguZ28=) | `13.77% <100.00%> (ø)` | |
| [app/vmselect/promql/aggr\_incremental.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1310/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-YXBwL3Ztc2VsZWN0L3Byb21xbC9hZ2dyX2luY3JlbWVudGFsLmdv) | `83.33% <0.00%> (-3.11%)` | :arrow_down: |
| [lib/storage/index\_db.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1310/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-bGliL3N0b3JhZ2UvaW5kZXhfZGIuZ28=) | `66.06% <0.00%> (-0.16%)` | :arrow_down: |
| [lib/storage/partition.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1310/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-bGliL3N0b3JhZ2UvcGFydGl0aW9uLmdv) | `65.63% <0.00%> (+0.23%)` | :arrow_up: |
| [lib/mergeset/table.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1310/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-bGliL21lcmdlc2V0L3RhYmxlLmdv) | `69.26% <0.00%> (+0.44%)` | :arrow_up: |
| [app/vmalert/remotewrite/remotewrite.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1310/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics#diff-YXBwL3ZtYWxlcnQvcmVtb3Rld3JpdGUvcmVtb3Rld3JpdGUuZ28=) | `62.61% <0.00%> (+3.73%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1310?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/1310?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=VictoriaMetrics). Last update [ede2ba5...64176a2](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/1310?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/vmagent/remotewrite/pendingseries.go b/app/vmagent/remotewrite/pendingseries.go
index 05c545331..a5dff6c7d 100644
--- a/app/vmagent/remotewrite/pendingseries.go
+++ b/app/vmagent/remotewrite/pendingseries.go
@@ -128,7 +128,6 @@ func (wr *writeRequest) reset() {
 }
 
 func (wr *writeRequest) flush() {
-	sortLabelsIfNeeded(wr.tss)
 	wr.wr.Timeseries = wr.tss
 	wr.adjustSampleValues()
 	atomic.StoreUint64(&wr.lastFlushTime, fasttime.UnixTimestamp())
diff --git a/app/vmagent/remotewrite/remotewrite.go b/app/vmagent/remotewrite/remotewrite.go
index 09ec04102..de07aa7b4 100644
--- a/app/vmagent/remotewrite/remotewrite.go
+++ b/app/vmagent/remotewrite/remotewrite.go
@@ -13,6 +13,7 @@ import (
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/persistentqueue"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/procutil"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
+	"github.com/VictoriaMetrics/VictoriaMetrics/lib/promrelabel"
 	"github.com/VictoriaMetrics/metrics"
 	xxhash "github.com/cespare/xxhash/v2"
 )
@@ -38,6 +39,10 @@ var (
 		"Examples: -remoteWrite.roundDigits=2 would round 1.236 to 1.24, while -remoteWrite.roundDigits=-1 would round 126.78 to 130. "+
 		"By default digits rounding is disabled. Set it to 100 for disabling it for a particular remote storage. "+
 		"This option may be used for improving data compression for the stored metrics")
+	sortLabels = flag.Bool("sortLabels", false, `Whether to sort labels for incoming samples before writing them to all the configured remote storage systems. `+
+		`This may be needed for reducing memory usage at remote storage when the order of labels in incoming samples is random. `+
+		`For example, if m{k1="v1",k2="v2"} may be sent as m{k2="v2",k1="v1"}`+
+		`Enabled sorting for labels can slow down ingestion performance a bit`)
 )
 
 var rwctxs []*remoteWriteCtx
@@ -173,6 +178,7 @@ func Push(wr *prompbmarshal.WriteRequest) {
 			tssBlock = rctx.applyRelabeling(tssBlock, labelsGlobal, pcsGlobal)
 			globalRelabelMetricsDropped.Add(tssBlockLen - len(tssBlock))
 		}
+		sortLabelsIfNeeded(tssBlock)
 		for _, rwctx := range rwctxs {
 			rwctx.Push(tssBlock)
 		}
@@ -185,6 +191,16 @@ func Push(wr *prompbmarshal.WriteRequest) {
 	}
 }
 
+// sortLabelsIfNeeded sorts labels if -sortLabels command-line flag is set.
+func sortLabelsIfNeeded(tss []prompbmarshal.TimeSeries) {
+	if !*sortLabels {
+		return
+	}
+	for i := range tss {
+		promrelabel.SortLabels(tss[i].Labels)
+	}
+}
+
 var globalRelabelMetricsDropped = metrics.NewCounter("vmagent_remotewrite_global_relabel_metrics_dropped_total")
 
 type remoteWriteCtx struct {
diff --git a/app/vmagent/remotewrite/sort_labels.go b/app/vmagent/remotewrite/sort_labels.go
deleted file mode 100644
index e9ec252bd..000000000
--- a/app/vmagent/remotewrite/sort_labels.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package remotewrite
-
-import (
-	"flag"
-	"sort"
-	"sync"
-
-	"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
-)
-
-var sortLabels = flag.Bool("sortLabels", false, `Whether to sort labels for incoming samples before writing them to all the configured remote storage systems. `+
-	`This may be needed for reducing memory usage at remote storage when the order of labels in incoming samples is random. `+
-	`For example, if m{k1="v1",k2="v2"} may be sent as m{k2="v2",k1="v1"}`+
-	`Enabled sorting for labels can slow down ingestion performance a bit`)
-
-// sortLabelsIfNeeded sorts labels if -sortLabels command-line flag is set.
-func sortLabelsIfNeeded(tss []prompbmarshal.TimeSeries) {
-	if !*sortLabels {
-		return
-	}
-	// The slc is used for avoiding memory allocation when passing labels to sort.Sort.
-	slc := sortLabelsCtxPool.Get().(*sortLabelsCtx)
-	for i := range tss {
-		slc.labels = tss[i].Labels
-		sort.Sort(&slc.labels)
-	}
-	slc.labels = nil
-	sortLabelsCtxPool.Put(slc)
-}
-
-type sortLabelsCtx struct {
-	labels sortedLabels
-}
-
-var sortLabelsCtxPool = &sync.Pool{
-	New: func() interface{} {
-		return &sortLabelsCtx{}
-	},
-}
-
-type sortedLabels []prompbmarshal.Label
-
-func (sl *sortedLabels) Len() int { return len(*sl) }
-func (sl *sortedLabels) Less(i, j int) bool {
-	a := *sl
-	return a[i].Name < a[j].Name
-}
-func (sl *sortedLabels) Swap(i, j int) {
-	a := *sl
-	a[i], a[j] = a[j], a[i]
-}
diff --git a/app/vmctl/influx/influx.go b/app/vmctl/influx/influx.go
index 79ca5f249..8c77fed49 100644
--- a/app/vmctl/influx/influx.go
+++ b/app/vmctl/influx/influx.go
@@ -61,7 +61,7 @@ func (s Series) fetchQuery(timeFilter string) string {
 	}
 	for i, pair := range s.LabelPairs {
 		pairV := valueEscaper.Replace(pair.Value)
-		fmt.Fprintf(f, " %q='%s'", pair.Name, pairV)
+		fmt.Fprintf(f, " %q::tag='%s'", pair.Name, pairV)
 		if i != len(s.LabelPairs)-1 {
 			f.WriteString(" and")
 		}
diff --git a/app/vmselect/promql/transform.go b/app/vmselect/promql/transform.go
index 4402772b7..18998475d 100644
--- a/app/vmselect/promql/transform.go
+++ b/app/vmselect/promql/transform.go
@@ -123,6 +123,7 @@ var transformFuncs = map[string]transformFunc{
 	"histogram_stddev":   transformHistogramStddev,
 	"sort_by_label":      newTransformFuncSortByLabel(false),
 	"sort_by_label_desc": newTransformFuncSortByLabel(true),
+	"timezone_offset":    transformTimezoneOffset,
 }
 
 func getTransformFunc(s string) transformFunc {
@@ -1906,6 +1907,32 @@ func transformPi(tfa *transformFuncArg) ([]*timeseries, error) {
 	return evalNumber(tfa.ec, math.Pi), nil
 }
 
+func transformTimezoneOffset(tfa *transformFuncArg) ([]*timeseries, error) {
+	args := tfa.args
+	if err := expectTransformArgsNum(args, 1); err != nil {
+		return nil, err
+	}
+	tzString, err := getString(args[0], 0)
+	if err != nil {
+		return nil, fmt.Errorf("cannot get timezone name: %w", err)
+	}
+	tzOffset, err := getTimezoneOffset(tzString)
+	if err != nil {
+		return nil, fmt.Errorf("cannot get timezone offset for %q: %w", tzString, err)
+	}
+	rv := evalNumber(tfa.ec, float64(tzOffset))
+	return rv, nil
+}
+
+func getTimezoneOffset(tzString string) (int, error) {
+	loc, err := time.LoadLocation(tzString)
+	if err != nil {
+		return 0, fmt.Errorf("cannot load timezone %q: %w", tzString, err)
+	}
+	_, tzOffset := time.Now().In(loc).Zone()
+	return tzOffset, nil
+}
+
 func transformTime(tfa *transformFuncArg) ([]*timeseries, error) {
 	if err := expectTransformArgsNum(tfa.args, 0); err != nil {
 		return nil, err
diff --git a/app/vmselect/promql/tzdata.go b/app/vmselect/promql/tzdata.go
new file mode 100644
index 000000000..c513285bd
--- /dev/null
+++ b/app/vmselect/promql/tzdata.go
@@ -0,0 +1,9 @@
+// +build go1.15
+
+package promql
+
+import (
+	// This is needed for embedding tzdata into binary, so `timezone_offset` could work in an app running on a scratch base Docker image.
+	// The "time/tzdata" package has been appeared starting from Go1.15 - see https://golang.org/doc/go1.15#time/tzdata
+	_ "time/tzdata"
+)
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 7f13fb640..19db00024 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -14,6 +14,7 @@ sort: 15
 * FEATURE: add ability to filter `/api/v1/status/tsdb` output with arbitrary [time series selectors](https://prometheus.io/docs/prometheus/latest/querying/basics/#time-series-selectors) passed via `match[]` query args. See [these docs](https://docs.victoriametrics.com/#tsdb-stats) and [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1168) for details.
 * FEATURE: automatically detect memory and cpu limits for VictoriaMetrics components running under [cgroup v2](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html) environments such as [HashiCorp Nomad](https://www.nomadproject.io/). See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1269).
 * FEATURE: vmauth: allow `-auth.config` reloading via `/-/reload` http endpoint. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1194).
+* FEATURE: add `timezone_offset(tz)` function. It returns offset in seconds for the given timezone `tz` relative to UTC. This can be useful when combining with datetime-related functions. For example, `day_of_week(time()+timezone_offset("America/Los_Angeles"))` would return weekdays for `America/Los_Angeles` time zone. Special `Local` time zone can be used for returning an offset for the time zone set on the host where VictoriaMetrics runs. See [MetricsQL docs](https://docs.victoriametrics.com/MetricsQL.html) for more details.
 
 * BUGFIX: vmagent: do not retry scraping targets, which don't support HTTP. This should reduce CPU load and network usage at `vmagent` and at scrape target. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1289).
 * 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).
diff --git a/docs/MetricsQL.md b/docs/MetricsQL.md
index 446cb2a87..ff7eb13eb 100644
--- a/docs/MetricsQL.md
+++ b/docs/MetricsQL.md
@@ -156,3 +156,4 @@ This functionality can be tried at [an editable Grafana dashboard](http://play-g
   anomalies in time series comparing to historical samples.
 - `zscore(q) by (group)` - returns independent [z-score](https://en.wikipedia.org/wiki/Standard_score) values for every point in every `group` of `q`.
   Useful for detecting anomalies in the group of related time series.
+- `timezone_offset("tz")` - returns offset in seconds for the given timezone `tz` relative to UTC. This can be useful when combining with datetime-related functions. For example, `day_of_week(time()+timezone_offset("America/Los_Angeles"))` would return weekdays for `America/Los_Angeles` time zone. Special `Local` time zone can be used for returning an offset for the time zone set on the host where VictoriaMetrics runs.
diff --git a/go.mod b/go.mod
index c7dea1c9e..fe9b0151b 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@ require (
 	// like https://github.com/valyala/fasthttp/commit/996610f021ff45fdc98c2ce7884d5fa4e7f9199b
 	github.com/VictoriaMetrics/fasthttp v1.0.15
 	github.com/VictoriaMetrics/metrics v1.17.2
-	github.com/VictoriaMetrics/metricsql v0.14.0
+	github.com/VictoriaMetrics/metricsql v0.15.0
 	github.com/VividCortex/ewma v1.2.0 // indirect
 	github.com/aws/aws-sdk-go v1.38.40
 	github.com/cespare/xxhash/v2 v2.1.1
diff --git a/go.sum b/go.sum
index 21489c4cd..93326342f 100644
--- a/go.sum
+++ b/go.sum
@@ -88,8 +88,8 @@ github.com/VictoriaMetrics/fasthttp v1.0.15/go.mod h1:s9o5H4T58Kt4CTrdyJp4RorBKC
 github.com/VictoriaMetrics/metrics v1.12.2/go.mod h1:Z1tSfPfngDn12bTfZSCqArT3OPY3u88J12hSoOhuiRE=
 github.com/VictoriaMetrics/metrics v1.17.2 h1:9zPJ7DPfxdJWshOGLPLpAtPL0ZZ9AeUyQC3fIqG6Lvo=
 github.com/VictoriaMetrics/metrics v1.17.2/go.mod h1:Z1tSfPfngDn12bTfZSCqArT3OPY3u88J12hSoOhuiRE=
-github.com/VictoriaMetrics/metricsql v0.14.0 h1:XGbpZJVskUPJFo2C7vG6ATxXBwkBFPe7EWZXB2HZt2U=
-github.com/VictoriaMetrics/metricsql v0.14.0/go.mod h1:ylO7YITho/Iw6P71oEaGyHbO94bGoGtzWfLGqFhMIg8=
+github.com/VictoriaMetrics/metricsql v0.15.0 h1:7tveqhA0xLumXhokcmgxiUn90VlKqiVtYZQ3p6y9Fu4=
+github.com/VictoriaMetrics/metricsql v0.15.0/go.mod h1:ylO7YITho/Iw6P71oEaGyHbO94bGoGtzWfLGqFhMIg8=
 github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
 github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
 github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
diff --git a/lib/promscrape/discovery/kubernetes/api_watcher.go b/lib/promscrape/discovery/kubernetes/api_watcher.go
index 98ac71d13..9171ab856 100644
--- a/lib/promscrape/discovery/kubernetes/api_watcher.go
+++ b/lib/promscrape/discovery/kubernetes/api_watcher.go
@@ -573,15 +573,11 @@ func (uw *urlWatcher) readObjectUpdateStream(r io.Reader) error {
 			key := o.key()
 			uw.gw.mu.Lock()
 			if _, ok := uw.objectsByKey[key]; !ok {
-				if we.Type == "MODIFIED" {
-					// This is expected condition after recovering from the bookmarked resourceVersion.
-				}
+				// if we.Type == "MODIFIED" is expected condition after recovering from the bookmarked resourceVersion.
 				uw.objectsCount.Inc()
 				uw.objectsAdded.Inc()
 			} else {
-				if we.Type == "ADDED" {
-					// This is expected condition after recovering from the bookmarked resourceVersion.
-				}
+				// if we.Type == "ADDED" is expected condition after recovering from the bookmarked resourceVersion.
 				uw.objectsUpdated.Inc()
 			}
 			uw.objectsByKey[key] = o
@@ -599,9 +595,7 @@ func (uw *urlWatcher) readObjectUpdateStream(r io.Reader) error {
 			}
 			key := o.key()
 			uw.gw.mu.Lock()
-			if _, ok := uw.objectsByKey[key]; !ok {
-				// This is expected condition after recovering from the bookmarked resourceVersion.
-			} else {
+			if _, ok := uw.objectsByKey[key]; ok {
 				uw.objectsCount.Dec()
 				uw.objectsRemoved.Inc()
 				delete(uw.objectsByKey, key)
diff --git a/lib/storage/metric_name.go b/lib/storage/metric_name.go
index d5f486744..e1ab7da75 100644
--- a/lib/storage/metric_name.go
+++ b/lib/storage/metric_name.go
@@ -8,10 +8,10 @@ import (
 	"strings"
 	"sync"
 	"sync/atomic"
+	"time"
 
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
-	"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb"
 )
@@ -497,21 +497,16 @@ var (
 
 func trackDroppedLabels(labels, droppedLabels []prompb.Label) {
 	atomic.AddUint64(&MetricsWithDroppedLabels, 1)
-	ct := fasttime.UnixTimestamp()
-	if ct < atomic.LoadUint64(&droppedLabelsLogNextTimestamp) {
-		return
-	}
-	droppedLabelsLogOnce.Do(func() {
-		atomic.StoreUint64(&droppedLabelsLogNextTimestamp, ct+5)
+	select {
+	case <-droppedLabelsLogTicker.C:
 		logger.Warnf("dropping %d labels for %s; dropped labels: %s; either reduce the number of labels for this metric "+
 			"or increase -maxLabelsPerTimeseries=%d command-line flag value",
 			len(droppedLabels), labelsToString(labels), labelsToString(droppedLabels), maxLabelsPerTimeseries)
-		droppedLabelsLogOnce = &sync.Once{}
-	})
+	default:
+	}
 }
 
-var droppedLabelsLogOnce = &sync.Once{}
-var droppedLabelsLogNextTimestamp uint64
+var droppedLabelsLogTicker = time.NewTicker(5 * time.Second)
 
 func labelsToString(labels []prompb.Label) string {
 	labelsCopy := append([]prompb.Label{}, labels...)
diff --git a/vendor/github.com/VictoriaMetrics/metricsql/transform.go b/vendor/github.com/VictoriaMetrics/metricsql/transform.go
index df4f9b2b5..3dbac052a 100644
--- a/vendor/github.com/VictoriaMetrics/metricsql/transform.go
+++ b/vendor/github.com/VictoriaMetrics/metricsql/transform.go
@@ -88,6 +88,7 @@ var transformFuncs = map[string]bool{
 	"histogram_stddev":   true,
 	"sort_by_label":      true,
 	"sort_by_label_desc": true,
+	"timezone_offset":    true,
 }
 
 // IsTransformFunc returns whether funcName is known transform function.
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 11411be79..0e750dfa3 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -20,7 +20,7 @@ github.com/VictoriaMetrics/fasthttp/stackless
 # github.com/VictoriaMetrics/metrics v1.17.2
 ## explicit
 github.com/VictoriaMetrics/metrics
-# github.com/VictoriaMetrics/metricsql v0.14.0
+# github.com/VictoriaMetrics/metricsql v0.15.0
 ## explicit
 github.com/VictoriaMetrics/metricsql
 github.com/VictoriaMetrics/metricsql/binaryop


Test patch:
diff --git a/app/vmctl/influx/influx_test.go b/app/vmctl/influx/influx_test.go
index c92b0f5e0..94a90bac7 100644
--- a/app/vmctl/influx/influx_test.go
+++ b/app/vmctl/influx/influx_test.go
@@ -19,7 +19,7 @@ func TestFetchQuery(t *testing.T) {
 					},
 				},
 			},
-			expected: `select "value" from "cpu" where "foo"='bar'`,
+			expected: `select "value" from "cpu" where "foo"::tag='bar'`,
 		},
 		{
 			s: Series{
@@ -36,7 +36,7 @@ func TestFetchQuery(t *testing.T) {
 					},
 				},
 			},
-			expected: `select "value" from "cpu" where "foo"='bar' and "baz"='qux'`,
+			expected: `select "value" from "cpu" where "foo"::tag='bar' and "baz"::tag='qux'`,
 		},
 		{
 			s: Series{
@@ -50,7 +50,7 @@ func TestFetchQuery(t *testing.T) {
 				},
 			},
 			timeFilter: "time >= now()",
-			expected:   `select "value" from "cpu" where "foo"='b\'ar' and time >= now()`,
+			expected:   `select "value" from "cpu" where "foo"::tag='b\'ar' and time >= now()`,
 		},
 		{
 			s: Series{
@@ -68,7 +68,7 @@ func TestFetchQuery(t *testing.T) {
 				},
 			},
 			timeFilter: "time >= now()",
-			expected:   `select "value" from "cpu" where "name"='dev-mapper-centos\\x2dswap.swap' and "state"='dev-mapp\'er-c\'en\'tos' and time >= now()`,
+			expected:   `select "value" from "cpu" where "name"::tag='dev-mapper-centos\\x2dswap.swap' and "state"::tag='dev-mapp\'er-c\'en\'tos' and time >= now()`,
 		},
 		{
 			s: Series{
diff --git a/app/vmselect/promql/exec_test.go b/app/vmselect/promql/exec_test.go
index 61d9318ff..917b0587e 100644
--- a/app/vmselect/promql/exec_test.go
+++ b/app/vmselect/promql/exec_test.go
@@ -132,6 +132,49 @@ func TestExecSuccess(t *testing.T) {
 		resultExpected := []netstorage.Result{r}
 		f(q, resultExpected)
 	})
+	t.Run("timezone_offset(UTC)", func(t *testing.T) {
+		t.Parallel()
+		q := `timezone_offset("UTC")`
+		r := netstorage.Result{
+			MetricName: metricNameExpected,
+			Values:     []float64{0, 0, 0, 0, 0, 0},
+			Timestamps: timestampsExpected,
+		}
+		resultExpected := []netstorage.Result{r}
+		f(q, resultExpected)
+	})
+	t.Run("timezone_offset(America/New_York)", func(t *testing.T) {
+		t.Parallel()
+		q := `timezone_offset("America/New_York")`
+		offset, err := getTimezoneOffset("America/New_York")
+		if err != nil {
+			t.Fatalf("cannot obtain timezone: %s", err)
+		}
+		off := float64(offset)
+		r := netstorage.Result{
+			MetricName: metricNameExpected,
+			Values:     []float64{off, off, off, off, off, off},
+			Timestamps: timestampsExpected,
+		}
+		resultExpected := []netstorage.Result{r}
+		f(q, resultExpected)
+	})
+	t.Run("timezone_offset(Local)", func(t *testing.T) {
+		t.Parallel()
+		q := `timezone_offset("Local")`
+		offset, err := getTimezoneOffset("Local")
+		if err != nil {
+			t.Fatalf("cannot obtain timezone: %s", err)
+		}
+		off := float64(offset)
+		r := netstorage.Result{
+			MetricName: metricNameExpected,
+			Values:     []float64{off, off, off, off, off, off},
+			Timestamps: timestampsExpected,
+		}
+		resultExpected := []netstorage.Result{r}
+		f(q, resultExpected)
+	})
 	t.Run("time()", func(t *testing.T) {
 		t.Parallel()
 		q := `time()`