Skip to content

Commit

Permalink
mcrouter: supply stable tags for Rendezvous hashing
Browse files Browse the repository at this point in the history
Summary: Titled. Rendezvous hashing needs a list of stable tags matching route handles. This can be supplied in the config of pool route.

Reviewed By: spalamarchuk

Differential Revision: D5910343

fbshipit-source-id: bef4adc13efb58aa352717bd5e8832b18e9b414b
  • Loading branch information
shodoco authored and facebook-github-bot committed Oct 11, 2017
1 parent 7e1d83a commit 8ac86e3
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 20 deletions.
5 changes: 3 additions & 2 deletions mcrouter/lib/RendezvousHashFunc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ inline uint64_t hash128to64(const uint64_t upper, const uint64_t lower) {
namespace facebook {
namespace memcache {

RendezvousHashFunc::RendezvousHashFunc(std::vector<std::string> endpoints) {
RendezvousHashFunc::RendezvousHashFunc(
std::vector<folly::StringPiece> endpoints) {
endpointHashes_.reserve(endpoints.size());
for (const folly::StringPiece ap : endpoints) {
for (const auto ap : endpoints) {
const uint64_t hash = murmur_hash_64A(ap.data(), ap.size(), kHashSeed);
endpointHashes_.push_back(hash);
}
Expand Down
2 changes: 1 addition & 1 deletion mcrouter/lib/RendezvousHashFunc.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class RendezvousHashFunc {
/**
* @param endpoints A list of backend servers
*/
explicit RendezvousHashFunc(std::vector<std::string> endpoints);
explicit RendezvousHashFunc(std::vector<folly::StringPiece> endpoints);

size_t operator()(folly::StringPiece key) const;

Expand Down
36 changes: 24 additions & 12 deletions mcrouter/lib/test/RendezvousHashTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,29 @@
using namespace facebook::memcache;

namespace {
std::vector<std::string> genEndpoints(int n) {
std::vector<std::string> result;
std::pair<std::vector<std::string>, std::vector<folly::StringPiece>>
genEndpoints(int n) {
std::vector<std::string> raw;
std::vector<folly::StringPiece> ref;
for (int i = 0; i < n; ++i) {
auto endpoint = "xxx." + folly::to<std::string>(i) + ".yy";
result.push_back(endpoint);
raw.push_back(endpoint);
}
return result;
for (const auto& e : raw) {
ref.push_back(e);
}
return std::make_pair(std::move(raw), std::move(ref));
}

RendezvousHashFunc genRendezvousHashFunc(int n) {
auto combined = genEndpoints(n);
return RendezvousHashFunc(combined.second);
}

} // namespace

TEST(RendezvousHashFunc, basic) {
RendezvousHashFunc func_3(genEndpoints(3));
auto func_3 = genRendezvousHashFunc(3);

EXPECT_EQ(func_3("sample"), 1);
EXPECT_EQ(func_3(""), 1);
Expand All @@ -42,7 +53,7 @@ TEST(RendezvousHashFunc, basic) {
}
EXPECT_EQ(func_3(test_max_key), 1);

RendezvousHashFunc func_343(genEndpoints(343));
auto func_343 = genRendezvousHashFunc(343);

EXPECT_EQ(func_343(test_max_key), 183);
EXPECT_EQ(func_343("sample"), 45);
Expand All @@ -51,7 +62,7 @@ TEST(RendezvousHashFunc, basic) {
}

TEST(RendezvousHashFunc, rendezvous_3) {
RendezvousHashFunc rendezvous_3(genEndpoints(3));
auto rendezvous_3 = genRendezvousHashFunc(3);

std::vector<size_t> rendezvous_counts(3, 0);
for (size_t i = 0; i < 1000; ++i) {
Expand All @@ -63,9 +74,9 @@ TEST(RendezvousHashFunc, rendezvous_3) {
}

TEST(RendezvousHashFunc, rendezvous_10) {
RendezvousHashFunc rendezvous_10(genEndpoints(10));
std::vector<size_t> rendezvous_counts(10, 0);
auto rendezvous_10 = genRendezvousHashFunc(10);

std::vector<size_t> rendezvous_counts(10, 0);
for (size_t i = 0; i < 10000; ++i) {
auto key = "mykey:" + folly::to<std::string>(i);
++rendezvous_counts[rendezvous_10(key)];
Expand All @@ -79,13 +90,14 @@ TEST(RendezvousHashFunc, rendezvous_10) {

TEST(RendezvousHashFunc, rendezvous_rehash) {
const uint32_t n = 499;
auto endpoints = genEndpoints(n);
auto combined = genEndpoints(n);
const auto& endpoints = combined.second;

RendezvousHashFunc rendezvous(endpoints);

// Number of rehashes if we remove one element
auto removeCompare = [&](std::vector<std::string>& newEndpoints,
std::vector<std::string>::iterator it) {
auto removeCompare = [&](std::vector<folly::StringPiece>& newEndpoints,
std::vector<folly::StringPiece>::iterator it) {
newEndpoints.erase(it);

RendezvousHashFunc newRendezvous(newEndpoints);
Expand Down
17 changes: 12 additions & 5 deletions mcrouter/routes/HashRouteFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,18 @@ std::shared_ptr<typename RouterInfo::RouteHandleIf> createHashRoute(
return createHashRoute<RouterInfo, ConstShardHashFunc>(
std::move(rh), std::move(salt), ConstShardHashFunc(n));
} else if (funcType == RendezvousHashFunc::type()) {
// Construct a list of endpoints
std::vector<std::string> endpoints;
endpoints.reserve(rh.size());
for (const auto& handle : rh) {
endpoints.push_back(handle->routeName());
std::vector<folly::StringPiece> endpoints;

auto jtags = json.get_ptr("tags");
checkLogic(jtags, "HashRoute: tags needed for Rendezvous hash route");
checkLogic(jtags->isArray(), "HashRoute: tags is not an array");
checkLogic(
jtags->size() == rh.size(),
"HashRoute: number of tags doesn't match number of route handles");

for (const auto& jtag : *jtags) {
checkLogic(jtag.isString(), "HashRoute: tag is not a string");
endpoints.push_back(jtag.stringPiece());
}

return createHashRoute<RouterInfo, RendezvousHashFunc>(
Expand Down
4 changes: 4 additions & 0 deletions mcrouter/routes/McRouteHandleProvider-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ McRouteHandleProvider<RouterInfo>::makePoolRoute(
"hash_func", WeightedCh3HashFunc::type())("weights", *jWeights);
}

if (auto jTags = poolJson.json.get_ptr("tags")) {
jhashWithWeights["tags"] = *jTags;
}

if (json.isObject()) {
if (auto jhash = json.get_ptr("hash")) {
checkLogic(
Expand Down

0 comments on commit 8ac86e3

Please sign in to comment.