-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
Split by range of instant queries #5662
Conversation
084407e
to
6449bf9
Compare
797d2b1
to
e116c31
Compare
return nil, httpgrpc.Errorf(http.StatusBadRequest, err.Error()) | ||
} | ||
|
||
interval := validation.SmallestPositiveNonZeroDurationPerTenant(tenants, s.limits.QuerySplitDuration) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The split duration is still subject to change and requires further testing. We may need to introduce a second setting, or even calculate the split duration dynamically based on the range of the query.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking really good, but it's in need of some equivalence testing (see TestMappingEquivalence
in logql/downstream_test.go
)
Operation: overrideDownstream.Operation, | ||
} | ||
} | ||
return &syntax.BinOpExpr{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clever how you've accounted for all this inside logql evaluation!
pkg/logql/rangemapper.go
Outdated
// Currently, it is sending the last inner expression grouping dowstream. | ||
// Which grouping should be sent downstream? | ||
var vectorAggrPushdown *syntax.VectorAggregationExpr | ||
if expr.Grouping != nil && expr.Grouping.Groups != nil && expr.Operation != syntax.OpTypeCount { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure we need the expr.Grouping.Groups != nil
check. Can't we also push down a sum (foo)
, which is effectively sum by () (foo)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll check.
} | ||
|
||
switch res.Data.Type() { | ||
case parser.ValueTypeMatrix: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: by having two separate middlewares, we're converting back and forth between promtheus types (Matrix, Vector, Scalar
) and queryrange.Response
types multiple times in each query (since each middleware has it's own logql engine which downstreams. This is probably fine for simplicity's sake unless we find a better way to handle this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also parse the query each time. That's probably not as bad. In any case it feels these middlewares could be refactored at some point and "just" pass down the modified query or query plan.
There are a bunch of range aggregation and vector aggregation queries tested in |
I've taken another look and found the equivalence tests -- nice work! I've also dismissed a few of my earlier comments. This is looking about ready, @cyriltovena do you want to give it another pair of 👀 ? |
@owen-d thanks for the review! I think this new feature still needs additional instrumentation, like in the shard mapper. Hope it's ok to do that in a separate PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice. I'm half way through the mapper. Need to go once more through it.
} | ||
|
||
switch res.Data.Type() { | ||
case parser.ValueTypeMatrix: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also parse the query each time. That's probably not as bad. In any case it feels these middlewares could be refactored at some point and "just" pass down the modified query or query plan.
83a67d4
to
43bdf55
Compare
|
||
// Non-splittable vector aggregators | ||
// TODO: Fix topk | ||
//{`topk(2, count_over_time({a=~".+"}[2s]))`, time.Second}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to see some successful topk.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's do it in a 2nd iteration :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM ship it 🚀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well that was a lesson in LogQL. Very cool stuff, excited for how this will improve our queries. Just a couple nits for you as that's about all I could contribute to this already impressive effort. Nice work!
pkg/logql/rangemapper.go
Outdated
} | ||
|
||
var downstreams *ConcatSampleExpr | ||
for interval = 0; interval < splitCount; interval++ { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super small nit: interval
is a bit confusing here since it's an iterator while rangeInterval
and m.splitByInterval
are both durations. Maybe call this split
, since it's an iterator across the splits in the splitCount
?
pkg/logql/rangemapper_test.go
Outdated
for _, tc := range []struct { | ||
expr string | ||
expected string | ||
expectNoop bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this test is massive. I think we could move all the noop cases to their own test for ease of finding them / organizational sake, which means we could get rid of this property in the struct. WDYT?
|
||
// clone returns a copy of the given sample expression | ||
// This is needed whenever we want to modify the existing query tree. | ||
func clone(expr syntax.SampleExpr) (syntax.SampleExpr, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not syntax.Clone(expr)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syntax.Clone()
does basically the same thing, but with different types: Expr->Expr
, whereas here I want SampleExpr->SampleExpr
. This is mainly for convenience.
Co-authored-by: Christian Haudum <christian.haudum@gmail.com>
Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
if the outer query range contains an offset as well. Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
Since this is the main function of the mapper, we can ensure here that only supported vector/range aggregations are handled. Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
…sent Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
This test is essentially the same as the test Test_SplitRangeVectorMapping, just using a different representation of the result. Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
… is present Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
…rouping is present Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
to have the test for noop queries separated Signed-off-by: Christian Haudum <christian.haudum@gmail.com>
699c354
to
8267d00
Compare
Hey @owen-d I think we are ready to merge this PR. When you squash the commits, please feel free to remove all the "intermediate" commit messages from the individual commits, and use the PR description instead. |
What this PR does / why we need it:
This PR introduces a new middleware that splits instant queries with a large range into several smaller sub-queries to parallelize their execution.
The interval by which instant queries are split is defined by the setting
split_queries_by_interval
.Note: Using a too small split interval may result in a lot of sub-queries, which can cause the query queue to fill up and make requests slower than faster.
Special notes for your reviewer:
The following screenshot shows the p99 query latency for old and new implementation, using a split interval of
1d
and various vector and range aggregations with7d
and30d
ranges respectively.Checklist
CHANGELOG.md
about the changes.