fix warning
[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 "mem_config.h"
39 #include "libtest/test.hpp"
40
41 using namespace libtest;
42
43 #include "libmemcached-1.0/memcached.h"
44
45 #include "libmemcached/server_instance.h"
46
47 #include "tests/replication.h"
48 #include "tests/debug.h"
49
50 #include "tests/libmemcached-1.0/setup_and_teardowns.h"
51
52 test_return_t check_replication_sanity_TEST(memcached_st *memc)
53 {
54 test_true(memc);
55 test_compare(uint64_t(1),
56 memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL));
57
58 /*
59 * Make sure that we store the item on all servers
60 * (master + replicas == number of servers)
61 */
62 test_compare(memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS), uint64_t(memcached_server_count(memc) - 1));
63
64 return TEST_SUCCESS;
65 }
66
67 test_return_t replication_set_test(memcached_st *memc)
68 {
69 memcached_st *memc_clone= memcached_clone(NULL, memc);
70 test_true(memc_clone);
71 test_compare(MEMCACHED_SUCCESS,
72 memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 0));
73
74 test_compare(MEMCACHED_SUCCESS,
75 memcached_set(memc, "bubba", 5, "0", 1, 0, 0));
76
77 /*
78 ** We are using the quiet commands to store the replicas, so we need
79 ** to ensure that all of them are processed before we can continue.
80 ** In the test we go directly from storing the object to trying to
81 ** receive the object from all of the different servers, so we
82 ** could end up in a race condition (the memcached server hasn't yet
83 ** processed the quiet command from the replication set when it process
84 ** the request from the other client (created by the clone)). As a
85 ** workaround for that we call memcached_quit to send the quit command
86 ** to the server and wait for the response ;-) If you use the test code
87 ** as an example for your own code, please note that you shouldn't need
88 ** to do this ;-)
89 */
90 memcached_quit(memc);
91
92 /*
93 ** "bubba" should now be stored on all of our servers. We don't have an
94 ** easy to use API to address each individual server, so I'll just iterate
95 ** through a bunch of "master keys" and I should most likely hit all of the
96 ** servers...
97 */
98 for (int x= 'a'; x <= 'z'; ++x)
99 {
100 const char key[2]= { (char)x, 0 };
101 size_t len;
102 uint32_t flags;
103 memcached_return_t rc;
104 char *val= memcached_get_by_key(memc_clone, key, 1, "bubba", 5,
105 &len, &flags, &rc);
106 test_compare(MEMCACHED_SUCCESS, rc);
107 test_true(val);
108 free(val);
109 }
110
111 memcached_free(memc_clone);
112
113 return TEST_SUCCESS;
114 }
115
116 #include "libmemcached/instance.hpp"
117
118 test_return_t replication_get_test(memcached_st *memc)
119 {
120
121 /*
122 * Don't do the following in your code. I am abusing the internal details
123 * within the library, and this is not a supported interface.
124 * This is to verify correct behavior in the library
125 */
126 for (uint32_t host= 0; host < memcached_server_count(memc); ++host)
127 {
128 memcached_st *memc_clone= memcached_clone(NULL, memc);
129 memcached_instance_st* instance= (memcached_instance_st*)memcached_server_instance_by_position(memc_clone, host);
130
131 instance->port(0);
132
133 for (int x= 'a'; x <= 'z'; ++x)
134 {
135 const char key[2]= { (char)x, 0 };
136 size_t len;
137 uint32_t flags;
138 memcached_return_t rc;
139 char *val= memcached_get_by_key(memc_clone, key, 1, "bubba", 5,
140 &len, &flags, &rc);
141 test_compare(MEMCACHED_SUCCESS, rc);
142 test_true(val);
143 free(val);
144 }
145
146 memcached_free(memc_clone);
147 }
148
149 return TEST_SUCCESS;
150 }
151
152 test_return_t replication_mget_test(memcached_st *memc)
153 {
154 memcached_st *memc_clone= memcached_clone(NULL, memc);
155 test_true(memc_clone);
156 test_compare(MEMCACHED_SUCCESS,
157 memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 0));
158
159 const char *keys[]= { "bubba", "key1", "key2", "key3" };
160 size_t len[]= { 5, 4, 4, 4 };
161
162 for (size_t x= 0; x< 4; ++x)
163 {
164 test_compare(MEMCACHED_SUCCESS, memcached_set(memc, keys[x], len[x], "0", 1, 0, 0));
165 }
166
167 /*
168 ** We are using the quiet commands to store the replicas, so we need
169 ** to ensure that all of them are processed before we can continue.
170 ** In the test we go directly from storing the object to trying to
171 ** receive the object from all of the different servers, so we
172 ** could end up in a race condition (the memcached server hasn't yet
173 ** processed the quiet command from the replication set when it process
174 ** the request from the other client (created by the clone)). As a
175 ** workaround for that we call memcached_quit to send the quit command
176 ** to the server and wait for the response ;-) If you use the test code
177 ** as an example for your own code, please note that you shouldn't need
178 ** to do this ;-)
179 */
180 memcached_quit(memc);
181
182 /*
183 * Don't do the following in your code. I am abusing the internal details
184 * within the library, and this is not a supported interface.
185 * This is to verify correct behavior in the library
186 */
187 memcached_result_st result_obj;
188 for (uint32_t host= 0; host < memcached_server_count(memc_clone); host++)
189 {
190 memcached_st *new_clone= memcached_clone(NULL, memc);
191 const memcached_instance_st * instance= memcached_server_instance_by_position(new_clone, host);
192 ((memcached_server_write_instance_st)instance)->port(0);
193
194 for (int x= 'a'; x <= 'z'; ++x)
195 {
196 char key[2]= { (char)x, 0 };
197
198 test_compare(MEMCACHED_SUCCESS,
199 memcached_mget_by_key(new_clone, key, 1, keys, len, 4));
200
201 memcached_result_st *results= memcached_result_create(new_clone, &result_obj);
202 test_true(results);
203
204 int hits= 0;
205 memcached_return_t rc;
206 while ((results= memcached_fetch_result(new_clone, &result_obj, &rc)) != NULL)
207 {
208 hits++;
209 }
210 test_compare(4, hits);
211 memcached_result_free(&result_obj);
212 }
213
214 memcached_free(new_clone);
215 }
216
217 memcached_free(memc_clone);
218
219 return TEST_SUCCESS;
220 }
221
222 test_return_t replication_randomize_mget_test(memcached_st *memc)
223 {
224 memcached_result_st result_obj;
225 memcached_st *memc_clone= memcached_clone(NULL, memc);
226 memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 3);
227 memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ, 1);
228
229 const char *keys[]= { "key1", "key2", "key3", "key4", "key5", "key6", "key7" };
230 size_t len[]= { 4, 4, 4, 4, 4, 4, 4 };
231
232 for (size_t x= 0; x< 7; ++x)
233 {
234 test_compare(MEMCACHED_SUCCESS,
235 memcached_set(memc, keys[x], len[x], "1", 1, 0, 0));
236 }
237
238 memcached_quit(memc);
239
240 for (size_t x= 0; x< 7; ++x)
241 {
242 const char key[2]= { (char)x, 0 };
243
244 test_compare(MEMCACHED_SUCCESS,
245 memcached_mget_by_key(memc_clone, key, 1, keys, len, 7));
246
247 memcached_result_st *results= memcached_result_create(memc_clone, &result_obj);
248 test_true(results);
249
250 int hits= 0;
251 memcached_return_t rc;
252 while ((results= memcached_fetch_result(memc_clone, &result_obj, &rc)) != NULL)
253 {
254 ++hits;
255 }
256 test_compare(hits, 7);
257 memcached_result_free(&result_obj);
258 }
259 memcached_free(memc_clone);
260
261 return TEST_SUCCESS;
262 }
263
264 test_return_t replication_delete_test(memcached_st *memc_just_cloned)
265 {
266 memcached_flush(memc_just_cloned, 0);
267 memcached_st *memc_not_replicate= memcached_clone(NULL, memc_just_cloned);
268 memcached_st *memc_replicated= memcached_clone(NULL, memc_just_cloned);
269 const char *keys[]= { "bubba", "key1", "key2", "key3", "key4" };
270
271 test_compare(uint64_t(1), memcached_behavior_get(memc_replicated, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL));
272 test_compare(MEMCACHED_SUCCESS, memcached_behavior_set(memc_replicated, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ, false));
273
274 // Make one copy
275 test_compare(MEMCACHED_SUCCESS, memcached_behavior_set(memc_replicated, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 1UL));
276 test_compare(uint64_t(1), memcached_behavior_get(memc_replicated, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS));
277
278 test_compare(MEMCACHED_SUCCESS, memcached_behavior_set(memc_not_replicate, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 0UL));
279 test_compare(uint64_t(0), memcached_behavior_get(memc_not_replicate, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS));
280
281 for (size_t x= 0; x < test_array_length(keys); ++x)
282 {
283 memcached_set(memc_replicated,
284 test_string_make_from_cstr(keys[x]), // Keys
285 test_string_make_from_cstr(keys[x]), // We use the keys as values
286 0, 0);
287 }
288
289 memcached_flush_buffers(memc_replicated);
290
291 // Confirm keys with replication read
292 test_compare(TEST_SUCCESS, confirm_keys_exist(memc_replicated, keys, test_array_length(keys), true, true));
293 test_compare(TEST_SUCCESS, confirm_keys_exist(memc_not_replicate, keys, test_array_length(keys), true, true));
294
295 /* Delete the items from all of the servers except 1, we use the non replicated memc so that we know we deleted the keys */
296 for (size_t x= 0; x < test_array_length(keys); ++x)
297 {
298 memcached_return_t del_rc= memcached_delete(memc_replicated,
299 test_string_make_from_cstr(keys[x]), // Keys
300 0);
301 if (del_rc == MEMCACHED_SUCCESS or del_rc == MEMCACHED_NOTFOUND)
302 { }
303 else
304 {
305 test_compare(MEMCACHED_SUCCESS, del_rc);
306 }
307 }
308
309 test_compare(TEST_SUCCESS, confirm_keys_dont_exist(memc_replicated, keys, test_array_length(keys)));
310 test_compare(TEST_SUCCESS, confirm_keys_dont_exist(memc_not_replicate, keys, test_array_length(keys)));
311 #if 0
312 test_zero(confirm_key_count(memc_not_replicate));
313 #endif
314
315 memcached_free(memc_not_replicate);
316 memcached_free(memc_replicated);
317
318 return TEST_SUCCESS;
319 }
320
321 test_return_t replication_randomize_mget_fail_test(memcached_st *memc)
322 {
323 memcached_st *memc_clone= memcached_clone(NULL, memc);
324 memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 3);
325
326 for (int x= int(MEMCACHED_SUCCESS); x < int(MEMCACHED_MAXIMUM_RETURN); ++x)
327 {
328 const char *key= memcached_strerror(NULL, memcached_return_t(x));
329 test_compare(MEMCACHED_SUCCESS,
330 memcached_set(memc,
331 key, strlen(key),
332 key, strlen(key), 0, 0));
333 }
334
335 memcached_flush_buffers(memc);
336
337 // We need to now cause a failure in one server, never do this in your own
338 // code.
339 close(memc_clone->servers[1].fd);
340 memc_clone->servers[1].port(1);
341 memc_clone->servers[1].address_info_next= NULL;
342
343 for (int x= int(MEMCACHED_SUCCESS); x < int(MEMCACHED_MAXIMUM_RETURN); ++x)
344 {
345 const char *key= memcached_strerror(NULL, memcached_return_t(x));
346 uint32_t flags;
347 size_t value_length;
348 memcached_return_t rc;
349 char *value= memcached_get(memc_clone, key, strlen(key), &value_length, &flags, &rc);
350 test_compare(MEMCACHED_SUCCESS, rc);
351 test_compare(strlen(key), value_length);
352 test_strcmp(key, value);
353 free(value);
354 }
355 memcached_free(memc_clone);
356 return TEST_SUCCESS;
357 }
358
359 /* Test that single miss does not cause replica reads to fail */
360 test_return_t replication_miss_test(memcached_st *memc)
361 {
362 test_skip(true, false);
363
364 memcached_st *memc_repl= memcached_clone(NULL, memc);
365 test_true(memc_repl);
366 memcached_st *memc_single= memcached_clone(NULL, memc);
367 test_true(memc_single);
368
369 const char *value = "my_value";
370 size_t vlen;
371 uint32_t flags;
372
373 /* this test makes sense only with 2 or more servers */
374 test_true(memcached_server_count(memc_repl) > 1);
375
376 /* Consistent hash */
377 test_compare(MEMCACHED_SUCCESS,
378 memcached_behavior_set_distribution(memc_repl, MEMCACHED_DISTRIBUTION_CONSISTENT));
379 test_compare(MEMCACHED_SUCCESS,
380 memcached_behavior_set_distribution(memc_single, MEMCACHED_DISTRIBUTION_CONSISTENT));
381
382 /* The first clone writes to all servers */
383 test_compare(MEMCACHED_SUCCESS,
384 memcached_behavior_set(memc_repl, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, true));
385 test_compare(MEMCACHED_SUCCESS,
386 memcached_behavior_set(memc_repl, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS,
387 memcached_server_count(memc_repl)));
388
389 /* Write to servers */
390 {
391 memcached_return_t rc= memcached_set(memc_repl,
392 test_literal_param(__func__),
393 value, strlen(value),
394 time_t(1200), uint32_t(0));
395 test_true(rc == MEMCACHED_SUCCESS or rc == MEMCACHED_BUFFERED);
396 }
397
398 /* Use the second clone to remove the key from primary server.
399 This should remove the key from only one server */
400 {
401 memcached_return_t rc= memcached_delete(memc_single,
402 test_literal_param(__func__),
403 0);
404 test_true(rc == MEMCACHED_SUCCESS or rc == MEMCACHED_BUFFERED);
405 }
406
407 /* Remove the server where the key was deleted */
408 {
409 #if 0
410 memcached_return_t rc;
411 const memcached_server_st *instance= memcached_server_by_key(memc_single,
412 test_literal_param(__func__),
413 &rc);
414 test_compare(MEMCACHED_SUCCESS, rc);
415 test_compare(MEMCACHED_SUCCESS,
416 memcached_server_remove(instance));
417 #endif
418 }
419
420 /* Test that others still have it */
421 {
422 memcached_return_t rc;
423 char *get_value= memcached_get(memc_single,
424 test_literal_param(__func__),
425 &vlen, &flags, &rc);
426 test_true(rc == MEMCACHED_SUCCESS or rc == MEMCACHED_BUFFERED);
427 test_true(get_value and strcmp(get_value, value) == 0);
428 free(get_value);
429 }
430
431 /* This read should still return the value as we have it on other servers */
432 {
433 memcached_return_t rc;
434 char *get_value= memcached_get(memc_repl,
435 test_literal_param(__func__),
436 &vlen, &flags, &rc);
437 test_true(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
438 test_true(get_value and strcmp(get_value, value) == 0);
439 free(get_value);
440 }
441
442 memcached_free(memc_repl);
443 memcached_free(memc_single);
444
445 return TEST_SUCCESS;
446 }