From 76034ad60dfd991232ac3053c07f7a72de234e43 Mon Sep 17 00:00:00 2001 From: Razvan Crainea Date: Wed, 13 Jan 2016 12:17:55 +0200 Subject: [PATCH] redis: add timeout for connect and query This prevents OpenSIPS from blocking waiting for a connection to be established. Fixes #753 reported by Nick Altmann --- modules/cachedb_redis/README | 47 +++++++-- modules/cachedb_redis/cachedb_redis.c | 2 + modules/cachedb_redis/cachedb_redis_dbase.c | 48 +++++++-- modules/cachedb_redis/cachedb_redis_dbase.h | 6 ++ .../cachedb_redis/doc/cachedb_redis_admin.xml | 99 ++++++++++++++----- 5 files changed, 157 insertions(+), 45 deletions(-) diff --git a/modules/cachedb_redis/README b/modules/cachedb_redis/README index 1bf31bfb31..0f12dac602 100644 --- a/modules/cachedb_redis/README +++ b/modules/cachedb_redis/README @@ -8,7 +8,7 @@ Edited by Vladut-Stefan Paiu - Copyright © 2011 www.opensips-solutions.com + Copyright © 2011 www.opensips-solutions.com __________________________________________________________ Table of Contents @@ -26,14 +26,19 @@ Vladut-Stefan Paiu 1.5. Exported Parameters 1.5.1. cachedb_url (string) - 1.5.2. Exported Functions - 1.5.3. Raw Query Syntax + 1.5.2. connect_timeout (integer) + 1.5.3. query_timeout (integer) + + 1.6. Exported Functions + 1.7. Raw Query Syntax List of Examples 1.1. Set cachedb_url parameter 1.2. Use Redis servers - 1.3. Redis Raw Query Examples + 1.3. Set connect_timeout parameter + 1.4. Set connect_timeout parameter + 1.5. Redis Raw Query Examples Chapter 1. Admin Guide @@ -94,9 +99,9 @@ Chapter 1. Admin Guide Example 1.1. Set cachedb_url parameter ... modparam("cachedb_redis", "cachedb_url","redis:group1://localhost:6379/" -); +) modparam("cachedb_redis", "cachedb_url","redis:cluster1://random_url:888 -8/"); +8/") ... Example 1.2. Use Redis servers @@ -106,12 +111,36 @@ cache_fetch("redis:cluster1","key",$avp(10)); cache_remove("redis:cluster1","key"); ... -1.5.2. Exported Functions +1.5.2. connect_timeout (integer) + + This parameter specifies how many milliseconds OpenSIPS should + wait for connecting to a Redis node. + Default value is "5000 ms". + + Example 1.3. Set connect_timeout parameter +... +# wait 1 seconds for Redis to connect +modparam("cachedb_redis", "connect_timeout",1000) +... + +1.5.3. query_timeout (integer) + + This parameter specifies how many milliseconds OpenSIPS should + wait for a query response from a Redis node. + Default value is "5000 ms". + + Example 1.4. Set connect_timeout parameter +... +# wait 1 seconds for Redis queries +modparam("cachedb_redis", "query_timeout",1000) +... + +1.6. Exported Functions The module does not export functions to be used in configuration script. -1.5.3. Raw Query Syntax +1.7. Raw Query Syntax The cachedb_redis module allows to run RAW queries, thus taking full advantage of the capabilities of the back-end. The query @@ -119,7 +148,7 @@ cache_remove("redis:cluster1","key"); Here are a couple examples of running some Redis queries : - Example 1.3. Redis Raw Query Examples + Example 1.5. Redis Raw Query Examples ... $var(my_hash) = "my_hash_name"; $var(my_key) = "my_key_name"; diff --git a/modules/cachedb_redis/cachedb_redis.c b/modules/cachedb_redis/cachedb_redis.c index f9afe04491..b4efeaafd1 100644 --- a/modules/cachedb_redis/cachedb_redis.c +++ b/modules/cachedb_redis/cachedb_redis.c @@ -53,6 +53,8 @@ int set_connection(unsigned int type, void *val) } static param_export_t params[]={ + { "connect_timeout", INT_PARAM, &redis_connnection_tout}, + { "query_timeout", INT_PARAM, &redis_query_tout }, { "cachedb_url", STR_PARAM|USE_FUNC_PARAM, (void *)&set_connection}, {0,0,0} }; diff --git a/modules/cachedb_redis/cachedb_redis_dbase.c b/modules/cachedb_redis/cachedb_redis_dbase.c index 1a09560469..161dcfa4de 100644 --- a/modules/cachedb_redis/cachedb_redis_dbase.c +++ b/modules/cachedb_redis/cachedb_redis_dbase.c @@ -33,16 +33,48 @@ #include #include +int redis_query_tout = CACHEDB_REDIS_DEFAULT_TIMEOUT; +int redis_connnection_tout = CACHEDB_REDIS_DEFAULT_TIMEOUT; + +redisContext *redis_get_ctx(char *ip, int port) +{ + struct timeval tv; + static char warned = 0; + redisContext *ctx; + + if (!redis_connnection_tout) { + if (!warned++) + LM_WARN("Connecting to redis without timeout might block your server\n"); + ctx = redisConnect(ip,port); + } else { + tv.tv_sec = redis_connnection_tout / 1000; + tv.tv_usec = (redis_connnection_tout * 1000) % 1000000; + ctx = redisConnectWithTimeout(ip,port,tv); + } + if (ctx && ctx->err != REDIS_OK) { + LM_ERR("failed to open redis connection %s:%hu - %s\n",ip, + port,ctx->errstr); + return NULL; + } + + if (redis_query_tout) { + tv.tv_sec = redis_query_tout / 1000; + tv.tv_usec = (redis_query_tout * 1000) % 1000000; + if (redisSetTimeout(ctx, tv) != REDIS_OK) { + LM_ERR("Cannot set query timeout to %dms\n", redis_query_tout); + return NULL; + } + } + return ctx; +} + int redis_connect_node(redis_con *con,cluster_node *node) { redisReply *rpl; - node->context = redisConnect(node->ip,node->port); - if (node->context->err != REDIS_OK) { - LM_ERR("failed to open redis connection %s:%hu - %s\n",node->ip, - node->port,node->context->errstr); + node->context = redis_get_ctx(node->ip,node->port); + if (!node->context) return -1; - } if (con->id->password) { rpl = redisCommand(node->context,"AUTH %s",con->id->password); @@ -93,11 +125,9 @@ int redis_connect(redis_con *con) int len; /* connect to redis DB */ - ctx = redisConnect(con->id->host,con->id->port); - if (ctx->err != REDIS_OK) { - LM_ERR("failed to open redis connection - %s\n",ctx->errstr); + ctx = redis_get_ctx(con->id->host,con->id->port); + if (!ctx) return -1; - } /* auth using password, if any */ if (con->id->password) { diff --git a/modules/cachedb_redis/cachedb_redis_dbase.h b/modules/cachedb_redis/cachedb_redis_dbase.h index ff0590842b..11e1e270bb 100644 --- a/modules/cachedb_redis/cachedb_redis_dbase.h +++ b/modules/cachedb_redis/cachedb_redis_dbase.h @@ -39,6 +39,12 @@ typedef struct cluster_nodes { struct cluster_nodes *next; } cluster_node; + +#define CACHEDB_REDIS_DEFAULT_TIMEOUT 5000 + +extern int redis_query_tout; +extern int redis_connnection_tout; + #define REDIS_SINGLE_INSTANCE (1<<0) #define REDIS_CLUSTER_INSTANCE (1<<1) typedef struct { diff --git a/modules/cachedb_redis/doc/cachedb_redis_admin.xml b/modules/cachedb_redis/doc/cachedb_redis_admin.xml index 9ff67bd342..29e179981e 100644 --- a/modules/cachedb_redis/doc/cachedb_redis_admin.xml +++ b/modules/cachedb_redis/doc/cachedb_redis_admin.xml @@ -1,13 +1,13 @@ - + &adminguide; - +
Overview - This module is an implementation of a cache system designed to work with a + This module is an implementation of a cache system designed to work with a Redis server. It uses hiredis client library to connect to either a single Redis server instance, or to a Redis Server inside a Redis Cluster. It uses the Key-Value interface exported from the core. @@ -66,8 +66,8 @@
Limitations - - + + @@ -90,7 +90,7 @@ None.
- +
External Libraries or Applications @@ -102,7 +102,7 @@ hiredis: - + On the latest Debian based distributions, hiredis can be installed by running 'apt-get install libhiredis-dev' @@ -111,12 +111,12 @@ hiredis can be downloaded from: https://github.com/antirez/hiredis . Download the archive, extract sources, run make,sudo make install. - +
- +
Exported Parameters
@@ -128,35 +128,80 @@ The prefix part of the URL will be the identifier that will be used from the script. - + Set <varname>cachedb_url</varname> parameter ... -modparam("cachedb_redis", "cachedb_url","redis:group1://localhost:6379/"); -modparam("cachedb_redis", "cachedb_url","redis:cluster1://random_url:8888/"); +modparam("cachedb_redis", "cachedb_url","redis:group1://localhost:6379/") +modparam("cachedb_redis", "cachedb_url","redis:cluster1://random_url:8888/") ... - - - - - Use Redis servers - + + + + + Use Redis servers + ... cache_store("redis:group1","key","$ru value"); cache_fetch("redis:cluster1","key",$avp(10)); cache_remove("redis:cluster1","key"); ... - + + +
+ +
+ <varname>connect_timeout</varname> (integer) + + This parameter specifies how many milliseconds &osips; should wait + for connecting to a Redis node. + + + Default value is 5000 ms. + + + + Set <varname>connect_timeout</varname> parameter + +... +# wait 1 seconds for Redis to connect +modparam("cachedb_redis", "connect_timeout",1000) +... + + +
+ +
+ <varname>query_timeout</varname> (integer) + + This parameter specifies how many milliseconds &osips; should wait + for a query response from a Redis node. + + + Default value is 5000 ms. + + + + Set <varname>connect_timeout</varname> parameter + +... +# wait 1 seconds for Redis queries +modparam("cachedb_redis", "query_timeout",1000) +... + + + +
- +
Exported Functions The module does not export functions to be used in configuration script. -
+
Raw Query Syntax @@ -175,22 +220,22 @@ cache_remove("redis:cluster1","key"); $var(my_hash) = "my_hash_name"; $var(my_key) = "my_key_name"; $var(my_value) = "my_key_value"; - cache_raw_query("redis","HSET $var(my_hash) $var(my_key) $var(my_value)"); + cache_raw_query("redis","HSET $var(my_hash) $var(my_key) $var(my_value)"); cache_raw_query("redis","HGET $var(my_hash) $var(my_key)","$avp(result)"); - xlog("We have fetched $avp(result) \n"); + xlog("We have fetched $avp(result) \n"); ... $var(my_hash) = "my_hash_name"; $var(my_key1) = "my_key1_name"; $var(my_key2) = "my_key2_name"; $var(my_value1) = "my_key1_value"; $var(my_value2) = "my_key2_value"; - cache_raw_query("redis","HSET $var(my_hash) $var(my_key1) $var(my_value1)"); - cache_raw_query("redis","HSET $var(my_hash) $var(my_key2) $var(my_value2)"); + cache_raw_query("redis","HSET $var(my_hash) $var(my_key1) $var(my_value1)"); + cache_raw_query("redis","HSET $var(my_hash) $var(my_key2) $var(my_value2)"); cache_raw_query("redis","HGETALL $var(my_hash)","$avp(result)"); $var(it) = 0; while ($(avp(result_final)[$var(it)]) != NULL) { - xlog("Multiple key reply: - we have fetched $(avp(result_final)[$var(it)]) \n"); + xlog("Multiple key reply: - we have fetched $(avp(result_final)[$var(it)]) \n"); $var(it) = $var(it) + 1; } ... @@ -199,7 +244,7 @@ cache_remove("redis:cluster1","key");
- +