diff --git a/CODEOWNERS b/CODEOWNERS index 8b71656fcb5a7..31ab629c130d4 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -164,5 +164,7 @@ 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 # user space socket pair and event /*/extensions/io_socket/user_space @lambdai @antoniovicente diff --git a/api/BUILD b/api/BUILD index 046fac3d012de..3dd1e1fbabf6d 100644 --- a/api/BUILD +++ b/api/BUILD @@ -243,6 +243,7 @@ proto_library( "//envoy/extensions/internal_redirect/allow_listed_routes/v3:pkg", "//envoy/extensions/internal_redirect/previous_routes/v3:pkg", "//envoy/extensions/internal_redirect/safe_cross_scheme/v3:pkg", + "//envoy/extensions/matching/input_matchers/consistent_hashing/v3:pkg", "//envoy/extensions/network/socket_interface/v3:pkg", "//envoy/extensions/rate_limit_descriptors/expr/v3:pkg", "//envoy/extensions/resource_monitors/fixed_heap/v3:pkg", diff --git a/api/envoy/config/common/matcher/v3/matcher.proto b/api/envoy/config/common/matcher/v3/matcher.proto index 3128d28e7a591..aa49132ce990d 100644 --- a/api/envoy/config/common/matcher/v3/matcher.proto +++ b/api/envoy/config/common/matcher/v3/matcher.proto @@ -59,6 +59,7 @@ message Matcher { type.matcher.v3.StringMatcher value_match = 2; // Extension for custom matching logic. + // [#extension-category: envoy.matching.input_matchers] core.v3.TypedExtensionConfig custom_match = 3; } } diff --git a/api/envoy/config/common/matcher/v4alpha/matcher.proto b/api/envoy/config/common/matcher/v4alpha/matcher.proto index 7c846e1e93755..586a4a920154a 100644 --- a/api/envoy/config/common/matcher/v4alpha/matcher.proto +++ b/api/envoy/config/common/matcher/v4alpha/matcher.proto @@ -75,6 +75,7 @@ message Matcher { type.matcher.v4alpha.StringMatcher value_match = 2; // Extension for custom matching logic. + // [#extension-category: envoy.matching.input_matchers] core.v4alpha.TypedExtensionConfig custom_match = 3; } } diff --git a/api/envoy/extensions/matching/input_matchers/consistent_hashing/v3/BUILD b/api/envoy/extensions/matching/input_matchers/consistent_hashing/v3/BUILD new file mode 100644 index 0000000000000..ee92fb652582e --- /dev/null +++ b/api/envoy/extensions/matching/input_matchers/consistent_hashing/v3/BUILD @@ -0,0 +1,9 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], +) diff --git a/api/envoy/extensions/matching/input_matchers/consistent_hashing/v3/consistent_hashing.proto b/api/envoy/extensions/matching/input_matchers/consistent_hashing/v3/consistent_hashing.proto new file mode 100644 index 0000000000000..acf4377f3c284 --- /dev/null +++ b/api/envoy/extensions/matching/input_matchers/consistent_hashing/v3/consistent_hashing.proto @@ -0,0 +1,39 @@ +syntax = "proto3"; + +package envoy.extensions.matching.input_matchers.consistent_hashing.v3; + +import "udpa/annotations/migrate.proto"; +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.matching.input_matchers.consistent_hashing.v3"; +option java_outer_classname = "ConsistentHashingProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Consistent Hashing Matcher] +// [#extension: envoy.matching.input_matchers.consistent_hashing] + +// 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, seed) % modulo >= threshold. +// Note that the consistency of the match result relies on the internal hash function (xxhash) remaining +// unchanged. While this is unlikely to happen intentionally, this could cause inconsistent match results +// between deployments. +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. + // Setting this to 0 is equivalent to this matcher always matching. + uint32 threshold = 1; + + // The value to use for the modulus in the calculation. This effectively bounds the hash output, + // specifying the range of possible values. + // This value must be above the configured threshold. + uint32 modulo = 2 [(validate.rules).uint32 = {gt: 0}]; + + // Optional seed passed through the hash function. This allows using additional information when computing + // the hash value: by changing the seed value, a different partition of matching and non-matching inputs will + // be created that remains consistent for that seed value. + uint64 seed = 3; +} diff --git a/api/versioning/BUILD b/api/versioning/BUILD index f5bb3ce6e8d9e..5275788512b0e 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -126,6 +126,7 @@ proto_library( "//envoy/extensions/internal_redirect/allow_listed_routes/v3:pkg", "//envoy/extensions/internal_redirect/previous_routes/v3:pkg", "//envoy/extensions/internal_redirect/safe_cross_scheme/v3:pkg", + "//envoy/extensions/matching/input_matchers/consistent_hashing/v3:pkg", "//envoy/extensions/network/socket_interface/v3:pkg", "//envoy/extensions/rate_limit_descriptors/expr/v3:pkg", "//envoy/extensions/resource_monitors/fixed_heap/v3:pkg", diff --git a/bazel/envoy_library.bzl b/bazel/envoy_library.bzl index 0131f3c132c75..aba4e50fbb584 100644 --- a/bazel/envoy_library.bzl +++ b/bazel/envoy_library.bzl @@ -82,6 +82,7 @@ EXTENSION_CATEGORIES = [ "envoy.health_checkers", "envoy.internal_redirect_predicates", "envoy.io_socket", + "envoy.matching.input_matchers", "envoy.rate_limit_descriptors", "envoy.resource_monitors", "envoy.retry_host_predicates", diff --git a/docs/root/api-v3/common_messages/common_messages.rst b/docs/root/api-v3/common_messages/common_messages.rst index 1e04318e47366..d130b1c74585d 100644 --- a/docs/root/api-v3/common_messages/common_messages.rst +++ b/docs/root/api-v3/common_messages/common_messages.rst @@ -24,3 +24,4 @@ Common messages ../extensions/common/matching/v3/extension_matcher.proto ../extensions/filters/common/dependency/v3/dependency.proto ../extensions/filters/common/matcher/action/v3/skip_action.proto + ../extensions/matching/input_matchers/consistent_hashing/v3/consistent_hashing.proto diff --git a/generated_api_shadow/BUILD b/generated_api_shadow/BUILD index e2e5b3bfeb772..3dd1e1fbabf6d 100644 --- a/generated_api_shadow/BUILD +++ b/generated_api_shadow/BUILD @@ -145,7 +145,6 @@ proto_library( "//envoy/config/resource_monitor/injected_resource/v2alpha:pkg", "//envoy/config/retry/omit_canary_hosts/v2:pkg", "//envoy/config/retry/previous_hosts/v2:pkg", - "//envoy/config/retry/previous_hosts/v3:pkg", "//envoy/config/route/v3:pkg", "//envoy/config/tap/v3:pkg", "//envoy/config/trace/v3:pkg", @@ -244,6 +243,7 @@ proto_library( "//envoy/extensions/internal_redirect/allow_listed_routes/v3:pkg", "//envoy/extensions/internal_redirect/previous_routes/v3:pkg", "//envoy/extensions/internal_redirect/safe_cross_scheme/v3:pkg", + "//envoy/extensions/matching/input_matchers/consistent_hashing/v3:pkg", "//envoy/extensions/network/socket_interface/v3:pkg", "//envoy/extensions/rate_limit_descriptors/expr/v3:pkg", "//envoy/extensions/resource_monitors/fixed_heap/v3:pkg", diff --git a/generated_api_shadow/envoy/config/common/matcher/v3/matcher.proto b/generated_api_shadow/envoy/config/common/matcher/v3/matcher.proto index 3128d28e7a591..aa49132ce990d 100644 --- a/generated_api_shadow/envoy/config/common/matcher/v3/matcher.proto +++ b/generated_api_shadow/envoy/config/common/matcher/v3/matcher.proto @@ -59,6 +59,7 @@ message Matcher { type.matcher.v3.StringMatcher value_match = 2; // Extension for custom matching logic. + // [#extension-category: envoy.matching.input_matchers] core.v3.TypedExtensionConfig custom_match = 3; } } diff --git a/generated_api_shadow/envoy/config/common/matcher/v4alpha/matcher.proto b/generated_api_shadow/envoy/config/common/matcher/v4alpha/matcher.proto index 7c846e1e93755..586a4a920154a 100644 --- a/generated_api_shadow/envoy/config/common/matcher/v4alpha/matcher.proto +++ b/generated_api_shadow/envoy/config/common/matcher/v4alpha/matcher.proto @@ -75,6 +75,7 @@ message Matcher { type.matcher.v4alpha.StringMatcher value_match = 2; // Extension for custom matching logic. + // [#extension-category: envoy.matching.input_matchers] core.v4alpha.TypedExtensionConfig custom_match = 3; } } diff --git a/generated_api_shadow/envoy/extensions/matching/input_matchers/consistent_hashing/v3/BUILD b/generated_api_shadow/envoy/extensions/matching/input_matchers/consistent_hashing/v3/BUILD new file mode 100644 index 0000000000000..ee92fb652582e --- /dev/null +++ b/generated_api_shadow/envoy/extensions/matching/input_matchers/consistent_hashing/v3/BUILD @@ -0,0 +1,9 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], +) diff --git a/generated_api_shadow/envoy/extensions/matching/input_matchers/consistent_hashing/v3/consistent_hashing.proto b/generated_api_shadow/envoy/extensions/matching/input_matchers/consistent_hashing/v3/consistent_hashing.proto new file mode 100644 index 0000000000000..acf4377f3c284 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/matching/input_matchers/consistent_hashing/v3/consistent_hashing.proto @@ -0,0 +1,39 @@ +syntax = "proto3"; + +package envoy.extensions.matching.input_matchers.consistent_hashing.v3; + +import "udpa/annotations/migrate.proto"; +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.matching.input_matchers.consistent_hashing.v3"; +option java_outer_classname = "ConsistentHashingProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Consistent Hashing Matcher] +// [#extension: envoy.matching.input_matchers.consistent_hashing] + +// 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, seed) % modulo >= threshold. +// Note that the consistency of the match result relies on the internal hash function (xxhash) remaining +// unchanged. While this is unlikely to happen intentionally, this could cause inconsistent match results +// between deployments. +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. + // Setting this to 0 is equivalent to this matcher always matching. + uint32 threshold = 1; + + // The value to use for the modulus in the calculation. This effectively bounds the hash output, + // specifying the range of possible values. + // This value must be above the configured threshold. + uint32 modulo = 2 [(validate.rules).uint32 = {gt: 0}]; + + // Optional seed passed through the hash function. This allows using additional information when computing + // the hash value: by changing the seed value, a different partition of matching and non-matching inputs will + // be created that remains consistent for that seed value. + uint64 seed = 3; +} diff --git a/include/envoy/matcher/matcher.h b/include/envoy/matcher/matcher.h index d9d4084a30156..4c65305f54163 100644 --- a/include/envoy/matcher/matcher.h +++ b/include/envoy/matcher/matcher.h @@ -155,7 +155,7 @@ class InputMatcherFactory : public Config::TypedFactory { createInputMatcher(const Protobuf::Message& config, Server::Configuration::FactoryContext& factory_context) PURE; - std::string category() const override { return "envoy.matching.input_matcher"; } + std::string category() const override { return "envoy.matching.input_matchers"; } }; // The result of retrieving data from a DataInput. As the data is generally made available diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index 007264727bf10..b9988551741a9 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -44,6 +44,12 @@ EXTENSIONS = { "envoy.health_checkers.redis": "//source/extensions/health_checkers/redis:config", + # + # Input Matchers + # + + "envoy.matching.input_matchers.consistent_hashing": "//source/extensions/matching/input_matchers/consistent_hashing:config", + # # HTTP filters # diff --git a/source/extensions/matching/input_matchers/consistent_hashing/BUILD b/source/extensions/matching/input_matchers/consistent_hashing/BUILD new file mode 100644 index 0000000000000..753f6ae6756a2 --- /dev/null +++ b/source/extensions/matching/input_matchers/consistent_hashing/BUILD @@ -0,0 +1,35 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_extension", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_library( + name = "consistent_hashing_lib", + hdrs = ["matcher.h"], + deps = [ + "//include/envoy/matcher:matcher_interface", + "//include/envoy/upstream:retry_interface", + "//source/common/common:hash_lib", + ], +) + +envoy_cc_extension( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + category = "envoy.matching.input_matchers", + security_posture = "robust_to_untrusted_downstream", + deps = [ + ":consistent_hashing_lib", + "//include/envoy/matcher:matcher_interface", + "//include/envoy/registry", + "//include/envoy/server:factory_context_interface", + "@envoy_api//envoy/extensions/matching/input_matchers/consistent_hashing/v3:pkg_cc_proto", + ], +) diff --git a/source/extensions/matching/input_matchers/consistent_hashing/config.cc b/source/extensions/matching/input_matchers/consistent_hashing/config.cc new file mode 100644 index 0000000000000..4f0580442f499 --- /dev/null +++ b/source/extensions/matching/input_matchers/consistent_hashing/config.cc @@ -0,0 +1,35 @@ +#include "extensions/matching/input_matchers/consistent_hashing/config.h" + +namespace Envoy { +namespace Extensions { +namespace Matching { +namespace InputMatchers { +namespace ConsistentHashing { + +Envoy::Matcher::InputMatcherPtr ConsistentHashingConfig::createInputMatcher( + const Protobuf::Message& config, Server::Configuration::FactoryContext& factory_context) { + const auto& consistent_hashing_config = + MessageUtil::downcastAndValidate( + config, factory_context.messageValidationVisitor()); + + if (consistent_hashing_config.threshold() > consistent_hashing_config.modulo()) { + throw EnvoyException(fmt::format("threshold cannot be greater than modulo: {} > {}", + consistent_hashing_config.threshold(), + consistent_hashing_config.modulo())); + } + + return std::make_unique(consistent_hashing_config.threshold(), + consistent_hashing_config.modulo(), + consistent_hashing_config.seed()); +} +/** + * Static registration for the consistent hashing matcher. @see RegisterFactory. + */ +REGISTER_FACTORY(ConsistentHashingConfig, Envoy::Matcher::InputMatcherFactory); + +} // namespace ConsistentHashing +} // namespace InputMatchers +} // namespace Matching +} // namespace Extensions +} // namespace Envoy \ No newline at end of file diff --git a/source/extensions/matching/input_matchers/consistent_hashing/config.h b/source/extensions/matching/input_matchers/consistent_hashing/config.h new file mode 100644 index 0000000000000..29a1eba73916b --- /dev/null +++ b/source/extensions/matching/input_matchers/consistent_hashing/config.h @@ -0,0 +1,35 @@ +#pragma once + +#include "envoy/extensions/matching/input_matchers/consistent_hashing/v3/consistent_hashing.pb.h" +#include "envoy/extensions/matching/input_matchers/consistent_hashing/v3/consistent_hashing.pb.validate.h" +#include "envoy/matcher/matcher.h" +#include "envoy/server/factory_context.h" + +#include "common/protobuf/utility.h" + +#include "extensions/matching/input_matchers/consistent_hashing/matcher.h" + +namespace Envoy { +namespace Extensions { +namespace Matching { +namespace InputMatchers { +namespace ConsistentHashing { + +class ConsistentHashingConfig : public Envoy::Matcher::InputMatcherFactory { +public: + Envoy::Matcher::InputMatcherPtr + createInputMatcher(const Protobuf::Message& config, + Server::Configuration::FactoryContext& factory_context) override; + + std::string name() const override { return "envoy.matching.matchers.consistent_hashing"; } + + ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique< + envoy::extensions::matching::input_matchers::consistent_hashing::v3::ConsistentHashing>(); + } +}; +} // namespace ConsistentHashing +} // namespace InputMatchers +} // namespace Matching +} // namespace Extensions +} // namespace Envoy \ No newline at end of file diff --git a/source/extensions/matching/input_matchers/consistent_hashing/matcher.h b/source/extensions/matching/input_matchers/consistent_hashing/matcher.h new file mode 100644 index 0000000000000..32b430b7038f9 --- /dev/null +++ b/source/extensions/matching/input_matchers/consistent_hashing/matcher.h @@ -0,0 +1,36 @@ +#pragma once + +#include "envoy/matcher/matcher.h" + +#include "common/common/hash.h" + +namespace Envoy { +namespace Extensions { +namespace Matching { +namespace InputMatchers { +namespace ConsistentHashing { + +class Matcher : public Envoy::Matcher::InputMatcher { +public: + Matcher(uint32_t threshold, uint32_t modulo, uint64_t seed) + : threshold_(threshold), modulo_(modulo), seed_(seed) {} + bool match(absl::optional input) override { + // Only match if the value is present. + if (!input) { + return false; + } + + // Otherwise, match if (hash(input) % modulo) >= threshold. + return HashUtil::xxHash64(*input, seed_) % modulo_ >= threshold_; + } + +private: + const uint32_t threshold_; + const uint32_t modulo_; + const uint64_t seed_; +}; +} // namespace ConsistentHashing +} // namespace InputMatchers +} // namespace Matching +} // namespace Extensions +} // namespace Envoy \ No newline at end of file diff --git a/test/extensions/matching/input_matchers/consistent_hashing/BUILD b/test/extensions/matching/input_matchers/consistent_hashing/BUILD new file mode 100644 index 0000000000000..17cf83c0df353 --- /dev/null +++ b/test/extensions/matching/input_matchers/consistent_hashing/BUILD @@ -0,0 +1,31 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_package", +) +load( + "//test/extensions:extensions_build_system.bzl", + "envoy_extension_cc_test", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_extension_cc_test( + name = "config_test", + srcs = ["config_test.cc"], + extension_name = "envoy.matching.input_matchers.consistent_hashing", + deps = [ + "//source/extensions/matching/input_matchers/consistent_hashing:config", + "//test/mocks/server:factory_context_mocks", + ], +) + +envoy_extension_cc_test( + name = "matcher_test", + srcs = ["matcher_test.cc"], + extension_name = "envoy.matching.input_matchers.consistent_hashing", + deps = [ + "//source/extensions/matching/input_matchers/consistent_hashing:consistent_hashing_lib", + ], +) diff --git a/test/extensions/matching/input_matchers/consistent_hashing/config_test.cc b/test/extensions/matching/input_matchers/consistent_hashing/config_test.cc new file mode 100644 index 0000000000000..10e80cd053129 --- /dev/null +++ b/test/extensions/matching/input_matchers/consistent_hashing/config_test.cc @@ -0,0 +1,58 @@ +#include "extensions/matching/input_matchers/consistent_hashing/config.h" + +#include "test/mocks/server/factory_context.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace Matching { +namespace InputMatchers { +namespace ConsistentHashing { + +TEST(ConfigTest, TestConfig) { + NiceMock context; + + const std::string yaml_string = R"EOF( + name: hashing + typed_config: + "@type": type.googleapis.com/envoy.extensions.matching.input_matchers.consistent_hashing.v3.ConsistentHashing + modulo: 100 + threshold: 10 +)EOF"; + + envoy::config::core::v3::TypedExtensionConfig config; + TestUtility::loadFromYaml(yaml_string, config); + + ConsistentHashingConfig factory; + auto message = Config::Utility::translateAnyToFactoryConfig( + config.typed_config(), ProtobufMessage::getStrictValidationVisitor(), factory); + auto matcher = factory.createInputMatcher(*message, context); + EXPECT_NE(nullptr, matcher); +} + +TEST(ConfigTest, InvalidConfig) { + NiceMock context; + + const std::string yaml_string = R"EOF( + name: hashing + typed_config: + "@type": type.googleapis.com/envoy.extensions.matching.input_matchers.consistent_hashing.v3.ConsistentHashing + modulo: 100 + threshold: 200 +)EOF"; + + envoy::config::core::v3::TypedExtensionConfig config; + TestUtility::loadFromYaml(yaml_string, config); + + ConsistentHashingConfig factory; + auto message = Config::Utility::translateAnyToFactoryConfig( + config.typed_config(), ProtobufMessage::getStrictValidationVisitor(), factory); + EXPECT_THROW_WITH_MESSAGE(factory.createInputMatcher(*message, context), EnvoyException, + "threshold cannot be greater than modulo: 200 > 100"); +} +} // namespace ConsistentHashing +} // namespace InputMatchers +} // namespace Matching +} // namespace Extensions +} // namespace Envoy \ No newline at end of file diff --git a/test/extensions/matching/input_matchers/consistent_hashing/matcher_test.cc b/test/extensions/matching/input_matchers/consistent_hashing/matcher_test.cc new file mode 100644 index 0000000000000..c070a2c3c7299 --- /dev/null +++ b/test/extensions/matching/input_matchers/consistent_hashing/matcher_test.cc @@ -0,0 +1,77 @@ +#include "extensions/matching/input_matchers/consistent_hashing/matcher.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace Matching { +namespace InputMatchers { +namespace ConsistentHashing { + +// Validates that two independent matchers agree on the +// match result for various inputs. +TEST(MatcherTest, BasicUsage) { + { + Matcher matcher1(10, 100, 0); + Matcher matcher2(10, 100, 0); + + EXPECT_FALSE(matcher1.match(absl::nullopt)); + EXPECT_FALSE(matcher2.match(absl::nullopt)); + } + { + Matcher matcher1(58, 100, 0); + Matcher matcher2(58, 100, 0); + + // The string 'hello' hashes to 2794345569481354659 + // With mod 100 this results in 59, which is greater + // than the threshold. + EXPECT_TRUE(matcher1.match("hello")); + EXPECT_TRUE(matcher2.match("hello")); + } + { + Matcher matcher1(59, 100, 0); + Matcher matcher2(59, 100, 0); + + // The string 'hello' hashes to 2794345569481354659 + // With mod 100 this results in 59, which is equal + // to the threshold. + EXPECT_TRUE(matcher1.match("hello")); + EXPECT_TRUE(matcher2.match("hello")); + } + { + Matcher matcher1(60, 100, 0); + Matcher matcher2(60, 100, 0); + + // The string 'hello' hashes to 2794345569481354659 + // With mod 100 this results in 59, which is less + // than the threshold. + EXPECT_FALSE(matcher1.match("hello")); + EXPECT_FALSE(matcher2.match("hello")); + } + { + Matcher matcher1(0, 1, 0); + Matcher matcher2(0, 1, 0); + + // The string 'hello' hashes to 2794345569481354659 + // With mod 1 this results in 0, which is equal to + // the threshold. + EXPECT_TRUE(matcher1.match("hello")); + EXPECT_TRUE(matcher2.match("hello")); + } + { + Matcher matcher1(80, 100, 0); + Matcher matcher2(80, 100, 13221); + + // The string 'hello' hashes to 2794345569481354659 with seed 0 + // and to 10451234660802341186 with seed 13221. + // This means that with seed 0 the string is below the threshold, + // while for seed 13221 the value is above the threshold. + EXPECT_FALSE(matcher1.match("hello")); + EXPECT_TRUE(matcher2.match("hello")); + } +} +} // namespace ConsistentHashing +} // namespace InputMatchers +} // namespace Matching +} // namespace Extensions +} // namespace Envoy