-
Notifications
You must be signed in to change notification settings - Fork 833
Query Frontend For Logical Query Plan Support #6884
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
yeya24
merged 14 commits into
cortexproject:master
from
rubywtl:frontend/middleware-logicalplan-support
Jul 24, 2025
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
923ddf6
frontend: Implement logical query plan middleware under new distribut…
rubywtl dcfccb5
Combine instant and range logical query plan gen middleware + Update …
rubywtl 16e1f87
remove unneccessary configs in query frontend + restructure logical-p…
rubywtl bd386f3
adjust prom request logicalplan type and update tests
rubywtl 55386c3
merge related tests and improve readability
rubywtl 1f2bf91
edit feature flag name and visibility
rubywtl 5770302
Add lookbackDelta and EnableStepStats back to query frontend config
rubywtl 9ab3f81
style: code cleanup and readability improvements
rubywtl 03c17ef
fix import files lint error
rubywtl d8a1239
lint: fix import orders
rubywtl bab8813
lint: fix import order
rubywtl 970ab81
remove enableStepStats from query frontend
rubywtl 6662c5e
add logical plan optimization-related config param to middleware
rubywtl 8fdebab
change params passed-in in logical plan middleware
rubywtl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package tripperware | ||
|
||
import ( | ||
"context" | ||
"net/http" | ||
"time" | ||
|
||
"github.com/prometheus/prometheus/promql/parser" | ||
"github.com/thanos-io/promql-engine/logicalplan" | ||
"github.com/thanos-io/promql-engine/query" | ||
"github.com/weaveworks/common/httpgrpc" | ||
) | ||
|
||
const ( | ||
stepBatch = 10 | ||
) | ||
|
||
func DistributedQueryMiddleware(defaultEvaluationInterval time.Duration, lookbackDelta time.Duration) Middleware { | ||
return MiddlewareFunc(func(next Handler) Handler { | ||
return distributedQueryMiddleware{ | ||
next: next, | ||
lookbackDelta: lookbackDelta, | ||
defaultEvaluationInterval: defaultEvaluationInterval, | ||
} | ||
}) | ||
} | ||
|
||
func getStartAndEnd(start time.Time, end time.Time, step time.Duration) (time.Time, time.Time) { | ||
if step == 0 { | ||
return start, start | ||
} | ||
return start, end | ||
} | ||
|
||
type distributedQueryMiddleware struct { | ||
next Handler | ||
defaultEvaluationInterval time.Duration | ||
lookbackDelta time.Duration | ||
} | ||
|
||
func (d distributedQueryMiddleware) newLogicalPlan(qs string, start time.Time, end time.Time, step time.Duration) (*logicalplan.Plan, error) { | ||
|
||
start, end = getStartAndEnd(start, end, step) | ||
|
||
qOpts := query.Options{ | ||
Start: start, | ||
End: end, | ||
Step: step, | ||
StepsBatch: stepBatch, | ||
NoStepSubqueryIntervalFn: func(duration time.Duration) time.Duration { | ||
return d.defaultEvaluationInterval | ||
}, | ||
// Hardcoded value for execution-time-params that will be re-populated again in the querier stage | ||
LookbackDelta: d.lookbackDelta, | ||
EnablePerStepStats: false, | ||
} | ||
|
||
expr, err := parser.NewParser(qs, parser.WithFunctions(parser.Functions)).ParseExpr() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
planOpts := logicalplan.PlanOptions{ | ||
DisableDuplicateLabelCheck: false, | ||
} | ||
|
||
logicalPlan := logicalplan.NewFromAST(expr, &qOpts, planOpts) | ||
optimizedPlan, _ := logicalPlan.Optimize(logicalplan.DefaultOptimizers) | ||
|
||
return &optimizedPlan, nil | ||
} | ||
|
||
func (d distributedQueryMiddleware) Do(ctx context.Context, r Request) (Response, error) { | ||
promReq, ok := r.(*PrometheusRequest) | ||
if !ok { | ||
return nil, httpgrpc.Errorf(http.StatusBadRequest, "invalid request format") | ||
} | ||
|
||
startTime := time.Unix(0, promReq.Start*int64(time.Millisecond)) | ||
endTime := time.Unix(0, promReq.End*int64(time.Millisecond)) | ||
step := time.Duration(promReq.Step) * time.Millisecond | ||
|
||
var err error | ||
|
||
newLogicalPlan, err := d.newLogicalPlan(promReq.Query, startTime, endTime, step) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
promReq.LogicalPlan = *newLogicalPlan | ||
|
||
return d.next.Do(ctx, r) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package tripperware | ||
|
||
import ( | ||
"context" | ||
"strconv" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestLogicalPlanGeneration(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
queryType string // "instant" or "range" | ||
input *PrometheusRequest | ||
err error | ||
}{ | ||
// instant query test cases | ||
{ | ||
name: "instant - rate vector selector", | ||
queryType: "instant", | ||
input: &PrometheusRequest{ | ||
Start: 100000, | ||
End: 100000, | ||
Query: "rate(node_cpu_seconds_total{mode!=\"idle\"}[5m])", | ||
}, | ||
}, | ||
{ | ||
name: "instant - memory usage expression", | ||
queryType: "instant", | ||
input: &PrometheusRequest{ | ||
Start: 100000, | ||
End: 100000, | ||
Query: "100 * (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes))", | ||
}, | ||
}, | ||
{ | ||
name: "instant - scalar only query", | ||
queryType: "instant", | ||
input: &PrometheusRequest{ | ||
Start: 100000, | ||
End: 100000, | ||
Query: "42", | ||
}, | ||
}, | ||
{ | ||
name: "instant - vector arithmetic", | ||
queryType: "instant", | ||
input: &PrometheusRequest{ | ||
Start: 100000, | ||
End: 100000, | ||
Query: "node_load1 / ignoring(cpu) node_cpu_seconds_total", | ||
}, | ||
}, | ||
{ | ||
name: "instant - avg_over_time with nested rate", | ||
queryType: "instant", | ||
input: &PrometheusRequest{ | ||
Start: 100000, | ||
End: 100000, | ||
Query: "avg_over_time(rate(http_requests_total[5m])[30m:5m])", | ||
}, | ||
}, | ||
|
||
// query range test cases | ||
{ | ||
name: "range - rate vector over time", | ||
queryType: "range", | ||
input: &PrometheusRequest{ | ||
Start: 100000, | ||
End: 200000, | ||
Step: 15000, | ||
Query: "rate(node_cpu_seconds_total{mode!=\"idle\"}[5m])", | ||
}, | ||
}, | ||
{ | ||
name: "range - memory usage ratio", | ||
queryType: "range", | ||
input: &PrometheusRequest{ | ||
Start: 100000, | ||
End: 200000, | ||
Step: 30000, | ||
Query: "100 * (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes))", | ||
}, | ||
}, | ||
{ | ||
name: "range - avg_over_time function", | ||
queryType: "range", | ||
input: &PrometheusRequest{ | ||
Start: 100000, | ||
End: 200000, | ||
Step: 60000, | ||
Query: "avg_over_time(http_requests_total[5m])", | ||
}, | ||
}, | ||
{ | ||
name: "range - vector arithmetic with range", | ||
queryType: "range", | ||
input: &PrometheusRequest{ | ||
Start: 100000, | ||
End: 200000, | ||
Step: 10000, | ||
Query: "rate(node_network_receive_bytes_total[1m]) / rate(node_network_transmit_bytes_total[1m])", | ||
}, | ||
}, | ||
{ | ||
name: "range - simple scalar operation", | ||
queryType: "range", | ||
input: &PrometheusRequest{ | ||
Start: 100000, | ||
End: 200000, | ||
Step: 15000, | ||
Query: "2 + 2", | ||
}, | ||
}, | ||
} | ||
|
||
for i, tc := range testCases { | ||
tc := tc | ||
t.Run(strconv.Itoa(i)+"_"+tc.name, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
middleware := DistributedQueryMiddleware(time.Minute, 5*time.Minute) | ||
|
||
handler := middleware.Wrap(HandlerFunc(func(_ context.Context, req Request) (Response, error) { | ||
return nil, nil | ||
})) | ||
|
||
// additional validation on the test cases based on query type | ||
if tc.queryType == "range" { | ||
require.NotZero(t, tc.input.Step, "range query should have non-zero step") | ||
require.NotEqual(t, tc.input.Start, tc.input.End, "range query should have different start and end times") | ||
} else { | ||
require.Equal(t, tc.input.Start, tc.input.End, "instant query should have equal start and end times") | ||
require.Zero(t, tc.input.Step, "instant query should have zero step") | ||
} | ||
|
||
// test: execute middleware to populate the logical plan | ||
_, err := handler.Do(context.Background(), tc.input) | ||
require.NoError(t, err) | ||
require.NotEmpty(t, tc.input.LogicalPlan, "logical plan should be populated") | ||
|
||
}) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.