Merge in all of libtest updates.
[m6w6/libmemcached] / tests / 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/common.h>
44 #include <tests/replication.h>
45 #include <tests/debug.h>
46
47 test_return_t replication_set_test(memcached_st *memc)
48 {
49 memcached_return_t rc;
50 memcached_st *memc_clone= memcached_clone(NULL, memc);
51 memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 0);
52
53 test_compare(MEMCACHED_SUCCESS,
54 memcached_set(memc, "bubba", 5, "0", 1, 0, 0));
55
56 /*
57 ** We are using the quiet commands to store the replicas, so we need
58 ** to ensure that all of them are processed before we can continue.
59 ** In the test we go directly from storing the object to trying to
60 ** receive the object from all of the different servers, so we
61 ** could end up in a race condition (the memcached server hasn't yet
62 ** processed the quiet command from the replication set when it process
63 ** the request from the other client (created by the clone)). As a
64 ** workaround for that we call memcached_quit to send the quit command
65 ** to the server and wait for the response ;-) If you use the test code
66 ** as an example for your own code, please note that you shouldn't need
67 ** to do this ;-)
68 */
69 memcached_quit(memc);
70
71 /*
72 ** "bubba" should now be stored on all of our servers. We don't have an
73 ** easy to use API to address each individual server, so I'll just iterate
74 ** through a bunch of "master keys" and I should most likely hit all of the
75 ** servers...
76 */
77 for (int x= 'a'; x <= 'z'; ++x)
78 {
79 const char key[2]= { (char)x, 0 };
80 size_t len;
81 uint32_t flags;
82 char *val= memcached_get_by_key(memc_clone, key, 1, "bubba", 5,
83 &len, &flags, &rc);
84 test_compare(MEMCACHED_SUCCESS, rc);
85 test_true(val);
86 free(val);
87 }
88
89 memcached_free(memc_clone);
90
91 return TEST_SUCCESS;
92 }
93
94 test_return_t replication_get_test(memcached_st *memc)
95 {
96 memcached_return_t rc;
97
98 /*
99 * Don't do the following in your code. I am abusing the internal details
100 * within the library, and this is not a supported interface.
101 * This is to verify correct behavior in the library
102 */
103 for (uint32_t host= 0; host < memcached_server_count(memc); ++host)
104 {
105 memcached_st *memc_clone= memcached_clone(NULL, memc);
106 memcached_server_instance_st instance=
107 memcached_server_instance_by_position(memc_clone, host);
108
109 ((memcached_server_write_instance_st)instance)->port= 0;
110
111 for (int x= 'a'; x <= 'z'; ++x)
112 {
113 const char key[2]= { (char)x, 0 };
114 size_t len;
115 uint32_t flags;
116 char *val= memcached_get_by_key(memc_clone, key, 1, "bubba", 5,
117 &len, &flags, &rc);
118 test_true(rc == MEMCACHED_SUCCESS);
119 test_true(val != NULL);
120 free(val);
121 }
122
123 memcached_free(memc_clone);
124 }
125
126 return TEST_SUCCESS;
127 }
128
129 test_return_t replication_mget_test(memcached_st *memc)
130 {
131 memcached_return_t rc;
132 memcached_st *memc_clone= memcached_clone(NULL, memc);
133 memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 0);
134
135 const char *keys[]= { "bubba", "key1", "key2", "key3" };
136 size_t len[]= { 5, 4, 4, 4 };
137
138 for (size_t x= 0; x< 4; ++x)
139 {
140 rc= memcached_set(memc, keys[x], len[x], "0", 1, 0, 0);
141 test_true(rc == MEMCACHED_SUCCESS);
142 }
143
144 /*
145 ** We are using the quiet commands to store the replicas, so we need
146 ** to ensure that all of them are processed before we can continue.
147 ** In the test we go directly from storing the object to trying to
148 ** receive the object from all of the different servers, so we
149 ** could end up in a race condition (the memcached server hasn't yet
150 ** processed the quiet command from the replication set when it process
151 ** the request from the other client (created by the clone)). As a
152 ** workaround for that we call memcached_quit to send the quit command
153 ** to the server and wait for the response ;-) If you use the test code
154 ** as an example for your own code, please note that you shouldn't need
155 ** to do this ;-)
156 */
157 memcached_quit(memc);
158
159 /*
160 * Don't do the following in your code. I am abusing the internal details
161 * within the library, and this is not a supported interface.
162 * This is to verify correct behavior in the library
163 */
164 memcached_result_st result_obj;
165 for (uint32_t host= 0; host < memcached_server_count(memc_clone); host++)
166 {
167 memcached_st *new_clone= memcached_clone(NULL, memc);
168 memcached_server_instance_st instance=
169 memcached_server_instance_by_position(new_clone, host);
170 ((memcached_server_write_instance_st)instance)->port= 0;
171
172 for (int x= 'a'; x <= 'z'; ++x)
173 {
174 char key[2]= { (char)x, 0 };
175
176 rc= memcached_mget_by_key(new_clone, key, 1, keys, len, 4);
177 test_true(rc == MEMCACHED_SUCCESS);
178
179 memcached_result_st *results= memcached_result_create(new_clone, &result_obj);
180 test_true(results);
181
182 int hits= 0;
183 while ((results= memcached_fetch_result(new_clone, &result_obj, &rc)) != NULL)
184 {
185 hits++;
186 }
187 test_true(hits == 4);
188 memcached_result_free(&result_obj);
189 }
190
191 memcached_free(new_clone);
192 }
193
194 memcached_free(memc_clone);
195
196 return TEST_SUCCESS;
197 }
198
199 test_return_t replication_randomize_mget_test(memcached_st *memc)
200 {
201 memcached_result_st result_obj;
202 memcached_return_t rc;
203 memcached_st *memc_clone= memcached_clone(NULL, memc);
204 memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 3);
205 memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ, 1);
206
207 const char *keys[]= { "key1", "key2", "key3", "key4", "key5", "key6", "key7" };
208 size_t len[]= { 4, 4, 4, 4, 4, 4, 4 };
209
210 for (size_t x= 0; x< 7; ++x)
211 {
212 rc= memcached_set(memc, keys[x], len[x], "1", 1, 0, 0);
213 test_true(rc == MEMCACHED_SUCCESS);
214 }
215
216 memcached_quit(memc);
217
218 for (size_t x= 0; x< 7; ++x)
219 {
220 const char key[2]= { (char)x, 0 };
221
222 test_compare(MEMCACHED_SUCCESS,
223 memcached_mget_by_key(memc_clone, key, 1, keys, len, 7));
224
225 memcached_result_st *results= memcached_result_create(memc_clone, &result_obj);
226 test_true(results);
227
228 int hits= 0;
229 while ((results= memcached_fetch_result(memc_clone, &result_obj, &rc)) != NULL)
230 {
231 ++hits;
232 }
233 test_compare(hits, 7);
234 memcached_result_free(&result_obj);
235 }
236 memcached_free(memc_clone);
237 return TEST_SUCCESS;
238 }
239
240 test_return_t replication_delete_test(memcached_st *memc_just_cloned)
241 {
242 memcached_flush(memc_just_cloned, 0);
243 memcached_st *memc_not_replicate= memcached_clone(NULL, memc_just_cloned);
244 memcached_st *memc_replicated= memcached_clone(NULL, memc_just_cloned);
245 const char *keys[]= { "bubba", "key1", "key2", "key3", "key4" };
246
247 test_true(memcached_behavior_get(memc_replicated, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL));
248 test_compare(MEMCACHED_SUCCESS, memcached_behavior_set(memc_replicated, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ, false));
249
250 // Make one copy
251 test_compare(MEMCACHED_SUCCESS, memcached_behavior_set(memc_replicated, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 1UL));
252 test_compare(1UL, memcached_behavior_get(memc_replicated, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS));
253
254 test_compare(MEMCACHED_SUCCESS, memcached_behavior_set(memc_not_replicate, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 0UL));
255 test_compare(0UL, memcached_behavior_get(memc_not_replicate, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS));
256
257 for (size_t x= 0; x < test_array_length(keys); ++x)
258 {
259 memcached_set(memc_replicated,
260 test_string_make_from_cstr(keys[x]), // Keys
261 test_string_make_from_cstr(keys[x]), // We use the keys as values
262 0, 0);
263 }
264
265 memcached_flush_buffers(memc_replicated);
266
267 // Confirm keys with replication read
268 test_compare(TEST_SUCCESS, confirm_keys_exist(memc_replicated, keys, test_array_length(keys), true));
269 test_compare(TEST_SUCCESS, confirm_keys_exist(memc_not_replicate, keys, test_array_length(keys), true));
270
271 /* Delete the items from all of the servers except 1, we use the non replicated memc so that we know we deleted the keys */
272 for (size_t x= 0; x < test_array_length(keys); ++x)
273 {
274 test_compare(MEMCACHED_SUCCESS,
275 memcached_delete(memc_replicated,
276 test_string_make_from_cstr(keys[x]), // Keys
277 0));
278 }
279
280 test_compare(TEST_SUCCESS, confirm_keys_dont_exist(memc_replicated, keys, test_array_length(keys)));
281 test_compare(TEST_SUCCESS, confirm_keys_dont_exist(memc_not_replicate, keys, test_array_length(keys)));
282 #if 0
283 test_zero(confirm_key_count(memc_not_replicate));
284 #endif
285
286 memcached_free(memc_not_replicate);
287 memcached_free(memc_replicated);
288
289 return TEST_SUCCESS;
290 }
291
292 test_return_t replication_randomize_mget_fail_test(memcached_st *memc)
293 {
294 memcached_st *memc_clone= memcached_clone(NULL, memc);
295 memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 3);
296
297 for (int x= int(MEMCACHED_SUCCESS); x < int(MEMCACHED_MAXIMUM_RETURN); ++x)
298 {
299 const char *key= memcached_strerror(NULL, memcached_return_t(x));
300 test_compare(MEMCACHED_SUCCESS,
301 memcached_set(memc,
302 key, strlen(key),
303 key, strlen(key), 0, 0));
304 }
305
306 memcached_flush_buffers(memc);
307
308 // We need to now cause a failure in one server, never do this in your own
309 // code.
310 close(memc_clone->servers[1].fd);
311 memc_clone->servers[1].port= 1;
312 memc_clone->servers[1].address_info_next= NULL;
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 memcached_return_t rc;
318 uint32_t flags;
319 size_t value_length;
320 char *value= memcached_get(memc_clone, key, strlen(key), &value_length, &flags, &rc);
321 test_true(rc == MEMCACHED_SUCCESS);
322 test_compare(strlen(key), value_length);
323 test_strcmp(key, value);
324 free(value);
325 }
326 memcached_free(memc_clone);
327 return TEST_SUCCESS;
328 }