From 0842bb92945d07a52ac930d3fc2e92daa0bfcad5 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Thu, 20 May 2021 11:01:29 +0300 Subject: [PATCH] app/vmselect/promql: add `timezone_offset(tz)` function Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1306 --- app/vmselect/promql/exec_test.go | 43 +++++++++++++++++++ app/vmselect/promql/transform.go | 27 ++++++++++++ app/vmselect/promql/tzdata.go | 9 ++++ docs/CHANGELOG.md | 1 + docs/MetricsQL.md | 1 + go.mod | 2 +- go.sum | 4 +- .../VictoriaMetrics/metricsql/transform.go | 1 + vendor/modules.txt | 2 +- 9 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 app/vmselect/promql/tzdata.go diff --git a/app/vmselect/promql/exec_test.go b/app/vmselect/promql/exec_test.go index 61d9318ffa..917b0587ef 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()` diff --git a/app/vmselect/promql/transform.go b/app/vmselect/promql/transform.go index 4402772b70..18998475d0 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 0000000000..c513285bd7 --- /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 7f13fb6400..19db000241 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 446cb2a87d..ff7eb13eb4 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 c7dea1c9e8..fe9b0151bd 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 21489c4cde..93326342fd 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/vendor/github.com/VictoriaMetrics/metricsql/transform.go b/vendor/github.com/VictoriaMetrics/metricsql/transform.go index df4f9b2b5b..3dbac052a9 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 11411be794..0e750dfa3c 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