proxygen
RendezvousHashTest.cpp File Reference
#include <folly/Conv.h>
#include <folly/container/Foreach.h>
#include <folly/portability/GTest.h>
#include <map>
#include <vector>
#include <proxygen/lib/utils/RendezvousHash.h>

Go to the source code of this file.

Functions

 TEST (RendezvousHash, Consistency)
 
 TEST (RendezvousHash, ConsistencyWithNewNode)
 
 TEST (RendezvousHash, ConsistencyWithIncreasedWeight)
 
 TEST (RendezvousHash, ConsistentFlowToIncreasedWeightNode)
 
 TEST (RendezvousHash, ConsistentFlowToDecreasedWeightNodes)
 
 TEST (RendezvousHash, ConsistentFlowToDecreasedWeightNode)
 
 TEST (RendezvousHash, ConsistencyWithDecreasedWeight)
 
 TEST (ConsistentHashRing, DistributionAccuracy)
 
 TEST (RendezvousHash, selectNUnweighted)
 

Function Documentation

TEST ( RendezvousHash  ,
Consistency   
)

Definition at line 20 of file RendezvousHashTest.cpp.

References proxygen::RendezvousHash::build(), EXPECT_EQ, FOR_EACH_KV, proxygen::RendezvousHash::get(), and i.

20  {
21  RendezvousHash hashes;
22  std::vector<std::pair<std::string, uint64_t> > nodes;
23  for (int i = 0; i < 10; ++i) {
24  nodes.emplace_back(folly::to<std::string>("key", i), 1);
25  }
26  hashes.build(nodes);
27 
28  for (size_t rank = 0; rank < nodes.size() + 2; rank++) {
29  std::map<uint64_t, size_t> mapping;
30  for (int i = 0; i < 10000; ++i) {
31  mapping[i] = hashes.get(i, rank);
32  }
33 
34  FOR_EACH_KV(key, expected, mapping) {
35  EXPECT_EQ(expected, hashes.get(key, rank));
36  }
37  }
38 }
#define FOR_EACH_KV(k, v, c)
Definition: Foreach.h:197
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
void build(std::vector< std::pair< std::string, uint64_t >> &) override
size_t get(const uint64_t key, const size_t rank=0) const override
TEST ( RendezvousHash  ,
ConsistencyWithNewNode   
)

Definition at line 40 of file RendezvousHashTest.cpp.

References proxygen::RendezvousHash::build(), EXPECT_TRUE, FOR_EACH_KV, proxygen::RendezvousHash::get(), i, and uint64_t.

40  {
41  RendezvousHash hashes;
42  int numNodes = 10;
43  std::vector<std::pair<std::string, uint64_t> > nodes;
44  for (int i = 0; i < numNodes; ++i) {
45  nodes.emplace_back(folly::to<std::string>("key", i), 1);
46  }
47  hashes.build(nodes);
48  std::map<uint64_t, size_t> mapping;
49  for (uint64_t i = 0; i < 10000; ++i) {
50  mapping[i] = hashes.get(i);
51  }
52  hashes = RendezvousHash();
53  // Adding a new node and rebuild the hash
54  nodes.emplace_back(folly::to<std::string>("key", numNodes), 1);
55  hashes.build(nodes);
56  // traffic should only flow to the new node
57  FOR_EACH_KV (key, expected, mapping) {
58  size_t id = hashes.get(key);
59  EXPECT_TRUE(expected == id || numNodes == int(id));
60  }
61 }
#define FOR_EACH_KV(k, v, c)
Definition: Foreach.h:197
void build(std::vector< std::pair< std::string, uint64_t >> &) override
size_t get(const uint64_t key, const size_t rank=0) const override
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
TEST ( RendezvousHash  ,
ConsistencyWithIncreasedWeight   
)

Definition at line 63 of file RendezvousHashTest.cpp.

References proxygen::RendezvousHash::build(), EXPECT_EQ, FOR_EACH_KV, proxygen::RendezvousHash::get(), i, and uint64_t.

63  {
64  RendezvousHash hashes;
65  int numNodes = 10;
66  std::vector<std::pair<std::string, uint64_t> > nodes;
67  for (int i = 0; i < numNodes; ++i) {
68  nodes.emplace_back(folly::to<std::string>("key", i), i);
69  }
70  hashes.build(nodes);
71 
72  std::map<uint64_t, size_t> mapping;
73  for (uint64_t i = 0; i < 10000; ++i) {
74  mapping[i] = hashes.get(i);
75  }
76 
77  // Increase the weight by 2
78  nodes.clear();
79  hashes = RendezvousHash();
80  for (int i = 0; i < numNodes; ++i) {
81  nodes.emplace_back(folly::to<std::string>("key", i), i*2);
82  }
83  hashes.build(nodes);
84 
85  // traffic shouldn't flow at all
86  FOR_EACH_KV (key, expected, mapping) {
87  EXPECT_EQ(expected, hashes.get(key));
88  }
89 }
#define FOR_EACH_KV(k, v, c)
Definition: Foreach.h:197
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
void build(std::vector< std::pair< std::string, uint64_t >> &) override
size_t get(const uint64_t key, const size_t rank=0) const override
TEST ( RendezvousHash  ,
ConsistentFlowToIncreasedWeightNode   
)

Definition at line 91 of file RendezvousHashTest.cpp.

References proxygen::RendezvousHash::build(), EXPECT_TRUE, FOR_EACH_KV, proxygen::RendezvousHash::get(), i, and uint64_t.

91  {
92  RendezvousHash hashes;
93  int numNodes = 10;
94  std::vector<std::pair<std::string, uint64_t> > nodes;
95  for (int i = 0; i < numNodes; ++i) {
96  nodes.emplace_back(folly::to<std::string>("key", i), i);
97  }
98  hashes.build(nodes);
99 
100  std::map<uint64_t, size_t> mapping;
101  for (uint64_t i = 0; i < 10000; ++i) {
102  mapping[i] = hashes.get(i);
103  }
104 
105  nodes.clear();
106  // Increase the weight for a single node
107  hashes = RendezvousHash();
108 
109  nodes.emplace_back(folly::to<std::string>("key", 0), 10);
110 
111  for (int i = 1; i < numNodes; ++i) {
112  nodes.emplace_back(folly::to<std::string>("key", i), i);
113  }
114  hashes.build(nodes);
115  // traffic should only flow to the first node
116  FOR_EACH_KV (key, expected, mapping) {
117  size_t id = hashes.get(key);
118  EXPECT_TRUE(expected == id || 0 == int(id));
119  }
120 }
#define FOR_EACH_KV(k, v, c)
Definition: Foreach.h:197
void build(std::vector< std::pair< std::string, uint64_t >> &) override
size_t get(const uint64_t key, const size_t rank=0) const override
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
TEST ( RendezvousHash  ,
ConsistentFlowToDecreasedWeightNodes   
)

Definition at line 122 of file RendezvousHashTest.cpp.

References proxygen::RendezvousHash::build(), EXPECT_TRUE, FOR_EACH_KV, proxygen::RendezvousHash::get(), i, and uint64_t.

122  {
123  RendezvousHash hashes;
124  int numNodes = 18;
125  std::vector<std::pair<std::string, uint64_t> > nodes;
126  for (int i = 0; i < numNodes; ++i) {
127  nodes.emplace_back(folly::to<std::string>("key", i), 100);
128  }
129  hashes.build(nodes);
130  std::map<uint64_t, size_t> mapping;
131  for (uint64_t i = 0; i < 10000; ++i) {
132  mapping[i] = hashes.get(i);
133  }
134 
135  nodes.clear();
136  hashes = RendezvousHash();
137 
138  // decrease the weights for 5 nodes
139  for (int i = 0; i < 5; ++i) {
140  nodes.emplace_back(folly::to<std::string>("key", i), 50);
141  }
142 
143  // keep the weights for the rest unchanged
144  for (int i = 5; i < numNodes; ++i) {
145  nodes.emplace_back(folly::to<std::string>("key", i), 100);
146  }
147 
148  hashes.build(nodes);
149  FOR_EACH_KV (key, expected, mapping) {
150  // traffic should only flow to nodes with decreased nodes
151  size_t id = hashes.get(key);
152  EXPECT_TRUE(expected == id || id >= 5);
153  }
154 }
#define FOR_EACH_KV(k, v, c)
Definition: Foreach.h:197
void build(std::vector< std::pair< std::string, uint64_t >> &) override
size_t get(const uint64_t key, const size_t rank=0) const override
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
TEST ( RendezvousHash  ,
ConsistentFlowToDecreasedWeightNode   
)

Definition at line 156 of file RendezvousHashTest.cpp.

References proxygen::RendezvousHash::build(), EXPECT_TRUE, FOR_EACH_KV, proxygen::RendezvousHash::get(), i, and uint64_t.

156  {
157  RendezvousHash hashes;
158  int numNodes = 10;
159  std::vector<std::pair<std::string, uint64_t> > nodes;
160  for (int i = 0; i < numNodes; ++i) {
161  nodes.emplace_back(folly::to<std::string>("key", i), i);
162  }
163  hashes.build(nodes);
164  std::map<uint64_t, size_t> mapping;
165  for (uint64_t i = 0; i < 10000; ++i) {
166  mapping[i] = hashes.get(i);
167  }
168 
169  // Increase the weight for a single node
170  nodes.clear();
171  hashes = RendezvousHash();
172 
173  for (int i = 0; i < numNodes - 1; ++i) {
174  nodes.emplace_back(folly::to<std::string>("key", i), i);
175  }
176 
177  // zero the weight of the last node
178  nodes.emplace_back(folly::to<std::string>("key", numNodes-1), 0);
179  hashes.build(nodes);
180  FOR_EACH_KV (key, expected, mapping) {
181  // traffic should only flow from the zero weight cluster to others
182  size_t id = hashes.get(key);
183  if (expected == (uint64_t)numNodes-1) {
184  EXPECT_TRUE(expected != id);
185  } else {
186  EXPECT_TRUE(expected == id);
187  }
188  }
189 }
#define FOR_EACH_KV(k, v, c)
Definition: Foreach.h:197
void build(std::vector< std::pair< std::string, uint64_t >> &) override
size_t get(const uint64_t key, const size_t rank=0) const override
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
TEST ( RendezvousHash  ,
ConsistencyWithDecreasedWeight   
)

Definition at line 191 of file RendezvousHashTest.cpp.

References proxygen::RendezvousHash::build(), EXPECT_EQ, FOR_EACH_KV, proxygen::RendezvousHash::get(), i, and uint64_t.

191  {
192  RendezvousHash hashes;
193  int numNodes = 10;
194  std::vector<std::pair<std::string, uint64_t> > nodes;
195  for (int i = 0; i < numNodes; ++i) {
196  nodes.emplace_back(folly::to<std::string>("key", i), i*2);
197  }
198  hashes.build(nodes);
199  std::map<uint64_t, size_t> mapping;
200  for (uint64_t i = 0; i < 10000; ++i) {
201  mapping[i] = hashes.get(i);
202  }
203 
204  // Decrease the weight by 2
205  nodes.clear();
206  hashes = RendezvousHash();
207 
208  for (int i = 0; i < numNodes; ++i) {
209  nodes.emplace_back(folly::to<std::string>("key", i), i);
210  }
211  hashes.build(nodes);
212 
213  // traffic shouldn't flow at all
214  FOR_EACH_KV (key, expected, mapping) {
215  EXPECT_EQ(expected, hashes.get(key));
216  }
217 }
#define FOR_EACH_KV(k, v, c)
Definition: Foreach.h:197
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
void build(std::vector< std::pair< std::string, uint64_t >> &) override
size_t get(const uint64_t key, const size_t rank=0) const override
TEST ( ConsistentHashRing  ,
DistributionAccuracy   
)

Definition at line 219 of file RendezvousHashTest.cpp.

References proxygen::RendezvousHash::build(), EXPECT_LE, FOR_EACH_RANGE, proxygen::RendezvousHash::get(), i, max, and uint64_t.

219  {
220  std::vector<std::string> keys =
221  {"ash_proxy", "prn_proxy", "snc_proxy", "frc_proxy"};
222 
223  std::vector<std::vector<uint64_t>> weights = {
224  {248, 342, 2, 384},
225  {10, 10, 10, 10},
226  {25, 25, 10, 10},
227  {100, 10, 10, 1},
228  {100, 5, 5, 5},
229  {922337203685, 12395828300, 50192385101, 59293845010}
230  };
231 
232  for (auto& weight: weights) {
233  RendezvousHash hashes;
234  std::vector<std::pair<std::string, uint64_t> > nodes;
235  FOR_EACH_RANGE (i, 0, keys.size()) {
236  nodes.emplace_back(keys[i], weight[i]);
237  }
238  hashes.build(nodes);
239 
240  std::vector<uint64_t> distribution(keys.size());
241 
242  for (uint64_t i = 0; i < 21000; ++i) {
243  distribution[hashes.get(i)]++;
244  }
245 
246  uint64_t totalWeight = 0;
247 
248  for (auto& w: weight) {
249  totalWeight += w;
250  }
251 
252  double maxError = 0.0;
253  for (size_t i = 0; i < keys.size(); ++i) {
254  double expected = 100.0 * weight[i] / totalWeight;
255  double actual = 100.0 * distribution[i] / 21000;
256 
257  maxError = std::max(maxError, fabs(expected - actual));
258  }
259  // make sure the error rate is less than 1.0%
260  EXPECT_LE(maxError, 1.0);
261  }
262 }
#define EXPECT_LE(val1, val2)
Definition: gtest.h:1928
LogLevel max
Definition: LogLevel.cpp:31
void build(std::vector< std::pair< std::string, uint64_t >> &) override
size_t get(const uint64_t key, const size_t rank=0) const override
#define FOR_EACH_RANGE(i, begin, end)
Definition: Foreach.h:313
TEST ( RendezvousHash  ,
selectNUnweighted   
)

Definition at line 264 of file RendezvousHashTest.cpp.

References proxygen::RendezvousHash::build(), EXPECT_EQ, EXPECT_GT, EXPECT_LT, i, seed, proxygen::RendezvousHash::selectNUnweighted(), and folly::size().

264  {
265  RendezvousHash hashes;
266  std::vector<std::pair<std::string, uint64_t>> nodes;
267  int size = 100;
268  for (int i = 0; i < size; ++i) {
269  nodes.emplace_back(folly::to<std::string>("key", i), 1);
270  }
271  hashes.build(nodes);
272  auto seed = 91484253;
273 
274  // rank > size
275  auto select = hashes.selectNUnweighted(seed, size + 10);
276  EXPECT_EQ(select.size(), size);
277  for (int i = 0; i < size; i++) {
278  EXPECT_EQ(select[i], i);
279  }
280 
281  // check valid index in selection
282  int rank = size / 4;
283  select = hashes.selectNUnweighted(seed, rank);
284  EXPECT_EQ(select.size(), rank);
285  std::set<size_t> uniqueIndex;
286  for (auto index : select) {
287  EXPECT_EQ(uniqueIndex.count(index), 0);
288  uniqueIndex.insert(index);
289  EXPECT_LT(index, size);
290  }
291 
292  // change seed, check different selection
293  for (int i = 1; i < 100; i++) {
294  select = hashes.selectNUnweighted(seed + i, rank);
295  int different = 0;
296  for (auto index : select) {
297  if (uniqueIndex.count(index) == 0) {
298  different++;
299  }
300  }
301  EXPECT_GT(different, 0);
302  }
303 }
static const int seed
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
void build(std::vector< std::pair< std::string, uint64_t >> &) override
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
std::vector< size_t > selectNUnweighted(const uint64_t key, const size_t rank) const
#define EXPECT_LT(val1, val2)
Definition: gtest.h:1930
#define EXPECT_GT(val1, val2)
Definition: gtest.h:1934