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

TraceQL: make comparison to nil symmetric #4869

Merged
merged 9 commits into from
Mar 24, 2025

Conversation

stoewer
Copy link
Contributor

@stoewer stoewer commented Mar 18, 2025

What this PR does:
Make comparison to nil such as { nil != .attr } symmetric.

Which issue(s) this PR fixes:
Fixes #3989

Checklist

  • Tests updated
  • Documentation added
  • CHANGELOG.md updated - the order of entries should be [CHANGE], [FEATURE], [ENHANCEMENT], [BUGFIX]

@stoewer stoewer changed the title TraceQL: make comparison to nil TraceQL: make comparison to nil symmetric Mar 18, 2025
Copy link
Member

@joe-elliott joe-elliott left a comment

Choose a reason for hiding this comment

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

changelog?

one question but this looks simple enough to approve. did it really only require a single line in isMatchingOperand?

@@ -250,7 +250,7 @@ func (a Aggregate) evaluate(input []*Spanset) (output []*Spanset, err error) {
count := 0
for _, s := range ss.Spans {
val, err := a.e.execute(s)
if err != nil {
if err != nil || val.Equals(&StaticNil) {
Copy link
Member

Choose a reason for hiding this comment

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

is this related to symmetric traceql? i'm not opposed. it makes sense to ignore nils here but i wonder if this is part of a larger change to have better nil handling across traceql.

Copy link
Contributor Author

@stoewer stoewer Mar 20, 2025

Choose a reason for hiding this comment

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

After making the nil check in isMatchingOperand() symmetric the tests for the query {} | avg(.dne) != 0} failed, because the aggregation over the non existing attribute returned results. The explanation is that parts of the logic relied on the asymmetric nil check in isMatchingOperand()

i wonder if this is part of a larger change to have better nil handling across traceql

Good point. I'll run some more tests and look out for other missing nil checks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've checked the aggregates min, max, and sum and they now all return results when they are applied to non existing attributes like this {} | sum(.dne) != 0}.

image

Since there is a test for this in searchesThatDontMatch here, I assume that the correct behavior is that all those aggregations don't return results.

On the other hand: looking at the results above the sum(.dne) column is populated with nil and technically nil != 0 is true. Therefore returning results is technically also not wrong. I would just like to confirm the correct behavior here...

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 behavior that I've now implemented for avg, sum, min, and max in this commit is as follows:

  • Spans without the attributes are skipped from the aggregate
  • Spansets without any any span with the aggregated attribute are removed from the results

Copy link
Member

Choose a reason for hiding this comment

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

@electron0zero was asking questions about nil handling awhile back and we never answered that Q. I personally like the behavior you've chosen. I think it's the most consistent with the language.

@stoewer
Copy link
Contributor Author

stoewer commented Mar 20, 2025

did it really only require a single line in isMatchingOperand?

Yes, the nil check in isMatchingOperand() was not symmetric. The other type check in BinaryOperation.execute() take both lhs and rhs into account. Therefor if one operand is nil, it always leads to a call of lhs.Equals(&rhs) which is symmetric. Since we are relying on this I'll add some more test cases to check for symmetry to TestStatic_Equals

Comment on lines +168 to +172
// Operations containing nil
{".foo != nil", traceql.MustExtractFetchSpansRequestWithMetadata(`{.foo != nil}`)},
{"nil != .foo", traceql.MustExtractFetchSpansRequestWithMetadata(`{nil != .foo}`)},
{"span.http.status_code != nil", traceql.MustExtractFetchSpansRequestWithMetadata(`{span.http.status_code != nil}`)},
{"nil != span.http.status_code", traceql.MustExtractFetchSpansRequestWithMetadata(`{nil != span.http.status_code}`)},
Copy link
Contributor Author

@stoewer stoewer Mar 21, 2025

Choose a reason for hiding this comment

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

I'll consolidate those test cases with similar test cases from #4864 once the other PR is merged

@stoewer stoewer merged commit 8fbb29a into grafana:main Mar 24, 2025
15 checks passed
@stoewer stoewer deleted the nil-not-symmetric branch March 26, 2025 02:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[TraceQL] != nil is not symmetric
2 participants