app/vmselect/promql: support composite durations like Prometheus 2.21 does

The following durations are supported now: `1h5m35s` or `1s543ms`

See https://github.com/prometheus/prometheus/releases/tag/v2.21.0
and https://github.com/prometheus/prometheus/pull/7713
This commit is contained in:
Aliaksandr Valialkin 2020-09-11 22:15:22 +03:00
parent 0e19f35af5
commit 0e533d1a9c
5 changed files with 75 additions and 22 deletions

View file

@ -150,7 +150,7 @@ func TestExecSuccess(t *testing.T) {
resultExpected := []netstorage.Result{r} resultExpected := []netstorage.Result{r}
f(q, resultExpected) f(q, resultExpected)
}) })
t.Run("time() offset 100s", func(t *testing.T) { t.Run("time() offset 1m40s0ms", func(t *testing.T) {
t.Parallel() t.Parallel()
q := `time() offset 100s` q := `time() offset 100s`
r := netstorage.Result{ r := netstorage.Result{

2
go.mod
View file

@ -8,7 +8,7 @@ require (
// like https://github.com/valyala/fasthttp/commit/996610f021ff45fdc98c2ce7884d5fa4e7f9199b // like https://github.com/valyala/fasthttp/commit/996610f021ff45fdc98c2ce7884d5fa4e7f9199b
github.com/VictoriaMetrics/fasthttp v1.0.5 github.com/VictoriaMetrics/fasthttp v1.0.5
github.com/VictoriaMetrics/metrics v1.12.3 github.com/VictoriaMetrics/metrics v1.12.3
github.com/VictoriaMetrics/metricsql v0.5.1 github.com/VictoriaMetrics/metricsql v0.6.0
github.com/aws/aws-sdk-go v1.34.20 github.com/aws/aws-sdk-go v1.34.20
github.com/cespare/xxhash/v2 v2.1.1 github.com/cespare/xxhash/v2 v2.1.1
github.com/golang/snappy v0.0.1 github.com/golang/snappy v0.0.1

4
go.sum
View file

@ -58,8 +58,8 @@ github.com/VictoriaMetrics/metrics v1.12.2 h1:SG8iAmqavDNuh7GIdHPoGHUhDL23KeKfvS
github.com/VictoriaMetrics/metrics v1.12.2/go.mod h1:Z1tSfPfngDn12bTfZSCqArT3OPY3u88J12hSoOhuiRE= github.com/VictoriaMetrics/metrics v1.12.2/go.mod h1:Z1tSfPfngDn12bTfZSCqArT3OPY3u88J12hSoOhuiRE=
github.com/VictoriaMetrics/metrics v1.12.3 h1:Fe6JHC6MSEKa+BtLhPN8WIvS+HKPzMc2evEpNeCGy7I= github.com/VictoriaMetrics/metrics v1.12.3 h1:Fe6JHC6MSEKa+BtLhPN8WIvS+HKPzMc2evEpNeCGy7I=
github.com/VictoriaMetrics/metrics v1.12.3/go.mod h1:Z1tSfPfngDn12bTfZSCqArT3OPY3u88J12hSoOhuiRE= github.com/VictoriaMetrics/metrics v1.12.3/go.mod h1:Z1tSfPfngDn12bTfZSCqArT3OPY3u88J12hSoOhuiRE=
github.com/VictoriaMetrics/metricsql v0.5.1 h1:hG6Nck7R2/amLgHECobSTXsjZ2z9JP5J3W1sYlBoqfU= github.com/VictoriaMetrics/metricsql v0.6.0 h1:JnHUmifuA3fdy1GQrmkZJFO+CwFrhLxKwzMv89wNgJ4=
github.com/VictoriaMetrics/metricsql v0.5.1/go.mod h1:ylO7YITho/Iw6P71oEaGyHbO94bGoGtzWfLGqFhMIg8= github.com/VictoriaMetrics/metricsql v0.6.0/go.mod h1:ylO7YITho/Iw6P71oEaGyHbO94bGoGtzWfLGqFhMIg8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=

View file

@ -103,7 +103,7 @@ again:
token = s[:n] token = s[:n]
goto tokenFoundLabel goto tokenFoundLabel
} }
if n := scanDuration(s, false); n > 0 { if n := scanDuration(s); n > 0 {
token = s[:n] token = s[:n]
goto tokenFoundLabel goto tokenFoundLabel
} }
@ -393,7 +393,7 @@ func scanSpecialIntegerPrefix(s string) int {
} }
func isPositiveDuration(s string) bool { func isPositiveDuration(s string) bool {
n := scanDuration(s, false) n := scanDuration(s)
return n == len(s) return n == len(s)
} }
@ -413,33 +413,57 @@ func PositiveDurationValue(s string, step int64) (int64, error) {
// DurationValue returns the duration in milliseconds for the given s // DurationValue returns the duration in milliseconds for the given s
// and the given step. // and the given step.
// //
// Duration in s may be combined, i.e. 2h5m or 2h-5m.
//
// The returned duration value can be negative. // The returned duration value can be negative.
func DurationValue(s string, step int64) (int64, error) { func DurationValue(s string, step int64) (int64, error) {
n := scanDuration(s, true) if len(s) == 0 {
if n != len(s) { return 0, fmt.Errorf("duration cannot be empty")
return 0, fmt.Errorf("cannot parse duration %q", s)
} }
var d int64
for len(s) > 0 {
n := scanSingleDuration(s, true)
if n <= 0 {
return 0, fmt.Errorf("cannot parse duration %q", s)
}
ds := s[:n]
s = s[n:]
dLocal, err := parseSingleDuration(ds, step)
if err != nil {
return 0, err
}
d += dLocal
}
return d, nil
}
f, err := strconv.ParseFloat(s[:len(s)-1], 64) func parseSingleDuration(s string, step int64) (int64, error) {
numPart := s[:len(s)-1]
if strings.HasSuffix(numPart, "m") {
// Duration in ms
numPart = numPart[:len(numPart)-1]
}
f, err := strconv.ParseFloat(numPart, 64)
if err != nil { if err != nil {
return 0, fmt.Errorf("cannot parse duration %q: %s", s, err) return 0, fmt.Errorf("cannot parse duration %q: %s", s, err)
} }
var mp float64 var mp float64
switch s[len(s)-1] { switch s[len(numPart):] {
case 's': case "ms":
mp = 1e-3
case "s":
mp = 1 mp = 1
case 'm': case "m":
mp = 60 mp = 60
case 'h': case "h":
mp = 60 * 60 mp = 60 * 60
case 'd': case "d":
mp = 24 * 60 * 60 mp = 24 * 60 * 60
case 'w': case "w":
mp = 7 * 24 * 60 * 60 mp = 7 * 24 * 60 * 60
case 'y': case "y":
mp = 365 * 24 * 60 * 60 mp = 365 * 24 * 60 * 60
case 'i': case "i":
mp = float64(step) / 1e3 mp = float64(step) / 1e3
default: default:
return 0, fmt.Errorf("invalid duration suffix in %q", s) return 0, fmt.Errorf("invalid duration suffix in %q", s)
@ -447,7 +471,29 @@ func DurationValue(s string, step int64) (int64, error) {
return int64(mp * f * 1e3), nil return int64(mp * f * 1e3), nil
} }
func scanDuration(s string, canBeNegative bool) int { // scanDuration scans duration, which must start with positive num.
//
// I.e. 123h, 3h5m or 3.4d-35.66s
func scanDuration(s string) int {
// The first part must be non-negative
n := scanSingleDuration(s, false)
if n <= 0 {
return -1
}
s = s[n:]
i := n
for {
// Other parts may be negative
n := scanSingleDuration(s, true)
if n <= 0 {
return i
}
s = s[n:]
i += n
}
}
func scanSingleDuration(s string, canBeNegative bool) int {
if len(s) == 0 { if len(s) == 0 {
return -1 return -1
} }
@ -472,7 +518,14 @@ func scanDuration(s string, canBeNegative bool) int {
} }
} }
switch s[i] { switch s[i] {
case 's', 'm', 'h', 'd', 'w', 'y', 'i': case 'm':
if i+1 < len(s) && s[i+1] == 's' {
// duration in ms
return i + 2
}
// duration in minutes
return i + 1
case 's', 'h', 'd', 'w', 'y', 'i':
return i + 1 return i + 1
default: default:
return -1 return -1

2
vendor/modules.txt vendored
View file

@ -16,7 +16,7 @@ github.com/VictoriaMetrics/fasthttp/fasthttputil
github.com/VictoriaMetrics/fasthttp/stackless github.com/VictoriaMetrics/fasthttp/stackless
# github.com/VictoriaMetrics/metrics v1.12.3 # github.com/VictoriaMetrics/metrics v1.12.3
github.com/VictoriaMetrics/metrics github.com/VictoriaMetrics/metrics
# github.com/VictoriaMetrics/metricsql v0.5.1 # github.com/VictoriaMetrics/metricsql v0.6.0
github.com/VictoriaMetrics/metricsql github.com/VictoriaMetrics/metricsql
github.com/VictoriaMetrics/metricsql/binaryop github.com/VictoriaMetrics/metricsql/binaryop
# github.com/aws/aws-sdk-go v1.34.20 # github.com/aws/aws-sdk-go v1.34.20