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

Loki: Display error with label filter conflicts #63109

Merged
merged 22 commits into from
Feb 22, 2023

Conversation

gwdawson
Copy link
Member

@gwdawson gwdawson commented Feb 8, 2023

What is this feature?

  • Part 1: display a visual error/warning if two or more labels are conflicting.
  • Part 2: display a visual error/warning if two or more label filter operations are conflicting

Why do we need this feature?
...

Who is this feature for?
...

Which issue(s) does this PR fix?:
Fixes #59548

Special notes for your reviewer
test queries should be constructed in the builder mode.

Testing these conflict errors
construct the following query in the builder mode.
{job="tns/app", job!="tns/app"} | level = 'error' | level != 'error'

Screenshot 2023-02-20 at 16 29 43

@github-actions
Copy link
Contributor

github-actions bot commented Feb 8, 2023

Backend code coverage report for PR #63109
No changes

@github-actions
Copy link
Contributor

github-actions bot commented Feb 8, 2023

Frontend code coverage report for PR #63109

Plugin Main PR Difference
loki 83.49% 83.51% .02%

Copy link
Member

@ivanahuckova ivanahuckova left a comment

Choose a reason for hiding this comment

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

@gwdawson Could you also do {job="tns/app", job!="tns/app"}?

@gwdawson gwdawson requested review from ivanahuckova and removed request for ivanahuckova February 9, 2023 17:28
Copy link
Member

@ivanahuckova ivanahuckova left a comment

Choose a reason for hiding this comment

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

Exciting improvement! 🎉 🎈

We already have a UI pattern for showings errors related to label selectors. Could we use this pattern also in this case and just show which label is conflicting and highlight only that one with error message.
image

Also left couple of other feedback.

@gwdawson gwdawson requested a review from matyax February 21, 2023 09:44
Copy link
Contributor

@matyax matyax left a comment

Choose a reason for hiding this comment

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

Looks great! This is a partial review, will share some more feedback shortly.

Copy link
Contributor

@matyax matyax left a comment

Choose a reason for hiding this comment

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

There are some suggestions for your consideration. If you don't plan to implement them, I'd appreciate if you can share the reason with me.

Comment on lines 160 to 185
export function isConflictingFilter(
operation: QueryBuilderOperation,
queryOperations: QueryBuilderOperation[]
): boolean {
let conflictingFilter = false;

const labelFilters = queryOperations.filter((op) => {
return op.id === LokiOperationId.LabelFilter && op.params !== operation.params;
});

// check if the new filter conflicts with any other label filter
labelFilters.forEach((filter) => {
if (operation.params[0] !== filter.params[0] || operation.params[2] !== filter.params[2]) {
return;
}

if (
(String(operation.params[1]).startsWith('!') && !String(filter.params[1]).startsWith('!')) ||
(String(filter.params[1]).startsWith('!') && !String(operation.params[1]).startsWith('!'))
) {
conflictingFilter = true;
}
});

return conflictingFilter;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

An alternative approach to consider.

Suggested change
export function isConflictingFilter(
operation: QueryBuilderOperation,
queryOperations: QueryBuilderOperation[]
): boolean {
let conflictingFilter = false;
const labelFilters = queryOperations.filter((op) => {
return op.id === LokiOperationId.LabelFilter && op.params !== operation.params;
});
// check if the new filter conflicts with any other label filter
labelFilters.forEach((filter) => {
if (operation.params[0] !== filter.params[0] || operation.params[2] !== filter.params[2]) {
return;
}
if (
(String(operation.params[1]).startsWith('!') && !String(filter.params[1]).startsWith('!')) ||
(String(filter.params[1]).startsWith('!') && !String(operation.params[1]).startsWith('!'))
) {
conflictingFilter = true;
}
});
return conflictingFilter;
}
export function isConflictingFilter(
operation: QueryBuilderOperation,
queryOperations: QueryBuilderOperation[]
): boolean {
const operationIsNegative = operation.params[1].toString().startsWith('!');
// Discard non label filters and different params
const candidates = queryOperations.filter(queryOperation => (queryOperation.id === LokiOperationId.LabelFilter && queryOperation.params[0] === operation.params[0] && queryOperation.params[2] === operation.params[2]));
const conflict = candidates.find((candidate) => {
if (operationIsNegative && candidate.params[1].toString().startsWith('!') === false) {
return true;
}
if (operationIsNegative === false && candidate.params[1].toString().startsWith('!')) {
return true;
}
return false;
});
return conflict !== undefined;
}

A limitation of the current implementation is that it will continue iterating over all the label filters, even if one has already been found in line 180. I would suggest to change the forEach to a find or findIndex.

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess some would also be possible.

Copy link
Contributor

Choose a reason for hiding this comment

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

Definitely. And it looks like it's potentially faster https://www.measurethat.net/Benchmarks/Show/4337/0/array-find-vs-some

Copy link
Member Author

Choose a reason for hiding this comment

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

@matyax cool website, based off the fact some is faster, ill update to use that.

@matyax
Copy link
Contributor

matyax commented Feb 21, 2023

It took longer than I would like to admit to understand why this wasn't being validated.

opposing filters

@gwdawson gwdawson requested a review from matyax February 21, 2023 16:49
Copy link
Contributor

@matyax matyax left a comment

Choose a reason for hiding this comment

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

👏 👏 great work! Really appreciate that you were on board with the latest iterations.

@gwdawson gwdawson merged commit c5ed939 into main Feb 22, 2023
@gwdawson gwdawson deleted the gareth/conflicting-operations branch February 22, 2023 09:56
ryantxu pushed a commit that referenced this pull request Mar 2, 2023
* feat: add logic to check for conflicting operator

* feat: display error if there is a conflict

* feat: check if label filter conflicts with other filters

* fix: remove capitalisation from "no data"

* test: add tests for isConflictingFilter function

* feat(labels): add validation for label matchers

* test(labels): add tests for isConflictingSelector

* refactor(labels): add suggestions from code review

* test(labels): add case for labels without op

* refactor(operations): add suggestions from code review

* feat(operations): display tooltip when you have conflicts

* fix: remove unused props from test

* fix: remove old test

* fix(labels): error message now displays in the correct location

* fix(operations): operations can be dragged, even with error

* fix: remove unused vars

* fix: failing tests

* fix(operations): hide error message whilst dragging

* refactor: use more appropriate variable naming

* refactor: add suggestions from code review

* perf: use array.some instead of array.find
@gwdawson gwdawson restored the gareth/conflicting-operations branch July 19, 2023 11:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Loki: If we have identical label filters that oppose each other, notify user
5 participants