First pass at adding in exception for servers which have gone away.
[m6w6/libmemcached] / tests / libmemcached-1.0 / ketama.cc
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * Libmemcached library
4 *
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following disclaimer
16 * in the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * * The names of its contributors may not be used to endorse or
20 * promote products derived from this software without specific prior
21 * written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 */
36
37 #include <config.h>
38 #include <libtest/test.hpp>
39
40 #include <libmemcached/memcached.h>
41 #include <libmemcached/server_instance.h>
42 #include <libmemcached/continuum.hpp>
43
44 #include <tests/ketama.h>
45 #include <tests/ketama_test_cases.h>
46
47 test_return_t ketama_compatibility_libmemcached(memcached_st *)
48 {
49 memcached_st *memc= memcached_create(NULL);
50 test_true(memc);
51
52 test_compare(MEMCACHED_SUCCESS,
53 memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED, 1));
54
55 test_compare(uint64_t(1), memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED));
56
57 test_compare(MEMCACHED_SUCCESS, memcached_behavior_set_distribution(memc, MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA));
58 test_compare(MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA, memcached_behavior_get_distribution(memc));
59
60 memcached_server_st *server_pool= memcached_servers_parse("10.0.1.1:11211 600,10.0.1.2:11211 300,10.0.1.3:11211 200,10.0.1.4:11211 350,10.0.1.5:11211 1000,10.0.1.6:11211 800,10.0.1.7:11211 950,10.0.1.8:11211 100");
61 memcached_server_push(memc, server_pool);
62
63 /* verify that the server list was parsed okay. */
64 test_compare(8U, memcached_server_count(memc));
65 test_strcmp(server_pool[0].hostname, "10.0.1.1");
66 test_compare(in_port_t(11211), server_pool[0].port);
67 test_compare(600U, server_pool[0].weight);
68 test_strcmp(server_pool[2].hostname, "10.0.1.3");
69 test_compare(in_port_t(11211), server_pool[2].port);
70 test_compare(200U, server_pool[2].weight);
71 test_strcmp(server_pool[7].hostname, "10.0.1.8");
72 test_compare(in_port_t(11211), server_pool[7].port);
73 test_compare(100U, server_pool[7].weight);
74
75 /* VDEAAAAA hashes to fffcd1b5, after the last continuum point, and lets
76 * us test the boundary wraparound.
77 */
78 test_true(memcached_generate_hash(memc, (char *)"VDEAAAAA", 8) == memc->ketama.continuum[0].index);
79
80 /* verify the standard ketama set. */
81 for (uint32_t x= 0; x < 99; x++)
82 {
83 uint32_t server_idx = memcached_generate_hash(memc, ketama_test_cases[x].key, strlen(ketama_test_cases[x].key));
84 memcached_server_instance_st instance=
85 memcached_server_instance_by_position(memc, server_idx);
86 const char *hostname = memcached_server_name(instance);
87
88 test_strcmp(hostname, ketama_test_cases[x].server);
89 }
90
91 memcached_server_list_free(server_pool);
92 memcached_free(memc);
93
94 return TEST_SUCCESS;
95 }
96
97 test_return_t user_supplied_bug18(memcached_st *trash)
98 {
99 memcached_return_t rc;
100 uint64_t value;
101 int x;
102 memcached_st *memc;
103
104 (void)trash;
105
106 memc= memcached_create(NULL);
107 test_true(memc);
108
109 rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED, 1);
110 test_compare(MEMCACHED_SUCCESS, rc);
111
112 value= memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED);
113 test_true(value == 1);
114
115 rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_HASH, MEMCACHED_HASH_MD5);
116 test_compare(MEMCACHED_SUCCESS, rc);
117
118 value= memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_HASH);
119 test_true(value == MEMCACHED_HASH_MD5);
120
121 memcached_server_st *server_pool= memcached_servers_parse("10.0.1.1:11211 600,10.0.1.2:11211 300,10.0.1.3:11211 200,10.0.1.4:11211 350,10.0.1.5:11211 1000,10.0.1.6:11211 800,10.0.1.7:11211 950,10.0.1.8:11211 100");
122 memcached_server_push(memc, server_pool);
123
124 /* verify that the server list was parsed okay. */
125 test_true(memcached_server_count(memc) == 8);
126 test_strcmp(server_pool[0].hostname, "10.0.1.1");
127 test_true(server_pool[0].port == 11211);
128 test_true(server_pool[0].weight == 600);
129 test_strcmp(server_pool[2].hostname, "10.0.1.3");
130 test_true(server_pool[2].port == 11211);
131 test_true(server_pool[2].weight == 200);
132 test_strcmp(server_pool[7].hostname, "10.0.1.8");
133 test_true(server_pool[7].port == 11211);
134 test_true(server_pool[7].weight == 100);
135
136 /* VDEAAAAA hashes to fffcd1b5, after the last continuum point, and lets
137 * us test the boundary wraparound.
138 */
139 test_true(memcached_generate_hash(memc, (char *)"VDEAAAAA", 8) == memc->ketama.continuum[0].index);
140
141 /* verify the standard ketama set. */
142 for (x= 0; x < 99; x++)
143 {
144 uint32_t server_idx = memcached_generate_hash(memc, ketama_test_cases[x].key, strlen(ketama_test_cases[x].key));
145
146 memcached_server_instance_st instance=
147 memcached_server_instance_by_position(memc, server_idx);
148
149 const char *hostname = memcached_server_name(instance);
150 test_strcmp(hostname, ketama_test_cases[x].server);
151 }
152
153 memcached_server_list_free(server_pool);
154 memcached_free(memc);
155
156 return TEST_SUCCESS;
157 }
158
159 test_return_t auto_eject_hosts(memcached_st *trash)
160 {
161 (void) trash;
162 memcached_server_instance_st instance;
163
164 memcached_return_t rc;
165 memcached_st *memc= memcached_create(NULL);
166 test_true(memc);
167
168 rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED, 1);
169 test_compare(MEMCACHED_SUCCESS, rc);
170
171 uint64_t value= memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED);
172 test_true(value == 1);
173
174 rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_HASH, MEMCACHED_HASH_MD5);
175 test_compare(MEMCACHED_SUCCESS, rc);
176
177 value= memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_HASH);
178 test_true(value == MEMCACHED_HASH_MD5);
179
180 /* server should be removed when in delay */
181 rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS, 1);
182 test_compare(MEMCACHED_SUCCESS, rc);
183
184 value= memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS);
185 test_true(value == 1);
186
187 memcached_server_st *server_pool;
188 server_pool = memcached_servers_parse("10.0.1.1:11211 600,10.0.1.2:11211 300,10.0.1.3:11211 200,10.0.1.4:11211 350,10.0.1.5:11211 1000,10.0.1.6:11211 800,10.0.1.7:11211 950,10.0.1.8:11211 100");
189 memcached_server_push(memc, server_pool);
190
191 /* verify that the server list was parsed okay. */
192 test_true(memcached_server_count(memc) == 8);
193 test_strcmp(server_pool[0].hostname, "10.0.1.1");
194 test_true(server_pool[0].port == 11211);
195 test_true(server_pool[0].weight == 600);
196 test_strcmp(server_pool[2].hostname, "10.0.1.3");
197 test_true(server_pool[2].port == 11211);
198 test_true(server_pool[2].weight == 200);
199 test_strcmp(server_pool[7].hostname, "10.0.1.8");
200 test_true(server_pool[7].port == 11211);
201 test_true(server_pool[7].weight == 100);
202
203 instance= memcached_server_instance_by_position(memc, 2);
204 ((memcached_server_write_instance_st)instance)->next_retry = time(NULL) + 15;
205 memc->ketama.next_distribution_rebuild= time(NULL) - 1;
206
207 /*
208 This would not work if there were only two hosts.
209 */
210 for (ptrdiff_t x= 0; x < 99; x++)
211 {
212 memcached_autoeject(memc);
213 uint32_t server_idx= memcached_generate_hash(memc, ketama_test_cases[x].key, strlen(ketama_test_cases[x].key));
214 test_true(server_idx != 2);
215 }
216
217 /* and re-added when it's back. */
218 ((memcached_server_write_instance_st)instance)->next_retry = time(NULL) - 1;
219 memc->ketama.next_distribution_rebuild= time(NULL) - 1;
220 memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_DISTRIBUTION,
221 memc->distribution);
222 for (ptrdiff_t x= 0; x < 99; x++)
223 {
224 uint32_t server_idx = memcached_generate_hash(memc, ketama_test_cases[x].key, strlen(ketama_test_cases[x].key));
225 // We re-use instance from above.
226 instance=
227 memcached_server_instance_by_position(memc, server_idx);
228 const char *hostname = memcached_server_name(instance);
229 test_strcmp(hostname, ketama_test_cases[x].server);
230 }
231
232 memcached_server_list_free(server_pool);
233 memcached_free(memc);
234
235 return TEST_SUCCESS;
236 }
237
238 test_return_t ketama_compatibility_spymemcached(memcached_st *)
239 {
240 memcached_st *memc= memcached_create(NULL);
241 test_true(memc);
242
243 test_compare(MEMCACHED_SUCCESS,
244 memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED, 1));
245
246 test_compare(UINT64_C(1), memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED));
247
248 test_compare(MEMCACHED_SUCCESS, memcached_behavior_set_distribution(memc, MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY));
249 test_compare(MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY, memcached_behavior_get_distribution(memc));
250
251 memcached_server_st *server_pool= memcached_servers_parse("10.0.1.1:11211 600,10.0.1.2:11211 300,10.0.1.3:11211 200,10.0.1.4:11211 350,10.0.1.5:11211 1000,10.0.1.6:11211 800,10.0.1.7:11211 950,10.0.1.8:11211 100");
252 test_true(server_pool);
253 memcached_server_push(memc, server_pool);
254
255 /* verify that the server list was parsed okay. */
256 test_compare(8U, memcached_server_count(memc));
257 test_strcmp(server_pool[0].hostname, "10.0.1.1");
258 test_compare(in_port_t(11211), server_pool[0].port);
259 test_compare(600U, server_pool[0].weight);
260 test_strcmp(server_pool[2].hostname, "10.0.1.3");
261 test_compare(in_port_t(11211), server_pool[2].port);
262 test_compare(200U, server_pool[2].weight);
263 test_strcmp(server_pool[7].hostname, "10.0.1.8");
264 test_compare(in_port_t(11211), server_pool[7].port);
265 test_compare(100U, server_pool[7].weight);
266
267 /* VDEAAAAA hashes to fffcd1b5, after the last continuum point, and lets
268 * us test the boundary wraparound.
269 */
270 test_true(memcached_generate_hash(memc, (char *)"VDEAAAAA", 8) == memc->ketama.continuum[0].index);
271
272 /* verify the standard ketama set. */
273 for (uint32_t x= 0; x < 99; x++)
274 {
275 uint32_t server_idx= memcached_generate_hash(memc, ketama_test_cases_spy[x].key, strlen(ketama_test_cases_spy[x].key));
276
277 memcached_server_instance_st instance=
278 memcached_server_instance_by_position(memc, server_idx);
279
280 const char *hostname= memcached_server_name(instance);
281
282 test_strcmp(hostname, ketama_test_cases_spy[x].server);
283 }
284
285 memcached_server_list_free(server_pool);
286 memcached_free(memc);
287
288 return TEST_SUCCESS;
289 }