1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3 * Libmemcached client and server library.
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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
20 * * The names of its contributors may not be used to endorse or
21 * promote products derived from this software without specific prior
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.
39 #include <libtest/test.hpp>
41 using namespace libtest
;
43 #include <libmemcached/memcached.h>
44 #include <libmemcached/server_instance.h>
45 #include <tests/replication.h>
46 #include <tests/debug.h>
48 #include "tests/libmemcached-1.0/setup_and_teardowns.h"
50 test_return_t
check_replication_sanity_TEST(memcached_st
*memc
)
53 test_true(memcached_behavior_get(memc
, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL
));
56 * Make sure that we store the item on all servers
57 * (master + replicas == number of servers)
59 test_compare(memcached_behavior_get(memc
, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS
), uint64_t(memcached_server_count(memc
) - 1));
64 test_return_t
replication_set_test(memcached_st
*memc
)
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);
70 test_compare(MEMCACHED_SUCCESS
,
71 memcached_set(memc
, "bubba", 5, "0", 1, 0, 0));
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
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
94 for (int x
= 'a'; x
<= 'z'; ++x
)
96 const char key
[2]= { (char)x
, 0 };
99 char *val
= memcached_get_by_key(memc_clone
, key
, 1, "bubba", 5,
101 test_compare(MEMCACHED_SUCCESS
, rc
);
106 memcached_free(memc_clone
);
111 test_return_t
replication_get_test(memcached_st
*memc
)
113 memcached_return_t rc
;
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
120 for (uint32_t host
= 0; host
< memcached_server_count(memc
); ++host
)
122 memcached_st
*memc_clone
= memcached_clone(NULL
, memc
);
123 memcached_server_instance_st instance
=
124 memcached_server_instance_by_position(memc_clone
, host
);
126 ((memcached_server_write_instance_st
)instance
)->port
= 0;
128 for (int x
= 'a'; x
<= 'z'; ++x
)
130 const char key
[2]= { (char)x
, 0 };
133 char *val
= memcached_get_by_key(memc_clone
, key
, 1, "bubba", 5,
135 test_compare(MEMCACHED_SUCCESS
, rc
);
140 memcached_free(memc_clone
);
146 test_return_t
replication_mget_test(memcached_st
*memc
)
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);
152 const char *keys
[]= { "bubba", "key1", "key2", "key3" };
153 size_t len
[]= { 5, 4, 4, 4 };
155 for (size_t x
= 0; x
< 4; ++x
)
157 rc
= memcached_set(memc
, keys
[x
], len
[x
], "0", 1, 0, 0);
158 test_true(rc
== MEMCACHED_SUCCESS
);
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
174 memcached_quit(memc
);
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
181 memcached_result_st result_obj
;
182 for (uint32_t host
= 0; host
< memcached_server_count(memc_clone
); host
++)
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;
189 for (int x
= 'a'; x
<= 'z'; ++x
)
191 char key
[2]= { (char)x
, 0 };
193 rc
= memcached_mget_by_key(new_clone
, key
, 1, keys
, len
, 4);
194 test_true(rc
== MEMCACHED_SUCCESS
);
196 memcached_result_st
*results
= memcached_result_create(new_clone
, &result_obj
);
200 while ((results
= memcached_fetch_result(new_clone
, &result_obj
, &rc
)) != NULL
)
204 test_compare(4, hits
);
205 memcached_result_free(&result_obj
);
208 memcached_free(new_clone
);
211 memcached_free(memc_clone
);
216 test_return_t
replication_randomize_mget_test(memcached_st
*memc
)
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);
224 const char *keys
[]= { "key1", "key2", "key3", "key4", "key5", "key6", "key7" };
225 size_t len
[]= { 4, 4, 4, 4, 4, 4, 4 };
227 for (size_t x
= 0; x
< 7; ++x
)
229 rc
= memcached_set(memc
, keys
[x
], len
[x
], "1", 1, 0, 0);
230 test_true(rc
== MEMCACHED_SUCCESS
);
233 memcached_quit(memc
);
235 for (size_t x
= 0; x
< 7; ++x
)
237 const char key
[2]= { (char)x
, 0 };
239 test_compare(MEMCACHED_SUCCESS
,
240 memcached_mget_by_key(memc_clone
, key
, 1, keys
, len
, 7));
242 memcached_result_st
*results
= memcached_result_create(memc_clone
, &result_obj
);
246 while ((results
= memcached_fetch_result(memc_clone
, &result_obj
, &rc
)) != NULL
)
250 test_compare(hits
, 7);
251 memcached_result_free(&result_obj
);
253 memcached_free(memc_clone
);
257 test_return_t
replication_delete_test(memcached_st
*memc_just_cloned
)
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" };
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));
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
));
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
));
274 for (size_t x
= 0; x
< test_array_length(keys
); ++x
)
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
282 memcached_flush_buffers(memc_replicated
);
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));
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
)
291 test_compare(MEMCACHED_SUCCESS
,
292 memcached_delete(memc_replicated
,
293 test_string_make_from_cstr(keys
[x
]), // Keys
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
)));
300 test_zero(confirm_key_count(memc_not_replicate
));
303 memcached_free(memc_not_replicate
);
304 memcached_free(memc_replicated
);
309 test_return_t
replication_randomize_mget_fail_test(memcached_st
*memc
)
311 memcached_st
*memc_clone
= memcached_clone(NULL
, memc
);
312 memcached_behavior_set(memc_clone
, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS
, 3);
314 for (int x
= int(MEMCACHED_SUCCESS
); x
< int(MEMCACHED_MAXIMUM_RETURN
); ++x
)
316 const char *key
= memcached_strerror(NULL
, memcached_return_t(x
));
317 test_compare(MEMCACHED_SUCCESS
,
320 key
, strlen(key
), 0, 0));
323 memcached_flush_buffers(memc
);
325 // We need to now cause a failure in one server, never do this in your own
327 close(memc_clone
->servers
[1].fd
);
328 memc_clone
->servers
[1].port
= 1;
329 memc_clone
->servers
[1].address_info_next
= NULL
;
331 for (int x
= int(MEMCACHED_SUCCESS
); x
< int(MEMCACHED_MAXIMUM_RETURN
); ++x
)
333 const char *key
= memcached_strerror(NULL
, memcached_return_t(x
));
334 memcached_return_t rc
;
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
);
343 memcached_free(memc_clone
);
347 /* Test that single miss does not cause replica reads to fail */
348 test_return_t
replication_miss_test(memcached_st
*memc
)
350 test_skip(true, false);
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
);
357 const char *value
= "my_value";
361 /* this test makes sense only with 2 or more servers */
362 test_true(memcached_server_count(memc_repl
) > 1);
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
));
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
)));
377 /* Write to servers */
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
);
386 /* Use the second clone to remove the key from primary server.
387 This should remove the key from only one server */
389 memcached_return_t rc
= memcached_delete(memc_single
,
390 test_literal_param(__func__
),
392 test_true(rc
== MEMCACHED_SUCCESS
or rc
== MEMCACHED_BUFFERED
);
395 /* Remove the server where the key was deleted */
398 memcached_return_t rc
;
399 const memcached_server_st
*instance
= memcached_server_by_key(memc_single
,
400 test_literal_param(__func__
),
402 test_compare(MEMCACHED_SUCCESS
, rc
);
403 test_compare(MEMCACHED_SUCCESS
,
404 memcached_server_remove(instance
));
408 /* Test that others still have it */
410 memcached_return_t rc
;
411 char *get_value
= memcached_get(memc_single
,
412 test_literal_param(__func__
),
414 test_true(rc
== MEMCACHED_SUCCESS
or rc
== MEMCACHED_BUFFERED
);
415 test_true(get_value
and strcmp(get_value
, value
) == 0);
419 /* This read should still return the value as we have it on other servers */
421 memcached_return_t rc
;
422 char *get_value
= memcached_get(memc_repl
,
423 test_literal_param(__func__
),
425 test_true(rc
== MEMCACHED_SUCCESS
|| rc
== MEMCACHED_BUFFERED
);
426 test_true(get_value
and strcmp(get_value
, value
) == 0);
430 memcached_free(memc_repl
);
431 memcached_free(memc_single
);