Skip to content
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

Send query plan to querier. #11246

Merged
merged 31 commits into from
Nov 22, 2023
Merged

Conversation

jeschkies
Copy link
Contributor

@jeschkies jeschkies commented Nov 16, 2023

What this PR does / why we need it:
Following #11123 and in order to enable #10417 the query frontend should send the serialized LogQL AST instead of the query string to the queriers. This enables the frontend to change the AST and inject expressions that are not expressible in LogQL.

Checklist

  • Reviewed the CONTRIBUTING.md guide (required)
  • Documentation added
  • Tests updated
  • CHANGELOG.md updated
    • If the change is worth mentioning in the release notes, add add-to-release-notes label
  • Changes that require user attention or interaction to upgrade are documented in docs/sources/setup/upgrade/_index.md
  • For Helm chart changes bump the Helm chart version in production/helm/loki/Chart.yaml and update production/helm/loki/CHANGELOG.md and production/helm/loki/README.md. Example PR
  • If the change is deprecating or removing a configuration option, update the deprecated-config.yaml and deleted-config.yaml files respectively in the tools/deprecated-config-checker directory. Example PR

Copy link
Contributor

Trivy scan found the following vulnerabilities:

@jeschkies jeschkies marked this pull request as ready for review November 20, 2023 15:47
@jeschkies jeschkies requested a review from a team as a code owner November 20, 2023 15:47
Copy link
Contributor

@cstyan cstyan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a few comments, some bits are still missing from my mental model for how this will work but I'll ask about that somewhere else to not clog up the PR review

Comment on lines 117 to 118
// ParamsWithExpressionOverride overrides the query expression so that the query
// string and the expression can differ. This is useful for sharding etc.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this comment be useful for query planning when plan my not match externally available logql syntax?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Done.

pkg/logql/evaluator.go Show resolved Hide resolved
@@ -73,6 +73,7 @@ func (q *QuerierAPI) RangeQueryHandler(ctx context.Context, req *queryrange.Loki
return logqlmodel.Result{}, err
}

// TODO: fun fact req should implement params. So no wrapper would be required 🤷
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this true? are you saying it should, or it does currently?

Following the chain, req is queryrangebase.Request, which is definitions.Request. That interface doesn't have a function to get the params, but I think it makes sense for it to have one. I think that's what you're suggesting 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logql.Params interface is defined as

type Params interface {
	Query() string
	Start() time.Time
	End() time.Time
	Step() time.Duration
	Interval() time.Duration
	Limit() uint32
	Direction() logproto.Direction
	Shards() []string
}

queryrange.LokiRequest is very similar but has GetEndTs instead of End. I'd not change it here but wanted to surface the difference.

Comment on lines +63 to +65
var err error
p.queryExpr, err = syntax.ParseExpr(qs)
return p, err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to keep both the string and parsed expression on this struct?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

afaict we either pass the query string to parsing functions like engine.go we call
expr, err := q.parse(ctx, q.params.Query()) in Eval or evaluator.go we call expr, err := syntax.ParseSampleExpr(q.Query()) in Sortable

Other than that we only call Query to get the query string for passing to error logging. And the Expr interface has a fmt.Stringer to support String, so I think we could store just the parsed expression and remove storing the query string.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to keep the string it might be nice to rename the function QueryString, can be confusing at first glance to differentiate between the logql turning Params into a Query that can be Exec'd when we have another function here Query.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than that we only call Query to get the query string for passing to error logging.

That's the sole reason to keep it around. The engine should not use the string other than for logging.

If we want to keep the string it might be nice to rename the function QueryString,

That's a good idea.

if q.checkBlocked(ctx, tenants) {
return nil, logqlmodel.ErrBlocked
}

switch e := expr.(type) {
switch e := q.params.GetExpression().(type) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we have the Expr interface also support a Type function? or maybe even via the query struct modify it to save the parsed Expr instead of just having Params and a parse function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could but it would be very similar to this type switch.

This is btw the benefit of the change. We don't have to parse the query.

Copy link
Contributor

@dannykopping dannykopping left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks fantastic! Very excited to see this all come together.
Marked the remaining TODOs, left one or two nits, and even a couple substantive questions!

pkg/logcli/client/file.go Outdated Show resolved Hide resolved
pkg/logql/blocker.go Show resolved Hide resolved
pkg/logql/evaluator.go Outdated Show resolved Hide resolved
pkg/logql/evaluator.go Show resolved Hide resolved
pkg/logql/test_utils.go Outdated Show resolved Hide resolved
pkg/querier/plan/plan.go Outdated Show resolved Hide resolved
pkg/querier/plan/plan.go Outdated Show resolved Hide resolved
return p.GetQuery()
}

func (p paramsLabelWrapper) GetExpression() syntax.Expr {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hhmm, does it make sense for these to even implement the interface?
Seems like we need a parent interface here, and GetExpression(), QueryString() etc can move into a sub-interface of that

Copy link
Contributor Author

@jeschkies jeschkies Nov 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. logql.Params is an odd interface. I did try to change it just to find out there a logql.QueryParams that does not really fit the need. Let's see.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I don't this is feasible in this change. stats is using the params and they must have an expression for the RecordRangeAndInstantQueryMetrics. I can do a follow up and change them so they use their own interface.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, it's a non-blocking nit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah lets clean these up in a follow up. Nice that we have a better sense of how all this should fit together as a result of this PR 👍

pkg/querier/queryrange/codec.go Outdated Show resolved Hide resolved
pkg/querier/queryrange/queryrange.proto Show resolved Hide resolved
Comment on lines -1948 to -1963
_, _, err = rvm.Parse(`{app="foo"} |= "err"`)
_, _, err = rvm.Parse(syntax.MustParseExpr(`{app="foo"} |= "err"`))
require.Error(t, err)
_, _, err = rvm.Parse(`topk(0, sum(count_over_time({app="foo"} | json | __error__="" [15m])))`)
require.Error(t, err)
// Check fixes for bug where missing or empty parameters for regexp and pattern parsers threw a panic
// Missing parameter to regexp parser
_, _, err = rvm.Parse(`topk(10,sum by(namespace)(count_over_time({application="nginx", site!="eu-west-1-dev"} |= "/artifactory/" != "api" != "binarystore" | regexp [1d])))`)
require.ErrorIs(t, err, logqlmodel.ErrParse)
// Empty parameter to regexp parser
_, _, err = rvm.Parse(`topk(10,sum by(namespace)(count_over_time({application="nginx", site!="eu-west-1-dev"} |= "/artifactory/" != "api" != "binarystore" | regexp ` + "``" + ` [1d])))`)
require.ErrorIs(t, err, logqlmodel.ErrParse)
// Empty parameter to pattern parser
_, _, err = rvm.Parse(`topk(10,sum by(namespace)(count_over_time({application="nginx", site!="eu-west-1-dev"} |= "/artifactory/" != "api" != "binarystore" | pattern ` + `""` + ` [1d])))`)
require.ErrorIs(t, err, logqlmodel.ErrParse)
// Empty parameter to json parser
_, _, err = rvm.Parse(`topk(10,sum by(namespace)(count_over_time({application="nginx", site!="eu-west-1-dev"} |= "/artifactory/" != "api" != "binarystore" | json [1d])))`)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are using GetExpressions these cases do not apply anymore. Parsing errors are caught when a request is decoded.

Copy link
Contributor

@dannykopping dannykopping left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's go!

@jeschkies jeschkies merged commit 5b97fcf into grafana:main Nov 22, 2023
7 checks passed
@jeschkies jeschkies deleted the karsten/send-query-plan branch November 22, 2023 07:29
jeschkies added a commit that referenced this pull request Nov 22, 2023
**What this PR does / why we need it**:
The recent change #11246 requires
that `LokiRequest.Plan` is always set.

**Checklist**
- [ ] Reviewed the
[`CONTRIBUTING.md`](https://github.com/grafana/loki/blob/main/CONTRIBUTING.md)
guide (**required**)
- [ ] Documentation added
- [x] Tests updated
- [ ] `CHANGELOG.md` updated
- [ ] If the change is worth mentioning in the release notes, add
`add-to-release-notes` label
- [ ] Changes that require user attention or interaction to upgrade are
documented in `docs/sources/setup/upgrade/_index.md`
- [ ] For Helm chart changes bump the Helm chart version in
`production/helm/loki/Chart.yaml` and update
`production/helm/loki/CHANGELOG.md` and
`production/helm/loki/README.md`. [Example
PR](d10549e)
- [ ] If the change is deprecating or removing a configuration option,
update the `deprecated-config.yaml` and `deleted-config.yaml` files
respectively in the `tools/deprecated-config-checker` directory.
[Example
PR](0d4416a)
rhnasc pushed a commit to inloco/loki that referenced this pull request Apr 12, 2024
**What this PR does / why we need it**:
Following grafana#11123 and in order to
enable grafana#10417 the query frontend
should send the serialized LogQL AST instead of the query string to the
queriers. This enables the frontend to change the AST and inject
expressions that are not expressible in LogQL.

**Checklist**
- [ ] Reviewed the
[`CONTRIBUTING.md`](https://github.com/grafana/loki/blob/main/CONTRIBUTING.md)
guide (**required**)
- [ ] Documentation added
- [x] Tests updated
- [ ] `CHANGELOG.md` updated
- [ ] If the change is worth mentioning in the release notes, add
`add-to-release-notes` label
- [ ] Changes that require user attention or interaction to upgrade are
documented in `docs/sources/setup/upgrade/_index.md`
- [ ] For Helm chart changes bump the Helm chart version in
`production/helm/loki/Chart.yaml` and update
`production/helm/loki/CHANGELOG.md` and
`production/helm/loki/README.md`. [Example
PR](grafana@d10549e)
- [ ] If the change is deprecating or removing a configuration option,
update the `deprecated-config.yaml` and `deleted-config.yaml` files
respectively in the `tools/deprecated-config-checker` directory.
[Example
PR](grafana@0d4416a)

---------

Signed-off-by: Callum Styan <[email protected]>
Co-authored-by: Callum Styan <[email protected]>
rhnasc pushed a commit to inloco/loki that referenced this pull request Apr 12, 2024
**What this PR does / why we need it**:
The recent change grafana#11246 requires
that `LokiRequest.Plan` is always set.

**Checklist**
- [ ] Reviewed the
[`CONTRIBUTING.md`](https://github.com/grafana/loki/blob/main/CONTRIBUTING.md)
guide (**required**)
- [ ] Documentation added
- [x] Tests updated
- [ ] `CHANGELOG.md` updated
- [ ] If the change is worth mentioning in the release notes, add
`add-to-release-notes` label
- [ ] Changes that require user attention or interaction to upgrade are
documented in `docs/sources/setup/upgrade/_index.md`
- [ ] For Helm chart changes bump the Helm chart version in
`production/helm/loki/Chart.yaml` and update
`production/helm/loki/CHANGELOG.md` and
`production/helm/loki/README.md`. [Example
PR](grafana@d10549e)
- [ ] If the change is deprecating or removing a configuration option,
update the `deprecated-config.yaml` and `deleted-config.yaml` files
respectively in the `tools/deprecated-config-checker` directory.
[Example
PR](grafana@0d4416a)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants