mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
Implement offset operator.
This allows changing the time offset for individual instant and range vectors in a query. For example, this returns the value of `foo` 5 minutes in the past relative to the current query evaluation time: foo offset 5m Note that the `offset` modifier always needs to follow the selector immediately. I.e. the following would be correct: sum(foo offset 5m) // GOOD. While the following would be *incorrect*: sum(foo) offset 5m // INVALID. The same works for range vectors. This returns the 5-minutes-rate that `foo` had a week ago: rate(foo[5m] offset 1w) This change touches the following components: * Lexer/parser: additions to correctly parse the new `offset`/`OFFSET` keyword. * AST: vector and matrix nodes now have an additional `offset` field. This is used during their evaluation to adjust query and result times appropriately. * Query analyzer: now works on separate sets of ranges and instants per offset. Isolating different offsets from each other completely in this way keeps the preloading code relatively simple. No storage engine changes were needed by this change. The rules tests have been changed to not probe the internal implementation details of the query analyzer anymore (how many instants and ranges have been preloaded). This would also become too cumbersome to test with the new model, and measuring the result of the query should be sufficient. This fixes https://github.com/prometheus/prometheus/issues/529 This fixed https://github.com/prometheus/promdash/issues/201
This commit is contained in:
parent
79a4a6d8e8
commit
72d7b325a1
|
@ -215,6 +215,7 @@ type (
|
||||||
// A VectorSelector represents a metric name plus labelset.
|
// A VectorSelector represents a metric name plus labelset.
|
||||||
VectorSelector struct {
|
VectorSelector struct {
|
||||||
labelMatchers metric.LabelMatchers
|
labelMatchers metric.LabelMatchers
|
||||||
|
offset time.Duration
|
||||||
// The series iterators are populated at query analysis time.
|
// The series iterators are populated at query analysis time.
|
||||||
iterators map[clientmodel.Fingerprint]local.SeriesIterator
|
iterators map[clientmodel.Fingerprint]local.SeriesIterator
|
||||||
metrics map[clientmodel.Fingerprint]clientmodel.COWMetric
|
metrics map[clientmodel.Fingerprint]clientmodel.COWMetric
|
||||||
|
@ -261,6 +262,7 @@ type (
|
||||||
// Fingerprints are populated from label matchers at query analysis time.
|
// Fingerprints are populated from label matchers at query analysis time.
|
||||||
fingerprints clientmodel.Fingerprints
|
fingerprints clientmodel.Fingerprints
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
|
offset time.Duration
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -561,8 +563,8 @@ func (node *VectorSelector) Eval(timestamp clientmodel.Timestamp) Vector {
|
||||||
//// timer := v.stats.GetTimer(stats.GetValueAtTimeTime).Start()
|
//// timer := v.stats.GetTimer(stats.GetValueAtTimeTime).Start()
|
||||||
samples := Vector{}
|
samples := Vector{}
|
||||||
for fp, it := range node.iterators {
|
for fp, it := range node.iterators {
|
||||||
sampleCandidates := it.GetValueAtTime(timestamp)
|
sampleCandidates := it.GetValueAtTime(timestamp.Add(-node.offset))
|
||||||
samplePair := chooseClosestSample(sampleCandidates, timestamp)
|
samplePair := chooseClosestSample(sampleCandidates, timestamp.Add(-node.offset))
|
||||||
if samplePair != nil {
|
if samplePair != nil {
|
||||||
samples = append(samples, &Sample{
|
samples = append(samples, &Sample{
|
||||||
Metric: node.metrics[fp],
|
Metric: node.metrics[fp],
|
||||||
|
@ -823,8 +825,8 @@ func (node *VectorArithExpr) Eval(timestamp clientmodel.Timestamp) Vector {
|
||||||
// the selector.
|
// the selector.
|
||||||
func (node *MatrixSelector) Eval(timestamp clientmodel.Timestamp) Matrix {
|
func (node *MatrixSelector) Eval(timestamp clientmodel.Timestamp) Matrix {
|
||||||
interval := &metric.Interval{
|
interval := &metric.Interval{
|
||||||
OldestInclusive: timestamp.Add(-node.interval),
|
OldestInclusive: timestamp.Add(-node.interval - node.offset),
|
||||||
NewestInclusive: timestamp,
|
NewestInclusive: timestamp.Add(-node.offset),
|
||||||
}
|
}
|
||||||
|
|
||||||
//// timer := v.stats.GetTimer(stats.GetRangeValuesTime).Start()
|
//// timer := v.stats.GetTimer(stats.GetRangeValuesTime).Start()
|
||||||
|
@ -835,6 +837,12 @@ func (node *MatrixSelector) Eval(timestamp clientmodel.Timestamp) Matrix {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if node.offset != 0 {
|
||||||
|
for _, sp := range samplePairs {
|
||||||
|
sp.Timestamp = sp.Timestamp.Add(node.offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sampleStream := SampleStream{
|
sampleStream := SampleStream{
|
||||||
Metric: node.metrics[fp],
|
Metric: node.metrics[fp],
|
||||||
Values: samplePairs,
|
Values: samplePairs,
|
||||||
|
@ -910,9 +918,10 @@ func NewScalarLiteral(value clientmodel.SampleValue) *ScalarLiteral {
|
||||||
|
|
||||||
// NewVectorSelector returns a (not yet evaluated) VectorSelector with
|
// NewVectorSelector returns a (not yet evaluated) VectorSelector with
|
||||||
// the given LabelSet.
|
// the given LabelSet.
|
||||||
func NewVectorSelector(m metric.LabelMatchers) *VectorSelector {
|
func NewVectorSelector(m metric.LabelMatchers, offset time.Duration) *VectorSelector {
|
||||||
return &VectorSelector{
|
return &VectorSelector{
|
||||||
labelMatchers: m,
|
labelMatchers: m,
|
||||||
|
offset: offset,
|
||||||
iterators: map[clientmodel.Fingerprint]local.SeriesIterator{},
|
iterators: map[clientmodel.Fingerprint]local.SeriesIterator{},
|
||||||
metrics: map[clientmodel.Fingerprint]clientmodel.COWMetric{},
|
metrics: map[clientmodel.Fingerprint]clientmodel.COWMetric{},
|
||||||
}
|
}
|
||||||
|
@ -1002,10 +1011,11 @@ func NewArithExpr(opType BinOpType, lhs Node, rhs Node) (Node, error) {
|
||||||
|
|
||||||
// NewMatrixSelector returns a (not yet evaluated) MatrixSelector with
|
// NewMatrixSelector returns a (not yet evaluated) MatrixSelector with
|
||||||
// the given VectorSelector and Duration.
|
// the given VectorSelector and Duration.
|
||||||
func NewMatrixSelector(vector *VectorSelector, interval time.Duration) *MatrixSelector {
|
func NewMatrixSelector(vector *VectorSelector, interval time.Duration, offset time.Duration) *MatrixSelector {
|
||||||
return &MatrixSelector{
|
return &MatrixSelector{
|
||||||
labelMatchers: vector.labelMatchers,
|
labelMatchers: vector.labelMatchers,
|
||||||
interval: interval,
|
interval: interval,
|
||||||
|
offset: offset,
|
||||||
iterators: map[clientmodel.Fingerprint]local.SeriesIterator{},
|
iterators: map[clientmodel.Fingerprint]local.SeriesIterator{},
|
||||||
metrics: map[clientmodel.Fingerprint]clientmodel.COWMetric{},
|
metrics: map[clientmodel.Fingerprint]clientmodel.COWMetric{},
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,28 +22,27 @@ import (
|
||||||
"github.com/prometheus/prometheus/storage/local"
|
"github.com/prometheus/prometheus/storage/local"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FullRangeMap maps the fingerprint of a full range to the duration
|
// preloadTimes tracks which instants or ranges to preload for a set of
|
||||||
// of the matrix selector it resulted from.
|
// fingerprints. One of these structs is collected for each offset by the query
|
||||||
type FullRangeMap map[clientmodel.Fingerprint]time.Duration
|
// analyzer.
|
||||||
|
type preloadTimes struct {
|
||||||
// IntervalRangeMap is a set of fingerprints of interval ranges.
|
// Instants require single samples to be loaded along the entire query
|
||||||
type IntervalRangeMap map[clientmodel.Fingerprint]bool
|
// range, with intervals between the samples corresponding to the query
|
||||||
|
// resolution.
|
||||||
|
instants map[clientmodel.Fingerprint]struct{}
|
||||||
|
// Ranges require loading a range of samples at each resolution step,
|
||||||
|
// stretching backwards from the current evaluation timestamp. The length of
|
||||||
|
// the range into the past is given by the duration, as in "foo[5m]".
|
||||||
|
ranges map[clientmodel.Fingerprint]time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
// A QueryAnalyzer recursively traverses the AST to look for any nodes
|
// A QueryAnalyzer recursively traverses the AST to look for any nodes
|
||||||
// which will need data from the datastore. Instantiate with
|
// which will need data from the datastore. Instantiate with
|
||||||
// NewQueryAnalyzer.
|
// NewQueryAnalyzer.
|
||||||
type QueryAnalyzer struct {
|
type QueryAnalyzer struct {
|
||||||
// Values collected by query analysis.
|
// Tracks one set of times to preload per offset that occurs in the query
|
||||||
//
|
// expression.
|
||||||
// Full ranges always implicitly span a time range of:
|
offsetPreloadTimes map[time.Duration]preloadTimes
|
||||||
// - start: query interval start - duration
|
|
||||||
// - end: query interval end
|
|
||||||
//
|
|
||||||
// This is because full ranges can only result from matrix selectors (like
|
|
||||||
// "foo[5m]"), which have said time-spanning behavior during a ranged query.
|
|
||||||
FullRanges FullRangeMap
|
|
||||||
// Interval ranges always implicitly span the whole query range.
|
|
||||||
IntervalRanges IntervalRangeMap
|
|
||||||
// The underlying storage to which the query will be applied. Needed for
|
// The underlying storage to which the query will be applied. Needed for
|
||||||
// extracting timeseries fingerprint information during query analysis.
|
// extracting timeseries fingerprint information during query analysis.
|
||||||
storage local.Storage
|
storage local.Storage
|
||||||
|
@ -54,37 +53,49 @@ type QueryAnalyzer struct {
|
||||||
// fingerprint information during query analysis.
|
// fingerprint information during query analysis.
|
||||||
func NewQueryAnalyzer(storage local.Storage) *QueryAnalyzer {
|
func NewQueryAnalyzer(storage local.Storage) *QueryAnalyzer {
|
||||||
return &QueryAnalyzer{
|
return &QueryAnalyzer{
|
||||||
FullRanges: FullRangeMap{},
|
offsetPreloadTimes: map[time.Duration]preloadTimes{},
|
||||||
IntervalRanges: IntervalRangeMap{},
|
storage: storage,
|
||||||
storage: storage,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (analyzer *QueryAnalyzer) getPreloadTimes(offset time.Duration) preloadTimes {
|
||||||
|
if _, ok := analyzer.offsetPreloadTimes[offset]; !ok {
|
||||||
|
analyzer.offsetPreloadTimes[offset] = preloadTimes{
|
||||||
|
instants: map[clientmodel.Fingerprint]struct{}{},
|
||||||
|
ranges: map[clientmodel.Fingerprint]time.Duration{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return analyzer.offsetPreloadTimes[offset]
|
||||||
|
}
|
||||||
|
|
||||||
// Visit implements the Visitor interface.
|
// Visit implements the Visitor interface.
|
||||||
func (analyzer *QueryAnalyzer) Visit(node Node) {
|
func (analyzer *QueryAnalyzer) Visit(node Node) {
|
||||||
switch n := node.(type) {
|
switch n := node.(type) {
|
||||||
case *VectorSelector:
|
case *VectorSelector:
|
||||||
|
pt := analyzer.getPreloadTimes(n.offset)
|
||||||
fingerprints := analyzer.storage.GetFingerprintsForLabelMatchers(n.labelMatchers)
|
fingerprints := analyzer.storage.GetFingerprintsForLabelMatchers(n.labelMatchers)
|
||||||
n.fingerprints = fingerprints
|
n.fingerprints = fingerprints
|
||||||
for _, fp := range fingerprints {
|
for _, fp := range fingerprints {
|
||||||
// Only add the fingerprint to IntervalRanges if not yet present in FullRanges.
|
// Only add the fingerprint to the instants if not yet present in the
|
||||||
// Full ranges always contain more points and span more time than interval ranges.
|
// ranges. Ranges always contain more points and span more time than
|
||||||
if _, alreadyInFullRanges := analyzer.FullRanges[fp]; !alreadyInFullRanges {
|
// instants for the same offset.
|
||||||
analyzer.IntervalRanges[fp] = true
|
if _, alreadyInRanges := pt.ranges[fp]; !alreadyInRanges {
|
||||||
|
pt.instants[fp] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
n.metrics[fp] = analyzer.storage.GetMetricForFingerprint(fp)
|
n.metrics[fp] = analyzer.storage.GetMetricForFingerprint(fp)
|
||||||
}
|
}
|
||||||
case *MatrixSelector:
|
case *MatrixSelector:
|
||||||
|
pt := analyzer.getPreloadTimes(n.offset)
|
||||||
fingerprints := analyzer.storage.GetFingerprintsForLabelMatchers(n.labelMatchers)
|
fingerprints := analyzer.storage.GetFingerprintsForLabelMatchers(n.labelMatchers)
|
||||||
n.fingerprints = fingerprints
|
n.fingerprints = fingerprints
|
||||||
for _, fp := range fingerprints {
|
for _, fp := range fingerprints {
|
||||||
if analyzer.FullRanges[fp] < n.interval {
|
if pt.ranges[fp] < n.interval {
|
||||||
analyzer.FullRanges[fp] = n.interval
|
pt.ranges[fp] = n.interval
|
||||||
// Delete the fingerprint from IntervalRanges. Full ranges always contain
|
// Delete the fingerprint from the instants. Ranges always contain more
|
||||||
// more points and span more time than interval ranges, so we don't need
|
// points and span more time than instants, so we don't need to track
|
||||||
// an interval range for the same fingerprint, should we have one.
|
// an instant for the same fingerprint, should we have one.
|
||||||
delete(analyzer.IntervalRanges, fp)
|
delete(pt.instants, fp)
|
||||||
}
|
}
|
||||||
|
|
||||||
n.metrics[fp] = analyzer.storage.GetMetricForFingerprint(fp)
|
n.metrics[fp] = analyzer.storage.GetMetricForFingerprint(fp)
|
||||||
|
@ -119,28 +130,31 @@ func prepareInstantQuery(node Node, timestamp clientmodel.Timestamp, storage loc
|
||||||
|
|
||||||
preloadTimer := queryStats.GetTimer(stats.PreloadTime).Start()
|
preloadTimer := queryStats.GetTimer(stats.PreloadTime).Start()
|
||||||
p := storage.NewPreloader()
|
p := storage.NewPreloader()
|
||||||
for fp, rangeDuration := range analyzer.FullRanges {
|
for offset, pt := range analyzer.offsetPreloadTimes {
|
||||||
if et := totalTimer.ElapsedTime(); et > *queryTimeout {
|
ts := timestamp.Add(-offset)
|
||||||
preloadTimer.Stop()
|
for fp, rangeDuration := range pt.ranges {
|
||||||
p.Close()
|
if et := totalTimer.ElapsedTime(); et > *queryTimeout {
|
||||||
return nil, queryTimeoutError{et}
|
preloadTimer.Stop()
|
||||||
|
p.Close()
|
||||||
|
return nil, queryTimeoutError{et}
|
||||||
|
}
|
||||||
|
if err := p.PreloadRange(fp, ts.Add(-rangeDuration), ts, *stalenessDelta); err != nil {
|
||||||
|
preloadTimer.Stop()
|
||||||
|
p.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err := p.PreloadRange(fp, timestamp.Add(-rangeDuration), timestamp, *stalenessDelta); err != nil {
|
for fp := range pt.instants {
|
||||||
preloadTimer.Stop()
|
if et := totalTimer.ElapsedTime(); et > *queryTimeout {
|
||||||
p.Close()
|
preloadTimer.Stop()
|
||||||
return nil, err
|
p.Close()
|
||||||
}
|
return nil, queryTimeoutError{et}
|
||||||
}
|
}
|
||||||
for fp := range analyzer.IntervalRanges {
|
if err := p.PreloadRange(fp, ts, ts, *stalenessDelta); err != nil {
|
||||||
if et := totalTimer.ElapsedTime(); et > *queryTimeout {
|
preloadTimer.Stop()
|
||||||
preloadTimer.Stop()
|
p.Close()
|
||||||
p.Close()
|
return nil, err
|
||||||
return nil, queryTimeoutError{et}
|
}
|
||||||
}
|
|
||||||
if err := p.PreloadRange(fp, timestamp, timestamp, *stalenessDelta); err != nil {
|
|
||||||
preloadTimer.Stop()
|
|
||||||
p.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
preloadTimer.Stop()
|
preloadTimer.Stop()
|
||||||
|
@ -163,41 +177,45 @@ func prepareRangeQuery(node Node, start clientmodel.Timestamp, end clientmodel.T
|
||||||
|
|
||||||
preloadTimer := queryStats.GetTimer(stats.PreloadTime).Start()
|
preloadTimer := queryStats.GetTimer(stats.PreloadTime).Start()
|
||||||
p := storage.NewPreloader()
|
p := storage.NewPreloader()
|
||||||
for fp, rangeDuration := range analyzer.FullRanges {
|
for offset, pt := range analyzer.offsetPreloadTimes {
|
||||||
if et := totalTimer.ElapsedTime(); et > *queryTimeout {
|
offsetStart := start.Add(-offset)
|
||||||
preloadTimer.Stop()
|
offsetEnd := end.Add(-offset)
|
||||||
p.Close()
|
for fp, rangeDuration := range pt.ranges {
|
||||||
return nil, queryTimeoutError{et}
|
if et := totalTimer.ElapsedTime(); et > *queryTimeout {
|
||||||
}
|
preloadTimer.Stop()
|
||||||
if err := p.PreloadRange(fp, start.Add(-rangeDuration), end, *stalenessDelta); err != nil {
|
p.Close()
|
||||||
preloadTimer.Stop()
|
return nil, queryTimeoutError{et}
|
||||||
p.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
if interval < rangeDuration {
|
|
||||||
if err := p.GetMetricRange(fp, end, end.Sub(start)+rangeDuration); err != nil {
|
|
||||||
p.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := p.GetMetricRangeAtInterval(fp, start, end, interval, rangeDuration); err != nil {
|
|
||||||
p.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
*/
|
if err := p.PreloadRange(fp, offsetStart.Add(-rangeDuration), offsetEnd, *stalenessDelta); err != nil {
|
||||||
}
|
preloadTimer.Stop()
|
||||||
for fp := range analyzer.IntervalRanges {
|
p.Close()
|
||||||
if et := totalTimer.ElapsedTime(); et > *queryTimeout {
|
return nil, err
|
||||||
preloadTimer.Stop()
|
}
|
||||||
p.Close()
|
/*
|
||||||
return nil, queryTimeoutError{et}
|
if interval < rangeDuration {
|
||||||
|
if err := p.GetMetricRange(fp, offsetEnd, offsetEnd.Sub(offsetStart)+rangeDuration); err != nil {
|
||||||
|
p.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := p.GetMetricRangeAtInterval(fp, offsetStart, offsetEnd, interval, rangeDuration); err != nil {
|
||||||
|
p.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
if err := p.PreloadRange(fp, start, end, *stalenessDelta); err != nil {
|
for fp := range pt.instants {
|
||||||
preloadTimer.Stop()
|
if et := totalTimer.ElapsedTime(); et > *queryTimeout {
|
||||||
p.Close()
|
preloadTimer.Stop()
|
||||||
return nil, err
|
p.Close()
|
||||||
|
return nil, queryTimeoutError{et}
|
||||||
|
}
|
||||||
|
if err := p.PreloadRange(fp, offsetStart, offsetEnd, *stalenessDelta); err != nil {
|
||||||
|
preloadTimer.Stop()
|
||||||
|
p.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
preloadTimer.Stop()
|
preloadTimer.Stop()
|
||||||
|
|
|
@ -110,22 +110,30 @@ func NewArithExpr(opTypeStr string, lhs ast.Node, rhs ast.Node) (ast.Node, error
|
||||||
return expr, nil
|
return expr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMatrixSelector is a convenience function to create a new AST matrix selector.
|
// NewVectorSelector is a convenience function to create a new AST vector selector.
|
||||||
func NewMatrixSelector(vector ast.Node, intervalStr string) (ast.MatrixNode, error) {
|
func NewVectorSelector(m metric.LabelMatchers, offsetStr string) (ast.VectorNode, error) {
|
||||||
switch vector.(type) {
|
offset, err := utility.StringToDuration(offsetStr)
|
||||||
case *ast.VectorSelector:
|
if err != nil {
|
||||||
{
|
return nil, err
|
||||||
break
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("intervals are currently only supported for vector selectors")
|
|
||||||
}
|
}
|
||||||
|
return ast.NewVectorSelector(m, offset), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMatrixSelector is a convenience function to create a new AST matrix selector.
|
||||||
|
func NewMatrixSelector(vector ast.Node, intervalStr string, offsetStr string) (ast.MatrixNode, error) {
|
||||||
interval, err := utility.StringToDuration(intervalStr)
|
interval, err := utility.StringToDuration(intervalStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
vectorSelector := vector.(*ast.VectorSelector)
|
offset, err := utility.StringToDuration(offsetStr)
|
||||||
return ast.NewMatrixSelector(vectorSelector, interval), nil
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
vectorSelector, ok := vector.(*ast.VectorSelector)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("intervals are currently only supported for vector selectors")
|
||||||
|
}
|
||||||
|
return ast.NewMatrixSelector(vectorSelector, interval, offset), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLabelMatcher(matchTypeStr string, name clientmodel.LabelName, value clientmodel.LabelValue) (*metric.LabelMatcher, error) {
|
func newLabelMatcher(matchTypeStr string, name clientmodel.LabelName, value clientmodel.LabelValue) (*metric.LabelMatcher, error) {
|
||||||
|
|
|
@ -80,6 +80,7 @@ DESCRIPTION|description return DESCRIPTION
|
||||||
PERMANENT|permanent return PERMANENT
|
PERMANENT|permanent return PERMANENT
|
||||||
BY|by return GROUP_OP
|
BY|by return GROUP_OP
|
||||||
KEEPING_EXTRA|keeping_extra return KEEPING_EXTRA
|
KEEPING_EXTRA|keeping_extra return KEEPING_EXTRA
|
||||||
|
OFFSET|offset return OFFSET
|
||||||
AVG|SUM|MAX|MIN|COUNT lval.str = lexer.token(); return AGGR_OP
|
AVG|SUM|MAX|MIN|COUNT lval.str = lexer.token(); return AGGR_OP
|
||||||
avg|sum|max|min|count lval.str = strings.ToUpper(lexer.token()); return AGGR_OP
|
avg|sum|max|min|count lval.str = strings.ToUpper(lexer.token()); return AGGR_OP
|
||||||
\<|>|AND|OR|and|or lval.str = strings.ToUpper(lexer.token()); return CMP_OP
|
\<|>|AND|OR|and|or lval.str = strings.ToUpper(lexer.token()); return CMP_OP
|
||||||
|
|
1081
rules/lexer.l.go
1081
rules/lexer.l.go
File diff suppressed because it is too large
Load diff
|
@ -42,7 +42,7 @@
|
||||||
|
|
||||||
%token <str> IDENTIFIER STRING DURATION METRICNAME
|
%token <str> IDENTIFIER STRING DURATION METRICNAME
|
||||||
%token <num> NUMBER
|
%token <num> NUMBER
|
||||||
%token PERMANENT GROUP_OP KEEPING_EXTRA
|
%token PERMANENT GROUP_OP KEEPING_EXTRA OFFSET
|
||||||
%token <str> AGGR_OP CMP_OP ADDITIVE_OP MULT_OP
|
%token <str> AGGR_OP CMP_OP ADDITIVE_OP MULT_OP
|
||||||
%token ALERT IF FOR WITH SUMMARY DESCRIPTION
|
%token ALERT IF FOR WITH SUMMARY DESCRIPTION
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@
|
||||||
%type <labelMatchers> label_match_list label_matches
|
%type <labelMatchers> label_match_list label_matches
|
||||||
%type <ruleNode> rule_expr func_arg
|
%type <ruleNode> rule_expr func_arg
|
||||||
%type <boolean> qualifier extra_labels_opts
|
%type <boolean> qualifier extra_labels_opts
|
||||||
%type <str> for_duration metric_name label_match_type
|
%type <str> for_duration metric_name label_match_type offset_opts
|
||||||
|
|
||||||
%right '='
|
%right '='
|
||||||
%left CMP_OP
|
%left CMP_OP
|
||||||
|
@ -152,17 +152,28 @@ label_match_type : '='
|
||||||
{ $$ = $1 }
|
{ $$ = $1 }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
offset_opts : /* empty */
|
||||||
|
{ $$ = "0s" }
|
||||||
|
| OFFSET DURATION
|
||||||
|
{ $$ = $2 }
|
||||||
|
;
|
||||||
|
|
||||||
rule_expr : '(' rule_expr ')'
|
rule_expr : '(' rule_expr ')'
|
||||||
{ $$ = $2 }
|
{ $$ = $2 }
|
||||||
| '{' label_match_list '}'
|
| '{' label_match_list '}' offset_opts
|
||||||
{ $$ = ast.NewVectorSelector($2) }
|
{
|
||||||
| metric_name label_matches
|
var err error
|
||||||
|
$$, err = NewVectorSelector($2, $4)
|
||||||
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
|
}
|
||||||
|
| metric_name label_matches offset_opts
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
m, err := metric.NewLabelMatcher(metric.Equal, clientmodel.MetricNameLabel, clientmodel.LabelValue($1))
|
m, err := metric.NewLabelMatcher(metric.Equal, clientmodel.MetricNameLabel, clientmodel.LabelValue($1))
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
$2 = append($2, m)
|
$2 = append($2, m)
|
||||||
$$ = ast.NewVectorSelector($2)
|
$$, err = NewVectorSelector($2, $3)
|
||||||
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
}
|
}
|
||||||
| IDENTIFIER '(' func_arg_list ')'
|
| IDENTIFIER '(' func_arg_list ')'
|
||||||
{
|
{
|
||||||
|
@ -176,10 +187,10 @@ rule_expr : '(' rule_expr ')'
|
||||||
$$, err = NewFunctionCall($1, []ast.Node{})
|
$$, err = NewFunctionCall($1, []ast.Node{})
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
}
|
}
|
||||||
| rule_expr '[' DURATION ']'
|
| rule_expr '[' DURATION ']' offset_opts
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
$$, err = NewMatrixSelector($1, $3)
|
$$, err = NewMatrixSelector($1, $3, $5)
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
}
|
}
|
||||||
| AGGR_OP '(' rule_expr ')' grouping_opts extra_labels_opts
|
| AGGR_OP '(' rule_expr ')' grouping_opts extra_labels_opts
|
||||||
|
|
|
@ -35,16 +35,17 @@ const NUMBER = 57352
|
||||||
const PERMANENT = 57353
|
const PERMANENT = 57353
|
||||||
const GROUP_OP = 57354
|
const GROUP_OP = 57354
|
||||||
const KEEPING_EXTRA = 57355
|
const KEEPING_EXTRA = 57355
|
||||||
const AGGR_OP = 57356
|
const OFFSET = 57356
|
||||||
const CMP_OP = 57357
|
const AGGR_OP = 57357
|
||||||
const ADDITIVE_OP = 57358
|
const CMP_OP = 57358
|
||||||
const MULT_OP = 57359
|
const ADDITIVE_OP = 57359
|
||||||
const ALERT = 57360
|
const MULT_OP = 57360
|
||||||
const IF = 57361
|
const ALERT = 57361
|
||||||
const FOR = 57362
|
const IF = 57362
|
||||||
const WITH = 57363
|
const FOR = 57363
|
||||||
const SUMMARY = 57364
|
const WITH = 57364
|
||||||
const DESCRIPTION = 57365
|
const SUMMARY = 57365
|
||||||
|
const DESCRIPTION = 57366
|
||||||
|
|
||||||
var yyToknames = []string{
|
var yyToknames = []string{
|
||||||
"START_RULES",
|
"START_RULES",
|
||||||
|
@ -57,6 +58,7 @@ var yyToknames = []string{
|
||||||
"PERMANENT",
|
"PERMANENT",
|
||||||
"GROUP_OP",
|
"GROUP_OP",
|
||||||
"KEEPING_EXTRA",
|
"KEEPING_EXTRA",
|
||||||
|
"OFFSET",
|
||||||
"AGGR_OP",
|
"AGGR_OP",
|
||||||
"CMP_OP",
|
"CMP_OP",
|
||||||
"ADDITIVE_OP",
|
"ADDITIVE_OP",
|
||||||
|
@ -75,7 +77,7 @@ const yyEofCode = 1
|
||||||
const yyErrCode = 2
|
const yyErrCode = 2
|
||||||
const yyMaxDepth = 200
|
const yyMaxDepth = 200
|
||||||
|
|
||||||
//line parser.y:250
|
//line parser.y:261
|
||||||
|
|
||||||
//line yacctab:1
|
//line yacctab:1
|
||||||
var yyExca = []int{
|
var yyExca = []int{
|
||||||
|
@ -87,91 +89,97 @@ var yyExca = []int{
|
||||||
-2, 10,
|
-2, 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
const yyNprod = 50
|
const yyNprod = 52
|
||||||
const yyPrivate = 57344
|
const yyPrivate = 57344
|
||||||
|
|
||||||
var yyTokenNames []string
|
var yyTokenNames []string
|
||||||
var yyStates []string
|
var yyStates []string
|
||||||
|
|
||||||
const yyLast = 137
|
const yyLast = 142
|
||||||
|
|
||||||
var yyAct = []int{
|
var yyAct = []int{
|
||||||
|
|
||||||
56, 72, 50, 53, 30, 24, 6, 20, 49, 59,
|
58, 76, 55, 52, 51, 30, 45, 6, 24, 20,
|
||||||
22, 10, 51, 18, 13, 12, 21, 19, 20, 11,
|
61, 22, 10, 53, 18, 13, 12, 84, 68, 83,
|
||||||
18, 85, 36, 37, 38, 21, 19, 20, 81, 82,
|
67, 11, 18, 36, 37, 38, 21, 19, 20, 21,
|
||||||
8, 18, 52, 7, 48, 66, 21, 19, 20, 87,
|
19, 20, 8, 54, 90, 7, 50, 21, 19, 20,
|
||||||
18, 10, 51, 31, 13, 12, 60, 55, 28, 11,
|
92, 18, 10, 53, 18, 13, 12, 70, 63, 62,
|
||||||
65, 18, 21, 19, 20, 57, 21, 19, 20, 29,
|
88, 11, 18, 57, 31, 21, 19, 20, 86, 87,
|
||||||
8, 74, 23, 7, 62, 40, 39, 18, 73, 77,
|
66, 40, 8, 10, 78, 7, 13, 12, 79, 69,
|
||||||
76, 18, 80, 75, 10, 19, 20, 13, 12, 79,
|
18, 29, 11, 80, 82, 81, 28, 85, 21, 19,
|
||||||
86, 78, 11, 64, 89, 63, 41, 40, 71, 18,
|
20, 19, 20, 8, 91, 77, 7, 41, 40, 94,
|
||||||
46, 25, 93, 8, 44, 27, 7, 83, 69, 96,
|
25, 23, 39, 18, 59, 18, 44, 98, 27, 73,
|
||||||
94, 91, 58, 43, 9, 17, 54, 31, 92, 35,
|
101, 99, 60, 96, 17, 43, 75, 9, 46, 56,
|
||||||
33, 45, 16, 13, 97, 95, 90, 61, 73, 88,
|
31, 47, 16, 33, 97, 102, 13, 65, 35, 48,
|
||||||
32, 68, 25, 34, 2, 3, 14, 5, 4, 1,
|
100, 95, 64, 32, 77, 93, 72, 25, 34, 2,
|
||||||
42, 84, 15, 26, 70, 67, 47,
|
3, 14, 5, 4, 1, 42, 89, 15, 26, 74,
|
||||||
|
71, 49,
|
||||||
}
|
}
|
||||||
var yyPact = []int{
|
var yyPact = []int{
|
||||||
|
|
||||||
120, -1000, -1000, 68, 94, -1000, 41, 68, 116, 70,
|
125, -1000, -1000, 57, 93, -1000, 21, 57, 121, 72,
|
||||||
20, 31, -1000, -1000, -1000, 104, 117, -1000, 101, 68,
|
47, 42, -1000, -1000, -1000, 107, 122, -1000, 110, 57,
|
||||||
68, 68, 37, 60, -1000, 79, -1000, 85, 5, 68,
|
57, 57, 62, 60, -1000, 80, 94, 84, 6, 57,
|
||||||
93, 19, 30, -1000, 83, -22, -10, -17, 59, -1000,
|
96, 24, 68, -1000, 82, -22, -9, -17, 64, -1000,
|
||||||
116, -1000, 110, -1000, -1000, -1000, 38, 56, -1000, -1000,
|
121, 94, 115, -1000, -1000, -1000, 109, -1000, 33, -10,
|
||||||
41, -1000, 21, 7, -1000, 115, 74, 62, 68, -1000,
|
-1000, -1000, 21, -1000, 39, 18, -1000, 120, 74, 79,
|
||||||
-1000, -1000, -1000, -1000, 35, 95, 68, 52, -1000, 68,
|
57, 94, -1000, -1000, -1000, -1000, -1000, -1000, 36, 98,
|
||||||
2, -1000, -1000, 73, 1, -1000, 93, 10, -1000, 113,
|
57, -11, -1000, 57, 31, -1000, -1000, 25, 13, -1000,
|
||||||
41, -1000, 112, 109, 80, 100, -1000, -1000, -1000, -1000,
|
-1000, 96, 10, -1000, 119, 21, -1000, 118, 114, 81,
|
||||||
-1000, 30, -1000, 78, 108, 76, 107, -1000,
|
106, -1000, -1000, -1000, -1000, -1000, 68, -1000, 78, 113,
|
||||||
|
76, 108, -1000,
|
||||||
}
|
}
|
||||||
var yyPgo = []int{
|
var yyPgo = []int{
|
||||||
|
|
||||||
0, 136, 135, 4, 1, 134, 0, 5, 62, 133,
|
0, 141, 140, 5, 1, 139, 0, 8, 91, 138,
|
||||||
2, 8, 132, 3, 131, 104, 130, 129, 128, 127,
|
3, 4, 137, 2, 136, 107, 135, 6, 134, 133,
|
||||||
126,
|
132, 131,
|
||||||
}
|
}
|
||||||
var yyR1 = []int{
|
var yyR1 = []int{
|
||||||
|
|
||||||
0, 17, 17, 18, 18, 19, 20, 20, 14, 14,
|
0, 18, 18, 19, 19, 20, 21, 21, 14, 14,
|
||||||
12, 12, 15, 15, 6, 6, 6, 5, 5, 4,
|
12, 12, 15, 15, 6, 6, 6, 5, 5, 4,
|
||||||
9, 9, 9, 8, 8, 7, 16, 16, 10, 10,
|
9, 9, 9, 8, 8, 7, 16, 16, 17, 17,
|
||||||
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
|
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
|
||||||
13, 13, 3, 3, 2, 2, 1, 1, 11, 11,
|
10, 10, 13, 13, 3, 3, 2, 2, 1, 1,
|
||||||
|
11, 11,
|
||||||
}
|
}
|
||||||
var yyR2 = []int{
|
var yyR2 = []int{
|
||||||
|
|
||||||
0, 2, 2, 0, 2, 1, 5, 11, 0, 2,
|
0, 2, 2, 0, 2, 1, 5, 11, 0, 2,
|
||||||
0, 1, 1, 1, 0, 3, 2, 1, 3, 3,
|
0, 1, 1, 1, 0, 3, 2, 1, 3, 3,
|
||||||
0, 2, 3, 1, 3, 3, 1, 1, 3, 3,
|
0, 2, 3, 1, 3, 3, 1, 1, 0, 2,
|
||||||
2, 4, 3, 4, 6, 6, 3, 3, 3, 1,
|
3, 4, 3, 4, 3, 5, 6, 6, 3, 3,
|
||||||
0, 1, 0, 4, 1, 3, 1, 3, 1, 1,
|
3, 1, 0, 1, 0, 4, 1, 3, 1, 3,
|
||||||
|
1, 1,
|
||||||
}
|
}
|
||||||
var yyChk = []int{
|
var yyChk = []int{
|
||||||
|
|
||||||
-1000, -17, 4, 5, -18, -19, -10, 28, 25, -15,
|
-1000, -18, 4, 5, -19, -20, -10, 29, 26, -15,
|
||||||
6, 14, 10, 9, -20, -12, 18, 11, 30, 16,
|
6, 15, 10, 9, -21, -12, 19, 11, 31, 17,
|
||||||
17, 15, -10, -8, -7, 6, -9, 25, 28, 28,
|
18, 16, -10, -8, -7, 6, -9, 26, 29, 29,
|
||||||
-3, 12, -15, 6, 6, 8, -10, -10, -10, 29,
|
-3, 12, -15, 6, 6, 8, -10, -10, -10, 30,
|
||||||
27, 26, -16, 24, 15, 26, -8, -1, 29, -11,
|
28, 27, -16, 25, 16, -17, 14, 27, -8, -1,
|
||||||
-10, 7, -10, -13, 13, 28, -6, 25, 19, 31,
|
30, -11, -10, 7, -10, -13, 13, 29, -6, 26,
|
||||||
-7, 7, 26, 29, 27, 29, 28, -2, 6, 24,
|
20, 32, -7, -17, 7, 8, 27, 30, 28, 30,
|
||||||
-5, 26, -4, 6, -10, -11, -3, -10, 29, 27,
|
29, -2, 6, 25, -5, 27, -4, 6, -10, -17,
|
||||||
-10, 26, 27, 24, -14, 20, -13, 29, 6, -4,
|
-11, -3, -10, 30, 28, -10, 27, 28, 25, -14,
|
||||||
7, 21, 8, -6, 22, 7, 23, 7,
|
21, -13, 30, 6, -4, 7, 22, 8, -6, 23,
|
||||||
|
7, 24, 7,
|
||||||
}
|
}
|
||||||
var yyDef = []int{
|
var yyDef = []int{
|
||||||
|
|
||||||
0, -2, 3, 0, -2, 2, 5, 0, 0, 20,
|
0, -2, 3, 0, -2, 2, 5, 0, 0, 20,
|
||||||
13, 42, 39, 12, 4, 0, 0, 11, 0, 0,
|
13, 44, 41, 12, 4, 0, 0, 11, 0, 0,
|
||||||
0, 0, 0, 0, 23, 0, 30, 0, 0, 0,
|
0, 0, 0, 0, 23, 0, 28, 0, 0, 0,
|
||||||
40, 0, 14, 13, 0, 0, 36, 37, 38, 28,
|
42, 0, 14, 13, 0, 0, 38, 39, 40, 30,
|
||||||
0, 29, 0, 26, 27, 21, 0, 0, 32, 46,
|
0, 28, 0, 26, 27, 32, 0, 21, 0, 0,
|
||||||
48, 49, 0, 0, 41, 0, 0, 0, 0, 33,
|
34, 48, 50, 51, 0, 0, 43, 0, 0, 0,
|
||||||
24, 25, 22, 31, 0, 42, 0, 0, 44, 0,
|
0, 28, 24, 31, 25, 29, 22, 33, 0, 44,
|
||||||
0, 16, 17, 0, 8, 47, 40, 0, 43, 0,
|
0, 0, 46, 0, 0, 16, 17, 0, 8, 35,
|
||||||
6, 15, 0, 0, 0, 0, 34, 35, 45, 18,
|
49, 42, 0, 45, 0, 6, 15, 0, 0, 0,
|
||||||
19, 14, 9, 0, 0, 0, 0, 7,
|
0, 36, 37, 47, 18, 19, 14, 9, 0, 0,
|
||||||
|
0, 0, 7,
|
||||||
}
|
}
|
||||||
var yyTok1 = []int{
|
var yyTok1 = []int{
|
||||||
|
|
||||||
|
@ -179,21 +187,21 @@ var yyTok1 = []int{
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
28, 29, 3, 3, 27, 3, 3, 3, 3, 3,
|
29, 30, 3, 3, 28, 3, 3, 3, 3, 3,
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
3, 24, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 25, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
3, 30, 3, 31, 3, 3, 3, 3, 3, 3,
|
3, 31, 3, 32, 3, 3, 3, 3, 3, 3,
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
3, 3, 3, 25, 3, 26,
|
3, 3, 3, 26, 3, 27,
|
||||||
}
|
}
|
||||||
var yyTok2 = []int{
|
var yyTok2 = []int{
|
||||||
|
|
||||||
2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
|
2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
|
||||||
12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
|
12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
|
||||||
22, 23,
|
22, 23, 24,
|
||||||
}
|
}
|
||||||
var yyTok3 = []int{
|
var yyTok3 = []int{
|
||||||
0,
|
0,
|
||||||
|
@ -559,27 +567,46 @@ yydefault:
|
||||||
case 28:
|
case 28:
|
||||||
//line parser.y:156
|
//line parser.y:156
|
||||||
{
|
{
|
||||||
yyVAL.ruleNode = yyS[yypt-1].ruleNode
|
yyVAL.str = "0s"
|
||||||
}
|
}
|
||||||
case 29:
|
case 29:
|
||||||
//line parser.y:158
|
//line parser.y:158
|
||||||
{
|
{
|
||||||
yyVAL.ruleNode = ast.NewVectorSelector(yyS[yypt-1].labelMatchers)
|
yyVAL.str = yyS[yypt-0].str
|
||||||
}
|
}
|
||||||
case 30:
|
case 30:
|
||||||
//line parser.y:160
|
//line parser.y:162
|
||||||
|
{
|
||||||
|
yyVAL.ruleNode = yyS[yypt-1].ruleNode
|
||||||
|
}
|
||||||
|
case 31:
|
||||||
|
//line parser.y:164
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
m, err := metric.NewLabelMatcher(metric.Equal, clientmodel.MetricNameLabel, clientmodel.LabelValue(yyS[yypt-1].str))
|
yyVAL.ruleNode, err = NewVectorSelector(yyS[yypt-2].labelMatchers, yyS[yypt-0].str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
yylex.Error(err.Error())
|
yylex.Error(err.Error())
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
yyS[yypt-0].labelMatchers = append(yyS[yypt-0].labelMatchers, m)
|
|
||||||
yyVAL.ruleNode = ast.NewVectorSelector(yyS[yypt-0].labelMatchers)
|
|
||||||
}
|
}
|
||||||
case 31:
|
case 32:
|
||||||
//line parser.y:168
|
//line parser.y:170
|
||||||
|
{
|
||||||
|
var err error
|
||||||
|
m, err := metric.NewLabelMatcher(metric.Equal, clientmodel.MetricNameLabel, clientmodel.LabelValue(yyS[yypt-2].str))
|
||||||
|
if err != nil {
|
||||||
|
yylex.Error(err.Error())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
yyS[yypt-1].labelMatchers = append(yyS[yypt-1].labelMatchers, m)
|
||||||
|
yyVAL.ruleNode, err = NewVectorSelector(yyS[yypt-1].labelMatchers, yyS[yypt-0].str)
|
||||||
|
if err != nil {
|
||||||
|
yylex.Error(err.Error())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 33:
|
||||||
|
//line parser.y:179
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-3].str, yyS[yypt-1].ruleNodeSlice)
|
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-3].str, yyS[yypt-1].ruleNodeSlice)
|
||||||
|
@ -588,8 +615,8 @@ yydefault:
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 32:
|
case 34:
|
||||||
//line parser.y:174
|
//line parser.y:185
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-2].str, []ast.Node{})
|
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-2].str, []ast.Node{})
|
||||||
|
@ -598,18 +625,18 @@ yydefault:
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 33:
|
case 35:
|
||||||
//line parser.y:180
|
//line parser.y:191
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
yyVAL.ruleNode, err = NewMatrixSelector(yyS[yypt-3].ruleNode, yyS[yypt-1].str)
|
yyVAL.ruleNode, err = NewMatrixSelector(yyS[yypt-4].ruleNode, yyS[yypt-2].str, yyS[yypt-0].str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
yylex.Error(err.Error())
|
yylex.Error(err.Error())
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 34:
|
case 36:
|
||||||
//line parser.y:186
|
//line parser.y:197
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
yyVAL.ruleNode, err = NewVectorAggregation(yyS[yypt-5].str, yyS[yypt-3].ruleNode, yyS[yypt-1].labelNameSlice, yyS[yypt-0].boolean)
|
yyVAL.ruleNode, err = NewVectorAggregation(yyS[yypt-5].str, yyS[yypt-3].ruleNode, yyS[yypt-1].labelNameSlice, yyS[yypt-0].boolean)
|
||||||
|
@ -618,8 +645,8 @@ yydefault:
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 35:
|
case 37:
|
||||||
//line parser.y:192
|
//line parser.y:203
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
yyVAL.ruleNode, err = NewVectorAggregation(yyS[yypt-5].str, yyS[yypt-1].ruleNode, yyS[yypt-4].labelNameSlice, yyS[yypt-3].boolean)
|
yyVAL.ruleNode, err = NewVectorAggregation(yyS[yypt-5].str, yyS[yypt-1].ruleNode, yyS[yypt-4].labelNameSlice, yyS[yypt-3].boolean)
|
||||||
|
@ -628,28 +655,8 @@ yydefault:
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 36:
|
|
||||||
//line parser.y:200
|
|
||||||
{
|
|
||||||
var err error
|
|
||||||
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
|
||||||
if err != nil {
|
|
||||||
yylex.Error(err.Error())
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 37:
|
|
||||||
//line parser.y:206
|
|
||||||
{
|
|
||||||
var err error
|
|
||||||
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
|
||||||
if err != nil {
|
|
||||||
yylex.Error(err.Error())
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 38:
|
case 38:
|
||||||
//line parser.y:212
|
//line parser.y:211
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
||||||
|
@ -659,57 +666,77 @@ yydefault:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 39:
|
case 39:
|
||||||
//line parser.y:218
|
//line parser.y:217
|
||||||
|
{
|
||||||
|
var err error
|
||||||
|
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
||||||
|
if err != nil {
|
||||||
|
yylex.Error(err.Error())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 40:
|
||||||
|
//line parser.y:223
|
||||||
|
{
|
||||||
|
var err error
|
||||||
|
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
||||||
|
if err != nil {
|
||||||
|
yylex.Error(err.Error())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 41:
|
||||||
|
//line parser.y:229
|
||||||
{
|
{
|
||||||
yyVAL.ruleNode = ast.NewScalarLiteral(yyS[yypt-0].num)
|
yyVAL.ruleNode = ast.NewScalarLiteral(yyS[yypt-0].num)
|
||||||
}
|
}
|
||||||
case 40:
|
case 42:
|
||||||
//line parser.y:222
|
//line parser.y:233
|
||||||
{
|
{
|
||||||
yyVAL.boolean = false
|
yyVAL.boolean = false
|
||||||
}
|
}
|
||||||
case 41:
|
case 43:
|
||||||
//line parser.y:224
|
//line parser.y:235
|
||||||
{
|
{
|
||||||
yyVAL.boolean = true
|
yyVAL.boolean = true
|
||||||
}
|
}
|
||||||
case 42:
|
case 44:
|
||||||
//line parser.y:228
|
//line parser.y:239
|
||||||
{
|
{
|
||||||
yyVAL.labelNameSlice = clientmodel.LabelNames{}
|
yyVAL.labelNameSlice = clientmodel.LabelNames{}
|
||||||
}
|
}
|
||||||
case 43:
|
case 45:
|
||||||
//line parser.y:230
|
//line parser.y:241
|
||||||
{
|
{
|
||||||
yyVAL.labelNameSlice = yyS[yypt-1].labelNameSlice
|
yyVAL.labelNameSlice = yyS[yypt-1].labelNameSlice
|
||||||
}
|
}
|
||||||
case 44:
|
case 46:
|
||||||
//line parser.y:234
|
//line parser.y:245
|
||||||
{
|
{
|
||||||
yyVAL.labelNameSlice = clientmodel.LabelNames{clientmodel.LabelName(yyS[yypt-0].str)}
|
yyVAL.labelNameSlice = clientmodel.LabelNames{clientmodel.LabelName(yyS[yypt-0].str)}
|
||||||
}
|
}
|
||||||
case 45:
|
case 47:
|
||||||
//line parser.y:236
|
//line parser.y:247
|
||||||
{
|
{
|
||||||
yyVAL.labelNameSlice = append(yyVAL.labelNameSlice, clientmodel.LabelName(yyS[yypt-0].str))
|
yyVAL.labelNameSlice = append(yyVAL.labelNameSlice, clientmodel.LabelName(yyS[yypt-0].str))
|
||||||
}
|
}
|
||||||
case 46:
|
case 48:
|
||||||
//line parser.y:240
|
//line parser.y:251
|
||||||
{
|
{
|
||||||
yyVAL.ruleNodeSlice = []ast.Node{yyS[yypt-0].ruleNode}
|
yyVAL.ruleNodeSlice = []ast.Node{yyS[yypt-0].ruleNode}
|
||||||
}
|
}
|
||||||
case 47:
|
case 49:
|
||||||
//line parser.y:242
|
//line parser.y:253
|
||||||
{
|
{
|
||||||
yyVAL.ruleNodeSlice = append(yyVAL.ruleNodeSlice, yyS[yypt-0].ruleNode)
|
yyVAL.ruleNodeSlice = append(yyVAL.ruleNodeSlice, yyS[yypt-0].ruleNode)
|
||||||
}
|
}
|
||||||
case 48:
|
case 50:
|
||||||
//line parser.y:246
|
//line parser.y:257
|
||||||
{
|
{
|
||||||
yyVAL.ruleNode = yyS[yypt-0].ruleNode
|
yyVAL.ruleNode = yyS[yypt-0].ruleNode
|
||||||
}
|
}
|
||||||
case 49:
|
case 51:
|
||||||
//line parser.y:248
|
//line parser.y:259
|
||||||
{
|
{
|
||||||
yyVAL.ruleNode = ast.NewStringLiteral(yyS[yypt-0].str)
|
yyVAL.ruleNode = ast.NewStringLiteral(yyS[yypt-0].str)
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,42 +62,32 @@ func newTestStorage(t testing.TB) (storage local.Storage, closer test.Closer) {
|
||||||
func TestExpressions(t *testing.T) {
|
func TestExpressions(t *testing.T) {
|
||||||
// Labels in expected output need to be alphabetically sorted.
|
// Labels in expected output need to be alphabetically sorted.
|
||||||
expressionTests := []struct {
|
expressionTests := []struct {
|
||||||
expr string
|
expr string
|
||||||
output []string
|
output []string
|
||||||
shouldFail bool
|
shouldFail bool
|
||||||
checkOrder bool
|
checkOrder bool
|
||||||
fullRanges int
|
|
||||||
intervalRanges int
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
expr: `SUM(http_requests)`,
|
expr: `SUM(http_requests)`,
|
||||||
output: []string{`{} => 3600 @[%v]`},
|
output: []string{`{} => 3600 @[%v]`},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `SUM(http_requests{instance="0"}) BY(job)`,
|
expr: `SUM(http_requests{instance="0"}) BY(job)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="api-server"} => 400 @[%v]`,
|
`{job="api-server"} => 400 @[%v]`,
|
||||||
`{job="app-server"} => 1200 @[%v]`,
|
`{job="app-server"} => 1200 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 4,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `SUM(http_requests{instance="0"}) BY(job) KEEPING_EXTRA`,
|
expr: `SUM(http_requests{instance="0"}) BY(job) KEEPING_EXTRA`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{instance="0", job="api-server"} => 400 @[%v]`,
|
`{instance="0", job="api-server"} => 400 @[%v]`,
|
||||||
`{instance="0", job="app-server"} => 1200 @[%v]`,
|
`{instance="0", job="app-server"} => 1200 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 4,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `SUM(http_requests) BY (job)`,
|
expr: `SUM(http_requests) BY (job)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="api-server"} => 1000 @[%v]`,
|
`{job="api-server"} => 1000 @[%v]`,
|
||||||
`{job="app-server"} => 2600 @[%v]`,
|
`{job="app-server"} => 2600 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
// Non-existent labels mentioned in BY-clauses shouldn't propagate to output.
|
// Non-existent labels mentioned in BY-clauses shouldn't propagate to output.
|
||||||
expr: `SUM(http_requests) BY (job, nonexistent)`,
|
expr: `SUM(http_requests) BY (job, nonexistent)`,
|
||||||
|
@ -105,8 +95,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{job="api-server"} => 1000 @[%v]`,
|
`{job="api-server"} => 1000 @[%v]`,
|
||||||
`{job="app-server"} => 2600 @[%v]`,
|
`{job="app-server"} => 2600 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `
|
expr: `
|
||||||
// Test comment.
|
// Test comment.
|
||||||
|
@ -116,16 +104,12 @@ func TestExpressions(t *testing.T) {
|
||||||
`{job="api-server"} => 1000 @[%v]`,
|
`{job="api-server"} => 1000 @[%v]`,
|
||||||
`{job="app-server"} => 2600 @[%v]`,
|
`{job="app-server"} => 2600 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `COUNT(http_requests) BY (job)`,
|
expr: `COUNT(http_requests) BY (job)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="api-server"} => 4 @[%v]`,
|
`{job="api-server"} => 4 @[%v]`,
|
||||||
`{job="app-server"} => 4 @[%v]`,
|
`{job="app-server"} => 4 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `SUM(http_requests) BY (job, group)`,
|
expr: `SUM(http_requests) BY (job, group)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
|
@ -134,139 +118,103 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", job="api-server"} => 300 @[%v]`,
|
`{group="production", job="api-server"} => 300 @[%v]`,
|
||||||
`{group="production", job="app-server"} => 1100 @[%v]`,
|
`{group="production", job="app-server"} => 1100 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `AVG(http_requests) BY (job)`,
|
expr: `AVG(http_requests) BY (job)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="api-server"} => 250 @[%v]`,
|
`{job="api-server"} => 250 @[%v]`,
|
||||||
`{job="app-server"} => 650 @[%v]`,
|
`{job="app-server"} => 650 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `MIN(http_requests) BY (job)`,
|
expr: `MIN(http_requests) BY (job)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="api-server"} => 100 @[%v]`,
|
`{job="api-server"} => 100 @[%v]`,
|
||||||
`{job="app-server"} => 500 @[%v]`,
|
`{job="app-server"} => 500 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `MAX(http_requests) BY (job)`,
|
expr: `MAX(http_requests) BY (job)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="api-server"} => 400 @[%v]`,
|
`{job="api-server"} => 400 @[%v]`,
|
||||||
`{job="app-server"} => 800 @[%v]`,
|
`{job="app-server"} => 800 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `SUM(http_requests) BY (job) - COUNT(http_requests) BY (job)`,
|
expr: `SUM(http_requests) BY (job) - COUNT(http_requests) BY (job)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="api-server"} => 996 @[%v]`,
|
`{job="api-server"} => 996 @[%v]`,
|
||||||
`{job="app-server"} => 2596 @[%v]`,
|
`{job="app-server"} => 2596 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `2 - SUM(http_requests) BY (job)`,
|
expr: `2 - SUM(http_requests) BY (job)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="api-server"} => -998 @[%v]`,
|
`{job="api-server"} => -998 @[%v]`,
|
||||||
`{job="app-server"} => -2598 @[%v]`,
|
`{job="app-server"} => -2598 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `1000 / SUM(http_requests) BY (job)`,
|
expr: `1000 / SUM(http_requests) BY (job)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="api-server"} => 1 @[%v]`,
|
`{job="api-server"} => 1 @[%v]`,
|
||||||
`{job="app-server"} => 0.38461538461538464 @[%v]`,
|
`{job="app-server"} => 0.38461538461538464 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `SUM(http_requests) BY (job) - 2`,
|
expr: `SUM(http_requests) BY (job) - 2`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="api-server"} => 998 @[%v]`,
|
`{job="api-server"} => 998 @[%v]`,
|
||||||
`{job="app-server"} => 2598 @[%v]`,
|
`{job="app-server"} => 2598 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `SUM(http_requests) BY (job) % 3`,
|
expr: `SUM(http_requests) BY (job) % 3`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="api-server"} => 1 @[%v]`,
|
`{job="api-server"} => 1 @[%v]`,
|
||||||
`{job="app-server"} => 2 @[%v]`,
|
`{job="app-server"} => 2 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `SUM(http_requests) BY (job) / 0`,
|
expr: `SUM(http_requests) BY (job) / 0`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="api-server"} => +Inf @[%v]`,
|
`{job="api-server"} => +Inf @[%v]`,
|
||||||
`{job="app-server"} => +Inf @[%v]`,
|
`{job="app-server"} => +Inf @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `SUM(http_requests) BY (job) > 1000`,
|
expr: `SUM(http_requests) BY (job) > 1000`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="app-server"} => 2600 @[%v]`,
|
`{job="app-server"} => 2600 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `1000 < SUM(http_requests) BY (job)`,
|
expr: `1000 < SUM(http_requests) BY (job)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="app-server"} => 1000 @[%v]`,
|
`{job="app-server"} => 1000 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `SUM(http_requests) BY (job) <= 1000`,
|
expr: `SUM(http_requests) BY (job) <= 1000`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="api-server"} => 1000 @[%v]`,
|
`{job="api-server"} => 1000 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `SUM(http_requests) BY (job) != 1000`,
|
expr: `SUM(http_requests) BY (job) != 1000`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="app-server"} => 2600 @[%v]`,
|
`{job="app-server"} => 2600 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `SUM(http_requests) BY (job) == 1000`,
|
expr: `SUM(http_requests) BY (job) == 1000`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="api-server"} => 1000 @[%v]`,
|
`{job="api-server"} => 1000 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `SUM(http_requests) BY (job) + SUM(http_requests) BY (job)`,
|
expr: `SUM(http_requests) BY (job) + SUM(http_requests) BY (job)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="api-server"} => 2000 @[%v]`,
|
`{job="api-server"} => 2000 @[%v]`,
|
||||||
`{job="app-server"} => 5200 @[%v]`,
|
`{job="app-server"} => 5200 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `http_requests{job="api-server", group="canary"}`,
|
expr: `http_requests{job="api-server", group="canary"}`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`http_requests{group="canary", instance="0", job="api-server"} => 300 @[%v]`,
|
`http_requests{group="canary", instance="0", job="api-server"} => 300 @[%v]`,
|
||||||
`http_requests{group="canary", instance="1", job="api-server"} => 400 @[%v]`,
|
`http_requests{group="canary", instance="1", job="api-server"} => 400 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `http_requests{job="api-server", group="canary"} + rate(http_requests{job="api-server"}[5m]) * 5 * 60`,
|
expr: `http_requests{job="api-server", group="canary"} + rate(http_requests{job="api-server"}[5m]) * 5 * 60`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{group="canary", instance="0", job="api-server"} => 330 @[%v]`,
|
`{group="canary", instance="0", job="api-server"} => 330 @[%v]`,
|
||||||
`{group="canary", instance="1", job="api-server"} => 440 @[%v]`,
|
`{group="canary", instance="1", job="api-server"} => 440 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 4,
|
|
||||||
intervalRanges: 0,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `rate(http_requests[25m]) * 25 * 60`,
|
expr: `rate(http_requests[25m]) * 25 * 60`,
|
||||||
output: []string{
|
output: []string{
|
||||||
|
@ -279,8 +227,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="1", job="api-server"} => 100 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 100 @[%v]`,
|
||||||
`{group="production", instance="1", job="app-server"} => 300 @[%v]`,
|
`{group="production", instance="1", job="app-server"} => 300 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 8,
|
|
||||||
intervalRanges: 0,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `delta(http_requests[25m], 1)`,
|
expr: `delta(http_requests[25m], 1)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
|
@ -293,8 +239,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="1", job="api-server"} => 100 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 100 @[%v]`,
|
||||||
`{group="production", instance="1", job="app-server"} => 300 @[%v]`,
|
`{group="production", instance="1", job="app-server"} => 300 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 8,
|
|
||||||
intervalRanges: 0,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `sort(http_requests)`,
|
expr: `sort(http_requests)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
|
@ -307,9 +251,7 @@ func TestExpressions(t *testing.T) {
|
||||||
`http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`,
|
`http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`,
|
||||||
`http_requests{group="canary", instance="1", job="app-server"} => 800 @[%v]`,
|
`http_requests{group="canary", instance="1", job="app-server"} => 800 @[%v]`,
|
||||||
},
|
},
|
||||||
checkOrder: true,
|
checkOrder: true,
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `sort_desc(http_requests)`,
|
expr: `sort_desc(http_requests)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
|
@ -322,9 +264,7 @@ func TestExpressions(t *testing.T) {
|
||||||
`http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`,
|
`http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`,
|
||||||
`http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`,
|
`http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`,
|
||||||
},
|
},
|
||||||
checkOrder: true,
|
checkOrder: true,
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `topk(3, http_requests)`,
|
expr: `topk(3, http_requests)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
|
@ -332,18 +272,14 @@ func TestExpressions(t *testing.T) {
|
||||||
`http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`,
|
`http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`,
|
||||||
`http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`,
|
`http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`,
|
||||||
},
|
},
|
||||||
checkOrder: true,
|
checkOrder: true,
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `topk(5, http_requests{group="canary",job="app-server"})`,
|
expr: `topk(5, http_requests{group="canary",job="app-server"})`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`http_requests{group="canary", instance="1", job="app-server"} => 800 @[%v]`,
|
`http_requests{group="canary", instance="1", job="app-server"} => 800 @[%v]`,
|
||||||
`http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`,
|
`http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`,
|
||||||
},
|
},
|
||||||
checkOrder: true,
|
checkOrder: true,
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `bottomk(3, http_requests)`,
|
expr: `bottomk(3, http_requests)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
|
@ -351,26 +287,20 @@ func TestExpressions(t *testing.T) {
|
||||||
`http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`,
|
`http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`,
|
||||||
`http_requests{group="canary", instance="0", job="api-server"} => 300 @[%v]`,
|
`http_requests{group="canary", instance="0", job="api-server"} => 300 @[%v]`,
|
||||||
},
|
},
|
||||||
checkOrder: true,
|
checkOrder: true,
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `bottomk(5, http_requests{group="canary",job="app-server"})`,
|
expr: `bottomk(5, http_requests{group="canary",job="app-server"})`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`,
|
`http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`,
|
||||||
`http_requests{group="canary", instance="1", job="app-server"} => 800 @[%v]`,
|
`http_requests{group="canary", instance="1", job="app-server"} => 800 @[%v]`,
|
||||||
},
|
},
|
||||||
checkOrder: true,
|
checkOrder: true,
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
}, {
|
}, {
|
||||||
// Single-letter label names and values.
|
// Single-letter label names and values.
|
||||||
expr: `x{y="testvalue"}`,
|
expr: `x{y="testvalue"}`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`x{y="testvalue"} => 100 @[%v]`,
|
`x{y="testvalue"} => 100 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 1,
|
|
||||||
}, {
|
}, {
|
||||||
// Lower-cased aggregation operators should work too.
|
// Lower-cased aggregation operators should work too.
|
||||||
expr: `sum(http_requests) by (job) + min(http_requests) by (job) + max(http_requests) by (job) + avg(http_requests) by (job)`,
|
expr: `sum(http_requests) by (job) + min(http_requests) by (job) + max(http_requests) by (job) + avg(http_requests) by (job)`,
|
||||||
|
@ -378,62 +308,42 @@ func TestExpressions(t *testing.T) {
|
||||||
`{job="app-server"} => 4550 @[%v]`,
|
`{job="app-server"} => 4550 @[%v]`,
|
||||||
`{job="api-server"} => 1750 @[%v]`,
|
`{job="api-server"} => 1750 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
// Deltas should be adjusted for target interval vs. samples under target interval.
|
// Deltas should be adjusted for target interval vs. samples under target interval.
|
||||||
expr: `delta(http_requests{group="canary", instance="1", job="app-server"}[18m])`,
|
expr: `delta(http_requests{group="canary", instance="1", job="app-server"}[18m])`,
|
||||||
output: []string{`{group="canary", instance="1", job="app-server"} => 288 @[%v]`},
|
output: []string{`{group="canary", instance="1", job="app-server"} => 288 @[%v]`},
|
||||||
fullRanges: 1,
|
|
||||||
intervalRanges: 0,
|
|
||||||
}, {
|
}, {
|
||||||
// Deltas should perform the same operation when 2nd argument is 0.
|
// Deltas should perform the same operation when 2nd argument is 0.
|
||||||
expr: `delta(http_requests{group="canary", instance="1", job="app-server"}[18m], 0)`,
|
expr: `delta(http_requests{group="canary", instance="1", job="app-server"}[18m], 0)`,
|
||||||
output: []string{`{group="canary", instance="1", job="app-server"} => 288 @[%v]`},
|
output: []string{`{group="canary", instance="1", job="app-server"} => 288 @[%v]`},
|
||||||
fullRanges: 1,
|
|
||||||
intervalRanges: 0,
|
|
||||||
}, {
|
}, {
|
||||||
// Rates should calculate per-second rates.
|
// Rates should calculate per-second rates.
|
||||||
expr: `rate(http_requests{group="canary", instance="1", job="app-server"}[60m])`,
|
expr: `rate(http_requests{group="canary", instance="1", job="app-server"}[60m])`,
|
||||||
output: []string{`{group="canary", instance="1", job="app-server"} => 0.26666666666666666 @[%v]`},
|
output: []string{`{group="canary", instance="1", job="app-server"} => 0.26666666666666666 @[%v]`},
|
||||||
fullRanges: 1,
|
|
||||||
intervalRanges: 0,
|
|
||||||
}, {
|
}, {
|
||||||
// Deriv should return the same as rate in simple cases.
|
// Deriv should return the same as rate in simple cases.
|
||||||
expr: `deriv(http_requests{group="canary", instance="1", job="app-server"}[60m])`,
|
expr: `deriv(http_requests{group="canary", instance="1", job="app-server"}[60m])`,
|
||||||
output: []string{`{group="canary", instance="1", job="app-server"} => 0.26666666666666666 @[%v]`},
|
output: []string{`{group="canary", instance="1", job="app-server"} => 0.26666666666666666 @[%v]`},
|
||||||
fullRanges: 1,
|
|
||||||
intervalRanges: 0,
|
|
||||||
}, {
|
}, {
|
||||||
// Counter resets at in the middle of range are handled correctly by rate().
|
// Counter resets at in the middle of range are handled correctly by rate().
|
||||||
expr: `rate(testcounter_reset_middle[60m])`,
|
expr: `rate(testcounter_reset_middle[60m])`,
|
||||||
output: []string{`{} => 0.03 @[%v]`},
|
output: []string{`{} => 0.03 @[%v]`},
|
||||||
fullRanges: 1,
|
|
||||||
intervalRanges: 0,
|
|
||||||
}, {
|
}, {
|
||||||
// Counter resets at end of range are ignored by rate().
|
// Counter resets at end of range are ignored by rate().
|
||||||
expr: `rate(testcounter_reset_end[5m])`,
|
expr: `rate(testcounter_reset_end[5m])`,
|
||||||
output: []string{`{} => 0 @[%v]`},
|
output: []string{`{} => 0 @[%v]`},
|
||||||
fullRanges: 1,
|
|
||||||
intervalRanges: 0,
|
|
||||||
}, {
|
}, {
|
||||||
// Deriv should return correct result.
|
// Deriv should return correct result.
|
||||||
expr: `deriv(testcounter_reset_middle[100m])`,
|
expr: `deriv(testcounter_reset_middle[100m])`,
|
||||||
output: []string{`{} => 0.010606060606060607 @[%v]`},
|
output: []string{`{} => 0.010606060606060607 @[%v]`},
|
||||||
fullRanges: 1,
|
|
||||||
intervalRanges: 0,
|
|
||||||
}, {
|
}, {
|
||||||
// count_scalar for a non-empty vector should return scalar element count.
|
// count_scalar for a non-empty vector should return scalar element count.
|
||||||
expr: `count_scalar(http_requests)`,
|
expr: `count_scalar(http_requests)`,
|
||||||
output: []string{`scalar: 8 @[%v]`},
|
output: []string{`scalar: 8 @[%v]`},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
}, {
|
}, {
|
||||||
// count_scalar for an empty vector should return scalar 0.
|
// count_scalar for an empty vector should return scalar 0.
|
||||||
expr: `count_scalar(nonexistent)`,
|
expr: `count_scalar(nonexistent)`,
|
||||||
output: []string{`scalar: 0 @[%v]`},
|
output: []string{`scalar: 0 @[%v]`},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 0,
|
|
||||||
}, {
|
}, {
|
||||||
// Empty expressions shouldn't parse.
|
// Empty expressions shouldn't parse.
|
||||||
expr: ``,
|
expr: ``,
|
||||||
|
@ -454,8 +364,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`,
|
`http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`,
|
||||||
`http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`,
|
`http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 4,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `http_requests{job=~"server",group!="canary"}`,
|
expr: `http_requests{job=~"server",group!="canary"}`,
|
||||||
output: []string{
|
output: []string{
|
||||||
|
@ -464,29 +372,21 @@ func TestExpressions(t *testing.T) {
|
||||||
`http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`,
|
`http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`,
|
||||||
`http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`,
|
`http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 4,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `http_requests{job!~"api",group!="canary"}`,
|
expr: `http_requests{job!~"api",group!="canary"}`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`,
|
`http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`,
|
||||||
`http_requests{group="production", instance="0", job="app-server"} => 500 @[%v]`,
|
`http_requests{group="production", instance="0", job="app-server"} => 500 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `count_scalar(http_requests{job=~"^server$"})`,
|
expr: `count_scalar(http_requests{job=~"^server$"})`,
|
||||||
output: []string{`scalar: 0 @[%v]`},
|
output: []string{`scalar: 0 @[%v]`},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 0,
|
|
||||||
}, {
|
}, {
|
||||||
expr: `http_requests{group="production",job=~"^api"}`,
|
expr: `http_requests{group="production",job=~"^api"}`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`,
|
`http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`,
|
||||||
`http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`,
|
`http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `abs(-1 * http_requests{group="production",job="api-server"})`,
|
expr: `abs(-1 * http_requests{group="production",job="api-server"})`,
|
||||||
|
@ -494,8 +394,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 100 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 100 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => 200 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 200 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `floor(0.004 * http_requests{group="production",job="api-server"})`,
|
expr: `floor(0.004 * http_requests{group="production",job="api-server"})`,
|
||||||
|
@ -503,8 +401,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 0 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 0 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => 0 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 0 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `ceil(0.004 * http_requests{group="production",job="api-server"})`,
|
expr: `ceil(0.004 * http_requests{group="production",job="api-server"})`,
|
||||||
|
@ -512,8 +408,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 1 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 1 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => 1 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 1 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `round(0.004 * http_requests{group="production",job="api-server"})`,
|
expr: `round(0.004 * http_requests{group="production",job="api-server"})`,
|
||||||
|
@ -521,8 +415,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 0 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 0 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => 1 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 1 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
},
|
},
|
||||||
{ // Round should correctly handle negative numbers.
|
{ // Round should correctly handle negative numbers.
|
||||||
expr: `round(-1 * (0.004 * http_requests{group="production",job="api-server"}))`,
|
expr: `round(-1 * (0.004 * http_requests{group="production",job="api-server"}))`,
|
||||||
|
@ -530,8 +422,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 0 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 0 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => -1 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => -1 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
},
|
},
|
||||||
{ // Round should round half up.
|
{ // Round should round half up.
|
||||||
expr: `round(0.005 * http_requests{group="production",job="api-server"})`,
|
expr: `round(0.005 * http_requests{group="production",job="api-server"})`,
|
||||||
|
@ -539,8 +429,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 1 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 1 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => 1 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 1 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `round(-1 * (0.005 * http_requests{group="production",job="api-server"}))`,
|
expr: `round(-1 * (0.005 * http_requests{group="production",job="api-server"}))`,
|
||||||
|
@ -548,8 +436,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 0 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 0 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => -1 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => -1 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `round(1 + 0.005 * http_requests{group="production",job="api-server"})`,
|
expr: `round(1 + 0.005 * http_requests{group="production",job="api-server"})`,
|
||||||
|
@ -557,8 +443,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 2 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 2 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => 2 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 2 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `round(-1 * (1 + 0.005 * http_requests{group="production",job="api-server"}))`,
|
expr: `round(-1 * (1 + 0.005 * http_requests{group="production",job="api-server"}))`,
|
||||||
|
@ -566,8 +450,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => -1 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => -1 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => -2 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => -2 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
},
|
},
|
||||||
{ // Round should accept the number to round nearest to.
|
{ // Round should accept the number to round nearest to.
|
||||||
expr: `round(0.0005 * http_requests{group="production",job="api-server"}, 0.1)`,
|
expr: `round(0.0005 * http_requests{group="production",job="api-server"}, 0.1)`,
|
||||||
|
@ -575,8 +457,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 0.1 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 0.1 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => 0.1 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 0.1 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `round(2.1 + 0.0005 * http_requests{group="production",job="api-server"}, 0.1)`,
|
expr: `round(2.1 + 0.0005 * http_requests{group="production",job="api-server"}, 0.1)`,
|
||||||
|
@ -584,8 +464,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 2.2 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 2.2 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => 2.2 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 2.2 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `round(5.2 + 0.0005 * http_requests{group="production",job="api-server"}, 0.1)`,
|
expr: `round(5.2 + 0.0005 * http_requests{group="production",job="api-server"}, 0.1)`,
|
||||||
|
@ -593,8 +471,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 5.3 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 5.3 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => 5.3 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 5.3 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
},
|
},
|
||||||
{ // Round should work correctly with negative numbers and multiple decimal places.
|
{ // Round should work correctly with negative numbers and multiple decimal places.
|
||||||
expr: `round(-1 * (5.2 + 0.0005 * http_requests{group="production",job="api-server"}), 0.1)`,
|
expr: `round(-1 * (5.2 + 0.0005 * http_requests{group="production",job="api-server"}), 0.1)`,
|
||||||
|
@ -602,8 +478,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => -5.2 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => -5.2 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => -5.3 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => -5.3 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
},
|
},
|
||||||
{ // Round should work correctly with big toNearests.
|
{ // Round should work correctly with big toNearests.
|
||||||
expr: `round(0.025 * http_requests{group="production",job="api-server"}, 5)`,
|
expr: `round(0.025 * http_requests{group="production",job="api-server"}, 5)`,
|
||||||
|
@ -611,8 +485,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 5 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 5 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => 5 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 5 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `round(0.045 * http_requests{group="production",job="api-server"}, 5)`,
|
expr: `round(0.045 * http_requests{group="production",job="api-server"}, 5)`,
|
||||||
|
@ -620,8 +492,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 5 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 5 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => 10 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 10 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `avg_over_time(http_requests{group="production",job="api-server"}[1h])`,
|
expr: `avg_over_time(http_requests{group="production",job="api-server"}[1h])`,
|
||||||
|
@ -629,8 +499,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 50 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 50 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => 100 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 100 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 2,
|
|
||||||
intervalRanges: 0,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `count_over_time(http_requests{group="production",job="api-server"}[1h])`,
|
expr: `count_over_time(http_requests{group="production",job="api-server"}[1h])`,
|
||||||
|
@ -638,8 +506,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 11 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 11 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => 11 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 11 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 2,
|
|
||||||
intervalRanges: 0,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `max_over_time(http_requests{group="production",job="api-server"}[1h])`,
|
expr: `max_over_time(http_requests{group="production",job="api-server"}[1h])`,
|
||||||
|
@ -647,8 +513,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 100 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 100 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => 200 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 200 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 2,
|
|
||||||
intervalRanges: 0,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `min_over_time(http_requests{group="production",job="api-server"}[1h])`,
|
expr: `min_over_time(http_requests{group="production",job="api-server"}[1h])`,
|
||||||
|
@ -656,8 +520,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 0 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 0 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => 0 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 0 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 2,
|
|
||||||
intervalRanges: 0,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `sum_over_time(http_requests{group="production",job="api-server"}[1h])`,
|
expr: `sum_over_time(http_requests{group="production",job="api-server"}[1h])`,
|
||||||
|
@ -665,14 +527,10 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="production", instance="0", job="api-server"} => 550 @[%v]`,
|
`{group="production", instance="0", job="api-server"} => 550 @[%v]`,
|
||||||
`{group="production", instance="1", job="api-server"} => 1100 @[%v]`,
|
`{group="production", instance="1", job="api-server"} => 1100 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 2,
|
|
||||||
intervalRanges: 0,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `time()`,
|
expr: `time()`,
|
||||||
output: []string{`scalar: 3000 @[%v]`},
|
output: []string{`scalar: 3000 @[%v]`},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 0,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `drop_common_labels(http_requests{group="production",job="api-server"})`,
|
expr: `drop_common_labels(http_requests{group="production",job="api-server"})`,
|
||||||
|
@ -680,8 +538,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`http_requests{instance="0"} => 100 @[%v]`,
|
`http_requests{instance="0"} => 100 @[%v]`,
|
||||||
`http_requests{instance="1"} => 200 @[%v]`,
|
`http_requests{instance="1"} => 200 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 2,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `{` + string(clientmodel.MetricNameLabel) + `=~".*"}`,
|
expr: `{` + string(clientmodel.MetricNameLabel) + `=~".*"}`,
|
||||||
|
@ -698,8 +554,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`testcounter_reset_middle => 50 @[%v]`,
|
`testcounter_reset_middle => 50 @[%v]`,
|
||||||
`x{y="testvalue"} => 100 @[%v]`,
|
`x{y="testvalue"} => 100 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 11,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `{job=~"server", job!~"api"}`,
|
expr: `{job=~"server", job!~"api"}`,
|
||||||
|
@ -709,8 +563,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`http_requests{group="production", instance="0", job="app-server"} => 500 @[%v]`,
|
`http_requests{group="production", instance="0", job="app-server"} => 500 @[%v]`,
|
||||||
`http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`,
|
`http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 4,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test alternative "by"-clause order.
|
// Test alternative "by"-clause order.
|
||||||
|
@ -719,8 +571,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="canary"} => 700 @[%v]`,
|
`{group="canary"} => 700 @[%v]`,
|
||||||
`{group="production"} => 300 @[%v]`,
|
`{group="production"} => 300 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 4,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test alternative "by"-clause order with "keeping_extra".
|
// Test alternative "by"-clause order with "keeping_extra".
|
||||||
|
@ -729,8 +579,6 @@ func TestExpressions(t *testing.T) {
|
||||||
`{group="canary", job="api-server"} => 700 @[%v]`,
|
`{group="canary", job="api-server"} => 700 @[%v]`,
|
||||||
`{group="production", job="api-server"} => 300 @[%v]`,
|
`{group="production", job="api-server"} => 300 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 4,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test both alternative "by"-clause orders in one expression.
|
// Test both alternative "by"-clause orders in one expression.
|
||||||
|
@ -740,48 +588,50 @@ func TestExpressions(t *testing.T) {
|
||||||
output: []string{
|
output: []string{
|
||||||
`{job="api-server"} => 1000 @[%v]`,
|
`{job="api-server"} => 1000 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 4,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `absent(nonexistent)`,
|
expr: `absent(nonexistent)`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{} => 1 @[%v]`,
|
`{} => 1 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 0,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `absent(nonexistent{job="testjob", instance="testinstance", method=~".*"})`,
|
expr: `absent(nonexistent{job="testjob", instance="testinstance", method=~".*"})`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{instance="testinstance", job="testjob"} => 1 @[%v]`,
|
`{instance="testinstance", job="testjob"} => 1 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 0,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `count_scalar(absent(http_requests))`,
|
expr: `count_scalar(absent(http_requests))`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`scalar: 0 @[%v]`,
|
`scalar: 0 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `count_scalar(absent(sum(http_requests)))`,
|
expr: `count_scalar(absent(sum(http_requests)))`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`scalar: 0 @[%v]`,
|
`scalar: 0 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
|
||||||
intervalRanges: 8,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expr: `absent(sum(nonexistent{job="testjob", instance="testinstance"}))`,
|
expr: `absent(sum(nonexistent{job="testjob", instance="testinstance"}))`,
|
||||||
output: []string{
|
output: []string{
|
||||||
`{} => 1 @[%v]`,
|
`{} => 1 @[%v]`,
|
||||||
},
|
},
|
||||||
fullRanges: 0,
|
},
|
||||||
intervalRanges: 0,
|
{
|
||||||
|
expr: `http_requests{group="production",job="api-server"} offset 5m`,
|
||||||
|
output: []string{
|
||||||
|
`http_requests{group="production", instance="0", job="api-server"} => 90 @[%v]`,
|
||||||
|
`http_requests{group="production", instance="1", job="api-server"} => 180 @[%v]`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `rate(http_requests{group="production",job="api-server"}[10m] offset 5m)`,
|
||||||
|
output: []string{
|
||||||
|
`{group="production", instance="0", job="api-server"} => 0.03333333333333333 @[%v]`,
|
||||||
|
`{group="production", instance="1", job="api-server"} => 0.06666666666666667 @[%v]`,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -834,17 +684,6 @@ func TestExpressions(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzer := ast.NewQueryAnalyzer(storage)
|
|
||||||
ast.Walk(analyzer, testExpr)
|
|
||||||
if exprTest.fullRanges != len(analyzer.FullRanges) {
|
|
||||||
t.Errorf("%d. Count of full ranges didn't match: %v vs %v", i, exprTest.fullRanges, len(analyzer.FullRanges))
|
|
||||||
failed = true
|
|
||||||
}
|
|
||||||
if exprTest.intervalRanges != len(analyzer.IntervalRanges) {
|
|
||||||
t.Errorf("%d. Count of interval ranges didn't match: %v vs %v", i, exprTest.intervalRanges, len(analyzer.IntervalRanges))
|
|
||||||
failed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if failed {
|
if failed {
|
||||||
t.Errorf("%d. Expression: %v\n%v", i, exprTest.expr, vectorComparisonString(expectedLines, resultLines))
|
t.Errorf("%d. Expression: %v\n%v", i, exprTest.expr, vectorComparisonString(expectedLines, resultLines))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue