-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Clean up Matcher::MatchResult's unnecessary templates #39673
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
Conversation
Signed-off-by: Raven Black <ravenblack@dropbox.com>
Signed-off-by: Raven Black <ravenblack@dropbox.com>
Signed-off-by: Raven Black <ravenblack@dropbox.com>
Signed-off-by: Raven Black <ravenblack@dropbox.com>
Signed-off-by: Raven Black <ravenblack@dropbox.com>
Signed-off-by: Raven Black <ravenblack@dropbox.com>
|
/retest |
keithmattix
left a comment
There was a problem hiding this comment.
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 = |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's fair
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). |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
| return {MatchState::MatchComplete, absl::nullopt}; | ||
| return MatchResult::noMatch(); | ||
| } | ||
| if (on_match->matcher_) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.)
Signed-off-by: Raven Black <ravenblack@dropbox.com>
wbpcode
left a comment
There was a problem hiding this comment.
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...
| 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()(); } |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
| 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(); } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. :)
| 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_); } |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.)
There was a problem hiding this comment.
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.
Signed-off-by: Raven Black <ravenblack@dropbox.com>
wbpcode
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks.
|
/retest |
|
/retest |
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 correspondingmatch()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