Adds connection timeout option to redis-cli (#10609)

This allows specifying the timeout value for opening the TCP
connection to a server. The timeout, default 0 means no limit,
depending on the OS. It can be specified using the new `-t` switch.

revive #3764, fixes #3763

---------

Co-authored-by: Itamar Haber <itamar@redislabs.com>
Co-authored-by: yoav-steinberg <yoav@redislabs.com>
This commit is contained in:
Binbin 2024-01-30 19:43:39 +08:00 committed by GitHub
parent 492021db95
commit 76adbf6ff0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 42 additions and 5 deletions

View File

@ -424,3 +424,21 @@ sds cliVersion(void) {
} }
return version; return version;
} }
/* This is a wrapper to call redisConnect or redisConnectWithTimeout. */
redisContext *redisConnectWrapper(const char *ip, int port, const struct timeval tv) {
if (tv.tv_sec == 0 && tv.tv_usec == 0) {
return redisConnect(ip, port);
} else {
return redisConnectWithTimeout(ip, port, tv);
}
}
/* This is a wrapper to call redisConnectUnix or redisConnectUnixWithTimeout. */
redisContext *redisConnectUnixWrapper(const char *path, const struct timeval tv) {
if (tv.tv_sec == 0 && tv.tv_usec == 0) {
return redisConnectUnix(path);
} else {
return redisConnectUnixWithTimeout(path, tv);
}
}

View File

@ -53,4 +53,7 @@ sds escapeJsonString(sds s, const char *p, size_t len);
sds cliVersion(void); sds cliVersion(void);
redisContext *redisConnectWrapper(const char *ip, int port, const struct timeval tv);
redisContext *redisConnectUnixWrapper(const char *path, const struct timeval tv);
#endif /* __CLICOMMON_H */ #endif /* __CLICOMMON_H */

View File

@ -211,6 +211,7 @@ static int createClusterManagerCommand(char *cmdname, int argc, char **argv);
static redisContext *context; static redisContext *context;
static struct config { static struct config {
cliConnInfo conn_info; cliConnInfo conn_info;
struct timeval connect_timeout;
char *hostsocket; char *hostsocket;
int tls; int tls;
cliSSLconfig sslconfig; cliSSLconfig sslconfig;
@ -1648,9 +1649,10 @@ static int cliConnect(int flags) {
/* Do not use hostsocket when we got redirected in cluster mode */ /* Do not use hostsocket when we got redirected in cluster mode */
if (config.hostsocket == NULL || if (config.hostsocket == NULL ||
(config.cluster_mode && config.cluster_reissue_command)) { (config.cluster_mode && config.cluster_reissue_command)) {
context = redisConnect(config.conn_info.hostip,config.conn_info.hostport); context = redisConnectWrapper(config.conn_info.hostip, config.conn_info.hostport,
config.connect_timeout);
} else { } else {
context = redisConnectUnix(config.hostsocket); context = redisConnectUnixWrapper(config.hostsocket, config.connect_timeout);
} }
if (!context->err && config.tls) { if (!context->err && config.tls) {
@ -2593,7 +2595,8 @@ static redisReply *reconnectingRedisCommand(redisContext *c, const char *fmt, ..
fflush(stdout); fflush(stdout);
redisFree(c); redisFree(c);
c = redisConnect(config.conn_info.hostip,config.conn_info.hostport); c = redisConnectWrapper(config.conn_info.hostip, config.conn_info.hostport,
config.connect_timeout);
if (!c->err && config.tls) { if (!c->err && config.tls) {
const char *err = NULL; const char *err = NULL;
if (cliSecureConnection(c, config.sslconfig, &err) == REDIS_ERR && err) { if (cliSecureConnection(c, config.sslconfig, &err) == REDIS_ERR && err) {
@ -2648,6 +2651,15 @@ static int parseOptions(int argc, char **argv) {
fprintf(stderr, "Invalid server port.\n"); fprintf(stderr, "Invalid server port.\n");
exit(1); exit(1);
} }
} else if (!strcmp(argv[i],"-t") && !lastarg) {
char *eptr;
double seconds = strtod(argv[++i], &eptr);
if (eptr[0] != '\0' || isnan(seconds) || seconds < 0.0) {
fprintf(stderr, "Invalid connection timeout for -t.\n");
exit(1);
}
config.connect_timeout.tv_sec = (long long)seconds;
config.connect_timeout.tv_usec = ((long long)(seconds * 1000000)) % 1000000;
} else if (!strcmp(argv[i],"-s") && !lastarg) { } else if (!strcmp(argv[i],"-s") && !lastarg) {
config.hostsocket = argv[++i]; config.hostsocket = argv[++i];
} else if (!strcmp(argv[i],"-r") && !lastarg) { } else if (!strcmp(argv[i],"-r") && !lastarg) {
@ -3011,6 +3023,8 @@ static void usage(int err) {
"Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]\n" "Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]\n"
" -h <hostname> Server hostname (default: 127.0.0.1).\n" " -h <hostname> Server hostname (default: 127.0.0.1).\n"
" -p <port> Server port (default: 6379).\n" " -p <port> Server port (default: 6379).\n"
" -t <timeout> Server connection timeout in seconds (decimals allowed).\n"
" Default timeout is 0, meaning no limit, depending on the OS.\n"
" -s <socket> Server socket (overrides hostname and port).\n" " -s <socket> Server socket (overrides hostname and port).\n"
" -a <password> Password to use when connecting to the server.\n" " -a <password> Password to use when connecting to the server.\n"
" You can also use the " REDIS_CLI_AUTH_ENV " environment\n" " You can also use the " REDIS_CLI_AUTH_ENV " environment\n"
@ -4072,7 +4086,7 @@ cleanup:
static int clusterManagerNodeConnect(clusterManagerNode *node) { static int clusterManagerNodeConnect(clusterManagerNode *node) {
if (node->context) redisFree(node->context); if (node->context) redisFree(node->context);
node->context = redisConnect(node->ip, node->port); node->context = redisConnectWrapper(node->ip, node->port, config.connect_timeout);
if (!node->context->err && config.tls) { if (!node->context->err && config.tls) {
const char *err = NULL; const char *err = NULL;
if (cliSecureConnection(node->context, config.sslconfig, &err) == REDIS_ERR && err) { if (cliSecureConnection(node->context, config.sslconfig, &err) == REDIS_ERR && err) {
@ -7897,7 +7911,7 @@ static int clusterManagerCommandImport(int argc, char **argv) {
char *reply_err = NULL; char *reply_err = NULL;
redisReply *src_reply = NULL; redisReply *src_reply = NULL;
// Connect to the source node. // Connect to the source node.
redisContext *src_ctx = redisConnect(src_ip, src_port); redisContext *src_ctx = redisConnectWrapper(src_ip, src_port, config.connect_timeout);
if (src_ctx->err) { if (src_ctx->err) {
success = 0; success = 0;
fprintf(stderr,"Could not connect to Redis at %s:%d: %s.\n", src_ip, fprintf(stderr,"Could not connect to Redis at %s:%d: %s.\n", src_ip,
@ -9832,6 +9846,8 @@ int main(int argc, char **argv) {
memset(&config.sslconfig, 0, sizeof(config.sslconfig)); memset(&config.sslconfig, 0, sizeof(config.sslconfig));
config.conn_info.hostip = sdsnew("127.0.0.1"); config.conn_info.hostip = sdsnew("127.0.0.1");
config.conn_info.hostport = 6379; config.conn_info.hostport = 6379;
config.connect_timeout.tv_sec = 0;
config.connect_timeout.tv_usec = 0;
config.hostsocket = NULL; config.hostsocket = NULL;
config.repeat = 1; config.repeat = 1;
config.interval = 0; config.interval = 0;