From 76caff985be37ed870f471816ebb3f01919f658f Mon Sep 17 00:00:00 2001 From: Charles Korn Date: Thu, 26 Sep 2024 13:22:29 +1000 Subject: [PATCH] MQE: add support for comparison operations (#9398) * Add feature flag and expand unsupported features test. * Simplify supported / unsupported assertions in tests * Add test cases * Implement comparison operations * Enable upstream test cases * Fix issue where scalar value is returned when doing a scalar / vector comparison with scalar on LHS * Remove features that are now supported from `TestUnsupportedPromQLFeatures` * Add test case for label manipulation when `on` and `ignoring` is used * Add changelog entry * Address PR feedback: update comment Co-authored-by: Arve Knudsen --------- Co-authored-by: Arve Knudsen --- CHANGELOG.md | 2 +- cmd/mimir/config-descriptor.json | 11 + cmd/mimir/help-all.txt.tmpl | 2 + .../configuration-parameters/index.md | 5 + pkg/streamingpromql/config.go | 7 +- pkg/streamingpromql/engine_test.go | 56 ++- .../scalar_scalar_binary_operation.go | 23 +- .../vector_scalar_binary_operation.go | 40 +- .../vector_vector_binary_operation.go | 117 +++++- pkg/streamingpromql/query.go | 12 +- .../testdata/ours-only/binary_operators.test | 22 ++ .../testdata/ours/binary_operators.test | 357 +++++++++++++++++- .../upstream/name_label_dropping.test | 10 +- .../testdata/upstream/operators.test | 79 ++-- 14 files changed, 652 insertions(+), 91 deletions(-) create mode 100644 pkg/streamingpromql/testdata/ours-only/binary_operators.test diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a31945536b..e05264b218f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,7 +36,7 @@ * [CHANGE] Querier: Remove deprecated `-querier.max-query-into-future`. The feature was deprecated in Mimir 2.12. #9407 * [FEATURE] Alertmanager: Added `-alertmanager.log-parsing-label-matchers` to control logging when parsing label matchers. This flag is intended to be used with `-alertmanager.utf8-strict-mode-enabled` to validate UTF-8 strict mode is working as intended. The default value is `false`. #9173 * [FEATURE] Alertmanager: Added `-alertmanager.utf8-migration-logging-enabled` to enable logging of tenant configurations that are incompatible with UTF-8 strict mode. The default value is `false`. #9174 -* [FEATURE] Querier: add experimental streaming PromQL engine, enabled with `-querier.query-engine=mimir`. #8422 #8430 #8454 #8455 #8360 #8490 #8508 #8577 #8660 #8671 #8677 #8747 #8850 #8872 #8838 #8911 #8909 #8923 #8924 #8925 #8932 #8933 #8934 #8962 #8986 #8993 #8995 #9008 #9017 #9018 #9019 #9120 #9121 #9136 #9139 #9140 #9145 #9191 #9192 #9194 #9196 #9201 #9212 #9225 #9260 #9272 #9277 #9278 #9280 #9281 #9342 #9343 #9367 #9368 #9371 #9399 +* [FEATURE] Querier: add experimental streaming PromQL engine, enabled with `-querier.query-engine=mimir`. #8422 #8430 #8454 #8455 #8360 #8490 #8508 #8577 #8660 #8671 #8677 #8747 #8850 #8872 #8838 #8911 #8909 #8923 #8924 #8925 #8932 #8933 #8934 #8962 #8986 #8993 #8995 #9008 #9017 #9018 #9019 #9120 #9121 #9136 #9139 #9140 #9145 #9191 #9192 #9194 #9196 #9201 #9212 #9225 #9260 #9272 #9277 #9278 #9280 #9281 #9342 #9343 #9367 #9368 #9371 #9398 #9399 * [FEATURE] Experimental Kafka-based ingest storage. #6888 #6894 #6929 #6940 #6951 #6974 #6982 #7029 #7030 #7091 #7142 #7147 #7148 #7153 #7160 #7193 #7349 #7376 #7388 #7391 #7393 #7394 #7402 #7404 #7423 #7424 #7437 #7486 #7503 #7508 #7540 #7621 #7682 #7685 #7694 #7695 #7696 #7697 #7701 #7733 #7734 #7741 #7752 #7838 #7851 #7871 #7877 #7880 #7882 #7887 #7891 #7925 #7955 #7967 #8031 #8063 #8077 #8088 #8135 #8176 #8184 #8194 #8216 #8217 #8222 #8233 #8503 #8542 #8579 #8657 #8686 #8688 #8703 #8706 #8708 #8738 #8750 #8778 #8808 #8809 #8841 #8842 #8845 #8853 #8886 #8988 * What it is: * When the new ingest storage architecture is enabled, distributors write incoming write requests to a Kafka-compatible backend, and the ingesters asynchronously replay ingested data from Kafka. In this architecture, the write and read path are de-coupled through a Kafka-compatible backend. The write path and Kafka load is a function of the incoming write traffic, the read path load is a function of received queries. Whatever the load on the read path, it doesn't affect the write path. diff --git a/cmd/mimir/config-descriptor.json b/cmd/mimir/config-descriptor.json index f8a3b38c3fd..4f6c671a54e 100644 --- a/cmd/mimir/config-descriptor.json +++ b/cmd/mimir/config-descriptor.json @@ -1978,6 +1978,17 @@ "fieldType": "boolean", "fieldCategory": "experimental" }, + { + "kind": "field", + "name": "enable_binary_comparison_operations", + "required": false, + "desc": "Enable support for binary comparison operations in Mimir's query engine. Only applies if the Mimir query engine is in use.", + "fieldValue": null, + "fieldDefaultValue": true, + "fieldFlag": "querier.mimir-query-engine.enable-binary-comparison-operations", + "fieldType": "boolean", + "fieldCategory": "experimental" + }, { "kind": "field", "name": "enable_scalars", diff --git a/cmd/mimir/help-all.txt.tmpl b/cmd/mimir/help-all.txt.tmpl index 42c819815b1..08830a1f54d 100644 --- a/cmd/mimir/help-all.txt.tmpl +++ b/cmd/mimir/help-all.txt.tmpl @@ -1921,6 +1921,8 @@ Usage of ./cmd/mimir/mimir: Maximum number of samples a single query can load into memory. This config option should be set on query-frontend too when query sharding is enabled. (default 50000000) -querier.mimir-query-engine.enable-aggregation-operations [experimental] Enable support for aggregation operations in Mimir's query engine. Only applies if the Mimir query engine is in use. (default true) + -querier.mimir-query-engine.enable-binary-comparison-operations + [experimental] Enable support for binary comparison operations in Mimir's query engine. Only applies if the Mimir query engine is in use. (default true) -querier.mimir-query-engine.enable-scalars [experimental] Enable support for scalars in Mimir's query engine. Only applies if the Mimir query engine is in use. (default true) -querier.minimize-ingester-requests diff --git a/docs/sources/mimir/configure/configuration-parameters/index.md b/docs/sources/mimir/configure/configuration-parameters/index.md index dd5988d7e4e..8d9a582f81a 100644 --- a/docs/sources/mimir/configure/configuration-parameters/index.md +++ b/docs/sources/mimir/configure/configuration-parameters/index.md @@ -1504,6 +1504,11 @@ mimir_query_engine: # CLI flag: -querier.mimir-query-engine.enable-aggregation-operations [enable_aggregation_operations: | default = true] + # (experimental) Enable support for binary comparison operations in Mimir's + # query engine. Only applies if the Mimir query engine is in use. + # CLI flag: -querier.mimir-query-engine.enable-binary-comparison-operations + [enable_binary_comparison_operations: | default = true] + # (experimental) Enable support for scalars in Mimir's query engine. Only # applies if the Mimir query engine is in use. # CLI flag: -querier.mimir-query-engine.enable-scalars diff --git a/pkg/streamingpromql/config.go b/pkg/streamingpromql/config.go index 42bbfb4bd52..77df57b3bfb 100644 --- a/pkg/streamingpromql/config.go +++ b/pkg/streamingpromql/config.go @@ -18,8 +18,9 @@ type EngineOpts struct { } type FeatureToggles struct { - EnableAggregationOperations bool `yaml:"enable_aggregation_operations" category:"experimental"` - EnableScalars bool `yaml:"enable_scalars" category:"experimental"` + EnableAggregationOperations bool `yaml:"enable_aggregation_operations" category:"experimental"` + EnableBinaryComparisonOperations bool `yaml:"enable_binary_comparison_operations" category:"experimental"` + EnableScalars bool `yaml:"enable_scalars" category:"experimental"` } // EnableAllFeatures enables all features supported by MQE, including experimental or incomplete features. @@ -27,9 +28,11 @@ var EnableAllFeatures = FeatureToggles{ // Note that we deliberately use a keyless literal here to force a compilation error if we don't keep this in sync with new fields added to FeatureToggles. true, true, + true, } func (t *FeatureToggles) RegisterFlags(f *flag.FlagSet) { f.BoolVar(&t.EnableAggregationOperations, "querier.mimir-query-engine.enable-aggregation-operations", true, "Enable support for aggregation operations in Mimir's query engine. Only applies if the Mimir query engine is in use.") + f.BoolVar(&t.EnableBinaryComparisonOperations, "querier.mimir-query-engine.enable-binary-comparison-operations", true, "Enable support for binary comparison operations in Mimir's query engine. Only applies if the Mimir query engine is in use.") f.BoolVar(&t.EnableScalars, "querier.mimir-query-engine.enable-scalars", true, "Enable support for scalars in Mimir's query engine. Only applies if the Mimir query engine is in use.") } diff --git a/pkg/streamingpromql/engine_test.go b/pkg/streamingpromql/engine_test.go index c0e814100b2..5c2dadeb425 100644 --- a/pkg/streamingpromql/engine_test.go +++ b/pkg/streamingpromql/engine_test.go @@ -42,7 +42,6 @@ func TestUnsupportedPromQLFeatures(t *testing.T) { // The goal of this is not to list every conceivable expression that is unsupported, but to cover all the // different cases and make sure we produce a reasonable error message when these cases are encountered. unsupportedExpressions := map[string]string{ - "metric{} < other_metric{}": "binary expression with '<'", "metric{} or other_metric{}": "binary expression with many-to-many matching", "metric{} + on() group_left() other_metric{}": "binary expression with many-to-one matching", "metric{} + on() group_right() other_metric{}": "binary expression with one-to-many matching", @@ -55,8 +54,7 @@ func TestUnsupportedPromQLFeatures(t *testing.T) { for expression, expectedError := range unsupportedExpressions { t.Run(expression, func(t *testing.T) { - requireRangeQueryIsUnsupported(t, featureToggles, expression, expectedError) - requireInstantQueryIsUnsupported(t, featureToggles, expression, expectedError) + requireQueryIsUnsupported(t, featureToggles, expression, expectedError) }) } @@ -78,19 +76,43 @@ func TestUnsupportedPromQLFeaturesWithFeatureToggles(t *testing.T) { featureToggles := EnableAllFeatures featureToggles.EnableAggregationOperations = false - requireRangeQueryIsUnsupported(t, featureToggles, "sum by (label) (metric)", "aggregation operations") - requireInstantQueryIsUnsupported(t, featureToggles, "sum by (label) (metric)", "aggregation operations") + requireQueryIsUnsupported(t, featureToggles, "sum by (label) (metric)", "aggregation operations") + }) + + t.Run("binary expressions with comparison operation", func(t *testing.T) { + featureToggles := EnableAllFeatures + featureToggles.EnableBinaryComparisonOperations = false + + requireQueryIsUnsupported(t, featureToggles, "metric{} > other_metric{}", "binary expression with '>'") + requireQueryIsUnsupported(t, featureToggles, "metric{} > 1", "binary expression with '>'") + requireQueryIsUnsupported(t, featureToggles, "1 > metric{}", "binary expression with '>'") + requireQueryIsUnsupported(t, featureToggles, "2 > bool 1", "binary expression with '>'") + + // Other operations should still be supported. + requireQueryIsSupported(t, featureToggles, "metric{} + other_metric{}") + requireQueryIsSupported(t, featureToggles, "metric{} + 1") + requireQueryIsSupported(t, featureToggles, "1 + metric{}") + requireQueryIsSupported(t, featureToggles, "2 + 1") }) t.Run("scalars", func(t *testing.T) { featureToggles := EnableAllFeatures featureToggles.EnableScalars = false - requireRangeQueryIsUnsupported(t, featureToggles, "2", "scalar values") - requireInstantQueryIsUnsupported(t, featureToggles, "2", "scalar values") + requireQueryIsUnsupported(t, featureToggles, "2", "scalar values") }) } +func requireQueryIsUnsupported(t *testing.T, toggles FeatureToggles, expression string, expectedError string) { + requireRangeQueryIsUnsupported(t, toggles, expression, expectedError) + requireInstantQueryIsUnsupported(t, toggles, expression, expectedError) +} + +func requireQueryIsSupported(t *testing.T, toggles FeatureToggles, expression string) { + requireRangeQueryIsSupported(t, toggles, expression) + requireInstantQueryIsSupported(t, toggles, expression) +} + func requireRangeQueryIsUnsupported(t *testing.T, featureToggles FeatureToggles, expression string, expectedError string) { opts := NewTestEngineOpts() opts.FeatureToggles = featureToggles @@ -117,6 +139,26 @@ func requireInstantQueryIsUnsupported(t *testing.T, featureToggles FeatureToggle require.Nil(t, qry) } +func requireRangeQueryIsSupported(t *testing.T, featureToggles FeatureToggles, expression string) { + opts := NewTestEngineOpts() + opts.FeatureToggles = featureToggles + engine, err := NewEngine(opts, NewStaticQueryLimitsProvider(0), stats.NewQueryMetrics(nil), log.NewNopLogger()) + require.NoError(t, err) + + _, err = engine.NewRangeQuery(context.Background(), nil, nil, expression, time.Now().Add(-time.Hour), time.Now(), time.Minute) + require.NoError(t, err) +} + +func requireInstantQueryIsSupported(t *testing.T, featureToggles FeatureToggles, expression string) { + opts := NewTestEngineOpts() + opts.FeatureToggles = featureToggles + engine, err := NewEngine(opts, NewStaticQueryLimitsProvider(0), stats.NewQueryMetrics(nil), log.NewNopLogger()) + require.NoError(t, err) + + _, err = engine.NewInstantQuery(context.Background(), nil, nil, expression, time.Now()) + require.NoError(t, err) +} + func TestNewRangeQuery_InvalidQueryTime(t *testing.T) { opts := NewTestEngineOpts() engine, err := NewEngine(opts, NewStaticQueryLimitsProvider(0), stats.NewQueryMetrics(nil), log.NewNopLogger()) diff --git a/pkg/streamingpromql/operators/scalar_scalar_binary_operation.go b/pkg/streamingpromql/operators/scalar_scalar_binary_operation.go index 453d1823c3b..5cb38bed809 100644 --- a/pkg/streamingpromql/operators/scalar_scalar_binary_operation.go +++ b/pkg/streamingpromql/operators/scalar_scalar_binary_operation.go @@ -32,20 +32,25 @@ func NewScalarScalarBinaryOperation( memoryConsumptionTracker *limiting.MemoryConsumptionTracker, expressionPosition posrange.PositionRange, ) (*ScalarScalarBinaryOperation, error) { - f := arithmeticOperationFuncs[op] - if f == nil { - return nil, compat.NewNotSupportedError(fmt.Sprintf("binary expression with '%s'", op)) - } - - return &ScalarScalarBinaryOperation{ + s := &ScalarScalarBinaryOperation{ Left: left, Right: right, Op: op, MemoryConsumptionTracker: memoryConsumptionTracker, + expressionPosition: expressionPosition, + } + + if op.IsComparisonOperator() { + s.opFunc = boolComparisonOperationFuncs[op] + } else { + s.opFunc = arithmeticAndComparisonOperationFuncs[op] + } + + if s.opFunc == nil { + return nil, compat.NewNotSupportedError(fmt.Sprintf("binary expression with '%s'", op)) + } - opFunc: f, - expressionPosition: expressionPosition, - }, nil + return s, nil } func (s *ScalarScalarBinaryOperation) GetValues(ctx context.Context) (types.ScalarData, error) { diff --git a/pkg/streamingpromql/operators/vector_scalar_binary_operation.go b/pkg/streamingpromql/operators/vector_scalar_binary_operation.go index 2acb7e0c79d..7ca6450fb81 100644 --- a/pkg/streamingpromql/operators/vector_scalar_binary_operation.go +++ b/pkg/streamingpromql/operators/vector_scalar_binary_operation.go @@ -24,6 +24,7 @@ type VectorScalarBinaryOperation struct { Vector types.InstantVectorOperator ScalarIsLeftSide bool Op parser.ItemType + ReturnBool bool MemoryConsumptionTracker *limiting.MemoryConsumptionTracker timeRange types.QueryTimeRange @@ -42,12 +43,20 @@ func NewVectorScalarBinaryOperation( vector types.InstantVectorOperator, scalarIsLeftSide bool, op parser.ItemType, + returnBool bool, timeRange types.QueryTimeRange, memoryConsumptionTracker *limiting.MemoryConsumptionTracker, annotations *annotations.Annotations, expressionPosition posrange.PositionRange, ) (*VectorScalarBinaryOperation, error) { - f := arithmeticOperationFuncs[op] + var f binaryOperationFunc + + if returnBool { + f = boolComparisonOperationFuncs[op] + } else { + f = arithmeticAndComparisonOperationFuncs[op] + } + if f == nil { return nil, compat.NewNotSupportedError(fmt.Sprintf("binary expression with '%s'", op)) } @@ -57,6 +66,7 @@ func NewVectorScalarBinaryOperation( Vector: vector, ScalarIsLeftSide: scalarIsLeftSide, Op: op, + ReturnBool: returnBool, MemoryConsumptionTracker: memoryConsumptionTracker, timeRange: timeRange, @@ -67,13 +77,23 @@ func NewVectorScalarBinaryOperation( annotations.Add(generator("", expressionPosition)) } - if b.ScalarIsLeftSide { + if !b.ScalarIsLeftSide { b.opFunc = func(scalar float64, vectorF float64, vectorH *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) { - return f(scalar, vectorF, nil, vectorH) + return f(vectorF, scalar, vectorH, nil) + } + } else if op.IsComparisonOperator() && !returnBool { + b.opFunc = func(scalar float64, vectorF float64, vectorH *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) { + _, _, ok, err := f(scalar, vectorF, nil, vectorH) + + // We always want to return the value from the vector when we're doing a filter-style comparison. + // + // We deliberately ignore the histogram value as we need to treat it as if it were a float with value 0, + // pending the resolution of the discussion in https://github.com/prometheus/prometheus/issues/13934#issuecomment-2372947976. + return vectorF, nil, ok, err } } else { b.opFunc = func(scalar float64, vectorF float64, vectorH *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) { - return f(vectorF, scalar, vectorH, nil) + return f(scalar, vectorF, nil, vectorH) } } @@ -93,11 +113,13 @@ func (v *VectorScalarBinaryOperation) SeriesMetadata(ctx context.Context) ([]typ return nil, err } - // We don't need to do deduplication and merging of series in this operator: we expect that this operator - // is wrapped in a DeduplicateAndMerge. - metadata, err = functions.DropSeriesName(metadata, v.MemoryConsumptionTracker) - if err != nil { - return nil, err + if !v.Op.IsComparisonOperator() || v.ReturnBool { + // We don't need to do deduplication and merging of series in this operator: we expect that this operator + // is wrapped in a DeduplicateAndMerge. + metadata, err = functions.DropSeriesName(metadata, v.MemoryConsumptionTracker) + if err != nil { + return nil, err + } } return metadata, nil diff --git a/pkg/streamingpromql/operators/vector_vector_binary_operation.go b/pkg/streamingpromql/operators/vector_vector_binary_operation.go index 4b8d9ded877..b0b86e14fe3 100644 --- a/pkg/streamingpromql/operators/vector_vector_binary_operation.go +++ b/pkg/streamingpromql/operators/vector_vector_binary_operation.go @@ -32,6 +32,7 @@ type VectorVectorBinaryOperation struct { Left types.InstantVectorOperator Right types.InstantVectorOperator Op parser.ItemType + ReturnBool bool MemoryConsumptionTracker *limiting.MemoryConsumptionTracker VectorMatching parser.VectorMatching @@ -80,15 +81,11 @@ func NewVectorVectorBinaryOperation( right types.InstantVectorOperator, vectorMatching parser.VectorMatching, op parser.ItemType, + returnBool bool, memoryConsumptionTracker *limiting.MemoryConsumptionTracker, annotations *annotations.Annotations, expressionPosition posrange.PositionRange, ) (*VectorVectorBinaryOperation, error) { - opFunc := arithmeticOperationFuncs[op] - if opFunc == nil { - return nil, compat.NewNotSupportedError(fmt.Sprintf("binary expression with '%s'", op)) - } - b := &VectorVectorBinaryOperation{ Left: left, Right: right, @@ -96,12 +93,22 @@ func NewVectorVectorBinaryOperation( rightIterator: types.InstantVectorSeriesDataIterator{}, VectorMatching: vectorMatching, Op: op, + ReturnBool: returnBool, MemoryConsumptionTracker: memoryConsumptionTracker, - opFunc: opFunc, expressionPosition: expressionPosition, } + if returnBool { + b.opFunc = boolComparisonOperationFuncs[op] + } else { + b.opFunc = arithmeticAndComparisonOperationFuncs[op] + } + + if b.opFunc == nil { + return nil, compat.NewNotSupportedError(fmt.Sprintf("binary expression with '%s'", op)) + } + b.emitAnnotation = func(generator functions.AnnotationGenerator) { annotations.Add(generator("", expressionPosition)) } @@ -373,6 +380,15 @@ func (b *VectorVectorBinaryOperation) groupLabelsFunc() func(labels.Labels) labe } } + if b.Op.IsComparisonOperator() && !b.ReturnBool { + // If this is a comparison operator, we want to retain the metric name, as the comparison acts like a filter. + return func(l labels.Labels) labels.Labels { + lb.Reset(l) + lb.Del(b.VectorMatching.MatchingLabels...) + return lb.Labels() + } + } + return func(l labels.Labels) labels.Labels { lb.Reset(l) lb.Del(labels.MetricName) @@ -666,7 +682,7 @@ func (b *VectorVectorBinaryOperation) Close() { type binaryOperationFunc func(lhs, rhs float64, hlhs, hrhs *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) // FIXME(jhesketh): Investigate avoiding copying histograms for binary ops. -var arithmeticOperationFuncs = map[parser.ItemType]binaryOperationFunc{ +var arithmeticAndComparisonOperationFuncs = map[parser.ItemType]binaryOperationFunc{ parser.ADD: func(lhs, rhs float64, hlhs, hrhs *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) { if hlhs != nil && hrhs != nil { res, err := hlhs.Copy().Add(hrhs) @@ -711,4 +727,91 @@ var arithmeticOperationFuncs = map[parser.ItemType]binaryOperationFunc{ parser.ATAN2: func(lhs, rhs float64, _, _ *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) { return math.Atan2(lhs, rhs), nil, true, nil }, + parser.EQLC: func(lhs, rhs float64, _, _ *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) { + if lhs == rhs { + return lhs, nil, true, nil + } + + return 0, nil, false, nil + }, + parser.NEQ: func(lhs, rhs float64, _, _ *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) { + if lhs != rhs { + return lhs, nil, true, nil + } + + return 0, nil, false, nil + }, + parser.LTE: func(lhs, rhs float64, _, _ *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) { + if lhs <= rhs { + return lhs, nil, true, nil + } + + return 0, nil, false, nil + }, + parser.LSS: func(lhs, rhs float64, _, _ *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) { + if lhs < rhs { + return lhs, nil, true, nil + } + + return 0, nil, false, nil + }, + parser.GTE: func(lhs, rhs float64, _, _ *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) { + if lhs >= rhs { + return lhs, nil, true, nil + } + + return 0, nil, false, nil + }, + parser.GTR: func(lhs, rhs float64, _, _ *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) { + if lhs > rhs { + return lhs, nil, true, nil + } + + return 0, nil, false, nil + }, +} + +var boolComparisonOperationFuncs = map[parser.ItemType]binaryOperationFunc{ + parser.EQLC: func(lhs, rhs float64, _, _ *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) { + if lhs == rhs { + return 1, nil, true, nil + } + + return 0, nil, true, nil + }, + parser.NEQ: func(lhs, rhs float64, _, _ *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) { + if lhs != rhs { + return 1, nil, true, nil + } + + return 0, nil, true, nil + }, + parser.LTE: func(lhs, rhs float64, _, _ *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) { + if lhs <= rhs { + return 1, nil, true, nil + } + + return 0, nil, true, nil + }, + parser.LSS: func(lhs, rhs float64, _, _ *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) { + if lhs < rhs { + return 1, nil, true, nil + } + + return 0, nil, true, nil + }, + parser.GTE: func(lhs, rhs float64, _, _ *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) { + if lhs >= rhs { + return 1, nil, true, nil + } + + return 0, nil, true, nil + }, + parser.GTR: func(lhs, rhs float64, _, _ *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool, error) { + if lhs > rhs { + return 1, nil, true, nil + } + + return 0, nil, true, nil + }, } diff --git a/pkg/streamingpromql/query.go b/pkg/streamingpromql/query.go index 0e2394fbdfe..7456503ce8e 100644 --- a/pkg/streamingpromql/query.go +++ b/pkg/streamingpromql/query.go @@ -165,6 +165,10 @@ func (q *Query) convertToInstantVectorOperator(expr parser.Expr) (types.InstantV case *parser.Call: return q.convertFunctionCallToInstantVectorOperator(e) case *parser.BinaryExpr: + if e.Op.IsComparisonOperator() && !q.engine.featureToggles.EnableBinaryComparisonOperations { + return nil, compat.NewNotSupportedError(fmt.Sprintf("binary expression with '%v'", e.Op)) + } + // We only need to handle three combinations of types here: // Scalar on left, vector on right // Vector on left, scalar on right @@ -201,7 +205,7 @@ func (q *Query) convertToInstantVectorOperator(expr parser.Expr) (types.InstantV scalarIsLeftSide := e.LHS.Type() == parser.ValueTypeScalar - o, err := operators.NewVectorScalarBinaryOperation(scalar, vector, scalarIsLeftSide, e.Op, q.timeRange, q.memoryConsumptionTracker, q.annotations, e.PositionRange()) + o, err := operators.NewVectorScalarBinaryOperation(scalar, vector, scalarIsLeftSide, e.Op, e.ReturnBool, q.timeRange, q.memoryConsumptionTracker, q.annotations, e.PositionRange()) if err != nil { return nil, err } @@ -225,7 +229,7 @@ func (q *Query) convertToInstantVectorOperator(expr parser.Expr) (types.InstantV return nil, err } - return operators.NewVectorVectorBinaryOperation(lhs, rhs, *e.VectorMatching, e.Op, q.memoryConsumptionTracker, q.annotations, e.PositionRange()) + return operators.NewVectorVectorBinaryOperation(lhs, rhs, *e.VectorMatching, e.Op, e.ReturnBool, q.memoryConsumptionTracker, q.annotations, e.PositionRange()) case *parser.UnaryExpr: if e.Op != parser.SUB { return nil, compat.NewNotSupportedError(fmt.Sprintf("unary expression with '%s'", e.Op)) @@ -338,6 +342,10 @@ func (q *Query) convertToScalarOperator(expr parser.Expr) (types.ScalarOperator, case *parser.ParenExpr: return q.convertToScalarOperator(e.Expr) case *parser.BinaryExpr: + if e.Op.IsComparisonOperator() && !q.engine.featureToggles.EnableBinaryComparisonOperations { + return nil, compat.NewNotSupportedError(fmt.Sprintf("binary expression with '%v'", e.Op)) + } + lhs, err := q.convertToScalarOperator(e.LHS) if err != nil { return nil, err diff --git a/pkg/streamingpromql/testdata/ours-only/binary_operators.test b/pkg/streamingpromql/testdata/ours-only/binary_operators.test new file mode 100644 index 00000000000..dc26d439c94 --- /dev/null +++ b/pkg/streamingpromql/testdata/ours-only/binary_operators.test @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: AGPL-3.0-only + +load 6m + left_histograms {{schema:3 sum:4 count:4 buckets:[1 2 1]}} {{schema:3 sum:4.5 count:5 buckets:[1 3 1]}} _ _ {{schema:3 sum:4.5 count:5 buckets:[1 3 1]}} + +# These cases currently fails in Prometheus' engine due to https://github.com/prometheus/prometheus/issues/13934#issuecomment-2373132091. +# Once the issue described there has been resolved, these test cases can be removed and the corresponding test cases in +# testdata/ours/binary_operators.test can be enabled. +eval range from 0 to 24m step 6m 0 == left_histograms + left_histograms 0 0 _ _ 0 + +eval range from 0 to 24m step 6m 3 != left_histograms + left_histograms 0 0 _ _ 0 + +eval range from 0 to 24m step 6m 3 > left_histograms + left_histograms 0 0 _ _ 0 + +eval range from 0 to 24m step 6m 3 >= left_histograms + left_histograms 0 0 _ _ 0 + +eval range from 0 to 24m step 6m 0 >= left_histograms + left_histograms 0 0 _ _ 0 diff --git a/pkg/streamingpromql/testdata/ours/binary_operators.test b/pkg/streamingpromql/testdata/ours/binary_operators.test index 3a8e281ba5b..a0612c6e1bc 100644 --- a/pkg/streamingpromql/testdata/ours/binary_operators.test +++ b/pkg/streamingpromql/testdata/ours/binary_operators.test @@ -361,10 +361,10 @@ eval_fail range from 0m to 24m step 6m 2 * {job="bar"} clear +# Scalars on both sides. load 6m metric 1 2 _ 3 stale 4 {{schema:3 sum:4 count:4 buckets:[1 2 1]}} 5 -# Scalars on both sides. eval range from 0m to 3m step 1m 1 + 2 {} 3 3 3 3 @@ -388,3 +388,358 @@ eval range from 0m to 3m step 1m 2 atan2 3 eval range from 0m to 42m step 6m scalar(metric) + 2 {} 3 4 NaN 5 NaN 6 NaN 7 + +clear + +# Comparison operations +# Note that native histograms are currently treated as 0s for comparison operations. +load 6m + left_floats 1 2 _ _ 3 stale 4 5 NaN Inf -Inf + right_floats 4 _ _ 5 3 7 -1 20 NaN Inf -Inf + left_histograms {{schema:3 sum:4 count:4 buckets:[1 2 1]}} {{schema:3 sum:4.5 count:5 buckets:[1 3 1]}} _ _ {{schema:3 sum:4.5 count:5 buckets:[1 3 1]}} + right_histograms {{schema:3 sum:4 count:4 buckets:[1 2 1]}} {{schema:3 sum:4 count:4 buckets:[1 2 1]}} {{schema:3 sum:4 count:4 buckets:[1 2 1]}} _ _ + right_floats_for_histograms 0 -1 2 3 4 + +# Vector/vector combinations +eval range from 0 to 60m step 6m left_floats == right_floats + left_floats _ _ _ _ 3 _ _ _ _ Inf -Inf + +eval range from 0 to 60m step 6m left_floats == bool right_floats + {} 0 _ _ _ 1 _ 0 0 0 1 1 + +eval range from 0 to 60m step 6m left_floats == does_not_match + # No results. + +eval range from 0 to 24m step 6m left_histograms == right_histograms + left_histograms 0 0 _ _ _ + +eval range from 0 to 24m step 6m left_histograms == bool right_histograms + {} 1 1 _ _ _ + +eval range from 0 to 24m step 6m left_histograms == right_floats_for_histograms + left_histograms 0 _ _ _ _ + +eval range from 0 to 24m step 6m left_histograms == bool right_floats_for_histograms + {} 1 0 _ _ 0 + +eval range from 0 to 60m step 6m left_floats != right_floats + left_floats 1 _ _ _ _ _ 4 5 NaN _ _ + +eval range from 0 to 60m step 6m left_floats != bool right_floats + {} 1 _ _ _ 0 _ 1 1 1 0 0 + +eval range from 0 to 24m step 6m left_histograms != right_histograms + # No results. + +eval range from 0 to 24m step 6m left_histograms != bool right_histograms + {} 0 0 _ _ _ + +eval range from 0 to 24m step 6m left_histograms != right_floats_for_histograms + left_histograms _ 0 _ _ 0 + +eval range from 0 to 24m step 6m left_histograms != bool right_floats_for_histograms + {} 0 1 _ _ 1 + +eval range from 0 to 60m step 6m left_floats > right_floats + left_floats _ _ _ _ _ _ 4 _ _ _ _ + +eval range from 0 to 60m step 6m left_floats > bool right_floats + {} 0 _ _ _ 0 _ 1 0 0 0 0 + +eval range from 0 to 24m step 6m left_histograms > right_histograms + # No results. + +eval range from 0 to 24m step 6m left_histograms > bool right_histograms + {} 0 0 _ _ _ + +eval range from 0 to 24m step 6m left_histograms > right_floats_for_histograms + left_histograms _ 0 _ _ _ + +eval range from 0 to 24m step 6m left_histograms > bool right_floats_for_histograms + {} 0 1 _ _ 0 + +eval range from 0 to 60m step 6m left_floats >= right_floats + left_floats _ _ _ _ 3 _ 4 _ _ Inf -Inf + +eval range from 0 to 60m step 6m left_floats >= bool right_floats + {} 0 _ _ _ 1 _ 1 0 0 1 1 + +eval range from 0 to 24m step 6m left_histograms >= right_histograms + left_histograms 0 0 _ _ _ + +eval range from 0 to 24m step 6m left_histograms >= bool right_histograms + {} 1 1 _ _ _ + +eval range from 0 to 24m step 6m left_histograms >= right_floats_for_histograms + left_histograms 0 0 _ _ _ + +eval range from 0 to 24m step 6m left_histograms >= bool right_floats_for_histograms + {} 1 1 _ _ 0 + +eval range from 0 to 60m step 6m left_floats < right_floats + left_floats 1 _ _ _ _ _ _ 5 _ _ _ + +eval range from 0 to 60m step 6m left_floats < bool right_floats + {} 1 _ _ _ 0 _ 0 1 0 0 0 + +eval range from 0 to 24m step 6m left_histograms < right_histograms + # No results. + +eval range from 0 to 24m step 6m left_histograms < bool right_histograms + {} 0 0 _ _ _ + +eval range from 0 to 24m step 6m left_histograms < right_floats_for_histograms + left_histograms _ _ _ _ 0 + +eval range from 0 to 24m step 6m left_histograms < bool right_floats_for_histograms + {} 0 0 _ _ 1 + +eval range from 0 to 60m step 6m left_floats <= right_floats + left_floats 1 _ _ _ 3 _ _ 5 _ Inf -Inf + +eval range from 0 to 60m step 6m left_floats <= bool right_floats + {} 1 _ _ _ 1 _ 0 1 0 1 1 + +eval range from 0 to 24m step 6m left_histograms <= right_histograms + left_histograms 0 0 _ _ _ + +eval range from 0 to 24m step 6m left_histograms <= bool right_histograms + {} 1 1 _ _ _ + +eval range from 0 to 24m step 6m left_histograms <= right_floats_for_histograms + left_histograms 0 _ _ _ 0 + +eval range from 0 to 24m step 6m left_histograms <= bool right_floats_for_histograms + {} 1 0 _ _ 1 + +# Vector / scalar combinations with scalar on right side +eval range from 0 to 60m step 6m left_floats == 3 + left_floats _ _ _ _ 3 _ _ _ _ _ _ + +eval range from 0 to 60m step 6m left_floats != 3 + left_floats 1 2 _ _ _ _ 4 5 NaN Inf -Inf + +eval range from 0 to 60m step 6m left_floats > 3 + left_floats _ _ _ _ _ _ 4 5 _ Inf _ + +eval range from 0 to 60m step 6m left_floats >= 3 + left_floats _ _ _ _ 3 _ 4 5 _ Inf _ + +eval range from 0 to 60m step 6m left_floats < 3 + left_floats 1 2 _ _ _ _ _ _ _ _ -Inf + +eval range from 0 to 60m step 6m left_floats <= 3 + left_floats 1 2 _ _ 3 _ _ _ _ _ -Inf + +eval range from 0 to 60m step 6m left_floats == bool 3 + {} 0 0 _ _ 1 _ 0 0 0 0 0 + +eval range from 0 to 60m step 6m left_floats == Inf + left_floats _ _ _ _ _ _ _ _ _ Inf _ + +eval range from 0 to 60m step 6m left_floats == bool Inf + {} 0 0 _ _ 0 _ 0 0 0 1 0 + +eval range from 0 to 60m step 6m left_floats == NaN + # No results. + +eval range from 0 to 60m step 6m left_floats == bool NaN + {} 0 0 _ _ 0 _ 0 0 0 0 0 + +eval range from 0 to 24m step 6m left_histograms == 3 + # No results. + +eval range from 0 to 24m step 6m left_histograms == 0 + left_histograms 0 0 _ _ 0 + +eval range from 0 to 24m step 6m left_histograms != 3 + left_histograms 0 0 _ _ 0 + +eval range from 0 to 24m step 6m left_histograms != 0 + # No results. + +eval range from 0 to 24m step 6m left_histograms > 3 + # No results. + +eval range from 0 to 24m step 6m left_histograms > 0 + # No results. + +eval range from 0 to 24m step 6m left_histograms >= 3 + # No results. + +eval range from 0 to 24m step 6m left_histograms >= 0 + left_histograms 0 0 _ _ 0 + +eval range from 0 to 24m step 6m left_histograms < 3 + left_histograms 0 0 _ _ 0 + +eval range from 0 to 24m step 6m left_histograms < 0 + # No results. + +eval range from 0 to 24m step 6m left_histograms <= 3 + left_histograms 0 0 _ _ 0 + +eval range from 0 to 24m step 6m left_histograms <= 0 + left_histograms 0 0 _ _ 0 + +eval range from 0 to 24m step 6m left_histograms == bool 3 + {} 0 0 _ _ 0 + +eval range from 0 to 24m step 6m left_histograms == bool 0 + {} 1 1 _ _ 1 + +eval range from 0 to 24m step 6m left_histograms != bool 3 + {} 1 1 _ _ 1 + +eval range from 0 to 24m step 6m left_histograms != bool 0 + {} 0 0 _ _ 0 + +eval range from 0 to 24m step 6m left_histograms > bool 3 + {} 0 0 _ _ 0 + +eval range from 0 to 24m step 6m left_histograms > bool 0 + {} 0 0 _ _ 0 + +eval range from 0 to 24m step 6m left_histograms >= bool 3 + {} 0 0 _ _ 0 + +eval range from 0 to 24m step 6m left_histograms >= bool 0 + {} 1 1 _ _ 1 + +eval range from 0 to 24m step 6m left_histograms < bool 3 + {} 1 1 _ _ 1 + +eval range from 0 to 24m step 6m left_histograms < bool 0 + {} 0 0 _ _ 0 + +eval range from 0 to 24m step 6m left_histograms <= bool 3 + {} 1 1 _ _ 1 + +eval range from 0 to 24m step 6m left_histograms <= bool 0 + {} 1 1 _ _ 1 + +# Vector / scalar combinations with scalar on left side +eval range from 0 to 60m step 6m 3 == left_floats + left_floats _ _ _ _ 3 _ _ _ _ _ _ + +eval range from 0 to 60m step 6m 3 != left_floats + left_floats 1 2 _ _ _ _ 4 5 NaN Inf -Inf + +eval range from 0 to 60m step 6m 3 < left_floats + left_floats _ _ _ _ _ _ 4 5 _ Inf _ + +eval range from 0 to 60m step 6m 3 <= left_floats + left_floats _ _ _ _ 3 _ 4 5 _ Inf _ + +eval range from 0 to 60m step 6m 3 > left_floats + left_floats 1 2 _ _ _ _ _ _ _ _ -Inf + +eval range from 0 to 60m step 6m 3 >= left_floats + left_floats 1 2 _ _ 3 _ _ _ _ _ -Inf + +eval range from 0 to 60m step 6m 3 == bool left_floats + {} 0 0 _ _ 1 _ 0 0 0 0 0 + +eval range from 0 to 60m step 6m Inf == left_floats + left_floats _ _ _ _ _ _ _ _ _ Inf _ + +eval range from 0 to 60m step 6m Inf == bool left_floats + {} 0 0 _ _ 0 _ 0 0 0 1 0 + +eval range from 0 to 60m step 6m NaN == left_floats + # No results. + +eval range from 0 to 60m step 6m NaN == bool left_floats + {} 0 0 _ _ 0 _ 0 0 0 0 0 + +eval range from 0 to 24m step 6m 3 == left_histograms + # No results. + +# This case currently fails in Prometheus' engine due to https://github.com/prometheus/prometheus/issues/13934#issuecomment-2373132091. +# It has been moved to testdata/ours-only/binary_operators.test until the issue described there +# has been resolved. +# eval range from 0 to 24m step 6m 0 == left_histograms +# left_histograms 0 0 _ _ 0 + +# This case currently fails in Prometheus' engine due to https://github.com/prometheus/prometheus/issues/13934#issuecomment-2373132091. +# It has been moved to testdata/ours-only/binary_operators.test until the issue described there +# has been resolved. +# eval range from 0 to 24m step 6m 3 != left_histograms +# left_histograms 0 0 _ _ 0 + +eval range from 0 to 24m step 6m 0 != left_histograms + # No results. + +eval range from 0 to 24m step 6m 3 < left_histograms + # No results. + +eval range from 0 to 24m step 6m 0 < left_histograms + # No results. + +eval range from 0 to 24m step 6m 3 < left_histograms + # No results. + +eval range from 0 to 24m step 6m 0 < left_histograms + # No results. + +# This case currently fails in Prometheus' engine due to https://github.com/prometheus/prometheus/issues/13934#issuecomment-2373132091. +# It has been moved to testdata/ours-only/binary_operators.test until the issue described there has been resolved. +# eval range from 0 to 24m step 6m 3 > left_histograms +# left_histograms 0 0 _ _ 0 + +eval range from 0 to 24m step 6m 0 > left_histograms + # No results. + +# This case currently fails in Prometheus' engine due to https://github.com/prometheus/prometheus/issues/13934#issuecomment-2373132091. +# It has been moved to testdata/ours-only/binary_operators.test until the issue described there has been resolved. +# eval range from 0 to 24m step 6m 3 >= left_histograms +# left_histograms 0 0 _ _ 0 + +# This case currently fails in Prometheus' engine due to https://github.com/prometheus/prometheus/issues/13934#issuecomment-2373132091. +# It has been moved to testdata/ours-only/binary_operators.test until the issue described there has been resolved. +# eval range from 0 to 24m step 6m 0 >= left_histograms +# left_histograms 0 0 _ _ 0 + +# Scalar / scalar combinations +eval range from 0 to 60m step 6m scalar(left_floats) == bool 3 + {} 0 0 0 0 1 0 0 0 0 0 0 + +eval range from 0 to 60m step 6m scalar(left_floats) != bool 3 + {} 1 1 1 1 0 1 1 1 1 1 1 + +eval range from 0 to 60m step 6m scalar(left_floats) > bool 3 + {} 0 0 0 0 0 0 1 1 0 1 0 + +eval range from 0 to 60m step 6m scalar(left_floats) >= bool 3 + {} 0 0 0 0 1 0 1 1 0 1 0 + +eval range from 0 to 60m step 6m scalar(left_floats) < bool 3 + {} 1 1 0 0 0 0 0 0 0 0 1 + +eval range from 0 to 60m step 6m scalar(left_floats) <= bool 3 + {} 1 1 0 0 1 0 0 0 0 0 1 + +clear + +# Vector/vector comparison operations with label matching with on and ignoring. +load 6m + left_side{env="test", pod="a"} 1 2 3 4 5 + left_side{env="prod", pod="b"} 10 20 30 40 50 + right_side{env="test", pod="c"} 5 4 3 2 1 + right_side{env="prod", pod="d"} 50 40 30 20 10 + +eval range from 0 to 24m step 6m left_side > on (env) right_side + {env="test"} _ _ _ 4 5 + {env="prod"} _ _ _ 40 50 + +eval range from 0 to 24m step 6m left_side > ignoring (pod) right_side + left_side{env="test"} _ _ _ 4 5 + left_side{env="prod"} _ _ _ 40 50 + +eval range from 0 to 24m step 6m left_side > bool on (env) right_side + {env="test"} 0 0 0 1 1 + {env="prod"} 0 0 0 1 1 + +eval range from 0 to 24m step 6m left_side > bool ignoring (pod) right_side + {env="test"} 0 0 0 1 1 + {env="prod"} 0 0 0 1 1 diff --git a/pkg/streamingpromql/testdata/upstream/name_label_dropping.test b/pkg/streamingpromql/testdata/upstream/name_label_dropping.test index 21ddba61158..64805de3458 100644 --- a/pkg/streamingpromql/testdata/upstream/name_label_dropping.test +++ b/pkg/streamingpromql/testdata/upstream/name_label_dropping.test @@ -21,14 +21,12 @@ eval instant at 15m metric + another_metric {env="1"} 300 # Does not drop __name__ for binary comparison operators -# Unsupported by streaming engine. -# eval instant at 15m metric <= another_metric -# metric{env="1"} 120 +eval instant at 15m metric <= another_metric + metric{env="1"} 120 # Drops __name__ for binary comparison operators with "bool" modifier -# Unsupported by streaming engine. -# eval instant at 15m metric <= bool another_metric -# {env="1"} 1 +eval instant at 15m metric <= bool another_metric + {env="1"} 1 # Drops __name__ for vector-scalar operations eval instant at 15m metric * 2 diff --git a/pkg/streamingpromql/testdata/upstream/operators.test b/pkg/streamingpromql/testdata/upstream/operators.test index 4d7bc84fa31..b1d0754a162 100644 --- a/pkg/streamingpromql/testdata/upstream/operators.test +++ b/pkg/streamingpromql/testdata/upstream/operators.test @@ -274,52 +274,41 @@ eval instant at 50m http_requests{group="canary"} / ignoring(group) http_request # Comparisons. -# Unsupported by streaming engine. -# eval instant at 50m SUM(http_requests) BY (job) > 1000 -# {job="app-server"} 2600 +eval instant at 50m SUM(http_requests) BY (job) > 1000 + {job="app-server"} 2600 -# Unsupported by streaming engine. -# eval instant at 50m 1000 < SUM(http_requests) BY (job) -# {job="app-server"} 2600 +eval instant at 50m 1000 < SUM(http_requests) BY (job) + {job="app-server"} 2600 -# Unsupported by streaming engine. -# eval instant at 50m SUM(http_requests) BY (job) <= 1000 -# {job="api-server"} 1000 +eval instant at 50m SUM(http_requests) BY (job) <= 1000 + {job="api-server"} 1000 -# Unsupported by streaming engine. -# eval instant at 50m SUM(http_requests) BY (job) != 1000 -# {job="app-server"} 2600 +eval instant at 50m SUM(http_requests) BY (job) != 1000 + {job="app-server"} 2600 -# Unsupported by streaming engine. -# eval instant at 50m SUM(http_requests) BY (job) == 1000 -# {job="api-server"} 1000 +eval instant at 50m SUM(http_requests) BY (job) == 1000 + {job="api-server"} 1000 -# Unsupported by streaming engine. -# eval instant at 50m SUM(http_requests) BY (job) == bool 1000 -# {job="api-server"} 1 -# {job="app-server"} 0 +eval instant at 50m SUM(http_requests) BY (job) == bool 1000 + {job="api-server"} 1 + {job="app-server"} 0 -# Unsupported by streaming engine. -# eval instant at 50m SUM(http_requests) BY (job) == bool SUM(http_requests) BY (job) -# {job="api-server"} 1 -# {job="app-server"} 1 +eval instant at 50m SUM(http_requests) BY (job) == bool SUM(http_requests) BY (job) + {job="api-server"} 1 + {job="app-server"} 1 -# Unsupported by streaming engine. -# eval instant at 50m SUM(http_requests) BY (job) != bool SUM(http_requests) BY (job) -# {job="api-server"} 0 -# {job="app-server"} 0 +eval instant at 50m SUM(http_requests) BY (job) != bool SUM(http_requests) BY (job) + {job="api-server"} 0 + {job="app-server"} 0 -# Unsupported by streaming engine. -# eval instant at 50m 0 == bool 1 -# 0 +eval instant at 50m 0 == bool 1 + 0 -# Unsupported by streaming engine. -# eval instant at 50m 1 == bool 1 -# 1 +eval instant at 50m 1 == bool 1 + 1 -# Unsupported by streaming engine. -# eval instant at 50m http_requests{job="api-server", instance="0", group="production"} == bool 100 -# {job="api-server", instance="0", group="production"} 1 +eval instant at 50m http_requests{job="api-server", instance="0", group="production"} == bool 100 + {job="api-server", instance="0", group="production"} 1 # group_left/group_right. @@ -507,20 +496,16 @@ load 5m test_total{instance="localhost"} 50 test_smaller{instance="localhost"} 10 -# Unsupported by streaming engine. -# eval instant at 5m test_total > bool test_smaller -# {instance="localhost"} 1 +eval instant at 5m test_total > bool test_smaller + {instance="localhost"} 1 -# Unsupported by streaming engine. -# eval instant at 5m test_total > test_smaller -# test_total{instance="localhost"} 50 +eval instant at 5m test_total > test_smaller + test_total{instance="localhost"} 50 -# Unsupported by streaming engine. -# eval instant at 5m test_total < bool test_smaller -# {instance="localhost"} 0 +eval instant at 5m test_total < bool test_smaller + {instance="localhost"} 0 -# Unsupported by streaming engine. -# eval instant at 5m test_total < test_smaller +eval instant at 5m test_total < test_smaller clear