Repository: VictoriaMetrics/VictoriaMetrics
Pull request #779 (2020-09-19 21:41:53)

vmalert: add support for `datasource.lookback` flag
New datasource flag `datasource.lookback` defines how far to look into
past when evaluating queries.

Address https://github.com/VictoriaMetrics/VictoriaMetrics/issues/668

codecov[bot]: # [Codecov](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779?src=pr&el=h1) Report
> Merging [#779](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779?src=pr&el=desc) into [master](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/commit/7a134b0fd7853dc3f8442d7c0250d578923089b4?el=desc) will **decrease** coverage by `1.73%`.
> The diff coverage is `85.71%`.

[![Impacted file tree graph](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779/graphs/tree.svg?width=650&height=150&src=pr&token=YND8dYVcXn)](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #779      +/-   ##
==========================================
- Coverage   67.31%   65.57%   -1.74%     
==========================================
  Files         172      172              
  Lines       22944    20143    -2801     
==========================================
- Hits        15444    13209    -2235     
+ Misses       6252     5717     -535     
+ Partials     1248     1217      -31     
```


| [Impacted Files](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [app/vmalert/datasource/init.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779/diff?src=pr&el=tree#diff-YXBwL3ZtYWxlcnQvZGF0YXNvdXJjZS9pbml0Lmdv) | `0.00% <0.00%> (ø)` | |
| [app/vmalert/datasource/vm.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779/diff?src=pr&el=tree#diff-YXBwL3ZtYWxlcnQvZGF0YXNvdXJjZS92bS5nbw==) | `91.83% <100.00%> (ø)` | |
| [lib/fasttime/fasttime.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779/diff?src=pr&el=tree#diff-bGliL2Zhc3R0aW1lL2Zhc3R0aW1lLmdv) | `37.50% <0.00%> (-16.35%)` | :arrow_down: |
| [lib/protoparser/prometheus/streamparser.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779/diff?src=pr&el=tree#diff-bGliL3Byb3RvcGFyc2VyL3Byb21ldGhldXMvc3RyZWFtcGFyc2VyLmdv) | `66.66% <0.00%> (-4.77%)` | :arrow_down: |
| [...ib/promscrape/discovery/kubernetes/common\_types.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779/diff?src=pr&el=tree#diff-bGliL3Byb21zY3JhcGUvZGlzY292ZXJ5L2t1YmVybmV0ZXMvY29tbW9uX3R5cGVzLmdv) | `30.00% <0.00%> (-4.62%)` | :arrow_down: |
| [app/vmalert/remotewrite/remotewrite.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779/diff?src=pr&el=tree#diff-YXBwL3ZtYWxlcnQvcmVtb3Rld3JpdGUvcmVtb3Rld3JpdGUuZ28=) | `56.60% <0.00%> (-4.42%)` | :arrow_down: |
| [lib/promscrape/discovery/kubernetes/node.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779/diff?src=pr&el=tree#diff-bGliL3Byb21zY3JhcGUvZGlzY292ZXJ5L2t1YmVybmV0ZXMvbm9kZS5nbw==) | `45.65% <0.00%> (-4.35%)` | :arrow_down: |
| [app/vmalert/utils/err\_group.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779/diff?src=pr&el=tree#diff-YXBwL3ZtYWxlcnQvdXRpbHMvZXJyX2dyb3VwLmdv) | `83.33% <0.00%> (-4.17%)` | :arrow_down: |
| [lib/mergeset/metaindex\_row.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779/diff?src=pr&el=tree#diff-bGliL21lcmdlc2V0L21ldGFpbmRleF9yb3cuZ28=) | `50.00% <0.00%> (-4.10%)` | :arrow_down: |
| [lib/mergeset/part\_header.go](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779/diff?src=pr&el=tree#diff-bGliL21lcmdlc2V0L3BhcnRfaGVhZGVyLmdv) | `58.22% <0.00%> (-3.85%)` | :arrow_down: |
| ... and [155 more](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779?src=pr&el=footer). Last update [7a134b0...0f81897](https://codecov.io/gh/VictoriaMetrics/VictoriaMetrics/pull/779?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

tenmozes: @hagen1778 thank you for contribution :) look good 

Patch:
diff --git a/app/vmalert/README.md b/app/vmalert/README.md
index 846245fa5..3b1df8103 100644
--- a/app/vmalert/README.md
+++ b/app/vmalert/README.md
@@ -166,6 +166,8 @@ The shortlist of configuration flags is the following:
     	Optional basic auth password for -datasource.url
   -datasource.basicAuth.username string
     	Optional basic auth username for -datasource.url
+ -datasource.lookback duration
+        Lookback defines how far to look into past when evaluating queries. For example, if datasource.lookback=5m then param "time" with value now()-5m will be added to every query.
   -datasource.tlsCAFile string
     	Optional path to TLS CA file to use for verifying connections to -datasource.url. By default system CA is used
   -datasource.tlsCertFile string
diff --git a/app/vmalert/datasource/init.go b/app/vmalert/datasource/init.go
index 244d3c9c3..89814dde5 100644
--- a/app/vmalert/datasource/init.go
+++ b/app/vmalert/datasource/init.go
@@ -21,6 +21,9 @@ var (
 		"By default system CA is used")
 	tlsServerName = flag.String("datasource.tlsServerName", "", "Optional TLS server name to use for connections to -datasource.url. "+
 		"By default the server name from -datasource.url is used")
+
+	lookBack = flag.Duration("datasource.lookback", 0, "Lookback defines how far to look into past when evaluating queries. "+
+		"For example, if datasource.lookback=5m then param \"time\" with value now()-5m will be added to every query.")
 )
 
 // Init creates a Querier from provided flag values.
@@ -34,5 +37,5 @@ func Init() (Querier, error) {
 		return nil, fmt.Errorf("failed to create transport: %w", err)
 	}
 	c := &http.Client{Transport: tr}
-	return NewVMStorage(*addr, *basicAuthUsername, *basicAuthPassword, c), nil
+	return NewVMStorage(*addr, *basicAuthUsername, *basicAuthPassword, *lookBack, c), nil
 }
diff --git a/app/vmalert/datasource/vm.go b/app/vmalert/datasource/vm.go
index 65c3db9e0..3db5552d0 100644
--- a/app/vmalert/datasource/vm.go
+++ b/app/vmalert/datasource/vm.go
@@ -9,6 +9,7 @@ import (
 	"net/url"
 	"strconv"
 	"strings"
+	"time"
 )
 
 type response struct {
@@ -45,23 +46,25 @@ func (r response) metrics() ([]Metric, error) {
 	return ms, nil
 }
 
-const queryPath = "/api/v1/query?query="
-
 // VMStorage represents vmstorage entity with ability to read and write metrics
 type VMStorage struct {
 	c             *http.Client
 	queryURL      string
 	basicAuthUser string
 	basicAuthPass string
+	lookBack      time.Duration
 }
 
+const queryPath = "/api/v1/query?query="
+
 // NewVMStorage is a constructor for VMStorage
-func NewVMStorage(baseURL, basicAuthUser, basicAuthPass string, c *http.Client) *VMStorage {
+func NewVMStorage(baseURL, basicAuthUser, basicAuthPass string, lookBack time.Duration, c *http.Client) *VMStorage {
 	return &VMStorage{
 		c:             c,
 		basicAuthUser: basicAuthUser,
 		basicAuthPass: basicAuthPass,
 		queryURL:      strings.TrimSuffix(baseURL, "/") + queryPath,
+		lookBack:      lookBack,
 	}
 }
 
@@ -70,7 +73,12 @@ func (s *VMStorage) Query(ctx context.Context, query string) ([]Metric, error) {
 	const (
 		statusSuccess, statusError, rtVector = "success", "error", "vector"
 	)
-	req, err := http.NewRequest("POST", s.queryURL+url.QueryEscape(query), nil)
+	q := s.queryURL + url.QueryEscape(query)
+	if s.lookBack > 0 {
+		lookBack := time.Now().UTC().Add(-s.lookBack)
+		q += fmt.Sprintf("&time=%d", lookBack.Unix())
+	}
+	req, err := http.NewRequest("POST", q, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -85,7 +93,7 @@ func (s *VMStorage) Query(ctx context.Context, query string) ([]Metric, error) {
 	defer func() { _ = resp.Body.Close() }()
 	if resp.StatusCode != http.StatusOK {
 		body, _ := ioutil.ReadAll(resp.Body)
-		return nil, fmt.Errorf("datasource returns unexpected response code %d for %s with err %w. Reponse body %s", resp.StatusCode, req.URL, err, body)
+		return nil, fmt.Errorf("datasource returns unexpected response code %d for %s. Reponse body %s", resp.StatusCode, req.URL, body)
 	}
 	r := &response{}
 	if err := json.NewDecoder(resp.Body).Decode(r); err != nil {
diff --git a/app/vmalert/remoteread/init.go b/app/vmalert/remoteread/init.go
index 69067493d..dfbcba8f6 100644
--- a/app/vmalert/remoteread/init.go
+++ b/app/vmalert/remoteread/init.go
@@ -35,5 +35,5 @@ func Init() (datasource.Querier, error) {
 		return nil, fmt.Errorf("failed to create transport: %w", err)
 	}
 	c := &http.Client{Transport: tr}
-	return datasource.NewVMStorage(*addr, *basicAuthUsername, *basicAuthPassword, c), nil
+	return datasource.NewVMStorage(*addr, *basicAuthUsername, *basicAuthPassword, 0, c), nil
 }


Test patch:
diff --git a/app/vmalert/datasource/vm_test.go b/app/vmalert/datasource/vm_test.go
index 708f76a04..d7998fb4e 100644
--- a/app/vmalert/datasource/vm_test.go
+++ b/app/vmalert/datasource/vm_test.go
@@ -4,7 +4,9 @@ import (
 	"context"
 	"net/http"
 	"net/http/httptest"
+	"strconv"
 	"testing"
+	"time"
 )
 
 var (
@@ -31,6 +33,13 @@ func TestVMSelectQuery(t *testing.T) {
 		if r.URL.Query().Get("query") != query {
 			t.Errorf("expected %s in query param, got %s", query, r.URL.Query().Get("query"))
 		}
+		timeParam := r.URL.Query().Get("time")
+		if timeParam == "" {
+			t.Errorf("expected 'time' in query param, got nil instead")
+		}
+		if _, err := strconv.ParseInt(timeParam, 10, 64); err != nil {
+			t.Errorf("failed to parse 'time' query param: %s", err)
+		}
 		switch c {
 		case 0:
 			conn, _, _ := w.(http.Hijacker).Hijack()
@@ -52,7 +61,7 @@ func TestVMSelectQuery(t *testing.T) {
 
 	srv := httptest.NewServer(mux)
 	defer srv.Close()
-	am := NewVMStorage(srv.URL, basicAuthName, basicAuthPass, srv.Client())
+	am := NewVMStorage(srv.URL, basicAuthName, basicAuthPass, time.Minute, srv.Client())
 	if _, err := am.Query(ctx, query); err == nil {
 		t.Fatalf("expected connection error got nil")
 	}
@@ -89,5 +98,4 @@ func TestVMSelectQuery(t *testing.T) {
 		m[0].Labels[0].Name != expected.Labels[0].Name {
 		t.Fatalf("unexpected metric %+v want %+v", m[0], expected)
 	}
-
 }