f1d013598e5d98f114a0508097d4687e04b730f7
[m6w6/libmemcached] / tests / libmemcached-1.0 / pool.cc
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * Libmemcached Client and Server
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 <vector>
44 #include <iostream>
45 #include <string>
46 #include <cerrno>
47
48 #include <semaphore.h>
49
50 #include <libmemcached-1.0/memcached.h>
51 #include <libmemcachedutil-1.0/util.h>
52 #include <libmemcached/is.h>
53 #include <tests/pool.h>
54
55 #include <pthread.h>
56
57 #ifndef __INTEL_COMPILER
58 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
59 #endif
60
61
62 test_return_t memcached_pool_test(memcached_st *)
63 {
64 memcached_return_t rc;
65 const char *config_string= "--SERVER=host10.example.com --SERVER=host11.example.com --SERVER=host10.example.com --POOL-MIN=10 --POOL-MAX=32";
66
67 char buffer[2048];
68 rc= libmemcached_check_configuration(config_string, sizeof(config_string) -1, buffer, sizeof(buffer));
69
70 test_true_got(rc != MEMCACHED_SUCCESS, buffer);
71
72 memcached_pool_st* pool= memcached_pool(config_string, strlen(config_string));
73 test_true_got(pool, strerror(errno));
74
75 memcached_st *memc= memcached_pool_pop(pool, false, &rc);
76
77 test_true(rc == MEMCACHED_SUCCESS);
78 test_true(memc);
79
80 /*
81 Release the memc_ptr that was pulled from the pool
82 */
83 memcached_pool_push(pool, memc);
84
85 /*
86 Destroy the pool.
87 */
88 memcached_pool_destroy(pool);
89
90 return TEST_SUCCESS;
91 }
92
93
94 #define POOL_SIZE 10
95 test_return_t connection_pool_test(memcached_st *memc)
96 {
97 memcached_pool_st* pool= memcached_pool_create(memc, 5, POOL_SIZE);
98 test_true(pool);
99 memcached_st *mmc[POOL_SIZE];
100
101 // Fill up our array that we will store the memc that are in the pool
102 for (size_t x= 0; x < POOL_SIZE; ++x)
103 {
104 memcached_return_t rc;
105 mmc[x]= memcached_pool_fetch(pool, NULL, &rc);
106 test_compare(MEMCACHED_SUCCESS, rc);
107 test_true(mmc[x]);
108 }
109
110 // All memc should be gone
111 {
112 memcached_return_t rc;
113 test_null(memcached_pool_fetch(pool, NULL, &rc));
114 test_compare(MEMCACHED_NOTFOUND, rc);
115 }
116
117 // Release them..
118 for (size_t x= 0; x < POOL_SIZE; ++x)
119 {
120 if (mmc[x])
121 {
122 test_compare(MEMCACHED_SUCCESS, memcached_pool_release(pool, mmc[x]));
123 }
124 }
125 test_true(memcached_pool_destroy(pool) == memc);
126
127 return TEST_SUCCESS;
128 }
129
130 test_return_t connection_pool2_test(memcached_st *memc)
131 {
132 memcached_pool_st* pool= memcached_pool_create(memc, 5, POOL_SIZE);
133 test_true(pool);
134 memcached_st *mmc[POOL_SIZE];
135
136 // Fill up our array that we will store the memc that are in the pool
137 for (size_t x= 0; x < POOL_SIZE; ++x)
138 {
139 memcached_return_t rc;
140 mmc[x]= memcached_pool_fetch(pool, NULL, &rc);
141 test_compare(MEMCACHED_SUCCESS, rc);
142 test_true(mmc[x]);
143 }
144
145 // All memc should be gone
146 {
147 memcached_return_t rc;
148 test_null(memcached_pool_fetch(pool, NULL, &rc));
149 test_compare(MEMCACHED_NOTFOUND, rc);
150 }
151
152 // verify that I can do ops with all connections
153 test_compare(MEMCACHED_SUCCESS,
154 memcached_set(mmc[0],
155 test_literal_param("key"),
156 "0", 1, 0, 0));
157
158 for (uint64_t x= 0; x < POOL_SIZE; ++x)
159 {
160 uint64_t number_value;
161 test_compare(MEMCACHED_SUCCESS,
162 memcached_increment(mmc[x],
163 test_literal_param("key"),
164 1, &number_value));
165 test_compare(number_value, (x+1));
166 }
167
168 // Release them..
169 for (size_t x= 0; x < POOL_SIZE; ++x)
170 {
171 test_compare(MEMCACHED_SUCCESS, memcached_pool_release(pool, mmc[x]));
172 }
173
174
175 /* verify that I can set behaviors on the pool when I don't have all
176 * of the connections in the pool. It should however be enabled
177 * when I push the item into the pool
178 */
179 mmc[0]= memcached_pool_fetch(pool, NULL, NULL);
180 test_true(mmc[0]);
181
182 test_compare(MEMCACHED_SUCCESS,
183 memcached_pool_behavior_set(pool, MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK, 9999));
184
185 {
186 memcached_return_t rc;
187 mmc[1]= memcached_pool_fetch(pool, NULL, &rc);
188 test_true(mmc[1]);
189 test_compare(MEMCACHED_SUCCESS, rc);
190 }
191
192 test_compare(UINT64_C(9999), memcached_behavior_get(mmc[1], MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK));
193 test_compare(MEMCACHED_SUCCESS, memcached_pool_release(pool, mmc[1]));
194 test_compare(MEMCACHED_SUCCESS, memcached_pool_release(pool, mmc[0]));
195
196 {
197 memcached_return_t rc;
198 mmc[0]= memcached_pool_fetch(pool, NULL, &rc);
199 test_true(mmc[0]);
200 test_compare(MEMCACHED_SUCCESS, rc);
201 }
202
203 test_compare(UINT64_C(9999), memcached_behavior_get(mmc[0], MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK));
204 test_compare(MEMCACHED_SUCCESS, memcached_pool_release(pool, mmc[0]));
205
206 test_true(memcached_pool_destroy(pool) == memc);
207
208 return TEST_SUCCESS;
209 }
210
211 struct test_pool_context_st {
212 volatile memcached_return_t rc;
213 memcached_pool_st* pool;
214 memcached_st* mmc;
215 sem_t _lock;
216
217 test_pool_context_st(memcached_pool_st *pool_arg, memcached_st *memc_arg):
218 rc(MEMCACHED_FAILURE),
219 pool(pool_arg),
220 mmc(memc_arg)
221 {
222 sem_init(&_lock, 0, 0);
223 }
224
225 void wait()
226 {
227 sem_wait(&_lock);
228 }
229
230 void release()
231 {
232 sem_post(&_lock);
233 }
234
235 ~test_pool_context_st()
236 {
237 sem_destroy(&_lock);
238 }
239 };
240
241 static void* connection_release(void *arg)
242 {
243 test_pool_context_st *resource= static_cast<test_pool_context_st *>(arg);
244 if (resource == NULL)
245 {
246 fatal_message("resource == NULL");
247 }
248
249 // Release all of the memc we are holding
250 resource->rc= memcached_pool_release(resource->pool, resource->mmc);
251 resource->release();
252
253 pthread_exit(arg);
254 }
255
256 test_return_t connection_pool3_test(memcached_st *memc)
257 {
258 #ifdef __APPLE__
259 return TEST_SKIPPED;
260 #endif
261
262 memcached_pool_st* pool= memcached_pool_create(memc, 1, 1);
263 test_true(pool);
264
265 memcached_st *pool_memc;
266 {
267 memcached_return_t rc;
268 pool_memc= memcached_pool_fetch(pool, NULL, &rc);
269 test_compare(MEMCACHED_SUCCESS, rc);
270 test_true(pool_memc);
271 }
272
273 /*
274 @note This comment was written to describe what was believed to be the original authors intent.
275
276 This portion of the test creates a thread that will wait until told to free a memcached_st
277 that will be grabbed by the main thread.
278
279 It is believed that this tests whether or not we are handling ownership correctly.
280 */
281 pthread_t tid;
282 test_pool_context_st item(pool, pool_memc);
283
284 test_zero(pthread_create(&tid, NULL, connection_release, &item));
285 item.wait();
286
287 memcached_return_t rc;
288 memcached_st *pop_memc;
289 // We do a hard loop, and try N times
290 int counter= 5;
291 do
292 {
293 struct timespec relative_time= { 0, 0 };
294 pop_memc= memcached_pool_fetch(pool, &relative_time, &rc);
295
296 if (memcached_success(rc))
297 {
298 break;
299 }
300
301 if (memcached_failed(rc))
302 {
303 test_null(pop_memc);
304 test_true(rc != MEMCACHED_TIMEOUT); // As long as relative_time is zero, MEMCACHED_TIMEOUT is invalid
305 }
306 } while (--counter);
307
308 if (memcached_failed(rc)) // Cleanup thread since we will exit once we test.
309 {
310 pthread_join(tid, NULL);
311 test_compare(MEMCACHED_SUCCESS, rc);
312 }
313
314 {
315 int pthread_ret= pthread_join(tid, NULL);
316 test_true(pthread_ret == 0 or pthread_ret == ESRCH);
317 }
318 test_compare(MEMCACHED_SUCCESS, rc);
319 test_true(pool_memc == pop_memc);
320
321 test_true(memcached_pool_destroy(pool) == memc);
322
323 return TEST_SUCCESS;
324 }
325
326 static memcached_st * create_single_instance_memcached(const memcached_st *original_memc, const char *options)
327 {
328 /*
329 If no options are given, copy over at least the binary flag.
330 */
331 char options_buffer[1024]= { 0 };
332 if (options == NULL)
333 {
334 if (memcached_is_binary(original_memc))
335 {
336 snprintf(options_buffer, sizeof(options_buffer), "--BINARY");
337 }
338 }
339
340 /*
341 * I only want to hit _one_ server so I know the number of requests I'm
342 * sending in the pipeline.
343 */
344 memcached_server_instance_st instance= memcached_server_instance_by_position(original_memc, 0);
345
346 char server_string[1024];
347 int server_string_length;
348 if (instance->type == MEMCACHED_CONNECTION_UNIX_SOCKET)
349 {
350 if (options)
351 {
352 server_string_length= snprintf(server_string, sizeof(server_string), "--SOCKET=\"%s\" %s",
353 memcached_server_name(instance), options);
354 }
355 else
356 {
357 server_string_length= snprintf(server_string, sizeof(server_string), "--SOCKET=\"%s\"",
358 memcached_server_name(instance));
359 }
360 }
361 else
362 {
363 if (options)
364 {
365 server_string_length= snprintf(server_string, sizeof(server_string), "--server=%s:%d %s",
366 memcached_server_name(instance), int(memcached_server_port(instance)),
367 options);
368 }
369 else
370 {
371 server_string_length= snprintf(server_string, sizeof(server_string), "--server=%s:%d",
372 memcached_server_name(instance), int(memcached_server_port(instance)));
373 }
374 }
375
376 if (server_string_length <= 0)
377 {
378 return NULL;
379 }
380
381 char errror_buffer[1024];
382 if (memcached_failed(libmemcached_check_configuration(server_string, server_string_length, errror_buffer, sizeof(errror_buffer))))
383 {
384 Error << "Failed to parse (" << server_string << ") " << errror_buffer;
385 return NULL;
386 }
387
388 return memcached(server_string, server_string_length);
389 }
390
391 pthread_mutex_t mutex= PTHREAD_MUTEX_INITIALIZER;
392 static bool _running= false;
393
394 static void set_running(const bool arg)
395 {
396 int error;
397 if ((error= pthread_mutex_lock(&mutex)) != 0)
398 {
399 fatal_message(strerror(error));
400 }
401
402 _running= arg;
403
404 if ((error= pthread_mutex_unlock(&mutex)) != 0)
405 {
406 fatal_message(strerror(error));
407 }
408 }
409
410 static bool running()
411 {
412 int error;
413 bool ret;
414
415 if ((error= pthread_mutex_lock(&mutex)) != 0)
416 {
417 fatal_message(strerror(error));
418 }
419
420 ret= _running;
421
422 if ((error= pthread_mutex_unlock(&mutex)) != 0)
423 {
424 fatal_message(strerror(error));
425 }
426
427 return ret;
428 }
429
430 static void *worker_thread(void *ctx)
431 {
432 memcached_pool_st *pool= (memcached_pool_st *)ctx;
433
434 while (running())
435 {
436 memcached_return_t rc;
437 memcached_st *mc= memcached_pool_pop(pool, true, &rc);
438
439 if (mc == NULL)
440 {
441 Error << "failed to fetch a connection from the pool" << memcached_strerror(NULL, rc);
442 dream(1, 0);
443 continue;
444 }
445
446 rc= memcached_set(mc, "test:kv", 7, "value", 5, 600, 0);
447 if (memcached_failed(rc))
448 {
449 Out << "failed memcached_set()";
450 }
451
452 rc= memcached_pool_push(pool, mc);
453 if (memcached_failed(rc))
454 {
455 Error << "failed to release a connection to the pool" << memcached_strerror(NULL, rc);
456 }
457 }
458
459 return NULL;
460 }
461
462 static void sig_handler(int sig)
463 {
464 switch (sig)
465 {
466 case SIGPROF:
467 set_running(false);
468 break;
469
470 break;
471
472 default:
473 Error << __func__ << " caught unknown signal";
474 break;
475 }
476 }
477
478 #define NUM_THREADS 20
479 test_return_t regression_bug_962815(memcached_st *memc)
480 {
481 test_skip_valgrind();
482
483 pthread_t pid[NUM_THREADS];
484
485 test_false(running());
486
487 memcached_st *master = create_single_instance_memcached(memc, 0);
488 test_true(master);
489
490 memcached_pool_st *pool= memcached_pool_create(master, 5, 10);
491
492 test_true(pool);
493
494 set_running(true);
495
496 struct itimerval new_value;
497 struct itimerval old_value;
498 memset(&new_value, 0, sizeof(itimerval));
499 memset(&old_value, 0, sizeof(itimerval));
500
501 new_value.it_interval.tv_sec=0;
502 new_value.it_interval.tv_usec=10*1000;
503 new_value.it_value.tv_sec=0;
504 new_value.it_value.tv_usec=10*1000;
505
506 test_true(signal(SIGPROF, sig_handler) != SIG_ERR);
507 test_compare(0, setitimer(ITIMER_PROF, &new_value, &old_value));
508
509 for (size_t x=0; x < NUM_THREADS; x++)
510 {
511 test_compare(0, pthread_create(&pid[x], NULL, worker_thread, (void*)pool));
512 }
513
514 for (size_t x=0; x < NUM_THREADS; x++)
515 {
516 test_compare(0, pthread_join(pid[x], NULL));
517 }
518
519 if (pool)
520 {
521 memcached_pool_destroy(pool);
522 }
523
524 if (master)
525 {
526 memcached_free(master);
527 }
528 test_true(signal(SIGPROF, SIG_DFL) != SIG_ERR);
529 test_compare(0, setitimer(ITIMER_PROF, &old_value, NULL));
530
531 return TEST_SUCCESS;
532 }