Skip to content

TraceQL: make comparison to nil symmetric - #4869

Merged
stoewer merged 9 commits into
grafana:mainfrom
stoewer:nil-not-symmetric
Mar 24, 2025
Merged

TraceQL: make comparison to nil symmetric#4869
stoewer merged 9 commits into
grafana:mainfrom
stoewer:nil-not-symmetric

Conversation

@stoewer

@stoewer stoewer commented Mar 18, 2025

Copy link
Copy Markdown
Contributor

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 Mar 18, 2025

@joe-elliott joe-elliott left a comment

Copy link
Copy Markdown
Collaborator

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?

Comment thread pkg/traceql/ast_execute.go Outdated
for _, s := range ss.Spans {
val, err := a.e.execute(s)
if err != nil {
if err != nil || val.Equals(&StaticNil) {

Copy link
Copy Markdown
Collaborator

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.

@stoewer stoewer Mar 20, 2025

Copy link
Copy Markdown
Contributor Author

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Collaborator

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

stoewer commented Mar 20, 2025

Copy link
Copy Markdown
Contributor Author

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}`)},

@stoewer stoewer Mar 21, 2025

Copy link
Copy Markdown
Contributor Author

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
@stoewer
stoewer deleted the nil-not-symmetric branch March 26, 2025 02:40
mattdurham pushed a commit to mattdurham/tempo that referenced this pull request Jun 18, 2026
* Add test case for symmetric .foo != nil

* Fix make .foo != nil symmetric

* Adjust test for isMatchingOperand()

* Fix false matches for {} | avg(.dne) != 0

* More test cases for symmetric .foo != nil

* More cases for symmetric Static.Equals()

* Test cases for queries with aggregates over non existing attrs

* Fix behavior of aggregates over non existing attrs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants