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

matching: introduce consistent hashing matcher #14875

Merged
merged 19 commits into from
Mar 8, 2021

Conversation

snowp
Copy link
Contributor

@snowp snowp commented Jan 30, 2021

This introduces a new matcher that allows matching on an input value by
computing a hash value and matching if the value % (configured value) is
greater than a configured threshold. This is useful in being able to
define match criteria that should match for a certain % of input values
in a way that is consistent between independent Envoy instances (e.g. it
does not rely on a random input).

Risk Level: Low, new extension
Testing: UTs
Docs Changes: Inline proto docs
Release Notes: n/a
Platform Specific Features: n/a
Fixes #14782

Snow Pettersen added 5 commits January 29, 2021 20:37
This introduces a new matcher that allows matching on an input value by
computing a hash value and matching if the value % (configured value) is
greater than a configured  threshold. This is useful in being able to
define match criteria that should match for a certain % of input values
in a way that is consistent between independent Envoy instances (e.g. it
does not rely on a random input).

Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
@repokitteh-read-only
Copy link

CC @envoyproxy/api-shepherds: Your approval is needed for changes made to api/envoy/.
API shepherd assignee is @htuch
CC @envoyproxy/api-watchers: FYI only for changes made to api/envoy/.

🐱

Caused by: #14875 was opened by snowp.

see: more, trace.

@snowp
Copy link
Contributor Author

snowp commented Jan 30, 2021

@donyu

@snowp
Copy link
Contributor Author

snowp commented Jan 30, 2021

Will split this into a PR for the changes to the core matching logic (since I want to make similar changes to the other factories) and leave this as just the extension change, hence the draft

Copy link
Member

@htuch htuch left a comment

Choose a reason for hiding this comment

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

Looks like a useful addition. How come this requirement never come up in RouteConfiguration matchers?

message ConsistentHashing {
// The threshold the resulting hash must be over in order for this matcher to evaluate to true.
// This value must be below the configured modulo value.
uint32 threshold = 1 [(validate.rules).uint32 = {gt: 0}];
Copy link
Member

Choose a reason for hiding this comment

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

Could this be zero to mean 100%?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah good point, will remove the validation

}

// Otherwise, match if (hash(input) % modulo) > threshold.
return HashUtil::xxHash64(*input) % modulo_ > threshold_;
Copy link
Member

Choose a reason for hiding this comment

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

One interesting implication of the consistent hasher API guarantee that the results are fleet-wide consistent is that if we ever change xxHash64, or xxhash itself internally makes some inconsistent breaking change, the property will be violated during rollouts. FWIW we have this problem today with our affinity load balancers. Not sure how you prefer to handle it (maybe warn in API documentation is enough?) but worth considering.

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'll add a warning to the docs, not sure what else we could do here besides implementing our own hash that we promise we'll never change (or never update xxhash?).

Copy link
Member

Choose a reason for hiding this comment

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

Can we model 100% match here given the >? Should it be >=?

TEST(MatcherTest, EmptyValue) {
Matcher matcher(10, 100);

ASSERT_FALSE(matcher.match(absl::nullopt));
Copy link
Member

Choose a reason for hiding this comment

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

Nit: these can all be EXPECT.

@@ -162,3 +162,5 @@ extensions/filters/http/oauth2 @rgs1 @derekargueta @snowp
/*/extensions/filters/http/kill_request @qqustc @htuch
# Rate limit expression descriptor
/*/extensions/rate_limit_descriptors/expr @kyessenov @lizan
# hash input matcher
/*/extensions/matching/input_matchers/consistent_hashing @snowp @donyu
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

@htuch htuch added the waiting label Feb 7, 2021
Snow Pettersen added 3 commits February 17, 2021 19:57
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Snow Pettersen added 3 commits February 17, 2021 21:52
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
@sschepens
Copy link
Contributor

Looks like a useful addition. How come this requirement never come up in RouteConfiguration matchers?

@htuch We're actually in need of this kind of matching in RouteConfiguration and are using Lua to get it done.

@sschepens
Copy link
Contributor

@snowp @htuch do you think it's possible to also add a configurable "seed", this is something we also need, because without a seed a given value would consistently match across every given service using a rule.

We're using this kind of hashing for "sticky" deployments based on a unique device identificator header, what happens without a seed is when a value of the header becomes covered by the threshold, then that user would simultaneously be enabled on all sticky deployments on the platform.

Signed-off-by: Snow Pettersen <snowp@lyft.com>
@snowp
Copy link
Contributor Author

snowp commented Feb 18, 2021

@sschepens Sure, I was going to add that in later as we'll need this as well. Would simply passing through a uint64_t to xxhash be sufficient for your use case?

@snowp snowp marked this pull request as ready for review February 18, 2021 16:22
Signed-off-by: Snow Pettersen <snowp@lyft.com>
@sschepens
Copy link
Contributor

@snowp yep, i think that would be alright, any string seed could be converted by management servers to a uint64

@sschepens
Copy link
Contributor

@snowp another question, will this be usable from RouteConfiguration or is this part of something else?

Signed-off-by: Snow Pettersen <snowp@lyft.com>
Copy link
Member

@htuch htuch 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. Just one question.

}

// Otherwise, match if (hash(input) % modulo) > threshold.
return HashUtil::xxHash64(*input) % modulo_ > threshold_;
Copy link
Member

Choose a reason for hiding this comment

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

Can we model 100% match here given the >? Should it be >=?

auto message = Config::Utility::translateAnyToFactoryConfig(
config.typed_config(), ProtobufMessage::getStrictValidationVisitor(), factory);
auto matcher = factory.createInputMatcher(*message, context);
ASSERT_NE(nullptr, matcher);
Copy link
Member

Choose a reason for hiding this comment

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

Nit: EXPECT for a final assertion (even if fatal).

Snow Pettersen added 4 commits February 19, 2021 17:00
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
Signed-off-by: Snow Pettersen <snowp@lyft.com>
@snowp
Copy link
Contributor Author

snowp commented Mar 2, 2021

/retest

@repokitteh-read-only
Copy link

Retrying Azure Pipelines:
Retried failed jobs in: envoy-presubmit

🐱

Caused by: a #14875 (comment) was created by @snowp.

see: more, trace.

@snowp
Copy link
Contributor Author

snowp commented Mar 2, 2021

@htuch ptal

Copy link
Member

@htuch htuch left a comment

Choose a reason for hiding this comment

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

LGTM modulo nits.

return false;
}

// Otherwise, match if (hash(input) % modulo) > threshold.
Copy link
Member

Choose a reason for hiding this comment

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

Nit: >=


// The consistent hashing matchers computes a consistent hash from the input and matches if the resulting hash
// is within the configured threshold.
// More specifically, this matcher evaluates to true if hash(input) % modulo > threshold.
Copy link
Member

Choose a reason for hiding this comment

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

Nit: >=

Signed-off-by: Snow Pettersen <snowp@lyft.com>
@snowp
Copy link
Contributor Author

snowp commented Mar 8, 2021

@htuch ptal

Copy link
Member

@htuch htuch left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

@htuch
Copy link
Member

htuch commented Mar 8, 2021

/lgtm api

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.

Add consistent hashing InputMatcher
4 participants