diff --git a/go.mod b/go.mod index 174641a80dc..98179af0ec1 100644 --- a/go.mod +++ b/go.mod @@ -52,7 +52,7 @@ require ( github.com/spf13/afero v1.9.5 github.com/stretchr/testify v1.8.4 github.com/thanos-io/objstore v0.0.0-20231231041903-61cfed8cbb9d - github.com/thanos-io/promql-engine v0.0.0-20231214130043-41b2cf818e81 + github.com/thanos-io/promql-engine v0.0.0-20240114143142-008379eda2f9 github.com/thanos-io/thanos v0.33.1-0.20231224215600-665e64370a2c github.com/uber/jaeger-client-go v2.30.0+incompatible github.com/weaveworks/common v0.0.0-20221201103051-7c2720a9024d diff --git a/go.sum b/go.sum index 2cb70f71f83..b90da8efeb8 100644 --- a/go.sum +++ b/go.sum @@ -1447,8 +1447,8 @@ github.com/thanos-community/galaxycache v0.0.0-20211122094458-3a32041a1f1e h1:f1 github.com/thanos-community/galaxycache v0.0.0-20211122094458-3a32041a1f1e/go.mod h1:jXcofnrSln/cLI6/dhlBxPQZEEQHVPCcFaH75M+nSzM= github.com/thanos-io/objstore v0.0.0-20231231041903-61cfed8cbb9d h1:rTz1FIWYPS4R7H/hdZPaVRfevHdfv7BOjJ2FQG747KQ= github.com/thanos-io/objstore v0.0.0-20231231041903-61cfed8cbb9d/go.mod h1:RMvJQnpB4QQiYGg1gF8mnPJg6IkIPY28Buh8f6b+F0c= -github.com/thanos-io/promql-engine v0.0.0-20231214130043-41b2cf818e81 h1:EdFfVjUhwfj6JRjuZf+EchsxBD+60T6X0rPbzhraJj4= -github.com/thanos-io/promql-engine v0.0.0-20231214130043-41b2cf818e81/go.mod h1:uzl2mg4OyB9A54Hhrk/wViZiZoHT2o2qq+NGQkEmfzs= +github.com/thanos-io/promql-engine v0.0.0-20240114143142-008379eda2f9 h1:eycUuFjQHWGwn1c/UkJqixYPq3vMxpMFiNvTcWjZNUs= +github.com/thanos-io/promql-engine v0.0.0-20240114143142-008379eda2f9/go.mod h1:3pmodeI6v0zeezI1m9dE0ZaUXqiNSceZj1ZrQIXvHE4= github.com/thanos-io/thanos v0.33.1-0.20231224215600-665e64370a2c h1:4Ftcc3CJL6puX5FQUvFzhjhM6xF6NdlEZPrxMzeZ6vY= github.com/thanos-io/thanos v0.33.1-0.20231224215600-665e64370a2c/go.mod h1:P7euwwXd8qA71hRJj/EiE4rDC3354FAW2wB2/qW4agA= github.com/themihai/gomemcache v0.0.0-20180902122335-24332e2d58ab h1:7ZR3hmisBWw77ZpO1/o86g+JV3VKlk3d48jopJxzTjU= diff --git a/pkg/cortexpb/compat.go b/pkg/cortexpb/compat.go index 79e446facea..e05222dec5b 100644 --- a/pkg/cortexpb/compat.go +++ b/pkg/cortexpb/compat.go @@ -10,13 +10,11 @@ import ( "time" "unsafe" + "github.com/cortexproject/cortex/pkg/util" jsoniter "github.com/json-iterator/go" "github.com/prometheus/common/model" "github.com/prometheus/prometheus/model/exemplar" "github.com/prometheus/prometheus/model/labels" - "github.com/prometheus/prometheus/model/textparse" - - "github.com/cortexproject/cortex/pkg/util" ) // ToWriteRequest converts matched slices of Labels, Samples and Metadata into a WriteRequest proto. @@ -158,26 +156,26 @@ func (s byLabel) Swap(i, j int) { s[i], s[j] = s[j], s[i] } // MetricMetadataMetricTypeToMetricType converts a metric type from our internal client // to a Prometheus one. -func MetricMetadataMetricTypeToMetricType(mt MetricMetadata_MetricType) textparse.MetricType { +func MetricMetadataMetricTypeToMetricType(mt MetricMetadata_MetricType) model.MetricType { switch mt { case UNKNOWN: - return textparse.MetricTypeUnknown + return model.MetricTypeUnknown case COUNTER: - return textparse.MetricTypeCounter + return model.MetricTypeCounter case GAUGE: - return textparse.MetricTypeGauge + return model.MetricTypeGauge case HISTOGRAM: - return textparse.MetricTypeHistogram + return model.MetricTypeHistogram case GAUGEHISTOGRAM: - return textparse.MetricTypeGaugeHistogram + return model.MetricTypeGaugeHistogram case SUMMARY: - return textparse.MetricTypeSummary + return model.MetricTypeSummary case INFO: - return textparse.MetricTypeInfo + return model.MetricTypeInfo case STATESET: - return textparse.MetricTypeStateset + return model.MetricTypeStateset default: - return textparse.MetricTypeUnknown + return model.MetricTypeUnknown } } diff --git a/pkg/cortexpb/compat_test.go b/pkg/cortexpb/compat_test.go index fed0870ba7f..6fda91a84ee 100644 --- a/pkg/cortexpb/compat_test.go +++ b/pkg/cortexpb/compat_test.go @@ -7,8 +7,8 @@ import ( "unsafe" jsoniter "github.com/json-iterator/go" + "github.com/prometheus/common/model" "github.com/prometheus/prometheus/model/labels" - "github.com/prometheus/prometheus/model/textparse" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -75,22 +75,22 @@ func TestMetricMetadataToMetricTypeToMetricType(t *testing.T) { tc := []struct { desc string input MetricMetadata_MetricType - expected textparse.MetricType + expected model.MetricType }{ { desc: "with a single-word metric", input: COUNTER, - expected: textparse.MetricTypeCounter, + expected: model.MetricTypeCounter, }, { desc: "with a two-word metric", input: STATESET, - expected: textparse.MetricTypeStateset, + expected: model.MetricTypeStateset, }, { desc: "with an unknown metric", input: MetricMetadata_MetricType(100), - expected: textparse.MetricTypeUnknown, + expected: model.MetricTypeUnknown, }, } diff --git a/vendor/github.com/thanos-io/promql-engine/engine/engine.go b/vendor/github.com/thanos-io/promql-engine/engine/engine.go index 7c2aa249285..dbfbdc8ac84 100644 --- a/vendor/github.com/thanos-io/promql-engine/engine/engine.go +++ b/vendor/github.com/thanos-io/promql-engine/engine/engine.go @@ -65,10 +65,6 @@ type Opts struct { // This will default to false. EnableXFunctions bool - // EnableSubqueries enables the engine to handle subqueries without falling back to prometheus. - // This will default to false. - EnableSubqueries bool - // FallbackEngine Engine v1.QueryEngine @@ -158,13 +154,19 @@ func New(opts Opts) *compatibilityEngine { metrics: metrics, extLookbackDelta: opts.ExtLookbackDelta, enableAnalysis: opts.EnableAnalysis, - enableSubqueries: opts.EnableSubqueries, noStepSubqueryIntervalFn: func(d time.Duration) time.Duration { return time.Duration(opts.NoStepSubqueryIntervalFn(d.Milliseconds()) * 1000000) }, } } +var ( + // Duplicate label checking logic uses a bitmap with 64 bits currently. + // As long as we use this method we need to have batches that are smaller + // then 64 steps. + ErrStepsBatchTooLarge = errors.New("'StepsBatch' must be less than 64") +) + type compatibilityEngine struct { prom v1.QueryEngine functions map[string]*parser.Function @@ -178,7 +180,6 @@ type compatibilityEngine struct { extLookbackDelta time.Duration enableAnalysis bool - enableSubqueries bool noStepSubqueryIntervalFn func(time.Duration) time.Duration } @@ -213,9 +214,11 @@ func (e *compatibilityEngine) NewInstantQuery(ctx context.Context, q storage.Que LookbackDelta: opts.LookbackDelta(), ExtLookbackDelta: e.extLookbackDelta, EnableAnalysis: e.enableAnalysis, - EnableSubqueries: e.enableSubqueries, NoStepSubqueryIntervalFn: e.noStepSubqueryIntervalFn, } + if qOpts.StepsBatch > 64 { + return nil, ErrStepsBatchTooLarge + } lplan, warns := logicalplan.New(expr, qOpts).Optimize(e.logicalOptimizers) exec, err := execution.New(lplan.Expr(), q, qOpts) @@ -266,9 +269,11 @@ func (e *compatibilityEngine) NewRangeQuery(ctx context.Context, q storage.Query LookbackDelta: opts.LookbackDelta(), ExtLookbackDelta: e.extLookbackDelta, EnableAnalysis: e.enableAnalysis, - EnableSubqueries: false, // not yet implemented for range queries. NoStepSubqueryIntervalFn: e.noStepSubqueryIntervalFn, } + if qOpts.StepsBatch > 64 { + return nil, ErrStepsBatchTooLarge + } lplan, warns := logicalplan.New(expr, qOpts).Optimize(e.logicalOptimizers) exec, err := execution.New(lplan.Expr(), q, qOpts) @@ -303,7 +308,7 @@ func (q *Query) Explain() *ExplainOutputNode { func (q *Query) Analyze() *AnalyzeOutputNode { if observableRoot, ok := q.exec.(model.ObservableVectorOperator); ok { - return analyzeVector(observableRoot) + return analyzeQuery(observableRoot) } return nil } @@ -418,11 +423,7 @@ loop: var result parser.Value switch q.expr.Type() { case parser.ValueTypeMatrix: - matrix := promql.Matrix(series) - if matrix.ContainsSameLabelset() { - return newErrResult(ret, extlabels.ErrDuplicateLabelSet) - } - result = matrix + result = promql.Matrix(series) case parser.ValueTypeVector: // Convert matrix with one value per series into vector. vector := make(promql.Vector, 0, len(resultSeries)) @@ -447,9 +448,6 @@ loop: } } sort.Slice(vector, q.resultSort.comparer(&vector)) - if vector.ContainsSameLabelset() { - return newErrResult(ret, extlabels.ErrDuplicateLabelSet) - } result = vector case parser.ValueTypeScalar: v := math.NaN() diff --git a/vendor/github.com/thanos-io/promql-engine/engine/explain.go b/vendor/github.com/thanos-io/promql-engine/engine/explain.go index 10d6f067615..61630b9b2be 100644 --- a/vendor/github.com/thanos-io/promql-engine/engine/explain.go +++ b/vendor/github.com/thanos-io/promql-engine/engine/explain.go @@ -28,17 +28,18 @@ type ExplainOutputNode struct { var _ ExplainableQuery = &compatibilityQuery{} -func analyzeVector(obsv model.ObservableVectorOperator) *AnalyzeOutputNode { - telemetry, obsVectors := obsv.Analyze() - - var children []AnalyzeOutputNode - for _, vector := range obsVectors { - children = append(children, *analyzeVector(vector)) +func analyzeQuery(obsv model.ObservableVectorOperator) *AnalyzeOutputNode { + _, children := obsv.Explain() + var childTelemetry []AnalyzeOutputNode + for _, child := range children { + if obsChild, ok := child.(model.ObservableVectorOperator); ok { + childTelemetry = append(childTelemetry, *analyzeQuery(obsChild)) + } } return &AnalyzeOutputNode{ - OperatorTelemetry: telemetry, - Children: children, + OperatorTelemetry: obsv, + Children: childTelemetry, } } diff --git a/vendor/github.com/thanos-io/promql-engine/execution/aggregate/accumulator.go b/vendor/github.com/thanos-io/promql-engine/execution/aggregate/accumulator.go index 575a372f6f2..c9f3a733831 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/aggregate/accumulator.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/aggregate/accumulator.go @@ -1,3 +1,6 @@ +// Copyright (c) The Thanos Community Authors. +// Licensed under the Apache License 2.0. + package aggregate import ( diff --git a/vendor/github.com/thanos-io/promql-engine/execution/aggregate/hashaggregate.go b/vendor/github.com/thanos-io/promql-engine/execution/aggregate/hashaggregate.go index 4f25e8bd567..89627e08a5d 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/aggregate/hashaggregate.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/aggregate/hashaggregate.go @@ -11,9 +11,9 @@ import ( "time" "github.com/efficientgo/core/errors" - "github.com/prometheus/prometheus/model/labels" "golang.org/x/exp/slices" + "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/promql/parser" "github.com/thanos-io/promql-engine/execution/model" @@ -59,8 +59,8 @@ func NewHashAggregate( // Grouping labels need to be sorted in order for metric hashing to work. // https://github.com/prometheus/prometheus/blob/8ed39fdab1ead382a354e45ded999eb3610f8d5f/model/labels/labels.go#L162-L181 slices.Sort(labels) - a := &aggregate{ - OperatorTelemetry: &model.TrackedTelemetry{}, + return &aggregate{ + OperatorTelemetry: model.NewTelemetry("[aggregate]", opts.EnableAnalysis), next: next, paramOp: paramOp, @@ -70,21 +70,7 @@ func NewHashAggregate( aggregation: aggregation, labels: labels, stepsBatch: opts.StepsBatch, - } - - return a, nil -} - -func (a *aggregate) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - a.SetName("[*aggregate]") - var ops []model.ObservableVectorOperator - if obsnextParamOp, ok := a.paramOp.(model.ObservableVectorOperator); ok { - ops = append(ops, obsnextParamOp) - } - if obsnext, ok := a.next.(model.ObservableVectorOperator); ok { - ops = append(ops, obsnext) - } - return a, ops + }, nil } func (a *aggregate) Explain() (me string, next []model.VectorOperator) { @@ -94,12 +80,15 @@ func (a *aggregate) Explain() (me string, next []model.VectorOperator) { } ops = append(ops, a.next) if a.by { - return fmt.Sprintf("[*aggregate] %v by (%v)", a.aggregation.String(), a.labels), ops + return fmt.Sprintf("[aggregate] %v by (%v)", a.aggregation.String(), a.labels), ops } - return fmt.Sprintf("[*aggregate] %v without (%v)", a.aggregation.String(), a.labels), ops + return fmt.Sprintf("[aggregate] %v without (%v)", a.aggregation.String(), a.labels), ops } func (a *aggregate) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { a.AddExecutionTimeTaken(time.Since(start)) }() + var err error a.once.Do(func() { err = a.initializeTables(ctx) }) if err != nil { @@ -114,13 +103,14 @@ func (a *aggregate) GetPool() *model.VectorPool { } func (a *aggregate) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { a.AddExecutionTimeTaken(time.Since(start)) }() + select { case <-ctx.Done(): return nil, ctx.Err() default: } - start := time.Now() - defer func() { a.AddExecutionTimeTaken(time.Since(start)) }() var err error a.once.Do(func() { err = a.initializeTables(ctx) }) @@ -247,7 +237,7 @@ func (a *aggregate) initializeScalarTables(ctx context.Context) ([]aggregateTabl labelsMap[lblName] = struct{}{} } for i := 0; i < len(series); i++ { - hash, _, lbls := hashMetric(builder, series[i], !a.by, a.labels, labelsMap, hashingBuf) + hash, lbls := hashMetric(builder, series[i], !a.by, a.labels, labelsMap, hashingBuf) output, ok := outputMap[hash] if !ok { output = &model.Series{ diff --git a/vendor/github.com/thanos-io/promql-engine/execution/aggregate/khashaggregate.go b/vendor/github.com/thanos-io/promql-engine/execution/aggregate/khashaggregate.go index 3fea33ce31c..6b63edd50df 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/aggregate/khashaggregate.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/aggregate/khashaggregate.go @@ -23,6 +23,8 @@ import ( ) type kAggregate struct { + model.OperatorTelemetry + next model.VectorOperator paramOp model.VectorOperator // params holds the aggregate parameter for each step. @@ -39,7 +41,6 @@ type kAggregate struct { inputToHeap []*samplesHeap heaps []*samplesHeap compare func(float64, float64) bool - model.OperatorTelemetry } func NewKHashAggregate( @@ -66,29 +67,34 @@ func NewKHashAggregate( // https://github.com/prometheus/prometheus/blob/8ed39fdab1ead382a354e45ded999eb3610f8d5f/model/labels/labels.go#L162-L181 slices.Sort(labels) - a := &kAggregate{ - next: next, - vectorPool: points, - by: by, - aggregation: aggregation, - labels: labels, - paramOp: paramOp, - compare: compare, - params: make([]float64, opts.StepsBatch), - } - a.OperatorTelemetry = &model.NoopTelemetry{} - if opts.EnableAnalysis { - a.OperatorTelemetry = &model.TrackedTelemetry{} - } - return a, nil + return &kAggregate{ + OperatorTelemetry: model.NewTelemetry("[kaggregate]", opts.EnableAnalysis), + next: next, + vectorPool: points, + by: by, + aggregation: aggregation, + labels: labels, + paramOp: paramOp, + compare: compare, + params: make([]float64, opts.StepsBatch), + }, nil } func (a *kAggregate) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { a.AddExecutionTimeTaken(time.Since(start)) }() + + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + in, err := a.next.Next(ctx) if err != nil { return nil, err } - start := time.Now() + args, err := a.paramOp.Next(ctx) if err != nil { return nil, err @@ -128,12 +134,14 @@ func (a *kAggregate) Next(ctx context.Context) ([]model.StepVector, error) { a.next.GetPool().PutStepVector(vector) } a.next.GetPool().PutVectors(in) - a.AddExecutionTimeTaken(time.Since(start)) return result, nil } func (a *kAggregate) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { a.AddExecutionTimeTaken(time.Since(start)) }() + var err error a.once.Do(func() { err = a.init(ctx) }) if err != nil { @@ -147,23 +155,11 @@ func (a *kAggregate) GetPool() *model.VectorPool { return a.vectorPool } -func (a *kAggregate) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - a.SetName("[*kaggregate]") - next := make([]model.ObservableVectorOperator, 0, 2) - if obsnextParamOp, ok := a.paramOp.(model.ObservableVectorOperator); ok { - next = append(next, obsnextParamOp) - } - if obsnext, ok := a.next.(model.ObservableVectorOperator); ok { - next = append(next, obsnext) - } - return a, next -} - func (a *kAggregate) Explain() (me string, next []model.VectorOperator) { if a.by { - return fmt.Sprintf("[*kaggregate] %v by (%v)", a.aggregation.String(), a.labels), []model.VectorOperator{a.paramOp, a.next} + return fmt.Sprintf("[kaggregate] %v by (%v)", a.aggregation.String(), a.labels), []model.VectorOperator{a.paramOp, a.next} } - return fmt.Sprintf("[*kaggregate] %v without (%v)", a.aggregation.String(), a.labels), []model.VectorOperator{a.paramOp, a.next} + return fmt.Sprintf("[kaggregate] %v without (%v)", a.aggregation.String(), a.labels), []model.VectorOperator{a.paramOp, a.next} } func (a *kAggregate) init(ctx context.Context) error { @@ -184,7 +180,7 @@ func (a *kAggregate) init(ctx context.Context) error { labelsMap[lblName] = struct{}{} } for i := 0; i < len(series); i++ { - hash, _, _ := hashMetric(builder, series[i], !a.by, a.labels, labelsMap, hashingBuf) + hash, _ := hashMetric(builder, series[i], !a.by, a.labels, labelsMap, hashingBuf) h, ok := heapsHash[hash] if !ok { h = &samplesHeap{compare: a.compare} diff --git a/vendor/github.com/thanos-io/promql-engine/execution/aggregate/scalar_table.go b/vendor/github.com/thanos-io/promql-engine/execution/aggregate/scalar_table.go index 3934f22feec..d362ab83dcd 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/aggregate/scalar_table.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/aggregate/scalar_table.go @@ -128,7 +128,7 @@ func hashMetric( grouping []string, groupingSet map[string]struct{}, buf []byte, -) (uint64, string, labels.Labels) { +) (uint64, labels.Labels) { buf = buf[:0] builder.Reset() @@ -142,12 +142,12 @@ func hashMetric( } builder.Add(lbl.Name, lbl.Value) }) - key, bytes := metric.HashWithoutLabels(buf, grouping...) - return key, string(bytes), builder.Labels() + key, _ := metric.HashWithoutLabels(buf, grouping...) + return key, builder.Labels() } if len(grouping) == 0 { - return 0, "", labels.Labels{} + return 0, labels.Labels{} } metric.Range(func(lbl labels.Label) { @@ -156,8 +156,8 @@ func hashMetric( } builder.Add(lbl.Name, lbl.Value) }) - key, bytes := metric.HashForLabels(buf, grouping...) - return key, string(bytes), builder.Labels() + key, _ := metric.HashForLabels(buf, grouping...) + return key, builder.Labels() } func newScalarAccumulator(expr parser.ItemType) (accumulator, error) { diff --git a/vendor/github.com/thanos-io/promql-engine/execution/binary/scalar.go b/vendor/github.com/thanos-io/promql-engine/execution/binary/scalar.go index cb8cc21c057..7b5caf58187 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/binary/scalar.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/binary/scalar.go @@ -28,6 +28,8 @@ const ( // scalarOperator evaluates expressions where one operand is a scalarOperator. type scalarOperator struct { + model.OperatorTelemetry + seriesOnce sync.Once series []labels.Labels @@ -45,7 +47,6 @@ type scalarOperator struct { // Keep the result if both sides are scalars. bothScalars bool - model.OperatorTelemetry } func NewScalar( @@ -70,7 +71,9 @@ func NewScalar( operandValIdx = 1 } - o := &scalarOperator{ + return &scalarOperator{ + OperatorTelemetry: model.NewTelemetry("[vectorScalarBinary]", opts.EnableAnalysis), + pool: pool, next: next, scalar: scalar, @@ -81,32 +84,18 @@ func NewScalar( operandValIdx: operandValIdx, returnBool: returnBool, bothScalars: scalarSide == ScalarSideBoth, - } - o.OperatorTelemetry = &model.NoopTelemetry{} - if opts.EnableAnalysis { - o.OperatorTelemetry = &model.TrackedTelemetry{} - } - return o, nil + }, nil } -func (o *scalarOperator) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - o.SetName("[*scalarOperator]") - next := make([]model.ObservableVectorOperator, 0, 2) - if obsnext, ok := o.next.(model.ObservableVectorOperator); ok { - next = append(next, obsnext) - } - if obsnextScalar, ok := o.scalar.(model.ObservableVectorOperator); ok { - next = append(next, obsnextScalar) - } - return o, next -} - func (o *scalarOperator) Explain() (me string, next []model.VectorOperator) { - return fmt.Sprintf("[*scalarOperator] %s", parser.ItemTypeStr[o.opType]), []model.VectorOperator{o.next, o.scalar} + return fmt.Sprintf("[vectorScalarBinary] %s", parser.ItemTypeStr[o.opType]), []model.VectorOperator{o.next, o.scalar} } func (o *scalarOperator) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + var err error o.seriesOnce.Do(func() { err = o.loadSeries(ctx) }) if err != nil { @@ -116,12 +105,14 @@ func (o *scalarOperator) Series(ctx context.Context) ([]labels.Labels, error) { } func (o *scalarOperator) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + select { case <-ctx.Done(): return nil, ctx.Err() default: } - start := time.Now() in, err := o.next.Next(ctx) if err != nil { @@ -181,7 +172,6 @@ func (o *scalarOperator) Next(ctx context.Context) ([]model.StepVector, error) { o.next.GetPool().PutVectors(in) o.scalar.GetPool().PutVectors(scalarIn) - o.AddExecutionTimeTaken(time.Since(start)) return out, nil } diff --git a/vendor/github.com/thanos-io/promql-engine/execution/binary/vector.go b/vendor/github.com/thanos-io/promql-engine/execution/binary/vector.go index ead3d31cc8e..f8ae8a6208f 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/binary/vector.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/binary/vector.go @@ -8,12 +8,12 @@ import ( "fmt" "math" "sync" - - "golang.org/x/exp/slices" + "time" "github.com/cespare/xxhash/v2" "github.com/efficientgo/core/errors" "github.com/zhangyunhao116/umap" + "golang.org/x/exp/slices" "github.com/prometheus/prometheus/model/histogram" "github.com/prometheus/prometheus/model/labels" @@ -67,7 +67,9 @@ func NewVectorOperator( returnBool bool, opts *query.Options, ) (model.VectorOperator, error) { - o := &vectorOperator{ + return &vectorOperator{ + OperatorTelemetry: model.NewTelemetry("[vectorBinary]", opts.EnableAnalysis), + pool: pool, lhs: lhs, rhs: rhs, @@ -75,35 +77,20 @@ func NewVectorOperator( opType: opType, returnBool: returnBool, sigFunc: signatureFunc(matching.On, matching.MatchingLabels...), - } - - o.OperatorTelemetry = &model.NoopTelemetry{} - if opts.EnableAnalysis { - o.OperatorTelemetry = &model.TrackedTelemetry{} - } - return o, nil -} - -func (o *vectorOperator) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - o.SetName("[*vectorOperator]") - next := make([]model.ObservableVectorOperator, 0, 2) - if obsnextParamOp, ok := o.lhs.(model.ObservableVectorOperator); ok { - next = append(next, obsnextParamOp) - } - if obsnext, ok := o.rhs.(model.ObservableVectorOperator); ok { - next = append(next, obsnext) - } - return o, next + }, nil } func (o *vectorOperator) Explain() (me string, next []model.VectorOperator) { if o.matching.On { - return fmt.Sprintf("[*vectorOperator] %s - %v, on: %v, group: %v", parser.ItemTypeStr[o.opType], o.matching.Card.String(), o.matching.MatchingLabels, o.matching.Include), []model.VectorOperator{o.lhs, o.rhs} + return fmt.Sprintf("[vectorBinary] %s - %v, on: %v, group: %v", parser.ItemTypeStr[o.opType], o.matching.Card.String(), o.matching.MatchingLabels, o.matching.Include), []model.VectorOperator{o.lhs, o.rhs} } - return fmt.Sprintf("[*vectorOperator] %s - %v, ignoring: %v, group: %v", parser.ItemTypeStr[o.opType], o.matching.Card.String(), o.matching.On, o.matching.Include), []model.VectorOperator{o.lhs, o.rhs} + return fmt.Sprintf("[vectorBinary] %s - %v, ignoring: %v, group: %v", parser.ItemTypeStr[o.opType], o.matching.Card.String(), o.matching.On, o.matching.Include), []model.VectorOperator{o.lhs, o.rhs} } func (o *vectorOperator) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + if err := o.initOnce(ctx); err != nil { return nil, err } @@ -111,6 +98,9 @@ func (o *vectorOperator) Series(ctx context.Context) ([]labels.Labels, error) { } func (o *vectorOperator) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + select { case <-ctx.Done(): return nil, ctx.Err() @@ -299,6 +289,11 @@ func (o *vectorOperator) execBinaryArithmetic(lhs, rhs model.StepVector) (model. return step, errors.Newf("Unexpected matching cardinality: %s", o.matching.Card.String()) } + // shortcut: if we have no samples on the high card side we cannot compute pairings + if len(hcs.Samples) == 0 { + return step, nil + } + for i, sampleID := range lcs.SampleIDs { jp := o.lcJoinBuckets[sampleID] // Hash collisions on the low-card-side would imply a many-to-many relation. @@ -505,6 +500,7 @@ func signatureFunc(on bool, names ...string) func(labels.Labels) uint64 { // Lifted from: https://github.com/prometheus/prometheus/blob/a38179c4e183d9b50b271167bf90050eda8ec3d1/promql/engine.go#L2430. // TODO: call with histogram values in followup PR. +// nolint: unparam func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool) { switch op { case parser.ADD: diff --git a/vendor/github.com/thanos-io/promql-engine/execution/exchange/coalesce.go b/vendor/github.com/thanos-io/promql-engine/execution/exchange/coalesce.go index 8650f9a4767..8c64e0ba23c 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/exchange/coalesce.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/exchange/coalesce.go @@ -34,6 +34,8 @@ func (c errorChan) getError() error { // coalesce guarantees that samples from different input vectors will be added to the output in the same order // as the input vectors themselves are provided in NewCoalesce. type coalesce struct { + model.OperatorTelemetry + once sync.Once series []labels.Labels @@ -46,37 +48,22 @@ type coalesce struct { inVectors [][]model.StepVector // sampleOffsets holds per-operator offsets needed to map an input sample ID to an output sample ID. sampleOffsets []uint64 - model.OperatorTelemetry } func NewCoalesce(pool *model.VectorPool, opts *query.Options, batchSize int64, operators ...model.VectorOperator) model.VectorOperator { - c := &coalesce{ + return &coalesce{ + OperatorTelemetry: model.NewTelemetry("[coalesce]", opts.EnableAnalysis), + pool: pool, sampleOffsets: make([]uint64, len(operators)), operators: operators, inVectors: make([][]model.StepVector, len(operators)), batchSize: batchSize, } - c.OperatorTelemetry = &model.NoopTelemetry{} - if opts.EnableAnalysis { - c.OperatorTelemetry = &model.TrackedTelemetry{} - } - return c -} - -func (c *coalesce) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - c.SetName("[*coalesce]") - obsOperators := make([]model.ObservableVectorOperator, 0, len(c.operators)) - for _, operator := range c.operators { - if obsOperator, ok := operator.(model.ObservableVectorOperator); ok { - obsOperators = append(obsOperators, obsOperator) - } - } - return c, obsOperators } func (c *coalesce) Explain() (me string, next []model.VectorOperator) { - return "[*coalesce]", c.operators + return "[coalesce]", c.operators } func (c *coalesce) GetPool() *model.VectorPool { @@ -84,6 +71,9 @@ func (c *coalesce) GetPool() *model.VectorPool { } func (c *coalesce) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { c.AddExecutionTimeTaken(time.Since(start)) }() + var err error c.once.Do(func() { err = c.loadSeries(ctx) }) if err != nil { @@ -93,13 +83,14 @@ func (c *coalesce) Series(ctx context.Context) ([]labels.Labels, error) { } func (c *coalesce) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { c.AddExecutionTimeTaken(time.Since(start)) }() + select { case <-ctx.Done(): return nil, ctx.Err() default: } - start := time.Now() - defer func() { c.AddExecutionTimeTaken(time.Since(start)) }() var err error c.once.Do(func() { err = c.loadSeries(ctx) }) diff --git a/vendor/github.com/thanos-io/promql-engine/execution/exchange/concurrent.go b/vendor/github.com/thanos-io/promql-engine/execution/exchange/concurrent.go index 35640582ef8..f41bb86dacd 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/exchange/concurrent.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/exchange/concurrent.go @@ -10,6 +10,7 @@ import ( "time" "github.com/thanos-io/promql-engine/execution/model" + "github.com/thanos-io/promql-engine/query" "github.com/prometheus/prometheus/model/labels" ) @@ -27,30 +28,24 @@ type concurrencyOperator struct { model.OperatorTelemetry } -func NewConcurrent(next model.VectorOperator, bufferSize int) model.VectorOperator { - c := &concurrencyOperator{ +func NewConcurrent(next model.VectorOperator, bufferSize int, opts *query.Options) model.VectorOperator { + return &concurrencyOperator{ + OperatorTelemetry: model.NewTelemetry("[concurrent]", opts.EnableAnalysis), + next: next, buffer: make(chan maybeStepVector, bufferSize), bufferSize: bufferSize, } - c.OperatorTelemetry = &model.TrackedTelemetry{} - return c -} - -func (c *concurrencyOperator) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - c.SetName(("[*concurrencyOperator]")) - next := make([]model.ObservableVectorOperator, 0, 1) - if obsnext, ok := c.next.(model.ObservableVectorOperator); ok { - next = append(next, obsnext) - } - return c, next } func (c *concurrencyOperator) Explain() (me string, next []model.VectorOperator) { - return fmt.Sprintf("[*concurrencyOperator(buff=%v)]", c.bufferSize), []model.VectorOperator{c.next} + return fmt.Sprintf("[concurrent(buff=%v)]", c.bufferSize), []model.VectorOperator{c.next} } func (c *concurrencyOperator) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { c.AddExecutionTimeTaken(time.Since(start)) }() + return c.next.Series(ctx) } @@ -59,13 +54,15 @@ func (c *concurrencyOperator) GetPool() *model.VectorPool { } func (c *concurrencyOperator) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { c.AddExecutionTimeTaken(time.Since(start)) }() + select { case <-ctx.Done(): return nil, ctx.Err() default: } - start := time.Now() c.once.Do(func() { go c.pull(ctx) go c.drainBufferOnCancel(ctx) @@ -78,7 +75,6 @@ func (c *concurrencyOperator) Next(ctx context.Context) ([]model.StepVector, err if r.err != nil { return nil, r.err } - c.AddExecutionTimeTaken(time.Since(start)) return r.stepVector, nil } diff --git a/vendor/github.com/thanos-io/promql-engine/execution/exchange/dedup.go b/vendor/github.com/thanos-io/promql-engine/execution/exchange/dedup.go index 7bcbdf37803..36b2d4b3c9c 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/exchange/dedup.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/exchange/dedup.go @@ -13,6 +13,7 @@ import ( "github.com/prometheus/prometheus/model/labels" "github.com/thanos-io/promql-engine/execution/model" + "github.com/thanos-io/promql-engine/query" ) type dedupSample struct { @@ -30,6 +31,8 @@ type dedupCache []dedupSample // if multiple samples with the same ID are present in a StepVector, dedupOperator // will keep the last sample in that vector. type dedupOperator struct { + model.OperatorTelemetry + once sync.Once series []labels.Labels @@ -38,34 +41,25 @@ type dedupOperator struct { // outputIndex is a slice that is used as an index from input sample ID to output sample ID. outputIndex []uint64 dedupCache dedupCache - model.OperatorTelemetry -} - -func NewDedupOperator(pool *model.VectorPool, next model.VectorOperator) model.VectorOperator { - d := &dedupOperator{ - next: next, - pool: pool, - } - d.OperatorTelemetry = &model.TrackedTelemetry{} - return d } -func (d *dedupOperator) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - d.SetName("[*dedup]") - next := make([]model.ObservableVectorOperator, 0, 1) - if obsnext, ok := d.next.(model.ObservableVectorOperator); ok { - next = append(next, obsnext) +func NewDedupOperator(pool *model.VectorPool, next model.VectorOperator, opts *query.Options) model.VectorOperator { + return &dedupOperator{ + OperatorTelemetry: model.NewTelemetry("[dedup]", opts.EnableAnalysis), + next: next, + pool: pool, } - return d, next } func (d *dedupOperator) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { d.AddExecutionTimeTaken(time.Since(start)) }() + var err error d.once.Do(func() { err = d.loadSeries(ctx) }) if err != nil { return nil, err } - start := time.Now() in, err := d.next.Next(ctx) if err != nil { @@ -105,12 +99,14 @@ func (d *dedupOperator) Next(ctx context.Context) ([]model.StepVector, error) { } result = append(result, out) } - d.AddExecutionTimeTaken(time.Since(start)) return result, nil } func (d *dedupOperator) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { d.AddExecutionTimeTaken(time.Since(start)) }() + var err error d.once.Do(func() { err = d.loadSeries(ctx) }) if err != nil { @@ -124,7 +120,7 @@ func (d *dedupOperator) GetPool() *model.VectorPool { } func (d *dedupOperator) Explain() (me string, next []model.VectorOperator) { - return "[*dedup]", []model.VectorOperator{d.next} + return "[dedup]", []model.VectorOperator{d.next} } func (d *dedupOperator) loadSeries(ctx context.Context) error { diff --git a/vendor/github.com/thanos-io/promql-engine/execution/exchange/duplicate_label.go b/vendor/github.com/thanos-io/promql-engine/execution/exchange/duplicate_label.go new file mode 100644 index 00000000000..e96845c3b5c --- /dev/null +++ b/vendor/github.com/thanos-io/promql-engine/execution/exchange/duplicate_label.go @@ -0,0 +1,124 @@ +// Copyright (c) The Thanos Community Authors. +// Licensed under the Apache License 2.0. + +package exchange + +import ( + "context" + "sync" + "time" + + "github.com/prometheus/prometheus/model/labels" + + "github.com/thanos-io/promql-engine/execution/model" + "github.com/thanos-io/promql-engine/extlabels" + "github.com/thanos-io/promql-engine/query" +) + +type pair struct{ a, b int } + +type duplicateLabelCheckOperator struct { + model.OperatorTelemetry + + once sync.Once + next model.VectorOperator + + p []pair + c []uint64 +} + +func NewDuplicateLabelCheck(next model.VectorOperator, opts *query.Options) model.VectorOperator { + return &duplicateLabelCheckOperator{ + OperatorTelemetry: model.NewTelemetry("[duplicateLabelCheck]", opts.EnableAnalysis), + next: next, + } +} + +func (d *duplicateLabelCheckOperator) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { d.AddExecutionTimeTaken(time.Since(start)) }() + + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + if err := d.init(ctx); err != nil { + return nil, err + } + + in, err := d.next.Next(ctx) + if err != nil { + return nil, err + } + if in == nil { + return nil, nil + } + + // TODO: currently there is a bug, we need to reset 'd.c's state + // if the current timestamp changes. With configured BatchSize we + // dont see all samples for a timestamp in the same batch, but this + // logic relies on that. + if len(d.p) > 0 { + for i := range d.p { + d.c[d.p[i].a] = 0 + d.c[d.p[i].b] = 0 + } + for i, sv := range in { + for _, sid := range sv.SampleIDs { + d.c[sid] |= 2 << i + } + } + for i := range d.p { + if d.c[d.p[i].a]&d.c[d.p[i].b] > 0 { + return nil, extlabels.ErrDuplicateLabelSet + } + } + } + + return in, nil +} + +func (d *duplicateLabelCheckOperator) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { d.AddExecutionTimeTaken(time.Since(start)) }() + + if err := d.init(ctx); err != nil { + return nil, err + } + return d.next.Series(ctx) +} + +func (d *duplicateLabelCheckOperator) GetPool() *model.VectorPool { + return d.next.GetPool() +} + +func (d *duplicateLabelCheckOperator) Explain() (me string, next []model.VectorOperator) { + return d.next.Explain() +} + +func (d *duplicateLabelCheckOperator) init(ctx context.Context) error { + var err error + d.once.Do(func() { + series, seriesErr := d.next.Series(ctx) + if seriesErr != nil { + err = seriesErr + return + } + m := make(map[uint64]int, len(series)) + p := make([]pair, 0) + c := make([]uint64, len(series)) + for i := range series { + h := series[i].Hash() + if j, ok := m[h]; ok { + p = append(p, pair{a: i, b: j}) + } else { + m[h] = i + } + } + d.p = p + d.c = c + }) + return err +} diff --git a/vendor/github.com/thanos-io/promql-engine/execution/execution.go b/vendor/github.com/thanos-io/promql-engine/execution/execution.go index e2d86dc670f..f432dac99e4 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/execution.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/execution.go @@ -24,9 +24,8 @@ import ( "github.com/efficientgo/core/errors" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/promql" - "github.com/prometheus/prometheus/storage" - "github.com/prometheus/prometheus/promql/parser" + "github.com/prometheus/prometheus/storage" "github.com/thanos-io/promql-engine/execution/aggregate" "github.com/thanos-io/promql-engine/execution/binary" @@ -53,7 +52,6 @@ func New(expr parser.Expr, queryable storage.Queryable, opts *query.Options) (mo End: opts.End.UnixMilli(), Step: opts.Step.Milliseconds(), } - return newOperator(expr, selectorPool, opts, hints) } @@ -61,168 +59,141 @@ func newOperator(expr parser.Expr, storage *engstore.SelectorPool, opts *query.O switch e := expr.(type) { case *parser.NumberLiteral: return scan.NewNumberLiteralSelector(model.NewVectorPool(opts.StepsBatch), opts, e.Val), nil - - case *parser.VectorSelector: - start, end := getTimeRangesForVectorSelector(e, opts, 0) - hints.Start = start - hints.End = end - filter := storage.GetSelector(start, end, opts.Step.Milliseconds(), e.LabelMatchers, hints) - return newShardedVectorSelector(filter, opts, e.Offset, 0) - case *logicalplan.VectorSelector: - start, end := getTimeRangesForVectorSelector(e.VectorSelector, opts, 0) - hints.Start = start - hints.End = end - selector := storage.GetFilteredSelector(start, end, opts.Step.Milliseconds(), e.LabelMatchers, e.Filters, hints) - return newShardedVectorSelector(selector, opts, e.Offset, e.BatchSize) - - case *parser.Call: - hints.Func = e.Func.Name - hints.Grouping = nil - hints.By = false - - if e.Func.Name == "absent_over_time" { - return newAbsentOverTimeOperator(e, storage, opts, hints) + op := newVectorSelector(e, storage, opts, hints) + if e.SelectTimestamp { + // we will drop the __name__ label here, so we need to check for duplicate labels + return exchange.NewDuplicateLabelCheck(op, opts), nil } - - // TODO(saswatamcode): Range vector result might need new operator - // before it can be non-nested. https://github.com/thanos-io/promql-engine/issues/39 - for i := range e.Args { - switch t := e.Args[i].(type) { - case *parser.SubqueryExpr: - if !opts.EnableSubqueries { - return nil, parse.ErrNotImplemented - } - return newSubqueryFunction(e, t, storage, opts, hints) - case *parser.MatrixSelector: - return newRangeVectorFunction(e, t, storage, opts, hints) - } - } - return newInstantVectorFunction(e, storage, opts, hints) - - case *parser.AggregateExpr: - hints.Func = e.Op.String() - hints.Grouping = e.Grouping - hints.By = !e.Without - var paramOp model.VectorOperator - - next, err := newOperator(e.Expr, storage, opts, hints) + return op, nil + case *parser.Call: + op, err := newCall(e, storage, opts, hints) if err != nil { return nil, err } - - if e.Param != nil && e.Param.Type() != parser.ValueTypeString { - paramOp, err = newOperator(e.Param, storage, opts, hints) - if err != nil { - return nil, err - } - } - - if e.Op == parser.TOPK || e.Op == parser.BOTTOMK { - next, err = aggregate.NewKHashAggregate(model.NewVectorPool(opts.StepsBatch), next, paramOp, e.Op, !e.Without, e.Grouping, opts) - } else { - next, err = aggregate.NewHashAggregate(model.NewVectorPool(opts.StepsBatch), next, paramOp, e.Op, !e.Without, e.Grouping, opts) - } - + return exchange.NewDuplicateLabelCheck(op, opts), nil + case *parser.AggregateExpr: + op, err := newAggregateExpression(e, storage, opts, hints) if err != nil { return nil, err } - - return exchange.NewConcurrent(next, 2), nil - + return exchange.NewDuplicateLabelCheck(op, opts), nil case *parser.BinaryExpr: - if e.LHS.Type() == parser.ValueTypeScalar || e.RHS.Type() == parser.ValueTypeScalar { - return newScalarBinaryOperator(e, storage, opts, hints) + op, err := newBinaryExpression(e, storage, opts, hints) + if err != nil { + return nil, err } - - return newVectorBinaryOperator(e, storage, opts, hints) - + return exchange.NewDuplicateLabelCheck(op, opts), nil case *parser.ParenExpr: return newOperator(e.Expr, storage, opts, hints) - case *parser.UnaryExpr: - next, err := newOperator(e.Expr, storage, opts, hints) + op, err := newUnaryExpression(e, storage, opts, hints) if err != nil { return nil, err } - switch e.Op { - case parser.ADD: - return next, nil - case parser.SUB: - return unary.NewUnaryNegation(next, opts.StepsBatch) - default: - // This shouldn't happen as Op was validated when parsing already - // https://github.com/prometheus/prometheus/blob/v2.38.0/promql/parser/parse.go#L573. - return nil, errors.Wrapf(parse.ErrNotSupportedExpr, "got: %s", e) - } - + return exchange.NewDuplicateLabelCheck(op, opts), nil case *parser.StepInvariantExpr: - switch t := e.Expr.(type) { - case *parser.NumberLiteral: - return scan.NewNumberLiteralSelector(model.NewVectorPool(opts.StepsBatch), opts, t.Val), nil - } - next, err := newOperator(e.Expr, storage, opts.WithEndTime(opts.Start), hints) - if err != nil { - return nil, err - } - return step_invariant.NewStepInvariantOperator(model.NewVectorPoolWithSize(opts.StepsBatch, 1), next, e.Expr, opts) - + return newStepInvariantExpression(e, storage, opts, hints) case logicalplan.Deduplicate: - // The Deduplicate operator will deduplicate samples using a last-sample-wins strategy. - // Sorting engines by MaxT ensures that samples produced due to - // staleness will be overwritten and corrected by samples coming from - // engines with a higher max time. - sort.Slice(e.Expressions, func(i, j int) bool { - return e.Expressions[i].Engine.MaxT() < e.Expressions[j].Engine.MaxT() - }) - - operators := make([]model.VectorOperator, len(e.Expressions)) - for i, expr := range e.Expressions { - operator, err := newOperator(expr, storage, opts, hints) - if err != nil { - return nil, err - } - operators[i] = operator - } - coalesce := exchange.NewCoalesce(model.NewVectorPool(opts.StepsBatch), opts, 0, operators...) - dedup := exchange.NewDedupOperator(model.NewVectorPool(opts.StepsBatch), coalesce) - return exchange.NewConcurrent(dedup, 2), nil - + return newDeduplication(e, storage, opts, hints) case logicalplan.RemoteExecution: - // Create a new remote query scoped to the calculated start time. - qry, err := e.Engine.NewRangeQuery(opts.Context, promql.NewPrometheusQueryOpts(false, opts.LookbackDelta), e.Query, e.QueryRangeStart, opts.End, opts.Step) - if err != nil { - return nil, err - } - - // The selector uses the original query time to make sure that steps from different - // operators have the same timestamps. - // We need to set the lookback for the selector to 0 since the remote query already applies one lookback. - selectorOpts := *opts - selectorOpts.LookbackDelta = 0 - remoteExec := remote.NewExecution(qry, model.NewVectorPool(opts.StepsBatch), e.QueryRangeStart, &selectorOpts) - return exchange.NewConcurrent(remoteExec, 2), nil + return newRemoteExecution(e, opts, hints) case logicalplan.Noop: return noop.NewOperator(), nil case logicalplan.UserDefinedExpr: return e.MakeExecutionOperator(model.NewVectorPool(opts.StepsBatch), storage, opts, hints) default: - return nil, errors.Wrapf(parse.ErrNotSupportedExpr, "got: %s", e) + return nil, errors.Wrapf(parse.ErrNotSupportedExpr, "got: %s (%T)", e, e) } } -func unpackVectorSelector(t *parser.MatrixSelector) (int64, *parser.VectorSelector, []*labels.Matcher, error) { - switch t := t.VectorSelector.(type) { - case *parser.VectorSelector: - return 0, t, nil, nil - case *logicalplan.VectorSelector: - return t.BatchSize, t.VectorSelector, t.Filters, nil +func newVectorSelector(e *logicalplan.VectorSelector, storage *engstore.SelectorPool, opts *query.Options, hints storage.SelectHints) model.VectorOperator { + start, end := getTimeRangesForVectorSelector(e, opts, 0) + hints.Start = start + hints.End = end + + offset := e.Offset + batchsize := e.BatchSize + selector := storage.GetFilteredSelector(start, end, opts.Step.Milliseconds(), e.LabelMatchers, e.Filters, hints) + selectTimestamp := e.SelectTimestamp + + numShards := runtime.GOMAXPROCS(0) / 2 + if numShards < 1 { + numShards = 1 + } + + operators := make([]model.VectorOperator, 0, numShards) + for i := 0; i < numShards; i++ { + operator := scan.NewVectorSelector( + model.NewVectorPool(opts.StepsBatch), selector, opts, offset, hints, batchsize, selectTimestamp, i, numShards) + operators = append(operators, exchange.NewConcurrent(operator, 2, opts)) + } + + return exchange.NewCoalesce(model.NewVectorPool(opts.StepsBatch), opts, batchsize*int64(numShards), operators...) +} + +func newCall(e *parser.Call, storage *engstore.SelectorPool, opts *query.Options, hints storage.SelectHints) (model.VectorOperator, error) { + hints.Func = e.Func.Name + hints.Grouping = nil + hints.By = false + + if e.Func.Name == "absent_over_time" { + return newAbsentOverTimeOperator(e, storage, opts, hints) + } + // TODO(saswatamcode): Range vector result might need new operator + // before it can be non-nested. https://github.com/thanos-io/promql-engine/issues/39 + for i := range e.Args { + switch t := e.Args[i].(type) { + case *parser.SubqueryExpr: + return newSubqueryFunction(e, t, storage, opts, hints) + case *logicalplan.MatrixSelector: + return newRangeVectorFunction(e, t, storage, opts, hints) + } + } + return newInstantVectorFunction(e, storage, opts, hints) +} + +func newAbsentOverTimeOperator(call *parser.Call, storage *engstore.SelectorPool, opts *query.Options, hints storage.SelectHints) (model.VectorOperator, error) { + switch arg := call.Args[0].(type) { + case *parser.SubqueryExpr: + matrixCall := &parser.Call{ + Func: &parser.Function{Name: "last_over_time"}, + } + argOp, err := newSubqueryFunction(matrixCall, arg, storage, opts, hints) + if err != nil { + return nil, err + } + f := &parser.Call{ + Func: &parser.Function{Name: "absent"}, + Args: []parser.Expr{matrixCall}, + } + return function.NewFunctionOperator(f, []model.VectorOperator{argOp}, opts.StepsBatch, opts) + case *logicalplan.MatrixSelector: + matrixCall := &parser.Call{ + Func: &parser.Function{Name: "last_over_time"}, + Args: call.Args, + } + argOp, err := newRangeVectorFunction(matrixCall, arg, storage, opts, hints) + if err != nil { + return nil, err + } + f := &parser.Call{ + Func: &parser.Function{Name: "absent"}, + Args: []parser.Expr{&logicalplan.MatrixSelector{ + MatrixSelector: &parser.MatrixSelector{ + VectorSelector: arg.VectorSelector, + Range: arg.Range, + }, + OriginalString: arg.String(), + }}, + } + return function.NewFunctionOperator(f, []model.VectorOperator{argOp}, opts.StepsBatch, opts) default: - return 0, nil, nil, parse.ErrNotSupportedExpr + return nil, parse.ErrNotSupportedExpr } } -func newRangeVectorFunction(e *parser.Call, t *parser.MatrixSelector, storage *engstore.SelectorPool, opts *query.Options, hints storage.SelectHints) (model.VectorOperator, error) { +func newRangeVectorFunction(e *parser.Call, t *logicalplan.MatrixSelector, storage *engstore.SelectorPool, opts *query.Options, hints storage.SelectHints) (model.VectorOperator, error) { // TODO(saswatamcode): Range vector result might need new operator // before it can be non-nested. https://github.com/thanos-io/promql-engine/issues/39 batchSize, vs, filters, err := unpackVectorSelector(t) @@ -271,7 +242,7 @@ func newRangeVectorFunction(e *parser.Call, t *parser.MatrixSelector, storage *e if err != nil { return nil, err } - operators = append(operators, exchange.NewConcurrent(operator, 2)) + operators = append(operators, exchange.NewConcurrent(operator, 2, opts)) } return exchange.NewCoalesce(model.NewVectorPool(opts.StepsBatch), opts, batchSize*int64(numShards), operators...), nil @@ -282,8 +253,8 @@ func newSubqueryFunction(e *parser.Call, t *parser.SubqueryExpr, storage *engsto if parse.IsExtFunction(e.Func.Name) { return nil, parse.ErrNotImplemented } - // TODO: only instant queries for now. - if !opts.IsInstantQuery() { + // TODO: We dont pass arguments yet + if e.Func.Name == "quantile_over_time" { return nil, parse.ErrNotImplemented } nOpts := query.NestedOptionsForSubquery(opts, t) @@ -296,7 +267,13 @@ func newSubqueryFunction(e *parser.Call, t *parser.SubqueryExpr, storage *engsto if err != nil { return nil, err } - return scan.NewSubqueryOperator(model.NewVectorPool(opts.StepsBatch), inner, opts, e, t) + + outerOpts := *opts + if t.Timestamp != nil { + outerOpts.Start = time.UnixMilli(*t.Timestamp) + outerOpts.End = time.UnixMilli(*t.Timestamp) + } + return scan.NewSubqueryOperator(model.NewVectorPool(opts.StepsBatch), inner, &outerOpts, e, t) } func newInstantVectorFunction(e *parser.Call, storage *engstore.SelectorPool, opts *query.Options, hints storage.SelectHints) (model.VectorOperator, error) { @@ -316,84 +293,63 @@ func newInstantVectorFunction(e *parser.Call, storage *engstore.SelectorPool, op return function.NewFunctionOperator(e, nextOperators, opts.StepsBatch, opts) } -func newShardedVectorSelector(selector engstore.SeriesSelector, opts *query.Options, offset time.Duration, batchSize int64) (model.VectorOperator, error) { - numShards := runtime.GOMAXPROCS(0) / 2 - if numShards < 1 { - numShards = 1 - } - operators := make([]model.VectorOperator, 0, numShards) - for i := 0; i < numShards; i++ { - operator := exchange.NewConcurrent( - scan.NewVectorSelector( - model.NewVectorPool(opts.StepsBatch), selector, opts, offset, batchSize, i, numShards), 2) - operators = append(operators, operator) - } +func newAggregateExpression(e *parser.AggregateExpr, storage *engstore.SelectorPool, opts *query.Options, hints storage.SelectHints) (model.VectorOperator, error) { + hints.Func = e.Op.String() + hints.Grouping = e.Grouping + hints.By = !e.Without + var paramOp model.VectorOperator - return exchange.NewCoalesce(model.NewVectorPool(opts.StepsBatch), opts, batchSize*int64(numShards), operators...), nil -} + next, err := newOperator(e.Expr, storage, opts, hints) + if err != nil { + return nil, err + } -func newAbsentOverTimeOperator(call *parser.Call, selectorPool *engstore.SelectorPool, opts *query.Options, hints storage.SelectHints) (model.VectorOperator, error) { - switch arg := call.Args[0].(type) { - case *parser.SubqueryExpr: - matrixCall := &parser.Call{ - Func: &parser.Function{Name: "last_over_time"}, - } - argOp, err := newSubqueryFunction(matrixCall, arg, selectorPool, opts, hints) + if e.Param != nil && e.Param.Type() != parser.ValueTypeString { + paramOp, err = newOperator(e.Param, storage, opts, hints) if err != nil { return nil, err } - f := &parser.Call{ - Func: &parser.Function{Name: "absent"}, - Args: []parser.Expr{matrixCall}, - } - return function.NewFunctionOperator(f, []model.VectorOperator{argOp}, opts.StepsBatch, opts) - case *parser.MatrixSelector: - matrixCall := &parser.Call{ - Func: &parser.Function{Name: "last_over_time"}, - Args: call.Args, - } - argOp, err := newRangeVectorFunction(matrixCall, arg, selectorPool, opts, hints) - if err != nil { - return nil, err - } - _, vs, filters, err := unpackVectorSelector(arg) - if err != nil { - return nil, err - } - // if we have a filtered selector we need to put the labels back for absent - // to compute its series properly - vs.LabelMatchers = append(vs.LabelMatchers, filters...) - f := &parser.Call{ - Func: &parser.Function{Name: "absent"}, - Args: []parser.Expr{&parser.MatrixSelector{ - VectorSelector: vs, - Range: arg.Range, - }}, - } - return function.NewFunctionOperator(f, []model.VectorOperator{argOp}, opts.StepsBatch, opts) - default: - return nil, parse.ErrNotSupportedExpr } + + if e.Op == parser.TOPK || e.Op == parser.BOTTOMK { + next, err = aggregate.NewKHashAggregate(model.NewVectorPool(opts.StepsBatch), next, paramOp, e.Op, !e.Without, e.Grouping, opts) + } else { + next, err = aggregate.NewHashAggregate(model.NewVectorPool(opts.StepsBatch), next, paramOp, e.Op, !e.Without, e.Grouping, opts) + } + + if err != nil { + return nil, err + } + + return exchange.NewConcurrent(next, 2, opts), nil + +} + +func newBinaryExpression(e *parser.BinaryExpr, storage *engstore.SelectorPool, opts *query.Options, hints storage.SelectHints) (model.VectorOperator, error) { + if e.LHS.Type() == parser.ValueTypeScalar || e.RHS.Type() == parser.ValueTypeScalar { + return newScalarBinaryOperator(e, storage, opts, hints) + } + return newVectorBinaryOperator(e, storage, opts, hints) } -func newVectorBinaryOperator(e *parser.BinaryExpr, selectorPool *engstore.SelectorPool, opts *query.Options, hints storage.SelectHints) (model.VectorOperator, error) { - leftOperator, err := newOperator(e.LHS, selectorPool, opts, hints) +func newVectorBinaryOperator(e *parser.BinaryExpr, storage *engstore.SelectorPool, opts *query.Options, hints storage.SelectHints) (model.VectorOperator, error) { + leftOperator, err := newOperator(e.LHS, storage, opts, hints) if err != nil { return nil, err } - rightOperator, err := newOperator(e.RHS, selectorPool, opts, hints) + rightOperator, err := newOperator(e.RHS, storage, opts, hints) if err != nil { return nil, err } return binary.NewVectorOperator(model.NewVectorPool(opts.StepsBatch), leftOperator, rightOperator, e.VectorMatching, e.Op, e.ReturnBool, opts) } -func newScalarBinaryOperator(e *parser.BinaryExpr, selectorPool *engstore.SelectorPool, opts *query.Options, hints storage.SelectHints) (model.VectorOperator, error) { - lhs, err := newOperator(e.LHS, selectorPool, opts, hints) +func newScalarBinaryOperator(e *parser.BinaryExpr, storage *engstore.SelectorPool, opts *query.Options, hints storage.SelectHints) (model.VectorOperator, error) { + lhs, err := newOperator(e.LHS, storage, opts, hints) if err != nil { return nil, err } - rhs, err := newOperator(e.RHS, selectorPool, opts, hints) + rhs, err := newOperator(e.RHS, storage, opts, hints) if err != nil { return nil, err } @@ -409,8 +365,75 @@ func newScalarBinaryOperator(e *parser.BinaryExpr, selectorPool *engstore.Select return binary.NewScalar(model.NewVectorPoolWithSize(opts.StepsBatch, 1), lhs, rhs, e.Op, scalarSide, e.ReturnBool, opts) } +func newUnaryExpression(e *parser.UnaryExpr, storage *engstore.SelectorPool, opts *query.Options, hints storage.SelectHints) (model.VectorOperator, error) { + next, err := newOperator(e.Expr, storage, opts, hints) + if err != nil { + return nil, err + } + switch e.Op { + case parser.ADD: + return next, nil + case parser.SUB: + return unary.NewUnaryNegation(next, opts) + default: + // This shouldn't happen as Op was validated when parsing already + // https://github.com/prometheus/prometheus/blob/v2.38.0/promql/parser/parse.go#L573. + return nil, errors.Wrapf(parse.ErrNotSupportedExpr, "got: %s", e) + } +} + +func newStepInvariantExpression(e *parser.StepInvariantExpr, storage *engstore.SelectorPool, opts *query.Options, hints storage.SelectHints) (model.VectorOperator, error) { + switch t := e.Expr.(type) { + case *parser.NumberLiteral: + return scan.NewNumberLiteralSelector(model.NewVectorPool(opts.StepsBatch), opts, t.Val), nil + } + next, err := newOperator(e.Expr, storage, opts.WithEndTime(opts.Start), hints) + if err != nil { + return nil, err + } + return step_invariant.NewStepInvariantOperator(model.NewVectorPoolWithSize(opts.StepsBatch, 1), next, e.Expr, opts) +} + +func newDeduplication(e logicalplan.Deduplicate, storage *engstore.SelectorPool, opts *query.Options, hints storage.SelectHints) (model.VectorOperator, error) { + // The Deduplicate operator will deduplicate samples using a last-sample-wins strategy. + // Sorting engines by MaxT ensures that samples produced due to + // staleness will be overwritten and corrected by samples coming from + // engines with a higher max time. + sort.Slice(e.Expressions, func(i, j int) bool { + return e.Expressions[i].Engine.MaxT() < e.Expressions[j].Engine.MaxT() + }) + + operators := make([]model.VectorOperator, len(e.Expressions)) + for i, expr := range e.Expressions { + operator, err := newOperator(expr, storage, opts, hints) + if err != nil { + return nil, err + } + operators[i] = operator + } + coalesce := exchange.NewCoalesce(model.NewVectorPool(opts.StepsBatch), opts, 0, operators...) + dedup := exchange.NewDedupOperator(model.NewVectorPool(opts.StepsBatch), coalesce, opts) + return exchange.NewConcurrent(dedup, 2, opts), nil +} + +func newRemoteExecution(e logicalplan.RemoteExecution, opts *query.Options, hints storage.SelectHints) (model.VectorOperator, error) { + // Create a new remote query scoped to the calculated start time. + qry, err := e.Engine.NewRangeQuery(opts.Context, promql.NewPrometheusQueryOpts(false, opts.LookbackDelta), e.Query, e.QueryRangeStart, opts.End, opts.Step) + if err != nil { + return nil, err + } + + // The selector uses the original query time to make sure that steps from different + // operators have the same timestamps. + // We need to set the lookback for the selector to 0 since the remote query already applies one lookback. + selectorOpts := *opts + selectorOpts.LookbackDelta = 0 + remoteExec := remote.NewExecution(qry, model.NewVectorPool(opts.StepsBatch), e.QueryRangeStart, &selectorOpts, hints) + return exchange.NewConcurrent(remoteExec, 2, opts), nil +} + // Copy from https://github.com/prometheus/prometheus/blob/v2.39.1/promql/engine.go#L791. -func getTimeRangesForVectorSelector(n *parser.VectorSelector, opts *query.Options, evalRange int64) (int64, int64) { +func getTimeRangesForVectorSelector(n *logicalplan.VectorSelector, opts *query.Options, evalRange int64) (int64, int64) { start := opts.Start.UnixMilli() end := opts.End.UnixMilli() if n.Timestamp != nil { @@ -436,3 +459,12 @@ func unwrapConstVal(e parser.Expr) (float64, error) { return 0, errors.Wrap(parse.ErrNotSupportedExpr, "matrix selector argument must be a constant") } + +func unpackVectorSelector(t *logicalplan.MatrixSelector) (int64, *logicalplan.VectorSelector, []*labels.Matcher, error) { + switch t := t.VectorSelector.(type) { + case *logicalplan.VectorSelector: + return t.BatchSize, t, t.Filters, nil + default: + return 0, nil, nil, parse.ErrNotSupportedExpr + } +} diff --git a/vendor/github.com/thanos-io/promql-engine/execution/function/absent.go b/vendor/github.com/thanos-io/promql-engine/execution/function/absent.go index b29f04dda24..f2286f54ed8 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/function/absent.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/function/absent.go @@ -9,50 +9,62 @@ import ( "time" "github.com/prometheus/prometheus/model/labels" - "github.com/prometheus/prometheus/promql/parser" "github.com/thanos-io/promql-engine/execution/model" + "github.com/thanos-io/promql-engine/logicalplan" + "github.com/thanos-io/promql-engine/query" ) type absentOperator struct { + model.OperatorTelemetry + once sync.Once funcExpr *parser.Call series []labels.Labels pool *model.VectorPool next model.VectorOperator - model.OperatorTelemetry } -func (o *absentOperator) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - o.SetName("[*absentOperator]") - next := make([]model.ObservableVectorOperator, 0, 1) - if obsnext, ok := o.next.(model.ObservableVectorOperator); ok { - next = append(next, obsnext) +func newAbsentOperator( + funcExpr *parser.Call, + pool *model.VectorPool, + next model.VectorOperator, + opts *query.Options, +) *absentOperator { + return &absentOperator{ + OperatorTelemetry: model.NewTelemetry(absentOperatorName, opts.EnableAnalysis), + funcExpr: funcExpr, + pool: pool, + next: next, } - return o, next } func (o *absentOperator) Explain() (me string, next []model.VectorOperator) { - return "[*absentOperator]", []model.VectorOperator{} + return absentOperatorName, []model.VectorOperator{o.next} } func (o *absentOperator) Series(_ context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + o.loadSeries() return o.series, nil } func (o *absentOperator) loadSeries() { + // we need to put the filtered labels back for absent to compute its series properly o.once.Do(func() { o.pool.SetStepSize(1) // https://github.com/prometheus/prometheus/blob/main/promql/functions.go#L1385 var lm []*labels.Matcher switch n := o.funcExpr.Args[0].(type) { - case *parser.VectorSelector: - lm = n.LabelMatchers - case *parser.MatrixSelector: - lm = n.VectorSelector.(*parser.VectorSelector).LabelMatchers + case *logicalplan.VectorSelector: + lm = append(n.LabelMatchers, n.Filters...) + case *logicalplan.MatrixSelector: + v := n.VectorSelector.(*logicalplan.VectorSelector) + lm = append(v.LabelMatchers, v.Filters...) default: o.series = []labels.Labels{labels.EmptyLabels()} return @@ -80,13 +92,16 @@ func (o *absentOperator) GetPool() *model.VectorPool { } func (o *absentOperator) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + select { case <-ctx.Done(): return nil, ctx.Err() default: } + o.loadSeries() - start := time.Now() vectors, err := o.next.Next(ctx) if err != nil { @@ -106,6 +121,5 @@ func (o *absentOperator) Next(ctx context.Context) ([]model.StepVector, error) { o.next.GetPool().PutStepVector(vectors[i]) } o.next.GetPool().PutVectors(vectors) - o.AddExecutionTimeTaken(time.Since(start)) return result, nil } diff --git a/vendor/github.com/thanos-io/promql-engine/execution/function/functions.go b/vendor/github.com/thanos-io/promql-engine/execution/function/functions.go index 06afa527060..5b76502fc16 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/function/functions.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/function/functions.go @@ -152,6 +152,15 @@ var instantVectorFuncs = map[string]functionCall{ "year": func(f float64, h *histogram.FloatHistogram, vargs ...float64) (float64, bool) { return year(dateFromSampleValue(f)), true }, + // hack we only have sort functions as argument for "timestamp" possibly so they dont actually + // need to sort anything. This is only for compatibility to prometheus as this sort of query does + // not make too much sense. + "sort": simpleFunc(func(v float64) float64 { + return v + }), + "sort_desc": simpleFunc(func(v float64) float64 { + return v + }), } type noArgFunctionCall func(t int64) float64 diff --git a/vendor/github.com/thanos-io/promql-engine/execution/function/histogram.go b/vendor/github.com/thanos-io/promql-engine/execution/function/histogram.go index 24f2665401a..3050badcd86 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/function/histogram.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/function/histogram.go @@ -13,11 +13,11 @@ import ( "github.com/cespare/xxhash/v2" "github.com/prometheus/prometheus/model/labels" - "github.com/prometheus/prometheus/promql/parser" "github.com/thanos-io/promql-engine/execution/model" "github.com/thanos-io/promql-engine/extlabels" + "github.com/thanos-io/promql-engine/query" ) type histogramSeries struct { @@ -28,12 +28,12 @@ type histogramSeries struct { // histogramOperator is a function operator that calculates percentiles. type histogramOperator struct { - pool *model.VectorPool + model.OperatorTelemetry + once sync.Once + series []labels.Labels + pool *model.VectorPool funcArgs parser.Expressions - - once sync.Once - series []labels.Labels scalarOp model.VectorOperator vectorOp model.VectorOperator @@ -47,27 +47,34 @@ type histogramOperator struct { // seriesBuckets are the buckets for each individual conventional histogram series. seriesBuckets []buckets - model.OperatorTelemetry } -func (o *histogramOperator) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - o.SetName("[*functionOperator]") - next := make([]model.ObservableVectorOperator, 0, 2) - if obsScalarOp, ok := o.scalarOp.(model.ObservableVectorOperator); ok { - next = append(next, obsScalarOp) +func newHistogramOperator( + pool *model.VectorPool, + funcArgs parser.Expressions, + scalarOp model.VectorOperator, + vectorOp model.VectorOperator, + opts *query.Options, +) *histogramOperator { + return &histogramOperator{ + pool: pool, + funcArgs: funcArgs, + scalarOp: scalarOp, + vectorOp: vectorOp, + scalarPoints: make([]float64, opts.StepsBatch), + OperatorTelemetry: model.NewTelemetry(histogramOperatorName, opts.EnableAnalysis), } - if obsVectorOp, ok := o.vectorOp.(model.ObservableVectorOperator); ok { - next = append(next, obsVectorOp) - } - return o, next } func (o *histogramOperator) Explain() (me string, next []model.VectorOperator) { next = []model.VectorOperator{o.scalarOp, o.vectorOp} - return fmt.Sprintf("[*functionOperator] histogram_quantile(%v)", o.funcArgs), next + return fmt.Sprintf("%s(%v)", histogramOperatorName, o.funcArgs), next } func (o *histogramOperator) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + var err error o.once.Do(func() { err = o.loadSeries(ctx) }) if err != nil { @@ -82,12 +89,15 @@ func (o *histogramOperator) GetPool() *model.VectorPool { } func (o *histogramOperator) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + select { case <-ctx.Done(): return nil, ctx.Err() default: } - start := time.Now() + var err error o.once.Do(func() { err = o.loadSeries(ctx) }) if err != nil { @@ -116,7 +126,6 @@ func (o *histogramOperator) Next(ctx context.Context) ([]model.StepVector, error o.scalarOp.GetPool().PutStepVector(scalar) } o.scalarOp.GetPool().PutVectors(scalars) - o.AddExecutionTimeTaken(time.Since(start)) return o.processInputSeries(vectors) } @@ -174,8 +183,8 @@ func (o *histogramOperator) processInputSeries(vectors []model.StepVector) ([]mo out = append(out, step) o.vectorOp.GetPool().PutStepVector(vector) } - o.vectorOp.GetPool().PutVectors(vectors) + return out, nil } diff --git a/vendor/github.com/thanos-io/promql-engine/execution/function/noarg.go b/vendor/github.com/thanos-io/promql-engine/execution/function/noarg.go index 9276a431a56..2eba119445c 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/function/noarg.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/function/noarg.go @@ -16,6 +16,8 @@ import ( ) type noArgFunctionOperator struct { + model.OperatorTelemetry + mint int64 maxt int64 step int64 @@ -26,20 +28,16 @@ type noArgFunctionOperator struct { vectorPool *model.VectorPool series []labels.Labels sampleIDs []uint64 - model.OperatorTelemetry -} - -func (o *noArgFunctionOperator) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - o.SetName("[*noArgFunctionOperator]") - return o, []model.ObservableVectorOperator{} } func (o *noArgFunctionOperator) Explain() (me string, next []model.VectorOperator) { - - return fmt.Sprintf("[*noArgFunctionOperator] %v()", o.funcExpr.Func.Name), []model.VectorOperator{} + return fmt.Sprintf("%s %s()", noArgFunctionOperatorName, o.funcExpr.Func.Name), []model.VectorOperator{} } func (o *noArgFunctionOperator) Series(_ context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + return o.series, nil } @@ -47,11 +45,20 @@ func (o *noArgFunctionOperator) GetPool() *model.VectorPool { return o.vectorPool } -func (o *noArgFunctionOperator) Next(_ context.Context) ([]model.StepVector, error) { +func (o *noArgFunctionOperator) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + if o.currentStep > o.maxt { return nil, nil } - start := time.Now() + ret := o.vectorPool.GetVectorBatch() for i := 0; i < o.stepsBatch && o.currentStep <= o.maxt; i++ { sv := o.vectorPool.GetStepVector(o.currentStep) @@ -60,7 +67,6 @@ func (o *noArgFunctionOperator) Next(_ context.Context) ([]model.StepVector, err ret = append(ret, sv) o.currentStep += o.step } - o.AddExecutionTimeTaken(time.Since(start)) return ret, nil } diff --git a/vendor/github.com/thanos-io/promql-engine/execution/function/operator.go b/vendor/github.com/thanos-io/promql-engine/execution/function/operator.go index 24a13cc3c62..42333d1894a 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/function/operator.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/function/operator.go @@ -12,7 +12,6 @@ import ( "github.com/efficientgo/core/errors" "github.com/prometheus/prometheus/model/labels" - "github.com/prometheus/prometheus/promql/parser" "github.com/thanos-io/promql-engine/execution/model" @@ -21,63 +20,29 @@ import ( "github.com/thanos-io/promql-engine/query" ) -// functionOperator returns []model.StepVector after processing input with desired function. -type functionOperator struct { - funcExpr *parser.Call - series []labels.Labels - once sync.Once - - vectorIndex int - nextOps []model.VectorOperator - - call functionCall - scalarPoints [][]float64 - model.OperatorTelemetry -} - -func SetTelemetry(opts *query.Options) model.OperatorTelemetry { - if opts.EnableAnalysis { - return &model.TrackedTelemetry{} - } - return &model.NoopTelemetry{} -} +const ( + absentOperatorName = "[absent]" + functionOperatorName = "[function]" + histogramOperatorName = "[histogram_quantile]" + relabelOperatorName = "[relabel]" + scalarOperatorName = "[scalar]" + timestampOperatorName = "[timestamp]" + noArgFunctionOperatorName = "[noArgFunction]" +) func NewFunctionOperator(funcExpr *parser.Call, nextOps []model.VectorOperator, stepsBatch int, opts *query.Options) (model.VectorOperator, error) { // Some functions need to be handled in special operators - switch funcExpr.Func.Name { case "scalar": - return &scalarFunctionOperator{ - next: nextOps[0], - pool: model.NewVectorPoolWithSize(stepsBatch, 1), - OperatorTelemetry: SetTelemetry(opts), - }, nil - + return newScalarOperator(model.NewVectorPoolWithSize(stepsBatch, 1), nextOps[0], opts), nil + case "timestamp": + return newTimestampOperator(nextOps[0], opts), nil case "label_join", "label_replace": - return &relabelFunctionOperator{ - next: nextOps[0], - funcExpr: funcExpr, - OperatorTelemetry: SetTelemetry(opts), - }, nil - + return newRelabelOperator(nextOps[0], funcExpr, opts), nil case "absent": - return &absentOperator{ - next: nextOps[0], - pool: model.NewVectorPool(stepsBatch), - funcExpr: funcExpr, - OperatorTelemetry: SetTelemetry(opts), - }, nil - + return newAbsentOperator(funcExpr, model.NewVectorPool(stepsBatch), nextOps[0], opts), nil case "histogram_quantile": - return &histogramOperator{ - pool: model.NewVectorPool(stepsBatch), - funcArgs: funcExpr.Args, - once: sync.Once{}, - scalarOp: nextOps[0], - vectorOp: nextOps[1], - scalarPoints: make([]float64, stepsBatch), - OperatorTelemetry: SetTelemetry(opts), - }, nil + return newHistogramOperator(model.NewVectorPool(stepsBatch), funcExpr.Args, nextOps[0], nextOps[1], opts), nil } // Short-circuit functions that take no args. Their only input is the step's timestamp. @@ -101,14 +66,15 @@ func newNoArgsFunctionOperator(funcExpr *parser.Call, stepsBatch int, opts *quer } op := &noArgFunctionOperator{ - currentStep: opts.Start.UnixMilli(), - mint: opts.Start.UnixMilli(), - maxt: opts.End.UnixMilli(), - step: interval, - stepsBatch: stepsBatch, - funcExpr: funcExpr, - call: call, - vectorPool: model.NewVectorPool(stepsBatch), + OperatorTelemetry: model.NewTelemetry(noArgFunctionOperatorName, opts.EnableAnalysis), + currentStep: opts.Start.UnixMilli(), + mint: opts.Start.UnixMilli(), + maxt: opts.End.UnixMilli(), + step: interval, + stepsBatch: stepsBatch, + funcExpr: funcExpr, + call: call, + vectorPool: model.NewVectorPool(stepsBatch), } switch funcExpr.Func.Name { case "pi", "time": @@ -118,14 +84,25 @@ func newNoArgsFunctionOperator(funcExpr *parser.Call, stepsBatch int, opts *quer op.series = []labels.Labels{{}} op.sampleIDs = []uint64{0} } - op.OperatorTelemetry = &model.NoopTelemetry{} - if opts.EnableAnalysis { - op.OperatorTelemetry = &model.TrackedTelemetry{} - } return op, nil } +// functionOperator returns []model.StepVector after processing input with desired function. +type functionOperator struct { + model.OperatorTelemetry + + funcExpr *parser.Call + series []labels.Labels + once sync.Once + + vectorIndex int + nextOps []model.VectorOperator + + call functionCall + scalarPoints [][]float64 +} + func newInstantVectorFunctionOperator(funcExpr *parser.Call, nextOps []model.VectorOperator, stepsBatch int, opts *query.Options) (model.VectorOperator, error) { call, ok := instantVectorFuncs[funcExpr.Func.Name] if !ok { @@ -137,11 +114,12 @@ func newInstantVectorFunctionOperator(funcExpr *parser.Call, nextOps []model.Vec scalarPoints[i] = make([]float64, len(nextOps)-1) } f := &functionOperator{ - nextOps: nextOps, - call: call, - funcExpr: funcExpr, - vectorIndex: 0, - scalarPoints: scalarPoints, + OperatorTelemetry: model.NewTelemetry(functionOperatorName, opts.EnableAnalysis), + nextOps: nextOps, + call: call, + funcExpr: funcExpr, + vectorIndex: 0, + scalarPoints: scalarPoints, } for i := range funcExpr.Args { @@ -150,10 +128,6 @@ func newInstantVectorFunctionOperator(funcExpr *parser.Call, nextOps []model.Vec break } } - f.OperatorTelemetry = &model.NoopTelemetry{} - if opts.EnableAnalysis { - f.OperatorTelemetry = &model.TrackedTelemetry{} - } // Check selector type. switch funcExpr.Args[f.vectorIndex].Type() { @@ -164,22 +138,14 @@ func newInstantVectorFunctionOperator(funcExpr *parser.Call, nextOps []model.Vec } } -func (o *functionOperator) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - o.SetName("[*functionOperator]") - obsOperators := make([]model.ObservableVectorOperator, 0, len(o.nextOps)) - for _, operator := range o.nextOps { - if obsOperator, ok := operator.(model.ObservableVectorOperator); ok { - obsOperators = append(obsOperators, obsOperator) - } - } - return o, obsOperators -} - func (o *functionOperator) Explain() (me string, next []model.VectorOperator) { - return fmt.Sprintf("[*functionOperator] %v(%v)", o.funcExpr.Func.Name, o.funcExpr.Args), o.nextOps + return fmt.Sprintf("%s %v(%v)", functionOperatorName, o.funcExpr.Func.Name, o.funcExpr.Args), o.nextOps } func (o *functionOperator) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + if err := o.loadSeries(ctx); err != nil { return nil, err } @@ -192,16 +158,18 @@ func (o *functionOperator) GetPool() *model.VectorPool { } func (o *functionOperator) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + select { case <-ctx.Done(): return nil, ctx.Err() default: } - if err := o.loadSeries(ctx); err != nil { return nil, err } - start := time.Now() + // Process non-variadic single/multi-arg instant vector and scalar input functions. // Call next on vector input. vectors, err := o.nextOps[o.vectorIndex].Next(ctx) @@ -261,8 +229,6 @@ func (o *functionOperator) Next(ctx context.Context) ([]model.StepVector, error) } } - o.AddExecutionTimeTaken(time.Since(start)) - return vectors, nil } diff --git a/vendor/github.com/thanos-io/promql-engine/execution/function/relabel.go b/vendor/github.com/thanos-io/promql-engine/execution/function/relabel.go index 38127f0fbce..3db44b320ed 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/function/relabel.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/function/relabel.go @@ -17,47 +17,55 @@ import ( "github.com/prometheus/prometheus/promql/parser" "github.com/thanos-io/promql-engine/execution/model" + "github.com/thanos-io/promql-engine/query" ) -type relabelFunctionOperator struct { +type relabelOperator struct { + model.OperatorTelemetry + next model.VectorOperator funcExpr *parser.Call once sync.Once series []labels.Labels - model.OperatorTelemetry } -func (o *relabelFunctionOperator) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - o.SetName("[*relabelFunctionOperator]") - next := make([]model.ObservableVectorOperator, 0, 1) - if obsnext, ok := o.next.(model.ObservableVectorOperator); ok { - next = append(next, obsnext) +func newRelabelOperator( + next model.VectorOperator, + funcExpr *parser.Call, + opts *query.Options, +) *relabelOperator { + return &relabelOperator{ + OperatorTelemetry: model.NewTelemetry(relabelOperatorName, opts.EnableAnalysis), + next: next, + funcExpr: funcExpr, } - return o, next } -func (o *relabelFunctionOperator) Explain() (me string, next []model.VectorOperator) { - return "[*relabelFunctionOperator]", []model.VectorOperator{} +func (o *relabelOperator) Explain() (me string, next []model.VectorOperator) { + return relabelOperatorName, []model.VectorOperator{} } -func (o *relabelFunctionOperator) Series(ctx context.Context) ([]labels.Labels, error) { +func (o *relabelOperator) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + var err error o.once.Do(func() { err = o.loadSeries(ctx) }) return o.series, err } -func (o *relabelFunctionOperator) GetPool() *model.VectorPool { +func (o *relabelOperator) GetPool() *model.VectorPool { return o.next.GetPool() } -func (o *relabelFunctionOperator) Next(ctx context.Context) ([]model.StepVector, error) { +func (o *relabelOperator) Next(ctx context.Context) ([]model.StepVector, error) { start := time.Now() - next, err := o.next.Next(ctx) - o.AddExecutionTimeTaken(time.Since(start)) - return next, err + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + + return o.next.Next(ctx) } -func (o *relabelFunctionOperator) loadSeries(ctx context.Context) (err error) { +func (o *relabelOperator) loadSeries(ctx context.Context) (err error) { series, err := o.next.Series(ctx) if err != nil { return err @@ -86,7 +94,7 @@ func unwrap(expr parser.Expr) (string, error) { } } -func (o *relabelFunctionOperator) loadSeriesForLabelJoin(series []labels.Labels) error { +func (o *relabelOperator) loadSeriesForLabelJoin(series []labels.Labels) error { labelJoinDst, err := unwrap(o.funcExpr.Args[1]) if err != nil { return errors.Wrap(err, "unable to unwrap string argument") @@ -124,7 +132,7 @@ func (o *relabelFunctionOperator) loadSeriesForLabelJoin(series []labels.Labels) } return nil } -func (o *relabelFunctionOperator) loadSeriesForLabelReplace(series []labels.Labels) error { +func (o *relabelOperator) loadSeriesForLabelReplace(series []labels.Labels) error { labelReplaceDst, err := unwrap(o.funcExpr.Args[1]) if err != nil { return errors.Wrap(err, "unable to unwrap string argument") diff --git a/vendor/github.com/thanos-io/promql-engine/execution/function/scalar.go b/vendor/github.com/thanos-io/promql-engine/execution/function/scalar.go index 9d6a8553170..fc1a77b070c 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/function/scalar.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/function/scalar.go @@ -11,42 +11,48 @@ import ( "github.com/prometheus/prometheus/model/labels" "github.com/thanos-io/promql-engine/execution/model" + "github.com/thanos-io/promql-engine/query" ) -type scalarFunctionOperator struct { +type scalarOperator struct { pool *model.VectorPool next model.VectorOperator model.OperatorTelemetry } -func (o *scalarFunctionOperator) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - o.SetName("[*scalarFunctionOperator]") - next := make([]model.ObservableVectorOperator, 0, 1) - if obsnext, ok := o.next.(model.ObservableVectorOperator); ok { - next = append(next, obsnext) +func newScalarOperator(pool *model.VectorPool, next model.VectorOperator, opts *query.Options) *scalarOperator { + return &scalarOperator{ + pool: pool, + next: next, + OperatorTelemetry: model.NewTelemetry(scalarOperatorName, opts.EnableAnalysis), } - return o, next } -func (o *scalarFunctionOperator) Explain() (me string, next []model.VectorOperator) { - return "[*scalarFunctionOperator]", []model.VectorOperator{} +func (o *scalarOperator) Explain() (me string, next []model.VectorOperator) { + return scalarOperatorName, []model.VectorOperator{o.next} } -func (o *scalarFunctionOperator) Series(ctx context.Context) ([]labels.Labels, error) { +func (o *scalarOperator) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + return nil, nil } -func (o *scalarFunctionOperator) GetPool() *model.VectorPool { +func (o *scalarOperator) GetPool() *model.VectorPool { return o.pool } -func (o *scalarFunctionOperator) Next(ctx context.Context) ([]model.StepVector, error) { +func (o *scalarOperator) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + select { case <-ctx.Done(): return nil, ctx.Err() default: } - start := time.Now() + in, err := o.next.Next(ctx) if err != nil { return nil, err @@ -67,7 +73,6 @@ func (o *scalarFunctionOperator) Next(ctx context.Context) ([]model.StepVector, o.next.GetPool().PutStepVector(vector) } o.next.GetPool().PutVectors(in) - o.AddExecutionTimeTaken(time.Since(start)) return result, nil } diff --git a/vendor/github.com/thanos-io/promql-engine/execution/function/timestamp.go b/vendor/github.com/thanos-io/promql-engine/execution/function/timestamp.go new file mode 100644 index 00000000000..471f367fda9 --- /dev/null +++ b/vendor/github.com/thanos-io/promql-engine/execution/function/timestamp.go @@ -0,0 +1,91 @@ +// Copyright (c) The Thanos Community Authors. +// Licensed under the Apache License 2.0. + +package function + +import ( + "context" + "sync" + "time" + + "github.com/prometheus/prometheus/model/labels" + + "github.com/thanos-io/promql-engine/execution/model" + "github.com/thanos-io/promql-engine/extlabels" + "github.com/thanos-io/promql-engine/query" +) + +type timestampOperator struct { + next model.VectorOperator + model.OperatorTelemetry + + series []labels.Labels + once sync.Once +} + +func newTimestampOperator(next model.VectorOperator, opts *query.Options) *timestampOperator { + return ×tampOperator{ + next: next, + OperatorTelemetry: model.NewTelemetry(timestampOperatorName, opts.EnableAnalysis), + } +} + +func (o *timestampOperator) Explain() (me string, next []model.VectorOperator) { + return timestampOperatorName, []model.VectorOperator{o.next} +} + +func (o *timestampOperator) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + + if err := o.loadSeries(ctx); err != nil { + return nil, err + } + return o.series, nil +} + +func (o *timestampOperator) loadSeries(ctx context.Context) error { + var err error + o.once.Do(func() { + series, loadErr := o.next.Series(ctx) + if loadErr != nil { + err = loadErr + return + } + o.series = make([]labels.Labels, len(series)) + + b := labels.ScratchBuilder{} + for i, s := range series { + lbls, _ := extlabels.DropMetricName(s, b) + o.series[i] = lbls + } + }) + + return err +} + +func (o *timestampOperator) GetPool() *model.VectorPool { + return o.next.GetPool() +} + +func (o *timestampOperator) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + in, err := o.next.Next(ctx) + if err != nil { + return nil, err + } + for _, vector := range in { + for i := range vector.Samples { + vector.Samples[i] = float64(vector.T / 1000) + } + } + return in, nil +} diff --git a/vendor/github.com/thanos-io/promql-engine/execution/model/operator.go b/vendor/github.com/thanos-io/promql-engine/execution/model/operator.go index 021a36fe43a..176377dc957 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/model/operator.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/model/operator.go @@ -10,44 +10,51 @@ import ( "github.com/prometheus/prometheus/model/labels" ) -type NoopTelemetry struct{} - -type TrackedTelemetry struct { - name string - ExecutionTime time.Duration +type OperatorTelemetry interface { + AddExecutionTimeTaken(time.Duration) + ExecutionTimeTaken() time.Duration + Name() string } -func (ti *NoopTelemetry) AddExecutionTimeTaken(t time.Duration) {} +func NewTelemetry(name string, enabled bool) OperatorTelemetry { + if enabled { + return NewTrackedTelemetry(name) + } + return NewNoopTelemetry(name) -func (ti *TrackedTelemetry) AddExecutionTimeTaken(t time.Duration) { - ti.ExecutionTime += t } -func (ti *TrackedTelemetry) Name() string { - return ti.name +type NoopTelemetry struct { + name string } -func (ti *TrackedTelemetry) SetName(operatorName string) { - ti.name = operatorName +func NewNoopTelemetry(name string) *NoopTelemetry { + return &NoopTelemetry{name: name} } -func (ti *NoopTelemetry) Name() string { - return "" +func (tm *NoopTelemetry) Name() string { return tm.name } + +func (tm *NoopTelemetry) AddExecutionTimeTaken(t time.Duration) {} + +func (tm *NoopTelemetry) ExecutionTimeTaken() time.Duration { + return time.Duration(0) } -func (ti *NoopTelemetry) SetName(operatorName string) {} +type TrackedTelemetry struct { + name string + ExecutionTime time.Duration +} -type OperatorTelemetry interface { - AddExecutionTimeTaken(time.Duration) - ExecutionTimeTaken() time.Duration - SetName(string) - Name() string +func NewTrackedTelemetry(name string) *TrackedTelemetry { + return &TrackedTelemetry{name: name} } -func (ti *NoopTelemetry) ExecutionTimeTaken() time.Duration { - return time.Duration(0) +func (ti *TrackedTelemetry) Name() string { + return ti.name } +func (ti *TrackedTelemetry) AddExecutionTimeTaken(t time.Duration) { ti.ExecutionTime += t } + func (ti *TrackedTelemetry) ExecutionTimeTaken() time.Duration { return ti.ExecutionTime } @@ -55,7 +62,6 @@ func (ti *TrackedTelemetry) ExecutionTimeTaken() time.Duration { type ObservableVectorOperator interface { VectorOperator OperatorTelemetry - Analyze() (OperatorTelemetry, []ObservableVectorOperator) } // VectorOperator performs operations on series in step by step fashion. diff --git a/vendor/github.com/thanos-io/promql-engine/execution/parse/functions.go b/vendor/github.com/thanos-io/promql-engine/execution/parse/functions.go index 64a4630923d..0ba8173df69 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/parse/functions.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/parse/functions.go @@ -1,3 +1,6 @@ +// Copyright (c) The Thanos Community Authors. +// Licensed under the Apache License 2.0. + package parse import ( diff --git a/vendor/github.com/thanos-io/promql-engine/execution/remote/operator.go b/vendor/github.com/thanos-io/promql-engine/execution/remote/operator.go index 5e1928b34b5..7e376bcb257 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/remote/operator.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/remote/operator.go @@ -11,6 +11,7 @@ import ( "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/promql" + "github.com/prometheus/prometheus/storage" "github.com/thanos-io/promql-engine/execution/model" "github.com/thanos-io/promql-engine/execution/scan" @@ -28,35 +29,30 @@ type Execution struct { model.OperatorTelemetry } -func NewExecution(query promql.Query, pool *model.VectorPool, queryRangeStart time.Time, opts *query.Options) *Execution { +func NewExecution(query promql.Query, pool *model.VectorPool, queryRangeStart time.Time, opts *query.Options, hints storage.SelectHints) *Execution { storage := newStorageFromQuery(query, opts) - e := &Execution{ - storage: storage, - query: query, - opts: opts, - queryRangeStart: queryRangeStart, - vectorSelector: scan.NewVectorSelector(pool, storage, opts, 0, 0, 0, 1), + return &Execution{ + storage: storage, + query: query, + opts: opts, + queryRangeStart: queryRangeStart, + vectorSelector: scan.NewVectorSelector(pool, storage, opts, 0, hints, 0, false, 0, 1), + OperatorTelemetry: model.NewTelemetry("[remoteExec]", opts.EnableAnalysis), } - e.OperatorTelemetry = &model.NoopTelemetry{} - if opts.EnableAnalysis { - e.OperatorTelemetry = &model.TrackedTelemetry{} - } - return e -} - -func (e *Execution) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - e.SetName("[*remoteExec]") - return e, nil } func (e *Execution) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { e.AddExecutionTimeTaken(time.Since(start)) }() + return e.vectorSelector.Series(ctx) } func (e *Execution) Next(ctx context.Context) ([]model.StepVector, error) { start := time.Now() + defer func() { e.AddExecutionTimeTaken(time.Since(start)) }() + next, err := e.vectorSelector.Next(ctx) - e.AddExecutionTimeTaken(time.Since(start)) if next == nil { // Closing the storage prematurely can lead to results from the query // engine to be recycled. Because of this, we close the storage only @@ -71,7 +67,7 @@ func (e *Execution) GetPool() *model.VectorPool { } func (e *Execution) Explain() (me string, next []model.VectorOperator) { - return fmt.Sprintf("[*remoteExec] %s (%d, %d)", e.query, e.opts.Start.Unix(), e.opts.End.Unix()), nil + return fmt.Sprintf("[remoteExec] %s (%d, %d)", e.query, e.queryRangeStart.Unix(), e.opts.End.Unix()), nil } type storageAdapter struct { @@ -108,7 +104,6 @@ func (s *storageAdapter) executeQuery(ctx context.Context) { s.err = result.Err return } - switch val := result.Value.(type) { case promql.Matrix: s.series = make([]engstore.SignedSeries, len(val)) diff --git a/vendor/github.com/thanos-io/promql-engine/execution/scan/functions.go b/vendor/github.com/thanos-io/promql-engine/execution/scan/functions.go index b3460f7b40c..548a656b3e0 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/scan/functions.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/scan/functions.go @@ -10,16 +10,19 @@ import ( "github.com/thanos-io/promql-engine/execution/aggregate" "github.com/thanos-io/promql-engine/execution/parse" + "github.com/thanos-io/promql-engine/ringbuffer" ) -type Sample struct { - T int64 +type Value struct { F float64 H *histogram.FloatHistogram } +type Sample ringbuffer.Sample[Value] +type SamplesBuffer ringbuffer.RingBuffer[Value] + type FunctionArgs struct { - Samples []Sample + Samples []ringbuffer.Sample[Value] StepTime int64 SelectRange int64 ScalarPoints []float64 @@ -29,16 +32,16 @@ type FunctionArgs struct { type FunctionCall func(f FunctionArgs) (float64, *histogram.FloatHistogram, bool) -func instantValue(samples []Sample, isRate bool) (float64, bool) { +func instantValue(samples []ringbuffer.Sample[Value], isRate bool) (float64, bool) { lastSample := samples[len(samples)-1] previousSample := samples[len(samples)-2] var resultValue float64 - if isRate && lastSample.F < previousSample.F { + if isRate && lastSample.V.F < previousSample.V.F { // Counter reset. - resultValue = lastSample.F + resultValue = lastSample.V.F } else { - resultValue = lastSample.F - previousSample.F + resultValue = lastSample.V.F - previousSample.V.F } sampledInterval := lastSample.T - previousSample.T @@ -102,7 +105,7 @@ var rangeVectorFuncs = map[string]FunctionCall{ if len(f.Samples) == 0 { return 0., nil, false } - return f.Samples[len(f.Samples)-1].F, nil, true + return f.Samples[len(f.Samples)-1].V.F, nil, true }, "present_over_time": func(f FunctionArgs) (float64, *histogram.FloatHistogram, bool) { if len(f.Samples) == 0 { @@ -115,8 +118,8 @@ var rangeVectorFuncs = map[string]FunctionCall{ return 0., nil, false } floats := make([]float64, len(f.Samples)) - for i, v := range f.Samples { - floats[i] = v.F + for i, sample := range f.Samples { + floats[i] = sample.V.F } return aggregate.Quantile(f.ScalarPoints[0], floats), nil, true }, @@ -225,7 +228,7 @@ func NewRangeVectorFunc(name string) (FunctionCall, error) { // It calculates the rate (allowing for counter resets if isCounter is true), // extrapolates if the first/last sample is close to the boundary, and returns // the result as either per-second (if isRate is true) or overall. -func extrapolatedRate(samples []Sample, isCounter, isRate bool, stepTime int64, selectRange int64, offset int64) (float64, *histogram.FloatHistogram) { +func extrapolatedRate(samples []ringbuffer.Sample[Value], isCounter, isRate bool, stepTime int64, selectRange int64, offset int64) (float64, *histogram.FloatHistogram) { var ( rangeStart = stepTime - (selectRange + offset) rangeEnd = stepTime - offset @@ -233,17 +236,17 @@ func extrapolatedRate(samples []Sample, isCounter, isRate bool, stepTime int64, resultHistogram *histogram.FloatHistogram ) - if samples[0].H != nil { + if samples[0].V.H != nil { resultHistogram = histogramRate(samples, isCounter) } else { - resultValue = samples[len(samples)-1].F - samples[0].F + resultValue = samples[len(samples)-1].V.F - samples[0].V.F if isCounter { var lastValue float64 for _, sample := range samples { - if sample.F < lastValue { + if sample.V.F < lastValue { resultValue += lastValue } - lastValue = sample.F + lastValue = sample.V.F } } } @@ -255,7 +258,7 @@ func extrapolatedRate(samples []Sample, isCounter, isRate bool, stepTime int64, sampledInterval := float64(samples[len(samples)-1].T-samples[0].T) / 1000 averageDurationBetweenSamples := sampledInterval / float64(len(samples)-1) - if isCounter && resultValue > 0 && samples[0].F >= 0 { + if isCounter && resultValue > 0 && samples[0].V.F >= 0 { // Counters cannot be negative. If we have any slope at // all (i.e. resultValue went up), we can extrapolate // the zero point of the counter. If the duration to the @@ -263,7 +266,7 @@ func extrapolatedRate(samples []Sample, isCounter, isRate bool, stepTime int64, // take the zero point as the start of the series, // thereby avoiding extrapolation to negative counter // values. - durationToZero := sampledInterval * (samples[0].F / resultValue) + durationToZero := sampledInterval * (samples[0].V.F / resultValue) if durationToZero < durationToStart { durationToStart = durationToZero } @@ -304,7 +307,7 @@ func extrapolatedRate(samples []Sample, isCounter, isRate bool, stepTime int64, // It calculates the rate (allowing for counter resets if isCounter is true), // taking into account the last sample before the range start, and returns // the result as either per-second (if isRate is true) or overall. -func extendedRate(samples []Sample, isCounter, isRate bool, stepTime int64, selectRange int64, offset int64, metricAppearedTs int64) (float64, *histogram.FloatHistogram) { +func extendedRate(samples []ringbuffer.Sample[Value], isCounter, isRate bool, stepTime int64, selectRange int64, offset int64, metricAppearedTs int64) (float64, *histogram.FloatHistogram) { var ( rangeStart = stepTime - (selectRange + offset) rangeEnd = stepTime - offset @@ -312,7 +315,7 @@ func extendedRate(samples []Sample, isCounter, isRate bool, stepTime int64, sele resultHistogram *histogram.FloatHistogram ) - if samples[0].H != nil { + if samples[0].V.H != nil { // TODO - support extended rate for histograms resultHistogram = histogramRate(samples, isCounter) return resultValue, resultHistogram @@ -320,7 +323,7 @@ func extendedRate(samples []Sample, isCounter, isRate bool, stepTime int64, sele sameVals := true for i := range samples { - if i > 0 && samples[i-1].F != samples[i].F { + if i > 0 && samples[i-1].V.F != samples[i].V.F { sameVals = false break } @@ -332,7 +335,7 @@ func extendedRate(samples []Sample, isCounter, isRate bool, stepTime int64, sele if isCounter && !isRate && sameVals { // Make sure we are not at the end of the range. if stepTime-offset <= until { - return samples[0].F, nil + return samples[0].V.F, nil } } @@ -360,13 +363,13 @@ func extendedRate(samples []Sample, isCounter, isRate bool, stepTime int64, sele if isCounter { for i := firstPoint; i < len(samples); i++ { sample := samples[i] - if sample.F < lastValue { + if sample.V.F < lastValue { counterCorrection += lastValue } - lastValue = sample.F + lastValue = sample.V.F } } - resultValue = samples[len(samples)-1].F - samples[firstPoint].F + counterCorrection + resultValue = samples[len(samples)-1].V.F - samples[firstPoint].V.F + counterCorrection // Duration between last sample and boundary of range. durationToEnd := float64(rangeEnd - samples[len(samples)-1].T) @@ -391,14 +394,14 @@ func extendedRate(samples []Sample, isCounter, isRate bool, stepTime int64, sele // histogramRate is a helper function for extrapolatedRate. It requires // points[0] to be a histogram. It returns nil if any other Point in points is // not a histogram. -func histogramRate(points []Sample, isCounter bool) *histogram.FloatHistogram { +func histogramRate(points []ringbuffer.Sample[Value], isCounter bool) *histogram.FloatHistogram { // Calculating a rate on a single sample is not defined. if len(points) < 2 { return nil } - prev := points[0].H // We already know that this is a histogram. - last := points[len(points)-1].H + prev := points[0].V.H // We already know that this is a histogram. + last := points[len(points)-1].V.H if last == nil { return nil // Range contains a mix of histograms and floats. } @@ -413,7 +416,7 @@ func histogramRate(points []Sample, isCounter bool) *histogram.FloatHistogram { // - Are all data points histograms? // []FloatPoint and a []HistogramPoint separately. for _, currPoint := range points[1 : len(points)-1] { - curr := currPoint.H + curr := currPoint.V.H if curr == nil { return nil // Range contains a mix of histograms and floats. } @@ -431,7 +434,7 @@ func histogramRate(points []Sample, isCounter bool) *histogram.FloatHistogram { if isCounter { // Second iteration to deal with counter resets. for _, currPoint := range points[1:] { - curr := currPoint.H + curr := currPoint.V.H if curr.DetectReset(prev) { h.Add(prev) } @@ -442,42 +445,42 @@ func histogramRate(points []Sample, isCounter bool) *histogram.FloatHistogram { return h.Compact(0) } -func maxOverTime(points []Sample) float64 { - max := points[0].F +func maxOverTime(points []ringbuffer.Sample[Value]) float64 { + max := points[0].V.F for _, v := range points { - if v.F > max || math.IsNaN(max) { - max = v.F + if v.V.F > max || math.IsNaN(max) { + max = v.V.F } } return max } -func minOverTime(points []Sample) float64 { - min := points[0].F +func minOverTime(points []ringbuffer.Sample[Value]) float64 { + min := points[0].V.F for _, v := range points { - if v.F < min || math.IsNaN(min) { - min = v.F + if v.V.F < min || math.IsNaN(min) { + min = v.V.F } } return min } -func countOverTime(points []Sample) float64 { +func countOverTime(points []ringbuffer.Sample[Value]) float64 { return float64(len(points)) } -func avgOverTime(points []Sample) float64 { +func avgOverTime(points []ringbuffer.Sample[Value]) float64 { var mean, count, c float64 for _, v := range points { count++ if math.IsInf(mean, 0) { - if math.IsInf(v.F, 0) && (mean > 0) == (v.F > 0) { - // The `mean` and `v.F` values are `Inf` of the same sign. They + if math.IsInf(v.V.F, 0) && (mean > 0) == (v.V.F > 0) { + // The `mean` and `v.V.F` values are `Inf` of the same sign. They // can't be subtracted, but the value of `mean` is correct // already. continue } - if !math.IsInf(v.F, 0) && !math.IsNaN(v.F) { + if !math.IsInf(v.V.F, 0) && !math.IsNaN(v.V.F) { // At this stage, the mean is an infinite. If the added // value is neither an Inf or a Nan, we can keep that mean // value. @@ -487,7 +490,7 @@ func avgOverTime(points []Sample) float64 { continue } } - mean, c = kahanSumInc(v.F/count-mean/count, mean, c) + mean, c = kahanSumInc(v.V.F/count-mean/count, mean, c) } if math.IsInf(mean, 0) { @@ -496,10 +499,10 @@ func avgOverTime(points []Sample) float64 { return mean + c } -func sumOverTime(points []Sample) float64 { +func sumOverTime(points []ringbuffer.Sample[Value]) float64 { var sum, c float64 for _, v := range points { - sum, c = kahanSumInc(v.F, sum, c) + sum, c = kahanSumInc(v.V.F, sum, c) } if math.IsInf(sum, 0) { return sum @@ -507,38 +510,38 @@ func sumOverTime(points []Sample) float64 { return sum + c } -func stddevOverTime(points []Sample) float64 { +func stddevOverTime(points []ringbuffer.Sample[Value]) float64 { var count float64 var mean, cMean float64 var aux, cAux float64 for _, v := range points { count++ - delta := v.F - (mean + cMean) + delta := v.V.F - (mean + cMean) mean, cMean = kahanSumInc(delta/count, mean, cMean) - aux, cAux = kahanSumInc(delta*(v.F-(mean+cMean)), aux, cAux) + aux, cAux = kahanSumInc(delta*(v.V.F-(mean+cMean)), aux, cAux) } return math.Sqrt((aux + cAux) / count) } -func stdvarOverTime(points []Sample) float64 { +func stdvarOverTime(points []ringbuffer.Sample[Value]) float64 { var count float64 var mean, cMean float64 var aux, cAux float64 for _, v := range points { count++ - delta := v.F - (mean + cMean) + delta := v.V.F - (mean + cMean) mean, cMean = kahanSumInc(delta/count, mean, cMean) - aux, cAux = kahanSumInc(delta*(v.F-(mean+cMean)), aux, cAux) + aux, cAux = kahanSumInc(delta*(v.V.F-(mean+cMean)), aux, cAux) } return (aux + cAux) / count } -func changes(points []Sample) float64 { +func changes(points []ringbuffer.Sample[Value]) float64 { var count float64 - prev := points[0].F + prev := points[0].V.F count = 0 for _, sample := range points[1:] { - current := sample.F + current := sample.V.F if current != prev && !(math.IsNaN(current) && math.IsNaN(prev)) { count++ } @@ -547,7 +550,7 @@ func changes(points []Sample) float64 { return count } -func deriv(points []Sample) float64 { +func deriv(points []ringbuffer.Sample[Value]) float64 { // We pass in an arbitrary timestamp that is near the values in use // to avoid floating point accuracy issues, see // https://github.com/prometheus/prometheus/issues/2674 @@ -555,11 +558,11 @@ func deriv(points []Sample) float64 { return slope } -func resets(points []Sample) float64 { +func resets(points []ringbuffer.Sample[Value]) float64 { count := 0 - prev := points[0].F + prev := points[0].V.F for _, sample := range points[1:] { - current := sample.F + current := sample.V.F if current < prev { count++ } @@ -569,7 +572,7 @@ func resets(points []Sample) float64 { return float64(count) } -func linearRegression(Samples []Sample, interceptTime int64) (slope, intercept float64) { +func linearRegression(Samples []ringbuffer.Sample[Value], interceptTime int64) (slope, intercept float64) { var ( n float64 sumX, cX float64 @@ -579,18 +582,18 @@ func linearRegression(Samples []Sample, interceptTime int64) (slope, intercept f initY float64 constY bool ) - initY = Samples[0].F + initY = Samples[0].V.F constY = true for i, sample := range Samples { // Set constY to false if any new y values are encountered. - if constY && i > 0 && sample.F != initY { + if constY && i > 0 && sample.V.F != initY { constY = false } n += 1.0 x := float64(sample.T-interceptTime) / 1e3 sumX, cX = kahanSumInc(x, sumX, cX) - sumY, cY = kahanSumInc(sample.F, sumY, cY) - sumXY, cXY = kahanSumInc(x*sample.F, sumXY, cXY) + sumY, cY = kahanSumInc(sample.V.F, sumY, cY) + sumXY, cXY = kahanSumInc(x*sample.V.F, sumXY, cXY) sumX2, cX2 = kahanSumInc(x*x, sumX2, cX2) } if constY { @@ -612,10 +615,10 @@ func linearRegression(Samples []Sample, interceptTime int64) (slope, intercept f return slope, intercept } -func filterFloatOnlySamples(samples []Sample) []Sample { +func filterFloatOnlySamples(samples []ringbuffer.Sample[Value]) []ringbuffer.Sample[Value] { i := 0 for _, sample := range samples { - if sample.H == nil { + if sample.V.H == nil { samples[i] = sample i++ } diff --git a/vendor/github.com/thanos-io/promql-engine/execution/scan/literal_selector.go b/vendor/github.com/thanos-io/promql-engine/execution/scan/literal_selector.go index c65192f14fe..382e5dd3f37 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/scan/literal_selector.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/scan/literal_selector.go @@ -32,7 +32,9 @@ type numberLiteralSelector struct { } func NewNumberLiteralSelector(pool *model.VectorPool, opts *query.Options, val float64) *numberLiteralSelector { - op := &numberLiteralSelector{ + return &numberLiteralSelector{ + OperatorTelemetry: model.NewTelemetry("[numberLiteral]", opts.EnableAnalysis), + vectorPool: pool, numSteps: opts.NumSteps(), mint: opts.Start.UnixMilli(), @@ -41,25 +43,16 @@ func NewNumberLiteralSelector(pool *model.VectorPool, opts *query.Options, val f currentStep: opts.Start.UnixMilli(), val: val, } - - op.OperatorTelemetry = &model.NoopTelemetry{} - if opts.EnableAnalysis { - op.OperatorTelemetry = &model.TrackedTelemetry{} - } - - return op -} - -func (o *numberLiteralSelector) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - o.SetName("[*numberLiteralSelector] ") - return o, nil } func (o *numberLiteralSelector) Explain() (me string, next []model.VectorOperator) { - return fmt.Sprintf("[*numberLiteralSelector] %v", o.val), nil + return fmt.Sprintf("[numberLiteral] %v", o.val), nil } func (o *numberLiteralSelector) Series(context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + o.loadSeries() return o.series, nil } @@ -69,12 +62,14 @@ func (o *numberLiteralSelector) GetPool() *model.VectorPool { } func (o *numberLiteralSelector) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + select { case <-ctx.Done(): return nil, ctx.Err() default: } - start := time.Now() if o.currentStep > o.maxt { return nil, nil @@ -98,7 +93,6 @@ func (o *numberLiteralSelector) Next(ctx context.Context) ([]model.StepVector, e o.step = 1 } o.currentStep += o.step * int64(o.numSteps) - o.AddExecutionTimeTaken(time.Since(start)) return vectors, nil } diff --git a/vendor/github.com/thanos-io/promql-engine/execution/scan/matrix_selector.go b/vendor/github.com/thanos-io/promql-engine/execution/scan/matrix_selector.go index 384046d2b12..02555344312 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/scan/matrix_selector.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/scan/matrix_selector.go @@ -14,30 +14,35 @@ import ( "github.com/prometheus/prometheus/model/value" "github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/tsdb/chunkenc" + "golang.org/x/exp/slices" "github.com/thanos-io/promql-engine/execution/function" "github.com/thanos-io/promql-engine/execution/model" engstore "github.com/thanos-io/promql-engine/execution/storage" "github.com/thanos-io/promql-engine/extlabels" "github.com/thanos-io/promql-engine/query" + "github.com/thanos-io/promql-engine/ringbuffer" ) type matrixScanner struct { labels labels.Labels signature uint64 - previousSamples []Sample + previousSamples []ringbuffer.Sample[Value] samples *storage.BufferedSeriesIterator metricAppearedTs *int64 deltaReduced bool } type matrixSelector struct { + model.OperatorTelemetry + vectorPool *model.VectorPool functionName string storage engstore.SeriesSelector scalarArgs []float64 call FunctionCall scanners []matrixScanner + bufferTail []ringbuffer.Sample[Value] series []labels.Labels once sync.Once @@ -58,7 +63,6 @@ type matrixSelector struct { // Lookback delta for extended range functions. extLookbackDelta int64 - model.OperatorTelemetry } var ErrNativeHistogramsNotSupported = errors.New("native histograms are not supported in extended range functions") @@ -80,11 +84,14 @@ func NewMatrixSelector( } isExtFunction := function.IsExtFunction(functionName) m := &matrixSelector{ + OperatorTelemetry: model.NewTelemetry("matrixSelector", opts.EnableAnalysis), + storage: selector, call: call, functionName: functionName, vectorPool: pool, scalarArgs: []float64{arg}, + bufferTail: make([]ringbuffer.Sample[Value], 16), numSteps: opts.NumSteps(), mint: opts.Start.UnixMilli(), @@ -102,10 +109,7 @@ func NewMatrixSelector( extLookbackDelta: opts.ExtLookbackDelta.Milliseconds(), } - m.OperatorTelemetry = &model.NoopTelemetry{} - if opts.EnableAnalysis { - m.OperatorTelemetry = &model.TrackedTelemetry{} - } + // For instant queries, set the step to a positive value // so that the operator can terminate. if m.step == 0 { @@ -115,20 +119,18 @@ func NewMatrixSelector( return m, nil } -func (o *matrixSelector) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - o.SetName("[*matrixSelector]") - return o, nil -} - func (o *matrixSelector) Explain() (me string, next []model.VectorOperator) { r := time.Duration(o.selectRange) * time.Millisecond if o.call != nil { - return fmt.Sprintf("[*matrixSelector] %v({%v}[%s] %v mod %v)", o.functionName, o.storage.Matchers(), r, o.shard, o.numShards), nil + return fmt.Sprintf("[matrixSelector] %v({%v}[%s] %v mod %v)", o.functionName, o.storage.Matchers(), r, o.shard, o.numShards), nil } - return fmt.Sprintf("[*matrixSelector] {%v}[%s] %v mod %v", o.storage.Matchers(), r, o.shard, o.numShards), nil + return fmt.Sprintf("[matrixSelector] {%v}[%s] %v mod %v", o.storage.Matchers(), r, o.shard, o.numShards), nil } func (o *matrixSelector) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + if err := o.loadSeries(ctx); err != nil { return nil, err } @@ -140,13 +142,14 @@ func (o *matrixSelector) GetPool() *model.VectorPool { } func (o *matrixSelector) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + select { case <-ctx.Done(): return nil, ctx.Err() default: } - start := time.Now() - defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() if o.currentStep > o.maxt { return nil, nil @@ -175,11 +178,11 @@ func (o *matrixSelector) Next(ctx context.Context) ([]model.StepVector, error) { maxt := seriesTs - o.offset mint := maxt - o.selectRange - var rangeSamples []Sample + var rangeSamples []ringbuffer.Sample[Value] var err error if !o.isExtFunction { - rangeSamples, err = selectPoints(series.samples, mint, maxt, series.previousSamples) + rangeSamples, o.bufferTail, err = selectPoints(series.samples, mint, maxt, series.previousSamples, o.bufferTail) } else { rangeSamples, err = selectExtPoints(series.samples, mint, maxt, series.previousSamples, o.extLookbackDelta, &series.metricAppearedTs) } @@ -287,7 +290,11 @@ func (o *matrixSelector) loadSeries(ctx context.Context) error { // into the [mint, maxt] range are retained; only points with later timestamps // are populated from the iterator. // TODO(fpetkovski): Add max samples limit. -func selectPoints(it *storage.BufferedSeriesIterator, mint, maxt int64, out []Sample) ([]Sample, error) { +func selectPoints( + it *storage.BufferedSeriesIterator, + mint, maxt int64, + out, tail []ringbuffer.Sample[Value], +) ([]ringbuffer.Sample[Value], []ringbuffer.Sample[Value], error) { if len(out) > 0 && out[len(out)-1].T >= mint { // There is an overlap between previous and current ranges, retain common // points. In most such cases: @@ -297,7 +304,11 @@ func selectPoints(it *storage.BufferedSeriesIterator, mint, maxt int64, out []Sa var drop int for drop = 0; out[drop].T < mint; drop++ { } + // Rotate the slice around drop and reduce the length to remove samples. + tail = slices.Grow(tail, drop)[:drop] + copy(tail, out[:drop]) copy(out, out[drop:]) + copy(out[len(out)-drop:], tail) out = out[:len(out)-drop] // Only append points with timestamps after the last timestamp we have. mint = out[len(out)-1].T + 1 @@ -308,7 +319,7 @@ func selectPoints(it *storage.BufferedSeriesIterator, mint, maxt int64, out []Sa soughtValueType := it.Seek(maxt) if soughtValueType == chunkenc.ValNone { if it.Err() != nil { - return nil, it.Err() + return nil, tail, it.Err() } } @@ -319,18 +330,19 @@ loop: case chunkenc.ValNone: break loop case chunkenc.ValHistogram, chunkenc.ValFloatHistogram: - t, fh := buf.AtFloatHistogram() - if value.IsStaleNaN(fh.Sum) { - continue loop - } + t := buf.AtT() if t >= mint { n := len(out) if cap(out) > n { out = out[:len(out)+1] } else { - out = append(out, Sample{}) + out = append(out, ringbuffer.Sample[Value]{}) + } + out[n].T, out[n].V.H = buf.AtFloatHistogram(out[n].V.H) + if value.IsStaleNaN(out[n].V.H.Sum) { + out = out[:n] + continue loop } - out[n].T, out[n].H = t, fh } case chunkenc.ValFloat: t, v := buf.At() @@ -343,9 +355,9 @@ loop: if cap(out) > n { out = out[:len(out)+1] } else { - out = append(out, Sample{}) + out = append(out, ringbuffer.Sample[Value]{}) } - out[n].T, out[n].F, out[n].H = t, v, nil + out[n].T, out[n].V.F, out[n].V.H = t, v, nil } } } @@ -353,15 +365,19 @@ loop: // The sought sample might also be in the range. switch soughtValueType { case chunkenc.ValHistogram, chunkenc.ValFloatHistogram: - t, fh := it.AtFloatHistogram() - if t == maxt && !value.IsStaleNaN(fh.Sum) { + t := it.AtT() + if t != maxt { + break + } + _, fh := it.AtFloatHistogram() + if !value.IsStaleNaN(fh.Sum) { n := len(out) if cap(out) > n { out = out[:len(out)+1] } else { - out = append(out, Sample{}) + out = append(out, ringbuffer.Sample[Value]{}) } - out[n].T, out[n].H = t, fh + out[n].T, out[n].V.H = t, fh.Copy() } case chunkenc.ValFloat: t, v := it.At() @@ -370,13 +386,13 @@ loop: if cap(out) > n { out = out[:len(out)+1] } else { - out = append(out, Sample{}) + out = append(out, ringbuffer.Sample[Value]{}) } - out[n].T, out[n].F, out[n].H = t, v, nil + out[n].T, out[n].V.F, out[n].V.H = t, v, nil } } - return out, nil + return out, tail, nil } // matrixIterSlice populates a matrix vector covering the requested range for a @@ -388,7 +404,7 @@ loop: // into the [mint, maxt] range are retained; only points with later timestamps // are populated from the iterator. // TODO(fpetkovski): Add max samples limit. -func selectExtPoints(it *storage.BufferedSeriesIterator, mint, maxt int64, out []Sample, extLookbackDelta int64, metricAppearedTs **int64) ([]Sample, error) { +func selectExtPoints(it *storage.BufferedSeriesIterator, mint, maxt int64, out []ringbuffer.Sample[Value], extLookbackDelta int64, metricAppearedTs **int64) ([]ringbuffer.Sample[Value], error) { extMint := mint - extLookbackDelta selectsNativeHistograms := false @@ -435,15 +451,22 @@ loop: break loop case chunkenc.ValHistogram, chunkenc.ValFloatHistogram: selectsNativeHistograms = true - t, fh := buf.AtFloatHistogram() - if value.IsStaleNaN(fh.Sum) { - continue loop - } - if *metricAppearedTs == nil { - *metricAppearedTs = &t - } + t := buf.AtT() if t >= mint { - out = append(out, Sample{T: t, H: fh}) + n := len(out) + if cap(out) > n { + out = out[:len(out)+1] + } else { + out = append(out, ringbuffer.Sample[Value]{}) + } + out[n].T, out[n].V.H = buf.AtFloatHistogram(out[n].V.H) + + if value.IsStaleNaN(out[n].V.H.Sum) { + continue loop + } + if *metricAppearedTs == nil { + *metricAppearedTs = &t + } } case chunkenc.ValFloat: t, v := buf.At() @@ -458,10 +481,10 @@ loop: // exists at or before range start, add it and then keep replacing // it with later points while not yet (strictly) inside the range. if t >= mint || !appendedPointBeforeMint { - out = append(out, Sample{T: t, F: v}) + out = append(out, ringbuffer.Sample[Value]{T: t, V: Value{F: v}}) appendedPointBeforeMint = true } else { - out[len(out)-1] = Sample{T: t, F: v} + out[len(out)-1] = ringbuffer.Sample[Value]{T: t, V: Value{F: v}} } } @@ -476,7 +499,7 @@ loop: if *metricAppearedTs == nil { *metricAppearedTs = &t } - out = append(out, Sample{T: t, H: fh}) + out = append(out, ringbuffer.Sample[Value]{T: t, V: Value{H: fh}}) } case chunkenc.ValFloat: t, v := it.At() @@ -484,7 +507,7 @@ loop: if *metricAppearedTs == nil { *metricAppearedTs = &t } - out = append(out, Sample{T: t, F: v}) + out = append(out, ringbuffer.Sample[Value]{T: t, V: Value{F: v}}) } } diff --git a/vendor/github.com/thanos-io/promql-engine/execution/scan/subquery.go b/vendor/github.com/thanos-io/promql-engine/execution/scan/subquery.go index 598ebf0b7a0..df9057d585b 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/scan/subquery.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/scan/subquery.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "sync" + "time" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/promql/parser" @@ -14,23 +15,30 @@ import ( "github.com/thanos-io/promql-engine/execution/model" "github.com/thanos-io/promql-engine/extlabels" "github.com/thanos-io/promql-engine/query" + "github.com/thanos-io/promql-engine/ringbuffer" ) -// TODO: only instant subqueries for now. type subqueryOperator struct { + model.OperatorTelemetry + next model.VectorOperator pool *model.VectorPool call FunctionCall mint int64 maxt int64 currentStep int64 + step int64 + stepsBatch int funcExpr *parser.Call subQuery *parser.SubqueryExpr onceSeries sync.Once series []labels.Labels - acc [][]Sample + + lastVectors []model.StepVector + lastCollected int + buffers []*ringbuffer.RingBuffer[Value] } func NewSubqueryOperator(pool *model.VectorPool, next model.VectorOperator, opts *query.Options, funcExpr *parser.Call, subQuery *parser.SubqueryExpr) (model.VectorOperator, error) { @@ -38,25 +46,37 @@ func NewSubqueryOperator(pool *model.VectorPool, next model.VectorOperator, opts if err != nil { return nil, err } + step := opts.Step.Milliseconds() + if step == 0 { + step = 1 + } return &subqueryOperator{ - next: next, - call: call, - pool: pool, - funcExpr: funcExpr, - subQuery: subQuery, - mint: opts.Start.UnixMilli(), - maxt: opts.End.UnixMilli(), - currentStep: opts.Start.UnixMilli(), + OperatorTelemetry: model.NewTelemetry("[subquery]", opts.EnableAnalysis), + + next: next, + call: call, + pool: pool, + funcExpr: funcExpr, + subQuery: subQuery, + mint: opts.Start.UnixMilli(), + maxt: opts.End.UnixMilli(), + currentStep: opts.Start.UnixMilli(), + step: step, + stepsBatch: opts.StepsBatch, + lastCollected: -1, }, nil } func (o *subqueryOperator) Explain() (me string, next []model.VectorOperator) { - return fmt.Sprintf("[*subqueryOperator] %v()", o.funcExpr.Func.Name), []model.VectorOperator{o.next} + return fmt.Sprintf("[subquery] %v()", o.funcExpr.Func.Name), []model.VectorOperator{o.next} } func (o *subqueryOperator) GetPool() *model.VectorPool { return o.pool } func (o *subqueryOperator) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { o.OperatorTelemetry.AddExecutionTimeTaken(time.Since(start)) }() + select { case <-ctx.Done(): return nil, ctx.Err() @@ -69,51 +89,96 @@ func (o *subqueryOperator) Next(ctx context.Context) ([]model.StepVector, error) return nil, err } -ACC: - for { - vectors, err := o.next.Next(ctx) - if err != nil { - return nil, err + res := o.pool.GetVectorBatch() + for i := 0; o.currentStep <= o.maxt && i < o.stepsBatch; i++ { + mint := o.currentStep - o.subQuery.Range.Milliseconds() - o.subQuery.OriginalOffset.Milliseconds() + maxt := o.currentStep - o.subQuery.OriginalOffset.Milliseconds() + for _, b := range o.buffers { + b.DropBefore(mint) } - if len(vectors) == 0 { - break ACC + if len(o.lastVectors) > 0 { + for _, v := range o.lastVectors[o.lastCollected+1:] { + if v.T > maxt { + break + } + o.collect(v, mint) + o.lastCollected++ + } + if o.lastCollected == len(o.lastVectors)-1 { + o.next.GetPool().PutVectors(o.lastVectors) + o.lastVectors = nil + o.lastCollected = -1 + } } - for _, vector := range vectors { - for j, s := range vector.Samples { - o.acc[vector.SampleIDs[j]] = append(o.acc[vector.SampleIDs[j]], Sample{T: vector.T, F: s}) + + ACC: + for len(o.lastVectors) == 0 { + vectors, err := o.next.Next(ctx) + if err != nil { + return nil, err } - for j, s := range vector.Histograms { - o.acc[vector.HistogramIDs[j]] = append(o.acc[vector.HistogramIDs[j]], Sample{T: vector.T, H: s}) + if len(vectors) == 0 { + break ACC } - o.next.GetPool().PutStepVector(vector) + for j, vector := range vectors { + if vector.T > maxt { + o.lastVectors = vectors + o.lastCollected = j - 1 + break ACC + } + o.collect(vector, mint) + } + o.next.GetPool().PutVectors(vectors) } - o.next.GetPool().PutVectors(vectors) - } - res := o.pool.GetVectorBatch() - sv := o.pool.GetStepVector(o.currentStep) - for sampleId, rangeSamples := range o.acc { - f, h, ok := o.call(FunctionArgs{ - Samples: rangeSamples, - StepTime: o.currentStep, - SelectRange: o.subQuery.Range.Milliseconds(), - Offset: o.subQuery.Offset.Milliseconds(), - }) - if ok { - if h != nil { - sv.AppendHistogram(o.pool, uint64(sampleId), h) - } else { - sv.AppendSample(o.pool, uint64(sampleId), f) + sv := o.pool.GetStepVector(o.currentStep) + for sampleId, rangeSamples := range o.buffers { + f, h, ok := o.call(FunctionArgs{ + Samples: rangeSamples.Samples(), + StepTime: maxt, + SelectRange: o.subQuery.Range.Milliseconds(), + }) + if ok { + if h != nil { + sv.AppendHistogram(o.pool, uint64(sampleId), h) + } else { + sv.AppendSample(o.pool, uint64(sampleId), f) + } } } - } - res = append(res, sv) + res = append(res, sv) - o.currentStep++ + o.currentStep += o.step + } return res, nil } +func (o *subqueryOperator) collect(v model.StepVector, mint int64) { + if v.T < mint { + o.next.GetPool().PutStepVector(v) + return + } + for i, s := range v.Samples { + buffer := o.buffers[v.SampleIDs[i]] + if buffer.Len() > 0 && v.T <= buffer.MaxT() { + continue + } + buffer.Push(v.T, Value{F: s}) + } + for i, s := range v.Histograms { + buffer := o.buffers[v.HistogramIDs[i]] + if buffer.Len() > 0 && v.T < buffer.MaxT() { + continue + } + buffer.Push(v.T, Value{H: s}) + } + o.next.GetPool().PutStepVector(v) +} + func (o *subqueryOperator) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { o.OperatorTelemetry.AddExecutionTimeTaken(time.Since(start)) }() + if err := o.initSeries(ctx); err != nil { return nil, err } @@ -130,7 +195,10 @@ func (o *subqueryOperator) initSeries(ctx context.Context) error { } o.series = make([]labels.Labels, len(series)) - o.acc = make([][]Sample, len(series)) + o.buffers = make([]*ringbuffer.RingBuffer[Value], len(series)) + for i := range o.buffers { + o.buffers[i] = ringbuffer.New[Value](8) + } var b labels.ScratchBuilder for i, s := range series { lbls := s @@ -139,6 +207,7 @@ func (o *subqueryOperator) initSeries(ctx context.Context) error { } o.series[i] = lbls } + o.pool.SetStepSize(len(o.series)) }) return err } diff --git a/vendor/github.com/thanos-io/promql-engine/execution/scan/vector_selector.go b/vendor/github.com/thanos-io/promql-engine/execution/scan/vector_selector.go index 8337269aedb..ee21ef32797 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/scan/vector_selector.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/scan/vector_selector.go @@ -10,7 +10,6 @@ import ( "time" "github.com/efficientgo/core/errors" - "github.com/prometheus/prometheus/tsdb/chunkenc" "github.com/thanos-io/promql-engine/execution/model" engstore "github.com/thanos-io/promql-engine/execution/storage" @@ -19,8 +18,8 @@ import ( "github.com/prometheus/prometheus/model/histogram" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/model/value" - "github.com/prometheus/prometheus/storage" + "github.com/prometheus/prometheus/tsdb/chunkenc" ) type vectorScanner struct { @@ -52,6 +51,8 @@ type vectorSelector struct { shard int numShards int + + selectTimestamp bool } // NewVectorSelector creates operator which selects vector of series. @@ -60,13 +61,16 @@ func NewVectorSelector( selector engstore.SeriesSelector, queryOpts *query.Options, offset time.Duration, + hints storage.SelectHints, batchSize int64, + selectTimestamp bool, shard, numShards int, ) model.VectorOperator { o := &vectorSelector{ - OperatorTelemetry: &model.NoopTelemetry{}, - storage: selector, - vectorPool: pool, + OperatorTelemetry: model.NewTelemetry("[vectorSelector]", queryOpts.EnableAnalysis), + + storage: selector, + vectorPool: pool, mint: queryOpts.Start.UnixMilli(), maxt: queryOpts.End.UnixMilli(), @@ -79,9 +83,8 @@ func NewVectorSelector( shard: shard, numShards: numShards, - } - if queryOpts.EnableAnalysis { - o.OperatorTelemetry = &model.TrackedTelemetry{} + + selectTimestamp: selectTimestamp, } // For instant queries, set the step to a positive value // so that the operator can terminate. @@ -92,16 +95,14 @@ func NewVectorSelector( return o } -func (o *vectorSelector) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - o.SetName("[*vectorSelector]") - return o, nil -} - func (o *vectorSelector) Explain() (me string, next []model.VectorOperator) { - return fmt.Sprintf("[*vectorSelector] {%v} %v mod %v", o.storage.Matchers(), o.shard, o.numShards), nil + return fmt.Sprintf("[vectorSelector] {%v} %v mod %v", o.storage.Matchers(), o.shard, o.numShards), nil } func (o *vectorSelector) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + if err := o.loadSeries(ctx); err != nil { return nil, err } @@ -113,6 +114,9 @@ func (o *vectorSelector) GetPool() *model.VectorPool { } func (o *vectorSelector) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() + select { case <-ctx.Done(): return nil, ctx.Err() @@ -122,8 +126,6 @@ func (o *vectorSelector) Next(ctx context.Context) ([]model.StepVector, error) { return nil, nil } - start := time.Now() - defer func() { o.AddExecutionTimeTaken(time.Since(start)) }() if err := o.loadSeries(ctx); err != nil { return nil, err } @@ -144,12 +146,15 @@ func (o *vectorSelector) Next(ctx context.Context) ([]model.StepVector, error) { seriesTs = ts ) for currStep := 0; currStep < o.numSteps && seriesTs <= o.maxt; currStep++ { - _, v, h, ok, err := selectPoint(series.samples, seriesTs, o.lookbackDelta, o.offset) + t, v, h, ok, err := selectPoint(series.samples, seriesTs, o.lookbackDelta, o.offset) if err != nil { return nil, err } + if o.selectTimestamp { + v = float64(t) / 1000 + } if ok { - if h != nil { + if h != nil && !o.selectTimestamp { vectors[currStep].AppendHistogram(o.vectorPool, series.signature, h) } else { vectors[currStep].AppendSample(o.vectorPool, series.signature, v) @@ -162,7 +167,6 @@ func (o *vectorSelector) Next(ctx context.Context) ([]model.StepVector, error) { o.currentStep += o.step * int64(o.numSteps) o.currentSeries = 0 } - return vectors, nil } @@ -175,6 +179,7 @@ func (o *vectorSelector) loadSeries(ctx context.Context) error { return } + b := labels.NewBuilder(labels.EmptyLabels()) o.scanners = make([]vectorScanner, len(series)) o.series = make([]labels.Labels, len(series)) for i, s := range series { @@ -183,7 +188,13 @@ func (o *vectorSelector) loadSeries(ctx context.Context) error { signature: s.Signature, samples: storage.NewMemoizedIterator(s.Iterator(nil), o.lookbackDelta), } - o.series[i] = s.Labels() + b.Reset(s.Labels()) + // if we have pushed down a timestamp function into the scan we need to drop + // the __name__ label + if o.selectTimestamp { + b.Del(labels.MetricName) + } + o.series[i] = b.Labels() } numSeries := int64(len(o.series)) diff --git a/vendor/github.com/thanos-io/promql-engine/execution/step_invariant/step_invariant.go b/vendor/github.com/thanos-io/promql-engine/execution/step_invariant/step_invariant.go index 5654b40778f..e4126148b86 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/step_invariant/step_invariant.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/step_invariant/step_invariant.go @@ -10,10 +10,10 @@ import ( "github.com/efficientgo/core/errors" "github.com/prometheus/prometheus/model/labels" - "github.com/prometheus/prometheus/promql/parser" "github.com/thanos-io/promql-engine/execution/model" + "github.com/thanos-io/promql-engine/logicalplan" "github.com/thanos-io/promql-engine/query" ) @@ -35,17 +35,8 @@ type stepInvariantOperator struct { model.OperatorTelemetry } -func (u *stepInvariantOperator) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - u.SetName("[*stepInvariantOperator]") - next := make([]model.ObservableVectorOperator, 0, 1) - if obsnext, ok := u.next.(model.ObservableVectorOperator); ok { - next = append(next, obsnext) - } - return u, next -} - func (u *stepInvariantOperator) Explain() (me string, next []model.VectorOperator) { - return "[*stepInvariantOperator]", []model.VectorOperator{u.next} + return "[stepInvariant]", []model.VectorOperator{u.next} } func NewStepInvariantOperator( @@ -54,36 +45,36 @@ func NewStepInvariantOperator( expr parser.Expr, opts *query.Options, ) (model.VectorOperator, error) { - interval := opts.Step.Milliseconds() // We set interval to be at least 1. - if interval == 0 { - interval = 1 - } u := &stepInvariantOperator{ + OperatorTelemetry: model.NewTelemetry("[stepInvariant]", opts.EnableAnalysis), + vectorPool: pool, next: next, currentStep: opts.Start.UnixMilli(), mint: opts.Start.UnixMilli(), maxt: opts.End.UnixMilli(), - step: interval, + step: opts.Step.Milliseconds(), stepsBatch: opts.StepsBatch, cacheResult: true, } + if u.step == 0 { + u.step = 1 + } // We do not duplicate results for range selectors since result is a matrix // with their unique timestamps which does not depend on the step. switch expr.(type) { - case *parser.MatrixSelector, *parser.SubqueryExpr: + case *logicalplan.MatrixSelector, *parser.SubqueryExpr: u.cacheResult = false } - u.OperatorTelemetry = &model.NoopTelemetry{} - if opts.EnableAnalysis { - u.OperatorTelemetry = &model.TrackedTelemetry{} - } return u, nil } func (u *stepInvariantOperator) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { u.AddExecutionTimeTaken(time.Since(start)) }() + var err error u.seriesOnce.Do(func() { u.series, err = u.next.Series(ctx) @@ -100,10 +91,8 @@ func (u *stepInvariantOperator) GetPool() *model.VectorPool { } func (u *stepInvariantOperator) Next(ctx context.Context) ([]model.StepVector, error) { - if u.currentStep > u.maxt { - return nil, nil - } start := time.Now() + defer func() { u.AddExecutionTimeTaken(time.Since(start)) }() select { case <-ctx.Done(): @@ -111,6 +100,10 @@ func (u *stepInvariantOperator) Next(ctx context.Context) ([]model.StepVector, e default: } + if u.currentStep > u.maxt { + return nil, nil + } + if !u.cacheResult { return u.next.Next(ctx) } @@ -127,7 +120,6 @@ func (u *stepInvariantOperator) Next(ctx context.Context) ([]model.StepVector, e result = append(result, outVector) u.currentStep += u.step } - u.AddExecutionTimeTaken(time.Since(start)) return result, nil } diff --git a/vendor/github.com/thanos-io/promql-engine/execution/unary/unary.go b/vendor/github.com/thanos-io/promql-engine/execution/unary/unary.go index e1a0a18aa2e..4200752a9e0 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/unary/unary.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/unary/unary.go @@ -12,6 +12,7 @@ import ( "gonum.org/v1/gonum/floats" "github.com/thanos-io/promql-engine/execution/model" + "github.com/thanos-io/promql-engine/query" ) type unaryNegation struct { @@ -22,31 +23,23 @@ type unaryNegation struct { model.OperatorTelemetry } -func (u *unaryNegation) Explain() (me string, next []model.VectorOperator) { - return "[*unaryNegation]", []model.VectorOperator{u.next} -} - -func NewUnaryNegation( - next model.VectorOperator, - stepsBatch int, -) (model.VectorOperator, error) { +func NewUnaryNegation(next model.VectorOperator, opts *query.Options) (model.VectorOperator, error) { u := &unaryNegation{ next: next, - OperatorTelemetry: &model.TrackedTelemetry{}, + OperatorTelemetry: model.NewTelemetry("[unaryNegation]", opts.EnableAnalysis), } return u, nil } -func (u *unaryNegation) Analyze() (model.OperatorTelemetry, []model.ObservableVectorOperator) { - u.SetName("[*unaryNegation]") - next := make([]model.ObservableVectorOperator, 0, 1) - if obsnext, ok := u.next.(model.ObservableVectorOperator); ok { - next = append(next, obsnext) - } - return u, next + +func (u *unaryNegation) Explain() (me string, next []model.VectorOperator) { + return "[unaryNegation]", []model.VectorOperator{u.next} } func (u *unaryNegation) Series(ctx context.Context) ([]labels.Labels, error) { + start := time.Now() + defer func() { u.AddExecutionTimeTaken(time.Since(start)) }() + if err := u.loadSeries(ctx); err != nil { return nil, err } @@ -75,12 +68,15 @@ func (u *unaryNegation) GetPool() *model.VectorPool { } func (u *unaryNegation) Next(ctx context.Context) ([]model.StepVector, error) { + start := time.Now() + defer func() { u.AddExecutionTimeTaken(time.Since(start)) }() + select { case <-ctx.Done(): return nil, ctx.Err() default: } - start := time.Now() + in, err := u.next.Next(ctx) if err != nil { return nil, err @@ -91,6 +87,5 @@ func (u *unaryNegation) Next(ctx context.Context) ([]model.StepVector, error) { for i := range in { floats.Scale(-1, in[i].Samples) } - u.AddExecutionTimeTaken(time.Since(start)) return in, nil } diff --git a/vendor/github.com/thanos-io/promql-engine/execution/warnings/context.go b/vendor/github.com/thanos-io/promql-engine/execution/warnings/context.go index 772009de4b3..2472adab5ed 100644 --- a/vendor/github.com/thanos-io/promql-engine/execution/warnings/context.go +++ b/vendor/github.com/thanos-io/promql-engine/execution/warnings/context.go @@ -1,3 +1,6 @@ +// Copyright (c) The Thanos Community Authors. +// Licensed under the Apache License 2.0. + package warnings import ( diff --git a/vendor/github.com/thanos-io/promql-engine/logicalplan/distribute.go b/vendor/github.com/thanos-io/promql-engine/logicalplan/distribute.go index 2732864cf67..ff46d898a8e 100644 --- a/vendor/github.com/thanos-io/promql-engine/logicalplan/distribute.go +++ b/vendor/github.com/thanos-io/promql-engine/logicalplan/distribute.go @@ -175,6 +175,12 @@ func (m DistributedExecutionOptimizer) Optimize(plan parser.Expr, opts *query.Op return plan, annotations.New().Add(RewrittenExternalLabelWarning) } + // TODO(fpetkovski): Consider changing TraverseBottomUp to pass in a list of parents in the transform function. + parents := make(map[*parser.Expr]*parser.Expr) + TraverseBottomUp(nil, &plan, func(parent, current *parser.Expr) (stop bool) { + parents[current] = parent + return false + }) TraverseBottomUp(nil, &plan, func(parent, current *parser.Expr) (stop bool) { // If the current operation is not distributive, stop the traversal. if !isDistributive(current, m.SkipBinaryPushdown) { @@ -190,7 +196,7 @@ func (m DistributedExecutionOptimizer) Optimize(plan parser.Expr, opts *query.Op } remoteAggregation := newRemoteAggregation(aggr, engines) - subQueries := m.distributeQuery(&remoteAggregation, engines, opts, minEngineOverlap) + subQueries := m.distributeQuery(&remoteAggregation, engines, m.subqueryOpts(parents, current, opts), minEngineOverlap) *current = &parser.AggregateExpr{ Op: localAggregation, Expr: subQueries, @@ -202,7 +208,7 @@ func (m DistributedExecutionOptimizer) Optimize(plan parser.Expr, opts *query.Op return true } if isAbsent(*current) { - *current = m.distributeAbsent(*current, engines, calculateStartOffset(current, opts.LookbackDelta), opts) + *current = m.distributeAbsent(*current, engines, calculateStartOffset(current, opts.LookbackDelta), m.subqueryOpts(parents, current, opts)) return true } @@ -211,13 +217,26 @@ func (m DistributedExecutionOptimizer) Optimize(plan parser.Expr, opts *query.Op return false } - *current = m.distributeQuery(current, engines, opts, minEngineOverlap) + *current = m.distributeQuery(current, engines, m.subqueryOpts(parents, current, opts), minEngineOverlap) return true }) return plan, nil } +func (m DistributedExecutionOptimizer) subqueryOpts(parents map[*parser.Expr]*parser.Expr, current *parser.Expr, opts *query.Options) *query.Options { + subqueryParents := make([]*parser.SubqueryExpr, 0, len(parents)) + for p := parents[current]; p != nil; p = parents[p] { + if subquery, ok := (*p).(*parser.SubqueryExpr); ok { + subqueryParents = append(subqueryParents, subquery) + } + } + for i := len(subqueryParents) - 1; i >= 0; i-- { + opts = query.NestedOptionsForSubquery(opts, subqueryParents[i]) + } + return opts +} + func newRemoteAggregation(rootAggregation *parser.AggregateExpr, engines []api.RemoteEngine) parser.Expr { groupingSet := make(map[string]struct{}) for _, lbl := range rootAggregation.Grouping { @@ -410,11 +429,13 @@ func calculateStartOffset(expr *parser.Expr, lookbackDelta time.Duration) time.D var selectRange time.Duration var offset time.Duration traverse(expr, func(node *parser.Expr) { - if matrixSelector, ok := (*node).(*parser.MatrixSelector); ok { - selectRange = matrixSelector.Range - } - if vectorSelector, ok := (*node).(*parser.VectorSelector); ok { - offset = vectorSelector.Offset + switch n := (*node).(type) { + case *parser.SubqueryExpr: + selectRange += n.Range + case *MatrixSelector: + selectRange += n.Range + case *VectorSelector: + offset = n.Offset } }) return maxDuration(offset+selectRange, lookbackDelta) @@ -467,7 +488,7 @@ func matchesExternalLabelSet(expr parser.Expr, externalLabelSet []labels.Labels) } var selectorSet [][]*labels.Matcher traverse(&expr, func(current *parser.Expr) { - vs, ok := (*current).(*parser.VectorSelector) + vs, ok := (*current).(*VectorSelector) if ok { selectorSet = append(selectorSet, vs.LabelMatchers) } diff --git a/vendor/github.com/thanos-io/promql-engine/logicalplan/distribute_avg.go b/vendor/github.com/thanos-io/promql-engine/logicalplan/distribute_avg.go index 2257a79d63b..a98f5284ff8 100644 --- a/vendor/github.com/thanos-io/promql-engine/logicalplan/distribute_avg.go +++ b/vendor/github.com/thanos-io/promql-engine/logicalplan/distribute_avg.go @@ -1,3 +1,6 @@ +// Copyright (c) The Thanos Community Authors. +// Licensed under the Apache License 2.0. + package logicalplan import ( diff --git a/vendor/github.com/thanos-io/promql-engine/logicalplan/filter.go b/vendor/github.com/thanos-io/promql-engine/logicalplan/filter.go deleted file mode 100644 index 7d13d8123a4..00000000000 --- a/vendor/github.com/thanos-io/promql-engine/logicalplan/filter.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) The Thanos Community Authors. -// Licensed under the Apache License 2.0. - -package logicalplan - -import ( - "fmt" - - "github.com/prometheus/prometheus/model/labels" - - "github.com/prometheus/prometheus/promql/parser" - "github.com/prometheus/prometheus/promql/parser/posrange" -) - -// VectorSelector is vector selector with additional configuration set by optimizers. -// TODO(fpetkovski): Consider replacing all VectorSelector nodes with this one as the first step in the plan. -// This should help us avoid dealing with both types in the entire codebase. -type VectorSelector struct { - *parser.VectorSelector - Filters []*labels.Matcher - BatchSize int64 -} - -func (f VectorSelector) String() string { - if f.BatchSize != 0 && len(f.Filters) != 0 { - return fmt.Sprintf("filter(%s, %s[batch=%d])", f.Filters, f.VectorSelector.String(), f.BatchSize) - } - if f.BatchSize != 0 { - return fmt.Sprintf("%s[batch=%d]", f.VectorSelector.String(), f.BatchSize) - } - return fmt.Sprintf("filter(%s, %s)", f.Filters, f.VectorSelector.String()) -} - -func (f VectorSelector) Pretty(level int) string { return f.String() } - -func (f VectorSelector) PositionRange() posrange.PositionRange { return posrange.PositionRange{} } - -func (f VectorSelector) Type() parser.ValueType { return parser.ValueTypeVector } - -func (f VectorSelector) PromQLExpr() {} diff --git a/vendor/github.com/thanos-io/promql-engine/logicalplan/merge_selects.go b/vendor/github.com/thanos-io/promql-engine/logicalplan/merge_selects.go index 39db81ea9b0..8865a50d35e 100644 --- a/vendor/github.com/thanos-io/promql-engine/logicalplan/merge_selects.go +++ b/vendor/github.com/thanos-io/promql-engine/logicalplan/merge_selects.go @@ -33,7 +33,7 @@ func (m MergeSelectsOptimizer) Optimize(plan parser.Expr, _ *query.Options) (par func extractSelectors(selectors matcherHeap, expr parser.Expr) { traverse(&expr, func(node *parser.Expr) { - e, ok := (*node).(*parser.VectorSelector) + e, ok := (*node).(*VectorSelector) if !ok { return } @@ -49,8 +49,6 @@ func replaceMatchers(selectors matcherHeap, expr *parser.Expr) { traverse(expr, func(node *parser.Expr) { var matchers []*labels.Matcher switch e := (*node).(type) { - case *parser.VectorSelector: - matchers = e.LabelMatchers case *VectorSelector: matchers = e.LabelMatchers default: @@ -85,12 +83,6 @@ func replaceMatchers(selectors matcherHeap, expr *parser.Expr) { } switch e := (*node).(type) { - case *parser.VectorSelector: - e.LabelMatchers = replacement - *node = &VectorSelector{ - VectorSelector: e, - Filters: filters, - } case *VectorSelector: e.LabelMatchers = replacement e.Filters = filters diff --git a/vendor/github.com/thanos-io/promql-engine/logicalplan/passthrough.go b/vendor/github.com/thanos-io/promql-engine/logicalplan/passthrough.go index 9827d5df268..1729b2b6652 100644 --- a/vendor/github.com/thanos-io/promql-engine/logicalplan/passthrough.go +++ b/vendor/github.com/thanos-io/promql-engine/logicalplan/passthrough.go @@ -63,7 +63,7 @@ func (m PassthroughOptimizer) Optimize(plan parser.Expr, opts *query.Options) (p matchingLabelsEngines := make([]api.RemoteEngine, 0, len(engines)) TraverseBottomUp(nil, &plan, func(parent, current *parser.Expr) (stop bool) { - if vs, ok := (*current).(*parser.VectorSelector); ok { + if vs, ok := (*current).(*VectorSelector); ok { for _, e := range engines { if !labelSetsMatch(vs.LabelMatchers, e.LabelSets()...) { continue diff --git a/vendor/github.com/thanos-io/promql-engine/logicalplan/plan.go b/vendor/github.com/thanos-io/promql-engine/logicalplan/plan.go index 0b72eadd726..d853b9e596d 100644 --- a/vendor/github.com/thanos-io/promql-engine/logicalplan/plan.go +++ b/vendor/github.com/thanos-io/promql-engine/logicalplan/plan.go @@ -6,14 +6,15 @@ package logicalplan import ( "fmt" "math" + "strings" "time" - "github.com/efficientgo/core/errors" + "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/model/timestamp" "github.com/prometheus/prometheus/promql" - "github.com/prometheus/prometheus/util/annotations" - "github.com/prometheus/prometheus/promql/parser" + "github.com/prometheus/prometheus/promql/parser/posrange" + "github.com/prometheus/prometheus/util/annotations" "github.com/thanos-io/promql-engine/query" ) @@ -50,6 +51,9 @@ func New(expr parser.Expr, opts *query.Options) Plan { // the engine handles sorting at the presentation layer expr = trimSorts(expr) + // replace scanners by our logical nodes + expr = replaceSelectors(expr) + return &plan{ expr: expr, opts: opts, @@ -63,8 +67,10 @@ func (p *plan) Optimize(optimizers []Optimizer) (Plan, annotations.Annotations) p.expr, a = o.Optimize(p.expr, p.opts) annos.Merge(a) } + // parens are just annoying and getting rid of them doesnt change the query + expr := trimParens(p.expr) - return &plan{expr: p.expr, opts: p.opts}, *annos + return &plan{expr: expr, opts: p.opts}, *annos } func (p *plan) Expr() parser.Expr { @@ -81,13 +87,19 @@ func traverse(expr *parser.Expr, transform func(*parser.Expr)) { var x parser.Expr = node.VectorSelector transform(expr) traverse(&x, transform) + case *MatrixSelector: + var x parser.Expr = node.MatrixSelector + transform(expr) + traverse(&x, transform) case *parser.MatrixSelector: transform(expr) traverse(&node.VectorSelector, transform) case *parser.AggregateExpr: transform(expr) + traverse(&node.Param, transform) traverse(&node.Expr, transform) case *parser.Call: + transform(expr) for i := range node.Args { traverse(&(node.Args[i]), transform) } @@ -96,10 +108,13 @@ func traverse(expr *parser.Expr, transform func(*parser.Expr)) { traverse(&node.LHS, transform) traverse(&node.RHS, transform) case *parser.UnaryExpr: + transform(expr) traverse(&node.Expr, transform) case *parser.ParenExpr: + transform(expr) traverse(&node.Expr, transform) case *parser.SubqueryExpr: + transform(expr) traverse(&node.Expr, transform) } } @@ -120,6 +135,12 @@ func TraverseBottomUp(parent *parser.Expr, current *parser.Expr, transform func( } var x parser.Expr = node.VectorSelector return TraverseBottomUp(current, &x, transform) + case *MatrixSelector: + if stop := transform(parent, current); stop { + return stop + } + var x parser.Expr = node.MatrixSelector + return TraverseBottomUp(current, &x, transform) case *parser.MatrixSelector: return transform(current, &node.VectorSelector) case *parser.AggregateExpr: @@ -149,13 +170,66 @@ func TraverseBottomUp(parent *parser.Expr, current *parser.Expr, transform func( } return transform(parent, current) case *parser.SubqueryExpr: - return TraverseBottomUp(current, &node.Expr, transform) + if stop := TraverseBottomUp(current, &node.Expr, transform); stop { + return stop + } + return transform(parent, current) } - return true } +func replaceSelectors(plan parser.Expr) parser.Expr { + traverse(&plan, func(current *parser.Expr) { + switch t := (*current).(type) { + case *parser.MatrixSelector: + *current = &MatrixSelector{MatrixSelector: t, OriginalString: t.String()} + case *parser.VectorSelector: + *current = &VectorSelector{VectorSelector: t} + case *parser.Call: + if t.Func.Name != "timestamp" { + return + } + switch v := unwrapParens(t.Args[0]).(type) { + case *parser.VectorSelector: + *current = &VectorSelector{VectorSelector: v, SelectTimestamp: true} + case *parser.StepInvariantExpr: + vs, ok := unwrapParens(v.Expr).(*parser.VectorSelector) + if ok { + // Prometheus weirdness + if vs.Timestamp != nil { + vs.OriginalOffset = 0 + } + *current = &VectorSelector{VectorSelector: vs, SelectTimestamp: true} + } + } + } + }) + return plan +} + func trimSorts(expr parser.Expr) parser.Expr { + canTrimSorts := true + // We cannot trim inner sort if its an argument to a timestamp function. + // If we would do it we could transform "timestamp(sort(X))" into "timestamp(X)" + // Which might return actual timestamps of samples instead of query execution timestamp. + TraverseBottomUp(nil, &expr, func(parent, current *parser.Expr) bool { + if current == nil || parent == nil { + return true + } + e, pok := (*parent).(*parser.Call) + f, cok := (*current).(*parser.Call) + + if pok && cok { + if e.Func.Name == "timestamp" && strings.HasPrefix(f.Func.Name, "sort") { + canTrimSorts = false + return true + } + } + return false + }) + if !canTrimSorts { + return expr + } TraverseBottomUp(nil, &expr, func(parent, current *parser.Expr) bool { if current == nil || parent == nil { return true @@ -172,6 +246,20 @@ func trimSorts(expr parser.Expr) parser.Expr { return expr } +func trimParens(expr parser.Expr) parser.Expr { + TraverseBottomUp(nil, &expr, func(parent, current *parser.Expr) bool { + if current == nil || parent == nil { + return true + } + switch (*parent).(type) { + case *parser.ParenExpr: + *parent = *current + } + return false + }) + return expr +} + // preprocessExpr wraps all possible step invariant parts of the given expression with // StepInvariantExpr. It also resolves the preprocessors. // Copied from Prometheus and adjusted to work with the vendored parser: @@ -284,6 +372,15 @@ func newStepInvariantExpr(expr parser.Expr) parser.Expr { return &parser.StepInvariantExpr{Expr: expr} } +func unwrapParens(expr parser.Expr) parser.Expr { + switch t := expr.(type) { + case *parser.ParenExpr: + return unwrapParens(t.Expr) + default: + return t + } +} + // Copy from https://github.com/prometheus/prometheus/blob/v2.39.1/promql/engine.go#L2658. func setOffsetForAtModifier(evalTime int64, expr parser.Expr) { getOffset := func(ts *int64, originalOffset time.Duration, path []parser.Node) time.Duration { @@ -347,14 +444,60 @@ func subqueryTimes(path []parser.Node) (time.Duration, time.Duration, *int64) { } func setOffsetForInnerSubqueries(expr parser.Expr, opts *query.Options) { - parser.Inspect(expr, func(node parser.Node, path []parser.Node) error { - switch n := node.(type) { - case *parser.SubqueryExpr: - nOpts := query.NestedOptionsForSubquery(opts, n) - setOffsetForAtModifier(nOpts.Start.UnixMilli(), n.Expr) - setOffsetForInnerSubqueries(n.Expr, nOpts) - return errors.New("stop iteration") + switch n := expr.(type) { + case *parser.SubqueryExpr: + nOpts := query.NestedOptionsForSubquery(opts, n) + setOffsetForAtModifier(nOpts.Start.UnixMilli(), n.Expr) + setOffsetForInnerSubqueries(n.Expr, nOpts) + default: + for _, c := range parser.Children(n) { + setOffsetForInnerSubqueries(c.(parser.Expr), opts) } - return nil - }) + } +} + +// VectorSelector is vector selector with additional configuration set by optimizers. +type VectorSelector struct { + *parser.VectorSelector + Filters []*labels.Matcher + BatchSize int64 + SelectTimestamp bool +} + +func (f VectorSelector) String() string { + if f.SelectTimestamp { + // If we pushed down timestamp into the vector selector we need to render the proper + // PromQL again. + return fmt.Sprintf("timestamp(%s)", f.VectorSelector.String()) + } + return f.VectorSelector.String() } + +func (f VectorSelector) Pretty(level int) string { return f.String() } + +func (f VectorSelector) PositionRange() posrange.PositionRange { return posrange.PositionRange{} } + +func (f VectorSelector) Type() parser.ValueType { return parser.ValueTypeVector } + +func (f VectorSelector) PromQLExpr() {} + +// MatrixSelector is matrix selector with additional configuration set by optimizers. +// It is used so we can get rid of VectorSelector in distributed mode too. +type MatrixSelector struct { + *parser.MatrixSelector + + // Needed because this operator is used in the distributed mode + OriginalString string +} + +func (f MatrixSelector) String() string { + return f.OriginalString +} + +func (f MatrixSelector) Pretty(level int) string { return f.String() } + +func (f MatrixSelector) PositionRange() posrange.PositionRange { return posrange.PositionRange{} } + +func (f MatrixSelector) Type() parser.ValueType { return parser.ValueTypeVector } + +func (f MatrixSelector) PromQLExpr() {} diff --git a/vendor/github.com/thanos-io/promql-engine/logicalplan/propagate_selectors.go b/vendor/github.com/thanos-io/promql-engine/logicalplan/propagate_selectors.go index 4509dbe1180..1aa400dee4b 100644 --- a/vendor/github.com/thanos-io/promql-engine/logicalplan/propagate_selectors.go +++ b/vendor/github.com/thanos-io/promql-engine/logicalplan/propagate_selectors.go @@ -47,11 +47,11 @@ func (m PropagateMatchersOptimizer) Optimize(plan parser.Expr, _ *query.Options) } func propagateMatchers(binOp *parser.BinaryExpr) { - lhSelector, ok := binOp.LHS.(*parser.VectorSelector) + lhSelector, ok := binOp.LHS.(*VectorSelector) if !ok { return } - rhSelector, ok := binOp.RHS.(*parser.VectorSelector) + rhSelector, ok := binOp.RHS.(*VectorSelector) if !ok { return } @@ -106,7 +106,7 @@ func makeUnion(lhMatchers map[string]*labels.Matcher, rhMatchers map[string]*lab return union, false } -func toMatcherMap(lhSelector *parser.VectorSelector) map[string]*labels.Matcher { +func toMatcherMap(lhSelector *VectorSelector) map[string]*labels.Matcher { lhMatchers := make(map[string]*labels.Matcher) for _, m := range lhSelector.LabelMatchers { lhMatchers[m.Name] = m diff --git a/vendor/github.com/thanos-io/promql-engine/logicalplan/set_batch_size.go b/vendor/github.com/thanos-io/promql-engine/logicalplan/set_batch_size.go index 63c8ef27ba7..06103963832 100644 --- a/vendor/github.com/thanos-io/promql-engine/logicalplan/set_batch_size.go +++ b/vendor/github.com/thanos-io/promql-engine/logicalplan/set_batch_size.go @@ -25,7 +25,10 @@ func (m SelectorBatchSize) Optimize(plan parser.Expr, _ *query.Options) (parser. traverse(&plan, func(current *parser.Expr) { switch e := (*current).(type) { case *parser.Call: - canBatch = e.Func.Name == "histogram_quantile" + //TODO: calls can reduce the labelset of the input; think histogram_quantile reducing + // multiple "le" labels into one output. We cannot handle this in batching. Revisit + // what is safe here. + canBatch = false case *parser.BinaryExpr: canBatch = false case *parser.AggregateExpr: @@ -39,11 +42,6 @@ func (m SelectorBatchSize) Optimize(plan parser.Expr, _ *query.Options) (parser. e.BatchSize = m.Size } canBatch = false - case *parser.VectorSelector: - if canBatch { - *current = &VectorSelector{VectorSelector: e, BatchSize: m.Size} - } - canBatch = false } }) return plan, nil diff --git a/vendor/github.com/thanos-io/promql-engine/logicalplan/sort_matchers.go b/vendor/github.com/thanos-io/promql-engine/logicalplan/sort_matchers.go index 8484a18d58d..bdaa1c584ff 100644 --- a/vendor/github.com/thanos-io/promql-engine/logicalplan/sort_matchers.go +++ b/vendor/github.com/thanos-io/promql-engine/logicalplan/sort_matchers.go @@ -19,7 +19,7 @@ type SortMatchers struct{} func (m SortMatchers) Optimize(plan parser.Expr, _ *query.Options) (parser.Expr, annotations.Annotations) { traverse(&plan, func(node *parser.Expr) { - e, ok := (*node).(*parser.VectorSelector) + e, ok := (*node).(*VectorSelector) if !ok { return } diff --git a/vendor/github.com/thanos-io/promql-engine/logicalplan/user_defined.go b/vendor/github.com/thanos-io/promql-engine/logicalplan/user_defined.go index 5fb8c3cad85..d33441dc227 100644 --- a/vendor/github.com/thanos-io/promql-engine/logicalplan/user_defined.go +++ b/vendor/github.com/thanos-io/promql-engine/logicalplan/user_defined.go @@ -1,3 +1,6 @@ +// Copyright (c) The Thanos Community Authors. +// Licensed under the Apache License 2.0. + package logicalplan import ( diff --git a/vendor/github.com/thanos-io/promql-engine/query/options.go b/vendor/github.com/thanos-io/promql-engine/query/options.go index 50ebce1f31b..3381c054d03 100644 --- a/vendor/github.com/thanos-io/promql-engine/query/options.go +++ b/vendor/github.com/thanos-io/promql-engine/query/options.go @@ -19,7 +19,6 @@ type Options struct { LookbackDelta time.Duration ExtLookbackDelta time.Duration NoStepSubqueryIntervalFn func(time.Duration) time.Duration - EnableSubqueries bool EnableAnalysis bool } @@ -54,7 +53,7 @@ func NestedOptionsForSubquery(opts *Options, t *parser.SubqueryExpr) *Options { StepsBatch: opts.StepsBatch, ExtLookbackDelta: opts.ExtLookbackDelta, NoStepSubqueryIntervalFn: opts.NoStepSubqueryIntervalFn, - EnableSubqueries: opts.EnableSubqueries, + EnableAnalysis: opts.EnableAnalysis, } if t.Step != 0 { nOpts.Step = t.Step diff --git a/vendor/github.com/thanos-io/promql-engine/ringbuffer/ringbuffer.go b/vendor/github.com/thanos-io/promql-engine/ringbuffer/ringbuffer.go new file mode 100644 index 00000000000..337af66980e --- /dev/null +++ b/vendor/github.com/thanos-io/promql-engine/ringbuffer/ringbuffer.go @@ -0,0 +1,76 @@ +// Copyright (c) The Thanos Community Authors. +// Licensed under the Apache License 2.0. + +package ringbuffer + +type Sample[T any] struct { + T int64 + V T +} + +type RingBuffer[T any] struct { + items []Sample[T] + tail []Sample[T] +} + +func New[T any](size int) *RingBuffer[T] { + return &RingBuffer[T]{ + items: make([]Sample[T], 0, size), + } +} + +func (r *RingBuffer[T]) Len() int { + return len(r.items) +} + +func (r *RingBuffer[T]) MaxT() int64 { + return r.items[len(r.items)-1].T +} + +func (r *RingBuffer[T]) ReadIntoNext(f func(*Sample[T])) { + n := len(r.items) + if cap(r.items) > len(r.items) { + r.items = r.items[:n+1] + } else { + r.items = append(r.items, Sample[T]{}) + } + f(&r.items[n]) +} + +func (r *RingBuffer[T]) Push(t int64, v T) { + if n := len(r.items); n < cap(r.items) { + r.items = r.items[:n+1] + r.items[n].T = t + r.items[n].V = v + } else { + r.items = append(r.items, Sample[T]{T: t, V: v}) + } +} + +func (r *RingBuffer[T]) DropBefore(ts int64) { + if len(r.items) == 0 || r.items[len(r.items)-1].T < ts { + r.items = r.items[:0] + return + } + var drop int + for drop = 0; drop < len(r.items) && r.items[drop].T < ts; drop++ { + } + keep := len(r.items) - drop + + r.tail = resize(r.tail, drop) + copy(r.tail, r.items[:drop]) + copy(r.items, r.items[drop:]) + copy(r.items[keep:], r.tail) + r.items = r.items[:keep] +} + +func (r *RingBuffer[T]) Samples() []Sample[T] { + return r.items +} + +func resize[T any](s []Sample[T], n int) []Sample[T] { + if cap(s) >= n { + return s[:n] + } + return make([]Sample[T], n) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 58a72252a18..627f4a43dda 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -896,7 +896,7 @@ github.com/thanos-io/objstore/providers/gcs github.com/thanos-io/objstore/providers/s3 github.com/thanos-io/objstore/providers/swift github.com/thanos-io/objstore/tracing/opentracing -# github.com/thanos-io/promql-engine v0.0.0-20231214130043-41b2cf818e81 +# github.com/thanos-io/promql-engine v0.0.0-20240114143142-008379eda2f9 ## explicit; go 1.20 github.com/thanos-io/promql-engine/api github.com/thanos-io/promql-engine/engine @@ -917,6 +917,7 @@ github.com/thanos-io/promql-engine/execution/warnings github.com/thanos-io/promql-engine/extlabels github.com/thanos-io/promql-engine/logicalplan github.com/thanos-io/promql-engine/query +github.com/thanos-io/promql-engine/ringbuffer # github.com/thanos-io/thanos v0.33.1-0.20231224215600-665e64370a2c ## explicit; go 1.21 github.com/thanos-io/thanos/pkg/block