vendor: update github.com/VictoriaMetrics/metricsql from v0.60.0 to v0.61.1

This adds support for passing durations via WITH template vars:

- `WITH (w = 5m) m[w]` is transformed to `m[5m]`
- `WITH (f(w, step, off) = m[w:step] offset off) f(5m, 10s, 1h)` is transformed to `m[5m:10s] offset 1h`

Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4025
Updates https://github.com/VictoriaMetrics/metricsql/issues/12

See also the initial implementation by @lujiajing1126 at https://github.com/VictoriaMetrics/metricsql/pull/13
This commit is contained in:
Aliaksandr Valialkin 2023-07-19 14:59:43 -07:00
parent ee933541b2
commit 262932f517
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
6 changed files with 140 additions and 27 deletions

View file

@ -30,9 +30,11 @@ The following `tip` changes can be tested by building VictoriaMetrics components
* FEATURE: reduce memory usage by up to 5x for setups with [high churn rate](https://docs.victoriametrics.com/FAQ.html#what-is-high-churn-rate) and long [retention](https://docs.victoriametrics.com/#retention). See [description for this change](https://github.com/VictoriaMetrics/VictoriaMetrics/commit/7094fa38bc207c7bd7330ea8a834310a310ce5e3) for details.
* FEATURE: [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html): allow selecting time series matching at least one of multiple `or` filters. For example, `{env="prod",job="a" or env="dev",job="b"}` selects series with either `{env="prod",job="a"}` or `{env="dev",job="b"}` labels. This functionality allows passing the selected series to [rollup functions](https://docs.victoriametrics.com/MetricsQL.html#rollup-functions) without the need to use [subqueries](https://docs.victoriametrics.com/MetricsQL.html#subqueries). See [these docs](https://docs.victoriametrics.com/keyConcepts.html#filtering-by-multiple-or-filters).
* FEATURE: [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html): add ability to preserve metric names for binary operation results via `keep_metric_names` modifier. For example, `({__name__=~"foo|bar"} / 10) keep_metric_names` leaves `foo` and `bar` metric names in division results. See [these docs](https://docs.victoriametrics.com/MetricsQL.html#keep_metric_names). This helps to address issues like [this one](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3710).
* FEATURE: [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html): add ability to copy all the labels from `one` side of [many-to-one operations](https://prometheus.io/docs/prometheus/latest/querying/operators/#many-to-one-and-one-to-many-vector-matches) by specifying `*` inside `group_left()` or `group_right()`. Also allow adding a prefix for copied label names via `group_left(*) prefix "..."` syntax. For example, the following query copies Kubernetes namespace labels to `kube_pod_info` series and adds `ns_` prefix for the copied label names: `kube_pod_info * on(namespace) group_left(*) prefix "ns_" kube_namespace_labels`. The labels from `on()` list aren't prefixed.
This feature resolves [this](https://stackoverflow.com/questions/76661818/how-to-add-namespace-labels-to-pod-labels-in-prometheus)
and [that](https://stackoverflow.com/questions/76653997/how-can-i-make-a-new-copy-of-kube-namespace-labels-metric-with-a-different-name) questions at StackOverflow.
* FEATURE: [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html): add ability to copy all the labels from `one` side of [many-to-one operations](https://prometheus.io/docs/prometheus/latest/querying/operators/#many-to-one-and-one-to-many-vector-matches) by specifying `*` inside `group_left()` or `group_right()`. Also allow adding a prefix for copied label names via `group_left(*) prefix "..."` syntax. For example, the following query copies Kubernetes namespace labels to `kube_pod_info` series and adds `ns_` prefix for the copied label names: `kube_pod_info * on(namespace) group_left(*) prefix "ns_" kube_namespace_labels`. The labels from `on()` list aren't prefixed. This feature resolves [this](https://stackoverflow.com/questions/76661818/how-to-add-namespace-labels-to-pod-labels-in-prometheus) and [that](https://stackoverflow.com/questions/76653997/how-can-i-make-a-new-copy-of-kube-namespace-labels-metric-with-a-different-name) questions at StackOverflow.
* FEATURE: [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html): add ability to specify durations via [`WITH` templates](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/prometheus/expand-with-exprs). Examples:
- `WITH (w = 5m) m[w]` is automatically transformed to `m[5m]`
- `WITH (f(window, step, off) = m[window:step] offset off) f(5m, 10s, 1h)` is automatically transformed to `m[5m:10s] offset 1h`
Thanks to @lujiajing1126 for the initial idea and [implementation](https://github.com/VictoriaMetrics/metricsql/pull/13). See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4025).
* FEATURE: [vmctl](https://docs.victoriametrics.com/vmctl.html): add verbose output for docker installations or when TTY isn't available. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4081).
* FEATURE: [vmctl](https://docs.victoriametrics.com/vmctl.html): interrupt backoff retries when import process is cancelled. The change makes vmctl more responsive in case of errors during the import. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/4442).
* FEATURE: [vmctl](https://docs.victoriametrics.com/vmctl.html): update backoff policy on retries to reduce probability of overloading for `source` or `destination` databases. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4402).

2
go.mod
View file

@ -12,7 +12,7 @@ require (
// like https://github.com/valyala/fasthttp/commit/996610f021ff45fdc98c2ce7884d5fa4e7f9199b
github.com/VictoriaMetrics/fasthttp v1.2.0
github.com/VictoriaMetrics/metrics v1.24.0
github.com/VictoriaMetrics/metricsql v0.60.0
github.com/VictoriaMetrics/metricsql v0.61.1
github.com/aws/aws-sdk-go-v2 v1.19.0
github.com/aws/aws-sdk-go-v2/config v1.18.28
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.72

4
go.sum
View file

@ -70,8 +70,8 @@ github.com/VictoriaMetrics/fasthttp v1.2.0 h1:nd9Wng4DlNtaI27WlYh5mGXCJOmee/2c2b
github.com/VictoriaMetrics/fasthttp v1.2.0/go.mod h1:zv5YSmasAoSyv8sBVexfArzFDIGGTN4TfCKAtAw7IfE=
github.com/VictoriaMetrics/metrics v1.24.0 h1:ILavebReOjYctAGY5QU2F9X0MYvkcrG3aEn2RKa1Zkw=
github.com/VictoriaMetrics/metrics v1.24.0/go.mod h1:eFT25kvsTidQFHb6U0oa0rTrDRdz4xTYjpL8+UPohys=
github.com/VictoriaMetrics/metricsql v0.60.0 h1:KlVWTabXGu/50U3Dp72TyJYo01WTaxSsHwMLiE9yHuA=
github.com/VictoriaMetrics/metricsql v0.60.0/go.mod h1:k4UaP/+CjuZslIjd+kCigNG9TQmUqh5v0TP/nMEy90I=
github.com/VictoriaMetrics/metricsql v0.61.1 h1:vYkVDVa+ROvrDkhlrCzBLKB273tE6N681ftfZP+Lav8=
github.com/VictoriaMetrics/metricsql v0.61.1/go.mod h1:k4UaP/+CjuZslIjd+kCigNG9TQmUqh5v0TP/nMEy90I=
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=

View file

@ -37,6 +37,11 @@ func (lex *lexer) Init(s string) {
lex.sTail = s
}
func (lex *lexer) PushBack(currToken, sHead string) {
lex.Token = currToken
lex.sTail = sHead + lex.sTail
}
func (lex *lexer) Next() error {
if lex.err != nil {
return lex.err

View file

@ -784,6 +784,18 @@ func expandWithExpr(was []*withArgExpr, e Expr) (Expr, error) {
}
re := *t
re.Expr = eNew
re.Window, err = expandDuration(was, re.Window)
if err != nil {
return nil, fmt.Errorf("cannot parse window for %s: %w", re.Expr.AppendString(nil), err)
}
re.Step, err = expandDuration(was, re.Step)
if err != nil {
return nil, fmt.Errorf("cannot parse step in %s: %w", re.Expr.AppendString(nil), err)
}
re.Offset, err = expandDuration(was, re.Offset)
if err != nil {
return nil, fmt.Errorf("cannot parse offset in %s: %w", re.Expr.AppendString(nil), err)
}
if t.At != nil {
atNew, err := expandWithExpr(was, t.At)
if err != nil {
@ -828,7 +840,7 @@ func expandWithExpr(was []*withArgExpr, e Expr) (Expr, error) {
lfe.Label, t.AppendString(nil), eNew.AppendString(nil))
}
if len(wme.labelFilterss) > 0 {
panic(fmt.Errorf("BUG: wme.labelFilterss must be empty after WITH template expansion; got %s", wme.labelFilterss))
panic(fmt.Errorf("BUG: wme.labelFilterss must be empty after WITH template expansion; got %s", wme.AppendString(nil)))
}
lfssSrc := wme.LabelFilterss
if len(lfssSrc) > 1 {
@ -888,7 +900,7 @@ func expandWithExpr(was []*withArgExpr, e Expr) (Expr, error) {
return nil, fmt.Errorf("cannot expand %q to non-metric expression %q", t.AppendString(nil), eNew.AppendString(nil))
}
if len(wme.labelFilterss) > 0 {
panic(fmt.Errorf("BUG: wme.labelFilterss must be empty after WITH templates expansion; got %s", wme.labelFilterss))
panic(fmt.Errorf("BUG: wme.labelFilterss must be empty after WITH templates expansion; got %s", wme.AppendString(nil)))
}
lfssSrc := wme.LabelFilterss
var lfssNew [][]LabelFilter
@ -945,6 +957,38 @@ func expandWithArgs(was []*withArgExpr, args []Expr) ([]Expr, error) {
return dstArgs, nil
}
func expandDuration(was []*withArgExpr, d *DurationExpr) (*DurationExpr, error) {
if d == nil {
return nil, nil
}
if !d.needsParsing {
return d, nil
}
wa := getWithArgExpr(was, d.s)
if wa == nil {
return nil, fmt.Errorf("cannot find WITH template for %q", d.s)
}
e, err := expandWithExprExt(was, wa, []Expr{})
if err != nil {
return nil, err
}
switch t := e.(type) {
case *DurationExpr:
if t.needsParsing {
panic(fmt.Errorf("BUG: DurationExpr %q must be already parsed", t.s))
}
return t, nil
case *NumberExpr:
// Convert number of seconds to DurationExpr
de := &DurationExpr{
s: t.s,
}
return de, nil
default:
return nil, fmt.Errorf("unexpected value for WITH template %q; got %s; want duration", d.s, e.AppendString(nil))
}
}
func expandModifierArgs(was []*withArgExpr, args []string) ([]string, error) {
if len(args) == 0 {
return nil, nil
@ -1335,8 +1379,24 @@ type labelFilterExpr struct {
IsNegative bool
}
func (lfe *labelFilterExpr) String() string {
return fmt.Sprintf("[label=%q, value=%+v, isRegexp=%v, isNegative=%v]", lfe.Label, lfe.Value, lfe.IsRegexp, lfe.IsNegative)
func (lfe *labelFilterExpr) AppendString(dst []byte) []byte {
dst = appendEscapedIdent(dst, lfe.Label)
if lfe.Value == nil {
return dst
}
dst = appendLabelFilterOp(dst, lfe.IsNegative, lfe.IsRegexp)
tokens := lfe.Value.tokens
if len(tokens) == 0 {
dst = strconv.AppendQuote(dst, lfe.Value.S)
return dst
}
for i, token := range tokens {
dst = append(dst, token...)
if i+1 < len(tokens) {
dst = append(dst, '+')
}
}
return dst
}
func (lfe *labelFilterExpr) toLabelFilter() (*LabelFilter, error) {
@ -1452,13 +1512,28 @@ func (p *parser) parseDuration() (*DurationExpr, error) {
func (p *parser) parsePositiveDuration() (*DurationExpr, error) {
s := p.lex.Token
if isIdentPrefix(s) {
n := strings.IndexByte(s, ':')
if n >= 0 {
p.lex.PushBack(s[:n], s[n:])
s = s[:n]
}
if err := p.lex.Next(); err != nil {
return nil, err
}
de := &DurationExpr{
s: s,
needsParsing: true,
}
return de, nil
}
if isPositiveDuration(s) {
if err := p.lex.Next(); err != nil {
return nil, err
}
} else {
if !isPositiveNumberPrefix(s) {
return nil, fmt.Errorf(`duration: unexpected token %q; want "duration"`, s)
return nil, fmt.Errorf(`duration: unexpected token %q; want valid duration`, s)
}
// Verify the duration in seconds without explicit suffix.
if _, err := p.parsePositiveNumberExpr(); err != nil {
@ -1478,6 +1553,9 @@ func (p *parser) parsePositiveDuration() (*DurationExpr, error) {
// DurationExpr contains the duration
type DurationExpr struct {
s string
// needsParsing is set to true if s isn't parsed yet with expandWithExpr()
needsParsing bool
}
// AppendString appends string representation of de to dst and returns the result.
@ -1485,6 +1563,9 @@ func (de *DurationExpr) AppendString(dst []byte) []byte {
if de == nil {
return dst
}
if de.needsParsing {
panic(fmt.Errorf("BUG: duration %q must be already parsed with expandWithExpr()", de.s))
}
return append(dst, de.s...)
}
@ -1493,6 +1574,9 @@ func (de *DurationExpr) Duration(step int64) int64 {
if de == nil {
return 0
}
if de.needsParsing {
panic(fmt.Errorf("BUG: duration %q must be already parsed", de.s))
}
d, err := DurationValue(de.s, step)
if err != nil {
panic(fmt.Errorf("BUG: cannot parse duration %q: %s", de.s, err))
@ -1617,6 +1701,9 @@ type StringExpr struct {
// AppendString appends string representation of se to dst and returns the result.
func (se *StringExpr) AppendString(dst []byte) []byte {
if len(se.tokens) > 0 {
panic(fmt.Errorf("BUG: StringExpr=%q must be already parsed with expandWithExpr()", se.tokens))
}
return strconv.AppendQuote(dst, se.S)
}
@ -2037,25 +2124,24 @@ type LabelFilter struct {
// AppendString appends string representation of me to dst and returns the result.
func (lf *LabelFilter) AppendString(dst []byte) []byte {
dst = appendEscapedIdent(dst, lf.Label)
var op string
if lf.IsNegative {
if lf.IsRegexp {
op = "!~"
} else {
op = "!="
}
} else {
if lf.IsRegexp {
op = "=~"
} else {
op = "="
}
}
dst = append(dst, op...)
dst = appendLabelFilterOp(dst, lf.IsNegative, lf.IsRegexp)
dst = strconv.AppendQuote(dst, lf.Value)
return dst
}
func appendLabelFilterOp(dst []byte, isNegative, isRegexp bool) []byte {
if isNegative {
if isRegexp {
return append(dst, "!~"...)
}
return append(dst, "!="...)
}
if isRegexp {
return append(dst, "=~"...)
}
return append(dst, '=')
}
// MetricExpr represents MetricsQL metric with optional filters, i.e. `foo{...}`.
//
// Curly braces may contain or-delimited list of filters. For example:
@ -2082,8 +2168,28 @@ type MetricExpr struct {
labelFilterss [][]*labelFilterExpr
}
func appendLabelFilterss(dst []byte, lfss [][]*labelFilterExpr) []byte {
dst = append(dst, '{')
for i, lfs := range lfss {
for j, lf := range lfs {
dst = lf.AppendString(dst)
if j+1 < len(lfs) {
dst = append(dst, ',')
}
}
if i+1 < len(lfss) {
dst = append(dst, " or "...)
}
}
dst = append(dst, '}')
return dst
}
// AppendString appends string representation of me to dst and returns the result.
func (me *MetricExpr) AppendString(dst []byte) []byte {
if len(me.labelFilterss) > 0 {
return appendLabelFilterss(dst, me.labelFilterss)
}
lfss := me.LabelFilterss
if len(lfss) == 0 {
dst = append(dst, "{}"...)

2
vendor/modules.txt vendored
View file

@ -99,7 +99,7 @@ github.com/VictoriaMetrics/fasthttp/stackless
# github.com/VictoriaMetrics/metrics v1.24.0
## explicit; go 1.20
github.com/VictoriaMetrics/metrics
# github.com/VictoriaMetrics/metricsql v0.60.0
# github.com/VictoriaMetrics/metricsql v0.61.1
## explicit; go 1.13
github.com/VictoriaMetrics/metricsql
github.com/VictoriaMetrics/metricsql/binaryop