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 
check_replication_sanity_TEST(memcached_st * memc)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 
replication_set_test(memcached_st * memc)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 
replication_get_test(memcached_st * memc)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 
replication_mget_test(memcached_st * memc)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 
replication_randomize_mget_test(memcached_st * memc)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 
replication_delete_test(memcached_st * memc_just_cloned)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 
replication_randomize_mget_fail_test(memcached_st * memc)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 */
replication_miss_test(memcached_st * memc)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 }
447