Apache HTTPD
testmemcache.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "testutil.h"
18#include "apr.h"
19#include "apu.h"
20#include "apr_general.h"
21#include "apr_strings.h"
22#include "apr_hash.h"
23#include "apr_memcache.h"
24#include "apr_network_io.h"
25
26#if APR_HAVE_STDLIB_H
27#include <stdlib.h> /* for exit() */
28#endif
29
30#define HOST "localhost"
31#define PORT 11211
32
33/* the total number of items to use for set/get testing */
34#define TDATA_SIZE 3000
35
36/* some smaller subset of TDATA_SIZE used for multiget testing */
37#define TDATA_SET 100
38
39/* our custom hash function just returns this all the time */
40#define HASH_FUNC_RESULT 510
41
42/* all keys will be prefixed with this */
43const char prefix[] = "testmemcache";
44
45/* text for values we store */
46const char txt[] =
47"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis at"
48"lacus in ligula hendrerit consectetuer. Vestibulum tristique odio"
49"iaculis leo. In massa arcu, ultricies a, laoreet nec, hendrerit non,"
50"neque. Nulla sagittis sapien ac risus. Morbi ligula dolor, vestibulum"
51"nec, viverra id, placerat dapibus, arcu. Curabitur egestas feugiat"
52"tellus. Donec dignissim. Nunc ante. Curabitur id lorem. In mollis"
53"tortor sit amet eros auctor dapibus. Proin nulla sem, tristique in,"
54"convallis id, iaculis feugiat cras amet.";
55
56/*
57 * this datatype is for our custom server determination function. this might
58 * be useful if you don't want to rely on simply hashing keys to determine
59 * where a key belongs, but instead want to write something fancy, or use some
60 * other kind of configuration data, i.e. a hash plus some data about a
61 * namespace, or whatever. see my_server_func, and test_memcache_user_funcs
62 * for the examples.
63 */
64typedef struct {
65 const char *someval;
68
69
70/* this could do something fancy and return some hash result.
71 * for simplicity, just return the same value, so we can test it later on.
72 * if you wanted to use some external hashing library or functions for
73 * consistent hashing, for example, this would be a good place to do it.
74 */
75static apr_uint32_t my_hash_func(void *baton, const char *data,
77{
78
79 return HASH_FUNC_RESULT;
80}
81
82/*
83 * a fancy function to determine which server to use given some kind of data
84 * and a hash value. this example actually ignores the hash value itself
85 * and pulls some number from the *baton, which is a struct that has some
86 * kind of meaningful stuff in it.
87 */
90 const apr_uint32_t hash)
91{
94
95 if(mc->ntotal == 0) {
96 return NULL;
97 }
98
99 if(mc->ntotal < mhsb->which_server) {
100 return NULL;
101 }
102
103 ms = mc->live_servers[mhsb->which_server - 1];
104
105 return ms;
106}
107
110{
111 apr_uint32_t i = 0;
112 double d = 0;
113
114 if (firsttime == 0) {
115 srand((unsigned) (getpid()));
116 firsttime = 1;
117 }
118
119 d = (double) rand() / ((double) RAND_MAX + 1);
120 i = (int) (d * (high - 0 + 1));
121
122 return i > 0 ? i : 1;
123}
124
125/*
126 * general test to make sure we can create the memcache struct and add
127 * some servers, but not more than we tell it we can add
128 */
129
130static void test_memcache_create(abts_case * tc, void *data)
131{
132 apr_pool_t *pool = p;
133 apr_status_t rv;
139
141 ABTS_ASSERT(tc, "memcache create failed", rv == APR_SUCCESS);
142
143 for (i = 1; i <= max_servers; i++) {
145
146 port = PORT + i;
147 rv =
148 apr_memcache_server_create(pool, HOST, PORT + i, 0, 1, 1, 60, &server);
149 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
150
152 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
153
155 ABTS_PTR_EQUAL(tc, server, s);
156
158 ABTS_ASSERT(tc, "server disable failed", rv == APR_SUCCESS);
159
161 ABTS_ASSERT(tc, "server enable failed", rv == APR_SUCCESS);
162
164 ABTS_ASSERT(tc, "hash failed", hash > 0);
165
167 ABTS_PTR_NOTNULL(tc, s);
168 }
169
170 rv = apr_memcache_server_create(pool, HOST, PORT, 0, 1, 1, 60, &server);
171 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
172
174 ABTS_ASSERT(tc, "server add should have failed", rv != APR_SUCCESS);
175
176}
177
178/* install our own custom hashing and server selection routines. */
179
181{
182 int i;
183
184 for (i = 0; i < TDATA_SIZE; i++) {
185 char *k, *v;
186
187 k = apr_pstrcat(p, prefix, apr_itoa(p, i), NULL);
188 v = apr_pstrndup(p, txt, randval((apr_uint32_t)strlen(txt)));
189
191 }
192
193 return i;
194}
195
197{
198 apr_pool_t *pool = p;
199 apr_status_t rv;
207
209 ABTS_ASSERT(tc, "memcache create failed", rv == APR_SUCCESS);
210
211 /* as noted above, install our custom hash function, and call
212 * apr_memcache_hash. the return value should be our predefined number,
213 * and our function just ignores the other args, for simplicity.
214 */
215 memcache->hash_func = my_hash_func;
216
217 hres = apr_memcache_hash(memcache, "whatever", sizeof("whatever") - 1);
219
220 /* add some servers */
221 for(i = 1; i <= 10; i++) {
223
224 rv = apr_memcache_server_create(pool, HOST, i, 0, 1, 1, 60, &ms);
225 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
226
228 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
229 }
230
231 /*
232 * set 'which_server' in our server_baton to find the third server
233 * which should have the same port.
234 */
235 baton->which_server = 3;
236 memcache->server_func = my_server_func;
237 memcache->server_baton = baton;
239 ABTS_ASSERT(tc, "wrong server found", found->port == baton->which_server);
240}
241
242/* test non data related commands like stats and version */
243static void test_memcache_meta(abts_case * tc, void *data)
244{
245 apr_pool_t *pool = p;
249 char *result;
250 apr_status_t rv;
251
252 rv = apr_memcache_create(pool, 1, 0, &memcache);
253 ABTS_ASSERT(tc, "memcache create failed", rv == APR_SUCCESS);
254
255 rv = apr_memcache_server_create(pool, HOST, PORT, 0, 1, 1, 60, &server);
256 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
257
259 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
260
263
266
267 ABTS_STR_NEQUAL(tc, stats->version, result, 5);
268
269 /*
270 * no way to know exactly what will be in most of these, so
271 * just make sure there is something.
272 */
273
274 ABTS_ASSERT(tc, "pid", stats->pid >= 0);
275 ABTS_ASSERT(tc, "time", stats->time >= 0);
276 /* ABTS_ASSERT(tc, "pointer_size", stats->pointer_size >= 0); */
277 ABTS_ASSERT(tc, "rusage_user", stats->rusage_user >= 0);
278 ABTS_ASSERT(tc, "rusage_system", stats->rusage_system >= 0);
279
280 ABTS_ASSERT(tc, "curr_items", stats->curr_items >= 0);
281 ABTS_ASSERT(tc, "total_items", stats->total_items >= 0);
282 ABTS_ASSERT(tc, "bytes", stats->bytes >= 0);
283
284 ABTS_ASSERT(tc, "curr_connections", stats->curr_connections >= 0);
285 ABTS_ASSERT(tc, "total_connections", stats->total_connections >= 0);
286 ABTS_ASSERT(tc, "connection_structures",
287 stats->connection_structures >= 0);
288
289 ABTS_ASSERT(tc, "cmd_get", stats->cmd_get >= 0);
290 ABTS_ASSERT(tc, "cmd_set", stats->cmd_set >= 0);
291 ABTS_ASSERT(tc, "get_hits", stats->get_hits >= 0);
292 ABTS_ASSERT(tc, "get_misses", stats->get_misses >= 0);
293
294 /* ABTS_ASSERT(tc, "evictions", stats->evictions >= 0); */
295
296 ABTS_ASSERT(tc, "bytes_read", stats->bytes_read >= 0);
297 ABTS_ASSERT(tc, "bytes_written", stats->bytes_written >= 0);
298 ABTS_ASSERT(tc, "limit_maxbytes", stats->limit_maxbytes >= 0);
299
300 /* ABTS_ASSERT(tc, "threads", stats->threads >= 0); */
301}
302
303/* test add and replace calls */
304
306{
307 apr_pool_t *pool = p;
308 apr_status_t rv;
313 char *result;
315
316 rv = apr_memcache_create(pool, 1, 0, &memcache);
317 ABTS_ASSERT(tc, "memcache create failed", rv == APR_SUCCESS);
318
319 rv = apr_memcache_server_create(pool, HOST, PORT, 0, 1, 1, 60, &server);
320 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
321
323 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
324
327
328 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) {
329 const void *k;
330 void *v;
331 const char *key;
332
333 apr_hash_this(hi, &k, NULL, &v);
334 key = k;
335
336 /* doesn't exist yet, fail */
337 rv = apr_memcache_replace(memcache, key, v, strlen(v) - 1, 0, 27);
338 ABTS_ASSERT(tc, "replace should have failed", rv != APR_SUCCESS);
339
340 /* doesn't exist yet, succeed */
341 rv = apr_memcache_add(memcache, key, v, strlen(v), 0, 27);
342 ABTS_ASSERT(tc, "add failed", rv == APR_SUCCESS);
343
344 /* exists now, succeed */
345 rv = apr_memcache_replace(memcache, key, "new", sizeof("new") - 1, 0, 27);
346 ABTS_ASSERT(tc, "replace failed", rv == APR_SUCCESS);
347
348 /* make sure its different */
350 ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS);
351 ABTS_STR_NEQUAL(tc, result, "new", 3);
352
353 /* exists now, fail */
354 rv = apr_memcache_add(memcache, key, v, strlen(v), 0, 27);
355 ABTS_ASSERT(tc, "add should have failed", rv != APR_SUCCESS);
356
357 /* clean up */
359 ABTS_ASSERT(tc, "delete failed", rv == APR_SUCCESS);
360 }
361}
362
363/* basic tests of the increment and decrement commands */
364static void test_memcache_incrdecr(abts_case * tc, void *data)
365{
366 apr_pool_t *pool = p;
367 apr_status_t rv;
370 apr_uint32_t new;
371 char *result;
374
375 rv = apr_memcache_create(pool, 1, 0, &memcache);
376 ABTS_ASSERT(tc, "memcache create failed", rv == APR_SUCCESS);
377
378 rv = apr_memcache_server_create(pool, HOST, PORT, 0, 1, 1, 60, &server);
379 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
380
382 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
383
384 rv = apr_memcache_set(memcache, prefix, "271", sizeof("271") - 1, 0, 27);
385 ABTS_ASSERT(tc, "set failed", rv == APR_SUCCESS);
386
387 for( i = 1; i <= TDATA_SIZE; i++) {
389
391 ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS);
392
393 expect = i + atoi(result);
394
395 rv = apr_memcache_incr(memcache, prefix, i, &new);
396 ABTS_ASSERT(tc, "incr failed", rv == APR_SUCCESS);
397
398 ABTS_INT_EQUAL(tc, expect, new);
399
400 rv = apr_memcache_decr(memcache, prefix, i, &new);
401 ABTS_ASSERT(tc, "decr failed", rv == APR_SUCCESS);
402 ABTS_INT_EQUAL(tc, atoi(result), new);
403
404 }
405
407 ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS);
408
409 ABTS_INT_EQUAL(tc, 271, atoi(result));
410
412 ABTS_ASSERT(tc, "delete failed", rv == APR_SUCCESS);
413}
414
415/* test the multiget functionality */
416static void test_memcache_multiget(abts_case * tc, void *data)
417{
418 apr_pool_t *pool = p;
420 apr_status_t rv;
426
427 rv = apr_memcache_create(pool, 1, 0, &memcache);
428 ABTS_ASSERT(tc, "memcache create failed", rv == APR_SUCCESS);
429
430 rv = apr_memcache_server_create(pool, HOST, PORT, 0, 1, 1, 60, &server);
431 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
432
434 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
435
438
440
441 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) {
442 const void *k;
443 void *v;
444 const char *key;
445
446 apr_hash_this(hi, &k, NULL, &v);
447 key = k;
448
449 rv = apr_memcache_set(memcache, key, v, strlen(v), 0, 27);
450 ABTS_ASSERT(tc, "set failed", rv == APR_SUCCESS);
451 }
452
454 for (i = 0; i < TDATA_SET; i++)
457 apr_itoa(pool, i), NULL),
458 &values);
459
461 tmppool,
462 pool,
463 values);
464
465 ABTS_ASSERT(tc, "multgetp failed", rv == APR_SUCCESS);
466 ABTS_ASSERT(tc, "multgetp returned too few results",
468
469 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) {
470 const void *k;
471 const char *key;
472
473 apr_hash_this(hi, &k, NULL, NULL);
474 key = k;
475
477 ABTS_ASSERT(tc, "delete failed", rv == APR_SUCCESS);
478 }
479
480}
481
482/* test setting and getting */
483
484static void test_memcache_setget(abts_case * tc, void *data)
485{
486 apr_pool_t *pool = p;
487 apr_status_t rv;
492 char *result;
494
495 rv = apr_memcache_create(pool, 1, 0, &memcache);
496 ABTS_ASSERT(tc, "memcache create failed", rv == APR_SUCCESS);
497
498 rv = apr_memcache_server_create(pool, HOST, PORT, 0, 1, 1, 60, &server);
499 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
500
502 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
503
505
507
508 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) {
509 const void *k;
510 void *v;
511 const char *key;
512
513 apr_hash_this(hi, &k, NULL, &v);
514 key = k;
515
516 rv = apr_memcache_set(memcache, key, v, strlen(v), 0, 27);
517 ABTS_ASSERT(tc, "set failed", rv == APR_SUCCESS);
519 ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS);
520 }
521
522 rv = apr_memcache_getp(memcache, pool, "nothere3423", &result, &len, NULL);
523
524 ABTS_ASSERT(tc, "get should have failed", rv != APR_SUCCESS);
525
526 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) {
527 const void *k;
528 const char *key;
529
530 apr_hash_this(hi, &k, NULL, NULL);
531 key = k;
532
534 ABTS_ASSERT(tc, "delete failed", rv == APR_SUCCESS);
535 }
536}
537
538/* use apr_socket stuff to see if there is in fact a memcached server
539 * running on PORT.
540 */
542{
543 apr_pool_t *pool = p;
544 apr_status_t rv;
547 struct iovec vec[2];
548 apr_size_t written;
549 char buf[128];
551
553 if(rv != APR_SUCCESS) {
554 return rv;
555 }
556
558 if(rv != APR_SUCCESS) {
559 return rv;
560 }
561
563 if (rv != APR_SUCCESS) {
564 return rv;
565 }
566
568 if (rv != APR_SUCCESS) {
569 return rv;
570 }
571
573 if (rv != APR_SUCCESS) {
574 return rv;
575 }
576
577 vec[0].iov_base = "version";
578 vec[0].iov_len = sizeof("version") - 1;
579
580 vec[1].iov_base = "\r\n";
581 vec[1].iov_len = sizeof("\r\n") -1;
582
583 rv = apr_socket_sendv(sock, vec, 2, &written);
584 if (rv != APR_SUCCESS) {
585 return rv;
586 }
587
588 len = sizeof(buf);
589 rv = apr_socket_recv(sock, buf, &len);
590 if(rv != APR_SUCCESS) {
591 return rv;
592 }
593
594 if(strncmp(buf, "VERSION", sizeof("VERSION")-1) != 0) {
595 rv = APR_EGENERAL;
596 }
597
599 return rv;
600}
601
603{
604 apr_status_t rv;
605 suite = ADD_SUITE(suite);
606 /* check for a running memcached on the typical port before
607 * trying to run the tests. succeed if we don't find one.
608 */
609 rv = check_mc();
610 if (rv == APR_SUCCESS) {
618 }
619 else {
620 abts_log_message("Error %d occurred attempting to reach memcached "
621 "on %s:%d. Skipping apr_memcache tests...",
622 rv, HOST, PORT);
623 }
624
625 return suite;
626}
const char apr_size_t len
Definition ap_regex.h:187
void abts_run_test(abts_suite *ts, test_func f, void *value)
Definition abts.c:175
void abts_log_message(const char *fmt,...)
Definition abts.c:270
#define ABTS_PTR_EQUAL(a, b, c)
Definition abts.h:126
#define ADD_SUITE(suite)
Definition abts.h:67
#define ABTS_PTR_NOTNULL(a, b)
Definition abts.h:125
#define ABTS_STR_NEQUAL(a, b, c, d)
Definition abts.h:124
#define ABTS_ASSERT(a, b, c)
Definition abts.h:130
#define ABTS_INT_EQUAL(a, b, c)
Definition abts.h:109
APR Miscellaneous library routines.
APR Hash Tables.
apr_memcache_version(apr_memcache_server_t *ms, apr_pool_t *p, char **baton)
apr_memcache_multgetp(apr_memcache_t *mc, apr_pool_t *temp_pool, apr_pool_t *data_pool, apr_hash_t *values)
apr_memcache_find_server_hash(apr_memcache_t *mc, const apr_uint32_t hash)
apr_memcache_add(apr_memcache_t *mc, const char *key, char *data, const apr_size_t data_size, apr_uint32_t timeout, apr_uint16_t flags)
apr_memcache_add_multget_key(apr_pool_t *data_pool, const char *key, apr_hash_t **values)
apr_memcache_incr(apr_memcache_t *mc, const char *key, apr_int32_t inc, apr_uint32_t *new_value)
apr_memcache_set(apr_memcache_t *mc, const char *key, char *data, const apr_size_t data_size, apr_uint32_t timeout, apr_uint16_t flags)
apr_memcache_delete(apr_memcache_t *mc, const char *key, apr_uint32_t timeout)
apr_memcache_decr(apr_memcache_t *mc, const char *key, apr_int32_t inc, apr_uint32_t *new_value)
apr_memcache_replace(apr_memcache_t *mc, const char *key, char *data, const apr_size_t data_size, apr_uint32_t timeout, apr_uint16_t flags)
apr_memcache_getp(apr_memcache_t *mc, apr_pool_t *p, const char *key, char **baton, apr_size_t *new_length, apr_uint16_t *flags_)
apr_memcache_stats(apr_memcache_server_t *ms, apr_pool_t *p, apr_memcache_stats_t **stats)
Client interface for memcached.
APR Network library.
#define hash(h, r, b, n)
Definition apr_random.c:51
apr_size_t const unsigned char unsigned int unsigned int d
Definition apr_siphash.h:72
APR Strings library.
return found
Definition core.c:2840
const unsigned char * buf
Definition util_md5.h:50
const char apr_port_t port
Definition http_vhost.h:125
ap_vhost_iterate_conn_cb void * baton
Definition http_vhost.h:87
#define APR_EGENERAL
Definition apr_errno.h:313
apr_memcache_t * mc
const char const apr_size_t data_len
const char apr_hash_t ** values
apr_memcache_server_t * server
apr_memcache_server_t * ms
apr_pool_t apr_memcache_stats_t ** stats
apr_size_t size
const char int apr_pool_t * pool
Definition apr_cstr.h:84
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
const char * key
const struct iovec * vec
void * data
apr_array_header_t ** result
const apr_hash_t * h
Definition apr_hash.h:97
#define APR_HASH_KEY_STRING
Definition apr_hash.h:47
apr_socket_t * sock
apr_sockaddr_t * sa
apr_uint16_t apr_port_t
#define APR_INET
#define apr_pool_create(newpool, parent)
Definition apr_pools.h:322
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
const char * s
Definition apr_strings.h:95
#define APR_USEC_PER_SEC
Definition apr_time.h:60
apr_pool_t * p
Definition md_event.c:32
#define RAND_MAX
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
static int max_servers
Definition mod_status.c:89
apr_memcache_server_t ** live_servers
apr_uint16_t ntotal
apr_uint32_t which_server
const char * someval
static int create_test_hash(apr_pool_t *p, apr_hash_t *h)
#define TDATA_SET
static void test_memcache_multiget(abts_case *tc, void *data)
#define HASH_FUNC_RESULT
static apr_uint32_t my_hash_func(void *baton, const char *data, apr_size_t data_len)
static void test_memcache_user_funcs(abts_case *tc, void *data)
#define PORT
static void test_memcache_addreplace(abts_case *tc, void *data)
static int randval(apr_uint32_t high)
const char txt[]
static void test_memcache_create(abts_case *tc, void *data)
static void test_memcache_meta(abts_case *tc, void *data)
#define HOST
static apr_status_t check_mc(void)
static apr_memcache_server_t * my_server_func(void *baton, apr_memcache_t *mc, const apr_uint32_t hash)
static void test_memcache_setget(abts_case *tc, void *data)
#define TDATA_SIZE
apr_uint16_t firsttime
static void test_memcache_incrdecr(abts_case *tc, void *data)
abts_suite * testmemcache(abts_suite *suite)
apr_status_t apr_socket_sendv(apr_socket_t *sock, const struct iovec *vec, apr_int32_t nvec, apr_size_t *len)
Definition sendrecv.c:194
apr_status_t apr_socket_recv(apr_socket_t *sock, char *buf, apr_size_t *len)
Definition sendrecv.c:70
apr_status_t apr_socket_close(apr_socket_t *thesocket)
Definition sockets.c:211
apr_status_t apr_socket_connect(apr_socket_t *sock, apr_sockaddr_t *sa)
Definition sockets.c:388
apr_status_t apr_socket_create(apr_socket_t **new, int ofamily, int type, int protocol, apr_pool_t *cont)
Definition sockets.c:116
apr_status_t apr_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t)
Definition sockopt.c:75
typedef int(WSAAPI *apr_winapi_fpt_WSAPoll)(IN OUT LPWSAPOLLFD fdArray