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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 #include "apr_general.h"
21 #include "apr_strings.h"
23 #include "apr_redis.h"
24 #include "apr_network_io.h"
28 #include <stdlib.h> /* for exit() */
31 #define HOST "localhost"
34 /* the total number of items to use for set/get testing */
35 #define TDATA_SIZE 3000
37 /* some smaller subset of TDATA_SIZE used for multiget testing */
40 /* our custom hash function just returns this all the time */
41 #define HASH_FUNC_RESULT 510
43 /* all keys will be prefixed with this */
44 static const char prefix[] = "testredis";
46 /* text for values we store */
47 static const char txt[] =
48 "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis at"
49 "lacus in ligula hendrerit consectetuer. Vestibulum tristique odio"
50 "iaculis leo. In massa arcu, ultricies a, laoreet nec, hendrerit non,"
51 "neque. Nulla sagittis sapien ac risus. Morbi ligula dolor, vestibulum"
52 "nec, viverra id, placerat dapibus, arcu. Curabitur egestas feugiat"
53 "tellus. Donec dignissim. Nunc ante. Curabitur id lorem. In mollis"
54 "tortor sit amet eros auctor dapibus. Proin nulla sem, tristique in,"
55 "convallis id, iaculis feugiat cras amet.";
58 * this datatype is for our custom server determination function. this might
59 * be useful if you don't want to rely on simply hashing keys to determine
60 * where a key belongs, but instead want to write something fancy, or use some
61 * other kind of configuration data, i.e. a hash plus some data about a
62 * namespace, or whatever. see my_server_func, and test_redis_user_funcs
67 apr_uint32_t which_server;
68 } my_hash_server_baton;
71 /* this could do something fancy and return some hash result.
72 * for simplicity, just return the same value, so we can test it later on.
73 * if you wanted to use some external hashing library or functions for
74 * consistent hashing, for example, this would be a good place to do it.
76 static apr_uint32_t my_hash_func(void *baton, const char *data,
80 return HASH_FUNC_RESULT;
84 * a fancy function to determine which server to use given some kind of data
85 * and a hash value. this example actually ignores the hash value itself
86 * and pulls some number from the *baton, which is a struct that has some
87 * kind of meaningful stuff in it.
89 static apr_redis_server_t *my_server_func(void *baton,
91 const apr_uint32_t hash)
93 apr_redis_server_t *ms = NULL;
94 my_hash_server_baton *mhsb = (my_hash_server_baton *)baton;
100 if(mc->ntotal < mhsb->which_server) {
104 ms = mc->live_servers[mhsb->which_server - 1];
109 static apr_uint16_t firsttime = 0;
110 static int randval(apr_uint32_t high)
115 if (firsttime == 0) {
116 srand((unsigned) (getpid()));
120 d = (double) rand() / ((double) RAND_MAX + 1);
121 i = (int) (d * (high - 0 + 1));
123 return i > 0 ? i : 1;
127 * general test to make sure we can create the redis struct and add
128 * some servers, but not more than we tell it we can add
131 static void test_redis_create(abts_case * tc, void *data)
133 apr_pool_t *pool = p;
136 apr_redis_server_t *server, *s;
137 apr_uint32_t max_servers = 10;
141 rv = apr_redis_create(pool, max_servers, 0, &redis);
142 ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
144 for (i = 1; i <= max_servers; i++) {
149 apr_redis_server_create(pool, HOST, PORT + i, 0, 1, 1, 60, 60, &server);
150 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
152 rv = apr_redis_add_server(redis, server);
153 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
155 s = apr_redis_find_server(redis, HOST, port);
156 ABTS_PTR_EQUAL(tc, server, s);
158 rv = apr_redis_disable_server(redis, s);
159 ABTS_ASSERT(tc, "server disable failed", rv == APR_SUCCESS);
161 rv = apr_redis_enable_server(redis, s);
162 ABTS_ASSERT(tc, "server enable failed", rv == APR_SUCCESS);
164 hash = apr_redis_hash(redis, prefix, strlen(prefix));
165 ABTS_ASSERT(tc, "hash failed", hash > 0);
167 s = apr_redis_find_server_hash(redis, hash);
168 ABTS_PTR_NOTNULL(tc, s);
171 rv = apr_redis_server_create(pool, HOST, PORT, 0, 1, 1, 60, 60, &server);
172 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
174 rv = apr_redis_add_server(redis, server);
175 ABTS_ASSERT(tc, "server add should have failed", rv != APR_SUCCESS);
179 /* install our own custom hashing and server selection routines. */
181 static int create_test_hash(apr_pool_t *p, apr_hash_t *h)
185 for (i = 0; i < TDATA_SIZE; i++) {
188 k = apr_pstrcat(p, prefix, apr_itoa(p, i), NULL);
189 v = apr_pstrndup(p, txt, randval((apr_uint32_t)strlen(txt)));
191 apr_hash_set(h, k, APR_HASH_KEY_STRING, v);
197 static void test_redis_user_funcs(abts_case * tc, void *data)
199 apr_pool_t *pool = p;
202 apr_redis_server_t *found;
203 apr_uint32_t max_servers = 10;
206 my_hash_server_baton *baton =
207 apr_pcalloc(pool, sizeof(my_hash_server_baton));
209 rv = apr_redis_create(pool, max_servers, 0, &redis);
210 ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
212 /* as noted above, install our custom hash function, and call
213 * apr_redis_hash. the return value should be our predefined number,
214 * and our function just ignores the other args, for simplicity.
216 redis->hash_func = my_hash_func;
218 hres = apr_redis_hash(redis, "whatever", sizeof("whatever") - 1);
219 ABTS_INT_EQUAL(tc, HASH_FUNC_RESULT, hres);
221 /* add some servers */
222 for(i = 1; i <= 10; i++) {
223 apr_redis_server_t *ms;
225 rv = apr_redis_server_create(pool, HOST, i, 0, 1, 1, 60, 60, &ms);
226 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
228 rv = apr_redis_add_server(redis, ms);
229 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
233 * set 'which_server' in our server_baton to find the third server
234 * which should have the same port.
236 baton->which_server = 3;
237 redis->server_func = my_server_func;
238 redis->server_baton = baton;
239 found = apr_redis_find_server_hash(redis, 0);
240 ABTS_ASSERT(tc, "wrong server found", found->port == baton->which_server);
243 /* test non data related commands like stats and version */
244 static void test_redis_meta(abts_case * tc, void *data)
246 apr_pool_t *pool = p;
248 apr_redis_server_t *server;
249 apr_redis_stats_t *stats;
253 rv = apr_redis_create(pool, 1, 0, &redis);
254 ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
256 rv = apr_redis_server_create(pool, HOST, PORT, 0, 1, 1, 60, 60, &server);
257 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
259 rv = apr_redis_add_server(redis, server);
260 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
262 rv = apr_redis_version(server, pool, &result);
263 ABTS_PTR_NOTNULL(tc, result);
265 rv = apr_redis_stats(server, p, &stats);
266 ABTS_PTR_NOTNULL(tc, stats);
269 * no way to know exactly what will be in most of these, so
270 * just make sure there is something.
272 ABTS_ASSERT(tc, "major", stats->major >= 1);
273 ABTS_ASSERT(tc, "minor", stats->minor >= 0);
274 ABTS_ASSERT(tc, "patch", stats->patch >= 0);
275 ABTS_ASSERT(tc, "process_id", stats->process_id >= 0);
276 ABTS_ASSERT(tc, "uptime_in_seconds", stats->uptime_in_seconds >= 0);
277 ABTS_ASSERT(tc, "arch_bits", stats->arch_bits >= 0);
278 ABTS_ASSERT(tc, "connected_clients", stats->connected_clients >= 0);
279 ABTS_ASSERT(tc, "blocked_clients", stats->blocked_clients >= 0);
280 ABTS_ASSERT(tc, "maxmemory", stats->maxmemory >= 0);
281 ABTS_ASSERT(tc, "used_memory", stats->used_memory >= 0);
282 ABTS_ASSERT(tc, "total_system_memory", stats->total_system_memory >= 0);
283 ABTS_ASSERT(tc, "total_connections_received", stats->total_connections_received >= 0);
284 ABTS_ASSERT(tc, "total_commands_processed", stats->total_commands_processed >= 0);
285 ABTS_ASSERT(tc, "total_net_input_bytes", stats->total_net_input_bytes >= 0);
286 ABTS_ASSERT(tc, "total_net_output_bytes", stats->total_net_output_bytes >= 0);
287 ABTS_ASSERT(tc, "keyspace_hits", stats->keyspace_hits >= 0);
288 ABTS_ASSERT(tc, "keyspace_misses", stats->keyspace_misses >= 0);
289 ABTS_ASSERT(tc, "role", stats->role >= 0);
290 ABTS_ASSERT(tc, "connected_slaves", stats->connected_slaves >= 0);
291 ABTS_ASSERT(tc, "used_cpu_sys", stats->used_cpu_sys >= 0);
292 ABTS_ASSERT(tc, "used_cpu_user", stats->used_cpu_user >= 0);
293 ABTS_ASSERT(tc, "cluster_enabled", stats->cluster_enabled >= 0);
297 /* basic tests of the increment and decrement commands */
298 static void test_redis_incrdecr(abts_case * tc, void *data)
300 apr_pool_t *pool = p;
303 apr_redis_server_t *server;
309 rv = apr_redis_create(pool, 1, 0, &redis);
310 ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
312 rv = apr_redis_server_create(pool, HOST, PORT, 0, 1, 1, 60, 60, &server);
313 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
315 rv = apr_redis_add_server(redis, server);
316 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
318 rv = apr_redis_set(redis, prefix, "271", sizeof("271") - 1, 27);
319 ABTS_ASSERT(tc, "set failed", rv == APR_SUCCESS);
321 for( i = 1; i <= TDATA_SIZE; i++) {
324 rv = apr_redis_getp(redis, pool, prefix, &result, &len, NULL);
325 ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS);
327 expect = i + atoi(result);
329 rv = apr_redis_incr(redis, prefix, i, &new);
330 ABTS_ASSERT(tc, "incr failed", rv == APR_SUCCESS);
332 ABTS_INT_EQUAL(tc, expect, new);
334 rv = apr_redis_decr(redis, prefix, i, &new);
335 ABTS_ASSERT(tc, "decr failed", rv == APR_SUCCESS);
337 ABTS_INT_EQUAL(tc, atoi(result), new);
341 rv = apr_redis_getp(redis, pool, prefix, &result, &len, NULL);
342 ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS);
344 ABTS_INT_EQUAL(tc, 271, atoi(result));
346 rv = apr_redis_delete(redis, prefix, 0);
347 ABTS_ASSERT(tc, "delete failed", rv == APR_SUCCESS);
351 /* test setting and getting */
353 static void test_redis_setget(abts_case * tc, void *data)
355 apr_pool_t *pool = p;
358 apr_redis_server_t *server;
360 apr_hash_index_t *hi;
364 rv = apr_redis_create(pool, 1, 0, &redis);
365 ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
367 rv = apr_redis_server_create(pool, HOST, PORT, 0, 1, 1, 60, 60, &server);
368 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
370 rv = apr_redis_add_server(redis, server);
371 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
373 tdata = apr_hash_make(pool);
375 create_test_hash(pool, tdata);
377 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) {
382 apr_hash_this(hi, &k, NULL, &v);
385 rv = apr_redis_set(redis, key, v, strlen(v), 27);
386 ABTS_ASSERT(tc, "set failed", rv == APR_SUCCESS);
387 rv = apr_redis_getp(redis, pool, key, &result, &len, NULL);
388 ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS);
391 rv = apr_redis_getp(redis, pool, "nothere3423", &result, &len, NULL);
393 ABTS_ASSERT(tc, "get should have failed", rv != APR_SUCCESS);
395 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) {
399 apr_hash_this(hi, &k, NULL, NULL);
402 rv = apr_redis_delete(redis, key, 0);
403 ABTS_ASSERT(tc, "delete failed", rv == APR_SUCCESS);
407 /* test setting and getting */
409 static void test_redis_setexget(abts_case * tc, void *data)
411 apr_pool_t *pool = p;
414 apr_redis_server_t *server;
416 apr_hash_index_t *hi;
420 rv = apr_redis_create(pool, 1, 0, &redis);
421 ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
423 rv = apr_redis_server_create(pool, HOST, PORT, 0, 1, 1, 60, 60, &server);
424 ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
426 rv = apr_redis_add_server(redis, server);
427 ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
429 tdata = apr_hash_make(pool);
431 create_test_hash(pool, tdata);
433 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) {
438 apr_hash_this(hi, &k, NULL, &v);
441 rv = apr_redis_ping(server);
442 ABTS_ASSERT(tc, "ping failed", rv == APR_SUCCESS);
443 rv = apr_redis_setex(redis, key, v, strlen(v), 10, 27);
444 ABTS_ASSERT(tc, "set failed", rv == APR_SUCCESS);
445 rv = apr_redis_getp(redis, pool, key, &result, &len, NULL);
446 ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS);
449 rv = apr_redis_getp(redis, pool, "nothere3423", &result, &len, NULL);
451 ABTS_ASSERT(tc, "get should have failed", rv != APR_SUCCESS);
453 for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) {
457 apr_hash_this(hi, &k, NULL, NULL);
460 rv = apr_redis_delete(redis, key, 0);
461 ABTS_ASSERT(tc, "delete failed", rv == APR_SUCCESS);
465 /* use apr_socket stuff to see if there is in fact a Redis server
468 static apr_status_t check_redis(void)
470 apr_pool_t *pool = p;
472 apr_socket_t *sock = NULL;
479 rv = apr_socket_create(&sock, APR_INET, SOCK_STREAM, 0, pool);
480 if(rv != APR_SUCCESS) {
484 rv = apr_sockaddr_info_get(&sa, HOST, APR_INET, PORT, 0, pool);
485 if(rv != APR_SUCCESS) {
489 rv = apr_socket_timeout_set(sock, 1 * APR_USEC_PER_SEC);
490 if (rv != APR_SUCCESS) {
494 rv = apr_socket_connect(sock, sa);
495 if (rv != APR_SUCCESS) {
499 rv = apr_socket_timeout_set(sock, -1);
500 if (rv != APR_SUCCESS) {
504 vec[0].iov_base = "PING";
505 vec[0].iov_len = sizeof("PING") - 1;
507 vec[1].iov_base = "\r\n";
508 vec[1].iov_len = sizeof("\r\n") -1;
510 rv = apr_socket_sendv(sock, vec, 2, &written);
511 if (rv != APR_SUCCESS) {
516 rv = apr_socket_recv(sock, buf, &len);
517 if(rv != APR_SUCCESS) {
520 if(strncmp(buf, "+PONG", sizeof("+PONG")-1) != 0) {
524 apr_socket_close(sock);
528 abts_suite *testredis(abts_suite * suite)
531 suite = ADD_SUITE(suite);
532 /* check for a running redis on the typical port before
533 * trying to run the tests. succeed if we don't find one.
536 if (rv == APR_SUCCESS) {
537 abts_run_test(suite, test_redis_create, NULL);
538 abts_run_test(suite, test_redis_user_funcs, NULL);
539 abts_run_test(suite, test_redis_meta, NULL);
540 abts_run_test(suite, test_redis_setget, NULL);
541 abts_run_test(suite, test_redis_setexget, NULL);
542 /* abts_run_test(suite, test_redis_multiget, NULL); */
543 abts_run_test(suite, test_redis_incrdecr, NULL);
546 abts_log_message("Error %d occurred attempting to reach Redis "
547 "on %s:%d. Skipping apr_redis tests...",