diff --git a/cachedb/redis.c b/cachedb/redis.c index 6cc975901..68c033535 100644 --- a/cachedb/redis.c +++ b/cachedb/redis.c @@ -58,7 +58,8 @@ struct redis_moddata { int server_port; /* server's TCP port */ const char* server_path; /* server's unix path, or "", NULL if unused */ const char* server_password; /* server's AUTH password, or "", NULL if unused */ - struct timeval timeout; /* timeout for connection setup and commands */ + struct timeval command_timeout; /* timeout for commands */ + struct timeval connect_timeout; /* timeout for connect */ int logical_db; /* the redis logical database to use */ }; @@ -88,10 +89,10 @@ redis_connect(const struct redis_moddata* moddata) if(moddata->server_path && moddata->server_path[0]!=0) { ctx = redisConnectUnixWithTimeout(moddata->server_path, - moddata->timeout); + moddata->connect_timeout); } else { ctx = redisConnectWithTimeout(moddata->server_host, - moddata->server_port, moddata->timeout); + moddata->server_port, moddata->connect_timeout); } if(!ctx || ctx->err) { const char *errstr = "out of memory"; @@ -100,7 +101,7 @@ redis_connect(const struct redis_moddata* moddata) log_err("failed to connect to redis server: %s", errstr); goto fail; } - if(redisSetTimeout(ctx, moddata->timeout) != REDIS_OK) { + if(redisSetTimeout(ctx, moddata->command_timeout) != REDIS_OK) { log_err("failed to set redis timeout"); goto fail; } @@ -159,8 +160,24 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env) moddata->server_port = env->cfg->redis_server_port; moddata->server_path = env->cfg->redis_server_path; moddata->server_password = env->cfg->redis_server_password; - moddata->timeout.tv_sec = env->cfg->redis_timeout / 1000; - moddata->timeout.tv_usec = (env->cfg->redis_timeout % 1000) * 1000; + moddata->command_timeout.tv_sec = env->cfg->redis_timeout / 1000; + moddata->command_timeout.tv_usec = + (env->cfg->redis_timeout % 1000) * 1000; + moddata->connect_timeout.tv_sec = env->cfg->redis_timeout / 1000; + moddata->connect_timeout.tv_usec = + (env->cfg->redis_timeout % 1000) * 1000; + if(env->cfg->redis_command_timeout != 0) { + moddata->command_timeout.tv_sec = + env->cfg->redis_command_timeout / 1000; + moddata->command_timeout.tv_usec = + (env->cfg->redis_command_timeout % 1000) * 1000; + } + if(env->cfg->redis_connect_timeout != 0) { + moddata->connect_timeout.tv_sec = + env->cfg->redis_connect_timeout / 1000; + moddata->connect_timeout.tv_usec = + (env->cfg->redis_connect_timeout % 1000) * 1000; + } moddata->logical_db = env->cfg->redis_logical_db; for(i = 0; i < moddata->numctxs; i++) { redisContext* ctx = redis_connect(moddata); diff --git a/doc/Changelog b/doc/Changelog index e5d778f1d..ed3bc8700 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,9 @@ +17 September 2024: Wouter + - Add redis-command-timeout: 20 and redis-connect-timeout: 200, + that can set the timeout separately for commands and the + connection set up to the redis server. If they are not + specified, the redis-timeout value is used. + 16 September 2024: Wouter - Merge #1140: Fix spelling mistake in comments. diff --git a/doc/example.conf.in b/doc/example.conf.in index cce65c0f5..e5bb5029f 100644 --- a/doc/example.conf.in +++ b/doc/example.conf.in @@ -1301,6 +1301,10 @@ remote-control: # # redis-server-password: "" # # timeout (in ms) for communication with the redis server # redis-timeout: 100 +# # timeout (in ms) for commands, if 0, uses redis-timeout. +# redis-command-timeout: 0 +# # timeout (in ms) for connection set up, if 0, uses redis-timeout. +# redis-connect-timeout: 0 # # set timeout on redis records based on DNS response TTL # redis-expire-records: no # # redis logical database to use, 0 is the default database. diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index d051e8850..f4cf81778 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -2810,6 +2810,14 @@ if the Redis server does not have the requested data, and will try to re-establish a new connection later. This option defaults to 100 milliseconds. .TP +.B redis-command-timeout: \fI\fR +The timeout to use for redis commands, in milliseconds. If 0, it uses the +redis\-timeout value. The default is 0. +.TP +.B redis-connect-timeout: \fI\fR +The timeout to use for redis connection set up, in milliseconds. If 0, it +uses the redis\-timeout value. The default is 0. +.TP .B redis-expire-records: \fI If Redis record expiration is enabled. If yes, Unbound sets timeout for Redis records so that Redis can evict keys that have expired automatically. If diff --git a/util/config_file.c b/util/config_file.c index d82e4374e..ab63d3fed 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -399,6 +399,8 @@ config_create(void) cfg->redis_server_path = NULL; cfg->redis_server_password = NULL; cfg->redis_timeout = 100; + cfg->redis_command_timeout = 0; + cfg->redis_connect_timeout = 0; cfg->redis_server_port = 6379; cfg->redis_expire_records = 0; cfg->redis_logical_db = 0; @@ -1364,6 +1366,8 @@ config_get_option(struct config_file* cfg, const char* opt, else O_STR(opt, "redis-server-path", redis_server_path) else O_STR(opt, "redis-server-password", redis_server_password) else O_DEC(opt, "redis-timeout", redis_timeout) + else O_DEC(opt, "redis-command-timeout", redis_command_timeout) + else O_DEC(opt, "redis-connect-timeout", redis_connect_timeout) else O_YNO(opt, "redis-expire-records", redis_expire_records) else O_DEC(opt, "redis-logical-db", redis_logical_db) #endif /* USE_REDIS */ diff --git a/util/config_file.h b/util/config_file.h index ae9c9cb5b..29dd71620 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -739,6 +739,10 @@ struct config_file { char* redis_server_password; /** timeout (in ms) for communication with the redis server */ int redis_timeout; + /** timeout (in ms) for redis commands */ + int redis_command_timeout; + /** timeout (in ms) for redis connection set up */ + int redis_connect_timeout; /** set timeout on redis records based on DNS response ttl */ int redis_expire_records; /** set the redis logical database upon connection */ diff --git a/util/configlexer.lex b/util/configlexer.lex index 527d5bfb9..b4442e913 100644 --- a/util/configlexer.lex +++ b/util/configlexer.lex @@ -574,6 +574,8 @@ redis-server-port{COLON} { YDVAR(1, VAR_CACHEDB_REDISPORT) } redis-server-path{COLON} { YDVAR(1, VAR_CACHEDB_REDISPATH) } redis-server-password{COLON} { YDVAR(1, VAR_CACHEDB_REDISPASSWORD) } redis-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISTIMEOUT) } +redis-command-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISCOMMANDTIMEOUT) } +redis-connect-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISCONNECTTIMEOUT) } redis-expire-records{COLON} { YDVAR(1, VAR_CACHEDB_REDISEXPIRERECORDS) } redis-logical-db{COLON} { YDVAR(1, VAR_CACHEDB_REDISLOGICALDB) } ipset{COLON} { YDVAR(0, VAR_IPSET) } diff --git a/util/configparser.y b/util/configparser.y index 4dc647f82..dfc0c58af 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -182,6 +182,7 @@ extern struct config_parser_state* cfg_parser; %token VAR_CACHEDB_REDISHOST VAR_CACHEDB_REDISPORT VAR_CACHEDB_REDISTIMEOUT %token VAR_CACHEDB_REDISEXPIRERECORDS VAR_CACHEDB_REDISPATH VAR_CACHEDB_REDISPASSWORD %token VAR_CACHEDB_REDISLOGICALDB +%token VAR_CACHEDB_REDISCOMMANDTIMEOUT VAR_CACHEDB_REDISCONNECTTIMEOUT %token VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM VAR_FOR_UPSTREAM %token VAR_AUTH_ZONE VAR_ZONEFILE VAR_MASTER VAR_URL VAR_FOR_DOWNSTREAM %token VAR_FALLBACK_ENABLED VAR_TLS_ADDITIONAL_PORT VAR_LOW_RTT VAR_LOW_RTT_PERMIL @@ -3838,7 +3839,8 @@ contents_cachedb: contents_cachedb content_cachedb content_cachedb: cachedb_backend_name | cachedb_secret_seed | redis_server_host | redis_server_port | redis_timeout | redis_expire_records | redis_server_path | redis_server_password | - cachedb_no_store | redis_logical_db | cachedb_check_when_serve_expired + cachedb_no_store | redis_logical_db | cachedb_check_when_serve_expired | + redis_command_timeout | redis_connect_timeout ; cachedb_backend_name: VAR_CACHEDB_BACKEND STRING_ARG { @@ -3954,6 +3956,32 @@ redis_timeout: VAR_CACHEDB_REDISTIMEOUT STRING_ARG free($2); } ; +redis_command_timeout: VAR_CACHEDB_REDISCOMMANDTIMEOUT STRING_ARG + { + #if defined(USE_CACHEDB) && defined(USE_REDIS) + OUTYY(("P(redis_command_timeout:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("redis command timeout value expected"); + else cfg_parser->cfg->redis_command_timeout = atoi($2); + #else + OUTYY(("P(Compiled without cachedb or redis, ignoring)\n")); + #endif + free($2); + } + ; +redis_connect_timeout: VAR_CACHEDB_REDISCONNECTTIMEOUT STRING_ARG + { + #if defined(USE_CACHEDB) && defined(USE_REDIS) + OUTYY(("P(redis_connect_timeout:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("redis connect timeout value expected"); + else cfg_parser->cfg->redis_connect_timeout = atoi($2); + #else + OUTYY(("P(Compiled without cachedb or redis, ignoring)\n")); + #endif + free($2); + } + ; redis_expire_records: VAR_CACHEDB_REDISEXPIRERECORDS STRING_ARG { #if defined(USE_CACHEDB) && defined(USE_REDIS)