Skip to content

Clean up Matcher::MatchResult's unnecessary templates#39673

Merged
ravenblackx merged 9 commits into
envoyproxy:mainfrom
ravenblackx:matcher_cleanup
Jun 6, 2025
Merged

Clean up Matcher::MatchResult's unnecessary templates#39673
ravenblackx merged 9 commits into
envoyproxy:mainfrom
ravenblackx:matcher_cleanup

Conversation

@ravenblackx

@ravenblackx ravenblackx commented May 29, 2025

Copy link
Copy Markdown
Contributor

Commit Message: Clean up Matcher::MatchResult's unnecessary templates
Additional Description: Since #38726 refactored Matcher impls to recurse inside the match function, there is no longer any need for MatchResults to have the capacity to return sub-match-trees, which means they also don't need to be templates. Cleaning this up makes the code much easier to follow. This may have also caught a few locations where the internal recursion wasn't fully updated correctly (minor negative, it does make one doMatch() implementation slightly more complicated, but the corresponding match() implementation is made much simpler, so still an improvement).
Risk Level: Small, existing tests should be ensuring that behaviors were unchanged.
Testing: Covered by existing tests, which are also cleaner.
Docs Changes: n/a
Release Notes: n/a
Platform Specific Features: n/a

@repokitteh-read-only

Copy link
Copy Markdown

As a reminder, PRs marked as draft will not be automatically assigned reviewers,
or be handled by maintainer-oncall triage.

Please mark your PR as ready when you want it to be reviewed!

🐱

Caused by: #39673 was opened by ravenblackx.

see: more, trace.

Signed-off-by: Raven Black <[email protected]>
Signed-off-by: Raven Black <[email protected]>
Signed-off-by: Raven Black <[email protected]>
@ravenblackx
ravenblackx marked this pull request as ready for review May 30, 2025 15:02
Signed-off-by: Raven Black <[email protected]>
@ravenblackx
ravenblackx requested a review from kyessenov as a code owner May 30, 2025 17:23
Signed-off-by: Raven Black <[email protected]>
Signed-off-by: Raven Black <[email protected]>
@ravenblackx
ravenblackx requested a review from adisuissa as a code owner May 30, 2025 19:01
@ravenblackx

Copy link
Copy Markdown
Contributor Author

/retest

@keithmattix keithmattix left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I built a patch off of this branch and it does appear to fix the regression we saw in Istio. Thanks for this!

const auto itr = children_.find(key);
if (itr != children_.end()) {
return {MatchState::MatchComplete, itr->second};
MatchResult result =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks like this is the part that fixes the regression we saw in Istio. Could a test be added/adapted to ensure on_no_match for the different tree types works as expected?

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 was planning on making a common "matcher tree implementation test" for this purpose that all matcher tree implementations are required to pass, because I agree that it being possible to have this regression was bad. But I would rather add that as a separate PR.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah that's fair

Comment thread envoy/matcher/matcher.h Outdated
// The result of a match. There are three possible results:
// - The match could not be completed due to lack of data (InsufficientData)
// - The match was completed, no match found (NoMatch)
// - The match was completed, match found (onMatch() will return the OnMatch).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

onMatch is no longer present, please update the comment.

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.

Done.

Comment thread envoy/matcher/matcher.h
return {MatchState::MatchComplete, absl::nullopt};
return MatchResult::noMatch();
}
if (on_match->matcher_) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

on_match could use a variant! then a switch or a visit would make it easier to parse.

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.

Yes, but doesn't have to be in this PR. (And that one isn't as big of an improvement since it doesn't get rid of the template. I think it's possible to get rid of the template too, but that's a much more aggressive change.)

@wbpcode wbpcode left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM overall. This change seems reasonable for me. Only minor comments to the naming, bla, bla...

Comment thread envoy/matcher/matcher.h
Comment on lines +134 to +152
struct MatchResult {
private:
struct InsufficientData {};
struct NoMatch {};
using Result = absl::variant<ActionFactoryCb, NoMatch, InsufficientData>;
Result result_;
MatchResult(NoMatch) : result_(NoMatch{}) {}
MatchResult(InsufficientData) : result_(InsufficientData{}) {}

public:
MatchResult(ActionFactoryCb cb) : result_(std::move(cb)) {}
static MatchResult noMatch() { return MatchResult(NoMatch{}); }
static MatchResult insufficientData() { return MatchResult(InsufficientData{}); }
bool isInsufficientData() const { return absl::holds_alternative<InsufficientData>(result_); }
bool isComplete() const { return !isInsufficientData(); }
bool isNoMatch() const { return absl::holds_alternative<NoMatch>(result_); }
bool isMatch() const { return absl::holds_alternative<ActionFactoryCb>(result_); }
ActionFactoryCb actionFactory() const { return absl::get<ActionFactoryCb>(result_); }
ActionPtr action() const { return actionFactory()(); }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: we may prefer place the public member at top?

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.

Ah, I thought the structs had to be declared before they're used, but apparently not the case in a class declaration. Done.

Comment thread envoy/matcher/matcher.h
static MatchResult noMatch() { return MatchResult(NoMatch{}); }
static MatchResult insufficientData() { return MatchResult(InsufficientData{}); }
bool isInsufficientData() const { return absl::holds_alternative<InsufficientData>(result_); }
bool isComplete() const { return !isInsufficientData(); }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this isComplete is unncessary.

@ravenblackx ravenblackx Jun 5, 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.

It's unnecessary but makes it more readable at the user end (!isInsufficientData doesn't describe what the user end cares about in many cases), and also makes it more resilient for future changes - if a fourth state were to be added that also means the match wasn't resolved yet, every location that wrote !isInsufficientData() to mean this would need to update, or if a fourth state was added that also means the match was resolved then every location that wrote isMatch() || isNoMatch() for the same meaning would need to be updated. Having a function for the specific thing that the user cares about means any change can be localized here, and also makes it unambiguous which thing you should use (removing the question "should I use isMatch() || isNoMatch() or should it be !isInsufficientData()?").

Also !isInsufficientData() being a double negative makes it horrible to read. :)

Comment thread envoy/matcher/matcher.h
bool isInsufficientData() const { return absl::holds_alternative<InsufficientData>(result_); }
bool isComplete() const { return !isInsufficientData(); }
bool isNoMatch() const { return absl::holds_alternative<NoMatch>(result_); }
bool isMatch() const { return absl::holds_alternative<ActionFactoryCb>(result_); }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

isMatch() -> isMatched. To keep same with the FieldMatchResult

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.

They have different meanings - isMatched() means the result was "it matched", a bool, vs. isMatch() means the result was a match, with an object.

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 would be happier to change it to isAction or isActionCb than to isMatched, but I'd rather not change it at all and get it landed so the regression is fixed.)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

hmmm, I feel a little weird to these naming and method, but okay.

@wbpcode wbpcode left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. Thanks.

@ravenblackx

Copy link
Copy Markdown
Contributor Author

/retest

@ravenblackx
ravenblackx enabled auto-merge (squash) June 5, 2025 18:07
@zirain

zirain commented Jun 6, 2025

Copy link
Copy Markdown
Member

/retest

@ravenblackx
ravenblackx merged commit 2fddebb into envoyproxy:main Jun 6, 2025
@ravenblackx
ravenblackx deleted the matcher_cleanup branch June 6, 2025 14:58
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.

5 participants