X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/ecc9109434002d4667cd01a3b7c067a508c876eb..07ac2daaaa6c844cc3171c8f01ad5ad7e786e2ea:/src/redis-cli.c diff --git a/src/redis-cli.c b/src/redis-cli.c index 1b23c0b1..cbaac3f9 100644 --- a/src/redis-cli.c +++ b/src/redis-cli.c @@ -55,11 +55,15 @@ static struct config { int hostport; char *hostsocket; long repeat; + long interval; int dbnum; int interactive; int shutdown; int monitor_mode; int pubsub_mode; + int latency_mode; + int cluster_mode; + int cluster_reissue_command; int stdinarg; /* get last arg from stdin. (-x option) */ char *auth; int raw_output; /* output mode per command */ @@ -87,9 +91,11 @@ static long long mstime(void) { static void cliRefreshPrompt(void) { if (config.dbnum == 0) - snprintf(config.prompt,sizeof(config.prompt),"redis> "); + snprintf(config.prompt,sizeof(config.prompt),"redis %s:%d> ", + config.hostip, config.hostport); else - snprintf(config.prompt,sizeof(config.prompt),"redis:%d> ",config.dbnum); + snprintf(config.prompt,sizeof(config.prompt),"redis %s:%d[%d]> ", + config.hostip, config.hostport, config.dbnum); } /*------------------------------------------------------------------------------ @@ -314,10 +320,9 @@ static int cliConnect(int force) { return REDIS_OK; } -static void cliPrintContextErrorAndExit() { +static void cliPrintContextError() { if (context == NULL) return; fprintf(stderr,"Error: %s\n",context->errstr); - exit(1); } static sds cliFormatReplyTTY(redisReply *r, char *prefix) { @@ -425,6 +430,7 @@ static int cliReadReply(int output_raw_strings) { void *_reply; redisReply *reply; sds out; + int output = 1; if (redisGetReply(context,&_reply) != REDIS_OK) { if (config.shutdown) @@ -436,23 +442,55 @@ static int cliReadReply(int output_raw_strings) { if (context->err == REDIS_ERR_EOF) return REDIS_ERR; } - cliPrintContextErrorAndExit(); + cliPrintContextError(); + exit(1); return REDIS_ERR; /* avoid compiler warning */ } reply = (redisReply*)_reply; - if (output_raw_strings) { - out = cliFormatReplyRaw(reply); - } else { - if (config.raw_output) { + + /* Check if we need to connect to a different node and reissue the request. */ + if (config.cluster_mode && reply->type == REDIS_REPLY_ERROR && + (!strncmp(reply->str,"MOVED",5) || !strcmp(reply->str,"ASK"))) + { + char *p = reply->str, *s; + int slot; + + output = 0; + /* Comments show the position of the pointer as: + * + * [S] for pointer 's' + * [P] for pointer 'p' + */ + s = strchr(p,' '); /* MOVED[S]3999 127.0.0.1:6381 */ + p = strchr(s+1,' '); /* MOVED[S]3999[P]127.0.0.1:6381 */ + *p = '\0'; + slot = atoi(s+1); + s = strchr(p+1,':'); /* MOVED 3999[P]127.0.0.1[S]6381 */ + *s = '\0'; + sdsfree(config.hostip); + config.hostip = sdsnew(p+1); + config.hostport = atoi(s+1); + if (config.interactive) + printf("-> Redirected to slot [%d] located at %s:%d\n", + slot, config.hostip, config.hostport); + config.cluster_reissue_command = 1; + } + + if (output) { + if (output_raw_strings) { out = cliFormatReplyRaw(reply); - out = sdscat(out,"\n"); } else { - out = cliFormatReplyTTY(reply,""); + if (config.raw_output) { + out = cliFormatReplyRaw(reply); + out = sdscat(out,"\n"); + } else { + out = cliFormatReplyTTY(reply,""); + } } + fwrite(out,sdslen(out),1,stdout); + sdsfree(out); } - fwrite(out,sdslen(out),1,stdout); - sdsfree(out); freeReplyObject(reply); return REDIS_OK; } @@ -462,16 +500,16 @@ static int cliSendCommand(int argc, char **argv, int repeat) { size_t *argvlen; int j, output_raw; - if (context == NULL) { - printf("Not connected, please use: connect \n"); - return REDIS_OK; - } + if (context == NULL) return REDIS_ERR; output_raw = 0; if (!strcasecmp(command,"info") || (argc == 2 && !strcasecmp(command,"cluster") && (!strcasecmp(argv[1],"nodes") || - !strcasecmp(argv[1],"info")))) + !strcasecmp(argv[1],"info"))) || + (argc == 2 && !strcasecmp(command,"client") && + !strcasecmp(argv[1],"list"))) + { output_raw = 1; } @@ -515,6 +553,8 @@ static int cliSendCommand(int argc, char **argv, int repeat) { cliRefreshPrompt(); } } + if (config.interval) usleep(config.interval); + fflush(stdout); /* Make it grep friendly */ } free(argvlen); @@ -550,6 +590,10 @@ static int parseOptions(int argc, char **argv) { } else if (!strcmp(argv[i],"-r") && !lastarg) { config.repeat = strtoll(argv[i+1],NULL,10); i++; + } else if (!strcmp(argv[i],"-i") && !lastarg) { + double seconds = atof(argv[i+1]); + config.interval = seconds*1000000; + i++; } else if (!strcmp(argv[i],"-n") && !lastarg) { config.dbnum = atoi(argv[i+1]); i++; @@ -558,6 +602,10 @@ static int parseOptions(int argc, char **argv) { i++; } else if (!strcmp(argv[i],"--raw")) { config.raw_output = 1; + } else if (!strcmp(argv[i],"--latency")) { + config.latency_mode = 1; + } else if (!strcmp(argv[i],"-c")) { + config.cluster_mode = 1; } else if (!strcmp(argv[i],"-d") && !lastarg) { sdsfree(config.mb_delim); config.mb_delim = sdsnew(argv[i+1]); @@ -602,10 +650,14 @@ static void usage() { " -s Server socket (overrides hostname and port)\n" " -a Password to use when connecting to the server\n" " -r Execute specified command N times\n" +" -i When -r is used, waits seconds per command.\n" +" It is possible to specify sub-second times like -i 0.1.\n" " -n Database number\n" " -x Read last argument from STDIN\n" " -d Multi-bulk delimiter in for raw formatting (default: \\n)\n" +" -c Enable cluster mode (follow -ASK and -MOVED redirections)\n" " --raw Use raw formatting for replies (default when STDOUT is not a tty)\n" +" --latency Enter a special mode continuously sampling latency.\n" " --help Output this help and exit\n" " --version Output version and exit\n" "\n" @@ -613,6 +665,7 @@ static void usage() { " cat /etc/passwd | redis-cli -x set mypasswd\n" " redis-cli get mypasswd\n" " redis-cli -r 100 lpush mylist x\n" +" redis-cli -r 100 -i 1 info | grep used_memory_human:\n" "\n" "When no command is given, redis-cli starts in interactive mode.\n" "Type \"help\" in interactive mode for information on available commands.\n" @@ -663,6 +716,7 @@ static void repl() { if (argv == NULL) { printf("Invalid argument(s)\n"); + free(line); continue; } else if (argc > 0) { if (strcasecmp(argv[0],"quit") == 0 || @@ -678,14 +732,34 @@ static void repl() { linenoiseClearScreen(); } else { long long start_time = mstime(), elapsed; + int repeat, skipargs = 0; - if (cliSendCommand(argc,argv,1) != REDIS_OK) { - cliConnect(1); + repeat = atoi(argv[0]); + if (argc > 1 && repeat) { + skipargs = 1; + } else { + repeat = 1; + } - /* If we still cannot send the command, - * print error and abort. */ - if (cliSendCommand(argc,argv,1) != REDIS_OK) - cliPrintContextErrorAndExit(); + while (1) { + config.cluster_reissue_command = 0; + if (cliSendCommand(argc-skipargs,argv+skipargs,repeat) + != REDIS_OK) + { + cliConnect(1); + + /* If we still cannot send the command print error. + * We'll try to reconnect the next time. */ + if (cliSendCommand(argc-skipargs,argv+skipargs,repeat) + != REDIS_OK) + cliPrintContextError(); + } + /* Issue the command again if we got redirected in cluster mode */ + if (config.cluster_mode && config.cluster_reissue_command) { + cliConnect(1); + } else { + break; + } } elapsed = mstime()-start_time; if (elapsed >= 500) { @@ -716,6 +790,38 @@ static int noninteractive(int argc, char **argv) { return retval; } +static void latencyMode(void) { + redisReply *reply; + long long start, latency, min, max, tot, count = 0; + double avg; + + if (!context) exit(1); + while(1) { + start = mstime(); + reply = redisCommand(context,"PING"); + if (reply == NULL) { + fprintf(stderr,"\nI/O error\n"); + exit(1); + } + latency = mstime()-start; + freeReplyObject(reply); + count++; + if (count == 1) { + min = max = tot = latency; + avg = (double) latency; + } else { + if (latency < min) min = latency; + if (latency > max) max = latency; + tot += latency; + avg = (double) tot/count; + } + printf("\x1b[0G\x1b[2Kmin: %lld, max: %lld, avg: %.2f (%lld samples)", + min, max, avg, count); + fflush(stdout); + usleep(10000); + } +} + int main(int argc, char **argv) { int firstarg; @@ -723,11 +829,14 @@ int main(int argc, char **argv) { config.hostport = 6379; config.hostsocket = NULL; config.repeat = 1; + config.interval = 0; config.dbnum = 0; config.interactive = 0; config.shutdown = 0; config.monitor_mode = 0; config.pubsub_mode = 0; + config.latency_mode = 0; + config.cluster_mode = 0; config.stdinarg = 0; config.auth = NULL; config.raw_output = !isatty(fileno(stdout)) && (getenv("FAKETTY") == NULL); @@ -738,11 +847,21 @@ int main(int argc, char **argv) { argc -= firstarg; argv += firstarg; - /* Try to connect */ - if (cliConnect(0) != REDIS_OK) exit(1); + /* Start in latency mode if appropriate */ + if (config.latency_mode) { + cliConnect(0); + latencyMode(); + } /* Start interactive mode when no command is provided */ - if (argc == 0) repl(); + if (argc == 0) { + /* Note that in repl mode we don't abort on connection error. + * A new attempt will be performed for every command send. */ + cliConnect(0); + repl(); + } + /* Otherwise, we have some arguments to execute */ + if (cliConnect(0) != REDIS_OK) exit(1); return noninteractive(argc,convertToSds(argc,argv)); }