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;
}
/* 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);
redisContext *redisConnectWrapper(const char *ip, int port, const struct timeval tv);
redisContext *redisConnectUnixWrapper(const char *path, const struct timeval tv);
#endif /* __CLICOMMON_H */

View File

@ -211,6 +211,7 @@ static int createClusterManagerCommand(char *cmdname, int argc, char **argv);
static redisContext *context;
static struct config {
cliConnInfo conn_info;
struct timeval connect_timeout;
char *hostsocket;
int tls;
cliSSLconfig sslconfig;
@ -1648,9 +1649,10 @@ static int cliConnect(int flags) {
/* Do not use hostsocket when we got redirected in cluster mode */
if (config.hostsocket == NULL ||
(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 {
context = redisConnectUnix(config.hostsocket);
context = redisConnectUnixWrapper(config.hostsocket, config.connect_timeout);
}
if (!context->err && config.tls) {
@ -2593,7 +2595,8 @@ static redisReply *reconnectingRedisCommand(redisContext *c, const char *fmt, ..
fflush(stdout);
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) {
const char *err = NULL;
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");
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) {
config.hostsocket = argv[++i];
} else if (!strcmp(argv[i],"-r") && !lastarg) {
@ -3011,6 +3023,8 @@ static void usage(int err) {
"Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]\n"
" -h <hostname> Server hostname (default: 127.0.0.1).\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"
" -a <password> Password to use when connecting to the server.\n"
" You can also use the " REDIS_CLI_AUTH_ENV " environment\n"
@ -4072,7 +4086,7 @@ cleanup:
static int clusterManagerNodeConnect(clusterManagerNode *node) {
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) {
const char *err = NULL;
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;
redisReply *src_reply = NULL;
// 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) {
success = 0;
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));
config.conn_info.hostip = sdsnew("127.0.0.1");
config.conn_info.hostport = 6379;
config.connect_timeout.tv_sec = 0;
config.connect_timeout.tv_usec = 0;
config.hostsocket = NULL;
config.repeat = 1;
config.interval = 0;