da3f82b3b131e0738139b63c40243d48d13eea80
[m6w6/libmemcached] / tests / libmemcached-1.0 / replication.cc
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * Libmemcached client and server library.
4 *
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following disclaimer
17 * in the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * The names of its contributors may not be used to endorse or
21 * promote products derived from this software without specific prior
22 * written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 *
36 */
37
38 #include <config.h>
39 #include <libtest/test.hpp>
40
41 using namespace libtest;
42
43 #include <libmemcached/memcached.h>
44 #include <libmemcached/server_instance.h>
45 #include <tests/replication.h>
46 #include <tests/debug.h>
47
48 #include "tests/libmemcached-1.0/setup_and_teardowns.h"
49
50 test_return_t check_replication_sanity_TEST(memcached_st *memc)
51 {
52 test_true(memc);
53 test_true(memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL));
54
55 /*
56 * Make sure that we store the item on all servers
57 * (master + replicas == number of servers)
58 */
59 test_compare(memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS), uint64_t(memcached_server_count(memc) - 1));
60
61 return TEST_SUCCESS;
62 }
63
64 test_return_t replication_set_test(memcached_st *memc)
65 {
66 memcached_return_t rc;
67 memcached_st *memc_clone= memcached_clone(NULL, memc);
68 memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 0);
69
70 test_compare(MEMCACHED_SUCCESS,
71 memcached_set(memc, "bubba", 5, "0", 1, 0, 0));
72
73 /*
74 ** We are using the quiet commands to store the replicas, so we need
75 ** to ensure that all of them are processed before we can continue.
76 ** In the test we go directly from storing the object to trying to
77 ** receive the object from all of the different servers, so we
78 ** could end up in a race condition (the memcached server hasn't yet
79 ** processed the quiet command from the replication set when it process
80 ** the request from the other client (created by the clone)). As a
81 ** workaround for that we call memcached_quit to send the quit command
82 ** to the server and wait for the response ;-) If you use the test code
83 ** as an example for your own code, please note that you shouldn't need
84 ** to do this ;-)
85 */
86 memcached_quit(memc);
87
88 /*
89 ** "bubba" should now be stored on all of our servers. We don't have an
90 ** easy to use API to address each individual server, so I'll just iterate
91 ** through a bunch of "master keys" and I should most likely hit all of the
92 ** servers...
93 */
94 for (int x= 'a'; x <= 'z'; ++x)
95 {
96 const char key[2]= { (char)x, 0 };
97 size_t len;
98 uint32_t flags;
99 char *val= memcached_get_by_key(memc_clone, key, 1, "bubba", 5,
100 &len, &flags, &rc);
101 test_compare(MEMCACHED_SUCCESS, rc);
102 test_true(val);
103 free(val);
104 }
105
106 memcached_free(memc_clone);
107
108 return TEST_SUCCESS;
109 }
110
111 test_return_t replication_get_test(memcached_st *memc)
112 {
113 memcached_return_t rc;
114
115 /*
116 * Don't do the following in your code. I am abusing the internal details
117 * within the library, and this is not a supported interface.
118 * This is to verify correct behavior in the library
119 */
120 for (uint32_t host= 0; host < memcached_server_count(memc); ++host)
121 {
122 memcached_st *memc_clone= memcached_clone(NULL, memc);
123 memcached_server_instance_st instance=
124 memcached_server_instance_by_position(memc_clone, host);
125
126 ((memcached_server_write_instance_st)instance)->port= 0;
127
128 for (int x= 'a'; x <= 'z'; ++x)
129 {
130 const char key[2]= { (char)x, 0 };
131 size_t len;
132 uint32_t flags;
133 char *val= memcached_get_by_key(memc_clone, key, 1, "bubba", 5,
134 &len, &flags, &rc);
135 test_compare(MEMCACHED_SUCCESS, rc);
136 test_true(val);
137 free(val);
138 }
139
140 memcached_free(memc_clone);
141 }
142
143 return TEST_SUCCESS;
144 }
145
146 test_return_t replication_mget_test(memcached_st *memc)
147 {
148 memcached_return_t rc;
149 memcached_st *memc_clone= memcached_clone(NULL, memc);
150 memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 0);
151
152 const char *keys[]= { "bubba", "key1", "key2", "key3" };
153 size_t len[]= { 5, 4, 4, 4 };
154
155 for (size_t x= 0; x< 4; ++x)
156 {
157 rc= memcached_set(memc, keys[x], len[x], "0", 1, 0, 0);
158 test_true(rc == MEMCACHED_SUCCESS);
159 }
160
161 /*
162 ** We are using the quiet commands to store the replicas, so we need
163 ** to ensure that all of them are processed before we can continue.
164 ** In the test we go directly from storing the object to trying to
165 ** receive the object from all of the different servers, so we
166 ** could end up in a race condition (the memcached server hasn't yet
167 ** processed the quiet command from the replication set when it process
168 ** the request from the other client (created by the clone)). As a
169 ** workaround for that we call memcached_quit to send the quit command
170 ** to the server and wait for the response ;-) If you use the test code
171 ** as an example for your own code, please note that you shouldn't need
172 ** to do this ;-)
173 */
174 memcached_quit(memc);
175
176 /*
177 * Don't do the following in your code. I am abusing the internal details
178 * within the library, and this is not a supported interface.
179 * This is to verify correct behavior in the library
180 */
181 memcached_result_st result_obj;
182 for (uint32_t host= 0; host < memcached_server_count(memc_clone); host++)
183 {
184 memcached_st *new_clone= memcached_clone(NULL, memc);
185 memcached_server_instance_st instance=
186 memcached_server_instance_by_position(new_clone, host);
187 ((memcached_server_write_instance_st)instance)->port= 0;
188
189 for (int x= 'a'; x <= 'z'; ++x)
190 {
191 char key[2]= { (char)x, 0 };
192
193 rc= memcached_mget_by_key(new_clone, key, 1, keys, len, 4);
194 test_true(rc == MEMCACHED_SUCCESS);
195
196 memcached_result_st *results= memcached_result_create(new_clone, &result_obj);
197 test_true(results);
198
199 int hits= 0;
200 while ((results= memcached_fetch_result(new_clone, &result_obj, &rc)) != NULL)
201 {
202 hits++;
203 }
204 test_compare(4, hits);
205 memcached_result_free(&result_obj);
206 }
207
208 memcached_free(new_clone);
209 }
210
211 memcached_free(memc_clone);
212
213 return TEST_SUCCESS;
214 }
215
216 test_return_t replication_randomize_mget_test(memcached_st *memc)
217 {
218 memcached_result_st result_obj;
219 memcached_return_t rc;
220 memcached_st *memc_clone= memcached_clone(NULL, memc);
221 memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 3);
222 memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ, 1);
223
224 const char *keys[]= { "key1", "key2", "key3", "key4", "key5", "key6", "key7" };
225 size_t len[]= { 4, 4, 4, 4, 4, 4, 4 };
226
227 for (size_t x= 0; x< 7; ++x)
228 {
229 rc= memcached_set(memc, keys[x], len[x], "1", 1, 0, 0);
230 test_true(rc == MEMCACHED_SUCCESS);
231 }
232
233 memcached_quit(memc);
234
235 for (size_t x= 0; x< 7; ++x)
236 {
237 const char key[2]= { (char)x, 0 };
238
239 test_compare(MEMCACHED_SUCCESS,
240 memcached_mget_by_key(memc_clone, key, 1, keys, len, 7));
241
242 memcached_result_st *results= memcached_result_create(memc_clone, &result_obj);
243 test_true(results);
244
245 int hits= 0;
246 while ((results= memcached_fetch_result(memc_clone, &result_obj, &rc)) != NULL)
247 {
248 ++hits;
249 }
250 test_compare(hits, 7);
251 memcached_result_free(&result_obj);
252 }
253 memcached_free(memc_clone);
254 return TEST_SUCCESS;
255 }
256
257 test_return_t replication_delete_test(memcached_st *memc_just_cloned)
258 {
259 memcached_flush(memc_just_cloned, 0);
260 memcached_st *memc_not_replicate= memcached_clone(NULL, memc_just_cloned);
261 memcached_st *memc_replicated= memcached_clone(NULL, memc_just_cloned);
262 const char *keys[]= { "bubba", "key1", "key2", "key3", "key4" };
263
264 test_true(memcached_behavior_get(memc_replicated, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL));
265 test_compare(MEMCACHED_SUCCESS, memcached_behavior_set(memc_replicated, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ, false));
266
267 // Make one copy
268 test_compare(MEMCACHED_SUCCESS, memcached_behavior_set(memc_replicated, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 1UL));
269 test_compare(uint64_t(1), memcached_behavior_get(memc_replicated, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS));
270
271 test_compare(MEMCACHED_SUCCESS, memcached_behavior_set(memc_not_replicate, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 0UL));
272 test_compare(uint64_t(0), memcached_behavior_get(memc_not_replicate, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS));
273
274 for (size_t x= 0; x < test_array_length(keys); ++x)
275 {
276 memcached_set(memc_replicated,
277 test_string_make_from_cstr(keys[x]), // Keys
278 test_string_make_from_cstr(keys[x]), // We use the keys as values
279 0, 0);
280 }
281
282 memcached_flush_buffers(memc_replicated);
283
284 // Confirm keys with replication read
285 test_compare(TEST_SUCCESS, confirm_keys_exist(memc_replicated, keys, test_array_length(keys), true, true));
286 test_compare(TEST_SUCCESS, confirm_keys_exist(memc_not_replicate, keys, test_array_length(keys), true, true));
287
288 /* Delete the items from all of the servers except 1, we use the non replicated memc so that we know we deleted the keys */
289 for (size_t x= 0; x < test_array_length(keys); ++x)
290 {
291 test_compare(MEMCACHED_SUCCESS,
292 memcached_delete(memc_replicated,
293 test_string_make_from_cstr(keys[x]), // Keys
294 0));
295 }
296
297 test_compare(TEST_SUCCESS, confirm_keys_dont_exist(memc_replicated, keys, test_array_length(keys)));
298 test_compare(TEST_SUCCESS, confirm_keys_dont_exist(memc_not_replicate, keys, test_array_length(keys)));
299 #if 0
300 test_zero(confirm_key_count(memc_not_replicate));
301 #endif
302
303 memcached_free(memc_not_replicate);
304 memcached_free(memc_replicated);
305
306 return TEST_SUCCESS;
307 }
308
309 test_return_t replication_randomize_mget_fail_test(memcached_st *memc)
310 {
311 memcached_st *memc_clone= memcached_clone(NULL, memc);
312 memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 3);
313
314 for (int x= int(MEMCACHED_SUCCESS); x < int(MEMCACHED_MAXIMUM_RETURN); ++x)
315 {
316 const char *key= memcached_strerror(NULL, memcached_return_t(x));
317 test_compare(MEMCACHED_SUCCESS,
318 memcached_set(memc,
319 key, strlen(key),
320 key, strlen(key), 0, 0));
321 }
322
323 memcached_flush_buffers(memc);
324
325 // We need to now cause a failure in one server, never do this in your own
326 // code.
327 close(memc_clone->servers[1].fd);
328 memc_clone->servers[1].port= 1;
329 memc_clone->servers[1].address_info_next= NULL;
330
331 for (int x= int(MEMCACHED_SUCCESS); x < int(MEMCACHED_MAXIMUM_RETURN); ++x)
332 {
333 const char *key= memcached_strerror(NULL, memcached_return_t(x));
334 memcached_return_t rc;
335 uint32_t flags;
336 size_t value_length;
337 char *value= memcached_get(memc_clone, key, strlen(key), &value_length, &flags, &rc);
338 test_true(rc == MEMCACHED_SUCCESS);
339 test_compare(strlen(key), value_length);
340 test_strcmp(key, value);
341 free(value);
342 }
343 memcached_free(memc_clone);
344 return TEST_SUCCESS;
345 }
346
347 /* Test that single miss does not cause replica reads to fail */
348 test_return_t replication_miss_test(memcached_st *memc)
349 {
350 test_skip(true, false);
351
352 memcached_st *memc_repl= memcached_clone(NULL, memc);
353 test_true(memc_repl);
354 memcached_st *memc_single= memcached_clone(NULL, memc);
355 test_true(memc_single);
356
357 const char *value = "my_value";
358 size_t vlen;
359 uint32_t flags;
360
361 /* this test makes sense only with 2 or more servers */
362 test_true(memcached_server_count(memc_repl) > 1);
363
364 /* Consistent hash */
365 test_compare(MEMCACHED_SUCCESS,
366 memcached_behavior_set_distribution(memc_repl, MEMCACHED_DISTRIBUTION_CONSISTENT));
367 test_compare(MEMCACHED_SUCCESS,
368 memcached_behavior_set_distribution(memc_single, MEMCACHED_DISTRIBUTION_CONSISTENT));
369
370 /* The first clone writes to all servers */
371 test_compare(MEMCACHED_SUCCESS,
372 memcached_behavior_set(memc_repl, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, true));
373 test_compare(MEMCACHED_SUCCESS,
374 memcached_behavior_set(memc_repl, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS,
375 memcached_server_count(memc_repl)));
376
377 /* Write to servers */
378 {
379 memcached_return_t rc= memcached_set(memc_repl,
380 test_literal_param(__func__),
381 value, strlen(value),
382 time_t(1200), uint32_t(0));
383 test_true(rc == MEMCACHED_SUCCESS or rc == MEMCACHED_BUFFERED);
384 }
385
386 /* Use the second clone to remove the key from primary server.
387 This should remove the key from only one server */
388 {
389 memcached_return_t rc= memcached_delete(memc_single,
390 test_literal_param(__func__),
391 0);
392 test_true(rc == MEMCACHED_SUCCESS or rc == MEMCACHED_BUFFERED);
393 }
394
395 /* Remove the server where the key was deleted */
396 {
397 #if 0
398 memcached_return_t rc;
399 const memcached_server_st *instance= memcached_server_by_key(memc_single,
400 test_literal_param(__func__),
401 &rc);
402 test_compare(MEMCACHED_SUCCESS, rc);
403 test_compare(MEMCACHED_SUCCESS,
404 memcached_server_remove(instance));
405 #endif
406 }
407
408 /* Test that others still have it */
409 {
410 memcached_return_t rc;
411 char *get_value= memcached_get(memc_single,
412 test_literal_param(__func__),
413 &vlen, &flags, &rc);
414 test_true(rc == MEMCACHED_SUCCESS or rc == MEMCACHED_BUFFERED);
415 test_true(get_value and strcmp(get_value, value) == 0);
416 free(get_value);
417 }
418
419 /* This read should still return the value as we have it on other servers */
420 {
421 memcached_return_t rc;
422 char *get_value= memcached_get(memc_repl,
423 test_literal_param(__func__),
424 &vlen, &flags, &rc);
425 test_true(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
426 test_true(get_value and strcmp(get_value, value) == 0);
427 free(get_value);
428 }
429
430 memcached_free(memc_repl);
431 memcached_free(memc_single);
432
433 return TEST_SUCCESS;
434 }