-
Notifications
You must be signed in to change notification settings - Fork 0
/
cuckoorings.hpp
284 lines (253 loc) · 7.21 KB
/
cuckoorings.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
/** @class CuckooRings
* @brief This data structures combines two consistent hashing rings into
* a single data structure which uses cuckoo-like rehashing to ensure both
* that keys don't often need to get rehashed.
*
* @author ankitvgupta
* @author jonahkall
*/
#include <iostream>
#include <string>
#include <set>
#include <functional>
#include <map>
#include <vector>
#include "ringhash.hpp"
#include "farmhash.hpp"
#define STOP_ITERS 8
class CuckooRings {
private:
typedef long long server_id;
typedef std::map<long long, std::vector<int> > MapType;
typedef MapType::iterator MapIterator;
typedef std::vector<int>::iterator VectorIterator;
/**
* Key space size. Indicates what the maximum key to be hashed to is
*/
long long kss_;
/*
* Tracks the number of servers
*/
int num_servers_;
/**
* Stores pointers to the left and right rings
*/
RingHash* left_ring_;
RingHash* right_ring_;
unsigned insert_counter;
public:
/*
* @brief THis is the hash function to be used by left HashRing
* @param a the number that being hashed
* @param kss_ the key space size
* @returns a hash between 0 and kss_
* Source: Thomas Wang, http://burtleburtle.net/bob/hash/integer.html
*/
static
long long hash_left(long long a, long long kss_) {
a = (a+0x7ed55d16) + (a<<12);
a = (a^0xc761c23c) ^ (a>>19);
a = (a+0x165667b1) + (a<<5);
a = (a+0xd3a2646c) ^ (a<<9);
a = (a+0xfd7046c5) + (a<<3);
a = (a^0xb55a4f09) ^ (a>>16);
return abs(((a % kss_) + kss_) % kss_);
}
/*
* @brief THis is the hash function to be used by right HashRing
* @param a the number that being hashed
* @param kss_ the key space size
* @returns a hash between 0 and kss_
* Source: Thomas Wang via Geoffrey Irving, https://naml.us/blog/tag/thomas-wang
*/
static
long long hash_right(long long key, long long kss_) {
key = (~key) + (key << 21); // key = (key << 21) - key - 1;
key = key ^ (key >> 24);
key = (key + (key << 3)) + (key << 8); // key * 265
key = key ^ (key >> 14);
key = (key + (key << 2)) + (key << 4); // key * 21
key = key ^ (key >> 28);
key = key + (key << 31);
return abs(((key % kss_) + kss_) % kss_);
}
/**
* Constructor for CuckooRings
* @param key_space_size indicates that the max key that can be hashed to
* @param init_servers indicates the number of servers in EACH ring
*/
CuckooRings(long long key_space_size, int init_servers) :
kss_(key_space_size) {
// Set up keyspace now
left_ring_ = new RingHash(key_space_size, init_servers, hash_left);
right_ring_ = new RingHash(key_space_size, init_servers, hash_right);
num_servers_ = 2 * init_servers;
}
/**
* #param the key that is being inserted
* @brief Inserts a key into the HashRing
*/
void insert (int key) {
insert_counter = 0;
server_id ret;
if (left_ring_->num_keys() > right_ring_->num_keys()) {
ret = right_ring_->insert(key);
if (ret != -1) {
send_server_rtol(ret);
}
}
else {
ret = left_ring_->insert(key);
if (ret != -1) {
send_server_ltor(ret);
}
}
}
/**
* @brief Sends all the contents of a server from the left ring to the right ring
* @param s the server_id of the server in the Left Ring that is being cuckooed over
*/
void send_server_ltor(server_id s) {
++insert_counter;
if (insert_counter > STOP_ITERS) {
return;
}
server_id ret;
vector<server_id> to_send;
vector<int>& lserver = left_ring_->get_keys(s);
for (const auto& i : lserver) {
ret = right_ring_->insert(i);
if (ret != -1) {
to_send.push_back(ret);
}
}
left_ring_->clear_server(s);
for (const auto& server : to_send) {
send_server_rtol(server);
}
}
/**
* @brief Sends all the contents of a server from the right ring to the left ring
* @param s the server_id of the server in the right ring that is being cuckooed over
*/
void send_server_rtol(server_id s) {
++insert_counter;
if (insert_counter > STOP_ITERS) {
return;
}
server_id ret;
vector<server_id> to_send;
vector<int>& rserver = right_ring_->get_keys(s);
for (const auto& i : rserver) {
ret = left_ring_->insert(i);
if (ret != -1) {
to_send.push_back(ret);
}
}
right_ring_->clear_server(s);
for (const auto& server : to_send) {
send_server_ltor(server);
}
}
/**
* #param the key that is being removed
* @brief removes a key into the HashRing
* @brief Can be implemented by checking each ring and removing,
* but we left it out since we are not using it.
*/
void remove(int key) {
(void)key;
}
/**
* #param the key that is being looked up
* @brief Finds the server associated with a key
* @returns server_id of the associated server
* @brief Not implemented because not needed for us.
*/
server_id lookup (int key) {
(void)key;
return 0;
}
/**
* #param location where server will be put
* @brief adds a server to the RingHash
* not implemented because not needed for us
* @returns void
*/
void add_server(int server_loc) {
(void)server_loc;
}
/**
* @brief Removes a random server from the specified side
* @param side The side to remove the server from
*/
// removes from left if side = 0, from right if side = 1
void remove_random_server(server_id s, int side) {
(void) s;
if (side == 0){
left_ring_->remove_random_server();
}
else{
right_ring_->remove_random_server();
}
return;
}
/**
* @brief Prints the loads on each server,
* separated by a comma, followed by the total load
*/
void print_loads(void) {
cout << "Left ring loads are: " << "\n";
left_ring_->print_loads();
cout << "Right ring loads are: " << "\n";
right_ring_->print_loads();
}
/**
* @brief evaluates the cost of a CuckooRing by determing the cost of each ring
* @returns the cost as a long long
*/
long long cost_of_structure(void){
return (left_ring_->cost_of_structure() + right_ring_->cost_of_structure());
}
/**
* @brief finds the server with the highest load
* @returns the load of that server
*/
long long get_max_load(void) {
return max(right_ring_->get_max_load(), left_ring_->get_max_load());
}
/**
* @brief finds the server with the least load
* @returns the load of that server
*/
long long get_min_load(void){
return min(left_ring_->get_min_load(), left_ring_->get_min_load());
}
/**
* @brief determines the number of servers in the CuckooRing
* @returns that number as a long long
*/
long long getNumServers(void){
return (left_ring_->getNumServers() + right_ring_->getNumServers());
}
/**
* @brief finds the number of keys inserted in the structure
* @returns that number as a long long
*/
long long getNumKeys(void){
return (left_ring_->getNumKeys() + right_ring_->getNumKeys());
}
/**
* @brief adds a server to a random location in the specified side
*/
void add_random_server(int side){
side = side % 2;
if (side == 0) {
left_ring_->add_random_server();
}
else {
right_ring_->add_random_server();
}
return;
}
};