Skip to content

Commit

Permalink
Add terminal attribute to request hash and two more hash methods.
Browse files Browse the repository at this point in the history
Signed-off-by: Xin Zhuang <stevenzzz@google.com>
  • Loading branch information
stevenzzzz committed Aug 29, 2018
1 parent f952033 commit 4ef5408
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 25 deletions.
25 changes: 25 additions & 0 deletions api/envoy/api/v2/route/route.proto
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,15 @@ message RouteAction {
bool source_ip = 1;
}

message QueryParameter {
// Hash on query parameter value.
string key = 1;
}

message HostPath {
// Hash on host and path value.
}

oneof policy_specifier {
option (validate.required) = true;

Expand All @@ -584,7 +593,23 @@ message RouteAction {

// Connection properties hash policy.
ConnectionProperties connection_properties = 3;

// Query parameter hash policy.
QueryParameter query_parameter = 4;

// Host path hash policy.
HostPath host_path = 5;
}

// If true, and there is already hash computed, ignore rest of the
// list of hash polices.
// For example, if there are the following hash methods configured:
// [(specifier, terminal),...]
// [(Header A, true),
// (Cookie B, false),
// (Cookie C, false)]
// The generateHash process ends after computing "header A", as it's a terminal policy.
bool terminal = 6;
}

// Specifies a list of hash policies to use for ring hash load balancing. Each
Expand Down
90 changes: 82 additions & 8 deletions source/common/router/config_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ ShadowPolicyImpl::ShadowPolicyImpl(const envoy::api::v2::route::RouteAction& con

class HeaderHashMethod : public HashPolicyImpl::HashMethod {
public:
HeaderHashMethod(const std::string& header_name) : header_name_(header_name) {}
HeaderHashMethod(const std::string& header_name, bool terminal)
: header_name_(header_name), terminal_(terminal) {}

bool terminal() const { return terminal_; }

absl::optional<uint64_t> evaluate(const Network::Address::Instance*,
const Http::HeaderMap& headers,
Expand All @@ -94,20 +97,30 @@ class HeaderHashMethod : public HashPolicyImpl::HashMethod {

private:
const Http::LowerCaseString header_name_;
const bool terminal_;
};

class CookieHashMethod : public HashPolicyImpl::HashMethod {
public:
CookieHashMethod(const std::string& key, const std::string& path,
const absl::optional<std::chrono::seconds>& ttl)
: key_(key), path_(path), ttl_(ttl) {}
const absl::optional<std::chrono::seconds>& ttl, bool terminal)
: key_(key), path_(path), ttl_(ttl), terminal_(terminal) {}

bool terminal() const { return terminal_; }

absl::optional<uint64_t> evaluate(const Network::Address::Instance*,
const Http::HeaderMap& headers,
const HashPolicy::AddCookieCallback add_cookie) const override {
absl::optional<uint64_t> hash;
if (key_.empty()) {
// If no cookie key specified, use the whole cookie line.
const Http::HeaderEntry* cookie = headers.get(Http::Headers::get().Cookie);
if (cookie != nullptr && cookie->value().size() > 0) {
return HashUtil::xxHash64(std::string(cookie->value().c_str()));
}
return absl::nullopt;
}
std::string value = Http::Utility::parseCookieValue(headers, key_);

if (value.empty() && ttl_.has_value()) {
value = add_cookie(key_, path_, ttl_.value());
hash = HashUtil::xxHash64(value);
Expand All @@ -122,10 +135,15 @@ class CookieHashMethod : public HashPolicyImpl::HashMethod {
const std::string key_;
const std::string path_;
const absl::optional<std::chrono::seconds> ttl_;
const bool terminal_;
};

class IpHashMethod : public HashPolicyImpl::HashMethod {
public:
IpHashMethod(bool terminal) : terminal_(terminal) {}

bool terminal() const { return terminal_; }

absl::optional<uint64_t> evaluate(const Network::Address::Instance* downstream_addr,
const Http::HeaderMap&,
const HashPolicy::AddCookieCallback) const override {
Expand All @@ -142,6 +160,49 @@ class IpHashMethod : public HashPolicyImpl::HashMethod {
}
return HashUtil::xxHash64(downstream_addr_str);
}

private:
const bool terminal_;
};

class HostPathHashMethod : public HashPolicyImpl::HashMethod {
public:
HostPathHashMethod(bool terminal) : terminal_(terminal) {}
absl::optional<uint64_t> evaluate(const Network::Address::Instance*,
const Http::HeaderMap& headers,
const HashPolicy::AddCookieCallback) const override {
return HashUtil::xxHash64(
absl::StrCat(headers.Host()->value().c_str(), headers.Path()->value().c_str()));
}
bool terminal() const override { return terminal_; }

private:
bool terminal_ = false;
};

class QueryParameterHashMethod : public HashPolicyImpl::HashMethod {
public:
QueryParameterHashMethod(const std::string& key, bool terminal)
: key_(key), terminal_(terminal) {}
absl::optional<uint64_t> evaluate(const Network::Address::Instance*,
const Http::HeaderMap& headers,
const HashPolicy::AddCookieCallback) const override {
if (key_.empty()) {
return absl::nullopt;
}
Http::Utility::QueryParams query_parameters =
Http::Utility::parseQueryString(headers.Path()->value().c_str());
auto it = query_parameters.find(key_);
if (it == query_parameters.end() || it->second.empty()) {
return absl::nullopt;
}
return HashUtil::xxHash64(it->second);
}
bool terminal() const override { return terminal_; }

private:
const std::string key_;
bool terminal_ = false;
};

HashPolicyImpl::HashPolicyImpl(
Expand All @@ -153,22 +214,30 @@ HashPolicyImpl::HashPolicyImpl(
for (auto& hash_policy : hash_policies) {
switch (hash_policy.policy_specifier_case()) {
case envoy::api::v2::route::RouteAction::HashPolicy::kHeader:
hash_impls_.emplace_back(new HeaderHashMethod(hash_policy.header().header_name()));
hash_impls_.emplace_back(
new HeaderHashMethod(hash_policy.header().header_name(), hash_policy.terminal()));
break;
case envoy::api::v2::route::RouteAction::HashPolicy::kCookie: {
absl::optional<std::chrono::seconds> ttl;
if (hash_policy.cookie().has_ttl()) {
ttl = std::chrono::seconds(hash_policy.cookie().ttl().seconds());
}
hash_impls_.emplace_back(
new CookieHashMethod(hash_policy.cookie().name(), hash_policy.cookie().path(), ttl));
hash_impls_.emplace_back(new CookieHashMethod(
hash_policy.cookie().name(), hash_policy.cookie().path(), ttl, hash_policy.terminal()));
break;
}
case envoy::api::v2::route::RouteAction::HashPolicy::kConnectionProperties:
if (hash_policy.connection_properties().source_ip()) {
hash_impls_.emplace_back(new IpHashMethod());
hash_impls_.emplace_back(new IpHashMethod(hash_policy.terminal()));
}
break;
case envoy::api::v2::route::RouteAction::HashPolicy::kQueryParameter:
hash_impls_.emplace_back(new QueryParameterHashMethod(hash_policy.query_parameter().key(),
hash_policy.terminal()));
break;
case envoy::api::v2::route::RouteAction::HashPolicy::kHostPath:
hash_impls_.emplace_back(new HostPathHashMethod(hash_policy.terminal()));
break;
default:
throw EnvoyException(
fmt::format("Unsupported hash policy {}", hash_policy.policy_specifier_case()));
Expand All @@ -190,6 +259,11 @@ HashPolicyImpl::generateHash(const Network::Address::Instance* downstream_addr,
const uint64_t old_value = hash ? ((hash.value() << 1) | (hash.value() >> 63)) : 0;
hash = old_value ^ new_hash.value();
}
// If the policy is a terminal policy and there are hash generated, ignore
// the rest hash policies.
if (hash_impl->terminal() && hash) {
break;
}
}
return hash;
}
Expand Down
2 changes: 2 additions & 0 deletions source/common/router/config_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ class HashPolicyImpl : public HashPolicy {
virtual absl::optional<uint64_t> evaluate(const Network::Address::Instance* downstream_addr,
const Http::HeaderMap& headers,
const AddCookieCallback add_cookie) const PURE;
// If the method is a terminal method, ignore rest of the hash policy chain.
virtual bool terminal() const PURE;
};

typedef std::unique_ptr<HashMethod> HashMethodPtr;
Expand Down
Loading

0 comments on commit 4ef5408

Please sign in to comment.