1*572c4311Sfengbojiang /* Redis CLI (command line interface)
2*572c4311Sfengbojiang *
3*572c4311Sfengbojiang * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
4*572c4311Sfengbojiang * All rights reserved.
5*572c4311Sfengbojiang *
6*572c4311Sfengbojiang * Redistribution and use in source and binary forms, with or without
7*572c4311Sfengbojiang * modification, are permitted provided that the following conditions are met:
8*572c4311Sfengbojiang *
9*572c4311Sfengbojiang * * Redistributions of source code must retain the above copyright notice,
10*572c4311Sfengbojiang * this list of conditions and the following disclaimer.
11*572c4311Sfengbojiang * * Redistributions in binary form must reproduce the above copyright
12*572c4311Sfengbojiang * notice, this list of conditions and the following disclaimer in the
13*572c4311Sfengbojiang * documentation and/or other materials provided with the distribution.
14*572c4311Sfengbojiang * * Neither the name of Redis nor the names of its contributors may be used
15*572c4311Sfengbojiang * to endorse or promote products derived from this software without
16*572c4311Sfengbojiang * specific prior written permission.
17*572c4311Sfengbojiang *
18*572c4311Sfengbojiang * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19*572c4311Sfengbojiang * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*572c4311Sfengbojiang * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*572c4311Sfengbojiang * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22*572c4311Sfengbojiang * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23*572c4311Sfengbojiang * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24*572c4311Sfengbojiang * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25*572c4311Sfengbojiang * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26*572c4311Sfengbojiang * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27*572c4311Sfengbojiang * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28*572c4311Sfengbojiang * POSSIBILITY OF SUCH DAMAGE.
29*572c4311Sfengbojiang */
30*572c4311Sfengbojiang
31*572c4311Sfengbojiang #include "fmacros.h"
32*572c4311Sfengbojiang #include "version.h"
33*572c4311Sfengbojiang
34*572c4311Sfengbojiang #include <stdio.h>
35*572c4311Sfengbojiang #include <string.h>
36*572c4311Sfengbojiang #include <stdlib.h>
37*572c4311Sfengbojiang #include <signal.h>
38*572c4311Sfengbojiang #include <unistd.h>
39*572c4311Sfengbojiang #include <time.h>
40*572c4311Sfengbojiang #include <ctype.h>
41*572c4311Sfengbojiang #include <errno.h>
42*572c4311Sfengbojiang #include <sys/stat.h>
43*572c4311Sfengbojiang #include <sys/time.h>
44*572c4311Sfengbojiang #include <assert.h>
45*572c4311Sfengbojiang #include <fcntl.h>
46*572c4311Sfengbojiang #include <limits.h>
47*572c4311Sfengbojiang #include <math.h>
48*572c4311Sfengbojiang
49*572c4311Sfengbojiang #include <hiredis.h>
50*572c4311Sfengbojiang #include <sds.h> /* use sds.h from hiredis, so that only one set of sds functions will be present in the binary */
51*572c4311Sfengbojiang #include "dict.h"
52*572c4311Sfengbojiang #include "adlist.h"
53*572c4311Sfengbojiang #include "zmalloc.h"
54*572c4311Sfengbojiang #include "linenoise.h"
55*572c4311Sfengbojiang #include "help.h"
56*572c4311Sfengbojiang #include "anet.h"
57*572c4311Sfengbojiang #include "ae.h"
58*572c4311Sfengbojiang
59*572c4311Sfengbojiang #define UNUSED(V) ((void) V)
60*572c4311Sfengbojiang
61*572c4311Sfengbojiang #define OUTPUT_STANDARD 0
62*572c4311Sfengbojiang #define OUTPUT_RAW 1
63*572c4311Sfengbojiang #define OUTPUT_CSV 2
64*572c4311Sfengbojiang #define REDIS_CLI_KEEPALIVE_INTERVAL 15 /* seconds */
65*572c4311Sfengbojiang #define REDIS_CLI_DEFAULT_PIPE_TIMEOUT 30 /* seconds */
66*572c4311Sfengbojiang #define REDIS_CLI_HISTFILE_ENV "REDISCLI_HISTFILE"
67*572c4311Sfengbojiang #define REDIS_CLI_HISTFILE_DEFAULT ".rediscli_history"
68*572c4311Sfengbojiang #define REDIS_CLI_RCFILE_ENV "REDISCLI_RCFILE"
69*572c4311Sfengbojiang #define REDIS_CLI_RCFILE_DEFAULT ".redisclirc"
70*572c4311Sfengbojiang #define REDIS_CLI_AUTH_ENV "REDISCLI_AUTH"
71*572c4311Sfengbojiang #define REDIS_CLI_CLUSTER_YES_ENV "REDISCLI_CLUSTER_YES"
72*572c4311Sfengbojiang
73*572c4311Sfengbojiang #define CLUSTER_MANAGER_SLOTS 16384
74*572c4311Sfengbojiang #define CLUSTER_MANAGER_MIGRATE_TIMEOUT 60000
75*572c4311Sfengbojiang #define CLUSTER_MANAGER_MIGRATE_PIPELINE 10
76*572c4311Sfengbojiang #define CLUSTER_MANAGER_REBALANCE_THRESHOLD 2
77*572c4311Sfengbojiang
78*572c4311Sfengbojiang #define CLUSTER_MANAGER_INVALID_HOST_ARG \
79*572c4311Sfengbojiang "[ERR] Invalid arguments: you need to pass either a valid " \
80*572c4311Sfengbojiang "address (ie. 120.0.0.1:7000) or space separated IP " \
81*572c4311Sfengbojiang "and port (ie. 120.0.0.1 7000)\n"
82*572c4311Sfengbojiang #define CLUSTER_MANAGER_MODE() (config.cluster_manager_command.name != NULL)
83*572c4311Sfengbojiang #define CLUSTER_MANAGER_MASTERS_COUNT(nodes, replicas) (nodes/(replicas + 1))
84*572c4311Sfengbojiang #define CLUSTER_MANAGER_COMMAND(n,...) \
85*572c4311Sfengbojiang (redisCommand(n->context, __VA_ARGS__))
86*572c4311Sfengbojiang
87*572c4311Sfengbojiang #define CLUSTER_MANAGER_NODE_ARRAY_FREE(array) zfree(array->alloc)
88*572c4311Sfengbojiang
89*572c4311Sfengbojiang #define CLUSTER_MANAGER_PRINT_REPLY_ERROR(n, err) \
90*572c4311Sfengbojiang clusterManagerLogErr("Node %s:%d replied with error:\n%s\n", \
91*572c4311Sfengbojiang n->ip, n->port, err);
92*572c4311Sfengbojiang
93*572c4311Sfengbojiang #define clusterManagerLogInfo(...) \
94*572c4311Sfengbojiang clusterManagerLog(CLUSTER_MANAGER_LOG_LVL_INFO,__VA_ARGS__)
95*572c4311Sfengbojiang
96*572c4311Sfengbojiang #define clusterManagerLogErr(...) \
97*572c4311Sfengbojiang clusterManagerLog(CLUSTER_MANAGER_LOG_LVL_ERR,__VA_ARGS__)
98*572c4311Sfengbojiang
99*572c4311Sfengbojiang #define clusterManagerLogWarn(...) \
100*572c4311Sfengbojiang clusterManagerLog(CLUSTER_MANAGER_LOG_LVL_WARN,__VA_ARGS__)
101*572c4311Sfengbojiang
102*572c4311Sfengbojiang #define clusterManagerLogOk(...) \
103*572c4311Sfengbojiang clusterManagerLog(CLUSTER_MANAGER_LOG_LVL_SUCCESS,__VA_ARGS__)
104*572c4311Sfengbojiang
105*572c4311Sfengbojiang #define CLUSTER_MANAGER_FLAG_MYSELF 1 << 0
106*572c4311Sfengbojiang #define CLUSTER_MANAGER_FLAG_SLAVE 1 << 1
107*572c4311Sfengbojiang #define CLUSTER_MANAGER_FLAG_FRIEND 1 << 2
108*572c4311Sfengbojiang #define CLUSTER_MANAGER_FLAG_NOADDR 1 << 3
109*572c4311Sfengbojiang #define CLUSTER_MANAGER_FLAG_DISCONNECT 1 << 4
110*572c4311Sfengbojiang #define CLUSTER_MANAGER_FLAG_FAIL 1 << 5
111*572c4311Sfengbojiang
112*572c4311Sfengbojiang #define CLUSTER_MANAGER_CMD_FLAG_FIX 1 << 0
113*572c4311Sfengbojiang #define CLUSTER_MANAGER_CMD_FLAG_SLAVE 1 << 1
114*572c4311Sfengbojiang #define CLUSTER_MANAGER_CMD_FLAG_YES 1 << 2
115*572c4311Sfengbojiang #define CLUSTER_MANAGER_CMD_FLAG_AUTOWEIGHTS 1 << 3
116*572c4311Sfengbojiang #define CLUSTER_MANAGER_CMD_FLAG_EMPTYMASTER 1 << 4
117*572c4311Sfengbojiang #define CLUSTER_MANAGER_CMD_FLAG_SIMULATE 1 << 5
118*572c4311Sfengbojiang #define CLUSTER_MANAGER_CMD_FLAG_REPLACE 1 << 6
119*572c4311Sfengbojiang #define CLUSTER_MANAGER_CMD_FLAG_COPY 1 << 7
120*572c4311Sfengbojiang #define CLUSTER_MANAGER_CMD_FLAG_COLOR 1 << 8
121*572c4311Sfengbojiang #define CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS 1 << 9
122*572c4311Sfengbojiang
123*572c4311Sfengbojiang #define CLUSTER_MANAGER_OPT_GETFRIENDS 1 << 0
124*572c4311Sfengbojiang #define CLUSTER_MANAGER_OPT_COLD 1 << 1
125*572c4311Sfengbojiang #define CLUSTER_MANAGER_OPT_UPDATE 1 << 2
126*572c4311Sfengbojiang #define CLUSTER_MANAGER_OPT_QUIET 1 << 6
127*572c4311Sfengbojiang #define CLUSTER_MANAGER_OPT_VERBOSE 1 << 7
128*572c4311Sfengbojiang
129*572c4311Sfengbojiang #define CLUSTER_MANAGER_LOG_LVL_INFO 1
130*572c4311Sfengbojiang #define CLUSTER_MANAGER_LOG_LVL_WARN 2
131*572c4311Sfengbojiang #define CLUSTER_MANAGER_LOG_LVL_ERR 3
132*572c4311Sfengbojiang #define CLUSTER_MANAGER_LOG_LVL_SUCCESS 4
133*572c4311Sfengbojiang
134*572c4311Sfengbojiang #define LOG_COLOR_BOLD "29;1m"
135*572c4311Sfengbojiang #define LOG_COLOR_RED "31;1m"
136*572c4311Sfengbojiang #define LOG_COLOR_GREEN "32;1m"
137*572c4311Sfengbojiang #define LOG_COLOR_YELLOW "33;1m"
138*572c4311Sfengbojiang #define LOG_COLOR_RESET "0m"
139*572c4311Sfengbojiang
140*572c4311Sfengbojiang /* cliConnect() flags. */
141*572c4311Sfengbojiang #define CC_FORCE (1<<0) /* Re-connect if already connected. */
142*572c4311Sfengbojiang #define CC_QUIET (1<<1) /* Don't log connecting errors. */
143*572c4311Sfengbojiang
144*572c4311Sfengbojiang /* --latency-dist palettes. */
145*572c4311Sfengbojiang int spectrum_palette_color_size = 19;
146*572c4311Sfengbojiang int spectrum_palette_color[] = {0,233,234,235,237,239,241,243,245,247,144,143,142,184,226,214,208,202,196};
147*572c4311Sfengbojiang
148*572c4311Sfengbojiang int spectrum_palette_mono_size = 13;
149*572c4311Sfengbojiang int spectrum_palette_mono[] = {0,233,234,235,237,239,241,243,245,247,249,251,253};
150*572c4311Sfengbojiang
151*572c4311Sfengbojiang /* The actual palette in use. */
152*572c4311Sfengbojiang int *spectrum_palette;
153*572c4311Sfengbojiang int spectrum_palette_size;
154*572c4311Sfengbojiang
155*572c4311Sfengbojiang /* Dict Helpers */
156*572c4311Sfengbojiang
157*572c4311Sfengbojiang static uint64_t dictSdsHash(const void *key);
158*572c4311Sfengbojiang static int dictSdsKeyCompare(void *privdata, const void *key1,
159*572c4311Sfengbojiang const void *key2);
160*572c4311Sfengbojiang static void dictSdsDestructor(void *privdata, void *val);
161*572c4311Sfengbojiang static void dictListDestructor(void *privdata, void *val);
162*572c4311Sfengbojiang
163*572c4311Sfengbojiang /* Cluster Manager Command Info */
164*572c4311Sfengbojiang typedef struct clusterManagerCommand {
165*572c4311Sfengbojiang char *name;
166*572c4311Sfengbojiang int argc;
167*572c4311Sfengbojiang char **argv;
168*572c4311Sfengbojiang int flags;
169*572c4311Sfengbojiang int replicas;
170*572c4311Sfengbojiang char *from;
171*572c4311Sfengbojiang char *to;
172*572c4311Sfengbojiang char **weight;
173*572c4311Sfengbojiang int weight_argc;
174*572c4311Sfengbojiang char *master_id;
175*572c4311Sfengbojiang int slots;
176*572c4311Sfengbojiang int timeout;
177*572c4311Sfengbojiang int pipeline;
178*572c4311Sfengbojiang float threshold;
179*572c4311Sfengbojiang } clusterManagerCommand;
180*572c4311Sfengbojiang
181*572c4311Sfengbojiang static void createClusterManagerCommand(char *cmdname, int argc, char **argv);
182*572c4311Sfengbojiang
183*572c4311Sfengbojiang
184*572c4311Sfengbojiang static redisContext *context;
185*572c4311Sfengbojiang static struct config {
186*572c4311Sfengbojiang char *hostip;
187*572c4311Sfengbojiang int hostport;
188*572c4311Sfengbojiang char *hostsocket;
189*572c4311Sfengbojiang long repeat;
190*572c4311Sfengbojiang long interval;
191*572c4311Sfengbojiang int dbnum;
192*572c4311Sfengbojiang int interactive;
193*572c4311Sfengbojiang int shutdown;
194*572c4311Sfengbojiang int monitor_mode;
195*572c4311Sfengbojiang int pubsub_mode;
196*572c4311Sfengbojiang int latency_mode;
197*572c4311Sfengbojiang int latency_dist_mode;
198*572c4311Sfengbojiang int latency_history;
199*572c4311Sfengbojiang int lru_test_mode;
200*572c4311Sfengbojiang long long lru_test_sample_size;
201*572c4311Sfengbojiang int cluster_mode;
202*572c4311Sfengbojiang int cluster_reissue_command;
203*572c4311Sfengbojiang int slave_mode;
204*572c4311Sfengbojiang int pipe_mode;
205*572c4311Sfengbojiang int pipe_timeout;
206*572c4311Sfengbojiang int getrdb_mode;
207*572c4311Sfengbojiang int stat_mode;
208*572c4311Sfengbojiang int scan_mode;
209*572c4311Sfengbojiang int intrinsic_latency_mode;
210*572c4311Sfengbojiang int intrinsic_latency_duration;
211*572c4311Sfengbojiang char *pattern;
212*572c4311Sfengbojiang char *rdb_filename;
213*572c4311Sfengbojiang int bigkeys;
214*572c4311Sfengbojiang int memkeys;
215*572c4311Sfengbojiang unsigned memkeys_samples;
216*572c4311Sfengbojiang int hotkeys;
217*572c4311Sfengbojiang int stdinarg; /* get last arg from stdin. (-x option) */
218*572c4311Sfengbojiang char *auth;
219*572c4311Sfengbojiang int output; /* output mode, see OUTPUT_* defines */
220*572c4311Sfengbojiang sds mb_delim;
221*572c4311Sfengbojiang char prompt[128];
222*572c4311Sfengbojiang char *eval;
223*572c4311Sfengbojiang int eval_ldb;
224*572c4311Sfengbojiang int eval_ldb_sync; /* Ask for synchronous mode of the Lua debugger. */
225*572c4311Sfengbojiang int eval_ldb_end; /* Lua debugging session ended. */
226*572c4311Sfengbojiang int enable_ldb_on_eval; /* Handle manual SCRIPT DEBUG + EVAL commands. */
227*572c4311Sfengbojiang int last_cmd_type;
228*572c4311Sfengbojiang int verbose;
229*572c4311Sfengbojiang clusterManagerCommand cluster_manager_command;
230*572c4311Sfengbojiang int no_auth_warning;
231*572c4311Sfengbojiang } config;
232*572c4311Sfengbojiang
233*572c4311Sfengbojiang /* User preferences. */
234*572c4311Sfengbojiang static struct pref {
235*572c4311Sfengbojiang int hints;
236*572c4311Sfengbojiang } pref;
237*572c4311Sfengbojiang
238*572c4311Sfengbojiang static volatile sig_atomic_t force_cancel_loop = 0;
239*572c4311Sfengbojiang static void usage(void);
240*572c4311Sfengbojiang static void slaveMode(void);
241*572c4311Sfengbojiang char *redisGitSHA1(void);
242*572c4311Sfengbojiang char *redisGitDirty(void);
243*572c4311Sfengbojiang static int cliConnect(int force);
244*572c4311Sfengbojiang
245*572c4311Sfengbojiang static char *getInfoField(char *info, char *field);
246*572c4311Sfengbojiang static long getLongInfoField(char *info, char *field);
247*572c4311Sfengbojiang
248*572c4311Sfengbojiang /*------------------------------------------------------------------------------
249*572c4311Sfengbojiang * Utility functions
250*572c4311Sfengbojiang *--------------------------------------------------------------------------- */
251*572c4311Sfengbojiang
252*572c4311Sfengbojiang uint16_t crc16(const char *buf, int len);
253*572c4311Sfengbojiang
ustime(void)254*572c4311Sfengbojiang static long long ustime(void) {
255*572c4311Sfengbojiang struct timeval tv;
256*572c4311Sfengbojiang long long ust;
257*572c4311Sfengbojiang
258*572c4311Sfengbojiang gettimeofday(&tv, NULL);
259*572c4311Sfengbojiang ust = ((long long)tv.tv_sec)*1000000;
260*572c4311Sfengbojiang ust += tv.tv_usec;
261*572c4311Sfengbojiang return ust;
262*572c4311Sfengbojiang }
263*572c4311Sfengbojiang
mstime(void)264*572c4311Sfengbojiang static long long mstime(void) {
265*572c4311Sfengbojiang return ustime()/1000;
266*572c4311Sfengbojiang }
267*572c4311Sfengbojiang
cliRefreshPrompt(void)268*572c4311Sfengbojiang static void cliRefreshPrompt(void) {
269*572c4311Sfengbojiang if (config.eval_ldb) return;
270*572c4311Sfengbojiang
271*572c4311Sfengbojiang sds prompt = sdsempty();
272*572c4311Sfengbojiang if (config.hostsocket != NULL) {
273*572c4311Sfengbojiang prompt = sdscatfmt(prompt,"redis %s",config.hostsocket);
274*572c4311Sfengbojiang } else {
275*572c4311Sfengbojiang char addr[256];
276*572c4311Sfengbojiang anetFormatAddr(addr, sizeof(addr), config.hostip, config.hostport);
277*572c4311Sfengbojiang prompt = sdscatlen(prompt,addr,strlen(addr));
278*572c4311Sfengbojiang }
279*572c4311Sfengbojiang
280*572c4311Sfengbojiang /* Add [dbnum] if needed */
281*572c4311Sfengbojiang if (config.dbnum != 0)
282*572c4311Sfengbojiang prompt = sdscatfmt(prompt,"[%i]",config.dbnum);
283*572c4311Sfengbojiang
284*572c4311Sfengbojiang /* Copy the prompt in the static buffer. */
285*572c4311Sfengbojiang prompt = sdscatlen(prompt,"> ",2);
286*572c4311Sfengbojiang snprintf(config.prompt,sizeof(config.prompt),"%s",prompt);
287*572c4311Sfengbojiang sdsfree(prompt);
288*572c4311Sfengbojiang }
289*572c4311Sfengbojiang
290*572c4311Sfengbojiang /* Return the name of the dotfile for the specified 'dotfilename'.
291*572c4311Sfengbojiang * Normally it just concatenates user $HOME to the file specified
292*572c4311Sfengbojiang * in 'dotfilename'. However if the environment varialbe 'envoverride'
293*572c4311Sfengbojiang * is set, its value is taken as the path.
294*572c4311Sfengbojiang *
295*572c4311Sfengbojiang * The function returns NULL (if the file is /dev/null or cannot be
296*572c4311Sfengbojiang * obtained for some error), or an SDS string that must be freed by
297*572c4311Sfengbojiang * the user. */
getDotfilePath(char * envoverride,char * dotfilename)298*572c4311Sfengbojiang static sds getDotfilePath(char *envoverride, char *dotfilename) {
299*572c4311Sfengbojiang char *path = NULL;
300*572c4311Sfengbojiang sds dotPath = NULL;
301*572c4311Sfengbojiang
302*572c4311Sfengbojiang /* Check the env for a dotfile override. */
303*572c4311Sfengbojiang path = getenv(envoverride);
304*572c4311Sfengbojiang if (path != NULL && *path != '\0') {
305*572c4311Sfengbojiang if (!strcmp("/dev/null", path)) {
306*572c4311Sfengbojiang return NULL;
307*572c4311Sfengbojiang }
308*572c4311Sfengbojiang
309*572c4311Sfengbojiang /* If the env is set, return it. */
310*572c4311Sfengbojiang dotPath = sdsnew(path);
311*572c4311Sfengbojiang } else {
312*572c4311Sfengbojiang char *home = getenv("HOME");
313*572c4311Sfengbojiang if (home != NULL && *home != '\0') {
314*572c4311Sfengbojiang /* If no override is set use $HOME/<dotfilename>. */
315*572c4311Sfengbojiang dotPath = sdscatprintf(sdsempty(), "%s/%s", home, dotfilename);
316*572c4311Sfengbojiang }
317*572c4311Sfengbojiang }
318*572c4311Sfengbojiang return dotPath;
319*572c4311Sfengbojiang }
320*572c4311Sfengbojiang
321*572c4311Sfengbojiang /* URL-style percent decoding. */
322*572c4311Sfengbojiang #define isHexChar(c) (isdigit(c) || (c >= 'a' && c <= 'f'))
323*572c4311Sfengbojiang #define decodeHexChar(c) (isdigit(c) ? c - '0' : c - 'a' + 10)
324*572c4311Sfengbojiang #define decodeHex(h, l) ((decodeHexChar(h) << 4) + decodeHexChar(l))
325*572c4311Sfengbojiang
percentDecode(const char * pe,size_t len)326*572c4311Sfengbojiang static sds percentDecode(const char *pe, size_t len) {
327*572c4311Sfengbojiang const char *end = pe + len;
328*572c4311Sfengbojiang sds ret = sdsempty();
329*572c4311Sfengbojiang const char *curr = pe;
330*572c4311Sfengbojiang
331*572c4311Sfengbojiang while (curr < end) {
332*572c4311Sfengbojiang if (*curr == '%') {
333*572c4311Sfengbojiang if ((end - curr) < 2) {
334*572c4311Sfengbojiang fprintf(stderr, "Incomplete URI encoding\n");
335*572c4311Sfengbojiang exit(1);
336*572c4311Sfengbojiang }
337*572c4311Sfengbojiang
338*572c4311Sfengbojiang char h = tolower(*(++curr));
339*572c4311Sfengbojiang char l = tolower(*(++curr));
340*572c4311Sfengbojiang if (!isHexChar(h) || !isHexChar(l)) {
341*572c4311Sfengbojiang fprintf(stderr, "Illegal character in URI encoding\n");
342*572c4311Sfengbojiang exit(1);
343*572c4311Sfengbojiang }
344*572c4311Sfengbojiang char c = decodeHex(h, l);
345*572c4311Sfengbojiang ret = sdscatlen(ret, &c, 1);
346*572c4311Sfengbojiang curr++;
347*572c4311Sfengbojiang } else {
348*572c4311Sfengbojiang ret = sdscatlen(ret, curr++, 1);
349*572c4311Sfengbojiang }
350*572c4311Sfengbojiang }
351*572c4311Sfengbojiang
352*572c4311Sfengbojiang return ret;
353*572c4311Sfengbojiang }
354*572c4311Sfengbojiang
355*572c4311Sfengbojiang /* Parse a URI and extract the server connection information.
356*572c4311Sfengbojiang * URI scheme is based on the the provisional specification[1] excluding support
357*572c4311Sfengbojiang * for query parameters. Valid URIs are:
358*572c4311Sfengbojiang * scheme: "redis://"
359*572c4311Sfengbojiang * authority: [<username> ":"] <password> "@"] [<hostname> [":" <port>]]
360*572c4311Sfengbojiang * path: ["/" [<db>]]
361*572c4311Sfengbojiang *
362*572c4311Sfengbojiang * [1]: https://www.iana.org/assignments/uri-schemes/prov/redis */
parseRedisUri(const char * uri)363*572c4311Sfengbojiang static void parseRedisUri(const char *uri) {
364*572c4311Sfengbojiang
365*572c4311Sfengbojiang const char *scheme = "redis://";
366*572c4311Sfengbojiang const char *curr = uri;
367*572c4311Sfengbojiang const char *end = uri + strlen(uri);
368*572c4311Sfengbojiang const char *userinfo, *username, *port, *host, *path;
369*572c4311Sfengbojiang
370*572c4311Sfengbojiang /* URI must start with a valid scheme. */
371*572c4311Sfengbojiang if (strncasecmp(scheme, curr, strlen(scheme))) {
372*572c4311Sfengbojiang fprintf(stderr,"Invalid URI scheme\n");
373*572c4311Sfengbojiang exit(1);
374*572c4311Sfengbojiang }
375*572c4311Sfengbojiang curr += strlen(scheme);
376*572c4311Sfengbojiang if (curr == end) return;
377*572c4311Sfengbojiang
378*572c4311Sfengbojiang /* Extract user info. */
379*572c4311Sfengbojiang if ((userinfo = strchr(curr,'@'))) {
380*572c4311Sfengbojiang if ((username = strchr(curr, ':')) && username < userinfo) {
381*572c4311Sfengbojiang /* If provided, username is ignored. */
382*572c4311Sfengbojiang curr = username + 1;
383*572c4311Sfengbojiang }
384*572c4311Sfengbojiang
385*572c4311Sfengbojiang config.auth = percentDecode(curr, userinfo - curr);
386*572c4311Sfengbojiang curr = userinfo + 1;
387*572c4311Sfengbojiang }
388*572c4311Sfengbojiang if (curr == end) return;
389*572c4311Sfengbojiang
390*572c4311Sfengbojiang /* Extract host and port. */
391*572c4311Sfengbojiang path = strchr(curr, '/');
392*572c4311Sfengbojiang if (*curr != '/') {
393*572c4311Sfengbojiang host = path ? path - 1 : end;
394*572c4311Sfengbojiang if ((port = strchr(curr, ':'))) {
395*572c4311Sfengbojiang config.hostport = atoi(port + 1);
396*572c4311Sfengbojiang host = port - 1;
397*572c4311Sfengbojiang }
398*572c4311Sfengbojiang config.hostip = sdsnewlen(curr, host - curr + 1);
399*572c4311Sfengbojiang }
400*572c4311Sfengbojiang curr = path ? path + 1 : end;
401*572c4311Sfengbojiang if (curr == end) return;
402*572c4311Sfengbojiang
403*572c4311Sfengbojiang /* Extract database number. */
404*572c4311Sfengbojiang config.dbnum = atoi(curr);
405*572c4311Sfengbojiang }
406*572c4311Sfengbojiang
dictSdsHash(const void * key)407*572c4311Sfengbojiang static uint64_t dictSdsHash(const void *key) {
408*572c4311Sfengbojiang return dictGenHashFunction((unsigned char*)key, sdslen((char*)key));
409*572c4311Sfengbojiang }
410*572c4311Sfengbojiang
dictSdsKeyCompare(void * privdata,const void * key1,const void * key2)411*572c4311Sfengbojiang static int dictSdsKeyCompare(void *privdata, const void *key1,
412*572c4311Sfengbojiang const void *key2)
413*572c4311Sfengbojiang {
414*572c4311Sfengbojiang int l1,l2;
415*572c4311Sfengbojiang DICT_NOTUSED(privdata);
416*572c4311Sfengbojiang
417*572c4311Sfengbojiang l1 = sdslen((sds)key1);
418*572c4311Sfengbojiang l2 = sdslen((sds)key2);
419*572c4311Sfengbojiang if (l1 != l2) return 0;
420*572c4311Sfengbojiang return memcmp(key1, key2, l1) == 0;
421*572c4311Sfengbojiang }
422*572c4311Sfengbojiang
dictSdsDestructor(void * privdata,void * val)423*572c4311Sfengbojiang static void dictSdsDestructor(void *privdata, void *val)
424*572c4311Sfengbojiang {
425*572c4311Sfengbojiang DICT_NOTUSED(privdata);
426*572c4311Sfengbojiang sdsfree(val);
427*572c4311Sfengbojiang }
428*572c4311Sfengbojiang
dictListDestructor(void * privdata,void * val)429*572c4311Sfengbojiang void dictListDestructor(void *privdata, void *val)
430*572c4311Sfengbojiang {
431*572c4311Sfengbojiang DICT_NOTUSED(privdata);
432*572c4311Sfengbojiang listRelease((list*)val);
433*572c4311Sfengbojiang }
434*572c4311Sfengbojiang
435*572c4311Sfengbojiang /* _serverAssert is needed by dict */
_serverAssert(const char * estr,const char * file,int line)436*572c4311Sfengbojiang void _serverAssert(const char *estr, const char *file, int line) {
437*572c4311Sfengbojiang fprintf(stderr, "=== ASSERTION FAILED ===");
438*572c4311Sfengbojiang fprintf(stderr, "==> %s:%d '%s' is not true",file,line,estr);
439*572c4311Sfengbojiang *((char*)-1) = 'x';
440*572c4311Sfengbojiang }
441*572c4311Sfengbojiang
442*572c4311Sfengbojiang /*------------------------------------------------------------------------------
443*572c4311Sfengbojiang * Help functions
444*572c4311Sfengbojiang *--------------------------------------------------------------------------- */
445*572c4311Sfengbojiang
446*572c4311Sfengbojiang #define CLI_HELP_COMMAND 1
447*572c4311Sfengbojiang #define CLI_HELP_GROUP 2
448*572c4311Sfengbojiang
449*572c4311Sfengbojiang typedef struct {
450*572c4311Sfengbojiang int type;
451*572c4311Sfengbojiang int argc;
452*572c4311Sfengbojiang sds *argv;
453*572c4311Sfengbojiang sds full;
454*572c4311Sfengbojiang
455*572c4311Sfengbojiang /* Only used for help on commands */
456*572c4311Sfengbojiang struct commandHelp *org;
457*572c4311Sfengbojiang } helpEntry;
458*572c4311Sfengbojiang
459*572c4311Sfengbojiang static helpEntry *helpEntries;
460*572c4311Sfengbojiang static int helpEntriesLen;
461*572c4311Sfengbojiang
cliVersion(void)462*572c4311Sfengbojiang static sds cliVersion(void) {
463*572c4311Sfengbojiang sds version;
464*572c4311Sfengbojiang version = sdscatprintf(sdsempty(), "%s", REDIS_VERSION);
465*572c4311Sfengbojiang
466*572c4311Sfengbojiang /* Add git commit and working tree status when available */
467*572c4311Sfengbojiang if (strtoll(redisGitSHA1(),NULL,16)) {
468*572c4311Sfengbojiang version = sdscatprintf(version, " (git:%s", redisGitSHA1());
469*572c4311Sfengbojiang if (strtoll(redisGitDirty(),NULL,10))
470*572c4311Sfengbojiang version = sdscatprintf(version, "-dirty");
471*572c4311Sfengbojiang version = sdscat(version, ")");
472*572c4311Sfengbojiang }
473*572c4311Sfengbojiang return version;
474*572c4311Sfengbojiang }
475*572c4311Sfengbojiang
cliInitHelp(void)476*572c4311Sfengbojiang static void cliInitHelp(void) {
477*572c4311Sfengbojiang int commandslen = sizeof(commandHelp)/sizeof(struct commandHelp);
478*572c4311Sfengbojiang int groupslen = sizeof(commandGroups)/sizeof(char*);
479*572c4311Sfengbojiang int i, len, pos = 0;
480*572c4311Sfengbojiang helpEntry tmp;
481*572c4311Sfengbojiang
482*572c4311Sfengbojiang helpEntriesLen = len = commandslen+groupslen;
483*572c4311Sfengbojiang helpEntries = zmalloc(sizeof(helpEntry)*len);
484*572c4311Sfengbojiang
485*572c4311Sfengbojiang for (i = 0; i < groupslen; i++) {
486*572c4311Sfengbojiang tmp.argc = 1;
487*572c4311Sfengbojiang tmp.argv = zmalloc(sizeof(sds));
488*572c4311Sfengbojiang tmp.argv[0] = sdscatprintf(sdsempty(),"@%s",commandGroups[i]);
489*572c4311Sfengbojiang tmp.full = tmp.argv[0];
490*572c4311Sfengbojiang tmp.type = CLI_HELP_GROUP;
491*572c4311Sfengbojiang tmp.org = NULL;
492*572c4311Sfengbojiang helpEntries[pos++] = tmp;
493*572c4311Sfengbojiang }
494*572c4311Sfengbojiang
495*572c4311Sfengbojiang for (i = 0; i < commandslen; i++) {
496*572c4311Sfengbojiang tmp.argv = sdssplitargs(commandHelp[i].name,&tmp.argc);
497*572c4311Sfengbojiang tmp.full = sdsnew(commandHelp[i].name);
498*572c4311Sfengbojiang tmp.type = CLI_HELP_COMMAND;
499*572c4311Sfengbojiang tmp.org = &commandHelp[i];
500*572c4311Sfengbojiang helpEntries[pos++] = tmp;
501*572c4311Sfengbojiang }
502*572c4311Sfengbojiang }
503*572c4311Sfengbojiang
504*572c4311Sfengbojiang /* cliInitHelp() setups the helpEntries array with the command and group
505*572c4311Sfengbojiang * names from the help.h file. However the Redis instance we are connecting
506*572c4311Sfengbojiang * to may support more commands, so this function integrates the previous
507*572c4311Sfengbojiang * entries with additional entries obtained using the COMMAND command
508*572c4311Sfengbojiang * available in recent versions of Redis. */
cliIntegrateHelp(void)509*572c4311Sfengbojiang static void cliIntegrateHelp(void) {
510*572c4311Sfengbojiang if (cliConnect(CC_QUIET) == REDIS_ERR) return;
511*572c4311Sfengbojiang
512*572c4311Sfengbojiang redisReply *reply = redisCommand(context, "COMMAND");
513*572c4311Sfengbojiang if(reply == NULL || reply->type != REDIS_REPLY_ARRAY) return;
514*572c4311Sfengbojiang
515*572c4311Sfengbojiang /* Scan the array reported by COMMAND and fill only the entries that
516*572c4311Sfengbojiang * don't already match what we have. */
517*572c4311Sfengbojiang for (size_t j = 0; j < reply->elements; j++) {
518*572c4311Sfengbojiang redisReply *entry = reply->element[j];
519*572c4311Sfengbojiang if (entry->type != REDIS_REPLY_ARRAY || entry->elements < 4 ||
520*572c4311Sfengbojiang entry->element[0]->type != REDIS_REPLY_STRING ||
521*572c4311Sfengbojiang entry->element[1]->type != REDIS_REPLY_INTEGER ||
522*572c4311Sfengbojiang entry->element[3]->type != REDIS_REPLY_INTEGER) return;
523*572c4311Sfengbojiang char *cmdname = entry->element[0]->str;
524*572c4311Sfengbojiang int i;
525*572c4311Sfengbojiang
526*572c4311Sfengbojiang for (i = 0; i < helpEntriesLen; i++) {
527*572c4311Sfengbojiang helpEntry *he = helpEntries+i;
528*572c4311Sfengbojiang if (!strcasecmp(he->argv[0],cmdname))
529*572c4311Sfengbojiang break;
530*572c4311Sfengbojiang }
531*572c4311Sfengbojiang if (i != helpEntriesLen) continue;
532*572c4311Sfengbojiang
533*572c4311Sfengbojiang helpEntriesLen++;
534*572c4311Sfengbojiang helpEntries = zrealloc(helpEntries,sizeof(helpEntry)*helpEntriesLen);
535*572c4311Sfengbojiang helpEntry *new = helpEntries+(helpEntriesLen-1);
536*572c4311Sfengbojiang
537*572c4311Sfengbojiang new->argc = 1;
538*572c4311Sfengbojiang new->argv = zmalloc(sizeof(sds));
539*572c4311Sfengbojiang new->argv[0] = sdsnew(cmdname);
540*572c4311Sfengbojiang new->full = new->argv[0];
541*572c4311Sfengbojiang new->type = CLI_HELP_COMMAND;
542*572c4311Sfengbojiang sdstoupper(new->argv[0]);
543*572c4311Sfengbojiang
544*572c4311Sfengbojiang struct commandHelp *ch = zmalloc(sizeof(*ch));
545*572c4311Sfengbojiang ch->name = new->argv[0];
546*572c4311Sfengbojiang ch->params = sdsempty();
547*572c4311Sfengbojiang int args = llabs(entry->element[1]->integer);
548*572c4311Sfengbojiang args--; /* Remove the command name itself. */
549*572c4311Sfengbojiang if (entry->element[3]->integer == 1) {
550*572c4311Sfengbojiang ch->params = sdscat(ch->params,"key ");
551*572c4311Sfengbojiang args--;
552*572c4311Sfengbojiang }
553*572c4311Sfengbojiang while(args-- > 0) ch->params = sdscat(ch->params,"arg ");
554*572c4311Sfengbojiang if (entry->element[1]->integer < 0)
555*572c4311Sfengbojiang ch->params = sdscat(ch->params,"...options...");
556*572c4311Sfengbojiang ch->summary = "Help not available";
557*572c4311Sfengbojiang ch->group = 0;
558*572c4311Sfengbojiang ch->since = "not known";
559*572c4311Sfengbojiang new->org = ch;
560*572c4311Sfengbojiang }
561*572c4311Sfengbojiang freeReplyObject(reply);
562*572c4311Sfengbojiang }
563*572c4311Sfengbojiang
564*572c4311Sfengbojiang /* Output command help to stdout. */
cliOutputCommandHelp(struct commandHelp * help,int group)565*572c4311Sfengbojiang static void cliOutputCommandHelp(struct commandHelp *help, int group) {
566*572c4311Sfengbojiang printf("\r\n \x1b[1m%s\x1b[0m \x1b[90m%s\x1b[0m\r\n", help->name, help->params);
567*572c4311Sfengbojiang printf(" \x1b[33msummary:\x1b[0m %s\r\n", help->summary);
568*572c4311Sfengbojiang printf(" \x1b[33msince:\x1b[0m %s\r\n", help->since);
569*572c4311Sfengbojiang if (group) {
570*572c4311Sfengbojiang printf(" \x1b[33mgroup:\x1b[0m %s\r\n", commandGroups[help->group]);
571*572c4311Sfengbojiang }
572*572c4311Sfengbojiang }
573*572c4311Sfengbojiang
574*572c4311Sfengbojiang /* Print generic help. */
cliOutputGenericHelp(void)575*572c4311Sfengbojiang static void cliOutputGenericHelp(void) {
576*572c4311Sfengbojiang sds version = cliVersion();
577*572c4311Sfengbojiang printf(
578*572c4311Sfengbojiang "redis-cli %s\n"
579*572c4311Sfengbojiang "To get help about Redis commands type:\n"
580*572c4311Sfengbojiang " \"help @<group>\" to get a list of commands in <group>\n"
581*572c4311Sfengbojiang " \"help <command>\" for help on <command>\n"
582*572c4311Sfengbojiang " \"help <tab>\" to get a list of possible help topics\n"
583*572c4311Sfengbojiang " \"quit\" to exit\n"
584*572c4311Sfengbojiang "\n"
585*572c4311Sfengbojiang "To set redis-cli preferences:\n"
586*572c4311Sfengbojiang " \":set hints\" enable online hints\n"
587*572c4311Sfengbojiang " \":set nohints\" disable online hints\n"
588*572c4311Sfengbojiang "Set your preferences in ~/.redisclirc\n",
589*572c4311Sfengbojiang version
590*572c4311Sfengbojiang );
591*572c4311Sfengbojiang sdsfree(version);
592*572c4311Sfengbojiang }
593*572c4311Sfengbojiang
594*572c4311Sfengbojiang /* Output all command help, filtering by group or command name. */
cliOutputHelp(int argc,char ** argv)595*572c4311Sfengbojiang static void cliOutputHelp(int argc, char **argv) {
596*572c4311Sfengbojiang int i, j, len;
597*572c4311Sfengbojiang int group = -1;
598*572c4311Sfengbojiang helpEntry *entry;
599*572c4311Sfengbojiang struct commandHelp *help;
600*572c4311Sfengbojiang
601*572c4311Sfengbojiang if (argc == 0) {
602*572c4311Sfengbojiang cliOutputGenericHelp();
603*572c4311Sfengbojiang return;
604*572c4311Sfengbojiang } else if (argc > 0 && argv[0][0] == '@') {
605*572c4311Sfengbojiang len = sizeof(commandGroups)/sizeof(char*);
606*572c4311Sfengbojiang for (i = 0; i < len; i++) {
607*572c4311Sfengbojiang if (strcasecmp(argv[0]+1,commandGroups[i]) == 0) {
608*572c4311Sfengbojiang group = i;
609*572c4311Sfengbojiang break;
610*572c4311Sfengbojiang }
611*572c4311Sfengbojiang }
612*572c4311Sfengbojiang }
613*572c4311Sfengbojiang
614*572c4311Sfengbojiang assert(argc > 0);
615*572c4311Sfengbojiang for (i = 0; i < helpEntriesLen; i++) {
616*572c4311Sfengbojiang entry = &helpEntries[i];
617*572c4311Sfengbojiang if (entry->type != CLI_HELP_COMMAND) continue;
618*572c4311Sfengbojiang
619*572c4311Sfengbojiang help = entry->org;
620*572c4311Sfengbojiang if (group == -1) {
621*572c4311Sfengbojiang /* Compare all arguments */
622*572c4311Sfengbojiang if (argc == entry->argc) {
623*572c4311Sfengbojiang for (j = 0; j < argc; j++) {
624*572c4311Sfengbojiang if (strcasecmp(argv[j],entry->argv[j]) != 0) break;
625*572c4311Sfengbojiang }
626*572c4311Sfengbojiang if (j == argc) {
627*572c4311Sfengbojiang cliOutputCommandHelp(help,1);
628*572c4311Sfengbojiang }
629*572c4311Sfengbojiang }
630*572c4311Sfengbojiang } else {
631*572c4311Sfengbojiang if (group == help->group) {
632*572c4311Sfengbojiang cliOutputCommandHelp(help,0);
633*572c4311Sfengbojiang }
634*572c4311Sfengbojiang }
635*572c4311Sfengbojiang }
636*572c4311Sfengbojiang printf("\r\n");
637*572c4311Sfengbojiang }
638*572c4311Sfengbojiang
639*572c4311Sfengbojiang /* Linenoise completion callback. */
completionCallback(const char * buf,linenoiseCompletions * lc)640*572c4311Sfengbojiang static void completionCallback(const char *buf, linenoiseCompletions *lc) {
641*572c4311Sfengbojiang size_t startpos = 0;
642*572c4311Sfengbojiang int mask;
643*572c4311Sfengbojiang int i;
644*572c4311Sfengbojiang size_t matchlen;
645*572c4311Sfengbojiang sds tmp;
646*572c4311Sfengbojiang
647*572c4311Sfengbojiang if (strncasecmp(buf,"help ",5) == 0) {
648*572c4311Sfengbojiang startpos = 5;
649*572c4311Sfengbojiang while (isspace(buf[startpos])) startpos++;
650*572c4311Sfengbojiang mask = CLI_HELP_COMMAND | CLI_HELP_GROUP;
651*572c4311Sfengbojiang } else {
652*572c4311Sfengbojiang mask = CLI_HELP_COMMAND;
653*572c4311Sfengbojiang }
654*572c4311Sfengbojiang
655*572c4311Sfengbojiang for (i = 0; i < helpEntriesLen; i++) {
656*572c4311Sfengbojiang if (!(helpEntries[i].type & mask)) continue;
657*572c4311Sfengbojiang
658*572c4311Sfengbojiang matchlen = strlen(buf+startpos);
659*572c4311Sfengbojiang if (strncasecmp(buf+startpos,helpEntries[i].full,matchlen) == 0) {
660*572c4311Sfengbojiang tmp = sdsnewlen(buf,startpos);
661*572c4311Sfengbojiang tmp = sdscat(tmp,helpEntries[i].full);
662*572c4311Sfengbojiang linenoiseAddCompletion(lc,tmp);
663*572c4311Sfengbojiang sdsfree(tmp);
664*572c4311Sfengbojiang }
665*572c4311Sfengbojiang }
666*572c4311Sfengbojiang }
667*572c4311Sfengbojiang
668*572c4311Sfengbojiang /* Linenoise hints callback. */
hintsCallback(const char * buf,int * color,int * bold)669*572c4311Sfengbojiang static char *hintsCallback(const char *buf, int *color, int *bold) {
670*572c4311Sfengbojiang if (!pref.hints) return NULL;
671*572c4311Sfengbojiang
672*572c4311Sfengbojiang int i, argc, buflen = strlen(buf);
673*572c4311Sfengbojiang sds *argv = sdssplitargs(buf,&argc);
674*572c4311Sfengbojiang int endspace = buflen && isspace(buf[buflen-1]);
675*572c4311Sfengbojiang
676*572c4311Sfengbojiang /* Check if the argument list is empty and return ASAP. */
677*572c4311Sfengbojiang if (argc == 0) {
678*572c4311Sfengbojiang sdsfreesplitres(argv,argc);
679*572c4311Sfengbojiang return NULL;
680*572c4311Sfengbojiang }
681*572c4311Sfengbojiang
682*572c4311Sfengbojiang for (i = 0; i < helpEntriesLen; i++) {
683*572c4311Sfengbojiang if (!(helpEntries[i].type & CLI_HELP_COMMAND)) continue;
684*572c4311Sfengbojiang
685*572c4311Sfengbojiang if (strcasecmp(argv[0],helpEntries[i].full) == 0)
686*572c4311Sfengbojiang {
687*572c4311Sfengbojiang *color = 90;
688*572c4311Sfengbojiang *bold = 0;
689*572c4311Sfengbojiang sds hint = sdsnew(helpEntries[i].org->params);
690*572c4311Sfengbojiang
691*572c4311Sfengbojiang /* Remove arguments from the returned hint to show only the
692*572c4311Sfengbojiang * ones the user did not yet typed. */
693*572c4311Sfengbojiang int toremove = argc-1;
694*572c4311Sfengbojiang while(toremove > 0 && sdslen(hint)) {
695*572c4311Sfengbojiang if (hint[0] == '[') break;
696*572c4311Sfengbojiang if (hint[0] == ' ') toremove--;
697*572c4311Sfengbojiang sdsrange(hint,1,-1);
698*572c4311Sfengbojiang }
699*572c4311Sfengbojiang
700*572c4311Sfengbojiang /* Add an initial space if needed. */
701*572c4311Sfengbojiang if (!endspace) {
702*572c4311Sfengbojiang sds newhint = sdsnewlen(" ",1);
703*572c4311Sfengbojiang newhint = sdscatsds(newhint,hint);
704*572c4311Sfengbojiang sdsfree(hint);
705*572c4311Sfengbojiang hint = newhint;
706*572c4311Sfengbojiang }
707*572c4311Sfengbojiang
708*572c4311Sfengbojiang sdsfreesplitres(argv,argc);
709*572c4311Sfengbojiang return hint;
710*572c4311Sfengbojiang }
711*572c4311Sfengbojiang }
712*572c4311Sfengbojiang sdsfreesplitres(argv,argc);
713*572c4311Sfengbojiang return NULL;
714*572c4311Sfengbojiang }
715*572c4311Sfengbojiang
freeHintsCallback(void * ptr)716*572c4311Sfengbojiang static void freeHintsCallback(void *ptr) {
717*572c4311Sfengbojiang sdsfree(ptr);
718*572c4311Sfengbojiang }
719*572c4311Sfengbojiang
720*572c4311Sfengbojiang /*------------------------------------------------------------------------------
721*572c4311Sfengbojiang * Networking / parsing
722*572c4311Sfengbojiang *--------------------------------------------------------------------------- */
723*572c4311Sfengbojiang
724*572c4311Sfengbojiang /* Send AUTH command to the server */
cliAuth(void)725*572c4311Sfengbojiang static int cliAuth(void) {
726*572c4311Sfengbojiang redisReply *reply;
727*572c4311Sfengbojiang if (config.auth == NULL) return REDIS_OK;
728*572c4311Sfengbojiang
729*572c4311Sfengbojiang reply = redisCommand(context,"AUTH %s",config.auth);
730*572c4311Sfengbojiang if (reply != NULL) {
731*572c4311Sfengbojiang freeReplyObject(reply);
732*572c4311Sfengbojiang return REDIS_OK;
733*572c4311Sfengbojiang }
734*572c4311Sfengbojiang return REDIS_ERR;
735*572c4311Sfengbojiang }
736*572c4311Sfengbojiang
737*572c4311Sfengbojiang /* Send SELECT dbnum to the server */
cliSelect(void)738*572c4311Sfengbojiang static int cliSelect(void) {
739*572c4311Sfengbojiang redisReply *reply;
740*572c4311Sfengbojiang if (config.dbnum == 0) return REDIS_OK;
741*572c4311Sfengbojiang
742*572c4311Sfengbojiang reply = redisCommand(context,"SELECT %d",config.dbnum);
743*572c4311Sfengbojiang if (reply != NULL) {
744*572c4311Sfengbojiang int result = REDIS_OK;
745*572c4311Sfengbojiang if (reply->type == REDIS_REPLY_ERROR) result = REDIS_ERR;
746*572c4311Sfengbojiang freeReplyObject(reply);
747*572c4311Sfengbojiang return result;
748*572c4311Sfengbojiang }
749*572c4311Sfengbojiang return REDIS_ERR;
750*572c4311Sfengbojiang }
751*572c4311Sfengbojiang
752*572c4311Sfengbojiang /* Connect to the server. It is possible to pass certain flags to the function:
753*572c4311Sfengbojiang * CC_FORCE: The connection is performed even if there is already
754*572c4311Sfengbojiang * a connected socket.
755*572c4311Sfengbojiang * CC_QUIET: Don't print errors if connection fails. */
cliConnect(int flags)756*572c4311Sfengbojiang static int cliConnect(int flags) {
757*572c4311Sfengbojiang if (context == NULL || flags & CC_FORCE) {
758*572c4311Sfengbojiang if (context != NULL) {
759*572c4311Sfengbojiang redisFree(context);
760*572c4311Sfengbojiang }
761*572c4311Sfengbojiang
762*572c4311Sfengbojiang if (config.hostsocket == NULL) {
763*572c4311Sfengbojiang context = redisConnect(config.hostip,config.hostport);
764*572c4311Sfengbojiang } else {
765*572c4311Sfengbojiang context = redisConnectUnix(config.hostsocket);
766*572c4311Sfengbojiang }
767*572c4311Sfengbojiang
768*572c4311Sfengbojiang if (context->err) {
769*572c4311Sfengbojiang if (!(flags & CC_QUIET)) {
770*572c4311Sfengbojiang fprintf(stderr,"Could not connect to Redis at ");
771*572c4311Sfengbojiang if (config.hostsocket == NULL)
772*572c4311Sfengbojiang fprintf(stderr,"%s:%d: %s\n",
773*572c4311Sfengbojiang config.hostip,config.hostport,context->errstr);
774*572c4311Sfengbojiang else
775*572c4311Sfengbojiang fprintf(stderr,"%s: %s\n",
776*572c4311Sfengbojiang config.hostsocket,context->errstr);
777*572c4311Sfengbojiang }
778*572c4311Sfengbojiang redisFree(context);
779*572c4311Sfengbojiang context = NULL;
780*572c4311Sfengbojiang return REDIS_ERR;
781*572c4311Sfengbojiang }
782*572c4311Sfengbojiang
783*572c4311Sfengbojiang /* Set aggressive KEEP_ALIVE socket option in the Redis context socket
784*572c4311Sfengbojiang * in order to prevent timeouts caused by the execution of long
785*572c4311Sfengbojiang * commands. At the same time this improves the detection of real
786*572c4311Sfengbojiang * errors. */
787*572c4311Sfengbojiang anetKeepAlive(NULL, context->fd, REDIS_CLI_KEEPALIVE_INTERVAL);
788*572c4311Sfengbojiang
789*572c4311Sfengbojiang /* Do AUTH and select the right DB. */
790*572c4311Sfengbojiang if (cliAuth() != REDIS_OK)
791*572c4311Sfengbojiang return REDIS_ERR;
792*572c4311Sfengbojiang if (cliSelect() != REDIS_OK)
793*572c4311Sfengbojiang return REDIS_ERR;
794*572c4311Sfengbojiang }
795*572c4311Sfengbojiang return REDIS_OK;
796*572c4311Sfengbojiang }
797*572c4311Sfengbojiang
cliPrintContextError(void)798*572c4311Sfengbojiang static void cliPrintContextError(void) {
799*572c4311Sfengbojiang if (context == NULL) return;
800*572c4311Sfengbojiang fprintf(stderr,"Error: %s\n",context->errstr);
801*572c4311Sfengbojiang }
802*572c4311Sfengbojiang
cliFormatReplyTTY(redisReply * r,char * prefix)803*572c4311Sfengbojiang static sds cliFormatReplyTTY(redisReply *r, char *prefix) {
804*572c4311Sfengbojiang sds out = sdsempty();
805*572c4311Sfengbojiang switch (r->type) {
806*572c4311Sfengbojiang case REDIS_REPLY_ERROR:
807*572c4311Sfengbojiang out = sdscatprintf(out,"(error) %s\n", r->str);
808*572c4311Sfengbojiang break;
809*572c4311Sfengbojiang case REDIS_REPLY_STATUS:
810*572c4311Sfengbojiang out = sdscat(out,r->str);
811*572c4311Sfengbojiang out = sdscat(out,"\n");
812*572c4311Sfengbojiang break;
813*572c4311Sfengbojiang case REDIS_REPLY_INTEGER:
814*572c4311Sfengbojiang out = sdscatprintf(out,"(integer) %lld\n",r->integer);
815*572c4311Sfengbojiang break;
816*572c4311Sfengbojiang case REDIS_REPLY_STRING:
817*572c4311Sfengbojiang /* If you are producing output for the standard output we want
818*572c4311Sfengbojiang * a more interesting output with quoted characters and so forth */
819*572c4311Sfengbojiang out = sdscatrepr(out,r->str,r->len);
820*572c4311Sfengbojiang out = sdscat(out,"\n");
821*572c4311Sfengbojiang break;
822*572c4311Sfengbojiang case REDIS_REPLY_NIL:
823*572c4311Sfengbojiang out = sdscat(out,"(nil)\n");
824*572c4311Sfengbojiang break;
825*572c4311Sfengbojiang case REDIS_REPLY_ARRAY:
826*572c4311Sfengbojiang if (r->elements == 0) {
827*572c4311Sfengbojiang out = sdscat(out,"(empty list or set)\n");
828*572c4311Sfengbojiang } else {
829*572c4311Sfengbojiang unsigned int i, idxlen = 0;
830*572c4311Sfengbojiang char _prefixlen[16];
831*572c4311Sfengbojiang char _prefixfmt[16];
832*572c4311Sfengbojiang sds _prefix;
833*572c4311Sfengbojiang sds tmp;
834*572c4311Sfengbojiang
835*572c4311Sfengbojiang /* Calculate chars needed to represent the largest index */
836*572c4311Sfengbojiang i = r->elements;
837*572c4311Sfengbojiang do {
838*572c4311Sfengbojiang idxlen++;
839*572c4311Sfengbojiang i /= 10;
840*572c4311Sfengbojiang } while(i);
841*572c4311Sfengbojiang
842*572c4311Sfengbojiang /* Prefix for nested multi bulks should grow with idxlen+2 spaces */
843*572c4311Sfengbojiang memset(_prefixlen,' ',idxlen+2);
844*572c4311Sfengbojiang _prefixlen[idxlen+2] = '\0';
845*572c4311Sfengbojiang _prefix = sdscat(sdsnew(prefix),_prefixlen);
846*572c4311Sfengbojiang
847*572c4311Sfengbojiang /* Setup prefix format for every entry */
848*572c4311Sfengbojiang snprintf(_prefixfmt,sizeof(_prefixfmt),"%%s%%%ud) ",idxlen);
849*572c4311Sfengbojiang
850*572c4311Sfengbojiang for (i = 0; i < r->elements; i++) {
851*572c4311Sfengbojiang /* Don't use the prefix for the first element, as the parent
852*572c4311Sfengbojiang * caller already prepended the index number. */
853*572c4311Sfengbojiang out = sdscatprintf(out,_prefixfmt,i == 0 ? "" : prefix,i+1);
854*572c4311Sfengbojiang
855*572c4311Sfengbojiang /* Format the multi bulk entry */
856*572c4311Sfengbojiang tmp = cliFormatReplyTTY(r->element[i],_prefix);
857*572c4311Sfengbojiang out = sdscatlen(out,tmp,sdslen(tmp));
858*572c4311Sfengbojiang sdsfree(tmp);
859*572c4311Sfengbojiang }
860*572c4311Sfengbojiang sdsfree(_prefix);
861*572c4311Sfengbojiang }
862*572c4311Sfengbojiang break;
863*572c4311Sfengbojiang default:
864*572c4311Sfengbojiang fprintf(stderr,"Unknown reply type: %d\n", r->type);
865*572c4311Sfengbojiang exit(1);
866*572c4311Sfengbojiang }
867*572c4311Sfengbojiang return out;
868*572c4311Sfengbojiang }
869*572c4311Sfengbojiang
isColorTerm(void)870*572c4311Sfengbojiang int isColorTerm(void) {
871*572c4311Sfengbojiang char *t = getenv("TERM");
872*572c4311Sfengbojiang return t != NULL && strstr(t,"xterm") != NULL;
873*572c4311Sfengbojiang }
874*572c4311Sfengbojiang
875*572c4311Sfengbojiang /* Helper function for sdsCatColorizedLdbReply() appending colorize strings
876*572c4311Sfengbojiang * to an SDS string. */
sdscatcolor(sds o,char * s,size_t len,char * color)877*572c4311Sfengbojiang sds sdscatcolor(sds o, char *s, size_t len, char *color) {
878*572c4311Sfengbojiang if (!isColorTerm()) return sdscatlen(o,s,len);
879*572c4311Sfengbojiang
880*572c4311Sfengbojiang int bold = strstr(color,"bold") != NULL;
881*572c4311Sfengbojiang int ccode = 37; /* Defaults to white. */
882*572c4311Sfengbojiang if (strstr(color,"red")) ccode = 31;
883*572c4311Sfengbojiang else if (strstr(color,"green")) ccode = 32;
884*572c4311Sfengbojiang else if (strstr(color,"yellow")) ccode = 33;
885*572c4311Sfengbojiang else if (strstr(color,"blue")) ccode = 34;
886*572c4311Sfengbojiang else if (strstr(color,"magenta")) ccode = 35;
887*572c4311Sfengbojiang else if (strstr(color,"cyan")) ccode = 36;
888*572c4311Sfengbojiang else if (strstr(color,"white")) ccode = 37;
889*572c4311Sfengbojiang
890*572c4311Sfengbojiang o = sdscatfmt(o,"\033[%i;%i;49m",bold,ccode);
891*572c4311Sfengbojiang o = sdscatlen(o,s,len);
892*572c4311Sfengbojiang o = sdscat(o,"\033[0m");
893*572c4311Sfengbojiang return o;
894*572c4311Sfengbojiang }
895*572c4311Sfengbojiang
896*572c4311Sfengbojiang /* Colorize Lua debugger status replies according to the prefix they
897*572c4311Sfengbojiang * have. */
sdsCatColorizedLdbReply(sds o,char * s,size_t len)898*572c4311Sfengbojiang sds sdsCatColorizedLdbReply(sds o, char *s, size_t len) {
899*572c4311Sfengbojiang char *color = "white";
900*572c4311Sfengbojiang
901*572c4311Sfengbojiang if (strstr(s,"<debug>")) color = "bold";
902*572c4311Sfengbojiang if (strstr(s,"<redis>")) color = "green";
903*572c4311Sfengbojiang if (strstr(s,"<reply>")) color = "cyan";
904*572c4311Sfengbojiang if (strstr(s,"<error>")) color = "red";
905*572c4311Sfengbojiang if (strstr(s,"<hint>")) color = "bold";
906*572c4311Sfengbojiang if (strstr(s,"<value>") || strstr(s,"<retval>")) color = "magenta";
907*572c4311Sfengbojiang if (len > 4 && isdigit(s[3])) {
908*572c4311Sfengbojiang if (s[1] == '>') color = "yellow"; /* Current line. */
909*572c4311Sfengbojiang else if (s[2] == '#') color = "bold"; /* Break point. */
910*572c4311Sfengbojiang }
911*572c4311Sfengbojiang return sdscatcolor(o,s,len,color);
912*572c4311Sfengbojiang }
913*572c4311Sfengbojiang
cliFormatReplyRaw(redisReply * r)914*572c4311Sfengbojiang static sds cliFormatReplyRaw(redisReply *r) {
915*572c4311Sfengbojiang sds out = sdsempty(), tmp;
916*572c4311Sfengbojiang size_t i;
917*572c4311Sfengbojiang
918*572c4311Sfengbojiang switch (r->type) {
919*572c4311Sfengbojiang case REDIS_REPLY_NIL:
920*572c4311Sfengbojiang /* Nothing... */
921*572c4311Sfengbojiang break;
922*572c4311Sfengbojiang case REDIS_REPLY_ERROR:
923*572c4311Sfengbojiang out = sdscatlen(out,r->str,r->len);
924*572c4311Sfengbojiang out = sdscatlen(out,"\n",1);
925*572c4311Sfengbojiang break;
926*572c4311Sfengbojiang case REDIS_REPLY_STATUS:
927*572c4311Sfengbojiang case REDIS_REPLY_STRING:
928*572c4311Sfengbojiang if (r->type == REDIS_REPLY_STATUS && config.eval_ldb) {
929*572c4311Sfengbojiang /* The Lua debugger replies with arrays of simple (status)
930*572c4311Sfengbojiang * strings. We colorize the output for more fun if this
931*572c4311Sfengbojiang * is a debugging session. */
932*572c4311Sfengbojiang
933*572c4311Sfengbojiang /* Detect the end of a debugging session. */
934*572c4311Sfengbojiang if (strstr(r->str,"<endsession>") == r->str) {
935*572c4311Sfengbojiang config.enable_ldb_on_eval = 0;
936*572c4311Sfengbojiang config.eval_ldb = 0;
937*572c4311Sfengbojiang config.eval_ldb_end = 1; /* Signal the caller session ended. */
938*572c4311Sfengbojiang config.output = OUTPUT_STANDARD;
939*572c4311Sfengbojiang cliRefreshPrompt();
940*572c4311Sfengbojiang } else {
941*572c4311Sfengbojiang out = sdsCatColorizedLdbReply(out,r->str,r->len);
942*572c4311Sfengbojiang }
943*572c4311Sfengbojiang } else {
944*572c4311Sfengbojiang out = sdscatlen(out,r->str,r->len);
945*572c4311Sfengbojiang }
946*572c4311Sfengbojiang break;
947*572c4311Sfengbojiang case REDIS_REPLY_INTEGER:
948*572c4311Sfengbojiang out = sdscatprintf(out,"%lld",r->integer);
949*572c4311Sfengbojiang break;
950*572c4311Sfengbojiang case REDIS_REPLY_ARRAY:
951*572c4311Sfengbojiang for (i = 0; i < r->elements; i++) {
952*572c4311Sfengbojiang if (i > 0) out = sdscat(out,config.mb_delim);
953*572c4311Sfengbojiang tmp = cliFormatReplyRaw(r->element[i]);
954*572c4311Sfengbojiang out = sdscatlen(out,tmp,sdslen(tmp));
955*572c4311Sfengbojiang sdsfree(tmp);
956*572c4311Sfengbojiang }
957*572c4311Sfengbojiang break;
958*572c4311Sfengbojiang default:
959*572c4311Sfengbojiang fprintf(stderr,"Unknown reply type: %d\n", r->type);
960*572c4311Sfengbojiang exit(1);
961*572c4311Sfengbojiang }
962*572c4311Sfengbojiang return out;
963*572c4311Sfengbojiang }
964*572c4311Sfengbojiang
cliFormatReplyCSV(redisReply * r)965*572c4311Sfengbojiang static sds cliFormatReplyCSV(redisReply *r) {
966*572c4311Sfengbojiang unsigned int i;
967*572c4311Sfengbojiang
968*572c4311Sfengbojiang sds out = sdsempty();
969*572c4311Sfengbojiang switch (r->type) {
970*572c4311Sfengbojiang case REDIS_REPLY_ERROR:
971*572c4311Sfengbojiang out = sdscat(out,"ERROR,");
972*572c4311Sfengbojiang out = sdscatrepr(out,r->str,strlen(r->str));
973*572c4311Sfengbojiang break;
974*572c4311Sfengbojiang case REDIS_REPLY_STATUS:
975*572c4311Sfengbojiang out = sdscatrepr(out,r->str,r->len);
976*572c4311Sfengbojiang break;
977*572c4311Sfengbojiang case REDIS_REPLY_INTEGER:
978*572c4311Sfengbojiang out = sdscatprintf(out,"%lld",r->integer);
979*572c4311Sfengbojiang break;
980*572c4311Sfengbojiang case REDIS_REPLY_STRING:
981*572c4311Sfengbojiang out = sdscatrepr(out,r->str,r->len);
982*572c4311Sfengbojiang break;
983*572c4311Sfengbojiang case REDIS_REPLY_NIL:
984*572c4311Sfengbojiang out = sdscat(out,"NIL");
985*572c4311Sfengbojiang break;
986*572c4311Sfengbojiang case REDIS_REPLY_ARRAY:
987*572c4311Sfengbojiang for (i = 0; i < r->elements; i++) {
988*572c4311Sfengbojiang sds tmp = cliFormatReplyCSV(r->element[i]);
989*572c4311Sfengbojiang out = sdscatlen(out,tmp,sdslen(tmp));
990*572c4311Sfengbojiang if (i != r->elements-1) out = sdscat(out,",");
991*572c4311Sfengbojiang sdsfree(tmp);
992*572c4311Sfengbojiang }
993*572c4311Sfengbojiang break;
994*572c4311Sfengbojiang default:
995*572c4311Sfengbojiang fprintf(stderr,"Unknown reply type: %d\n", r->type);
996*572c4311Sfengbojiang exit(1);
997*572c4311Sfengbojiang }
998*572c4311Sfengbojiang return out;
999*572c4311Sfengbojiang }
1000*572c4311Sfengbojiang
cliReadReply(int output_raw_strings)1001*572c4311Sfengbojiang static int cliReadReply(int output_raw_strings) {
1002*572c4311Sfengbojiang void *_reply;
1003*572c4311Sfengbojiang redisReply *reply;
1004*572c4311Sfengbojiang sds out = NULL;
1005*572c4311Sfengbojiang int output = 1;
1006*572c4311Sfengbojiang
1007*572c4311Sfengbojiang if (redisGetReply(context,&_reply) != REDIS_OK) {
1008*572c4311Sfengbojiang if (config.shutdown) {
1009*572c4311Sfengbojiang redisFree(context);
1010*572c4311Sfengbojiang context = NULL;
1011*572c4311Sfengbojiang return REDIS_OK;
1012*572c4311Sfengbojiang }
1013*572c4311Sfengbojiang if (config.interactive) {
1014*572c4311Sfengbojiang /* Filter cases where we should reconnect */
1015*572c4311Sfengbojiang if (context->err == REDIS_ERR_IO &&
1016*572c4311Sfengbojiang (errno == ECONNRESET || errno == EPIPE))
1017*572c4311Sfengbojiang return REDIS_ERR;
1018*572c4311Sfengbojiang if (context->err == REDIS_ERR_EOF)
1019*572c4311Sfengbojiang return REDIS_ERR;
1020*572c4311Sfengbojiang }
1021*572c4311Sfengbojiang cliPrintContextError();
1022*572c4311Sfengbojiang exit(1);
1023*572c4311Sfengbojiang return REDIS_ERR; /* avoid compiler warning */
1024*572c4311Sfengbojiang }
1025*572c4311Sfengbojiang
1026*572c4311Sfengbojiang reply = (redisReply*)_reply;
1027*572c4311Sfengbojiang
1028*572c4311Sfengbojiang config.last_cmd_type = reply->type;
1029*572c4311Sfengbojiang
1030*572c4311Sfengbojiang /* Check if we need to connect to a different node and reissue the
1031*572c4311Sfengbojiang * request. */
1032*572c4311Sfengbojiang if (config.cluster_mode && reply->type == REDIS_REPLY_ERROR &&
1033*572c4311Sfengbojiang (!strncmp(reply->str,"MOVED",5) || !strcmp(reply->str,"ASK")))
1034*572c4311Sfengbojiang {
1035*572c4311Sfengbojiang char *p = reply->str, *s;
1036*572c4311Sfengbojiang int slot;
1037*572c4311Sfengbojiang
1038*572c4311Sfengbojiang output = 0;
1039*572c4311Sfengbojiang /* Comments show the position of the pointer as:
1040*572c4311Sfengbojiang *
1041*572c4311Sfengbojiang * [S] for pointer 's'
1042*572c4311Sfengbojiang * [P] for pointer 'p'
1043*572c4311Sfengbojiang */
1044*572c4311Sfengbojiang s = strchr(p,' '); /* MOVED[S]3999 127.0.0.1:6381 */
1045*572c4311Sfengbojiang p = strchr(s+1,' '); /* MOVED[S]3999[P]127.0.0.1:6381 */
1046*572c4311Sfengbojiang *p = '\0';
1047*572c4311Sfengbojiang slot = atoi(s+1);
1048*572c4311Sfengbojiang s = strrchr(p+1,':'); /* MOVED 3999[P]127.0.0.1[S]6381 */
1049*572c4311Sfengbojiang *s = '\0';
1050*572c4311Sfengbojiang sdsfree(config.hostip);
1051*572c4311Sfengbojiang config.hostip = sdsnew(p+1);
1052*572c4311Sfengbojiang config.hostport = atoi(s+1);
1053*572c4311Sfengbojiang if (config.interactive)
1054*572c4311Sfengbojiang printf("-> Redirected to slot [%d] located at %s:%d\n",
1055*572c4311Sfengbojiang slot, config.hostip, config.hostport);
1056*572c4311Sfengbojiang config.cluster_reissue_command = 1;
1057*572c4311Sfengbojiang cliRefreshPrompt();
1058*572c4311Sfengbojiang }
1059*572c4311Sfengbojiang
1060*572c4311Sfengbojiang if (output) {
1061*572c4311Sfengbojiang if (output_raw_strings) {
1062*572c4311Sfengbojiang out = cliFormatReplyRaw(reply);
1063*572c4311Sfengbojiang } else {
1064*572c4311Sfengbojiang if (config.output == OUTPUT_RAW) {
1065*572c4311Sfengbojiang out = cliFormatReplyRaw(reply);
1066*572c4311Sfengbojiang out = sdscat(out,"\n");
1067*572c4311Sfengbojiang } else if (config.output == OUTPUT_STANDARD) {
1068*572c4311Sfengbojiang out = cliFormatReplyTTY(reply,"");
1069*572c4311Sfengbojiang } else if (config.output == OUTPUT_CSV) {
1070*572c4311Sfengbojiang out = cliFormatReplyCSV(reply);
1071*572c4311Sfengbojiang out = sdscat(out,"\n");
1072*572c4311Sfengbojiang }
1073*572c4311Sfengbojiang }
1074*572c4311Sfengbojiang fwrite(out,sdslen(out),1,stdout);
1075*572c4311Sfengbojiang sdsfree(out);
1076*572c4311Sfengbojiang }
1077*572c4311Sfengbojiang freeReplyObject(reply);
1078*572c4311Sfengbojiang return REDIS_OK;
1079*572c4311Sfengbojiang }
1080*572c4311Sfengbojiang
cliSendCommand(int argc,char ** argv,long repeat)1081*572c4311Sfengbojiang static int cliSendCommand(int argc, char **argv, long repeat) {
1082*572c4311Sfengbojiang char *command = argv[0];
1083*572c4311Sfengbojiang size_t *argvlen;
1084*572c4311Sfengbojiang int j, output_raw;
1085*572c4311Sfengbojiang
1086*572c4311Sfengbojiang if (!config.eval_ldb && /* In debugging mode, let's pass "help" to Redis. */
1087*572c4311Sfengbojiang (!strcasecmp(command,"help") || !strcasecmp(command,"?"))) {
1088*572c4311Sfengbojiang cliOutputHelp(--argc, ++argv);
1089*572c4311Sfengbojiang return REDIS_OK;
1090*572c4311Sfengbojiang }
1091*572c4311Sfengbojiang
1092*572c4311Sfengbojiang if (context == NULL) return REDIS_ERR;
1093*572c4311Sfengbojiang
1094*572c4311Sfengbojiang output_raw = 0;
1095*572c4311Sfengbojiang if (!strcasecmp(command,"info") ||
1096*572c4311Sfengbojiang !strcasecmp(command,"lolwut") ||
1097*572c4311Sfengbojiang (argc >= 2 && !strcasecmp(command,"debug") &&
1098*572c4311Sfengbojiang !strcasecmp(argv[1],"htstats")) ||
1099*572c4311Sfengbojiang (argc >= 2 && !strcasecmp(command,"debug") &&
1100*572c4311Sfengbojiang !strcasecmp(argv[1],"htstats-key")) ||
1101*572c4311Sfengbojiang (argc >= 2 && !strcasecmp(command,"memory") &&
1102*572c4311Sfengbojiang (!strcasecmp(argv[1],"malloc-stats") ||
1103*572c4311Sfengbojiang !strcasecmp(argv[1],"doctor"))) ||
1104*572c4311Sfengbojiang (argc == 2 && !strcasecmp(command,"cluster") &&
1105*572c4311Sfengbojiang (!strcasecmp(argv[1],"nodes") ||
1106*572c4311Sfengbojiang !strcasecmp(argv[1],"info"))) ||
1107*572c4311Sfengbojiang (argc >= 2 && !strcasecmp(command,"client") &&
1108*572c4311Sfengbojiang !strcasecmp(argv[1],"list")) ||
1109*572c4311Sfengbojiang (argc == 3 && !strcasecmp(command,"latency") &&
1110*572c4311Sfengbojiang !strcasecmp(argv[1],"graph")) ||
1111*572c4311Sfengbojiang (argc == 2 && !strcasecmp(command,"latency") &&
1112*572c4311Sfengbojiang !strcasecmp(argv[1],"doctor")))
1113*572c4311Sfengbojiang {
1114*572c4311Sfengbojiang output_raw = 1;
1115*572c4311Sfengbojiang }
1116*572c4311Sfengbojiang
1117*572c4311Sfengbojiang if (!strcasecmp(command,"shutdown")) config.shutdown = 1;
1118*572c4311Sfengbojiang if (!strcasecmp(command,"monitor")) config.monitor_mode = 1;
1119*572c4311Sfengbojiang if (!strcasecmp(command,"subscribe") ||
1120*572c4311Sfengbojiang !strcasecmp(command,"psubscribe")) config.pubsub_mode = 1;
1121*572c4311Sfengbojiang if (!strcasecmp(command,"sync") ||
1122*572c4311Sfengbojiang !strcasecmp(command,"psync")) config.slave_mode = 1;
1123*572c4311Sfengbojiang
1124*572c4311Sfengbojiang /* When the user manually calls SCRIPT DEBUG, setup the activation of
1125*572c4311Sfengbojiang * debugging mode on the next eval if needed. */
1126*572c4311Sfengbojiang if (argc == 3 && !strcasecmp(argv[0],"script") &&
1127*572c4311Sfengbojiang !strcasecmp(argv[1],"debug"))
1128*572c4311Sfengbojiang {
1129*572c4311Sfengbojiang if (!strcasecmp(argv[2],"yes") || !strcasecmp(argv[2],"sync")) {
1130*572c4311Sfengbojiang config.enable_ldb_on_eval = 1;
1131*572c4311Sfengbojiang } else {
1132*572c4311Sfengbojiang config.enable_ldb_on_eval = 0;
1133*572c4311Sfengbojiang }
1134*572c4311Sfengbojiang }
1135*572c4311Sfengbojiang
1136*572c4311Sfengbojiang /* Actually activate LDB on EVAL if needed. */
1137*572c4311Sfengbojiang if (!strcasecmp(command,"eval") && config.enable_ldb_on_eval) {
1138*572c4311Sfengbojiang config.eval_ldb = 1;
1139*572c4311Sfengbojiang config.output = OUTPUT_RAW;
1140*572c4311Sfengbojiang }
1141*572c4311Sfengbojiang
1142*572c4311Sfengbojiang /* Setup argument length */
1143*572c4311Sfengbojiang argvlen = zmalloc(argc*sizeof(size_t));
1144*572c4311Sfengbojiang for (j = 0; j < argc; j++)
1145*572c4311Sfengbojiang argvlen[j] = sdslen(argv[j]);
1146*572c4311Sfengbojiang
1147*572c4311Sfengbojiang while(repeat-- > 0) {
1148*572c4311Sfengbojiang redisAppendCommandArgv(context,argc,(const char**)argv,argvlen);
1149*572c4311Sfengbojiang while (config.monitor_mode) {
1150*572c4311Sfengbojiang if (cliReadReply(output_raw) != REDIS_OK) exit(1);
1151*572c4311Sfengbojiang fflush(stdout);
1152*572c4311Sfengbojiang }
1153*572c4311Sfengbojiang
1154*572c4311Sfengbojiang if (config.pubsub_mode) {
1155*572c4311Sfengbojiang if (config.output != OUTPUT_RAW)
1156*572c4311Sfengbojiang printf("Reading messages... (press Ctrl-C to quit)\n");
1157*572c4311Sfengbojiang while (1) {
1158*572c4311Sfengbojiang if (cliReadReply(output_raw) != REDIS_OK) exit(1);
1159*572c4311Sfengbojiang }
1160*572c4311Sfengbojiang }
1161*572c4311Sfengbojiang
1162*572c4311Sfengbojiang if (config.slave_mode) {
1163*572c4311Sfengbojiang printf("Entering replica output mode... (press Ctrl-C to quit)\n");
1164*572c4311Sfengbojiang slaveMode();
1165*572c4311Sfengbojiang config.slave_mode = 0;
1166*572c4311Sfengbojiang zfree(argvlen);
1167*572c4311Sfengbojiang return REDIS_ERR; /* Error = slaveMode lost connection to master */
1168*572c4311Sfengbojiang }
1169*572c4311Sfengbojiang
1170*572c4311Sfengbojiang if (cliReadReply(output_raw) != REDIS_OK) {
1171*572c4311Sfengbojiang zfree(argvlen);
1172*572c4311Sfengbojiang return REDIS_ERR;
1173*572c4311Sfengbojiang } else {
1174*572c4311Sfengbojiang /* Store database number when SELECT was successfully executed. */
1175*572c4311Sfengbojiang if (!strcasecmp(command,"select") && argc == 2 && config.last_cmd_type != REDIS_REPLY_ERROR) {
1176*572c4311Sfengbojiang config.dbnum = atoi(argv[1]);
1177*572c4311Sfengbojiang cliRefreshPrompt();
1178*572c4311Sfengbojiang } else if (!strcasecmp(command,"auth") && argc == 2) {
1179*572c4311Sfengbojiang cliSelect();
1180*572c4311Sfengbojiang }
1181*572c4311Sfengbojiang }
1182*572c4311Sfengbojiang if (config.interval) usleep(config.interval);
1183*572c4311Sfengbojiang fflush(stdout); /* Make it grep friendly */
1184*572c4311Sfengbojiang }
1185*572c4311Sfengbojiang
1186*572c4311Sfengbojiang zfree(argvlen);
1187*572c4311Sfengbojiang return REDIS_OK;
1188*572c4311Sfengbojiang }
1189*572c4311Sfengbojiang
1190*572c4311Sfengbojiang /* Send a command reconnecting the link if needed. */
reconnectingRedisCommand(redisContext * c,const char * fmt,...)1191*572c4311Sfengbojiang static redisReply *reconnectingRedisCommand(redisContext *c, const char *fmt, ...) {
1192*572c4311Sfengbojiang redisReply *reply = NULL;
1193*572c4311Sfengbojiang int tries = 0;
1194*572c4311Sfengbojiang va_list ap;
1195*572c4311Sfengbojiang
1196*572c4311Sfengbojiang assert(!c->err);
1197*572c4311Sfengbojiang while(reply == NULL) {
1198*572c4311Sfengbojiang while (c->err & (REDIS_ERR_IO | REDIS_ERR_EOF)) {
1199*572c4311Sfengbojiang printf("\r\x1b[0K"); /* Cursor to left edge + clear line. */
1200*572c4311Sfengbojiang printf("Reconnecting... %d\r", ++tries);
1201*572c4311Sfengbojiang fflush(stdout);
1202*572c4311Sfengbojiang
1203*572c4311Sfengbojiang redisFree(c);
1204*572c4311Sfengbojiang c = redisConnect(config.hostip,config.hostport);
1205*572c4311Sfengbojiang usleep(1000000);
1206*572c4311Sfengbojiang }
1207*572c4311Sfengbojiang
1208*572c4311Sfengbojiang va_start(ap,fmt);
1209*572c4311Sfengbojiang reply = redisvCommand(c,fmt,ap);
1210*572c4311Sfengbojiang va_end(ap);
1211*572c4311Sfengbojiang
1212*572c4311Sfengbojiang if (c->err && !(c->err & (REDIS_ERR_IO | REDIS_ERR_EOF))) {
1213*572c4311Sfengbojiang fprintf(stderr, "Error: %s\n", c->errstr);
1214*572c4311Sfengbojiang exit(1);
1215*572c4311Sfengbojiang } else if (tries > 0) {
1216*572c4311Sfengbojiang printf("\r\x1b[0K"); /* Cursor to left edge + clear line. */
1217*572c4311Sfengbojiang }
1218*572c4311Sfengbojiang }
1219*572c4311Sfengbojiang
1220*572c4311Sfengbojiang context = c;
1221*572c4311Sfengbojiang return reply;
1222*572c4311Sfengbojiang }
1223*572c4311Sfengbojiang
1224*572c4311Sfengbojiang /*------------------------------------------------------------------------------
1225*572c4311Sfengbojiang * User interface
1226*572c4311Sfengbojiang *--------------------------------------------------------------------------- */
1227*572c4311Sfengbojiang
parseOptions(int argc,char ** argv)1228*572c4311Sfengbojiang static int parseOptions(int argc, char **argv) {
1229*572c4311Sfengbojiang int i;
1230*572c4311Sfengbojiang
1231*572c4311Sfengbojiang for (i = 1; i < argc; i++) {
1232*572c4311Sfengbojiang int lastarg = i==argc-1;
1233*572c4311Sfengbojiang
1234*572c4311Sfengbojiang if (!strcmp(argv[i],"-h") && !lastarg) {
1235*572c4311Sfengbojiang sdsfree(config.hostip);
1236*572c4311Sfengbojiang config.hostip = sdsnew(argv[++i]);
1237*572c4311Sfengbojiang } else if (!strcmp(argv[i],"-h") && lastarg) {
1238*572c4311Sfengbojiang usage();
1239*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--help")) {
1240*572c4311Sfengbojiang usage();
1241*572c4311Sfengbojiang } else if (!strcmp(argv[i],"-x")) {
1242*572c4311Sfengbojiang config.stdinarg = 1;
1243*572c4311Sfengbojiang } else if (!strcmp(argv[i],"-p") && !lastarg) {
1244*572c4311Sfengbojiang config.hostport = atoi(argv[++i]);
1245*572c4311Sfengbojiang } else if (!strcmp(argv[i],"-s") && !lastarg) {
1246*572c4311Sfengbojiang config.hostsocket = argv[++i];
1247*572c4311Sfengbojiang } else if (!strcmp(argv[i],"-r") && !lastarg) {
1248*572c4311Sfengbojiang config.repeat = strtoll(argv[++i],NULL,10);
1249*572c4311Sfengbojiang } else if (!strcmp(argv[i],"-i") && !lastarg) {
1250*572c4311Sfengbojiang double seconds = atof(argv[++i]);
1251*572c4311Sfengbojiang config.interval = seconds*1000000;
1252*572c4311Sfengbojiang } else if (!strcmp(argv[i],"-n") && !lastarg) {
1253*572c4311Sfengbojiang config.dbnum = atoi(argv[++i]);
1254*572c4311Sfengbojiang } else if (!strcmp(argv[i], "--no-auth-warning")) {
1255*572c4311Sfengbojiang config.no_auth_warning = 1;
1256*572c4311Sfengbojiang } else if (!strcmp(argv[i],"-a") && !lastarg) {
1257*572c4311Sfengbojiang config.auth = argv[++i];
1258*572c4311Sfengbojiang } else if (!strcmp(argv[i],"-u") && !lastarg) {
1259*572c4311Sfengbojiang parseRedisUri(argv[++i]);
1260*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--raw")) {
1261*572c4311Sfengbojiang config.output = OUTPUT_RAW;
1262*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--no-raw")) {
1263*572c4311Sfengbojiang config.output = OUTPUT_STANDARD;
1264*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--csv")) {
1265*572c4311Sfengbojiang config.output = OUTPUT_CSV;
1266*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--latency")) {
1267*572c4311Sfengbojiang config.latency_mode = 1;
1268*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--latency-dist")) {
1269*572c4311Sfengbojiang config.latency_dist_mode = 1;
1270*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--mono")) {
1271*572c4311Sfengbojiang spectrum_palette = spectrum_palette_mono;
1272*572c4311Sfengbojiang spectrum_palette_size = spectrum_palette_mono_size;
1273*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--latency-history")) {
1274*572c4311Sfengbojiang config.latency_mode = 1;
1275*572c4311Sfengbojiang config.latency_history = 1;
1276*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--lru-test") && !lastarg) {
1277*572c4311Sfengbojiang config.lru_test_mode = 1;
1278*572c4311Sfengbojiang config.lru_test_sample_size = strtoll(argv[++i],NULL,10);
1279*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--slave")) {
1280*572c4311Sfengbojiang config.slave_mode = 1;
1281*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--replica")) {
1282*572c4311Sfengbojiang config.slave_mode = 1;
1283*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--stat")) {
1284*572c4311Sfengbojiang config.stat_mode = 1;
1285*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--scan")) {
1286*572c4311Sfengbojiang config.scan_mode = 1;
1287*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--pattern") && !lastarg) {
1288*572c4311Sfengbojiang config.pattern = argv[++i];
1289*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--intrinsic-latency") && !lastarg) {
1290*572c4311Sfengbojiang config.intrinsic_latency_mode = 1;
1291*572c4311Sfengbojiang config.intrinsic_latency_duration = atoi(argv[++i]);
1292*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--rdb") && !lastarg) {
1293*572c4311Sfengbojiang config.getrdb_mode = 1;
1294*572c4311Sfengbojiang config.rdb_filename = argv[++i];
1295*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--pipe")) {
1296*572c4311Sfengbojiang config.pipe_mode = 1;
1297*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--pipe-timeout") && !lastarg) {
1298*572c4311Sfengbojiang config.pipe_timeout = atoi(argv[++i]);
1299*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--bigkeys")) {
1300*572c4311Sfengbojiang config.bigkeys = 1;
1301*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--memkeys")) {
1302*572c4311Sfengbojiang config.memkeys = 1;
1303*572c4311Sfengbojiang config.memkeys_samples = 0; /* use redis default */
1304*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--memkeys-samples")) {
1305*572c4311Sfengbojiang config.memkeys = 1;
1306*572c4311Sfengbojiang config.memkeys_samples = atoi(argv[++i]);
1307*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--hotkeys")) {
1308*572c4311Sfengbojiang config.hotkeys = 1;
1309*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--eval") && !lastarg) {
1310*572c4311Sfengbojiang config.eval = argv[++i];
1311*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--ldb")) {
1312*572c4311Sfengbojiang config.eval_ldb = 1;
1313*572c4311Sfengbojiang config.output = OUTPUT_RAW;
1314*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--ldb-sync-mode")) {
1315*572c4311Sfengbojiang config.eval_ldb = 1;
1316*572c4311Sfengbojiang config.eval_ldb_sync = 1;
1317*572c4311Sfengbojiang config.output = OUTPUT_RAW;
1318*572c4311Sfengbojiang } else if (!strcmp(argv[i],"-c")) {
1319*572c4311Sfengbojiang config.cluster_mode = 1;
1320*572c4311Sfengbojiang } else if (!strcmp(argv[i],"-d") && !lastarg) {
1321*572c4311Sfengbojiang sdsfree(config.mb_delim);
1322*572c4311Sfengbojiang config.mb_delim = sdsnew(argv[++i]);
1323*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--verbose")) {
1324*572c4311Sfengbojiang config.verbose = 1;
1325*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster") && !lastarg) {
1326*572c4311Sfengbojiang if (CLUSTER_MANAGER_MODE()) usage();
1327*572c4311Sfengbojiang char *cmd = argv[++i];
1328*572c4311Sfengbojiang int j = i;
1329*572c4311Sfengbojiang while (j < argc && argv[j][0] != '-') j++;
1330*572c4311Sfengbojiang if (j > i) j--;
1331*572c4311Sfengbojiang createClusterManagerCommand(cmd, j - i, argv + i + 1);
1332*572c4311Sfengbojiang i = j;
1333*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster") && lastarg) {
1334*572c4311Sfengbojiang usage();
1335*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster-replicas") && !lastarg) {
1336*572c4311Sfengbojiang config.cluster_manager_command.replicas = atoi(argv[++i]);
1337*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster-master-id") && !lastarg) {
1338*572c4311Sfengbojiang config.cluster_manager_command.master_id = argv[++i];
1339*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster-from") && !lastarg) {
1340*572c4311Sfengbojiang config.cluster_manager_command.from = argv[++i];
1341*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster-to") && !lastarg) {
1342*572c4311Sfengbojiang config.cluster_manager_command.to = argv[++i];
1343*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster-weight") && !lastarg) {
1344*572c4311Sfengbojiang if (config.cluster_manager_command.weight != NULL) {
1345*572c4311Sfengbojiang fprintf(stderr, "WARNING: you cannot use --cluster-weight "
1346*572c4311Sfengbojiang "more than once.\n"
1347*572c4311Sfengbojiang "You can set more weights by adding them "
1348*572c4311Sfengbojiang "as a space-separated list, ie:\n"
1349*572c4311Sfengbojiang "--cluster-weight n1=w n2=w\n");
1350*572c4311Sfengbojiang exit(1);
1351*572c4311Sfengbojiang }
1352*572c4311Sfengbojiang int widx = i + 1;
1353*572c4311Sfengbojiang char **weight = argv + widx;
1354*572c4311Sfengbojiang int wargc = 0;
1355*572c4311Sfengbojiang for (; widx < argc; widx++) {
1356*572c4311Sfengbojiang if (strstr(argv[widx], "--") == argv[widx]) break;
1357*572c4311Sfengbojiang if (strchr(argv[widx], '=') == NULL) break;
1358*572c4311Sfengbojiang wargc++;
1359*572c4311Sfengbojiang }
1360*572c4311Sfengbojiang if (wargc > 0) {
1361*572c4311Sfengbojiang config.cluster_manager_command.weight = weight;
1362*572c4311Sfengbojiang config.cluster_manager_command.weight_argc = wargc;
1363*572c4311Sfengbojiang i += wargc;
1364*572c4311Sfengbojiang }
1365*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster-slots") && !lastarg) {
1366*572c4311Sfengbojiang config.cluster_manager_command.slots = atoi(argv[++i]);
1367*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster-timeout") && !lastarg) {
1368*572c4311Sfengbojiang config.cluster_manager_command.timeout = atoi(argv[++i]);
1369*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster-pipeline") && !lastarg) {
1370*572c4311Sfengbojiang config.cluster_manager_command.pipeline = atoi(argv[++i]);
1371*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster-threshold") && !lastarg) {
1372*572c4311Sfengbojiang config.cluster_manager_command.threshold = atof(argv[++i]);
1373*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster-yes")) {
1374*572c4311Sfengbojiang config.cluster_manager_command.flags |=
1375*572c4311Sfengbojiang CLUSTER_MANAGER_CMD_FLAG_YES;
1376*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster-simulate")) {
1377*572c4311Sfengbojiang config.cluster_manager_command.flags |=
1378*572c4311Sfengbojiang CLUSTER_MANAGER_CMD_FLAG_SIMULATE;
1379*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster-replace")) {
1380*572c4311Sfengbojiang config.cluster_manager_command.flags |=
1381*572c4311Sfengbojiang CLUSTER_MANAGER_CMD_FLAG_REPLACE;
1382*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster-copy")) {
1383*572c4311Sfengbojiang config.cluster_manager_command.flags |=
1384*572c4311Sfengbojiang CLUSTER_MANAGER_CMD_FLAG_COPY;
1385*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster-slave")) {
1386*572c4311Sfengbojiang config.cluster_manager_command.flags |=
1387*572c4311Sfengbojiang CLUSTER_MANAGER_CMD_FLAG_SLAVE;
1388*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster-use-empty-masters")) {
1389*572c4311Sfengbojiang config.cluster_manager_command.flags |=
1390*572c4311Sfengbojiang CLUSTER_MANAGER_CMD_FLAG_EMPTYMASTER;
1391*572c4311Sfengbojiang } else if (!strcmp(argv[i],"--cluster-search-multiple-owners")) {
1392*572c4311Sfengbojiang config.cluster_manager_command.flags |=
1393*572c4311Sfengbojiang CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS;
1394*572c4311Sfengbojiang } else if (!strcmp(argv[i],"-v") || !strcmp(argv[i], "--version")) {
1395*572c4311Sfengbojiang sds version = cliVersion();
1396*572c4311Sfengbojiang printf("redis-cli %s\n", version);
1397*572c4311Sfengbojiang sdsfree(version);
1398*572c4311Sfengbojiang exit(0);
1399*572c4311Sfengbojiang } else if (CLUSTER_MANAGER_MODE() && argv[i][0] != '-') {
1400*572c4311Sfengbojiang if (config.cluster_manager_command.argc == 0) {
1401*572c4311Sfengbojiang int j = i + 1;
1402*572c4311Sfengbojiang while (j < argc && argv[j][0] != '-') j++;
1403*572c4311Sfengbojiang int cmd_argc = j - i;
1404*572c4311Sfengbojiang config.cluster_manager_command.argc = cmd_argc;
1405*572c4311Sfengbojiang config.cluster_manager_command.argv = argv + i;
1406*572c4311Sfengbojiang if (cmd_argc > 1) i = j - 1;
1407*572c4311Sfengbojiang }
1408*572c4311Sfengbojiang } else {
1409*572c4311Sfengbojiang if (argv[i][0] == '-') {
1410*572c4311Sfengbojiang fprintf(stderr,
1411*572c4311Sfengbojiang "Unrecognized option or bad number of args for: '%s'\n",
1412*572c4311Sfengbojiang argv[i]);
1413*572c4311Sfengbojiang exit(1);
1414*572c4311Sfengbojiang } else {
1415*572c4311Sfengbojiang /* Likely the command name, stop here. */
1416*572c4311Sfengbojiang break;
1417*572c4311Sfengbojiang }
1418*572c4311Sfengbojiang }
1419*572c4311Sfengbojiang }
1420*572c4311Sfengbojiang
1421*572c4311Sfengbojiang /* --ldb requires --eval. */
1422*572c4311Sfengbojiang if (config.eval_ldb && config.eval == NULL) {
1423*572c4311Sfengbojiang fprintf(stderr,"Options --ldb and --ldb-sync-mode require --eval.\n");
1424*572c4311Sfengbojiang fprintf(stderr,"Try %s --help for more information.\n", argv[0]);
1425*572c4311Sfengbojiang exit(1);
1426*572c4311Sfengbojiang }
1427*572c4311Sfengbojiang
1428*572c4311Sfengbojiang if (!config.no_auth_warning && config.auth != NULL) {
1429*572c4311Sfengbojiang fputs("Warning: Using a password with '-a' or '-u' option on the command"
1430*572c4311Sfengbojiang " line interface may not be safe.\n", stderr);
1431*572c4311Sfengbojiang }
1432*572c4311Sfengbojiang
1433*572c4311Sfengbojiang return i;
1434*572c4311Sfengbojiang }
1435*572c4311Sfengbojiang
parseEnv()1436*572c4311Sfengbojiang static void parseEnv() {
1437*572c4311Sfengbojiang /* Set auth from env, but do not overwrite CLI arguments if passed */
1438*572c4311Sfengbojiang char *auth = getenv(REDIS_CLI_AUTH_ENV);
1439*572c4311Sfengbojiang if (auth != NULL && config.auth == NULL) {
1440*572c4311Sfengbojiang config.auth = auth;
1441*572c4311Sfengbojiang }
1442*572c4311Sfengbojiang
1443*572c4311Sfengbojiang char *cluster_yes = getenv(REDIS_CLI_CLUSTER_YES_ENV);
1444*572c4311Sfengbojiang if (cluster_yes != NULL && !strcmp(cluster_yes, "1")) {
1445*572c4311Sfengbojiang config.cluster_manager_command.flags |= CLUSTER_MANAGER_CMD_FLAG_YES;
1446*572c4311Sfengbojiang }
1447*572c4311Sfengbojiang }
1448*572c4311Sfengbojiang
readArgFromStdin(void)1449*572c4311Sfengbojiang static sds readArgFromStdin(void) {
1450*572c4311Sfengbojiang char buf[1024];
1451*572c4311Sfengbojiang sds arg = sdsempty();
1452*572c4311Sfengbojiang
1453*572c4311Sfengbojiang while(1) {
1454*572c4311Sfengbojiang int nread = read(fileno(stdin),buf,1024);
1455*572c4311Sfengbojiang
1456*572c4311Sfengbojiang if (nread == 0) break;
1457*572c4311Sfengbojiang else if (nread == -1) {
1458*572c4311Sfengbojiang perror("Reading from standard input");
1459*572c4311Sfengbojiang exit(1);
1460*572c4311Sfengbojiang }
1461*572c4311Sfengbojiang arg = sdscatlen(arg,buf,nread);
1462*572c4311Sfengbojiang }
1463*572c4311Sfengbojiang return arg;
1464*572c4311Sfengbojiang }
1465*572c4311Sfengbojiang
usage(void)1466*572c4311Sfengbojiang static void usage(void) {
1467*572c4311Sfengbojiang sds version = cliVersion();
1468*572c4311Sfengbojiang fprintf(stderr,
1469*572c4311Sfengbojiang "redis-cli %s\n"
1470*572c4311Sfengbojiang "\n"
1471*572c4311Sfengbojiang "Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]\n"
1472*572c4311Sfengbojiang " -h <hostname> Server hostname (default: 127.0.0.1).\n"
1473*572c4311Sfengbojiang " -p <port> Server port (default: 6379).\n"
1474*572c4311Sfengbojiang " -s <socket> Server socket (overrides hostname and port).\n"
1475*572c4311Sfengbojiang " -a <password> Password to use when connecting to the server.\n"
1476*572c4311Sfengbojiang " You can also use the " REDIS_CLI_AUTH_ENV " environment\n"
1477*572c4311Sfengbojiang " variable to pass this password more safely\n"
1478*572c4311Sfengbojiang " (if both are used, this argument takes predecence).\n"
1479*572c4311Sfengbojiang " -u <uri> Server URI.\n"
1480*572c4311Sfengbojiang " -r <repeat> Execute specified command N times.\n"
1481*572c4311Sfengbojiang " -i <interval> When -r is used, waits <interval> seconds per command.\n"
1482*572c4311Sfengbojiang " It is possible to specify sub-second times like -i 0.1.\n"
1483*572c4311Sfengbojiang " -n <db> Database number.\n"
1484*572c4311Sfengbojiang " -x Read last argument from STDIN.\n"
1485*572c4311Sfengbojiang " -d <delimiter> Multi-bulk delimiter in for raw formatting (default: \\n).\n"
1486*572c4311Sfengbojiang " -c Enable cluster mode (follow -ASK and -MOVED redirections).\n"
1487*572c4311Sfengbojiang " --raw Use raw formatting for replies (default when STDOUT is\n"
1488*572c4311Sfengbojiang " not a tty).\n"
1489*572c4311Sfengbojiang " --no-raw Force formatted output even when STDOUT is not a tty.\n"
1490*572c4311Sfengbojiang " --csv Output in CSV format.\n"
1491*572c4311Sfengbojiang " --stat Print rolling stats about server: mem, clients, ...\n"
1492*572c4311Sfengbojiang " --latency Enter a special mode continuously sampling latency.\n"
1493*572c4311Sfengbojiang " If you use this mode in an interactive session it runs\n"
1494*572c4311Sfengbojiang " forever displaying real-time stats. Otherwise if --raw or\n"
1495*572c4311Sfengbojiang " --csv is specified, or if you redirect the output to a non\n"
1496*572c4311Sfengbojiang " TTY, it samples the latency for 1 second (you can use\n"
1497*572c4311Sfengbojiang " -i to change the interval), then produces a single output\n"
1498*572c4311Sfengbojiang " and exits.\n"
1499*572c4311Sfengbojiang " --latency-history Like --latency but tracking latency changes over time.\n"
1500*572c4311Sfengbojiang " Default time interval is 15 sec. Change it using -i.\n"
1501*572c4311Sfengbojiang " --latency-dist Shows latency as a spectrum, requires xterm 256 colors.\n"
1502*572c4311Sfengbojiang " Default time interval is 1 sec. Change it using -i.\n"
1503*572c4311Sfengbojiang " --lru-test <keys> Simulate a cache workload with an 80-20 distribution.\n"
1504*572c4311Sfengbojiang " --replica Simulate a replica showing commands received from the master.\n"
1505*572c4311Sfengbojiang " --rdb <filename> Transfer an RDB dump from remote server to local file.\n"
1506*572c4311Sfengbojiang " --pipe Transfer raw Redis protocol from stdin to server.\n"
1507*572c4311Sfengbojiang " --pipe-timeout <n> In --pipe mode, abort with error if after sending all data.\n"
1508*572c4311Sfengbojiang " no reply is received within <n> seconds.\n"
1509*572c4311Sfengbojiang " Default timeout: %d. Use 0 to wait forever.\n"
1510*572c4311Sfengbojiang " --bigkeys Sample Redis keys looking for keys with many elements (complexity).\n"
1511*572c4311Sfengbojiang " --memkeys Sample Redis keys looking for keys consuming a lot of memory.\n"
1512*572c4311Sfengbojiang " --memkeys-samples <n> Sample Redis keys looking for keys consuming a lot of memory.\n"
1513*572c4311Sfengbojiang " And define number of key elements to sample\n"
1514*572c4311Sfengbojiang " --hotkeys Sample Redis keys looking for hot keys.\n"
1515*572c4311Sfengbojiang " only works when maxmemory-policy is *lfu.\n"
1516*572c4311Sfengbojiang " --scan List all keys using the SCAN command.\n"
1517*572c4311Sfengbojiang " --pattern <pat> Useful with --scan to specify a SCAN pattern.\n"
1518*572c4311Sfengbojiang " --intrinsic-latency <sec> Run a test to measure intrinsic system latency.\n"
1519*572c4311Sfengbojiang " The test will run for the specified amount of seconds.\n"
1520*572c4311Sfengbojiang " --eval <file> Send an EVAL command using the Lua script at <file>.\n"
1521*572c4311Sfengbojiang " --ldb Used with --eval enable the Redis Lua debugger.\n"
1522*572c4311Sfengbojiang " --ldb-sync-mode Like --ldb but uses the synchronous Lua debugger, in\n"
1523*572c4311Sfengbojiang " this mode the server is blocked and script changes are\n"
1524*572c4311Sfengbojiang " not rolled back from the server memory.\n"
1525*572c4311Sfengbojiang " --cluster <command> [args...] [opts...]\n"
1526*572c4311Sfengbojiang " Cluster Manager command and arguments (see below).\n"
1527*572c4311Sfengbojiang " --verbose Verbose mode.\n"
1528*572c4311Sfengbojiang " --no-auth-warning Don't show warning message when using password on command\n"
1529*572c4311Sfengbojiang " line interface.\n"
1530*572c4311Sfengbojiang " --help Output this help and exit.\n"
1531*572c4311Sfengbojiang " --version Output version and exit.\n"
1532*572c4311Sfengbojiang "\n",
1533*572c4311Sfengbojiang version, REDIS_CLI_DEFAULT_PIPE_TIMEOUT);
1534*572c4311Sfengbojiang /* Using another fprintf call to avoid -Woverlength-strings compile warning */
1535*572c4311Sfengbojiang fprintf(stderr,
1536*572c4311Sfengbojiang "Cluster Manager Commands:\n"
1537*572c4311Sfengbojiang " Use --cluster help to list all available cluster manager commands.\n"
1538*572c4311Sfengbojiang "\n"
1539*572c4311Sfengbojiang "Examples:\n"
1540*572c4311Sfengbojiang " cat /etc/passwd | redis-cli -x set mypasswd\n"
1541*572c4311Sfengbojiang " redis-cli get mypasswd\n"
1542*572c4311Sfengbojiang " redis-cli -r 100 lpush mylist x\n"
1543*572c4311Sfengbojiang " redis-cli -r 100 -i 1 info | grep used_memory_human:\n"
1544*572c4311Sfengbojiang " redis-cli --eval myscript.lua key1 key2 , arg1 arg2 arg3\n"
1545*572c4311Sfengbojiang " redis-cli --scan --pattern '*:12345*'\n"
1546*572c4311Sfengbojiang "\n"
1547*572c4311Sfengbojiang " (Note: when using --eval the comma separates KEYS[] from ARGV[] items)\n"
1548*572c4311Sfengbojiang "\n"
1549*572c4311Sfengbojiang "When no command is given, redis-cli starts in interactive mode.\n"
1550*572c4311Sfengbojiang "Type \"help\" in interactive mode for information on available commands\n"
1551*572c4311Sfengbojiang "and settings.\n"
1552*572c4311Sfengbojiang "\n");
1553*572c4311Sfengbojiang sdsfree(version);
1554*572c4311Sfengbojiang exit(1);
1555*572c4311Sfengbojiang }
1556*572c4311Sfengbojiang
confirmWithYes(char * msg)1557*572c4311Sfengbojiang static int confirmWithYes(char *msg) {
1558*572c4311Sfengbojiang if (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_YES) {
1559*572c4311Sfengbojiang return 1;
1560*572c4311Sfengbojiang }
1561*572c4311Sfengbojiang printf("%s (type 'yes' to accept): ", msg);
1562*572c4311Sfengbojiang fflush(stdout);
1563*572c4311Sfengbojiang char buf[4];
1564*572c4311Sfengbojiang int nread = read(fileno(stdin),buf,4);
1565*572c4311Sfengbojiang buf[3] = '\0';
1566*572c4311Sfengbojiang return (nread != 0 && !strcmp("yes", buf));
1567*572c4311Sfengbojiang }
1568*572c4311Sfengbojiang
1569*572c4311Sfengbojiang /* Turn the plain C strings into Sds strings */
convertToSds(int count,char ** args)1570*572c4311Sfengbojiang static char **convertToSds(int count, char** args) {
1571*572c4311Sfengbojiang int j;
1572*572c4311Sfengbojiang char **sds = zmalloc(sizeof(char*)*count);
1573*572c4311Sfengbojiang
1574*572c4311Sfengbojiang for(j = 0; j < count; j++)
1575*572c4311Sfengbojiang sds[j] = sdsnew(args[j]);
1576*572c4311Sfengbojiang
1577*572c4311Sfengbojiang return sds;
1578*572c4311Sfengbojiang }
1579*572c4311Sfengbojiang
issueCommandRepeat(int argc,char ** argv,long repeat)1580*572c4311Sfengbojiang static int issueCommandRepeat(int argc, char **argv, long repeat) {
1581*572c4311Sfengbojiang while (1) {
1582*572c4311Sfengbojiang config.cluster_reissue_command = 0;
1583*572c4311Sfengbojiang if (cliSendCommand(argc,argv,repeat) != REDIS_OK) {
1584*572c4311Sfengbojiang cliConnect(CC_FORCE);
1585*572c4311Sfengbojiang
1586*572c4311Sfengbojiang /* If we still cannot send the command print error.
1587*572c4311Sfengbojiang * We'll try to reconnect the next time. */
1588*572c4311Sfengbojiang if (cliSendCommand(argc,argv,repeat) != REDIS_OK) {
1589*572c4311Sfengbojiang cliPrintContextError();
1590*572c4311Sfengbojiang return REDIS_ERR;
1591*572c4311Sfengbojiang }
1592*572c4311Sfengbojiang }
1593*572c4311Sfengbojiang /* Issue the command again if we got redirected in cluster mode */
1594*572c4311Sfengbojiang if (config.cluster_mode && config.cluster_reissue_command) {
1595*572c4311Sfengbojiang cliConnect(CC_FORCE);
1596*572c4311Sfengbojiang } else {
1597*572c4311Sfengbojiang break;
1598*572c4311Sfengbojiang }
1599*572c4311Sfengbojiang }
1600*572c4311Sfengbojiang return REDIS_OK;
1601*572c4311Sfengbojiang }
1602*572c4311Sfengbojiang
issueCommand(int argc,char ** argv)1603*572c4311Sfengbojiang static int issueCommand(int argc, char **argv) {
1604*572c4311Sfengbojiang return issueCommandRepeat(argc, argv, config.repeat);
1605*572c4311Sfengbojiang }
1606*572c4311Sfengbojiang
1607*572c4311Sfengbojiang /* Split the user provided command into multiple SDS arguments.
1608*572c4311Sfengbojiang * This function normally uses sdssplitargs() from sds.c which is able
1609*572c4311Sfengbojiang * to understand "quoted strings", escapes and so forth. However when
1610*572c4311Sfengbojiang * we are in Lua debugging mode and the "eval" command is used, we want
1611*572c4311Sfengbojiang * the remaining Lua script (after "e " or "eval ") to be passed verbatim
1612*572c4311Sfengbojiang * as a single big argument. */
cliSplitArgs(char * line,int * argc)1613*572c4311Sfengbojiang static sds *cliSplitArgs(char *line, int *argc) {
1614*572c4311Sfengbojiang if (config.eval_ldb && (strstr(line,"eval ") == line ||
1615*572c4311Sfengbojiang strstr(line,"e ") == line))
1616*572c4311Sfengbojiang {
1617*572c4311Sfengbojiang sds *argv = sds_malloc(sizeof(sds)*2);
1618*572c4311Sfengbojiang *argc = 2;
1619*572c4311Sfengbojiang int len = strlen(line);
1620*572c4311Sfengbojiang int elen = line[1] == ' ' ? 2 : 5; /* "e " or "eval "? */
1621*572c4311Sfengbojiang argv[0] = sdsnewlen(line,elen-1);
1622*572c4311Sfengbojiang argv[1] = sdsnewlen(line+elen,len-elen);
1623*572c4311Sfengbojiang return argv;
1624*572c4311Sfengbojiang } else {
1625*572c4311Sfengbojiang return sdssplitargs(line,argc);
1626*572c4311Sfengbojiang }
1627*572c4311Sfengbojiang }
1628*572c4311Sfengbojiang
1629*572c4311Sfengbojiang /* Set the CLI preferences. This function is invoked when an interactive
1630*572c4311Sfengbojiang * ":command" is called, or when reading ~/.redisclirc file, in order to
1631*572c4311Sfengbojiang * set user preferences. */
cliSetPreferences(char ** argv,int argc,int interactive)1632*572c4311Sfengbojiang void cliSetPreferences(char **argv, int argc, int interactive) {
1633*572c4311Sfengbojiang if (!strcasecmp(argv[0],":set") && argc >= 2) {
1634*572c4311Sfengbojiang if (!strcasecmp(argv[1],"hints")) pref.hints = 1;
1635*572c4311Sfengbojiang else if (!strcasecmp(argv[1],"nohints")) pref.hints = 0;
1636*572c4311Sfengbojiang else {
1637*572c4311Sfengbojiang printf("%sunknown redis-cli preference '%s'\n",
1638*572c4311Sfengbojiang interactive ? "" : ".redisclirc: ",
1639*572c4311Sfengbojiang argv[1]);
1640*572c4311Sfengbojiang }
1641*572c4311Sfengbojiang } else {
1642*572c4311Sfengbojiang printf("%sunknown redis-cli internal command '%s'\n",
1643*572c4311Sfengbojiang interactive ? "" : ".redisclirc: ",
1644*572c4311Sfengbojiang argv[0]);
1645*572c4311Sfengbojiang }
1646*572c4311Sfengbojiang }
1647*572c4311Sfengbojiang
1648*572c4311Sfengbojiang /* Load the ~/.redisclirc file if any. */
cliLoadPreferences(void)1649*572c4311Sfengbojiang void cliLoadPreferences(void) {
1650*572c4311Sfengbojiang sds rcfile = getDotfilePath(REDIS_CLI_RCFILE_ENV,REDIS_CLI_RCFILE_DEFAULT);
1651*572c4311Sfengbojiang if (rcfile == NULL) return;
1652*572c4311Sfengbojiang FILE *fp = fopen(rcfile,"r");
1653*572c4311Sfengbojiang char buf[1024];
1654*572c4311Sfengbojiang
1655*572c4311Sfengbojiang if (fp) {
1656*572c4311Sfengbojiang while(fgets(buf,sizeof(buf),fp) != NULL) {
1657*572c4311Sfengbojiang sds *argv;
1658*572c4311Sfengbojiang int argc;
1659*572c4311Sfengbojiang
1660*572c4311Sfengbojiang argv = sdssplitargs(buf,&argc);
1661*572c4311Sfengbojiang if (argc > 0) cliSetPreferences(argv,argc,0);
1662*572c4311Sfengbojiang sdsfreesplitres(argv,argc);
1663*572c4311Sfengbojiang }
1664*572c4311Sfengbojiang fclose(fp);
1665*572c4311Sfengbojiang }
1666*572c4311Sfengbojiang sdsfree(rcfile);
1667*572c4311Sfengbojiang }
1668*572c4311Sfengbojiang
repl(void)1669*572c4311Sfengbojiang static void repl(void) {
1670*572c4311Sfengbojiang sds historyfile = NULL;
1671*572c4311Sfengbojiang int history = 0;
1672*572c4311Sfengbojiang char *line;
1673*572c4311Sfengbojiang int argc;
1674*572c4311Sfengbojiang sds *argv;
1675*572c4311Sfengbojiang
1676*572c4311Sfengbojiang /* Initialize the help and, if possible, use the COMMAND command in order
1677*572c4311Sfengbojiang * to retrieve missing entries. */
1678*572c4311Sfengbojiang cliInitHelp();
1679*572c4311Sfengbojiang cliIntegrateHelp();
1680*572c4311Sfengbojiang
1681*572c4311Sfengbojiang config.interactive = 1;
1682*572c4311Sfengbojiang linenoiseSetMultiLine(1);
1683*572c4311Sfengbojiang linenoiseSetCompletionCallback(completionCallback);
1684*572c4311Sfengbojiang linenoiseSetHintsCallback(hintsCallback);
1685*572c4311Sfengbojiang linenoiseSetFreeHintsCallback(freeHintsCallback);
1686*572c4311Sfengbojiang
1687*572c4311Sfengbojiang /* Only use history and load the rc file when stdin is a tty. */
1688*572c4311Sfengbojiang if (isatty(fileno(stdin))) {
1689*572c4311Sfengbojiang historyfile = getDotfilePath(REDIS_CLI_HISTFILE_ENV,REDIS_CLI_HISTFILE_DEFAULT);
1690*572c4311Sfengbojiang //keep in-memory history always regardless if history file can be determined
1691*572c4311Sfengbojiang history = 1;
1692*572c4311Sfengbojiang if (historyfile != NULL) {
1693*572c4311Sfengbojiang linenoiseHistoryLoad(historyfile);
1694*572c4311Sfengbojiang }
1695*572c4311Sfengbojiang cliLoadPreferences();
1696*572c4311Sfengbojiang }
1697*572c4311Sfengbojiang
1698*572c4311Sfengbojiang cliRefreshPrompt();
1699*572c4311Sfengbojiang while((line = linenoise(context ? config.prompt : "not connected> ")) != NULL) {
1700*572c4311Sfengbojiang if (line[0] != '\0') {
1701*572c4311Sfengbojiang long repeat = 1;
1702*572c4311Sfengbojiang int skipargs = 0;
1703*572c4311Sfengbojiang char *endptr = NULL;
1704*572c4311Sfengbojiang
1705*572c4311Sfengbojiang argv = cliSplitArgs(line,&argc);
1706*572c4311Sfengbojiang
1707*572c4311Sfengbojiang /* check if we have a repeat command option and
1708*572c4311Sfengbojiang * need to skip the first arg */
1709*572c4311Sfengbojiang if (argv && argc > 0) {
1710*572c4311Sfengbojiang errno = 0;
1711*572c4311Sfengbojiang repeat = strtol(argv[0], &endptr, 10);
1712*572c4311Sfengbojiang if (argc > 1 && *endptr == '\0') {
1713*572c4311Sfengbojiang if (errno == ERANGE || errno == EINVAL || repeat <= 0) {
1714*572c4311Sfengbojiang fputs("Invalid redis-cli repeat command option value.\n", stdout);
1715*572c4311Sfengbojiang sdsfreesplitres(argv, argc);
1716*572c4311Sfengbojiang linenoiseFree(line);
1717*572c4311Sfengbojiang continue;
1718*572c4311Sfengbojiang }
1719*572c4311Sfengbojiang skipargs = 1;
1720*572c4311Sfengbojiang } else {
1721*572c4311Sfengbojiang repeat = 1;
1722*572c4311Sfengbojiang }
1723*572c4311Sfengbojiang }
1724*572c4311Sfengbojiang
1725*572c4311Sfengbojiang /* Won't save auth command in history file */
1726*572c4311Sfengbojiang if (!(argv && argc > 0 && !strcasecmp(argv[0+skipargs], "auth"))) {
1727*572c4311Sfengbojiang if (history) linenoiseHistoryAdd(line);
1728*572c4311Sfengbojiang if (historyfile) linenoiseHistorySave(historyfile);
1729*572c4311Sfengbojiang }
1730*572c4311Sfengbojiang
1731*572c4311Sfengbojiang if (argv == NULL) {
1732*572c4311Sfengbojiang printf("Invalid argument(s)\n");
1733*572c4311Sfengbojiang linenoiseFree(line);
1734*572c4311Sfengbojiang continue;
1735*572c4311Sfengbojiang } else if (argc > 0) {
1736*572c4311Sfengbojiang if (strcasecmp(argv[0],"quit") == 0 ||
1737*572c4311Sfengbojiang strcasecmp(argv[0],"exit") == 0)
1738*572c4311Sfengbojiang {
1739*572c4311Sfengbojiang exit(0);
1740*572c4311Sfengbojiang } else if (argv[0][0] == ':') {
1741*572c4311Sfengbojiang cliSetPreferences(argv,argc,1);
1742*572c4311Sfengbojiang sdsfreesplitres(argv,argc);
1743*572c4311Sfengbojiang linenoiseFree(line);
1744*572c4311Sfengbojiang continue;
1745*572c4311Sfengbojiang } else if (strcasecmp(argv[0],"restart") == 0) {
1746*572c4311Sfengbojiang if (config.eval) {
1747*572c4311Sfengbojiang config.eval_ldb = 1;
1748*572c4311Sfengbojiang config.output = OUTPUT_RAW;
1749*572c4311Sfengbojiang return; /* Return to evalMode to restart the session. */
1750*572c4311Sfengbojiang } else {
1751*572c4311Sfengbojiang printf("Use 'restart' only in Lua debugging mode.");
1752*572c4311Sfengbojiang }
1753*572c4311Sfengbojiang } else if (argc == 3 && !strcasecmp(argv[0],"connect")) {
1754*572c4311Sfengbojiang sdsfree(config.hostip);
1755*572c4311Sfengbojiang config.hostip = sdsnew(argv[1]);
1756*572c4311Sfengbojiang config.hostport = atoi(argv[2]);
1757*572c4311Sfengbojiang cliRefreshPrompt();
1758*572c4311Sfengbojiang cliConnect(CC_FORCE);
1759*572c4311Sfengbojiang } else if (argc == 1 && !strcasecmp(argv[0],"clear")) {
1760*572c4311Sfengbojiang linenoiseClearScreen();
1761*572c4311Sfengbojiang } else {
1762*572c4311Sfengbojiang long long start_time = mstime(), elapsed;
1763*572c4311Sfengbojiang
1764*572c4311Sfengbojiang issueCommandRepeat(argc-skipargs, argv+skipargs, repeat);
1765*572c4311Sfengbojiang
1766*572c4311Sfengbojiang /* If our debugging session ended, show the EVAL final
1767*572c4311Sfengbojiang * reply. */
1768*572c4311Sfengbojiang if (config.eval_ldb_end) {
1769*572c4311Sfengbojiang config.eval_ldb_end = 0;
1770*572c4311Sfengbojiang cliReadReply(0);
1771*572c4311Sfengbojiang printf("\n(Lua debugging session ended%s)\n\n",
1772*572c4311Sfengbojiang config.eval_ldb_sync ? "" :
1773*572c4311Sfengbojiang " -- dataset changes rolled back");
1774*572c4311Sfengbojiang }
1775*572c4311Sfengbojiang
1776*572c4311Sfengbojiang elapsed = mstime()-start_time;
1777*572c4311Sfengbojiang if (elapsed >= 500 &&
1778*572c4311Sfengbojiang config.output == OUTPUT_STANDARD)
1779*572c4311Sfengbojiang {
1780*572c4311Sfengbojiang printf("(%.2fs)\n",(double)elapsed/1000);
1781*572c4311Sfengbojiang }
1782*572c4311Sfengbojiang }
1783*572c4311Sfengbojiang }
1784*572c4311Sfengbojiang /* Free the argument vector */
1785*572c4311Sfengbojiang sdsfreesplitres(argv,argc);
1786*572c4311Sfengbojiang }
1787*572c4311Sfengbojiang /* linenoise() returns malloc-ed lines like readline() */
1788*572c4311Sfengbojiang linenoiseFree(line);
1789*572c4311Sfengbojiang }
1790*572c4311Sfengbojiang exit(0);
1791*572c4311Sfengbojiang }
1792*572c4311Sfengbojiang
noninteractive(int argc,char ** argv)1793*572c4311Sfengbojiang static int noninteractive(int argc, char **argv) {
1794*572c4311Sfengbojiang int retval = 0;
1795*572c4311Sfengbojiang if (config.stdinarg) {
1796*572c4311Sfengbojiang argv = zrealloc(argv, (argc+1)*sizeof(char*));
1797*572c4311Sfengbojiang argv[argc] = readArgFromStdin();
1798*572c4311Sfengbojiang retval = issueCommand(argc+1, argv);
1799*572c4311Sfengbojiang } else {
1800*572c4311Sfengbojiang retval = issueCommand(argc, argv);
1801*572c4311Sfengbojiang }
1802*572c4311Sfengbojiang return retval;
1803*572c4311Sfengbojiang }
1804*572c4311Sfengbojiang
1805*572c4311Sfengbojiang /*------------------------------------------------------------------------------
1806*572c4311Sfengbojiang * Eval mode
1807*572c4311Sfengbojiang *--------------------------------------------------------------------------- */
1808*572c4311Sfengbojiang
evalMode(int argc,char ** argv)1809*572c4311Sfengbojiang static int evalMode(int argc, char **argv) {
1810*572c4311Sfengbojiang sds script = NULL;
1811*572c4311Sfengbojiang FILE *fp;
1812*572c4311Sfengbojiang char buf[1024];
1813*572c4311Sfengbojiang size_t nread;
1814*572c4311Sfengbojiang char **argv2;
1815*572c4311Sfengbojiang int j, got_comma, keys;
1816*572c4311Sfengbojiang int retval = REDIS_OK;
1817*572c4311Sfengbojiang
1818*572c4311Sfengbojiang while(1) {
1819*572c4311Sfengbojiang if (config.eval_ldb) {
1820*572c4311Sfengbojiang printf(
1821*572c4311Sfengbojiang "Lua debugging session started, please use:\n"
1822*572c4311Sfengbojiang "quit -- End the session.\n"
1823*572c4311Sfengbojiang "restart -- Restart the script in debug mode again.\n"
1824*572c4311Sfengbojiang "help -- Show Lua script debugging commands.\n\n"
1825*572c4311Sfengbojiang );
1826*572c4311Sfengbojiang }
1827*572c4311Sfengbojiang
1828*572c4311Sfengbojiang sdsfree(script);
1829*572c4311Sfengbojiang script = sdsempty();
1830*572c4311Sfengbojiang got_comma = 0;
1831*572c4311Sfengbojiang keys = 0;
1832*572c4311Sfengbojiang
1833*572c4311Sfengbojiang /* Load the script from the file, as an sds string. */
1834*572c4311Sfengbojiang fp = fopen(config.eval,"r");
1835*572c4311Sfengbojiang if (!fp) {
1836*572c4311Sfengbojiang fprintf(stderr,
1837*572c4311Sfengbojiang "Can't open file '%s': %s\n", config.eval, strerror(errno));
1838*572c4311Sfengbojiang exit(1);
1839*572c4311Sfengbojiang }
1840*572c4311Sfengbojiang while((nread = fread(buf,1,sizeof(buf),fp)) != 0) {
1841*572c4311Sfengbojiang script = sdscatlen(script,buf,nread);
1842*572c4311Sfengbojiang }
1843*572c4311Sfengbojiang fclose(fp);
1844*572c4311Sfengbojiang
1845*572c4311Sfengbojiang /* If we are debugging a script, enable the Lua debugger. */
1846*572c4311Sfengbojiang if (config.eval_ldb) {
1847*572c4311Sfengbojiang redisReply *reply = redisCommand(context,
1848*572c4311Sfengbojiang config.eval_ldb_sync ?
1849*572c4311Sfengbojiang "SCRIPT DEBUG sync": "SCRIPT DEBUG yes");
1850*572c4311Sfengbojiang if (reply) freeReplyObject(reply);
1851*572c4311Sfengbojiang }
1852*572c4311Sfengbojiang
1853*572c4311Sfengbojiang /* Create our argument vector */
1854*572c4311Sfengbojiang argv2 = zmalloc(sizeof(sds)*(argc+3));
1855*572c4311Sfengbojiang argv2[0] = sdsnew("EVAL");
1856*572c4311Sfengbojiang argv2[1] = script;
1857*572c4311Sfengbojiang for (j = 0; j < argc; j++) {
1858*572c4311Sfengbojiang if (!got_comma && argv[j][0] == ',' && argv[j][1] == 0) {
1859*572c4311Sfengbojiang got_comma = 1;
1860*572c4311Sfengbojiang continue;
1861*572c4311Sfengbojiang }
1862*572c4311Sfengbojiang argv2[j+3-got_comma] = sdsnew(argv[j]);
1863*572c4311Sfengbojiang if (!got_comma) keys++;
1864*572c4311Sfengbojiang }
1865*572c4311Sfengbojiang argv2[2] = sdscatprintf(sdsempty(),"%d",keys);
1866*572c4311Sfengbojiang
1867*572c4311Sfengbojiang /* Call it */
1868*572c4311Sfengbojiang int eval_ldb = config.eval_ldb; /* Save it, may be reverteed. */
1869*572c4311Sfengbojiang retval = issueCommand(argc+3-got_comma, argv2);
1870*572c4311Sfengbojiang if (eval_ldb) {
1871*572c4311Sfengbojiang if (!config.eval_ldb) {
1872*572c4311Sfengbojiang /* If the debugging session ended immediately, there was an
1873*572c4311Sfengbojiang * error compiling the script. Show it and they don't enter
1874*572c4311Sfengbojiang * the REPL at all. */
1875*572c4311Sfengbojiang printf("Eval debugging session can't start:\n");
1876*572c4311Sfengbojiang cliReadReply(0);
1877*572c4311Sfengbojiang break; /* Return to the caller. */
1878*572c4311Sfengbojiang } else {
1879*572c4311Sfengbojiang strncpy(config.prompt,"lua debugger> ",sizeof(config.prompt));
1880*572c4311Sfengbojiang repl();
1881*572c4311Sfengbojiang /* Restart the session if repl() returned. */
1882*572c4311Sfengbojiang cliConnect(CC_FORCE);
1883*572c4311Sfengbojiang printf("\n");
1884*572c4311Sfengbojiang }
1885*572c4311Sfengbojiang } else {
1886*572c4311Sfengbojiang break; /* Return to the caller. */
1887*572c4311Sfengbojiang }
1888*572c4311Sfengbojiang }
1889*572c4311Sfengbojiang return retval;
1890*572c4311Sfengbojiang }
1891*572c4311Sfengbojiang
1892*572c4311Sfengbojiang /*------------------------------------------------------------------------------
1893*572c4311Sfengbojiang * Cluster Manager
1894*572c4311Sfengbojiang *--------------------------------------------------------------------------- */
1895*572c4311Sfengbojiang
1896*572c4311Sfengbojiang /* The Cluster Manager global structure */
1897*572c4311Sfengbojiang static struct clusterManager {
1898*572c4311Sfengbojiang list *nodes; /* List of nodes in the configuration. */
1899*572c4311Sfengbojiang list *errors;
1900*572c4311Sfengbojiang } cluster_manager;
1901*572c4311Sfengbojiang
1902*572c4311Sfengbojiang /* Used by clusterManagerFixSlotsCoverage */
1903*572c4311Sfengbojiang dict *clusterManagerUncoveredSlots = NULL;
1904*572c4311Sfengbojiang
1905*572c4311Sfengbojiang typedef struct clusterManagerNode {
1906*572c4311Sfengbojiang redisContext *context;
1907*572c4311Sfengbojiang sds name;
1908*572c4311Sfengbojiang char *ip;
1909*572c4311Sfengbojiang int port;
1910*572c4311Sfengbojiang uint64_t current_epoch;
1911*572c4311Sfengbojiang time_t ping_sent;
1912*572c4311Sfengbojiang time_t ping_recv;
1913*572c4311Sfengbojiang int flags;
1914*572c4311Sfengbojiang list *flags_str; /* Flags string representations */
1915*572c4311Sfengbojiang sds replicate; /* Master ID if node is a slave */
1916*572c4311Sfengbojiang int dirty; /* Node has changes that can be flushed */
1917*572c4311Sfengbojiang uint8_t slots[CLUSTER_MANAGER_SLOTS];
1918*572c4311Sfengbojiang int slots_count;
1919*572c4311Sfengbojiang int replicas_count;
1920*572c4311Sfengbojiang list *friends;
1921*572c4311Sfengbojiang sds *migrating; /* An array of sds where even strings are slots and odd
1922*572c4311Sfengbojiang * strings are the destination node IDs. */
1923*572c4311Sfengbojiang sds *importing; /* An array of sds where even strings are slots and odd
1924*572c4311Sfengbojiang * strings are the source node IDs. */
1925*572c4311Sfengbojiang int migrating_count; /* Length of the migrating array (migrating slots*2) */
1926*572c4311Sfengbojiang int importing_count; /* Length of the importing array (importing slots*2) */
1927*572c4311Sfengbojiang float weight; /* Weight used by rebalance */
1928*572c4311Sfengbojiang int balance; /* Used by rebalance */
1929*572c4311Sfengbojiang } clusterManagerNode;
1930*572c4311Sfengbojiang
1931*572c4311Sfengbojiang /* Data structure used to represent a sequence of cluster nodes. */
1932*572c4311Sfengbojiang typedef struct clusterManagerNodeArray {
1933*572c4311Sfengbojiang clusterManagerNode **nodes; /* Actual nodes array */
1934*572c4311Sfengbojiang clusterManagerNode **alloc; /* Pointer to the allocated memory */
1935*572c4311Sfengbojiang int len; /* Actual length of the array */
1936*572c4311Sfengbojiang int count; /* Non-NULL nodes count */
1937*572c4311Sfengbojiang } clusterManagerNodeArray;
1938*572c4311Sfengbojiang
1939*572c4311Sfengbojiang /* Used for the reshard table. */
1940*572c4311Sfengbojiang typedef struct clusterManagerReshardTableItem {
1941*572c4311Sfengbojiang clusterManagerNode *source;
1942*572c4311Sfengbojiang int slot;
1943*572c4311Sfengbojiang } clusterManagerReshardTableItem;
1944*572c4311Sfengbojiang
1945*572c4311Sfengbojiang static dictType clusterManagerDictType = {
1946*572c4311Sfengbojiang dictSdsHash, /* hash function */
1947*572c4311Sfengbojiang NULL, /* key dup */
1948*572c4311Sfengbojiang NULL, /* val dup */
1949*572c4311Sfengbojiang dictSdsKeyCompare, /* key compare */
1950*572c4311Sfengbojiang NULL, /* key destructor */
1951*572c4311Sfengbojiang dictSdsDestructor /* val destructor */
1952*572c4311Sfengbojiang };
1953*572c4311Sfengbojiang
1954*572c4311Sfengbojiang typedef int clusterManagerCommandProc(int argc, char **argv);
1955*572c4311Sfengbojiang typedef int (*clusterManagerOnReplyError)(redisReply *reply, int bulk_idx);
1956*572c4311Sfengbojiang
1957*572c4311Sfengbojiang /* Cluster Manager helper functions */
1958*572c4311Sfengbojiang
1959*572c4311Sfengbojiang static clusterManagerNode *clusterManagerNewNode(char *ip, int port);
1960*572c4311Sfengbojiang static clusterManagerNode *clusterManagerNodeByName(const char *name);
1961*572c4311Sfengbojiang static clusterManagerNode *clusterManagerNodeByAbbreviatedName(const char *n);
1962*572c4311Sfengbojiang static void clusterManagerNodeResetSlots(clusterManagerNode *node);
1963*572c4311Sfengbojiang static int clusterManagerNodeIsCluster(clusterManagerNode *node, char **err);
1964*572c4311Sfengbojiang static void clusterManagerPrintNotClusterNodeError(clusterManagerNode *node,
1965*572c4311Sfengbojiang char *err);
1966*572c4311Sfengbojiang static int clusterManagerNodeLoadInfo(clusterManagerNode *node, int opts,
1967*572c4311Sfengbojiang char **err);
1968*572c4311Sfengbojiang static int clusterManagerLoadInfoFromNode(clusterManagerNode *node, int opts);
1969*572c4311Sfengbojiang static int clusterManagerNodeIsEmpty(clusterManagerNode *node, char **err);
1970*572c4311Sfengbojiang static int clusterManagerGetAntiAffinityScore(clusterManagerNodeArray *ipnodes,
1971*572c4311Sfengbojiang int ip_count, clusterManagerNode ***offending, int *offending_len);
1972*572c4311Sfengbojiang static void clusterManagerOptimizeAntiAffinity(clusterManagerNodeArray *ipnodes,
1973*572c4311Sfengbojiang int ip_count);
1974*572c4311Sfengbojiang static sds clusterManagerNodeInfo(clusterManagerNode *node, int indent);
1975*572c4311Sfengbojiang static void clusterManagerShowNodes(void);
1976*572c4311Sfengbojiang static void clusterManagerShowClusterInfo(void);
1977*572c4311Sfengbojiang static int clusterManagerFlushNodeConfig(clusterManagerNode *node, char **err);
1978*572c4311Sfengbojiang static void clusterManagerWaitForClusterJoin(void);
1979*572c4311Sfengbojiang static int clusterManagerCheckCluster(int quiet);
1980*572c4311Sfengbojiang static void clusterManagerLog(int level, const char* fmt, ...);
1981*572c4311Sfengbojiang static int clusterManagerIsConfigConsistent(void);
1982*572c4311Sfengbojiang static void clusterManagerOnError(sds err);
1983*572c4311Sfengbojiang static void clusterManagerNodeArrayInit(clusterManagerNodeArray *array,
1984*572c4311Sfengbojiang int len);
1985*572c4311Sfengbojiang static void clusterManagerNodeArrayReset(clusterManagerNodeArray *array);
1986*572c4311Sfengbojiang static void clusterManagerNodeArrayShift(clusterManagerNodeArray *array,
1987*572c4311Sfengbojiang clusterManagerNode **nodeptr);
1988*572c4311Sfengbojiang static void clusterManagerNodeArrayAdd(clusterManagerNodeArray *array,
1989*572c4311Sfengbojiang clusterManagerNode *node);
1990*572c4311Sfengbojiang
1991*572c4311Sfengbojiang /* Cluster Manager commands. */
1992*572c4311Sfengbojiang
1993*572c4311Sfengbojiang static int clusterManagerCommandCreate(int argc, char **argv);
1994*572c4311Sfengbojiang static int clusterManagerCommandAddNode(int argc, char **argv);
1995*572c4311Sfengbojiang static int clusterManagerCommandDeleteNode(int argc, char **argv);
1996*572c4311Sfengbojiang static int clusterManagerCommandInfo(int argc, char **argv);
1997*572c4311Sfengbojiang static int clusterManagerCommandCheck(int argc, char **argv);
1998*572c4311Sfengbojiang static int clusterManagerCommandFix(int argc, char **argv);
1999*572c4311Sfengbojiang static int clusterManagerCommandReshard(int argc, char **argv);
2000*572c4311Sfengbojiang static int clusterManagerCommandRebalance(int argc, char **argv);
2001*572c4311Sfengbojiang static int clusterManagerCommandSetTimeout(int argc, char **argv);
2002*572c4311Sfengbojiang static int clusterManagerCommandImport(int argc, char **argv);
2003*572c4311Sfengbojiang static int clusterManagerCommandCall(int argc, char **argv);
2004*572c4311Sfengbojiang static int clusterManagerCommandHelp(int argc, char **argv);
2005*572c4311Sfengbojiang
2006*572c4311Sfengbojiang typedef struct clusterManagerCommandDef {
2007*572c4311Sfengbojiang char *name;
2008*572c4311Sfengbojiang clusterManagerCommandProc *proc;
2009*572c4311Sfengbojiang int arity;
2010*572c4311Sfengbojiang char *args;
2011*572c4311Sfengbojiang char *options;
2012*572c4311Sfengbojiang } clusterManagerCommandDef;
2013*572c4311Sfengbojiang
2014*572c4311Sfengbojiang clusterManagerCommandDef clusterManagerCommands[] = {
2015*572c4311Sfengbojiang {"create", clusterManagerCommandCreate, -2, "host1:port1 ... hostN:portN",
2016*572c4311Sfengbojiang "replicas <arg>"},
2017*572c4311Sfengbojiang {"check", clusterManagerCommandCheck, -1, "host:port",
2018*572c4311Sfengbojiang "search-multiple-owners"},
2019*572c4311Sfengbojiang {"info", clusterManagerCommandInfo, -1, "host:port", NULL},
2020*572c4311Sfengbojiang {"fix", clusterManagerCommandFix, -1, "host:port",
2021*572c4311Sfengbojiang "search-multiple-owners"},
2022*572c4311Sfengbojiang {"reshard", clusterManagerCommandReshard, -1, "host:port",
2023*572c4311Sfengbojiang "from <arg>,to <arg>,slots <arg>,yes,timeout <arg>,pipeline <arg>,"
2024*572c4311Sfengbojiang "replace"},
2025*572c4311Sfengbojiang {"rebalance", clusterManagerCommandRebalance, -1, "host:port",
2026*572c4311Sfengbojiang "weight <node1=w1...nodeN=wN>,use-empty-masters,"
2027*572c4311Sfengbojiang "timeout <arg>,simulate,pipeline <arg>,threshold <arg>,replace"},
2028*572c4311Sfengbojiang {"add-node", clusterManagerCommandAddNode, 2,
2029*572c4311Sfengbojiang "new_host:new_port existing_host:existing_port", "slave,master-id <arg>"},
2030*572c4311Sfengbojiang {"del-node", clusterManagerCommandDeleteNode, 2, "host:port node_id",NULL},
2031*572c4311Sfengbojiang {"call", clusterManagerCommandCall, -2,
2032*572c4311Sfengbojiang "host:port command arg arg .. arg", NULL},
2033*572c4311Sfengbojiang {"set-timeout", clusterManagerCommandSetTimeout, 2,
2034*572c4311Sfengbojiang "host:port milliseconds", NULL},
2035*572c4311Sfengbojiang {"import", clusterManagerCommandImport, 1, "host:port",
2036*572c4311Sfengbojiang "from <arg>,copy,replace"},
2037*572c4311Sfengbojiang {"help", clusterManagerCommandHelp, 0, NULL, NULL}
2038*572c4311Sfengbojiang };
2039*572c4311Sfengbojiang
2040*572c4311Sfengbojiang
createClusterManagerCommand(char * cmdname,int argc,char ** argv)2041*572c4311Sfengbojiang static void createClusterManagerCommand(char *cmdname, int argc, char **argv) {
2042*572c4311Sfengbojiang clusterManagerCommand *cmd = &config.cluster_manager_command;
2043*572c4311Sfengbojiang cmd->name = cmdname;
2044*572c4311Sfengbojiang cmd->argc = argc;
2045*572c4311Sfengbojiang cmd->argv = argc ? argv : NULL;
2046*572c4311Sfengbojiang if (isColorTerm()) cmd->flags |= CLUSTER_MANAGER_CMD_FLAG_COLOR;
2047*572c4311Sfengbojiang }
2048*572c4311Sfengbojiang
2049*572c4311Sfengbojiang
validateClusterManagerCommand(void)2050*572c4311Sfengbojiang static clusterManagerCommandProc *validateClusterManagerCommand(void) {
2051*572c4311Sfengbojiang int i, commands_count = sizeof(clusterManagerCommands) /
2052*572c4311Sfengbojiang sizeof(clusterManagerCommandDef);
2053*572c4311Sfengbojiang clusterManagerCommandProc *proc = NULL;
2054*572c4311Sfengbojiang char *cmdname = config.cluster_manager_command.name;
2055*572c4311Sfengbojiang int argc = config.cluster_manager_command.argc;
2056*572c4311Sfengbojiang for (i = 0; i < commands_count; i++) {
2057*572c4311Sfengbojiang clusterManagerCommandDef cmddef = clusterManagerCommands[i];
2058*572c4311Sfengbojiang if (!strcmp(cmddef.name, cmdname)) {
2059*572c4311Sfengbojiang if ((cmddef.arity > 0 && argc != cmddef.arity) ||
2060*572c4311Sfengbojiang (cmddef.arity < 0 && argc < (cmddef.arity * -1))) {
2061*572c4311Sfengbojiang fprintf(stderr, "[ERR] Wrong number of arguments for "
2062*572c4311Sfengbojiang "specified --cluster sub command\n");
2063*572c4311Sfengbojiang return NULL;
2064*572c4311Sfengbojiang }
2065*572c4311Sfengbojiang proc = cmddef.proc;
2066*572c4311Sfengbojiang }
2067*572c4311Sfengbojiang }
2068*572c4311Sfengbojiang if (!proc) fprintf(stderr, "Unknown --cluster subcommand\n");
2069*572c4311Sfengbojiang return proc;
2070*572c4311Sfengbojiang }
2071*572c4311Sfengbojiang
2072*572c4311Sfengbojiang /* Get host ip and port from command arguments. If only one argument has
2073*572c4311Sfengbojiang * been provided it must be in the form of 'ip:port', elsewhere
2074*572c4311Sfengbojiang * the first argument must be the ip and the second one the port.
2075*572c4311Sfengbojiang * If host and port can be detected, it returns 1 and it stores host and
2076*572c4311Sfengbojiang * port into variables referenced by'ip_ptr' and 'port_ptr' pointers,
2077*572c4311Sfengbojiang * elsewhere it returns 0. */
getClusterHostFromCmdArgs(int argc,char ** argv,char ** ip_ptr,int * port_ptr)2078*572c4311Sfengbojiang static int getClusterHostFromCmdArgs(int argc, char **argv,
2079*572c4311Sfengbojiang char **ip_ptr, int *port_ptr) {
2080*572c4311Sfengbojiang int port = 0;
2081*572c4311Sfengbojiang char *ip = NULL;
2082*572c4311Sfengbojiang if (argc == 1) {
2083*572c4311Sfengbojiang char *addr = argv[0];
2084*572c4311Sfengbojiang char *c = strrchr(addr, '@');
2085*572c4311Sfengbojiang if (c != NULL) *c = '\0';
2086*572c4311Sfengbojiang c = strrchr(addr, ':');
2087*572c4311Sfengbojiang if (c != NULL) {
2088*572c4311Sfengbojiang *c = '\0';
2089*572c4311Sfengbojiang ip = addr;
2090*572c4311Sfengbojiang port = atoi(++c);
2091*572c4311Sfengbojiang } else return 0;
2092*572c4311Sfengbojiang } else {
2093*572c4311Sfengbojiang ip = argv[0];
2094*572c4311Sfengbojiang port = atoi(argv[1]);
2095*572c4311Sfengbojiang }
2096*572c4311Sfengbojiang if (!ip || !port) return 0;
2097*572c4311Sfengbojiang else {
2098*572c4311Sfengbojiang *ip_ptr = ip;
2099*572c4311Sfengbojiang *port_ptr = port;
2100*572c4311Sfengbojiang }
2101*572c4311Sfengbojiang return 1;
2102*572c4311Sfengbojiang }
2103*572c4311Sfengbojiang
freeClusterManagerNodeFlags(list * flags)2104*572c4311Sfengbojiang static void freeClusterManagerNodeFlags(list *flags) {
2105*572c4311Sfengbojiang listIter li;
2106*572c4311Sfengbojiang listNode *ln;
2107*572c4311Sfengbojiang listRewind(flags, &li);
2108*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
2109*572c4311Sfengbojiang sds flag = ln->value;
2110*572c4311Sfengbojiang sdsfree(flag);
2111*572c4311Sfengbojiang }
2112*572c4311Sfengbojiang listRelease(flags);
2113*572c4311Sfengbojiang }
2114*572c4311Sfengbojiang
freeClusterManagerNode(clusterManagerNode * node)2115*572c4311Sfengbojiang static void freeClusterManagerNode(clusterManagerNode *node) {
2116*572c4311Sfengbojiang if (node->context != NULL) redisFree(node->context);
2117*572c4311Sfengbojiang if (node->friends != NULL) {
2118*572c4311Sfengbojiang listIter li;
2119*572c4311Sfengbojiang listNode *ln;
2120*572c4311Sfengbojiang listRewind(node->friends,&li);
2121*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
2122*572c4311Sfengbojiang clusterManagerNode *fn = ln->value;
2123*572c4311Sfengbojiang freeClusterManagerNode(fn);
2124*572c4311Sfengbojiang }
2125*572c4311Sfengbojiang listRelease(node->friends);
2126*572c4311Sfengbojiang node->friends = NULL;
2127*572c4311Sfengbojiang }
2128*572c4311Sfengbojiang if (node->name != NULL) sdsfree(node->name);
2129*572c4311Sfengbojiang if (node->replicate != NULL) sdsfree(node->replicate);
2130*572c4311Sfengbojiang if ((node->flags & CLUSTER_MANAGER_FLAG_FRIEND) && node->ip)
2131*572c4311Sfengbojiang sdsfree(node->ip);
2132*572c4311Sfengbojiang int i;
2133*572c4311Sfengbojiang if (node->migrating != NULL) {
2134*572c4311Sfengbojiang for (i = 0; i < node->migrating_count; i++) sdsfree(node->migrating[i]);
2135*572c4311Sfengbojiang zfree(node->migrating);
2136*572c4311Sfengbojiang }
2137*572c4311Sfengbojiang if (node->importing != NULL) {
2138*572c4311Sfengbojiang for (i = 0; i < node->importing_count; i++) sdsfree(node->importing[i]);
2139*572c4311Sfengbojiang zfree(node->importing);
2140*572c4311Sfengbojiang }
2141*572c4311Sfengbojiang if (node->flags_str != NULL) {
2142*572c4311Sfengbojiang freeClusterManagerNodeFlags(node->flags_str);
2143*572c4311Sfengbojiang node->flags_str = NULL;
2144*572c4311Sfengbojiang }
2145*572c4311Sfengbojiang zfree(node);
2146*572c4311Sfengbojiang }
2147*572c4311Sfengbojiang
freeClusterManager(void)2148*572c4311Sfengbojiang static void freeClusterManager(void) {
2149*572c4311Sfengbojiang listIter li;
2150*572c4311Sfengbojiang listNode *ln;
2151*572c4311Sfengbojiang if (cluster_manager.nodes != NULL) {
2152*572c4311Sfengbojiang listRewind(cluster_manager.nodes,&li);
2153*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
2154*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
2155*572c4311Sfengbojiang freeClusterManagerNode(n);
2156*572c4311Sfengbojiang }
2157*572c4311Sfengbojiang listRelease(cluster_manager.nodes);
2158*572c4311Sfengbojiang cluster_manager.nodes = NULL;
2159*572c4311Sfengbojiang }
2160*572c4311Sfengbojiang if (cluster_manager.errors != NULL) {
2161*572c4311Sfengbojiang listRewind(cluster_manager.errors,&li);
2162*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
2163*572c4311Sfengbojiang sds err = ln->value;
2164*572c4311Sfengbojiang sdsfree(err);
2165*572c4311Sfengbojiang }
2166*572c4311Sfengbojiang listRelease(cluster_manager.errors);
2167*572c4311Sfengbojiang cluster_manager.errors = NULL;
2168*572c4311Sfengbojiang }
2169*572c4311Sfengbojiang if (clusterManagerUncoveredSlots != NULL)
2170*572c4311Sfengbojiang dictRelease(clusterManagerUncoveredSlots);
2171*572c4311Sfengbojiang }
2172*572c4311Sfengbojiang
clusterManagerNewNode(char * ip,int port)2173*572c4311Sfengbojiang static clusterManagerNode *clusterManagerNewNode(char *ip, int port) {
2174*572c4311Sfengbojiang clusterManagerNode *node = zmalloc(sizeof(*node));
2175*572c4311Sfengbojiang node->context = NULL;
2176*572c4311Sfengbojiang node->name = NULL;
2177*572c4311Sfengbojiang node->ip = ip;
2178*572c4311Sfengbojiang node->port = port;
2179*572c4311Sfengbojiang node->current_epoch = 0;
2180*572c4311Sfengbojiang node->ping_sent = 0;
2181*572c4311Sfengbojiang node->ping_recv = 0;
2182*572c4311Sfengbojiang node->flags = 0;
2183*572c4311Sfengbojiang node->flags_str = NULL;
2184*572c4311Sfengbojiang node->replicate = NULL;
2185*572c4311Sfengbojiang node->dirty = 0;
2186*572c4311Sfengbojiang node->friends = NULL;
2187*572c4311Sfengbojiang node->migrating = NULL;
2188*572c4311Sfengbojiang node->importing = NULL;
2189*572c4311Sfengbojiang node->migrating_count = 0;
2190*572c4311Sfengbojiang node->importing_count = 0;
2191*572c4311Sfengbojiang node->replicas_count = 0;
2192*572c4311Sfengbojiang node->weight = 1.0f;
2193*572c4311Sfengbojiang node->balance = 0;
2194*572c4311Sfengbojiang clusterManagerNodeResetSlots(node);
2195*572c4311Sfengbojiang return node;
2196*572c4311Sfengbojiang }
2197*572c4311Sfengbojiang
2198*572c4311Sfengbojiang /* Check whether reply is NULL or its type is REDIS_REPLY_ERROR. In the
2199*572c4311Sfengbojiang * latest case, if the 'err' arg is not NULL, it gets allocated with a copy
2200*572c4311Sfengbojiang * of reply error (it's up to the caller function to free it), elsewhere
2201*572c4311Sfengbojiang * the error is directly printed. */
clusterManagerCheckRedisReply(clusterManagerNode * n,redisReply * r,char ** err)2202*572c4311Sfengbojiang static int clusterManagerCheckRedisReply(clusterManagerNode *n,
2203*572c4311Sfengbojiang redisReply *r, char **err)
2204*572c4311Sfengbojiang {
2205*572c4311Sfengbojiang int is_err = 0;
2206*572c4311Sfengbojiang if (!r || (is_err = (r->type == REDIS_REPLY_ERROR))) {
2207*572c4311Sfengbojiang if (is_err) {
2208*572c4311Sfengbojiang if (err != NULL) {
2209*572c4311Sfengbojiang *err = zmalloc((r->len + 1) * sizeof(char));
2210*572c4311Sfengbojiang strcpy(*err, r->str);
2211*572c4311Sfengbojiang } else CLUSTER_MANAGER_PRINT_REPLY_ERROR(n, r->str);
2212*572c4311Sfengbojiang }
2213*572c4311Sfengbojiang return 0;
2214*572c4311Sfengbojiang }
2215*572c4311Sfengbojiang return 1;
2216*572c4311Sfengbojiang }
2217*572c4311Sfengbojiang
2218*572c4311Sfengbojiang /* Execute MULTI command on a cluster node. */
clusterManagerStartTransaction(clusterManagerNode * node)2219*572c4311Sfengbojiang static int clusterManagerStartTransaction(clusterManagerNode *node) {
2220*572c4311Sfengbojiang redisReply *reply = CLUSTER_MANAGER_COMMAND(node, "MULTI");
2221*572c4311Sfengbojiang int success = clusterManagerCheckRedisReply(node, reply, NULL);
2222*572c4311Sfengbojiang if (reply) freeReplyObject(reply);
2223*572c4311Sfengbojiang return success;
2224*572c4311Sfengbojiang }
2225*572c4311Sfengbojiang
2226*572c4311Sfengbojiang /* Execute EXEC command on a cluster node. */
clusterManagerExecTransaction(clusterManagerNode * node,clusterManagerOnReplyError onerror)2227*572c4311Sfengbojiang static int clusterManagerExecTransaction(clusterManagerNode *node,
2228*572c4311Sfengbojiang clusterManagerOnReplyError onerror)
2229*572c4311Sfengbojiang {
2230*572c4311Sfengbojiang redisReply *reply = CLUSTER_MANAGER_COMMAND(node, "EXEC");
2231*572c4311Sfengbojiang int success = clusterManagerCheckRedisReply(node, reply, NULL);
2232*572c4311Sfengbojiang if (success) {
2233*572c4311Sfengbojiang if (reply->type != REDIS_REPLY_ARRAY) {
2234*572c4311Sfengbojiang success = 0;
2235*572c4311Sfengbojiang goto cleanup;
2236*572c4311Sfengbojiang }
2237*572c4311Sfengbojiang size_t i;
2238*572c4311Sfengbojiang for (i = 0; i < reply->elements; i++) {
2239*572c4311Sfengbojiang redisReply *r = reply->element[i];
2240*572c4311Sfengbojiang char *err = NULL;
2241*572c4311Sfengbojiang success = clusterManagerCheckRedisReply(node, r, &err);
2242*572c4311Sfengbojiang if (!success && onerror) success = onerror(r, i);
2243*572c4311Sfengbojiang if (err) {
2244*572c4311Sfengbojiang if (!success)
2245*572c4311Sfengbojiang CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);
2246*572c4311Sfengbojiang zfree(err);
2247*572c4311Sfengbojiang }
2248*572c4311Sfengbojiang if (!success) break;
2249*572c4311Sfengbojiang }
2250*572c4311Sfengbojiang }
2251*572c4311Sfengbojiang cleanup:
2252*572c4311Sfengbojiang if (reply) freeReplyObject(reply);
2253*572c4311Sfengbojiang return success;
2254*572c4311Sfengbojiang }
2255*572c4311Sfengbojiang
clusterManagerNodeConnect(clusterManagerNode * node)2256*572c4311Sfengbojiang static int clusterManagerNodeConnect(clusterManagerNode *node) {
2257*572c4311Sfengbojiang if (node->context) redisFree(node->context);
2258*572c4311Sfengbojiang node->context = redisConnect(node->ip, node->port);
2259*572c4311Sfengbojiang if (node->context->err) {
2260*572c4311Sfengbojiang fprintf(stderr,"Could not connect to Redis at ");
2261*572c4311Sfengbojiang fprintf(stderr,"%s:%d: %s\n", node->ip, node->port,
2262*572c4311Sfengbojiang node->context->errstr);
2263*572c4311Sfengbojiang redisFree(node->context);
2264*572c4311Sfengbojiang node->context = NULL;
2265*572c4311Sfengbojiang return 0;
2266*572c4311Sfengbojiang }
2267*572c4311Sfengbojiang /* Set aggressive KEEP_ALIVE socket option in the Redis context socket
2268*572c4311Sfengbojiang * in order to prevent timeouts caused by the execution of long
2269*572c4311Sfengbojiang * commands. At the same time this improves the detection of real
2270*572c4311Sfengbojiang * errors. */
2271*572c4311Sfengbojiang anetKeepAlive(NULL, node->context->fd, REDIS_CLI_KEEPALIVE_INTERVAL);
2272*572c4311Sfengbojiang if (config.auth) {
2273*572c4311Sfengbojiang redisReply *reply = redisCommand(node->context,"AUTH %s",config.auth);
2274*572c4311Sfengbojiang int ok = clusterManagerCheckRedisReply(node, reply, NULL);
2275*572c4311Sfengbojiang if (reply != NULL) freeReplyObject(reply);
2276*572c4311Sfengbojiang if (!ok) return 0;
2277*572c4311Sfengbojiang }
2278*572c4311Sfengbojiang return 1;
2279*572c4311Sfengbojiang }
2280*572c4311Sfengbojiang
clusterManagerRemoveNodeFromList(list * nodelist,clusterManagerNode * node)2281*572c4311Sfengbojiang static void clusterManagerRemoveNodeFromList(list *nodelist,
2282*572c4311Sfengbojiang clusterManagerNode *node) {
2283*572c4311Sfengbojiang listIter li;
2284*572c4311Sfengbojiang listNode *ln;
2285*572c4311Sfengbojiang listRewind(nodelist, &li);
2286*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
2287*572c4311Sfengbojiang if (node == ln->value) {
2288*572c4311Sfengbojiang listDelNode(nodelist, ln);
2289*572c4311Sfengbojiang break;
2290*572c4311Sfengbojiang }
2291*572c4311Sfengbojiang }
2292*572c4311Sfengbojiang }
2293*572c4311Sfengbojiang
2294*572c4311Sfengbojiang /* Return the node with the specified name (ID) or NULL. */
clusterManagerNodeByName(const char * name)2295*572c4311Sfengbojiang static clusterManagerNode *clusterManagerNodeByName(const char *name) {
2296*572c4311Sfengbojiang if (cluster_manager.nodes == NULL) return NULL;
2297*572c4311Sfengbojiang clusterManagerNode *found = NULL;
2298*572c4311Sfengbojiang sds lcname = sdsempty();
2299*572c4311Sfengbojiang lcname = sdscpy(lcname, name);
2300*572c4311Sfengbojiang sdstolower(lcname);
2301*572c4311Sfengbojiang listIter li;
2302*572c4311Sfengbojiang listNode *ln;
2303*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
2304*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
2305*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
2306*572c4311Sfengbojiang if (n->name && !sdscmp(n->name, lcname)) {
2307*572c4311Sfengbojiang found = n;
2308*572c4311Sfengbojiang break;
2309*572c4311Sfengbojiang }
2310*572c4311Sfengbojiang }
2311*572c4311Sfengbojiang sdsfree(lcname);
2312*572c4311Sfengbojiang return found;
2313*572c4311Sfengbojiang }
2314*572c4311Sfengbojiang
2315*572c4311Sfengbojiang /* Like clusterManagerNodeByName but the specified name can be just the first
2316*572c4311Sfengbojiang * part of the node ID as long as the prefix in unique across the
2317*572c4311Sfengbojiang * cluster.
2318*572c4311Sfengbojiang */
clusterManagerNodeByAbbreviatedName(const char * name)2319*572c4311Sfengbojiang static clusterManagerNode *clusterManagerNodeByAbbreviatedName(const char*name)
2320*572c4311Sfengbojiang {
2321*572c4311Sfengbojiang if (cluster_manager.nodes == NULL) return NULL;
2322*572c4311Sfengbojiang clusterManagerNode *found = NULL;
2323*572c4311Sfengbojiang sds lcname = sdsempty();
2324*572c4311Sfengbojiang lcname = sdscpy(lcname, name);
2325*572c4311Sfengbojiang sdstolower(lcname);
2326*572c4311Sfengbojiang listIter li;
2327*572c4311Sfengbojiang listNode *ln;
2328*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
2329*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
2330*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
2331*572c4311Sfengbojiang if (n->name &&
2332*572c4311Sfengbojiang strstr(n->name, lcname) == n->name) {
2333*572c4311Sfengbojiang found = n;
2334*572c4311Sfengbojiang break;
2335*572c4311Sfengbojiang }
2336*572c4311Sfengbojiang }
2337*572c4311Sfengbojiang sdsfree(lcname);
2338*572c4311Sfengbojiang return found;
2339*572c4311Sfengbojiang }
2340*572c4311Sfengbojiang
clusterManagerNodeResetSlots(clusterManagerNode * node)2341*572c4311Sfengbojiang static void clusterManagerNodeResetSlots(clusterManagerNode *node) {
2342*572c4311Sfengbojiang memset(node->slots, 0, sizeof(node->slots));
2343*572c4311Sfengbojiang node->slots_count = 0;
2344*572c4311Sfengbojiang }
2345*572c4311Sfengbojiang
2346*572c4311Sfengbojiang /* Call "INFO" redis command on the specified node and return the reply. */
clusterManagerGetNodeRedisInfo(clusterManagerNode * node,char ** err)2347*572c4311Sfengbojiang static redisReply *clusterManagerGetNodeRedisInfo(clusterManagerNode *node,
2348*572c4311Sfengbojiang char **err)
2349*572c4311Sfengbojiang {
2350*572c4311Sfengbojiang redisReply *info = CLUSTER_MANAGER_COMMAND(node, "INFO");
2351*572c4311Sfengbojiang if (err != NULL) *err = NULL;
2352*572c4311Sfengbojiang if (info == NULL) return NULL;
2353*572c4311Sfengbojiang if (info->type == REDIS_REPLY_ERROR) {
2354*572c4311Sfengbojiang if (err != NULL) {
2355*572c4311Sfengbojiang *err = zmalloc((info->len + 1) * sizeof(char));
2356*572c4311Sfengbojiang strcpy(*err, info->str);
2357*572c4311Sfengbojiang }
2358*572c4311Sfengbojiang freeReplyObject(info);
2359*572c4311Sfengbojiang return NULL;
2360*572c4311Sfengbojiang }
2361*572c4311Sfengbojiang return info;
2362*572c4311Sfengbojiang }
2363*572c4311Sfengbojiang
clusterManagerNodeIsCluster(clusterManagerNode * node,char ** err)2364*572c4311Sfengbojiang static int clusterManagerNodeIsCluster(clusterManagerNode *node, char **err) {
2365*572c4311Sfengbojiang redisReply *info = clusterManagerGetNodeRedisInfo(node, err);
2366*572c4311Sfengbojiang if (info == NULL) return 0;
2367*572c4311Sfengbojiang int is_cluster = (int) getLongInfoField(info->str, "cluster_enabled");
2368*572c4311Sfengbojiang freeReplyObject(info);
2369*572c4311Sfengbojiang return is_cluster;
2370*572c4311Sfengbojiang }
2371*572c4311Sfengbojiang
2372*572c4311Sfengbojiang /* Checks whether the node is empty. Node is considered not-empty if it has
2373*572c4311Sfengbojiang * some key or if it already knows other nodes */
clusterManagerNodeIsEmpty(clusterManagerNode * node,char ** err)2374*572c4311Sfengbojiang static int clusterManagerNodeIsEmpty(clusterManagerNode *node, char **err) {
2375*572c4311Sfengbojiang redisReply *info = clusterManagerGetNodeRedisInfo(node, err);
2376*572c4311Sfengbojiang int is_empty = 1;
2377*572c4311Sfengbojiang if (info == NULL) return 0;
2378*572c4311Sfengbojiang if (strstr(info->str, "db0:") != NULL) {
2379*572c4311Sfengbojiang is_empty = 0;
2380*572c4311Sfengbojiang goto result;
2381*572c4311Sfengbojiang }
2382*572c4311Sfengbojiang freeReplyObject(info);
2383*572c4311Sfengbojiang info = CLUSTER_MANAGER_COMMAND(node, "CLUSTER INFO");
2384*572c4311Sfengbojiang if (err != NULL) *err = NULL;
2385*572c4311Sfengbojiang if (!clusterManagerCheckRedisReply(node, info, err)) {
2386*572c4311Sfengbojiang is_empty = 0;
2387*572c4311Sfengbojiang goto result;
2388*572c4311Sfengbojiang }
2389*572c4311Sfengbojiang long known_nodes = getLongInfoField(info->str, "cluster_known_nodes");
2390*572c4311Sfengbojiang is_empty = (known_nodes == 1);
2391*572c4311Sfengbojiang result:
2392*572c4311Sfengbojiang freeReplyObject(info);
2393*572c4311Sfengbojiang return is_empty;
2394*572c4311Sfengbojiang }
2395*572c4311Sfengbojiang
2396*572c4311Sfengbojiang /* Return the anti-affinity score, which is a measure of the amount of
2397*572c4311Sfengbojiang * violations of anti-affinity in the current cluster layout, that is, how
2398*572c4311Sfengbojiang * badly the masters and slaves are distributed in the different IP
2399*572c4311Sfengbojiang * addresses so that slaves of the same master are not in the master
2400*572c4311Sfengbojiang * host and are also in different hosts.
2401*572c4311Sfengbojiang *
2402*572c4311Sfengbojiang * The score is calculated as follows:
2403*572c4311Sfengbojiang *
2404*572c4311Sfengbojiang * SAME_AS_MASTER = 10000 * each slave in the same IP of its master.
2405*572c4311Sfengbojiang * SAME_AS_SLAVE = 1 * each slave having the same IP as another slave
2406*572c4311Sfengbojiang of the same master.
2407*572c4311Sfengbojiang * FINAL_SCORE = SAME_AS_MASTER + SAME_AS_SLAVE
2408*572c4311Sfengbojiang *
2409*572c4311Sfengbojiang * So a greater score means a worse anti-affinity level, while zero
2410*572c4311Sfengbojiang * means perfect anti-affinity.
2411*572c4311Sfengbojiang *
2412*572c4311Sfengbojiang * The anti affinity optimizator will try to get a score as low as
2413*572c4311Sfengbojiang * possible. Since we do not want to sacrifice the fact that slaves should
2414*572c4311Sfengbojiang * not be in the same host as the master, we assign 10000 times the score
2415*572c4311Sfengbojiang * to this violation, so that we'll optimize for the second factor only
2416*572c4311Sfengbojiang * if it does not impact the first one.
2417*572c4311Sfengbojiang *
2418*572c4311Sfengbojiang * The ipnodes argument is an array of clusterManagerNodeArray, one for
2419*572c4311Sfengbojiang * each IP, while ip_count is the total number of IPs in the configuration.
2420*572c4311Sfengbojiang *
2421*572c4311Sfengbojiang * The function returns the above score, and the list of
2422*572c4311Sfengbojiang * offending slaves can be stored into the 'offending' argument,
2423*572c4311Sfengbojiang * so that the optimizer can try changing the configuration of the
2424*572c4311Sfengbojiang * slaves violating the anti-affinity goals. */
clusterManagerGetAntiAffinityScore(clusterManagerNodeArray * ipnodes,int ip_count,clusterManagerNode *** offending,int * offending_len)2425*572c4311Sfengbojiang static int clusterManagerGetAntiAffinityScore(clusterManagerNodeArray *ipnodes,
2426*572c4311Sfengbojiang int ip_count, clusterManagerNode ***offending, int *offending_len)
2427*572c4311Sfengbojiang {
2428*572c4311Sfengbojiang int score = 0, i, j;
2429*572c4311Sfengbojiang int node_len = cluster_manager.nodes->len;
2430*572c4311Sfengbojiang clusterManagerNode **offending_p = NULL;
2431*572c4311Sfengbojiang if (offending != NULL) {
2432*572c4311Sfengbojiang *offending = zcalloc(node_len * sizeof(clusterManagerNode*));
2433*572c4311Sfengbojiang offending_p = *offending;
2434*572c4311Sfengbojiang }
2435*572c4311Sfengbojiang /* For each set of nodes in the same host, split by
2436*572c4311Sfengbojiang * related nodes (masters and slaves which are involved in
2437*572c4311Sfengbojiang * replication of each other) */
2438*572c4311Sfengbojiang for (i = 0; i < ip_count; i++) {
2439*572c4311Sfengbojiang clusterManagerNodeArray *node_array = &(ipnodes[i]);
2440*572c4311Sfengbojiang dict *related = dictCreate(&clusterManagerDictType, NULL);
2441*572c4311Sfengbojiang char *ip = NULL;
2442*572c4311Sfengbojiang for (j = 0; j < node_array->len; j++) {
2443*572c4311Sfengbojiang clusterManagerNode *node = node_array->nodes[j];
2444*572c4311Sfengbojiang if (node == NULL) continue;
2445*572c4311Sfengbojiang if (!ip) ip = node->ip;
2446*572c4311Sfengbojiang sds types;
2447*572c4311Sfengbojiang /* We always use the Master ID as key. */
2448*572c4311Sfengbojiang sds key = (!node->replicate ? node->name : node->replicate);
2449*572c4311Sfengbojiang assert(key != NULL);
2450*572c4311Sfengbojiang dictEntry *entry = dictFind(related, key);
2451*572c4311Sfengbojiang if (entry) types = sdsdup((sds) dictGetVal(entry));
2452*572c4311Sfengbojiang else types = sdsempty();
2453*572c4311Sfengbojiang /* Master type 'm' is always set as the first character of the
2454*572c4311Sfengbojiang * types string. */
2455*572c4311Sfengbojiang if (!node->replicate) types = sdscatprintf(types, "m%s", types);
2456*572c4311Sfengbojiang else types = sdscat(types, "s");
2457*572c4311Sfengbojiang dictReplace(related, key, types);
2458*572c4311Sfengbojiang }
2459*572c4311Sfengbojiang /* Now it's trivial to check, for each related group having the
2460*572c4311Sfengbojiang * same host, what is their local score. */
2461*572c4311Sfengbojiang dictIterator *iter = dictGetIterator(related);
2462*572c4311Sfengbojiang dictEntry *entry;
2463*572c4311Sfengbojiang while ((entry = dictNext(iter)) != NULL) {
2464*572c4311Sfengbojiang sds types = (sds) dictGetVal(entry);
2465*572c4311Sfengbojiang sds name = (sds) dictGetKey(entry);
2466*572c4311Sfengbojiang int typeslen = sdslen(types);
2467*572c4311Sfengbojiang if (typeslen < 2) continue;
2468*572c4311Sfengbojiang if (types[0] == 'm') score += (10000 * (typeslen - 1));
2469*572c4311Sfengbojiang else score += (1 * typeslen);
2470*572c4311Sfengbojiang if (offending == NULL) continue;
2471*572c4311Sfengbojiang /* Populate the list of offending nodes. */
2472*572c4311Sfengbojiang listIter li;
2473*572c4311Sfengbojiang listNode *ln;
2474*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
2475*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
2476*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
2477*572c4311Sfengbojiang if (n->replicate == NULL) continue;
2478*572c4311Sfengbojiang if (!strcmp(n->replicate, name) && !strcmp(n->ip, ip)) {
2479*572c4311Sfengbojiang *(offending_p++) = n;
2480*572c4311Sfengbojiang if (offending_len != NULL) (*offending_len)++;
2481*572c4311Sfengbojiang break;
2482*572c4311Sfengbojiang }
2483*572c4311Sfengbojiang }
2484*572c4311Sfengbojiang }
2485*572c4311Sfengbojiang //if (offending_len != NULL) *offending_len = offending_p - *offending;
2486*572c4311Sfengbojiang dictReleaseIterator(iter);
2487*572c4311Sfengbojiang dictRelease(related);
2488*572c4311Sfengbojiang }
2489*572c4311Sfengbojiang return score;
2490*572c4311Sfengbojiang }
2491*572c4311Sfengbojiang
clusterManagerOptimizeAntiAffinity(clusterManagerNodeArray * ipnodes,int ip_count)2492*572c4311Sfengbojiang static void clusterManagerOptimizeAntiAffinity(clusterManagerNodeArray *ipnodes,
2493*572c4311Sfengbojiang int ip_count)
2494*572c4311Sfengbojiang {
2495*572c4311Sfengbojiang clusterManagerNode **offenders = NULL;
2496*572c4311Sfengbojiang int score = clusterManagerGetAntiAffinityScore(ipnodes, ip_count,
2497*572c4311Sfengbojiang NULL, NULL);
2498*572c4311Sfengbojiang if (score == 0) goto cleanup;
2499*572c4311Sfengbojiang clusterManagerLogInfo(">>> Trying to optimize slaves allocation "
2500*572c4311Sfengbojiang "for anti-affinity\n");
2501*572c4311Sfengbojiang int node_len = cluster_manager.nodes->len;
2502*572c4311Sfengbojiang int maxiter = 500 * node_len; // Effort is proportional to cluster size...
2503*572c4311Sfengbojiang srand(time(NULL));
2504*572c4311Sfengbojiang while (maxiter > 0) {
2505*572c4311Sfengbojiang int offending_len = 0;
2506*572c4311Sfengbojiang if (offenders != NULL) {
2507*572c4311Sfengbojiang zfree(offenders);
2508*572c4311Sfengbojiang offenders = NULL;
2509*572c4311Sfengbojiang }
2510*572c4311Sfengbojiang score = clusterManagerGetAntiAffinityScore(ipnodes,
2511*572c4311Sfengbojiang ip_count,
2512*572c4311Sfengbojiang &offenders,
2513*572c4311Sfengbojiang &offending_len);
2514*572c4311Sfengbojiang if (score == 0) break; // Optimal anti affinity reached
2515*572c4311Sfengbojiang /* We'll try to randomly swap a slave's assigned master causing
2516*572c4311Sfengbojiang * an affinity problem with another random slave, to see if we
2517*572c4311Sfengbojiang * can improve the affinity. */
2518*572c4311Sfengbojiang int rand_idx = rand() % offending_len;
2519*572c4311Sfengbojiang clusterManagerNode *first = offenders[rand_idx],
2520*572c4311Sfengbojiang *second = NULL;
2521*572c4311Sfengbojiang clusterManagerNode **other_replicas = zcalloc((node_len - 1) *
2522*572c4311Sfengbojiang sizeof(*other_replicas));
2523*572c4311Sfengbojiang int other_replicas_count = 0;
2524*572c4311Sfengbojiang listIter li;
2525*572c4311Sfengbojiang listNode *ln;
2526*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
2527*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
2528*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
2529*572c4311Sfengbojiang if (n != first && n->replicate != NULL)
2530*572c4311Sfengbojiang other_replicas[other_replicas_count++] = n;
2531*572c4311Sfengbojiang }
2532*572c4311Sfengbojiang if (other_replicas_count == 0) {
2533*572c4311Sfengbojiang zfree(other_replicas);
2534*572c4311Sfengbojiang break;
2535*572c4311Sfengbojiang }
2536*572c4311Sfengbojiang rand_idx = rand() % other_replicas_count;
2537*572c4311Sfengbojiang second = other_replicas[rand_idx];
2538*572c4311Sfengbojiang char *first_master = first->replicate,
2539*572c4311Sfengbojiang *second_master = second->replicate;
2540*572c4311Sfengbojiang first->replicate = second_master, first->dirty = 1;
2541*572c4311Sfengbojiang second->replicate = first_master, second->dirty = 1;
2542*572c4311Sfengbojiang int new_score = clusterManagerGetAntiAffinityScore(ipnodes,
2543*572c4311Sfengbojiang ip_count,
2544*572c4311Sfengbojiang NULL, NULL);
2545*572c4311Sfengbojiang /* If the change actually makes thing worse, revert. Otherwise
2546*572c4311Sfengbojiang * leave as it is because the best solution may need a few
2547*572c4311Sfengbojiang * combined swaps. */
2548*572c4311Sfengbojiang if (new_score > score) {
2549*572c4311Sfengbojiang first->replicate = first_master;
2550*572c4311Sfengbojiang second->replicate = second_master;
2551*572c4311Sfengbojiang }
2552*572c4311Sfengbojiang zfree(other_replicas);
2553*572c4311Sfengbojiang maxiter--;
2554*572c4311Sfengbojiang }
2555*572c4311Sfengbojiang score = clusterManagerGetAntiAffinityScore(ipnodes, ip_count, NULL, NULL);
2556*572c4311Sfengbojiang char *msg;
2557*572c4311Sfengbojiang int perfect = (score == 0);
2558*572c4311Sfengbojiang int log_level = (perfect ? CLUSTER_MANAGER_LOG_LVL_SUCCESS :
2559*572c4311Sfengbojiang CLUSTER_MANAGER_LOG_LVL_WARN);
2560*572c4311Sfengbojiang if (perfect) msg = "[OK] Perfect anti-affinity obtained!";
2561*572c4311Sfengbojiang else if (score >= 10000)
2562*572c4311Sfengbojiang msg = ("[WARNING] Some slaves are in the same host as their master");
2563*572c4311Sfengbojiang else
2564*572c4311Sfengbojiang msg=("[WARNING] Some slaves of the same master are in the same host");
2565*572c4311Sfengbojiang clusterManagerLog(log_level, "%s\n", msg);
2566*572c4311Sfengbojiang cleanup:
2567*572c4311Sfengbojiang zfree(offenders);
2568*572c4311Sfengbojiang }
2569*572c4311Sfengbojiang
2570*572c4311Sfengbojiang /* Return a representable string of the node's flags */
clusterManagerNodeFlagString(clusterManagerNode * node)2571*572c4311Sfengbojiang static sds clusterManagerNodeFlagString(clusterManagerNode *node) {
2572*572c4311Sfengbojiang sds flags = sdsempty();
2573*572c4311Sfengbojiang if (!node->flags_str) return flags;
2574*572c4311Sfengbojiang int empty = 1;
2575*572c4311Sfengbojiang listIter li;
2576*572c4311Sfengbojiang listNode *ln;
2577*572c4311Sfengbojiang listRewind(node->flags_str, &li);
2578*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
2579*572c4311Sfengbojiang sds flag = ln->value;
2580*572c4311Sfengbojiang if (strcmp(flag, "myself") == 0) continue;
2581*572c4311Sfengbojiang if (!empty) flags = sdscat(flags, ",");
2582*572c4311Sfengbojiang flags = sdscatfmt(flags, "%S", flag);
2583*572c4311Sfengbojiang empty = 0;
2584*572c4311Sfengbojiang }
2585*572c4311Sfengbojiang return flags;
2586*572c4311Sfengbojiang }
2587*572c4311Sfengbojiang
2588*572c4311Sfengbojiang /* Return a representable string of the node's slots */
clusterManagerNodeSlotsString(clusterManagerNode * node)2589*572c4311Sfengbojiang static sds clusterManagerNodeSlotsString(clusterManagerNode *node) {
2590*572c4311Sfengbojiang sds slots = sdsempty();
2591*572c4311Sfengbojiang int first_range_idx = -1, last_slot_idx = -1, i;
2592*572c4311Sfengbojiang for (i = 0; i < CLUSTER_MANAGER_SLOTS; i++) {
2593*572c4311Sfengbojiang int has_slot = node->slots[i];
2594*572c4311Sfengbojiang if (has_slot) {
2595*572c4311Sfengbojiang if (first_range_idx == -1) {
2596*572c4311Sfengbojiang if (sdslen(slots)) slots = sdscat(slots, ",");
2597*572c4311Sfengbojiang first_range_idx = i;
2598*572c4311Sfengbojiang slots = sdscatfmt(slots, "[%u", i);
2599*572c4311Sfengbojiang }
2600*572c4311Sfengbojiang last_slot_idx = i;
2601*572c4311Sfengbojiang } else {
2602*572c4311Sfengbojiang if (last_slot_idx >= 0) {
2603*572c4311Sfengbojiang if (first_range_idx == last_slot_idx)
2604*572c4311Sfengbojiang slots = sdscat(slots, "]");
2605*572c4311Sfengbojiang else slots = sdscatfmt(slots, "-%u]", last_slot_idx);
2606*572c4311Sfengbojiang }
2607*572c4311Sfengbojiang last_slot_idx = -1;
2608*572c4311Sfengbojiang first_range_idx = -1;
2609*572c4311Sfengbojiang }
2610*572c4311Sfengbojiang }
2611*572c4311Sfengbojiang if (last_slot_idx >= 0) {
2612*572c4311Sfengbojiang if (first_range_idx == last_slot_idx) slots = sdscat(slots, "]");
2613*572c4311Sfengbojiang else slots = sdscatfmt(slots, "-%u]", last_slot_idx);
2614*572c4311Sfengbojiang }
2615*572c4311Sfengbojiang return slots;
2616*572c4311Sfengbojiang }
2617*572c4311Sfengbojiang
2618*572c4311Sfengbojiang /* -----------------------------------------------------------------------------
2619*572c4311Sfengbojiang * Key space handling
2620*572c4311Sfengbojiang * -------------------------------------------------------------------------- */
2621*572c4311Sfengbojiang
2622*572c4311Sfengbojiang /* We have 16384 hash slots. The hash slot of a given key is obtained
2623*572c4311Sfengbojiang * as the least significant 14 bits of the crc16 of the key.
2624*572c4311Sfengbojiang *
2625*572c4311Sfengbojiang * However if the key contains the {...} pattern, only the part between
2626*572c4311Sfengbojiang * { and } is hashed. This may be useful in the future to force certain
2627*572c4311Sfengbojiang * keys to be in the same node (assuming no resharding is in progress). */
clusterManagerKeyHashSlot(char * key,int keylen)2628*572c4311Sfengbojiang static unsigned int clusterManagerKeyHashSlot(char *key, int keylen) {
2629*572c4311Sfengbojiang int s, e; /* start-end indexes of { and } */
2630*572c4311Sfengbojiang
2631*572c4311Sfengbojiang for (s = 0; s < keylen; s++)
2632*572c4311Sfengbojiang if (key[s] == '{') break;
2633*572c4311Sfengbojiang
2634*572c4311Sfengbojiang /* No '{' ? Hash the whole key. This is the base case. */
2635*572c4311Sfengbojiang if (s == keylen) return crc16(key,keylen) & 0x3FFF;
2636*572c4311Sfengbojiang
2637*572c4311Sfengbojiang /* '{' found? Check if we have the corresponding '}'. */
2638*572c4311Sfengbojiang for (e = s+1; e < keylen; e++)
2639*572c4311Sfengbojiang if (key[e] == '}') break;
2640*572c4311Sfengbojiang
2641*572c4311Sfengbojiang /* No '}' or nothing between {} ? Hash the whole key. */
2642*572c4311Sfengbojiang if (e == keylen || e == s+1) return crc16(key,keylen) & 0x3FFF;
2643*572c4311Sfengbojiang
2644*572c4311Sfengbojiang /* If we are here there is both a { and a } on its right. Hash
2645*572c4311Sfengbojiang * what is in the middle between { and }. */
2646*572c4311Sfengbojiang return crc16(key+s+1,e-s-1) & 0x3FFF;
2647*572c4311Sfengbojiang }
2648*572c4311Sfengbojiang
2649*572c4311Sfengbojiang /* Return a string representation of the cluster node. */
clusterManagerNodeInfo(clusterManagerNode * node,int indent)2650*572c4311Sfengbojiang static sds clusterManagerNodeInfo(clusterManagerNode *node, int indent) {
2651*572c4311Sfengbojiang sds info = sdsempty();
2652*572c4311Sfengbojiang sds spaces = sdsempty();
2653*572c4311Sfengbojiang int i;
2654*572c4311Sfengbojiang for (i = 0; i < indent; i++) spaces = sdscat(spaces, " ");
2655*572c4311Sfengbojiang if (indent) info = sdscat(info, spaces);
2656*572c4311Sfengbojiang int is_master = !(node->flags & CLUSTER_MANAGER_FLAG_SLAVE);
2657*572c4311Sfengbojiang char *role = (is_master ? "M" : "S");
2658*572c4311Sfengbojiang sds slots = NULL;
2659*572c4311Sfengbojiang if (node->dirty && node->replicate != NULL)
2660*572c4311Sfengbojiang info = sdscatfmt(info, "S: %S %s:%u", node->name, node->ip, node->port);
2661*572c4311Sfengbojiang else {
2662*572c4311Sfengbojiang slots = clusterManagerNodeSlotsString(node);
2663*572c4311Sfengbojiang sds flags = clusterManagerNodeFlagString(node);
2664*572c4311Sfengbojiang info = sdscatfmt(info, "%s: %S %s:%u\n"
2665*572c4311Sfengbojiang "%s slots:%S (%u slots) "
2666*572c4311Sfengbojiang "%S",
2667*572c4311Sfengbojiang role, node->name, node->ip, node->port, spaces,
2668*572c4311Sfengbojiang slots, node->slots_count, flags);
2669*572c4311Sfengbojiang sdsfree(slots);
2670*572c4311Sfengbojiang sdsfree(flags);
2671*572c4311Sfengbojiang }
2672*572c4311Sfengbojiang if (node->replicate != NULL)
2673*572c4311Sfengbojiang info = sdscatfmt(info, "\n%s replicates %S", spaces, node->replicate);
2674*572c4311Sfengbojiang else if (node->replicas_count)
2675*572c4311Sfengbojiang info = sdscatfmt(info, "\n%s %U additional replica(s)",
2676*572c4311Sfengbojiang spaces, node->replicas_count);
2677*572c4311Sfengbojiang sdsfree(spaces);
2678*572c4311Sfengbojiang return info;
2679*572c4311Sfengbojiang }
2680*572c4311Sfengbojiang
clusterManagerShowNodes(void)2681*572c4311Sfengbojiang static void clusterManagerShowNodes(void) {
2682*572c4311Sfengbojiang listIter li;
2683*572c4311Sfengbojiang listNode *ln;
2684*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
2685*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
2686*572c4311Sfengbojiang clusterManagerNode *node = ln->value;
2687*572c4311Sfengbojiang sds info = clusterManagerNodeInfo(node, 0);
2688*572c4311Sfengbojiang printf("%s\n", (char *) info);
2689*572c4311Sfengbojiang sdsfree(info);
2690*572c4311Sfengbojiang }
2691*572c4311Sfengbojiang }
2692*572c4311Sfengbojiang
clusterManagerShowClusterInfo(void)2693*572c4311Sfengbojiang static void clusterManagerShowClusterInfo(void) {
2694*572c4311Sfengbojiang int masters = 0;
2695*572c4311Sfengbojiang int keys = 0;
2696*572c4311Sfengbojiang listIter li;
2697*572c4311Sfengbojiang listNode *ln;
2698*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
2699*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
2700*572c4311Sfengbojiang clusterManagerNode *node = ln->value;
2701*572c4311Sfengbojiang if (!(node->flags & CLUSTER_MANAGER_FLAG_SLAVE)) {
2702*572c4311Sfengbojiang if (!node->name) continue;
2703*572c4311Sfengbojiang int replicas = 0;
2704*572c4311Sfengbojiang int dbsize = -1;
2705*572c4311Sfengbojiang char name[9];
2706*572c4311Sfengbojiang memcpy(name, node->name, 8);
2707*572c4311Sfengbojiang name[8] = '\0';
2708*572c4311Sfengbojiang listIter ri;
2709*572c4311Sfengbojiang listNode *rn;
2710*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &ri);
2711*572c4311Sfengbojiang while ((rn = listNext(&ri)) != NULL) {
2712*572c4311Sfengbojiang clusterManagerNode *n = rn->value;
2713*572c4311Sfengbojiang if (n == node || !(n->flags & CLUSTER_MANAGER_FLAG_SLAVE))
2714*572c4311Sfengbojiang continue;
2715*572c4311Sfengbojiang if (n->replicate && !strcmp(n->replicate, node->name))
2716*572c4311Sfengbojiang replicas++;
2717*572c4311Sfengbojiang }
2718*572c4311Sfengbojiang redisReply *reply = CLUSTER_MANAGER_COMMAND(node, "DBSIZE");
2719*572c4311Sfengbojiang if (reply != NULL || reply->type == REDIS_REPLY_INTEGER)
2720*572c4311Sfengbojiang dbsize = reply->integer;
2721*572c4311Sfengbojiang if (dbsize < 0) {
2722*572c4311Sfengbojiang char *err = "";
2723*572c4311Sfengbojiang if (reply != NULL && reply->type == REDIS_REPLY_ERROR)
2724*572c4311Sfengbojiang err = reply->str;
2725*572c4311Sfengbojiang CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);
2726*572c4311Sfengbojiang if (reply != NULL) freeReplyObject(reply);
2727*572c4311Sfengbojiang return;
2728*572c4311Sfengbojiang };
2729*572c4311Sfengbojiang if (reply != NULL) freeReplyObject(reply);
2730*572c4311Sfengbojiang printf("%s:%d (%s...) -> %d keys | %d slots | %d slaves.\n",
2731*572c4311Sfengbojiang node->ip, node->port, name, dbsize,
2732*572c4311Sfengbojiang node->slots_count, replicas);
2733*572c4311Sfengbojiang masters++;
2734*572c4311Sfengbojiang keys += dbsize;
2735*572c4311Sfengbojiang }
2736*572c4311Sfengbojiang }
2737*572c4311Sfengbojiang clusterManagerLogOk("[OK] %d keys in %d masters.\n", keys, masters);
2738*572c4311Sfengbojiang float keys_per_slot = keys / (float) CLUSTER_MANAGER_SLOTS;
2739*572c4311Sfengbojiang printf("%.2f keys per slot on average.\n", keys_per_slot);
2740*572c4311Sfengbojiang }
2741*572c4311Sfengbojiang
2742*572c4311Sfengbojiang /* Flush dirty slots configuration of the node by calling CLUSTER ADDSLOTS */
clusterManagerAddSlots(clusterManagerNode * node,char ** err)2743*572c4311Sfengbojiang static int clusterManagerAddSlots(clusterManagerNode *node, char**err)
2744*572c4311Sfengbojiang {
2745*572c4311Sfengbojiang redisReply *reply = NULL;
2746*572c4311Sfengbojiang void *_reply = NULL;
2747*572c4311Sfengbojiang int success = 1;
2748*572c4311Sfengbojiang /* First two args are used for the command itself. */
2749*572c4311Sfengbojiang int argc = node->slots_count + 2;
2750*572c4311Sfengbojiang sds *argv = zmalloc(argc * sizeof(*argv));
2751*572c4311Sfengbojiang size_t *argvlen = zmalloc(argc * sizeof(*argvlen));
2752*572c4311Sfengbojiang argv[0] = "CLUSTER";
2753*572c4311Sfengbojiang argv[1] = "ADDSLOTS";
2754*572c4311Sfengbojiang argvlen[0] = 7;
2755*572c4311Sfengbojiang argvlen[1] = 8;
2756*572c4311Sfengbojiang *err = NULL;
2757*572c4311Sfengbojiang int i, argv_idx = 2;
2758*572c4311Sfengbojiang for (i = 0; i < CLUSTER_MANAGER_SLOTS; i++) {
2759*572c4311Sfengbojiang if (argv_idx >= argc) break;
2760*572c4311Sfengbojiang if (node->slots[i]) {
2761*572c4311Sfengbojiang argv[argv_idx] = sdsfromlonglong((long long) i);
2762*572c4311Sfengbojiang argvlen[argv_idx] = sdslen(argv[argv_idx]);
2763*572c4311Sfengbojiang argv_idx++;
2764*572c4311Sfengbojiang }
2765*572c4311Sfengbojiang }
2766*572c4311Sfengbojiang if (!argv_idx) {
2767*572c4311Sfengbojiang success = 0;
2768*572c4311Sfengbojiang goto cleanup;
2769*572c4311Sfengbojiang }
2770*572c4311Sfengbojiang redisAppendCommandArgv(node->context,argc,(const char**)argv,argvlen);
2771*572c4311Sfengbojiang if (redisGetReply(node->context, &_reply) != REDIS_OK) {
2772*572c4311Sfengbojiang success = 0;
2773*572c4311Sfengbojiang goto cleanup;
2774*572c4311Sfengbojiang }
2775*572c4311Sfengbojiang reply = (redisReply*) _reply;
2776*572c4311Sfengbojiang success = clusterManagerCheckRedisReply(node, reply, err);
2777*572c4311Sfengbojiang cleanup:
2778*572c4311Sfengbojiang zfree(argvlen);
2779*572c4311Sfengbojiang if (argv != NULL) {
2780*572c4311Sfengbojiang for (i = 2; i < argc; i++) sdsfree(argv[i]);
2781*572c4311Sfengbojiang zfree(argv);
2782*572c4311Sfengbojiang }
2783*572c4311Sfengbojiang if (reply != NULL) freeReplyObject(reply);
2784*572c4311Sfengbojiang return success;
2785*572c4311Sfengbojiang }
2786*572c4311Sfengbojiang
2787*572c4311Sfengbojiang /* Set slot status to "importing" or "migrating" */
clusterManagerSetSlot(clusterManagerNode * node1,clusterManagerNode * node2,int slot,const char * status,char ** err)2788*572c4311Sfengbojiang static int clusterManagerSetSlot(clusterManagerNode *node1,
2789*572c4311Sfengbojiang clusterManagerNode *node2,
2790*572c4311Sfengbojiang int slot, const char *status, char **err) {
2791*572c4311Sfengbojiang redisReply *reply = CLUSTER_MANAGER_COMMAND(node1, "CLUSTER "
2792*572c4311Sfengbojiang "SETSLOT %d %s %s",
2793*572c4311Sfengbojiang slot, status,
2794*572c4311Sfengbojiang (char *) node2->name);
2795*572c4311Sfengbojiang if (err != NULL) *err = NULL;
2796*572c4311Sfengbojiang if (!reply) return 0;
2797*572c4311Sfengbojiang int success = 1;
2798*572c4311Sfengbojiang if (reply->type == REDIS_REPLY_ERROR) {
2799*572c4311Sfengbojiang success = 0;
2800*572c4311Sfengbojiang if (err != NULL) {
2801*572c4311Sfengbojiang *err = zmalloc((reply->len + 1) * sizeof(char));
2802*572c4311Sfengbojiang strcpy(*err, reply->str);
2803*572c4311Sfengbojiang } else CLUSTER_MANAGER_PRINT_REPLY_ERROR(node1, reply->str);
2804*572c4311Sfengbojiang goto cleanup;
2805*572c4311Sfengbojiang }
2806*572c4311Sfengbojiang cleanup:
2807*572c4311Sfengbojiang freeReplyObject(reply);
2808*572c4311Sfengbojiang return success;
2809*572c4311Sfengbojiang }
2810*572c4311Sfengbojiang
clusterManagerClearSlotStatus(clusterManagerNode * node,int slot)2811*572c4311Sfengbojiang static int clusterManagerClearSlotStatus(clusterManagerNode *node, int slot) {
2812*572c4311Sfengbojiang redisReply *reply = CLUSTER_MANAGER_COMMAND(node,
2813*572c4311Sfengbojiang "CLUSTER SETSLOT %d %s", slot, "STABLE");
2814*572c4311Sfengbojiang int success = clusterManagerCheckRedisReply(node, reply, NULL);
2815*572c4311Sfengbojiang if (reply) freeReplyObject(reply);
2816*572c4311Sfengbojiang return success;
2817*572c4311Sfengbojiang }
2818*572c4311Sfengbojiang
clusterManagerDelSlot(clusterManagerNode * node,int slot,int ignore_unassigned_err)2819*572c4311Sfengbojiang static int clusterManagerDelSlot(clusterManagerNode *node, int slot,
2820*572c4311Sfengbojiang int ignore_unassigned_err)
2821*572c4311Sfengbojiang {
2822*572c4311Sfengbojiang redisReply *reply = CLUSTER_MANAGER_COMMAND(node,
2823*572c4311Sfengbojiang "CLUSTER DELSLOTS %d", slot);
2824*572c4311Sfengbojiang char *err = NULL;
2825*572c4311Sfengbojiang int success = clusterManagerCheckRedisReply(node, reply, &err);
2826*572c4311Sfengbojiang if (!success && reply && reply->type == REDIS_REPLY_ERROR &&
2827*572c4311Sfengbojiang ignore_unassigned_err &&
2828*572c4311Sfengbojiang strstr(reply->str, "already unassigned") != NULL) success = 1;
2829*572c4311Sfengbojiang if (!success && err != NULL) {
2830*572c4311Sfengbojiang CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);
2831*572c4311Sfengbojiang zfree(err);
2832*572c4311Sfengbojiang }
2833*572c4311Sfengbojiang if (reply) freeReplyObject(reply);
2834*572c4311Sfengbojiang return success;
2835*572c4311Sfengbojiang }
2836*572c4311Sfengbojiang
clusterManagerAddSlot(clusterManagerNode * node,int slot)2837*572c4311Sfengbojiang static int clusterManagerAddSlot(clusterManagerNode *node, int slot) {
2838*572c4311Sfengbojiang redisReply *reply = CLUSTER_MANAGER_COMMAND(node,
2839*572c4311Sfengbojiang "CLUSTER ADDSLOTS %d", slot);
2840*572c4311Sfengbojiang int success = clusterManagerCheckRedisReply(node, reply, NULL);
2841*572c4311Sfengbojiang if (reply) freeReplyObject(reply);
2842*572c4311Sfengbojiang return success;
2843*572c4311Sfengbojiang }
2844*572c4311Sfengbojiang
clusterManagerCountKeysInSlot(clusterManagerNode * node,int slot)2845*572c4311Sfengbojiang static signed int clusterManagerCountKeysInSlot(clusterManagerNode *node,
2846*572c4311Sfengbojiang int slot)
2847*572c4311Sfengbojiang {
2848*572c4311Sfengbojiang redisReply *reply = CLUSTER_MANAGER_COMMAND(node,
2849*572c4311Sfengbojiang "CLUSTER COUNTKEYSINSLOT %d", slot);
2850*572c4311Sfengbojiang int count = -1;
2851*572c4311Sfengbojiang int success = clusterManagerCheckRedisReply(node, reply, NULL);
2852*572c4311Sfengbojiang if (success && reply->type == REDIS_REPLY_INTEGER) count = reply->integer;
2853*572c4311Sfengbojiang if (reply) freeReplyObject(reply);
2854*572c4311Sfengbojiang return count;
2855*572c4311Sfengbojiang }
2856*572c4311Sfengbojiang
clusterManagerBumpEpoch(clusterManagerNode * node)2857*572c4311Sfengbojiang static int clusterManagerBumpEpoch(clusterManagerNode *node) {
2858*572c4311Sfengbojiang redisReply *reply = CLUSTER_MANAGER_COMMAND(node, "CLUSTER BUMPEPOCH");
2859*572c4311Sfengbojiang int success = clusterManagerCheckRedisReply(node, reply, NULL);
2860*572c4311Sfengbojiang if (reply) freeReplyObject(reply);
2861*572c4311Sfengbojiang return success;
2862*572c4311Sfengbojiang }
2863*572c4311Sfengbojiang
clusterManagerIgnoreUnassignedErr(redisReply * reply,int bulk_idx)2864*572c4311Sfengbojiang static int clusterManagerIgnoreUnassignedErr(redisReply *reply, int bulk_idx) {
2865*572c4311Sfengbojiang if (bulk_idx == 0 && reply) {
2866*572c4311Sfengbojiang if (reply->type == REDIS_REPLY_ERROR)
2867*572c4311Sfengbojiang return strstr(reply->str, "already unassigned") != NULL;
2868*572c4311Sfengbojiang }
2869*572c4311Sfengbojiang return 0;
2870*572c4311Sfengbojiang }
2871*572c4311Sfengbojiang
clusterManagerSetSlotOwner(clusterManagerNode * owner,int slot,int do_clear)2872*572c4311Sfengbojiang static int clusterManagerSetSlotOwner(clusterManagerNode *owner,
2873*572c4311Sfengbojiang int slot,
2874*572c4311Sfengbojiang int do_clear)
2875*572c4311Sfengbojiang {
2876*572c4311Sfengbojiang int success = clusterManagerStartTransaction(owner);
2877*572c4311Sfengbojiang if (!success) return 0;
2878*572c4311Sfengbojiang /* Ensure the slot is not already assigned. */
2879*572c4311Sfengbojiang clusterManagerDelSlot(owner, slot, 1);
2880*572c4311Sfengbojiang /* Add the slot and bump epoch. */
2881*572c4311Sfengbojiang clusterManagerAddSlot(owner, slot);
2882*572c4311Sfengbojiang if (do_clear) clusterManagerClearSlotStatus(owner, slot);
2883*572c4311Sfengbojiang clusterManagerBumpEpoch(owner);
2884*572c4311Sfengbojiang success = clusterManagerExecTransaction(owner,
2885*572c4311Sfengbojiang clusterManagerIgnoreUnassignedErr);
2886*572c4311Sfengbojiang return success;
2887*572c4311Sfengbojiang }
2888*572c4311Sfengbojiang
2889*572c4311Sfengbojiang /* Migrate keys taken from reply->elements. It returns the reply from the
2890*572c4311Sfengbojiang * MIGRATE command, or NULL if something goes wrong. If the argument 'dots'
2891*572c4311Sfengbojiang * is not NULL, a dot will be printed for every migrated key. */
clusterManagerMigrateKeysInReply(clusterManagerNode * source,clusterManagerNode * target,redisReply * reply,int replace,int timeout,char * dots)2892*572c4311Sfengbojiang static redisReply *clusterManagerMigrateKeysInReply(clusterManagerNode *source,
2893*572c4311Sfengbojiang clusterManagerNode *target,
2894*572c4311Sfengbojiang redisReply *reply,
2895*572c4311Sfengbojiang int replace, int timeout,
2896*572c4311Sfengbojiang char *dots)
2897*572c4311Sfengbojiang {
2898*572c4311Sfengbojiang redisReply *migrate_reply = NULL;
2899*572c4311Sfengbojiang char **argv = NULL;
2900*572c4311Sfengbojiang size_t *argv_len = NULL;
2901*572c4311Sfengbojiang int c = (replace ? 8 : 7);
2902*572c4311Sfengbojiang if (config.auth) c += 2;
2903*572c4311Sfengbojiang size_t argc = c + reply->elements;
2904*572c4311Sfengbojiang size_t i, offset = 6; // Keys Offset
2905*572c4311Sfengbojiang argv = zcalloc(argc * sizeof(char *));
2906*572c4311Sfengbojiang argv_len = zcalloc(argc * sizeof(size_t));
2907*572c4311Sfengbojiang char portstr[255];
2908*572c4311Sfengbojiang char timeoutstr[255];
2909*572c4311Sfengbojiang snprintf(portstr, 10, "%d", target->port);
2910*572c4311Sfengbojiang snprintf(timeoutstr, 10, "%d", timeout);
2911*572c4311Sfengbojiang argv[0] = "MIGRATE";
2912*572c4311Sfengbojiang argv_len[0] = 7;
2913*572c4311Sfengbojiang argv[1] = target->ip;
2914*572c4311Sfengbojiang argv_len[1] = strlen(target->ip);
2915*572c4311Sfengbojiang argv[2] = portstr;
2916*572c4311Sfengbojiang argv_len[2] = strlen(portstr);
2917*572c4311Sfengbojiang argv[3] = "";
2918*572c4311Sfengbojiang argv_len[3] = 0;
2919*572c4311Sfengbojiang argv[4] = "0";
2920*572c4311Sfengbojiang argv_len[4] = 1;
2921*572c4311Sfengbojiang argv[5] = timeoutstr;
2922*572c4311Sfengbojiang argv_len[5] = strlen(timeoutstr);
2923*572c4311Sfengbojiang if (replace) {
2924*572c4311Sfengbojiang argv[offset] = "REPLACE";
2925*572c4311Sfengbojiang argv_len[offset] = 7;
2926*572c4311Sfengbojiang offset++;
2927*572c4311Sfengbojiang }
2928*572c4311Sfengbojiang if (config.auth) {
2929*572c4311Sfengbojiang argv[offset] = "AUTH";
2930*572c4311Sfengbojiang argv_len[offset] = 4;
2931*572c4311Sfengbojiang offset++;
2932*572c4311Sfengbojiang argv[offset] = config.auth;
2933*572c4311Sfengbojiang argv_len[offset] = strlen(config.auth);
2934*572c4311Sfengbojiang offset++;
2935*572c4311Sfengbojiang }
2936*572c4311Sfengbojiang argv[offset] = "KEYS";
2937*572c4311Sfengbojiang argv_len[offset] = 4;
2938*572c4311Sfengbojiang offset++;
2939*572c4311Sfengbojiang for (i = 0; i < reply->elements; i++) {
2940*572c4311Sfengbojiang redisReply *entry = reply->element[i];
2941*572c4311Sfengbojiang size_t idx = i + offset;
2942*572c4311Sfengbojiang assert(entry->type == REDIS_REPLY_STRING);
2943*572c4311Sfengbojiang argv[idx] = (char *) sdsnew(entry->str);
2944*572c4311Sfengbojiang argv_len[idx] = entry->len;
2945*572c4311Sfengbojiang if (dots) dots[i] = '.';
2946*572c4311Sfengbojiang }
2947*572c4311Sfengbojiang if (dots) dots[reply->elements] = '\0';
2948*572c4311Sfengbojiang void *_reply = NULL;
2949*572c4311Sfengbojiang redisAppendCommandArgv(source->context,argc,
2950*572c4311Sfengbojiang (const char**)argv,argv_len);
2951*572c4311Sfengbojiang int success = (redisGetReply(source->context, &_reply) == REDIS_OK);
2952*572c4311Sfengbojiang for (i = 0; i < reply->elements; i++) sdsfree(argv[i + offset]);
2953*572c4311Sfengbojiang if (!success) goto cleanup;
2954*572c4311Sfengbojiang migrate_reply = (redisReply *) _reply;
2955*572c4311Sfengbojiang cleanup:
2956*572c4311Sfengbojiang zfree(argv);
2957*572c4311Sfengbojiang zfree(argv_len);
2958*572c4311Sfengbojiang return migrate_reply;
2959*572c4311Sfengbojiang }
2960*572c4311Sfengbojiang
2961*572c4311Sfengbojiang /* Migrate all keys in the given slot from source to target.*/
clusterManagerMigrateKeysInSlot(clusterManagerNode * source,clusterManagerNode * target,int slot,int timeout,int pipeline,int verbose,char ** err)2962*572c4311Sfengbojiang static int clusterManagerMigrateKeysInSlot(clusterManagerNode *source,
2963*572c4311Sfengbojiang clusterManagerNode *target,
2964*572c4311Sfengbojiang int slot, int timeout,
2965*572c4311Sfengbojiang int pipeline, int verbose,
2966*572c4311Sfengbojiang char **err)
2967*572c4311Sfengbojiang {
2968*572c4311Sfengbojiang int success = 1;
2969*572c4311Sfengbojiang int replace_existing_keys = (config.cluster_manager_command.flags &
2970*572c4311Sfengbojiang (CLUSTER_MANAGER_CMD_FLAG_FIX | CLUSTER_MANAGER_CMD_FLAG_REPLACE));
2971*572c4311Sfengbojiang while (1) {
2972*572c4311Sfengbojiang char *dots = NULL;
2973*572c4311Sfengbojiang redisReply *reply = NULL, *migrate_reply = NULL;
2974*572c4311Sfengbojiang reply = CLUSTER_MANAGER_COMMAND(source, "CLUSTER "
2975*572c4311Sfengbojiang "GETKEYSINSLOT %d %d", slot,
2976*572c4311Sfengbojiang pipeline);
2977*572c4311Sfengbojiang success = (reply != NULL);
2978*572c4311Sfengbojiang if (!success) return 0;
2979*572c4311Sfengbojiang if (reply->type == REDIS_REPLY_ERROR) {
2980*572c4311Sfengbojiang success = 0;
2981*572c4311Sfengbojiang if (err != NULL) {
2982*572c4311Sfengbojiang *err = zmalloc((reply->len + 1) * sizeof(char));
2983*572c4311Sfengbojiang strcpy(*err, reply->str);
2984*572c4311Sfengbojiang CLUSTER_MANAGER_PRINT_REPLY_ERROR(source, *err);
2985*572c4311Sfengbojiang }
2986*572c4311Sfengbojiang goto next;
2987*572c4311Sfengbojiang }
2988*572c4311Sfengbojiang assert(reply->type == REDIS_REPLY_ARRAY);
2989*572c4311Sfengbojiang size_t count = reply->elements;
2990*572c4311Sfengbojiang if (count == 0) {
2991*572c4311Sfengbojiang freeReplyObject(reply);
2992*572c4311Sfengbojiang break;
2993*572c4311Sfengbojiang }
2994*572c4311Sfengbojiang if (verbose) dots = zmalloc((count+1) * sizeof(char));
2995*572c4311Sfengbojiang /* Calling MIGRATE command. */
2996*572c4311Sfengbojiang migrate_reply = clusterManagerMigrateKeysInReply(source, target,
2997*572c4311Sfengbojiang reply, 0, timeout,
2998*572c4311Sfengbojiang dots);
2999*572c4311Sfengbojiang if (migrate_reply == NULL) goto next;
3000*572c4311Sfengbojiang if (migrate_reply->type == REDIS_REPLY_ERROR) {
3001*572c4311Sfengbojiang int is_busy = strstr(migrate_reply->str, "BUSYKEY") != NULL;
3002*572c4311Sfengbojiang int not_served = strstr(migrate_reply->str, "slot not served") != NULL;
3003*572c4311Sfengbojiang if (replace_existing_keys && (is_busy || not_served)) {
3004*572c4311Sfengbojiang /* If the key already exists, try to migrate keys
3005*572c4311Sfengbojiang * adding REPLACE option.
3006*572c4311Sfengbojiang * If the key's slot is not served, try to assign slot
3007*572c4311Sfengbojiang * to the target node. */
3008*572c4311Sfengbojiang if (not_served)
3009*572c4311Sfengbojiang clusterManagerSetSlot(source, target, slot, "node", NULL);
3010*572c4311Sfengbojiang clusterManagerLogWarn("*** Target key exists. "
3011*572c4311Sfengbojiang "Replacing it for FIX.\n");
3012*572c4311Sfengbojiang freeReplyObject(migrate_reply);
3013*572c4311Sfengbojiang migrate_reply = clusterManagerMigrateKeysInReply(source,
3014*572c4311Sfengbojiang target,
3015*572c4311Sfengbojiang reply,
3016*572c4311Sfengbojiang is_busy,
3017*572c4311Sfengbojiang timeout,
3018*572c4311Sfengbojiang NULL);
3019*572c4311Sfengbojiang success = (migrate_reply != NULL &&
3020*572c4311Sfengbojiang migrate_reply->type != REDIS_REPLY_ERROR);
3021*572c4311Sfengbojiang } else success = 0;
3022*572c4311Sfengbojiang if (!success) {
3023*572c4311Sfengbojiang if (migrate_reply != NULL) {
3024*572c4311Sfengbojiang if (err) {
3025*572c4311Sfengbojiang *err = zmalloc((migrate_reply->len + 1) * sizeof(char));
3026*572c4311Sfengbojiang strcpy(*err, migrate_reply->str);
3027*572c4311Sfengbojiang }
3028*572c4311Sfengbojiang printf("\n");
3029*572c4311Sfengbojiang CLUSTER_MANAGER_PRINT_REPLY_ERROR(source,
3030*572c4311Sfengbojiang migrate_reply->str);
3031*572c4311Sfengbojiang }
3032*572c4311Sfengbojiang goto next;
3033*572c4311Sfengbojiang }
3034*572c4311Sfengbojiang }
3035*572c4311Sfengbojiang if (verbose) {
3036*572c4311Sfengbojiang printf("%s", dots);
3037*572c4311Sfengbojiang fflush(stdout);
3038*572c4311Sfengbojiang }
3039*572c4311Sfengbojiang next:
3040*572c4311Sfengbojiang if (reply != NULL) freeReplyObject(reply);
3041*572c4311Sfengbojiang if (migrate_reply != NULL) freeReplyObject(migrate_reply);
3042*572c4311Sfengbojiang if (dots) zfree(dots);
3043*572c4311Sfengbojiang if (!success) break;
3044*572c4311Sfengbojiang }
3045*572c4311Sfengbojiang return success;
3046*572c4311Sfengbojiang }
3047*572c4311Sfengbojiang
3048*572c4311Sfengbojiang /* Move slots between source and target nodes using MIGRATE.
3049*572c4311Sfengbojiang *
3050*572c4311Sfengbojiang * Options:
3051*572c4311Sfengbojiang * CLUSTER_MANAGER_OPT_VERBOSE -- Print a dot for every moved key.
3052*572c4311Sfengbojiang * CLUSTER_MANAGER_OPT_COLD -- Move keys without opening slots /
3053*572c4311Sfengbojiang * reconfiguring the nodes.
3054*572c4311Sfengbojiang * CLUSTER_MANAGER_OPT_UPDATE -- Update node->slots for source/target nodes.
3055*572c4311Sfengbojiang * CLUSTER_MANAGER_OPT_QUIET -- Don't print info messages.
3056*572c4311Sfengbojiang */
clusterManagerMoveSlot(clusterManagerNode * source,clusterManagerNode * target,int slot,int opts,char ** err)3057*572c4311Sfengbojiang static int clusterManagerMoveSlot(clusterManagerNode *source,
3058*572c4311Sfengbojiang clusterManagerNode *target,
3059*572c4311Sfengbojiang int slot, int opts, char**err)
3060*572c4311Sfengbojiang {
3061*572c4311Sfengbojiang if (!(opts & CLUSTER_MANAGER_OPT_QUIET)) {
3062*572c4311Sfengbojiang printf("Moving slot %d from %s:%d to %s:%d: ", slot, source->ip,
3063*572c4311Sfengbojiang source->port, target->ip, target->port);
3064*572c4311Sfengbojiang fflush(stdout);
3065*572c4311Sfengbojiang }
3066*572c4311Sfengbojiang if (err != NULL) *err = NULL;
3067*572c4311Sfengbojiang int pipeline = config.cluster_manager_command.pipeline,
3068*572c4311Sfengbojiang timeout = config.cluster_manager_command.timeout,
3069*572c4311Sfengbojiang print_dots = (opts & CLUSTER_MANAGER_OPT_VERBOSE),
3070*572c4311Sfengbojiang option_cold = (opts & CLUSTER_MANAGER_OPT_COLD),
3071*572c4311Sfengbojiang success = 1;
3072*572c4311Sfengbojiang if (!option_cold) {
3073*572c4311Sfengbojiang success = clusterManagerSetSlot(target, source, slot,
3074*572c4311Sfengbojiang "importing", err);
3075*572c4311Sfengbojiang if (!success) return 0;
3076*572c4311Sfengbojiang success = clusterManagerSetSlot(source, target, slot,
3077*572c4311Sfengbojiang "migrating", err);
3078*572c4311Sfengbojiang if (!success) return 0;
3079*572c4311Sfengbojiang }
3080*572c4311Sfengbojiang success = clusterManagerMigrateKeysInSlot(source, target, slot, timeout,
3081*572c4311Sfengbojiang pipeline, print_dots, err);
3082*572c4311Sfengbojiang if (!(opts & CLUSTER_MANAGER_OPT_QUIET)) printf("\n");
3083*572c4311Sfengbojiang if (!success) return 0;
3084*572c4311Sfengbojiang /* Set the new node as the owner of the slot in all the known nodes. */
3085*572c4311Sfengbojiang if (!option_cold) {
3086*572c4311Sfengbojiang listIter li;
3087*572c4311Sfengbojiang listNode *ln;
3088*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
3089*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
3090*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
3091*572c4311Sfengbojiang if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;
3092*572c4311Sfengbojiang redisReply *r = CLUSTER_MANAGER_COMMAND(n, "CLUSTER "
3093*572c4311Sfengbojiang "SETSLOT %d %s %s",
3094*572c4311Sfengbojiang slot, "node",
3095*572c4311Sfengbojiang target->name);
3096*572c4311Sfengbojiang success = (r != NULL);
3097*572c4311Sfengbojiang if (!success) return 0;
3098*572c4311Sfengbojiang if (r->type == REDIS_REPLY_ERROR) {
3099*572c4311Sfengbojiang success = 0;
3100*572c4311Sfengbojiang if (err != NULL) {
3101*572c4311Sfengbojiang *err = zmalloc((r->len + 1) * sizeof(char));
3102*572c4311Sfengbojiang strcpy(*err, r->str);
3103*572c4311Sfengbojiang CLUSTER_MANAGER_PRINT_REPLY_ERROR(n, *err);
3104*572c4311Sfengbojiang }
3105*572c4311Sfengbojiang }
3106*572c4311Sfengbojiang freeReplyObject(r);
3107*572c4311Sfengbojiang if (!success) return 0;
3108*572c4311Sfengbojiang }
3109*572c4311Sfengbojiang }
3110*572c4311Sfengbojiang /* Update the node logical config */
3111*572c4311Sfengbojiang if (opts & CLUSTER_MANAGER_OPT_UPDATE) {
3112*572c4311Sfengbojiang source->slots[slot] = 0;
3113*572c4311Sfengbojiang target->slots[slot] = 1;
3114*572c4311Sfengbojiang }
3115*572c4311Sfengbojiang return 1;
3116*572c4311Sfengbojiang }
3117*572c4311Sfengbojiang
3118*572c4311Sfengbojiang /* Flush the dirty node configuration by calling replicate for slaves or
3119*572c4311Sfengbojiang * adding the slots defined in the masters. */
clusterManagerFlushNodeConfig(clusterManagerNode * node,char ** err)3120*572c4311Sfengbojiang static int clusterManagerFlushNodeConfig(clusterManagerNode *node, char **err) {
3121*572c4311Sfengbojiang if (!node->dirty) return 0;
3122*572c4311Sfengbojiang redisReply *reply = NULL;
3123*572c4311Sfengbojiang int is_err = 0, success = 1;
3124*572c4311Sfengbojiang if (err != NULL) *err = NULL;
3125*572c4311Sfengbojiang if (node->replicate != NULL) {
3126*572c4311Sfengbojiang reply = CLUSTER_MANAGER_COMMAND(node, "CLUSTER REPLICATE %s",
3127*572c4311Sfengbojiang node->replicate);
3128*572c4311Sfengbojiang if (reply == NULL || (is_err = (reply->type == REDIS_REPLY_ERROR))) {
3129*572c4311Sfengbojiang if (is_err && err != NULL) {
3130*572c4311Sfengbojiang *err = zmalloc((reply->len + 1) * sizeof(char));
3131*572c4311Sfengbojiang strcpy(*err, reply->str);
3132*572c4311Sfengbojiang }
3133*572c4311Sfengbojiang success = 0;
3134*572c4311Sfengbojiang /* If the cluster did not already joined it is possible that
3135*572c4311Sfengbojiang * the slave does not know the master node yet. So on errors
3136*572c4311Sfengbojiang * we return ASAP leaving the dirty flag set, to flush the
3137*572c4311Sfengbojiang * config later. */
3138*572c4311Sfengbojiang goto cleanup;
3139*572c4311Sfengbojiang }
3140*572c4311Sfengbojiang } else {
3141*572c4311Sfengbojiang int added = clusterManagerAddSlots(node, err);
3142*572c4311Sfengbojiang if (!added || *err != NULL) success = 0;
3143*572c4311Sfengbojiang }
3144*572c4311Sfengbojiang node->dirty = 0;
3145*572c4311Sfengbojiang cleanup:
3146*572c4311Sfengbojiang if (reply != NULL) freeReplyObject(reply);
3147*572c4311Sfengbojiang return success;
3148*572c4311Sfengbojiang }
3149*572c4311Sfengbojiang
3150*572c4311Sfengbojiang /* Wait until the cluster configuration is consistent. */
clusterManagerWaitForClusterJoin(void)3151*572c4311Sfengbojiang static void clusterManagerWaitForClusterJoin(void) {
3152*572c4311Sfengbojiang printf("Waiting for the cluster to join\n");
3153*572c4311Sfengbojiang while(!clusterManagerIsConfigConsistent()) {
3154*572c4311Sfengbojiang printf(".");
3155*572c4311Sfengbojiang fflush(stdout);
3156*572c4311Sfengbojiang sleep(1);
3157*572c4311Sfengbojiang }
3158*572c4311Sfengbojiang printf("\n");
3159*572c4311Sfengbojiang }
3160*572c4311Sfengbojiang
3161*572c4311Sfengbojiang /* Load node's cluster configuration by calling "CLUSTER NODES" command.
3162*572c4311Sfengbojiang * Node's configuration (name, replicate, slots, ...) is then updated.
3163*572c4311Sfengbojiang * If CLUSTER_MANAGER_OPT_GETFRIENDS flag is set into 'opts' argument,
3164*572c4311Sfengbojiang * and node already knows other nodes, the node's friends list is populated
3165*572c4311Sfengbojiang * with the other nodes info. */
clusterManagerNodeLoadInfo(clusterManagerNode * node,int opts,char ** err)3166*572c4311Sfengbojiang static int clusterManagerNodeLoadInfo(clusterManagerNode *node, int opts,
3167*572c4311Sfengbojiang char **err)
3168*572c4311Sfengbojiang {
3169*572c4311Sfengbojiang redisReply *reply = CLUSTER_MANAGER_COMMAND(node, "CLUSTER NODES");
3170*572c4311Sfengbojiang int success = 1;
3171*572c4311Sfengbojiang *err = NULL;
3172*572c4311Sfengbojiang if (!clusterManagerCheckRedisReply(node, reply, err)) {
3173*572c4311Sfengbojiang success = 0;
3174*572c4311Sfengbojiang goto cleanup;
3175*572c4311Sfengbojiang }
3176*572c4311Sfengbojiang int getfriends = (opts & CLUSTER_MANAGER_OPT_GETFRIENDS);
3177*572c4311Sfengbojiang char *lines = reply->str, *p, *line;
3178*572c4311Sfengbojiang while ((p = strstr(lines, "\n")) != NULL) {
3179*572c4311Sfengbojiang *p = '\0';
3180*572c4311Sfengbojiang line = lines;
3181*572c4311Sfengbojiang lines = p + 1;
3182*572c4311Sfengbojiang char *name = NULL, *addr = NULL, *flags = NULL, *master_id = NULL,
3183*572c4311Sfengbojiang *ping_sent = NULL, *ping_recv = NULL, *config_epoch = NULL,
3184*572c4311Sfengbojiang *link_status = NULL;
3185*572c4311Sfengbojiang UNUSED(link_status);
3186*572c4311Sfengbojiang int i = 0;
3187*572c4311Sfengbojiang while ((p = strchr(line, ' ')) != NULL) {
3188*572c4311Sfengbojiang *p = '\0';
3189*572c4311Sfengbojiang char *token = line;
3190*572c4311Sfengbojiang line = p + 1;
3191*572c4311Sfengbojiang switch(i++){
3192*572c4311Sfengbojiang case 0: name = token; break;
3193*572c4311Sfengbojiang case 1: addr = token; break;
3194*572c4311Sfengbojiang case 2: flags = token; break;
3195*572c4311Sfengbojiang case 3: master_id = token; break;
3196*572c4311Sfengbojiang case 4: ping_sent = token; break;
3197*572c4311Sfengbojiang case 5: ping_recv = token; break;
3198*572c4311Sfengbojiang case 6: config_epoch = token; break;
3199*572c4311Sfengbojiang case 7: link_status = token; break;
3200*572c4311Sfengbojiang }
3201*572c4311Sfengbojiang if (i == 8) break; // Slots
3202*572c4311Sfengbojiang }
3203*572c4311Sfengbojiang if (!flags) {
3204*572c4311Sfengbojiang success = 0;
3205*572c4311Sfengbojiang goto cleanup;
3206*572c4311Sfengbojiang }
3207*572c4311Sfengbojiang int myself = (strstr(flags, "myself") != NULL);
3208*572c4311Sfengbojiang clusterManagerNode *currentNode = NULL;
3209*572c4311Sfengbojiang if (myself) {
3210*572c4311Sfengbojiang node->flags |= CLUSTER_MANAGER_FLAG_MYSELF;
3211*572c4311Sfengbojiang currentNode = node;
3212*572c4311Sfengbojiang clusterManagerNodeResetSlots(node);
3213*572c4311Sfengbojiang if (i == 8) {
3214*572c4311Sfengbojiang int remaining = strlen(line);
3215*572c4311Sfengbojiang while (remaining > 0) {
3216*572c4311Sfengbojiang p = strchr(line, ' ');
3217*572c4311Sfengbojiang if (p == NULL) p = line + remaining;
3218*572c4311Sfengbojiang remaining -= (p - line);
3219*572c4311Sfengbojiang
3220*572c4311Sfengbojiang char *slotsdef = line;
3221*572c4311Sfengbojiang *p = '\0';
3222*572c4311Sfengbojiang if (remaining) {
3223*572c4311Sfengbojiang line = p + 1;
3224*572c4311Sfengbojiang remaining--;
3225*572c4311Sfengbojiang } else line = p;
3226*572c4311Sfengbojiang char *dash = NULL;
3227*572c4311Sfengbojiang if (slotsdef[0] == '[') {
3228*572c4311Sfengbojiang slotsdef++;
3229*572c4311Sfengbojiang if ((p = strstr(slotsdef, "->-"))) { // Migrating
3230*572c4311Sfengbojiang *p = '\0';
3231*572c4311Sfengbojiang p += 3;
3232*572c4311Sfengbojiang char *closing_bracket = strchr(p, ']');
3233*572c4311Sfengbojiang if (closing_bracket) *closing_bracket = '\0';
3234*572c4311Sfengbojiang sds slot = sdsnew(slotsdef);
3235*572c4311Sfengbojiang sds dst = sdsnew(p);
3236*572c4311Sfengbojiang node->migrating_count += 2;
3237*572c4311Sfengbojiang node->migrating = zrealloc(node->migrating,
3238*572c4311Sfengbojiang (node->migrating_count * sizeof(sds)));
3239*572c4311Sfengbojiang node->migrating[node->migrating_count - 2] =
3240*572c4311Sfengbojiang slot;
3241*572c4311Sfengbojiang node->migrating[node->migrating_count - 1] =
3242*572c4311Sfengbojiang dst;
3243*572c4311Sfengbojiang } else if ((p = strstr(slotsdef, "-<-"))) {//Importing
3244*572c4311Sfengbojiang *p = '\0';
3245*572c4311Sfengbojiang p += 3;
3246*572c4311Sfengbojiang char *closing_bracket = strchr(p, ']');
3247*572c4311Sfengbojiang if (closing_bracket) *closing_bracket = '\0';
3248*572c4311Sfengbojiang sds slot = sdsnew(slotsdef);
3249*572c4311Sfengbojiang sds src = sdsnew(p);
3250*572c4311Sfengbojiang node->importing_count += 2;
3251*572c4311Sfengbojiang node->importing = zrealloc(node->importing,
3252*572c4311Sfengbojiang (node->importing_count * sizeof(sds)));
3253*572c4311Sfengbojiang node->importing[node->importing_count - 2] =
3254*572c4311Sfengbojiang slot;
3255*572c4311Sfengbojiang node->importing[node->importing_count - 1] =
3256*572c4311Sfengbojiang src;
3257*572c4311Sfengbojiang }
3258*572c4311Sfengbojiang } else if ((dash = strchr(slotsdef, '-')) != NULL) {
3259*572c4311Sfengbojiang p = dash;
3260*572c4311Sfengbojiang int start, stop;
3261*572c4311Sfengbojiang *p = '\0';
3262*572c4311Sfengbojiang start = atoi(slotsdef);
3263*572c4311Sfengbojiang stop = atoi(p + 1);
3264*572c4311Sfengbojiang node->slots_count += (stop - (start - 1));
3265*572c4311Sfengbojiang while (start <= stop) node->slots[start++] = 1;
3266*572c4311Sfengbojiang } else if (p > slotsdef) {
3267*572c4311Sfengbojiang node->slots[atoi(slotsdef)] = 1;
3268*572c4311Sfengbojiang node->slots_count++;
3269*572c4311Sfengbojiang }
3270*572c4311Sfengbojiang }
3271*572c4311Sfengbojiang }
3272*572c4311Sfengbojiang node->dirty = 0;
3273*572c4311Sfengbojiang } else if (!getfriends) {
3274*572c4311Sfengbojiang if (!(node->flags & CLUSTER_MANAGER_FLAG_MYSELF)) continue;
3275*572c4311Sfengbojiang else break;
3276*572c4311Sfengbojiang } else {
3277*572c4311Sfengbojiang if (addr == NULL) {
3278*572c4311Sfengbojiang fprintf(stderr, "Error: invalid CLUSTER NODES reply\n");
3279*572c4311Sfengbojiang success = 0;
3280*572c4311Sfengbojiang goto cleanup;
3281*572c4311Sfengbojiang }
3282*572c4311Sfengbojiang char *c = strrchr(addr, '@');
3283*572c4311Sfengbojiang if (c != NULL) *c = '\0';
3284*572c4311Sfengbojiang c = strrchr(addr, ':');
3285*572c4311Sfengbojiang if (c == NULL) {
3286*572c4311Sfengbojiang fprintf(stderr, "Error: invalid CLUSTER NODES reply\n");
3287*572c4311Sfengbojiang success = 0;
3288*572c4311Sfengbojiang goto cleanup;
3289*572c4311Sfengbojiang }
3290*572c4311Sfengbojiang *c = '\0';
3291*572c4311Sfengbojiang int port = atoi(++c);
3292*572c4311Sfengbojiang currentNode = clusterManagerNewNode(sdsnew(addr), port);
3293*572c4311Sfengbojiang currentNode->flags |= CLUSTER_MANAGER_FLAG_FRIEND;
3294*572c4311Sfengbojiang if (node->friends == NULL) node->friends = listCreate();
3295*572c4311Sfengbojiang listAddNodeTail(node->friends, currentNode);
3296*572c4311Sfengbojiang }
3297*572c4311Sfengbojiang if (name != NULL) {
3298*572c4311Sfengbojiang if (currentNode->name) sdsfree(currentNode->name);
3299*572c4311Sfengbojiang currentNode->name = sdsnew(name);
3300*572c4311Sfengbojiang }
3301*572c4311Sfengbojiang if (currentNode->flags_str != NULL)
3302*572c4311Sfengbojiang freeClusterManagerNodeFlags(currentNode->flags_str);
3303*572c4311Sfengbojiang currentNode->flags_str = listCreate();
3304*572c4311Sfengbojiang int flag_len;
3305*572c4311Sfengbojiang while ((flag_len = strlen(flags)) > 0) {
3306*572c4311Sfengbojiang sds flag = NULL;
3307*572c4311Sfengbojiang char *fp = strchr(flags, ',');
3308*572c4311Sfengbojiang if (fp) {
3309*572c4311Sfengbojiang *fp = '\0';
3310*572c4311Sfengbojiang flag = sdsnew(flags);
3311*572c4311Sfengbojiang flags = fp + 1;
3312*572c4311Sfengbojiang } else {
3313*572c4311Sfengbojiang flag = sdsnew(flags);
3314*572c4311Sfengbojiang flags += flag_len;
3315*572c4311Sfengbojiang }
3316*572c4311Sfengbojiang if (strcmp(flag, "noaddr") == 0)
3317*572c4311Sfengbojiang currentNode->flags |= CLUSTER_MANAGER_FLAG_NOADDR;
3318*572c4311Sfengbojiang else if (strcmp(flag, "disconnected") == 0)
3319*572c4311Sfengbojiang currentNode->flags |= CLUSTER_MANAGER_FLAG_DISCONNECT;
3320*572c4311Sfengbojiang else if (strcmp(flag, "fail") == 0)
3321*572c4311Sfengbojiang currentNode->flags |= CLUSTER_MANAGER_FLAG_FAIL;
3322*572c4311Sfengbojiang else if (strcmp(flag, "slave") == 0) {
3323*572c4311Sfengbojiang currentNode->flags |= CLUSTER_MANAGER_FLAG_SLAVE;
3324*572c4311Sfengbojiang if (master_id != NULL) {
3325*572c4311Sfengbojiang if (currentNode->replicate) sdsfree(currentNode->replicate);
3326*572c4311Sfengbojiang currentNode->replicate = sdsnew(master_id);
3327*572c4311Sfengbojiang }
3328*572c4311Sfengbojiang }
3329*572c4311Sfengbojiang listAddNodeTail(currentNode->flags_str, flag);
3330*572c4311Sfengbojiang }
3331*572c4311Sfengbojiang if (config_epoch != NULL)
3332*572c4311Sfengbojiang currentNode->current_epoch = atoll(config_epoch);
3333*572c4311Sfengbojiang if (ping_sent != NULL) currentNode->ping_sent = atoll(ping_sent);
3334*572c4311Sfengbojiang if (ping_recv != NULL) currentNode->ping_recv = atoll(ping_recv);
3335*572c4311Sfengbojiang if (!getfriends && myself) break;
3336*572c4311Sfengbojiang }
3337*572c4311Sfengbojiang cleanup:
3338*572c4311Sfengbojiang if (reply) freeReplyObject(reply);
3339*572c4311Sfengbojiang return success;
3340*572c4311Sfengbojiang }
3341*572c4311Sfengbojiang
3342*572c4311Sfengbojiang /* Retrieves info about the cluster using argument 'node' as the starting
3343*572c4311Sfengbojiang * point. All nodes will be loaded inside the cluster_manager.nodes list.
3344*572c4311Sfengbojiang * Warning: if something goes wrong, it will free the starting node before
3345*572c4311Sfengbojiang * returning 0. */
clusterManagerLoadInfoFromNode(clusterManagerNode * node,int opts)3346*572c4311Sfengbojiang static int clusterManagerLoadInfoFromNode(clusterManagerNode *node, int opts) {
3347*572c4311Sfengbojiang if (node->context == NULL && !clusterManagerNodeConnect(node)) {
3348*572c4311Sfengbojiang freeClusterManagerNode(node);
3349*572c4311Sfengbojiang return 0;
3350*572c4311Sfengbojiang }
3351*572c4311Sfengbojiang opts |= CLUSTER_MANAGER_OPT_GETFRIENDS;
3352*572c4311Sfengbojiang char *e = NULL;
3353*572c4311Sfengbojiang if (!clusterManagerNodeIsCluster(node, &e)) {
3354*572c4311Sfengbojiang clusterManagerPrintNotClusterNodeError(node, e);
3355*572c4311Sfengbojiang if (e) zfree(e);
3356*572c4311Sfengbojiang freeClusterManagerNode(node);
3357*572c4311Sfengbojiang return 0;
3358*572c4311Sfengbojiang }
3359*572c4311Sfengbojiang e = NULL;
3360*572c4311Sfengbojiang if (!clusterManagerNodeLoadInfo(node, opts, &e)) {
3361*572c4311Sfengbojiang if (e) {
3362*572c4311Sfengbojiang CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, e);
3363*572c4311Sfengbojiang zfree(e);
3364*572c4311Sfengbojiang }
3365*572c4311Sfengbojiang freeClusterManagerNode(node);
3366*572c4311Sfengbojiang return 0;
3367*572c4311Sfengbojiang }
3368*572c4311Sfengbojiang listIter li;
3369*572c4311Sfengbojiang listNode *ln;
3370*572c4311Sfengbojiang if (cluster_manager.nodes != NULL) {
3371*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
3372*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL)
3373*572c4311Sfengbojiang freeClusterManagerNode((clusterManagerNode *) ln->value);
3374*572c4311Sfengbojiang listRelease(cluster_manager.nodes);
3375*572c4311Sfengbojiang }
3376*572c4311Sfengbojiang cluster_manager.nodes = listCreate();
3377*572c4311Sfengbojiang listAddNodeTail(cluster_manager.nodes, node);
3378*572c4311Sfengbojiang if (node->friends != NULL) {
3379*572c4311Sfengbojiang listRewind(node->friends, &li);
3380*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
3381*572c4311Sfengbojiang clusterManagerNode *friend = ln->value;
3382*572c4311Sfengbojiang if (!friend->ip || !friend->port) goto invalid_friend;
3383*572c4311Sfengbojiang if (!friend->context && !clusterManagerNodeConnect(friend))
3384*572c4311Sfengbojiang goto invalid_friend;
3385*572c4311Sfengbojiang e = NULL;
3386*572c4311Sfengbojiang if (clusterManagerNodeLoadInfo(friend, 0, &e)) {
3387*572c4311Sfengbojiang if (friend->flags & (CLUSTER_MANAGER_FLAG_NOADDR |
3388*572c4311Sfengbojiang CLUSTER_MANAGER_FLAG_DISCONNECT |
3389*572c4311Sfengbojiang CLUSTER_MANAGER_FLAG_FAIL))
3390*572c4311Sfengbojiang goto invalid_friend;
3391*572c4311Sfengbojiang listAddNodeTail(cluster_manager.nodes, friend);
3392*572c4311Sfengbojiang } else {
3393*572c4311Sfengbojiang clusterManagerLogErr("[ERR] Unable to load info for "
3394*572c4311Sfengbojiang "node %s:%d\n",
3395*572c4311Sfengbojiang friend->ip, friend->port);
3396*572c4311Sfengbojiang goto invalid_friend;
3397*572c4311Sfengbojiang }
3398*572c4311Sfengbojiang continue;
3399*572c4311Sfengbojiang invalid_friend:
3400*572c4311Sfengbojiang freeClusterManagerNode(friend);
3401*572c4311Sfengbojiang }
3402*572c4311Sfengbojiang listRelease(node->friends);
3403*572c4311Sfengbojiang node->friends = NULL;
3404*572c4311Sfengbojiang }
3405*572c4311Sfengbojiang // Count replicas for each node
3406*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
3407*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
3408*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
3409*572c4311Sfengbojiang if (n->replicate != NULL) {
3410*572c4311Sfengbojiang clusterManagerNode *master = clusterManagerNodeByName(n->replicate);
3411*572c4311Sfengbojiang if (master == NULL) {
3412*572c4311Sfengbojiang clusterManagerLogWarn("*** WARNING: %s:%d claims to be "
3413*572c4311Sfengbojiang "slave of unknown node ID %s.\n",
3414*572c4311Sfengbojiang n->ip, n->port, n->replicate);
3415*572c4311Sfengbojiang } else master->replicas_count++;
3416*572c4311Sfengbojiang }
3417*572c4311Sfengbojiang }
3418*572c4311Sfengbojiang return 1;
3419*572c4311Sfengbojiang }
3420*572c4311Sfengbojiang
3421*572c4311Sfengbojiang /* Compare functions used by various sorting operations. */
clusterManagerSlotCompare(const void * slot1,const void * slot2)3422*572c4311Sfengbojiang int clusterManagerSlotCompare(const void *slot1, const void *slot2) {
3423*572c4311Sfengbojiang const char **i1 = (const char **)slot1;
3424*572c4311Sfengbojiang const char **i2 = (const char **)slot2;
3425*572c4311Sfengbojiang return strcmp(*i1, *i2);
3426*572c4311Sfengbojiang }
3427*572c4311Sfengbojiang
clusterManagerSlotCountCompareDesc(const void * n1,const void * n2)3428*572c4311Sfengbojiang int clusterManagerSlotCountCompareDesc(const void *n1, const void *n2) {
3429*572c4311Sfengbojiang clusterManagerNode *node1 = *((clusterManagerNode **) n1);
3430*572c4311Sfengbojiang clusterManagerNode *node2 = *((clusterManagerNode **) n2);
3431*572c4311Sfengbojiang return node2->slots_count - node1->slots_count;
3432*572c4311Sfengbojiang }
3433*572c4311Sfengbojiang
clusterManagerCompareNodeBalance(const void * n1,const void * n2)3434*572c4311Sfengbojiang int clusterManagerCompareNodeBalance(const void *n1, const void *n2) {
3435*572c4311Sfengbojiang clusterManagerNode *node1 = *((clusterManagerNode **) n1);
3436*572c4311Sfengbojiang clusterManagerNode *node2 = *((clusterManagerNode **) n2);
3437*572c4311Sfengbojiang return node1->balance - node2->balance;
3438*572c4311Sfengbojiang }
3439*572c4311Sfengbojiang
clusterManagerGetConfigSignature(clusterManagerNode * node)3440*572c4311Sfengbojiang static sds clusterManagerGetConfigSignature(clusterManagerNode *node) {
3441*572c4311Sfengbojiang sds signature = NULL;
3442*572c4311Sfengbojiang int node_count = 0, i = 0, name_len = 0;
3443*572c4311Sfengbojiang char **node_configs = NULL;
3444*572c4311Sfengbojiang redisReply *reply = CLUSTER_MANAGER_COMMAND(node, "CLUSTER NODES");
3445*572c4311Sfengbojiang if (reply == NULL || reply->type == REDIS_REPLY_ERROR)
3446*572c4311Sfengbojiang goto cleanup;
3447*572c4311Sfengbojiang char *lines = reply->str, *p, *line;
3448*572c4311Sfengbojiang while ((p = strstr(lines, "\n")) != NULL) {
3449*572c4311Sfengbojiang i = 0;
3450*572c4311Sfengbojiang *p = '\0';
3451*572c4311Sfengbojiang line = lines;
3452*572c4311Sfengbojiang lines = p + 1;
3453*572c4311Sfengbojiang char *nodename = NULL;
3454*572c4311Sfengbojiang int tot_size = 0;
3455*572c4311Sfengbojiang while ((p = strchr(line, ' ')) != NULL) {
3456*572c4311Sfengbojiang *p = '\0';
3457*572c4311Sfengbojiang char *token = line;
3458*572c4311Sfengbojiang line = p + 1;
3459*572c4311Sfengbojiang if (i == 0) {
3460*572c4311Sfengbojiang nodename = token;
3461*572c4311Sfengbojiang tot_size = (p - token);
3462*572c4311Sfengbojiang name_len = tot_size++; // Make room for ':' in tot_size
3463*572c4311Sfengbojiang }
3464*572c4311Sfengbojiang if (++i == 8) break;
3465*572c4311Sfengbojiang }
3466*572c4311Sfengbojiang if (i != 8) continue;
3467*572c4311Sfengbojiang if (nodename == NULL) continue;
3468*572c4311Sfengbojiang int remaining = strlen(line);
3469*572c4311Sfengbojiang if (remaining == 0) continue;
3470*572c4311Sfengbojiang char **slots = NULL;
3471*572c4311Sfengbojiang int c = 0;
3472*572c4311Sfengbojiang while (remaining > 0) {
3473*572c4311Sfengbojiang p = strchr(line, ' ');
3474*572c4311Sfengbojiang if (p == NULL) p = line + remaining;
3475*572c4311Sfengbojiang int size = (p - line);
3476*572c4311Sfengbojiang remaining -= size;
3477*572c4311Sfengbojiang tot_size += size;
3478*572c4311Sfengbojiang char *slotsdef = line;
3479*572c4311Sfengbojiang *p = '\0';
3480*572c4311Sfengbojiang if (remaining) {
3481*572c4311Sfengbojiang line = p + 1;
3482*572c4311Sfengbojiang remaining--;
3483*572c4311Sfengbojiang } else line = p;
3484*572c4311Sfengbojiang if (slotsdef[0] != '[') {
3485*572c4311Sfengbojiang c++;
3486*572c4311Sfengbojiang slots = zrealloc(slots, (c * sizeof(char *)));
3487*572c4311Sfengbojiang slots[c - 1] = slotsdef;
3488*572c4311Sfengbojiang }
3489*572c4311Sfengbojiang }
3490*572c4311Sfengbojiang if (c > 0) {
3491*572c4311Sfengbojiang if (c > 1)
3492*572c4311Sfengbojiang qsort(slots, c, sizeof(char *), clusterManagerSlotCompare);
3493*572c4311Sfengbojiang node_count++;
3494*572c4311Sfengbojiang node_configs =
3495*572c4311Sfengbojiang zrealloc(node_configs, (node_count * sizeof(char *)));
3496*572c4311Sfengbojiang /* Make room for '|' separators. */
3497*572c4311Sfengbojiang tot_size += (sizeof(char) * (c - 1));
3498*572c4311Sfengbojiang char *cfg = zmalloc((sizeof(char) * tot_size) + 1);
3499*572c4311Sfengbojiang memcpy(cfg, nodename, name_len);
3500*572c4311Sfengbojiang char *sp = cfg + name_len;
3501*572c4311Sfengbojiang *(sp++) = ':';
3502*572c4311Sfengbojiang for (i = 0; i < c; i++) {
3503*572c4311Sfengbojiang if (i > 0) *(sp++) = ',';
3504*572c4311Sfengbojiang int slen = strlen(slots[i]);
3505*572c4311Sfengbojiang memcpy(sp, slots[i], slen);
3506*572c4311Sfengbojiang sp += slen;
3507*572c4311Sfengbojiang }
3508*572c4311Sfengbojiang *(sp++) = '\0';
3509*572c4311Sfengbojiang node_configs[node_count - 1] = cfg;
3510*572c4311Sfengbojiang }
3511*572c4311Sfengbojiang zfree(slots);
3512*572c4311Sfengbojiang }
3513*572c4311Sfengbojiang if (node_count > 0) {
3514*572c4311Sfengbojiang if (node_count > 1) {
3515*572c4311Sfengbojiang qsort(node_configs, node_count, sizeof(char *),
3516*572c4311Sfengbojiang clusterManagerSlotCompare);
3517*572c4311Sfengbojiang }
3518*572c4311Sfengbojiang signature = sdsempty();
3519*572c4311Sfengbojiang for (i = 0; i < node_count; i++) {
3520*572c4311Sfengbojiang if (i > 0) signature = sdscatprintf(signature, "%c", '|');
3521*572c4311Sfengbojiang signature = sdscatfmt(signature, "%s", node_configs[i]);
3522*572c4311Sfengbojiang }
3523*572c4311Sfengbojiang }
3524*572c4311Sfengbojiang cleanup:
3525*572c4311Sfengbojiang if (reply != NULL) freeReplyObject(reply);
3526*572c4311Sfengbojiang if (node_configs != NULL) {
3527*572c4311Sfengbojiang for (i = 0; i < node_count; i++) zfree(node_configs[i]);
3528*572c4311Sfengbojiang zfree(node_configs);
3529*572c4311Sfengbojiang }
3530*572c4311Sfengbojiang return signature;
3531*572c4311Sfengbojiang }
3532*572c4311Sfengbojiang
clusterManagerIsConfigConsistent(void)3533*572c4311Sfengbojiang static int clusterManagerIsConfigConsistent(void) {
3534*572c4311Sfengbojiang if (cluster_manager.nodes == NULL) return 0;
3535*572c4311Sfengbojiang int consistent = (listLength(cluster_manager.nodes) <= 1);
3536*572c4311Sfengbojiang // If the Cluster has only one node, it's always consistent
3537*572c4311Sfengbojiang if (consistent) return 1;
3538*572c4311Sfengbojiang sds first_cfg = NULL;
3539*572c4311Sfengbojiang listIter li;
3540*572c4311Sfengbojiang listNode *ln;
3541*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
3542*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
3543*572c4311Sfengbojiang clusterManagerNode *node = ln->value;
3544*572c4311Sfengbojiang sds cfg = clusterManagerGetConfigSignature(node);
3545*572c4311Sfengbojiang if (cfg == NULL) {
3546*572c4311Sfengbojiang consistent = 0;
3547*572c4311Sfengbojiang break;
3548*572c4311Sfengbojiang }
3549*572c4311Sfengbojiang if (first_cfg == NULL) first_cfg = cfg;
3550*572c4311Sfengbojiang else {
3551*572c4311Sfengbojiang consistent = !sdscmp(first_cfg, cfg);
3552*572c4311Sfengbojiang sdsfree(cfg);
3553*572c4311Sfengbojiang if (!consistent) break;
3554*572c4311Sfengbojiang }
3555*572c4311Sfengbojiang }
3556*572c4311Sfengbojiang if (first_cfg != NULL) sdsfree(first_cfg);
3557*572c4311Sfengbojiang return consistent;
3558*572c4311Sfengbojiang }
3559*572c4311Sfengbojiang
3560*572c4311Sfengbojiang /* Add the error string to cluster_manager.errors and print it. */
clusterManagerOnError(sds err)3561*572c4311Sfengbojiang static void clusterManagerOnError(sds err) {
3562*572c4311Sfengbojiang if (cluster_manager.errors == NULL)
3563*572c4311Sfengbojiang cluster_manager.errors = listCreate();
3564*572c4311Sfengbojiang listAddNodeTail(cluster_manager.errors, err);
3565*572c4311Sfengbojiang clusterManagerLogErr("%s\n", (char *) err);
3566*572c4311Sfengbojiang }
3567*572c4311Sfengbojiang
3568*572c4311Sfengbojiang /* Check the slots coverage of the cluster. The 'all_slots' argument must be
3569*572c4311Sfengbojiang * and array of 16384 bytes. Every covered slot will be set to 1 in the
3570*572c4311Sfengbojiang * 'all_slots' array. The function returns the total number if covered slots.*/
clusterManagerGetCoveredSlots(char * all_slots)3571*572c4311Sfengbojiang static int clusterManagerGetCoveredSlots(char *all_slots) {
3572*572c4311Sfengbojiang if (cluster_manager.nodes == NULL) return 0;
3573*572c4311Sfengbojiang listIter li;
3574*572c4311Sfengbojiang listNode *ln;
3575*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
3576*572c4311Sfengbojiang int totslots = 0, i;
3577*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
3578*572c4311Sfengbojiang clusterManagerNode *node = ln->value;
3579*572c4311Sfengbojiang for (i = 0; i < CLUSTER_MANAGER_SLOTS; i++) {
3580*572c4311Sfengbojiang if (node->slots[i] && !all_slots[i]) {
3581*572c4311Sfengbojiang all_slots[i] = 1;
3582*572c4311Sfengbojiang totslots++;
3583*572c4311Sfengbojiang }
3584*572c4311Sfengbojiang }
3585*572c4311Sfengbojiang }
3586*572c4311Sfengbojiang return totslots;
3587*572c4311Sfengbojiang }
3588*572c4311Sfengbojiang
clusterManagerPrintSlotsList(list * slots)3589*572c4311Sfengbojiang static void clusterManagerPrintSlotsList(list *slots) {
3590*572c4311Sfengbojiang listIter li;
3591*572c4311Sfengbojiang listNode *ln;
3592*572c4311Sfengbojiang listRewind(slots, &li);
3593*572c4311Sfengbojiang sds first = NULL;
3594*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
3595*572c4311Sfengbojiang sds slot = ln->value;
3596*572c4311Sfengbojiang if (!first) first = slot;
3597*572c4311Sfengbojiang else printf(", ");
3598*572c4311Sfengbojiang printf("%s", slot);
3599*572c4311Sfengbojiang }
3600*572c4311Sfengbojiang printf("\n");
3601*572c4311Sfengbojiang }
3602*572c4311Sfengbojiang
3603*572c4311Sfengbojiang /* Return the node, among 'nodes' with the greatest number of keys
3604*572c4311Sfengbojiang * in the specified slot. */
clusterManagerGetNodeWithMostKeysInSlot(list * nodes,int slot,char ** err)3605*572c4311Sfengbojiang static clusterManagerNode * clusterManagerGetNodeWithMostKeysInSlot(list *nodes,
3606*572c4311Sfengbojiang int slot,
3607*572c4311Sfengbojiang char **err)
3608*572c4311Sfengbojiang {
3609*572c4311Sfengbojiang clusterManagerNode *node = NULL;
3610*572c4311Sfengbojiang int numkeys = 0;
3611*572c4311Sfengbojiang listIter li;
3612*572c4311Sfengbojiang listNode *ln;
3613*572c4311Sfengbojiang listRewind(nodes, &li);
3614*572c4311Sfengbojiang if (err) *err = NULL;
3615*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
3616*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
3617*572c4311Sfengbojiang if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE || n->replicate)
3618*572c4311Sfengbojiang continue;
3619*572c4311Sfengbojiang redisReply *r =
3620*572c4311Sfengbojiang CLUSTER_MANAGER_COMMAND(n, "CLUSTER COUNTKEYSINSLOT %d", slot);
3621*572c4311Sfengbojiang int success = clusterManagerCheckRedisReply(n, r, err);
3622*572c4311Sfengbojiang if (success) {
3623*572c4311Sfengbojiang if (r->integer > numkeys || node == NULL) {
3624*572c4311Sfengbojiang numkeys = r->integer;
3625*572c4311Sfengbojiang node = n;
3626*572c4311Sfengbojiang }
3627*572c4311Sfengbojiang }
3628*572c4311Sfengbojiang if (r != NULL) freeReplyObject(r);
3629*572c4311Sfengbojiang /* If the reply contains errors */
3630*572c4311Sfengbojiang if (!success) {
3631*572c4311Sfengbojiang if (err != NULL && *err != NULL)
3632*572c4311Sfengbojiang CLUSTER_MANAGER_PRINT_REPLY_ERROR(n, err);
3633*572c4311Sfengbojiang node = NULL;
3634*572c4311Sfengbojiang break;
3635*572c4311Sfengbojiang }
3636*572c4311Sfengbojiang }
3637*572c4311Sfengbojiang return node;
3638*572c4311Sfengbojiang }
3639*572c4311Sfengbojiang
3640*572c4311Sfengbojiang /* This function returns the master that has the least number of replicas
3641*572c4311Sfengbojiang * in the cluster. If there are multiple masters with the same smaller
3642*572c4311Sfengbojiang * number of replicas, one at random is returned. */
3643*572c4311Sfengbojiang
clusterManagerNodeWithLeastReplicas()3644*572c4311Sfengbojiang static clusterManagerNode *clusterManagerNodeWithLeastReplicas() {
3645*572c4311Sfengbojiang clusterManagerNode *node = NULL;
3646*572c4311Sfengbojiang int lowest_count = 0;
3647*572c4311Sfengbojiang listIter li;
3648*572c4311Sfengbojiang listNode *ln;
3649*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
3650*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
3651*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
3652*572c4311Sfengbojiang if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;
3653*572c4311Sfengbojiang if (node == NULL || n->replicas_count < lowest_count) {
3654*572c4311Sfengbojiang node = n;
3655*572c4311Sfengbojiang lowest_count = n->replicas_count;
3656*572c4311Sfengbojiang }
3657*572c4311Sfengbojiang }
3658*572c4311Sfengbojiang return node;
3659*572c4311Sfengbojiang }
3660*572c4311Sfengbojiang
3661*572c4311Sfengbojiang /* This fucntion returns a random master node, return NULL if none */
3662*572c4311Sfengbojiang
clusterManagerNodeMasterRandom()3663*572c4311Sfengbojiang static clusterManagerNode *clusterManagerNodeMasterRandom() {
3664*572c4311Sfengbojiang int master_count = 0;
3665*572c4311Sfengbojiang int idx;
3666*572c4311Sfengbojiang listIter li;
3667*572c4311Sfengbojiang listNode *ln;
3668*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
3669*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
3670*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
3671*572c4311Sfengbojiang if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;
3672*572c4311Sfengbojiang master_count++;
3673*572c4311Sfengbojiang }
3674*572c4311Sfengbojiang
3675*572c4311Sfengbojiang srand(time(NULL));
3676*572c4311Sfengbojiang idx = rand() % master_count;
3677*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
3678*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
3679*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
3680*572c4311Sfengbojiang if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;
3681*572c4311Sfengbojiang if (!idx--) {
3682*572c4311Sfengbojiang return n;
3683*572c4311Sfengbojiang }
3684*572c4311Sfengbojiang }
3685*572c4311Sfengbojiang /* Can not be reached */
3686*572c4311Sfengbojiang return NULL;
3687*572c4311Sfengbojiang }
3688*572c4311Sfengbojiang
clusterManagerFixSlotsCoverage(char * all_slots)3689*572c4311Sfengbojiang static int clusterManagerFixSlotsCoverage(char *all_slots) {
3690*572c4311Sfengbojiang int i, fixed = 0;
3691*572c4311Sfengbojiang list *none = NULL, *single = NULL, *multi = NULL;
3692*572c4311Sfengbojiang clusterManagerLogInfo(">>> Fixing slots coverage...\n");
3693*572c4311Sfengbojiang printf("List of not covered slots: \n");
3694*572c4311Sfengbojiang int uncovered_count = 0;
3695*572c4311Sfengbojiang sds log = sdsempty();
3696*572c4311Sfengbojiang for (i = 0; i < CLUSTER_MANAGER_SLOTS; i++) {
3697*572c4311Sfengbojiang int covered = all_slots[i];
3698*572c4311Sfengbojiang if (!covered) {
3699*572c4311Sfengbojiang sds key = sdsfromlonglong((long long) i);
3700*572c4311Sfengbojiang if (uncovered_count++ > 0) printf(",");
3701*572c4311Sfengbojiang printf("%s", (char *) key);
3702*572c4311Sfengbojiang list *slot_nodes = listCreate();
3703*572c4311Sfengbojiang sds slot_nodes_str = sdsempty();
3704*572c4311Sfengbojiang listIter li;
3705*572c4311Sfengbojiang listNode *ln;
3706*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
3707*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
3708*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
3709*572c4311Sfengbojiang if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE || n->replicate)
3710*572c4311Sfengbojiang continue;
3711*572c4311Sfengbojiang redisReply *reply = CLUSTER_MANAGER_COMMAND(n,
3712*572c4311Sfengbojiang "CLUSTER GETKEYSINSLOT %d %d", i, 1);
3713*572c4311Sfengbojiang if (!clusterManagerCheckRedisReply(n, reply, NULL)) {
3714*572c4311Sfengbojiang fixed = -1;
3715*572c4311Sfengbojiang if (reply) freeReplyObject(reply);
3716*572c4311Sfengbojiang goto cleanup;
3717*572c4311Sfengbojiang }
3718*572c4311Sfengbojiang assert(reply->type == REDIS_REPLY_ARRAY);
3719*572c4311Sfengbojiang if (reply->elements > 0) {
3720*572c4311Sfengbojiang listAddNodeTail(slot_nodes, n);
3721*572c4311Sfengbojiang if (listLength(slot_nodes) > 1)
3722*572c4311Sfengbojiang slot_nodes_str = sdscat(slot_nodes_str, ", ");
3723*572c4311Sfengbojiang slot_nodes_str = sdscatfmt(slot_nodes_str,
3724*572c4311Sfengbojiang "%s:%u", n->ip, n->port);
3725*572c4311Sfengbojiang }
3726*572c4311Sfengbojiang freeReplyObject(reply);
3727*572c4311Sfengbojiang }
3728*572c4311Sfengbojiang log = sdscatfmt(log, "\nSlot %S has keys in %u nodes: %S",
3729*572c4311Sfengbojiang key, listLength(slot_nodes), slot_nodes_str);
3730*572c4311Sfengbojiang sdsfree(slot_nodes_str);
3731*572c4311Sfengbojiang dictAdd(clusterManagerUncoveredSlots, key, slot_nodes);
3732*572c4311Sfengbojiang }
3733*572c4311Sfengbojiang }
3734*572c4311Sfengbojiang printf("\n%s\n", log);
3735*572c4311Sfengbojiang /* For every slot, take action depending on the actual condition:
3736*572c4311Sfengbojiang * 1) No node has keys for this slot.
3737*572c4311Sfengbojiang * 2) A single node has keys for this slot.
3738*572c4311Sfengbojiang * 3) Multiple nodes have keys for this slot. */
3739*572c4311Sfengbojiang none = listCreate();
3740*572c4311Sfengbojiang single = listCreate();
3741*572c4311Sfengbojiang multi = listCreate();
3742*572c4311Sfengbojiang dictIterator *iter = dictGetIterator(clusterManagerUncoveredSlots);
3743*572c4311Sfengbojiang dictEntry *entry;
3744*572c4311Sfengbojiang while ((entry = dictNext(iter)) != NULL) {
3745*572c4311Sfengbojiang sds slot = (sds) dictGetKey(entry);
3746*572c4311Sfengbojiang list *nodes = (list *) dictGetVal(entry);
3747*572c4311Sfengbojiang switch (listLength(nodes)){
3748*572c4311Sfengbojiang case 0: listAddNodeTail(none, slot); break;
3749*572c4311Sfengbojiang case 1: listAddNodeTail(single, slot); break;
3750*572c4311Sfengbojiang default: listAddNodeTail(multi, slot); break;
3751*572c4311Sfengbojiang }
3752*572c4311Sfengbojiang }
3753*572c4311Sfengbojiang dictReleaseIterator(iter);
3754*572c4311Sfengbojiang
3755*572c4311Sfengbojiang /* Handle case "1": keys in no node. */
3756*572c4311Sfengbojiang if (listLength(none) > 0) {
3757*572c4311Sfengbojiang printf("The following uncovered slots have no keys "
3758*572c4311Sfengbojiang "across the cluster:\n");
3759*572c4311Sfengbojiang clusterManagerPrintSlotsList(none);
3760*572c4311Sfengbojiang if (confirmWithYes("Fix these slots by covering with a random node?")){
3761*572c4311Sfengbojiang listIter li;
3762*572c4311Sfengbojiang listNode *ln;
3763*572c4311Sfengbojiang listRewind(none, &li);
3764*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
3765*572c4311Sfengbojiang sds slot = ln->value;
3766*572c4311Sfengbojiang int s = atoi(slot);
3767*572c4311Sfengbojiang clusterManagerNode *n = clusterManagerNodeMasterRandom();
3768*572c4311Sfengbojiang clusterManagerLogInfo(">>> Covering slot %s with %s:%d\n",
3769*572c4311Sfengbojiang slot, n->ip, n->port);
3770*572c4311Sfengbojiang if (!clusterManagerSetSlotOwner(n, s, 0)) {
3771*572c4311Sfengbojiang fixed = -1;
3772*572c4311Sfengbojiang goto cleanup;
3773*572c4311Sfengbojiang }
3774*572c4311Sfengbojiang /* Since CLUSTER ADDSLOTS succeeded, we also update the slot
3775*572c4311Sfengbojiang * info into the node struct, in order to keep it synced */
3776*572c4311Sfengbojiang n->slots[s] = 1;
3777*572c4311Sfengbojiang fixed++;
3778*572c4311Sfengbojiang }
3779*572c4311Sfengbojiang }
3780*572c4311Sfengbojiang }
3781*572c4311Sfengbojiang
3782*572c4311Sfengbojiang /* Handle case "2": keys only in one node. */
3783*572c4311Sfengbojiang if (listLength(single) > 0) {
3784*572c4311Sfengbojiang printf("The following uncovered slots have keys in just one node:\n");
3785*572c4311Sfengbojiang clusterManagerPrintSlotsList(single);
3786*572c4311Sfengbojiang if (confirmWithYes("Fix these slots by covering with those nodes?")){
3787*572c4311Sfengbojiang listIter li;
3788*572c4311Sfengbojiang listNode *ln;
3789*572c4311Sfengbojiang listRewind(single, &li);
3790*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
3791*572c4311Sfengbojiang sds slot = ln->value;
3792*572c4311Sfengbojiang int s = atoi(slot);
3793*572c4311Sfengbojiang dictEntry *entry = dictFind(clusterManagerUncoveredSlots, slot);
3794*572c4311Sfengbojiang assert(entry != NULL);
3795*572c4311Sfengbojiang list *nodes = (list *) dictGetVal(entry);
3796*572c4311Sfengbojiang listNode *fn = listFirst(nodes);
3797*572c4311Sfengbojiang assert(fn != NULL);
3798*572c4311Sfengbojiang clusterManagerNode *n = fn->value;
3799*572c4311Sfengbojiang clusterManagerLogInfo(">>> Covering slot %s with %s:%d\n",
3800*572c4311Sfengbojiang slot, n->ip, n->port);
3801*572c4311Sfengbojiang if (!clusterManagerSetSlotOwner(n, s, 0)) {
3802*572c4311Sfengbojiang fixed = -1;
3803*572c4311Sfengbojiang goto cleanup;
3804*572c4311Sfengbojiang }
3805*572c4311Sfengbojiang /* Since CLUSTER ADDSLOTS succeeded, we also update the slot
3806*572c4311Sfengbojiang * info into the node struct, in order to keep it synced */
3807*572c4311Sfengbojiang n->slots[atoi(slot)] = 1;
3808*572c4311Sfengbojiang fixed++;
3809*572c4311Sfengbojiang }
3810*572c4311Sfengbojiang }
3811*572c4311Sfengbojiang }
3812*572c4311Sfengbojiang
3813*572c4311Sfengbojiang /* Handle case "3": keys in multiple nodes. */
3814*572c4311Sfengbojiang if (listLength(multi) > 0) {
3815*572c4311Sfengbojiang printf("The following uncovered slots have keys in multiple nodes:\n");
3816*572c4311Sfengbojiang clusterManagerPrintSlotsList(multi);
3817*572c4311Sfengbojiang if (confirmWithYes("Fix these slots by moving keys "
3818*572c4311Sfengbojiang "into a single node?")) {
3819*572c4311Sfengbojiang listIter li;
3820*572c4311Sfengbojiang listNode *ln;
3821*572c4311Sfengbojiang listRewind(multi, &li);
3822*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
3823*572c4311Sfengbojiang sds slot = ln->value;
3824*572c4311Sfengbojiang dictEntry *entry = dictFind(clusterManagerUncoveredSlots, slot);
3825*572c4311Sfengbojiang assert(entry != NULL);
3826*572c4311Sfengbojiang list *nodes = (list *) dictGetVal(entry);
3827*572c4311Sfengbojiang int s = atoi(slot);
3828*572c4311Sfengbojiang clusterManagerNode *target =
3829*572c4311Sfengbojiang clusterManagerGetNodeWithMostKeysInSlot(nodes, s, NULL);
3830*572c4311Sfengbojiang if (target == NULL) {
3831*572c4311Sfengbojiang fixed = -1;
3832*572c4311Sfengbojiang goto cleanup;
3833*572c4311Sfengbojiang }
3834*572c4311Sfengbojiang clusterManagerLogInfo(">>> Covering slot %s moving keys "
3835*572c4311Sfengbojiang "to %s:%d\n", slot,
3836*572c4311Sfengbojiang target->ip, target->port);
3837*572c4311Sfengbojiang if (!clusterManagerSetSlotOwner(target, s, 1)) {
3838*572c4311Sfengbojiang fixed = -1;
3839*572c4311Sfengbojiang goto cleanup;
3840*572c4311Sfengbojiang }
3841*572c4311Sfengbojiang /* Since CLUSTER ADDSLOTS succeeded, we also update the slot
3842*572c4311Sfengbojiang * info into the node struct, in order to keep it synced */
3843*572c4311Sfengbojiang target->slots[atoi(slot)] = 1;
3844*572c4311Sfengbojiang listIter nli;
3845*572c4311Sfengbojiang listNode *nln;
3846*572c4311Sfengbojiang listRewind(nodes, &nli);
3847*572c4311Sfengbojiang while ((nln = listNext(&nli)) != NULL) {
3848*572c4311Sfengbojiang clusterManagerNode *src = nln->value;
3849*572c4311Sfengbojiang if (src == target) continue;
3850*572c4311Sfengbojiang /* Assign the slot to target node in the source node. */
3851*572c4311Sfengbojiang if (!clusterManagerSetSlot(src, target, s, "NODE", NULL))
3852*572c4311Sfengbojiang fixed = -1;
3853*572c4311Sfengbojiang if (fixed < 0) goto cleanup;
3854*572c4311Sfengbojiang /* Set the source node in 'importing' state
3855*572c4311Sfengbojiang * (even if we will actually migrate keys away)
3856*572c4311Sfengbojiang * in order to avoid receiving redirections
3857*572c4311Sfengbojiang * for MIGRATE. */
3858*572c4311Sfengbojiang if (!clusterManagerSetSlot(src, target, s,
3859*572c4311Sfengbojiang "IMPORTING", NULL)) fixed = -1;
3860*572c4311Sfengbojiang if (fixed < 0) goto cleanup;
3861*572c4311Sfengbojiang int opts = CLUSTER_MANAGER_OPT_VERBOSE |
3862*572c4311Sfengbojiang CLUSTER_MANAGER_OPT_COLD;
3863*572c4311Sfengbojiang if (!clusterManagerMoveSlot(src, target, s, opts, NULL)) {
3864*572c4311Sfengbojiang fixed = -1;
3865*572c4311Sfengbojiang goto cleanup;
3866*572c4311Sfengbojiang }
3867*572c4311Sfengbojiang if (!clusterManagerClearSlotStatus(src, s))
3868*572c4311Sfengbojiang fixed = -1;
3869*572c4311Sfengbojiang if (fixed < 0) goto cleanup;
3870*572c4311Sfengbojiang }
3871*572c4311Sfengbojiang fixed++;
3872*572c4311Sfengbojiang }
3873*572c4311Sfengbojiang }
3874*572c4311Sfengbojiang }
3875*572c4311Sfengbojiang cleanup:
3876*572c4311Sfengbojiang sdsfree(log);
3877*572c4311Sfengbojiang if (none) listRelease(none);
3878*572c4311Sfengbojiang if (single) listRelease(single);
3879*572c4311Sfengbojiang if (multi) listRelease(multi);
3880*572c4311Sfengbojiang return fixed;
3881*572c4311Sfengbojiang }
3882*572c4311Sfengbojiang
3883*572c4311Sfengbojiang /* Slot 'slot' was found to be in importing or migrating state in one or
3884*572c4311Sfengbojiang * more nodes. This function fixes this condition by migrating keys where
3885*572c4311Sfengbojiang * it seems more sensible. */
clusterManagerFixOpenSlot(int slot)3886*572c4311Sfengbojiang static int clusterManagerFixOpenSlot(int slot) {
3887*572c4311Sfengbojiang clusterManagerLogInfo(">>> Fixing open slot %d\n", slot);
3888*572c4311Sfengbojiang /* Try to obtain the current slot owner, according to the current
3889*572c4311Sfengbojiang * nodes configuration. */
3890*572c4311Sfengbojiang int success = 1;
3891*572c4311Sfengbojiang list *owners = listCreate();
3892*572c4311Sfengbojiang list *migrating = listCreate();
3893*572c4311Sfengbojiang list *importing = listCreate();
3894*572c4311Sfengbojiang sds migrating_str = sdsempty();
3895*572c4311Sfengbojiang sds importing_str = sdsempty();
3896*572c4311Sfengbojiang clusterManagerNode *owner = NULL;
3897*572c4311Sfengbojiang listIter li;
3898*572c4311Sfengbojiang listNode *ln;
3899*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
3900*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
3901*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
3902*572c4311Sfengbojiang if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;
3903*572c4311Sfengbojiang if (n->slots[slot]) listAddNodeTail(owners, n);
3904*572c4311Sfengbojiang else {
3905*572c4311Sfengbojiang redisReply *r = CLUSTER_MANAGER_COMMAND(n,
3906*572c4311Sfengbojiang "CLUSTER COUNTKEYSINSLOT %d", slot);
3907*572c4311Sfengbojiang success = clusterManagerCheckRedisReply(n, r, NULL);
3908*572c4311Sfengbojiang if (success && r->integer > 0) {
3909*572c4311Sfengbojiang clusterManagerLogWarn("*** Found keys about slot %d "
3910*572c4311Sfengbojiang "in non-owner node %s:%d!\n", slot,
3911*572c4311Sfengbojiang n->ip, n->port);
3912*572c4311Sfengbojiang listAddNodeTail(owners, n);
3913*572c4311Sfengbojiang }
3914*572c4311Sfengbojiang if (r) freeReplyObject(r);
3915*572c4311Sfengbojiang if (!success) goto cleanup;
3916*572c4311Sfengbojiang }
3917*572c4311Sfengbojiang }
3918*572c4311Sfengbojiang if (listLength(owners) == 1) owner = listFirst(owners)->value;
3919*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
3920*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
3921*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
3922*572c4311Sfengbojiang if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;
3923*572c4311Sfengbojiang int is_migrating = 0, is_importing = 0;
3924*572c4311Sfengbojiang if (n->migrating) {
3925*572c4311Sfengbojiang for (int i = 0; i < n->migrating_count; i += 2) {
3926*572c4311Sfengbojiang sds migrating_slot = n->migrating[i];
3927*572c4311Sfengbojiang if (atoi(migrating_slot) == slot) {
3928*572c4311Sfengbojiang char *sep = (listLength(migrating) == 0 ? "" : ",");
3929*572c4311Sfengbojiang migrating_str = sdscatfmt(migrating_str, "%s%s:%u",
3930*572c4311Sfengbojiang sep, n->ip, n->port);
3931*572c4311Sfengbojiang listAddNodeTail(migrating, n);
3932*572c4311Sfengbojiang is_migrating = 1;
3933*572c4311Sfengbojiang break;
3934*572c4311Sfengbojiang }
3935*572c4311Sfengbojiang }
3936*572c4311Sfengbojiang }
3937*572c4311Sfengbojiang if (!is_migrating && n->importing) {
3938*572c4311Sfengbojiang for (int i = 0; i < n->importing_count; i += 2) {
3939*572c4311Sfengbojiang sds importing_slot = n->importing[i];
3940*572c4311Sfengbojiang if (atoi(importing_slot) == slot) {
3941*572c4311Sfengbojiang char *sep = (listLength(importing) == 0 ? "" : ",");
3942*572c4311Sfengbojiang importing_str = sdscatfmt(importing_str, "%s%s:%u",
3943*572c4311Sfengbojiang sep, n->ip, n->port);
3944*572c4311Sfengbojiang listAddNodeTail(importing, n);
3945*572c4311Sfengbojiang is_importing = 1;
3946*572c4311Sfengbojiang break;
3947*572c4311Sfengbojiang }
3948*572c4311Sfengbojiang }
3949*572c4311Sfengbojiang }
3950*572c4311Sfengbojiang /* If the node is neither migrating nor importing and it's not
3951*572c4311Sfengbojiang * the owner, then is added to the importing list in case
3952*572c4311Sfengbojiang * it has keys in the slot. */
3953*572c4311Sfengbojiang if (!is_migrating && !is_importing && n != owner) {
3954*572c4311Sfengbojiang redisReply *r = CLUSTER_MANAGER_COMMAND(n,
3955*572c4311Sfengbojiang "CLUSTER COUNTKEYSINSLOT %d", slot);
3956*572c4311Sfengbojiang success = clusterManagerCheckRedisReply(n, r, NULL);
3957*572c4311Sfengbojiang if (success && r->integer > 0) {
3958*572c4311Sfengbojiang clusterManagerLogWarn("*** Found keys about slot %d "
3959*572c4311Sfengbojiang "in node %s:%d!\n", slot, n->ip,
3960*572c4311Sfengbojiang n->port);
3961*572c4311Sfengbojiang char *sep = (listLength(importing) == 0 ? "" : ",");
3962*572c4311Sfengbojiang importing_str = sdscatfmt(importing_str, "%s%S:%u",
3963*572c4311Sfengbojiang sep, n->ip, n->port);
3964*572c4311Sfengbojiang listAddNodeTail(importing, n);
3965*572c4311Sfengbojiang }
3966*572c4311Sfengbojiang if (r) freeReplyObject(r);
3967*572c4311Sfengbojiang if (!success) goto cleanup;
3968*572c4311Sfengbojiang }
3969*572c4311Sfengbojiang }
3970*572c4311Sfengbojiang if (sdslen(migrating_str) > 0)
3971*572c4311Sfengbojiang printf("Set as migrating in: %s\n", migrating_str);
3972*572c4311Sfengbojiang if (sdslen(importing_str) > 0)
3973*572c4311Sfengbojiang printf("Set as importing in: %s\n", importing_str);
3974*572c4311Sfengbojiang /* If there is no slot owner, set as owner the node with the biggest
3975*572c4311Sfengbojiang * number of keys, among the set of migrating / importing nodes. */
3976*572c4311Sfengbojiang if (owner == NULL) {
3977*572c4311Sfengbojiang clusterManagerLogInfo(">>> Nobody claims ownership, "
3978*572c4311Sfengbojiang "selecting an owner...\n");
3979*572c4311Sfengbojiang owner = clusterManagerGetNodeWithMostKeysInSlot(cluster_manager.nodes,
3980*572c4311Sfengbojiang slot, NULL);
3981*572c4311Sfengbojiang // If we still don't have an owner, we can't fix it.
3982*572c4311Sfengbojiang if (owner == NULL) {
3983*572c4311Sfengbojiang clusterManagerLogErr("[ERR] Can't select a slot owner. "
3984*572c4311Sfengbojiang "Impossible to fix.\n");
3985*572c4311Sfengbojiang success = 0;
3986*572c4311Sfengbojiang goto cleanup;
3987*572c4311Sfengbojiang }
3988*572c4311Sfengbojiang
3989*572c4311Sfengbojiang // Use ADDSLOTS to assign the slot.
3990*572c4311Sfengbojiang clusterManagerLogWarn("*** Configuring %s:%d as the slot owner\n",
3991*572c4311Sfengbojiang owner->ip, owner->port);
3992*572c4311Sfengbojiang success = clusterManagerClearSlotStatus(owner, slot);
3993*572c4311Sfengbojiang if (!success) goto cleanup;
3994*572c4311Sfengbojiang success = clusterManagerSetSlotOwner(owner, slot, 0);
3995*572c4311Sfengbojiang if (!success) goto cleanup;
3996*572c4311Sfengbojiang /* Since CLUSTER ADDSLOTS succeeded, we also update the slot
3997*572c4311Sfengbojiang * info into the node struct, in order to keep it synced */
3998*572c4311Sfengbojiang owner->slots[slot] = 1;
3999*572c4311Sfengbojiang /* Make sure this information will propagate. Not strictly needed
4000*572c4311Sfengbojiang * since there is no past owner, so all the other nodes will accept
4001*572c4311Sfengbojiang * whatever epoch this node will claim the slot with. */
4002*572c4311Sfengbojiang success = clusterManagerBumpEpoch(owner);
4003*572c4311Sfengbojiang if (!success) goto cleanup;
4004*572c4311Sfengbojiang /* Remove the owner from the list of migrating/importing
4005*572c4311Sfengbojiang * nodes. */
4006*572c4311Sfengbojiang clusterManagerRemoveNodeFromList(migrating, owner);
4007*572c4311Sfengbojiang clusterManagerRemoveNodeFromList(importing, owner);
4008*572c4311Sfengbojiang }
4009*572c4311Sfengbojiang /* If there are multiple owners of the slot, we need to fix it
4010*572c4311Sfengbojiang * so that a single node is the owner and all the other nodes
4011*572c4311Sfengbojiang * are in importing state. Later the fix can be handled by one
4012*572c4311Sfengbojiang * of the base cases above.
4013*572c4311Sfengbojiang *
4014*572c4311Sfengbojiang * Note that this case also covers multiple nodes having the slot
4015*572c4311Sfengbojiang * in migrating state, since migrating is a valid state only for
4016*572c4311Sfengbojiang * slot owners. */
4017*572c4311Sfengbojiang if (listLength(owners) > 1) {
4018*572c4311Sfengbojiang /* Owner cannot be NULL at this point, since if there are more owners,
4019*572c4311Sfengbojiang * the owner has been set in the previous condition (owner == NULL). */
4020*572c4311Sfengbojiang assert(owner != NULL);
4021*572c4311Sfengbojiang listRewind(owners, &li);
4022*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4023*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
4024*572c4311Sfengbojiang if (n == owner) continue;
4025*572c4311Sfengbojiang success = clusterManagerDelSlot(n, slot, 1);
4026*572c4311Sfengbojiang if (!success) goto cleanup;
4027*572c4311Sfengbojiang n->slots[slot] = 0;
4028*572c4311Sfengbojiang /* Assign the slot to the owner in the node 'n' configuration.' */
4029*572c4311Sfengbojiang success = clusterManagerSetSlot(n, owner, slot, "node", NULL);
4030*572c4311Sfengbojiang if (!success) goto cleanup;
4031*572c4311Sfengbojiang success = clusterManagerSetSlot(n, owner, slot, "importing", NULL);
4032*572c4311Sfengbojiang if (!success) goto cleanup;
4033*572c4311Sfengbojiang /* Avoid duplicates. */
4034*572c4311Sfengbojiang clusterManagerRemoveNodeFromList(importing, n);
4035*572c4311Sfengbojiang listAddNodeTail(importing, n);
4036*572c4311Sfengbojiang /* Ensure that the node is not in the migrating list. */
4037*572c4311Sfengbojiang clusterManagerRemoveNodeFromList(migrating, n);
4038*572c4311Sfengbojiang }
4039*572c4311Sfengbojiang }
4040*572c4311Sfengbojiang int move_opts = CLUSTER_MANAGER_OPT_VERBOSE;
4041*572c4311Sfengbojiang /* Case 1: The slot is in migrating state in one node, and in
4042*572c4311Sfengbojiang * importing state in 1 node. That's trivial to address. */
4043*572c4311Sfengbojiang if (listLength(migrating) == 1 && listLength(importing) == 1) {
4044*572c4311Sfengbojiang clusterManagerNode *src = listFirst(migrating)->value;
4045*572c4311Sfengbojiang clusterManagerNode *dst = listFirst(importing)->value;
4046*572c4311Sfengbojiang clusterManagerLogInfo(">>> Case 1: Moving slot %d from "
4047*572c4311Sfengbojiang "%s:%d to %s:%d\n", slot,
4048*572c4311Sfengbojiang src->ip, src->port, dst->ip, dst->port);
4049*572c4311Sfengbojiang move_opts |= CLUSTER_MANAGER_OPT_UPDATE;
4050*572c4311Sfengbojiang success = clusterManagerMoveSlot(src, dst, slot, move_opts, NULL);
4051*572c4311Sfengbojiang }
4052*572c4311Sfengbojiang /* Case 2: There are multiple nodes that claim the slot as importing,
4053*572c4311Sfengbojiang * they probably got keys about the slot after a restart so opened
4054*572c4311Sfengbojiang * the slot. In this case we just move all the keys to the owner
4055*572c4311Sfengbojiang * according to the configuration. */
4056*572c4311Sfengbojiang else if (listLength(migrating) == 0 && listLength(importing) > 0) {
4057*572c4311Sfengbojiang clusterManagerLogInfo(">>> Case 2: Moving all the %d slot keys to its "
4058*572c4311Sfengbojiang "owner %s:%d\n", slot, owner->ip, owner->port);
4059*572c4311Sfengbojiang move_opts |= CLUSTER_MANAGER_OPT_COLD;
4060*572c4311Sfengbojiang listRewind(importing, &li);
4061*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4062*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
4063*572c4311Sfengbojiang if (n == owner) continue;
4064*572c4311Sfengbojiang success = clusterManagerMoveSlot(n, owner, slot, move_opts, NULL);
4065*572c4311Sfengbojiang if (!success) goto cleanup;
4066*572c4311Sfengbojiang clusterManagerLogInfo(">>> Setting %d as STABLE in "
4067*572c4311Sfengbojiang "%s:%d\n", slot, n->ip, n->port);
4068*572c4311Sfengbojiang success = clusterManagerClearSlotStatus(n, slot);
4069*572c4311Sfengbojiang if (!success) goto cleanup;
4070*572c4311Sfengbojiang }
4071*572c4311Sfengbojiang /* Since the slot has been moved in "cold" mode, ensure that all the
4072*572c4311Sfengbojiang * other nodes update their own configuration about the slot itself. */
4073*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
4074*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4075*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
4076*572c4311Sfengbojiang if (n == owner) continue;
4077*572c4311Sfengbojiang if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;
4078*572c4311Sfengbojiang success = clusterManagerSetSlot(n, owner, slot, "NODE", NULL);
4079*572c4311Sfengbojiang if (!success) goto cleanup;
4080*572c4311Sfengbojiang }
4081*572c4311Sfengbojiang }
4082*572c4311Sfengbojiang /* Case 3: The slot is in migrating state in one node but multiple
4083*572c4311Sfengbojiang * other nodes claim to be in importing state and don't have any key in
4084*572c4311Sfengbojiang * the slot. We search for the importing node having the same ID as
4085*572c4311Sfengbojiang * the destination node of the migrating node.
4086*572c4311Sfengbojiang * In that case we move the slot from the migrating node to this node and
4087*572c4311Sfengbojiang * we close the importing states on all the other importing nodes.
4088*572c4311Sfengbojiang * If no importing node has the same ID as the destination node of the
4089*572c4311Sfengbojiang * migrating node, the slot's state is closed on both the migrating node
4090*572c4311Sfengbojiang * and the importing nodes. */
4091*572c4311Sfengbojiang else if (listLength(migrating) == 1 && listLength(importing) > 1) {
4092*572c4311Sfengbojiang int try_to_fix = 1;
4093*572c4311Sfengbojiang clusterManagerNode *src = listFirst(migrating)->value;
4094*572c4311Sfengbojiang clusterManagerNode *dst = NULL;
4095*572c4311Sfengbojiang sds target_id = NULL;
4096*572c4311Sfengbojiang for (int i = 0; i < src->migrating_count; i += 2) {
4097*572c4311Sfengbojiang sds migrating_slot = src->migrating[i];
4098*572c4311Sfengbojiang if (atoi(migrating_slot) == slot) {
4099*572c4311Sfengbojiang target_id = src->migrating[i + 1];
4100*572c4311Sfengbojiang break;
4101*572c4311Sfengbojiang }
4102*572c4311Sfengbojiang }
4103*572c4311Sfengbojiang assert(target_id != NULL);
4104*572c4311Sfengbojiang listIter li;
4105*572c4311Sfengbojiang listNode *ln;
4106*572c4311Sfengbojiang listRewind(importing, &li);
4107*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4108*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
4109*572c4311Sfengbojiang int count = clusterManagerCountKeysInSlot(n, slot);
4110*572c4311Sfengbojiang if (count > 0) {
4111*572c4311Sfengbojiang try_to_fix = 0;
4112*572c4311Sfengbojiang break;
4113*572c4311Sfengbojiang }
4114*572c4311Sfengbojiang if (strcmp(n->name, target_id) == 0) dst = n;
4115*572c4311Sfengbojiang }
4116*572c4311Sfengbojiang if (!try_to_fix) goto unhandled_case;
4117*572c4311Sfengbojiang if (dst != NULL) {
4118*572c4311Sfengbojiang clusterManagerLogInfo(">>> Case 3: Moving slot %d from %s:%d to "
4119*572c4311Sfengbojiang "%s:%d and closing it on all the other "
4120*572c4311Sfengbojiang "importing nodes.\n",
4121*572c4311Sfengbojiang slot, src->ip, src->port,
4122*572c4311Sfengbojiang dst->ip, dst->port);
4123*572c4311Sfengbojiang /* Move the slot to the destination node. */
4124*572c4311Sfengbojiang success = clusterManagerMoveSlot(src, dst, slot, move_opts, NULL);
4125*572c4311Sfengbojiang if (!success) goto cleanup;
4126*572c4311Sfengbojiang /* Close slot on all the other importing nodes. */
4127*572c4311Sfengbojiang listRewind(importing, &li);
4128*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4129*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
4130*572c4311Sfengbojiang if (dst == n) continue;
4131*572c4311Sfengbojiang success = clusterManagerClearSlotStatus(n, slot);
4132*572c4311Sfengbojiang if (!success) goto cleanup;
4133*572c4311Sfengbojiang }
4134*572c4311Sfengbojiang } else {
4135*572c4311Sfengbojiang clusterManagerLogInfo(">>> Case 3: Closing slot %d on both "
4136*572c4311Sfengbojiang "migrating and importing nodes.\n", slot);
4137*572c4311Sfengbojiang /* Close the slot on both the migrating node and the importing
4138*572c4311Sfengbojiang * nodes. */
4139*572c4311Sfengbojiang success = clusterManagerClearSlotStatus(src, slot);
4140*572c4311Sfengbojiang if (!success) goto cleanup;
4141*572c4311Sfengbojiang listRewind(importing, &li);
4142*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4143*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
4144*572c4311Sfengbojiang success = clusterManagerClearSlotStatus(n, slot);
4145*572c4311Sfengbojiang if (!success) goto cleanup;
4146*572c4311Sfengbojiang }
4147*572c4311Sfengbojiang }
4148*572c4311Sfengbojiang } else {
4149*572c4311Sfengbojiang int try_to_close_slot = (listLength(importing) == 0 &&
4150*572c4311Sfengbojiang listLength(migrating) == 1);
4151*572c4311Sfengbojiang if (try_to_close_slot) {
4152*572c4311Sfengbojiang clusterManagerNode *n = listFirst(migrating)->value;
4153*572c4311Sfengbojiang if (!owner || owner != n) {
4154*572c4311Sfengbojiang redisReply *r = CLUSTER_MANAGER_COMMAND(n,
4155*572c4311Sfengbojiang "CLUSTER GETKEYSINSLOT %d %d", slot, 10);
4156*572c4311Sfengbojiang success = clusterManagerCheckRedisReply(n, r, NULL);
4157*572c4311Sfengbojiang if (r) {
4158*572c4311Sfengbojiang if (success) try_to_close_slot = (r->elements == 0);
4159*572c4311Sfengbojiang freeReplyObject(r);
4160*572c4311Sfengbojiang }
4161*572c4311Sfengbojiang if (!success) goto cleanup;
4162*572c4311Sfengbojiang }
4163*572c4311Sfengbojiang }
4164*572c4311Sfengbojiang /* Case 4: There are no slots claiming to be in importing state, but
4165*572c4311Sfengbojiang * there is a migrating node that actually don't have any key or is the
4166*572c4311Sfengbojiang * slot owner. We can just close the slot, probably a reshard
4167*572c4311Sfengbojiang * interrupted in the middle. */
4168*572c4311Sfengbojiang if (try_to_close_slot) {
4169*572c4311Sfengbojiang clusterManagerNode *n = listFirst(migrating)->value;
4170*572c4311Sfengbojiang clusterManagerLogInfo(">>> Case 4: Closing slot %d on %s:%d\n",
4171*572c4311Sfengbojiang slot, n->ip, n->port);
4172*572c4311Sfengbojiang redisReply *r = CLUSTER_MANAGER_COMMAND(n, "CLUSTER SETSLOT %d %s",
4173*572c4311Sfengbojiang slot, "STABLE");
4174*572c4311Sfengbojiang success = clusterManagerCheckRedisReply(n, r, NULL);
4175*572c4311Sfengbojiang if (r) freeReplyObject(r);
4176*572c4311Sfengbojiang if (!success) goto cleanup;
4177*572c4311Sfengbojiang } else {
4178*572c4311Sfengbojiang unhandled_case:
4179*572c4311Sfengbojiang success = 0;
4180*572c4311Sfengbojiang clusterManagerLogErr("[ERR] Sorry, redis-cli can't fix this slot "
4181*572c4311Sfengbojiang "yet (work in progress). Slot is set as "
4182*572c4311Sfengbojiang "migrating in %s, as importing in %s, "
4183*572c4311Sfengbojiang "owner is %s:%d\n", migrating_str,
4184*572c4311Sfengbojiang importing_str, owner->ip, owner->port);
4185*572c4311Sfengbojiang }
4186*572c4311Sfengbojiang }
4187*572c4311Sfengbojiang cleanup:
4188*572c4311Sfengbojiang listRelease(owners);
4189*572c4311Sfengbojiang listRelease(migrating);
4190*572c4311Sfengbojiang listRelease(importing);
4191*572c4311Sfengbojiang sdsfree(migrating_str);
4192*572c4311Sfengbojiang sdsfree(importing_str);
4193*572c4311Sfengbojiang return success;
4194*572c4311Sfengbojiang }
4195*572c4311Sfengbojiang
clusterManagerFixMultipleSlotOwners(int slot,list * owners)4196*572c4311Sfengbojiang static int clusterManagerFixMultipleSlotOwners(int slot, list *owners) {
4197*572c4311Sfengbojiang clusterManagerLogInfo(">>> Fixing multiple owners for slot %d...\n", slot);
4198*572c4311Sfengbojiang int success = 0;
4199*572c4311Sfengbojiang assert(listLength(owners) > 1);
4200*572c4311Sfengbojiang clusterManagerNode *owner = clusterManagerGetNodeWithMostKeysInSlot(owners,
4201*572c4311Sfengbojiang slot,
4202*572c4311Sfengbojiang NULL);
4203*572c4311Sfengbojiang if (!owner) owner = listFirst(owners)->value;
4204*572c4311Sfengbojiang clusterManagerLogInfo(">>> Setting slot %d owner: %s:%d\n",
4205*572c4311Sfengbojiang slot, owner->ip, owner->port);
4206*572c4311Sfengbojiang /* Set the slot owner. */
4207*572c4311Sfengbojiang if (!clusterManagerSetSlotOwner(owner, slot, 0)) return 0;
4208*572c4311Sfengbojiang listIter li;
4209*572c4311Sfengbojiang listNode *ln;
4210*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
4211*572c4311Sfengbojiang /* Update configuration in all the other master nodes by assigning the slot
4212*572c4311Sfengbojiang * itself to the new owner, and by eventually migrating keys if the node
4213*572c4311Sfengbojiang * has keys for the slot. */
4214*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4215*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
4216*572c4311Sfengbojiang if (n == owner) continue;
4217*572c4311Sfengbojiang if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;
4218*572c4311Sfengbojiang int count = clusterManagerCountKeysInSlot(n, slot);
4219*572c4311Sfengbojiang success = (count >= 0);
4220*572c4311Sfengbojiang if (!success) break;
4221*572c4311Sfengbojiang clusterManagerDelSlot(n, slot, 1);
4222*572c4311Sfengbojiang if (!clusterManagerSetSlot(n, owner, slot, "node", NULL)) return 0;
4223*572c4311Sfengbojiang if (count > 0) {
4224*572c4311Sfengbojiang int opts = CLUSTER_MANAGER_OPT_VERBOSE |
4225*572c4311Sfengbojiang CLUSTER_MANAGER_OPT_COLD;
4226*572c4311Sfengbojiang success = clusterManagerMoveSlot(n, owner, slot, opts, NULL);
4227*572c4311Sfengbojiang if (!success) break;
4228*572c4311Sfengbojiang }
4229*572c4311Sfengbojiang }
4230*572c4311Sfengbojiang return success;
4231*572c4311Sfengbojiang }
4232*572c4311Sfengbojiang
clusterManagerCheckCluster(int quiet)4233*572c4311Sfengbojiang static int clusterManagerCheckCluster(int quiet) {
4234*572c4311Sfengbojiang listNode *ln = listFirst(cluster_manager.nodes);
4235*572c4311Sfengbojiang if (!ln) return 0;
4236*572c4311Sfengbojiang clusterManagerNode *node = ln->value;
4237*572c4311Sfengbojiang clusterManagerLogInfo(">>> Performing Cluster Check (using node %s:%d)\n",
4238*572c4311Sfengbojiang node->ip, node->port);
4239*572c4311Sfengbojiang int result = 1, consistent = 0;
4240*572c4311Sfengbojiang int do_fix = config.cluster_manager_command.flags &
4241*572c4311Sfengbojiang CLUSTER_MANAGER_CMD_FLAG_FIX;
4242*572c4311Sfengbojiang if (!quiet) clusterManagerShowNodes();
4243*572c4311Sfengbojiang consistent = clusterManagerIsConfigConsistent();
4244*572c4311Sfengbojiang if (!consistent) {
4245*572c4311Sfengbojiang sds err = sdsnew("[ERR] Nodes don't agree about configuration!");
4246*572c4311Sfengbojiang clusterManagerOnError(err);
4247*572c4311Sfengbojiang result = 0;
4248*572c4311Sfengbojiang } else {
4249*572c4311Sfengbojiang clusterManagerLogOk("[OK] All nodes agree about slots "
4250*572c4311Sfengbojiang "configuration.\n");
4251*572c4311Sfengbojiang }
4252*572c4311Sfengbojiang /* Check open slots */
4253*572c4311Sfengbojiang clusterManagerLogInfo(">>> Check for open slots...\n");
4254*572c4311Sfengbojiang listIter li;
4255*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
4256*572c4311Sfengbojiang int i;
4257*572c4311Sfengbojiang dict *open_slots = NULL;
4258*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4259*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
4260*572c4311Sfengbojiang if (n->migrating != NULL) {
4261*572c4311Sfengbojiang if (open_slots == NULL)
4262*572c4311Sfengbojiang open_slots = dictCreate(&clusterManagerDictType, NULL);
4263*572c4311Sfengbojiang sds errstr = sdsempty();
4264*572c4311Sfengbojiang errstr = sdscatprintf(errstr,
4265*572c4311Sfengbojiang "[WARNING] Node %s:%d has slots in "
4266*572c4311Sfengbojiang "migrating state ",
4267*572c4311Sfengbojiang n->ip,
4268*572c4311Sfengbojiang n->port);
4269*572c4311Sfengbojiang for (i = 0; i < n->migrating_count; i += 2) {
4270*572c4311Sfengbojiang sds slot = n->migrating[i];
4271*572c4311Sfengbojiang dictAdd(open_slots, slot, sdsdup(n->migrating[i + 1]));
4272*572c4311Sfengbojiang char *fmt = (i > 0 ? ",%S" : "%S");
4273*572c4311Sfengbojiang errstr = sdscatfmt(errstr, fmt, slot);
4274*572c4311Sfengbojiang }
4275*572c4311Sfengbojiang errstr = sdscat(errstr, ".");
4276*572c4311Sfengbojiang clusterManagerOnError(errstr);
4277*572c4311Sfengbojiang }
4278*572c4311Sfengbojiang if (n->importing != NULL) {
4279*572c4311Sfengbojiang if (open_slots == NULL)
4280*572c4311Sfengbojiang open_slots = dictCreate(&clusterManagerDictType, NULL);
4281*572c4311Sfengbojiang sds errstr = sdsempty();
4282*572c4311Sfengbojiang errstr = sdscatprintf(errstr,
4283*572c4311Sfengbojiang "[WARNING] Node %s:%d has slots in "
4284*572c4311Sfengbojiang "importing state ",
4285*572c4311Sfengbojiang n->ip,
4286*572c4311Sfengbojiang n->port);
4287*572c4311Sfengbojiang for (i = 0; i < n->importing_count; i += 2) {
4288*572c4311Sfengbojiang sds slot = n->importing[i];
4289*572c4311Sfengbojiang dictAdd(open_slots, slot, sdsdup(n->importing[i + 1]));
4290*572c4311Sfengbojiang char *fmt = (i > 0 ? ",%S" : "%S");
4291*572c4311Sfengbojiang errstr = sdscatfmt(errstr, fmt, slot);
4292*572c4311Sfengbojiang }
4293*572c4311Sfengbojiang errstr = sdscat(errstr, ".");
4294*572c4311Sfengbojiang clusterManagerOnError(errstr);
4295*572c4311Sfengbojiang }
4296*572c4311Sfengbojiang }
4297*572c4311Sfengbojiang if (open_slots != NULL) {
4298*572c4311Sfengbojiang result = 0;
4299*572c4311Sfengbojiang dictIterator *iter = dictGetIterator(open_slots);
4300*572c4311Sfengbojiang dictEntry *entry;
4301*572c4311Sfengbojiang sds errstr = sdsnew("[WARNING] The following slots are open: ");
4302*572c4311Sfengbojiang i = 0;
4303*572c4311Sfengbojiang while ((entry = dictNext(iter)) != NULL) {
4304*572c4311Sfengbojiang sds slot = (sds) dictGetKey(entry);
4305*572c4311Sfengbojiang char *fmt = (i++ > 0 ? ",%S" : "%S");
4306*572c4311Sfengbojiang errstr = sdscatfmt(errstr, fmt, slot);
4307*572c4311Sfengbojiang }
4308*572c4311Sfengbojiang clusterManagerLogErr("%s.\n", (char *) errstr);
4309*572c4311Sfengbojiang sdsfree(errstr);
4310*572c4311Sfengbojiang if (do_fix) {
4311*572c4311Sfengbojiang /* Fix open slots. */
4312*572c4311Sfengbojiang dictReleaseIterator(iter);
4313*572c4311Sfengbojiang iter = dictGetIterator(open_slots);
4314*572c4311Sfengbojiang while ((entry = dictNext(iter)) != NULL) {
4315*572c4311Sfengbojiang sds slot = (sds) dictGetKey(entry);
4316*572c4311Sfengbojiang result = clusterManagerFixOpenSlot(atoi(slot));
4317*572c4311Sfengbojiang if (!result) break;
4318*572c4311Sfengbojiang }
4319*572c4311Sfengbojiang }
4320*572c4311Sfengbojiang dictReleaseIterator(iter);
4321*572c4311Sfengbojiang dictRelease(open_slots);
4322*572c4311Sfengbojiang }
4323*572c4311Sfengbojiang clusterManagerLogInfo(">>> Check slots coverage...\n");
4324*572c4311Sfengbojiang char slots[CLUSTER_MANAGER_SLOTS];
4325*572c4311Sfengbojiang memset(slots, 0, CLUSTER_MANAGER_SLOTS);
4326*572c4311Sfengbojiang int coverage = clusterManagerGetCoveredSlots(slots);
4327*572c4311Sfengbojiang if (coverage == CLUSTER_MANAGER_SLOTS) {
4328*572c4311Sfengbojiang clusterManagerLogOk("[OK] All %d slots covered.\n",
4329*572c4311Sfengbojiang CLUSTER_MANAGER_SLOTS);
4330*572c4311Sfengbojiang } else {
4331*572c4311Sfengbojiang sds err = sdsempty();
4332*572c4311Sfengbojiang err = sdscatprintf(err, "[ERR] Not all %d slots are "
4333*572c4311Sfengbojiang "covered by nodes.\n",
4334*572c4311Sfengbojiang CLUSTER_MANAGER_SLOTS);
4335*572c4311Sfengbojiang clusterManagerOnError(err);
4336*572c4311Sfengbojiang result = 0;
4337*572c4311Sfengbojiang if (do_fix/* && result*/) {
4338*572c4311Sfengbojiang dictType dtype = clusterManagerDictType;
4339*572c4311Sfengbojiang dtype.keyDestructor = dictSdsDestructor;
4340*572c4311Sfengbojiang dtype.valDestructor = dictListDestructor;
4341*572c4311Sfengbojiang clusterManagerUncoveredSlots = dictCreate(&dtype, NULL);
4342*572c4311Sfengbojiang int fixed = clusterManagerFixSlotsCoverage(slots);
4343*572c4311Sfengbojiang if (fixed > 0) result = 1;
4344*572c4311Sfengbojiang }
4345*572c4311Sfengbojiang }
4346*572c4311Sfengbojiang int search_multiple_owners = config.cluster_manager_command.flags &
4347*572c4311Sfengbojiang CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS;
4348*572c4311Sfengbojiang if (search_multiple_owners) {
4349*572c4311Sfengbojiang /* Check whether there are multiple owners, even when slots are
4350*572c4311Sfengbojiang * fully covered and there are no open slots. */
4351*572c4311Sfengbojiang clusterManagerLogInfo(">>> Check for multiple slot owners...\n");
4352*572c4311Sfengbojiang int slot = 0;
4353*572c4311Sfengbojiang for (; slot < CLUSTER_MANAGER_SLOTS; slot++) {
4354*572c4311Sfengbojiang listIter li;
4355*572c4311Sfengbojiang listNode *ln;
4356*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
4357*572c4311Sfengbojiang list *owners = listCreate();
4358*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4359*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
4360*572c4311Sfengbojiang if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;
4361*572c4311Sfengbojiang if (n->slots[slot]) listAddNodeTail(owners, n);
4362*572c4311Sfengbojiang else {
4363*572c4311Sfengbojiang /* Nodes having keys for the slot will be considered
4364*572c4311Sfengbojiang * owners too. */
4365*572c4311Sfengbojiang int count = clusterManagerCountKeysInSlot(n, slot);
4366*572c4311Sfengbojiang if (count > 0) listAddNodeTail(owners, n);
4367*572c4311Sfengbojiang }
4368*572c4311Sfengbojiang }
4369*572c4311Sfengbojiang if (listLength(owners) > 1) {
4370*572c4311Sfengbojiang result = 0;
4371*572c4311Sfengbojiang clusterManagerLogErr("[WARNING] Slot %d has %d owners:\n",
4372*572c4311Sfengbojiang slot, listLength(owners));
4373*572c4311Sfengbojiang listRewind(owners, &li);
4374*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4375*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
4376*572c4311Sfengbojiang clusterManagerLogErr(" %s:%d\n", n->ip, n->port);
4377*572c4311Sfengbojiang }
4378*572c4311Sfengbojiang if (do_fix) {
4379*572c4311Sfengbojiang result = clusterManagerFixMultipleSlotOwners(slot, owners);
4380*572c4311Sfengbojiang if (!result) {
4381*572c4311Sfengbojiang clusterManagerLogErr("Failed to fix multiple owners "
4382*572c4311Sfengbojiang "for slot %d\n", slot);
4383*572c4311Sfengbojiang listRelease(owners);
4384*572c4311Sfengbojiang break;
4385*572c4311Sfengbojiang }
4386*572c4311Sfengbojiang }
4387*572c4311Sfengbojiang }
4388*572c4311Sfengbojiang listRelease(owners);
4389*572c4311Sfengbojiang }
4390*572c4311Sfengbojiang }
4391*572c4311Sfengbojiang return result;
4392*572c4311Sfengbojiang }
4393*572c4311Sfengbojiang
clusterNodeForResharding(char * id,clusterManagerNode * target,int * raise_err)4394*572c4311Sfengbojiang static clusterManagerNode *clusterNodeForResharding(char *id,
4395*572c4311Sfengbojiang clusterManagerNode *target,
4396*572c4311Sfengbojiang int *raise_err)
4397*572c4311Sfengbojiang {
4398*572c4311Sfengbojiang clusterManagerNode *node = NULL;
4399*572c4311Sfengbojiang const char *invalid_node_msg = "*** The specified node (%s) is not known "
4400*572c4311Sfengbojiang "or not a master, please retry.\n";
4401*572c4311Sfengbojiang node = clusterManagerNodeByName(id);
4402*572c4311Sfengbojiang *raise_err = 0;
4403*572c4311Sfengbojiang if (!node || node->flags & CLUSTER_MANAGER_FLAG_SLAVE) {
4404*572c4311Sfengbojiang clusterManagerLogErr(invalid_node_msg, id);
4405*572c4311Sfengbojiang *raise_err = 1;
4406*572c4311Sfengbojiang return NULL;
4407*572c4311Sfengbojiang } else if (node != NULL && target != NULL) {
4408*572c4311Sfengbojiang if (!strcmp(node->name, target->name)) {
4409*572c4311Sfengbojiang clusterManagerLogErr( "*** It is not possible to use "
4410*572c4311Sfengbojiang "the target node as "
4411*572c4311Sfengbojiang "source node.\n");
4412*572c4311Sfengbojiang return NULL;
4413*572c4311Sfengbojiang }
4414*572c4311Sfengbojiang }
4415*572c4311Sfengbojiang return node;
4416*572c4311Sfengbojiang }
4417*572c4311Sfengbojiang
clusterManagerComputeReshardTable(list * sources,int numslots)4418*572c4311Sfengbojiang static list *clusterManagerComputeReshardTable(list *sources, int numslots) {
4419*572c4311Sfengbojiang list *moved = listCreate();
4420*572c4311Sfengbojiang int src_count = listLength(sources), i = 0, tot_slots = 0, j;
4421*572c4311Sfengbojiang clusterManagerNode **sorted = zmalloc(src_count * sizeof(*sorted));
4422*572c4311Sfengbojiang listIter li;
4423*572c4311Sfengbojiang listNode *ln;
4424*572c4311Sfengbojiang listRewind(sources, &li);
4425*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4426*572c4311Sfengbojiang clusterManagerNode *node = ln->value;
4427*572c4311Sfengbojiang tot_slots += node->slots_count;
4428*572c4311Sfengbojiang sorted[i++] = node;
4429*572c4311Sfengbojiang }
4430*572c4311Sfengbojiang qsort(sorted, src_count, sizeof(clusterManagerNode *),
4431*572c4311Sfengbojiang clusterManagerSlotCountCompareDesc);
4432*572c4311Sfengbojiang for (i = 0; i < src_count; i++) {
4433*572c4311Sfengbojiang clusterManagerNode *node = sorted[i];
4434*572c4311Sfengbojiang float n = ((float) numslots / tot_slots * node->slots_count);
4435*572c4311Sfengbojiang if (i == 0) n = ceil(n);
4436*572c4311Sfengbojiang else n = floor(n);
4437*572c4311Sfengbojiang int max = (int) n, count = 0;
4438*572c4311Sfengbojiang for (j = 0; j < CLUSTER_MANAGER_SLOTS; j++) {
4439*572c4311Sfengbojiang int slot = node->slots[j];
4440*572c4311Sfengbojiang if (!slot) continue;
4441*572c4311Sfengbojiang if (count >= max || (int)listLength(moved) >= numslots) break;
4442*572c4311Sfengbojiang clusterManagerReshardTableItem *item = zmalloc(sizeof(*item));
4443*572c4311Sfengbojiang item->source = node;
4444*572c4311Sfengbojiang item->slot = j;
4445*572c4311Sfengbojiang listAddNodeTail(moved, item);
4446*572c4311Sfengbojiang count++;
4447*572c4311Sfengbojiang }
4448*572c4311Sfengbojiang }
4449*572c4311Sfengbojiang zfree(sorted);
4450*572c4311Sfengbojiang return moved;
4451*572c4311Sfengbojiang }
4452*572c4311Sfengbojiang
clusterManagerShowReshardTable(list * table)4453*572c4311Sfengbojiang static void clusterManagerShowReshardTable(list *table) {
4454*572c4311Sfengbojiang listIter li;
4455*572c4311Sfengbojiang listNode *ln;
4456*572c4311Sfengbojiang listRewind(table, &li);
4457*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4458*572c4311Sfengbojiang clusterManagerReshardTableItem *item = ln->value;
4459*572c4311Sfengbojiang clusterManagerNode *n = item->source;
4460*572c4311Sfengbojiang printf(" Moving slot %d from %s\n", item->slot, (char *) n->name);
4461*572c4311Sfengbojiang }
4462*572c4311Sfengbojiang }
4463*572c4311Sfengbojiang
clusterManagerReleaseReshardTable(list * table)4464*572c4311Sfengbojiang static void clusterManagerReleaseReshardTable(list *table) {
4465*572c4311Sfengbojiang if (table != NULL) {
4466*572c4311Sfengbojiang listIter li;
4467*572c4311Sfengbojiang listNode *ln;
4468*572c4311Sfengbojiang listRewind(table, &li);
4469*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4470*572c4311Sfengbojiang clusterManagerReshardTableItem *item = ln->value;
4471*572c4311Sfengbojiang zfree(item);
4472*572c4311Sfengbojiang }
4473*572c4311Sfengbojiang listRelease(table);
4474*572c4311Sfengbojiang }
4475*572c4311Sfengbojiang }
4476*572c4311Sfengbojiang
clusterManagerLog(int level,const char * fmt,...)4477*572c4311Sfengbojiang static void clusterManagerLog(int level, const char* fmt, ...) {
4478*572c4311Sfengbojiang int use_colors =
4479*572c4311Sfengbojiang (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_COLOR);
4480*572c4311Sfengbojiang if (use_colors) {
4481*572c4311Sfengbojiang printf("\033[");
4482*572c4311Sfengbojiang switch (level) {
4483*572c4311Sfengbojiang case CLUSTER_MANAGER_LOG_LVL_INFO: printf(LOG_COLOR_BOLD); break;
4484*572c4311Sfengbojiang case CLUSTER_MANAGER_LOG_LVL_WARN: printf(LOG_COLOR_YELLOW); break;
4485*572c4311Sfengbojiang case CLUSTER_MANAGER_LOG_LVL_ERR: printf(LOG_COLOR_RED); break;
4486*572c4311Sfengbojiang case CLUSTER_MANAGER_LOG_LVL_SUCCESS: printf(LOG_COLOR_GREEN); break;
4487*572c4311Sfengbojiang default: printf(LOG_COLOR_RESET); break;
4488*572c4311Sfengbojiang }
4489*572c4311Sfengbojiang }
4490*572c4311Sfengbojiang va_list ap;
4491*572c4311Sfengbojiang va_start(ap, fmt);
4492*572c4311Sfengbojiang vprintf(fmt, ap);
4493*572c4311Sfengbojiang va_end(ap);
4494*572c4311Sfengbojiang if (use_colors) printf("\033[" LOG_COLOR_RESET);
4495*572c4311Sfengbojiang }
4496*572c4311Sfengbojiang
clusterManagerNodeArrayInit(clusterManagerNodeArray * array,int alloc_len)4497*572c4311Sfengbojiang static void clusterManagerNodeArrayInit(clusterManagerNodeArray *array,
4498*572c4311Sfengbojiang int alloc_len)
4499*572c4311Sfengbojiang {
4500*572c4311Sfengbojiang array->nodes = zcalloc(alloc_len * sizeof(clusterManagerNode*));
4501*572c4311Sfengbojiang array->alloc = array->nodes;
4502*572c4311Sfengbojiang array->len = alloc_len;
4503*572c4311Sfengbojiang array->count = 0;
4504*572c4311Sfengbojiang }
4505*572c4311Sfengbojiang
4506*572c4311Sfengbojiang /* Reset array->nodes to the original array allocation and re-count non-NULL
4507*572c4311Sfengbojiang * nodes. */
clusterManagerNodeArrayReset(clusterManagerNodeArray * array)4508*572c4311Sfengbojiang static void clusterManagerNodeArrayReset(clusterManagerNodeArray *array) {
4509*572c4311Sfengbojiang if (array->nodes > array->alloc) {
4510*572c4311Sfengbojiang array->len = array->nodes - array->alloc;
4511*572c4311Sfengbojiang array->nodes = array->alloc;
4512*572c4311Sfengbojiang array->count = 0;
4513*572c4311Sfengbojiang int i = 0;
4514*572c4311Sfengbojiang for(; i < array->len; i++) {
4515*572c4311Sfengbojiang if (array->nodes[i] != NULL) array->count++;
4516*572c4311Sfengbojiang }
4517*572c4311Sfengbojiang }
4518*572c4311Sfengbojiang }
4519*572c4311Sfengbojiang
4520*572c4311Sfengbojiang /* Shift array->nodes and store the shifted node into 'nodeptr'. */
clusterManagerNodeArrayShift(clusterManagerNodeArray * array,clusterManagerNode ** nodeptr)4521*572c4311Sfengbojiang static void clusterManagerNodeArrayShift(clusterManagerNodeArray *array,
4522*572c4311Sfengbojiang clusterManagerNode **nodeptr)
4523*572c4311Sfengbojiang {
4524*572c4311Sfengbojiang assert(array->nodes < (array->nodes + array->len));
4525*572c4311Sfengbojiang /* If the first node to be shifted is not NULL, decrement count. */
4526*572c4311Sfengbojiang if (*array->nodes != NULL) array->count--;
4527*572c4311Sfengbojiang /* Store the first node to be shifted into 'nodeptr'. */
4528*572c4311Sfengbojiang *nodeptr = *array->nodes;
4529*572c4311Sfengbojiang /* Shift the nodes array and decrement length. */
4530*572c4311Sfengbojiang array->nodes++;
4531*572c4311Sfengbojiang array->len--;
4532*572c4311Sfengbojiang }
4533*572c4311Sfengbojiang
clusterManagerNodeArrayAdd(clusterManagerNodeArray * array,clusterManagerNode * node)4534*572c4311Sfengbojiang static void clusterManagerNodeArrayAdd(clusterManagerNodeArray *array,
4535*572c4311Sfengbojiang clusterManagerNode *node)
4536*572c4311Sfengbojiang {
4537*572c4311Sfengbojiang assert(array->nodes < (array->nodes + array->len));
4538*572c4311Sfengbojiang assert(node != NULL);
4539*572c4311Sfengbojiang assert(array->count < array->len);
4540*572c4311Sfengbojiang array->nodes[array->count++] = node;
4541*572c4311Sfengbojiang }
4542*572c4311Sfengbojiang
clusterManagerPrintNotEmptyNodeError(clusterManagerNode * node,char * err)4543*572c4311Sfengbojiang static void clusterManagerPrintNotEmptyNodeError(clusterManagerNode *node,
4544*572c4311Sfengbojiang char *err)
4545*572c4311Sfengbojiang {
4546*572c4311Sfengbojiang char *msg;
4547*572c4311Sfengbojiang if (err) msg = err;
4548*572c4311Sfengbojiang else {
4549*572c4311Sfengbojiang msg = "is not empty. Either the node already knows other "
4550*572c4311Sfengbojiang "nodes (check with CLUSTER NODES) or contains some "
4551*572c4311Sfengbojiang "key in database 0.";
4552*572c4311Sfengbojiang }
4553*572c4311Sfengbojiang clusterManagerLogErr("[ERR] Node %s:%d %s\n", node->ip, node->port, msg);
4554*572c4311Sfengbojiang }
4555*572c4311Sfengbojiang
clusterManagerPrintNotClusterNodeError(clusterManagerNode * node,char * err)4556*572c4311Sfengbojiang static void clusterManagerPrintNotClusterNodeError(clusterManagerNode *node,
4557*572c4311Sfengbojiang char *err)
4558*572c4311Sfengbojiang {
4559*572c4311Sfengbojiang char *msg = (err ? err : "is not configured as a cluster node.");
4560*572c4311Sfengbojiang clusterManagerLogErr("[ERR] Node %s:%d %s\n", node->ip, node->port, msg);
4561*572c4311Sfengbojiang }
4562*572c4311Sfengbojiang
4563*572c4311Sfengbojiang /* Execute redis-cli in Cluster Manager mode */
clusterManagerMode(clusterManagerCommandProc * proc)4564*572c4311Sfengbojiang static void clusterManagerMode(clusterManagerCommandProc *proc) {
4565*572c4311Sfengbojiang int argc = config.cluster_manager_command.argc;
4566*572c4311Sfengbojiang char **argv = config.cluster_manager_command.argv;
4567*572c4311Sfengbojiang cluster_manager.nodes = NULL;
4568*572c4311Sfengbojiang if (!proc(argc, argv)) goto cluster_manager_err;
4569*572c4311Sfengbojiang freeClusterManager();
4570*572c4311Sfengbojiang exit(0);
4571*572c4311Sfengbojiang cluster_manager_err:
4572*572c4311Sfengbojiang freeClusterManager();
4573*572c4311Sfengbojiang sdsfree(config.hostip);
4574*572c4311Sfengbojiang sdsfree(config.mb_delim);
4575*572c4311Sfengbojiang exit(1);
4576*572c4311Sfengbojiang }
4577*572c4311Sfengbojiang
4578*572c4311Sfengbojiang /* Cluster Manager Commands */
4579*572c4311Sfengbojiang
clusterManagerCommandCreate(int argc,char ** argv)4580*572c4311Sfengbojiang static int clusterManagerCommandCreate(int argc, char **argv) {
4581*572c4311Sfengbojiang int i, j, success = 1;
4582*572c4311Sfengbojiang cluster_manager.nodes = listCreate();
4583*572c4311Sfengbojiang for (i = 0; i < argc; i++) {
4584*572c4311Sfengbojiang char *addr = argv[i];
4585*572c4311Sfengbojiang char *c = strrchr(addr, '@');
4586*572c4311Sfengbojiang if (c != NULL) *c = '\0';
4587*572c4311Sfengbojiang c = strrchr(addr, ':');
4588*572c4311Sfengbojiang if (c == NULL) {
4589*572c4311Sfengbojiang fprintf(stderr, "Invalid address format: %s\n", addr);
4590*572c4311Sfengbojiang return 0;
4591*572c4311Sfengbojiang }
4592*572c4311Sfengbojiang *c = '\0';
4593*572c4311Sfengbojiang char *ip = addr;
4594*572c4311Sfengbojiang int port = atoi(++c);
4595*572c4311Sfengbojiang clusterManagerNode *node = clusterManagerNewNode(ip, port);
4596*572c4311Sfengbojiang if (!clusterManagerNodeConnect(node)) {
4597*572c4311Sfengbojiang freeClusterManagerNode(node);
4598*572c4311Sfengbojiang return 0;
4599*572c4311Sfengbojiang }
4600*572c4311Sfengbojiang char *err = NULL;
4601*572c4311Sfengbojiang if (!clusterManagerNodeIsCluster(node, &err)) {
4602*572c4311Sfengbojiang clusterManagerPrintNotClusterNodeError(node, err);
4603*572c4311Sfengbojiang if (err) zfree(err);
4604*572c4311Sfengbojiang freeClusterManagerNode(node);
4605*572c4311Sfengbojiang return 0;
4606*572c4311Sfengbojiang }
4607*572c4311Sfengbojiang err = NULL;
4608*572c4311Sfengbojiang if (!clusterManagerNodeLoadInfo(node, 0, &err)) {
4609*572c4311Sfengbojiang if (err) {
4610*572c4311Sfengbojiang CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);
4611*572c4311Sfengbojiang zfree(err);
4612*572c4311Sfengbojiang }
4613*572c4311Sfengbojiang freeClusterManagerNode(node);
4614*572c4311Sfengbojiang return 0;
4615*572c4311Sfengbojiang }
4616*572c4311Sfengbojiang err = NULL;
4617*572c4311Sfengbojiang if (!clusterManagerNodeIsEmpty(node, &err)) {
4618*572c4311Sfengbojiang clusterManagerPrintNotEmptyNodeError(node, err);
4619*572c4311Sfengbojiang if (err) zfree(err);
4620*572c4311Sfengbojiang freeClusterManagerNode(node);
4621*572c4311Sfengbojiang return 0;
4622*572c4311Sfengbojiang }
4623*572c4311Sfengbojiang listAddNodeTail(cluster_manager.nodes, node);
4624*572c4311Sfengbojiang }
4625*572c4311Sfengbojiang int node_len = cluster_manager.nodes->len;
4626*572c4311Sfengbojiang int replicas = config.cluster_manager_command.replicas;
4627*572c4311Sfengbojiang int masters_count = CLUSTER_MANAGER_MASTERS_COUNT(node_len, replicas);
4628*572c4311Sfengbojiang if (masters_count < 3) {
4629*572c4311Sfengbojiang clusterManagerLogErr(
4630*572c4311Sfengbojiang "*** ERROR: Invalid configuration for cluster creation.\n"
4631*572c4311Sfengbojiang "*** Redis Cluster requires at least 3 master nodes.\n"
4632*572c4311Sfengbojiang "*** This is not possible with %d nodes and %d replicas per node.",
4633*572c4311Sfengbojiang node_len, replicas);
4634*572c4311Sfengbojiang clusterManagerLogErr("\n*** At least %d nodes are required.\n",
4635*572c4311Sfengbojiang 3 * (replicas + 1));
4636*572c4311Sfengbojiang return 0;
4637*572c4311Sfengbojiang }
4638*572c4311Sfengbojiang clusterManagerLogInfo(">>> Performing hash slots allocation "
4639*572c4311Sfengbojiang "on %d nodes...\n", node_len);
4640*572c4311Sfengbojiang int interleaved_len = 0, ip_count = 0;
4641*572c4311Sfengbojiang clusterManagerNode **interleaved = zcalloc(node_len*sizeof(**interleaved));
4642*572c4311Sfengbojiang char **ips = zcalloc(node_len * sizeof(char*));
4643*572c4311Sfengbojiang clusterManagerNodeArray *ip_nodes = zcalloc(node_len * sizeof(*ip_nodes));
4644*572c4311Sfengbojiang listIter li;
4645*572c4311Sfengbojiang listNode *ln;
4646*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
4647*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4648*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
4649*572c4311Sfengbojiang int found = 0;
4650*572c4311Sfengbojiang for (i = 0; i < ip_count; i++) {
4651*572c4311Sfengbojiang char *ip = ips[i];
4652*572c4311Sfengbojiang if (!strcmp(ip, n->ip)) {
4653*572c4311Sfengbojiang found = 1;
4654*572c4311Sfengbojiang break;
4655*572c4311Sfengbojiang }
4656*572c4311Sfengbojiang }
4657*572c4311Sfengbojiang if (!found) {
4658*572c4311Sfengbojiang ips[ip_count++] = n->ip;
4659*572c4311Sfengbojiang }
4660*572c4311Sfengbojiang clusterManagerNodeArray *node_array = &(ip_nodes[i]);
4661*572c4311Sfengbojiang if (node_array->nodes == NULL)
4662*572c4311Sfengbojiang clusterManagerNodeArrayInit(node_array, node_len);
4663*572c4311Sfengbojiang clusterManagerNodeArrayAdd(node_array, n);
4664*572c4311Sfengbojiang }
4665*572c4311Sfengbojiang while (interleaved_len < node_len) {
4666*572c4311Sfengbojiang for (i = 0; i < ip_count; i++) {
4667*572c4311Sfengbojiang clusterManagerNodeArray *node_array = &(ip_nodes[i]);
4668*572c4311Sfengbojiang if (node_array->count > 0) {
4669*572c4311Sfengbojiang clusterManagerNode *n = NULL;
4670*572c4311Sfengbojiang clusterManagerNodeArrayShift(node_array, &n);
4671*572c4311Sfengbojiang interleaved[interleaved_len++] = n;
4672*572c4311Sfengbojiang }
4673*572c4311Sfengbojiang }
4674*572c4311Sfengbojiang }
4675*572c4311Sfengbojiang clusterManagerNode **masters = interleaved;
4676*572c4311Sfengbojiang interleaved += masters_count;
4677*572c4311Sfengbojiang interleaved_len -= masters_count;
4678*572c4311Sfengbojiang float slots_per_node = CLUSTER_MANAGER_SLOTS / (float) masters_count;
4679*572c4311Sfengbojiang long first = 0;
4680*572c4311Sfengbojiang float cursor = 0.0f;
4681*572c4311Sfengbojiang for (i = 0; i < masters_count; i++) {
4682*572c4311Sfengbojiang clusterManagerNode *master = masters[i];
4683*572c4311Sfengbojiang long last = lround(cursor + slots_per_node - 1);
4684*572c4311Sfengbojiang if (last > CLUSTER_MANAGER_SLOTS || i == (masters_count - 1))
4685*572c4311Sfengbojiang last = CLUSTER_MANAGER_SLOTS - 1;
4686*572c4311Sfengbojiang if (last < first) last = first;
4687*572c4311Sfengbojiang printf("Master[%d] -> Slots %lu - %lu\n", i, first, last);
4688*572c4311Sfengbojiang master->slots_count = 0;
4689*572c4311Sfengbojiang for (j = first; j <= last; j++) {
4690*572c4311Sfengbojiang master->slots[j] = 1;
4691*572c4311Sfengbojiang master->slots_count++;
4692*572c4311Sfengbojiang }
4693*572c4311Sfengbojiang master->dirty = 1;
4694*572c4311Sfengbojiang first = last + 1;
4695*572c4311Sfengbojiang cursor += slots_per_node;
4696*572c4311Sfengbojiang }
4697*572c4311Sfengbojiang
4698*572c4311Sfengbojiang /* Rotating the list sometimes helps to get better initial
4699*572c4311Sfengbojiang * anti-affinity before the optimizer runs. */
4700*572c4311Sfengbojiang clusterManagerNode *first_node = interleaved[0];
4701*572c4311Sfengbojiang for (i = 0; i < (interleaved_len - 1); i++)
4702*572c4311Sfengbojiang interleaved[i] = interleaved[i + 1];
4703*572c4311Sfengbojiang interleaved[interleaved_len - 1] = first_node;
4704*572c4311Sfengbojiang int assign_unused = 0, available_count = interleaved_len;
4705*572c4311Sfengbojiang assign_replicas:
4706*572c4311Sfengbojiang for (i = 0; i < masters_count; i++) {
4707*572c4311Sfengbojiang clusterManagerNode *master = masters[i];
4708*572c4311Sfengbojiang int assigned_replicas = 0;
4709*572c4311Sfengbojiang while (assigned_replicas < replicas) {
4710*572c4311Sfengbojiang if (available_count == 0) break;
4711*572c4311Sfengbojiang clusterManagerNode *found = NULL, *slave = NULL;
4712*572c4311Sfengbojiang int firstNodeIdx = -1;
4713*572c4311Sfengbojiang for (j = 0; j < interleaved_len; j++) {
4714*572c4311Sfengbojiang clusterManagerNode *n = interleaved[j];
4715*572c4311Sfengbojiang if (n == NULL) continue;
4716*572c4311Sfengbojiang if (strcmp(n->ip, master->ip)) {
4717*572c4311Sfengbojiang found = n;
4718*572c4311Sfengbojiang interleaved[j] = NULL;
4719*572c4311Sfengbojiang break;
4720*572c4311Sfengbojiang }
4721*572c4311Sfengbojiang if (firstNodeIdx < 0) firstNodeIdx = j;
4722*572c4311Sfengbojiang }
4723*572c4311Sfengbojiang if (found) slave = found;
4724*572c4311Sfengbojiang else if (firstNodeIdx >= 0) {
4725*572c4311Sfengbojiang slave = interleaved[firstNodeIdx];
4726*572c4311Sfengbojiang interleaved_len -= (interleaved - (interleaved + firstNodeIdx));
4727*572c4311Sfengbojiang interleaved += (firstNodeIdx + 1);
4728*572c4311Sfengbojiang }
4729*572c4311Sfengbojiang if (slave != NULL) {
4730*572c4311Sfengbojiang assigned_replicas++;
4731*572c4311Sfengbojiang available_count--;
4732*572c4311Sfengbojiang if (slave->replicate) sdsfree(slave->replicate);
4733*572c4311Sfengbojiang slave->replicate = sdsnew(master->name);
4734*572c4311Sfengbojiang slave->dirty = 1;
4735*572c4311Sfengbojiang } else break;
4736*572c4311Sfengbojiang printf("Adding replica %s:%d to %s:%d\n", slave->ip, slave->port,
4737*572c4311Sfengbojiang master->ip, master->port);
4738*572c4311Sfengbojiang if (assign_unused) break;
4739*572c4311Sfengbojiang }
4740*572c4311Sfengbojiang }
4741*572c4311Sfengbojiang if (!assign_unused && available_count > 0) {
4742*572c4311Sfengbojiang assign_unused = 1;
4743*572c4311Sfengbojiang printf("Adding extra replicas...\n");
4744*572c4311Sfengbojiang goto assign_replicas;
4745*572c4311Sfengbojiang }
4746*572c4311Sfengbojiang for (i = 0; i < ip_count; i++) {
4747*572c4311Sfengbojiang clusterManagerNodeArray *node_array = ip_nodes + i;
4748*572c4311Sfengbojiang clusterManagerNodeArrayReset(node_array);
4749*572c4311Sfengbojiang }
4750*572c4311Sfengbojiang clusterManagerOptimizeAntiAffinity(ip_nodes, ip_count);
4751*572c4311Sfengbojiang clusterManagerShowNodes();
4752*572c4311Sfengbojiang if (confirmWithYes("Can I set the above configuration?")) {
4753*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
4754*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4755*572c4311Sfengbojiang clusterManagerNode *node = ln->value;
4756*572c4311Sfengbojiang char *err = NULL;
4757*572c4311Sfengbojiang int flushed = clusterManagerFlushNodeConfig(node, &err);
4758*572c4311Sfengbojiang if (!flushed && node->dirty && !node->replicate) {
4759*572c4311Sfengbojiang if (err != NULL) {
4760*572c4311Sfengbojiang CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);
4761*572c4311Sfengbojiang zfree(err);
4762*572c4311Sfengbojiang }
4763*572c4311Sfengbojiang success = 0;
4764*572c4311Sfengbojiang goto cleanup;
4765*572c4311Sfengbojiang } else if (err != NULL) zfree(err);
4766*572c4311Sfengbojiang }
4767*572c4311Sfengbojiang clusterManagerLogInfo(">>> Nodes configuration updated\n");
4768*572c4311Sfengbojiang clusterManagerLogInfo(">>> Assign a different config epoch to "
4769*572c4311Sfengbojiang "each node\n");
4770*572c4311Sfengbojiang int config_epoch = 1;
4771*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
4772*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4773*572c4311Sfengbojiang clusterManagerNode *node = ln->value;
4774*572c4311Sfengbojiang redisReply *reply = NULL;
4775*572c4311Sfengbojiang reply = CLUSTER_MANAGER_COMMAND(node,
4776*572c4311Sfengbojiang "cluster set-config-epoch %d",
4777*572c4311Sfengbojiang config_epoch++);
4778*572c4311Sfengbojiang if (reply != NULL) freeReplyObject(reply);
4779*572c4311Sfengbojiang }
4780*572c4311Sfengbojiang clusterManagerLogInfo(">>> Sending CLUSTER MEET messages to join "
4781*572c4311Sfengbojiang "the cluster\n");
4782*572c4311Sfengbojiang clusterManagerNode *first = NULL;
4783*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
4784*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4785*572c4311Sfengbojiang clusterManagerNode *node = ln->value;
4786*572c4311Sfengbojiang if (first == NULL) {
4787*572c4311Sfengbojiang first = node;
4788*572c4311Sfengbojiang continue;
4789*572c4311Sfengbojiang }
4790*572c4311Sfengbojiang redisReply *reply = NULL;
4791*572c4311Sfengbojiang reply = CLUSTER_MANAGER_COMMAND(node, "cluster meet %s %d",
4792*572c4311Sfengbojiang first->ip, first->port);
4793*572c4311Sfengbojiang int is_err = 0;
4794*572c4311Sfengbojiang if (reply != NULL) {
4795*572c4311Sfengbojiang if ((is_err = reply->type == REDIS_REPLY_ERROR))
4796*572c4311Sfengbojiang CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, reply->str);
4797*572c4311Sfengbojiang freeReplyObject(reply);
4798*572c4311Sfengbojiang } else {
4799*572c4311Sfengbojiang is_err = 1;
4800*572c4311Sfengbojiang fprintf(stderr, "Failed to send CLUSTER MEET command.\n");
4801*572c4311Sfengbojiang }
4802*572c4311Sfengbojiang if (is_err) {
4803*572c4311Sfengbojiang success = 0;
4804*572c4311Sfengbojiang goto cleanup;
4805*572c4311Sfengbojiang }
4806*572c4311Sfengbojiang }
4807*572c4311Sfengbojiang /* Give one second for the join to start, in order to avoid that
4808*572c4311Sfengbojiang * waiting for cluster join will find all the nodes agree about
4809*572c4311Sfengbojiang * the config as they are still empty with unassigned slots. */
4810*572c4311Sfengbojiang sleep(1);
4811*572c4311Sfengbojiang clusterManagerWaitForClusterJoin();
4812*572c4311Sfengbojiang /* Useful for the replicas */
4813*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
4814*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4815*572c4311Sfengbojiang clusterManagerNode *node = ln->value;
4816*572c4311Sfengbojiang if (!node->dirty) continue;
4817*572c4311Sfengbojiang char *err = NULL;
4818*572c4311Sfengbojiang int flushed = clusterManagerFlushNodeConfig(node, &err);
4819*572c4311Sfengbojiang if (!flushed && !node->replicate) {
4820*572c4311Sfengbojiang if (err != NULL) {
4821*572c4311Sfengbojiang CLUSTER_MANAGER_PRINT_REPLY_ERROR(node, err);
4822*572c4311Sfengbojiang zfree(err);
4823*572c4311Sfengbojiang }
4824*572c4311Sfengbojiang success = 0;
4825*572c4311Sfengbojiang goto cleanup;
4826*572c4311Sfengbojiang }
4827*572c4311Sfengbojiang }
4828*572c4311Sfengbojiang // Reset Nodes
4829*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
4830*572c4311Sfengbojiang clusterManagerNode *first_node = NULL;
4831*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4832*572c4311Sfengbojiang clusterManagerNode *node = ln->value;
4833*572c4311Sfengbojiang if (!first_node) first_node = node;
4834*572c4311Sfengbojiang else freeClusterManagerNode(node);
4835*572c4311Sfengbojiang }
4836*572c4311Sfengbojiang listEmpty(cluster_manager.nodes);
4837*572c4311Sfengbojiang if (!clusterManagerLoadInfoFromNode(first_node, 0)) {
4838*572c4311Sfengbojiang success = 0;
4839*572c4311Sfengbojiang goto cleanup;
4840*572c4311Sfengbojiang }
4841*572c4311Sfengbojiang clusterManagerCheckCluster(0);
4842*572c4311Sfengbojiang }
4843*572c4311Sfengbojiang cleanup:
4844*572c4311Sfengbojiang /* Free everything */
4845*572c4311Sfengbojiang zfree(masters);
4846*572c4311Sfengbojiang zfree(ips);
4847*572c4311Sfengbojiang for (i = 0; i < node_len; i++) {
4848*572c4311Sfengbojiang clusterManagerNodeArray *node_array = ip_nodes + i;
4849*572c4311Sfengbojiang CLUSTER_MANAGER_NODE_ARRAY_FREE(node_array);
4850*572c4311Sfengbojiang }
4851*572c4311Sfengbojiang zfree(ip_nodes);
4852*572c4311Sfengbojiang return success;
4853*572c4311Sfengbojiang }
4854*572c4311Sfengbojiang
clusterManagerCommandAddNode(int argc,char ** argv)4855*572c4311Sfengbojiang static int clusterManagerCommandAddNode(int argc, char **argv) {
4856*572c4311Sfengbojiang int success = 1;
4857*572c4311Sfengbojiang redisReply *reply = NULL;
4858*572c4311Sfengbojiang char *ref_ip = NULL, *ip = NULL;
4859*572c4311Sfengbojiang int ref_port = 0, port = 0;
4860*572c4311Sfengbojiang if (!getClusterHostFromCmdArgs(argc - 1, argv + 1, &ref_ip, &ref_port))
4861*572c4311Sfengbojiang goto invalid_args;
4862*572c4311Sfengbojiang if (!getClusterHostFromCmdArgs(1, argv, &ip, &port))
4863*572c4311Sfengbojiang goto invalid_args;
4864*572c4311Sfengbojiang clusterManagerLogInfo(">>> Adding node %s:%d to cluster %s:%d\n", ip, port,
4865*572c4311Sfengbojiang ref_ip, ref_port);
4866*572c4311Sfengbojiang // Check the existing cluster
4867*572c4311Sfengbojiang clusterManagerNode *refnode = clusterManagerNewNode(ref_ip, ref_port);
4868*572c4311Sfengbojiang if (!clusterManagerLoadInfoFromNode(refnode, 0)) return 0;
4869*572c4311Sfengbojiang if (!clusterManagerCheckCluster(0)) return 0;
4870*572c4311Sfengbojiang
4871*572c4311Sfengbojiang /* If --cluster-master-id was specified, try to resolve it now so that we
4872*572c4311Sfengbojiang * abort before starting with the node configuration. */
4873*572c4311Sfengbojiang clusterManagerNode *master_node = NULL;
4874*572c4311Sfengbojiang if (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_SLAVE) {
4875*572c4311Sfengbojiang char *master_id = config.cluster_manager_command.master_id;
4876*572c4311Sfengbojiang if (master_id != NULL) {
4877*572c4311Sfengbojiang master_node = clusterManagerNodeByName(master_id);
4878*572c4311Sfengbojiang if (master_node == NULL) {
4879*572c4311Sfengbojiang clusterManagerLogErr("[ERR] No such master ID %s\n", master_id);
4880*572c4311Sfengbojiang return 0;
4881*572c4311Sfengbojiang }
4882*572c4311Sfengbojiang } else {
4883*572c4311Sfengbojiang master_node = clusterManagerNodeWithLeastReplicas();
4884*572c4311Sfengbojiang assert(master_node != NULL);
4885*572c4311Sfengbojiang printf("Automatically selected master %s:%d\n", master_node->ip,
4886*572c4311Sfengbojiang master_node->port);
4887*572c4311Sfengbojiang }
4888*572c4311Sfengbojiang }
4889*572c4311Sfengbojiang
4890*572c4311Sfengbojiang // Add the new node
4891*572c4311Sfengbojiang clusterManagerNode *new_node = clusterManagerNewNode(ip, port);
4892*572c4311Sfengbojiang int added = 0;
4893*572c4311Sfengbojiang if (!clusterManagerNodeConnect(new_node)) {
4894*572c4311Sfengbojiang clusterManagerLogErr("[ERR] Sorry, can't connect to node %s:%d\n",
4895*572c4311Sfengbojiang ip, port);
4896*572c4311Sfengbojiang success = 0;
4897*572c4311Sfengbojiang goto cleanup;
4898*572c4311Sfengbojiang }
4899*572c4311Sfengbojiang char *err = NULL;
4900*572c4311Sfengbojiang if (!(success = clusterManagerNodeIsCluster(new_node, &err))) {
4901*572c4311Sfengbojiang clusterManagerPrintNotClusterNodeError(new_node, err);
4902*572c4311Sfengbojiang if (err) zfree(err);
4903*572c4311Sfengbojiang goto cleanup;
4904*572c4311Sfengbojiang }
4905*572c4311Sfengbojiang if (!clusterManagerNodeLoadInfo(new_node, 0, &err)) {
4906*572c4311Sfengbojiang if (err) {
4907*572c4311Sfengbojiang CLUSTER_MANAGER_PRINT_REPLY_ERROR(new_node, err);
4908*572c4311Sfengbojiang zfree(err);
4909*572c4311Sfengbojiang }
4910*572c4311Sfengbojiang success = 0;
4911*572c4311Sfengbojiang goto cleanup;
4912*572c4311Sfengbojiang }
4913*572c4311Sfengbojiang if (!(success = clusterManagerNodeIsEmpty(new_node, &err))) {
4914*572c4311Sfengbojiang clusterManagerPrintNotEmptyNodeError(new_node, err);
4915*572c4311Sfengbojiang if (err) zfree(err);
4916*572c4311Sfengbojiang goto cleanup;
4917*572c4311Sfengbojiang }
4918*572c4311Sfengbojiang clusterManagerNode *first = listFirst(cluster_manager.nodes)->value;
4919*572c4311Sfengbojiang listAddNodeTail(cluster_manager.nodes, new_node);
4920*572c4311Sfengbojiang added = 1;
4921*572c4311Sfengbojiang
4922*572c4311Sfengbojiang // Send CLUSTER MEET command to the new node
4923*572c4311Sfengbojiang clusterManagerLogInfo(">>> Send CLUSTER MEET to node %s:%d to make it "
4924*572c4311Sfengbojiang "join the cluster.\n", ip, port);
4925*572c4311Sfengbojiang reply = CLUSTER_MANAGER_COMMAND(new_node, "CLUSTER MEET %s %d",
4926*572c4311Sfengbojiang first->ip, first->port);
4927*572c4311Sfengbojiang if (!(success = clusterManagerCheckRedisReply(new_node, reply, NULL)))
4928*572c4311Sfengbojiang goto cleanup;
4929*572c4311Sfengbojiang
4930*572c4311Sfengbojiang /* Additional configuration is needed if the node is added as a slave. */
4931*572c4311Sfengbojiang if (master_node) {
4932*572c4311Sfengbojiang sleep(1);
4933*572c4311Sfengbojiang clusterManagerWaitForClusterJoin();
4934*572c4311Sfengbojiang clusterManagerLogInfo(">>> Configure node as replica of %s:%d.\n",
4935*572c4311Sfengbojiang master_node->ip, master_node->port);
4936*572c4311Sfengbojiang freeReplyObject(reply);
4937*572c4311Sfengbojiang reply = CLUSTER_MANAGER_COMMAND(new_node, "CLUSTER REPLICATE %s",
4938*572c4311Sfengbojiang master_node->name);
4939*572c4311Sfengbojiang if (!(success = clusterManagerCheckRedisReply(new_node, reply, NULL)))
4940*572c4311Sfengbojiang goto cleanup;
4941*572c4311Sfengbojiang }
4942*572c4311Sfengbojiang clusterManagerLogOk("[OK] New node added correctly.\n");
4943*572c4311Sfengbojiang cleanup:
4944*572c4311Sfengbojiang if (!added && new_node) freeClusterManagerNode(new_node);
4945*572c4311Sfengbojiang if (reply) freeReplyObject(reply);
4946*572c4311Sfengbojiang return success;
4947*572c4311Sfengbojiang invalid_args:
4948*572c4311Sfengbojiang fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);
4949*572c4311Sfengbojiang return 0;
4950*572c4311Sfengbojiang }
4951*572c4311Sfengbojiang
clusterManagerCommandDeleteNode(int argc,char ** argv)4952*572c4311Sfengbojiang static int clusterManagerCommandDeleteNode(int argc, char **argv) {
4953*572c4311Sfengbojiang UNUSED(argc);
4954*572c4311Sfengbojiang int success = 1;
4955*572c4311Sfengbojiang int port = 0;
4956*572c4311Sfengbojiang char *ip = NULL;
4957*572c4311Sfengbojiang if (!getClusterHostFromCmdArgs(1, argv, &ip, &port)) goto invalid_args;
4958*572c4311Sfengbojiang char *node_id = argv[1];
4959*572c4311Sfengbojiang clusterManagerLogInfo(">>> Removing node %s from cluster %s:%d\n",
4960*572c4311Sfengbojiang node_id, ip, port);
4961*572c4311Sfengbojiang clusterManagerNode *ref_node = clusterManagerNewNode(ip, port);
4962*572c4311Sfengbojiang clusterManagerNode *node = NULL;
4963*572c4311Sfengbojiang
4964*572c4311Sfengbojiang // Load cluster information
4965*572c4311Sfengbojiang if (!clusterManagerLoadInfoFromNode(ref_node, 0)) return 0;
4966*572c4311Sfengbojiang
4967*572c4311Sfengbojiang // Check if the node exists and is not empty
4968*572c4311Sfengbojiang node = clusterManagerNodeByName(node_id);
4969*572c4311Sfengbojiang if (node == NULL) {
4970*572c4311Sfengbojiang clusterManagerLogErr("[ERR] No such node ID %s\n", node_id);
4971*572c4311Sfengbojiang return 0;
4972*572c4311Sfengbojiang }
4973*572c4311Sfengbojiang if (node->slots_count != 0) {
4974*572c4311Sfengbojiang clusterManagerLogErr("[ERR] Node %s:%d is not empty! Reshard data "
4975*572c4311Sfengbojiang "away and try again.\n", node->ip, node->port);
4976*572c4311Sfengbojiang return 0;
4977*572c4311Sfengbojiang }
4978*572c4311Sfengbojiang
4979*572c4311Sfengbojiang // Send CLUSTER FORGET to all the nodes but the node to remove
4980*572c4311Sfengbojiang clusterManagerLogInfo(">>> Sending CLUSTER FORGET messages to the "
4981*572c4311Sfengbojiang "cluster...\n");
4982*572c4311Sfengbojiang listIter li;
4983*572c4311Sfengbojiang listNode *ln;
4984*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
4985*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
4986*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
4987*572c4311Sfengbojiang if (n == node) continue;
4988*572c4311Sfengbojiang if (n->replicate && !strcasecmp(n->replicate, node_id)) {
4989*572c4311Sfengbojiang // Reconfigure the slave to replicate with some other node
4990*572c4311Sfengbojiang clusterManagerNode *master = clusterManagerNodeWithLeastReplicas();
4991*572c4311Sfengbojiang assert(master != NULL);
4992*572c4311Sfengbojiang clusterManagerLogInfo(">>> %s:%d as replica of %s:%d\n",
4993*572c4311Sfengbojiang n->ip, n->port, master->ip, master->port);
4994*572c4311Sfengbojiang redisReply *r = CLUSTER_MANAGER_COMMAND(n, "CLUSTER REPLICATE %s",
4995*572c4311Sfengbojiang master->name);
4996*572c4311Sfengbojiang success = clusterManagerCheckRedisReply(n, r, NULL);
4997*572c4311Sfengbojiang if (r) freeReplyObject(r);
4998*572c4311Sfengbojiang if (!success) return 0;
4999*572c4311Sfengbojiang }
5000*572c4311Sfengbojiang redisReply *r = CLUSTER_MANAGER_COMMAND(n, "CLUSTER FORGET %s",
5001*572c4311Sfengbojiang node_id);
5002*572c4311Sfengbojiang success = clusterManagerCheckRedisReply(n, r, NULL);
5003*572c4311Sfengbojiang if (r) freeReplyObject(r);
5004*572c4311Sfengbojiang if (!success) return 0;
5005*572c4311Sfengbojiang }
5006*572c4311Sfengbojiang
5007*572c4311Sfengbojiang // Finally shutdown the node
5008*572c4311Sfengbojiang clusterManagerLogInfo(">>> SHUTDOWN the node.\n");
5009*572c4311Sfengbojiang redisReply *r = redisCommand(node->context, "SHUTDOWN");
5010*572c4311Sfengbojiang success = clusterManagerCheckRedisReply(node, r, NULL);
5011*572c4311Sfengbojiang if (r) freeReplyObject(r);
5012*572c4311Sfengbojiang return success;
5013*572c4311Sfengbojiang invalid_args:
5014*572c4311Sfengbojiang fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);
5015*572c4311Sfengbojiang return 0;
5016*572c4311Sfengbojiang }
5017*572c4311Sfengbojiang
clusterManagerCommandInfo(int argc,char ** argv)5018*572c4311Sfengbojiang static int clusterManagerCommandInfo(int argc, char **argv) {
5019*572c4311Sfengbojiang int port = 0;
5020*572c4311Sfengbojiang char *ip = NULL;
5021*572c4311Sfengbojiang if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;
5022*572c4311Sfengbojiang clusterManagerNode *node = clusterManagerNewNode(ip, port);
5023*572c4311Sfengbojiang if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;
5024*572c4311Sfengbojiang clusterManagerShowClusterInfo();
5025*572c4311Sfengbojiang return 1;
5026*572c4311Sfengbojiang invalid_args:
5027*572c4311Sfengbojiang fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);
5028*572c4311Sfengbojiang return 0;
5029*572c4311Sfengbojiang }
5030*572c4311Sfengbojiang
clusterManagerCommandCheck(int argc,char ** argv)5031*572c4311Sfengbojiang static int clusterManagerCommandCheck(int argc, char **argv) {
5032*572c4311Sfengbojiang int port = 0;
5033*572c4311Sfengbojiang char *ip = NULL;
5034*572c4311Sfengbojiang if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;
5035*572c4311Sfengbojiang clusterManagerNode *node = clusterManagerNewNode(ip, port);
5036*572c4311Sfengbojiang if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;
5037*572c4311Sfengbojiang clusterManagerShowClusterInfo();
5038*572c4311Sfengbojiang return clusterManagerCheckCluster(0);
5039*572c4311Sfengbojiang invalid_args:
5040*572c4311Sfengbojiang fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);
5041*572c4311Sfengbojiang return 0;
5042*572c4311Sfengbojiang }
5043*572c4311Sfengbojiang
clusterManagerCommandFix(int argc,char ** argv)5044*572c4311Sfengbojiang static int clusterManagerCommandFix(int argc, char **argv) {
5045*572c4311Sfengbojiang config.cluster_manager_command.flags |= CLUSTER_MANAGER_CMD_FLAG_FIX;
5046*572c4311Sfengbojiang return clusterManagerCommandCheck(argc, argv);
5047*572c4311Sfengbojiang }
5048*572c4311Sfengbojiang
clusterManagerCommandReshard(int argc,char ** argv)5049*572c4311Sfengbojiang static int clusterManagerCommandReshard(int argc, char **argv) {
5050*572c4311Sfengbojiang int port = 0;
5051*572c4311Sfengbojiang char *ip = NULL;
5052*572c4311Sfengbojiang if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;
5053*572c4311Sfengbojiang clusterManagerNode *node = clusterManagerNewNode(ip, port);
5054*572c4311Sfengbojiang if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;
5055*572c4311Sfengbojiang clusterManagerCheckCluster(0);
5056*572c4311Sfengbojiang if (cluster_manager.errors && listLength(cluster_manager.errors) > 0) {
5057*572c4311Sfengbojiang fflush(stdout);
5058*572c4311Sfengbojiang fprintf(stderr,
5059*572c4311Sfengbojiang "*** Please fix your cluster problems before resharding\n");
5060*572c4311Sfengbojiang return 0;
5061*572c4311Sfengbojiang }
5062*572c4311Sfengbojiang int slots = config.cluster_manager_command.slots;
5063*572c4311Sfengbojiang if (!slots) {
5064*572c4311Sfengbojiang while (slots <= 0 || slots > CLUSTER_MANAGER_SLOTS) {
5065*572c4311Sfengbojiang printf("How many slots do you want to move (from 1 to %d)? ",
5066*572c4311Sfengbojiang CLUSTER_MANAGER_SLOTS);
5067*572c4311Sfengbojiang fflush(stdout);
5068*572c4311Sfengbojiang char buf[6];
5069*572c4311Sfengbojiang int nread = read(fileno(stdin),buf,6);
5070*572c4311Sfengbojiang if (nread <= 0) continue;
5071*572c4311Sfengbojiang int last_idx = nread - 1;
5072*572c4311Sfengbojiang if (buf[last_idx] != '\n') {
5073*572c4311Sfengbojiang int ch;
5074*572c4311Sfengbojiang while ((ch = getchar()) != '\n' && ch != EOF) {}
5075*572c4311Sfengbojiang }
5076*572c4311Sfengbojiang buf[last_idx] = '\0';
5077*572c4311Sfengbojiang slots = atoi(buf);
5078*572c4311Sfengbojiang }
5079*572c4311Sfengbojiang }
5080*572c4311Sfengbojiang char buf[255];
5081*572c4311Sfengbojiang char *to = config.cluster_manager_command.to,
5082*572c4311Sfengbojiang *from = config.cluster_manager_command.from;
5083*572c4311Sfengbojiang while (to == NULL) {
5084*572c4311Sfengbojiang printf("What is the receiving node ID? ");
5085*572c4311Sfengbojiang fflush(stdout);
5086*572c4311Sfengbojiang int nread = read(fileno(stdin),buf,255);
5087*572c4311Sfengbojiang if (nread <= 0) continue;
5088*572c4311Sfengbojiang int last_idx = nread - 1;
5089*572c4311Sfengbojiang if (buf[last_idx] != '\n') {
5090*572c4311Sfengbojiang int ch;
5091*572c4311Sfengbojiang while ((ch = getchar()) != '\n' && ch != EOF) {}
5092*572c4311Sfengbojiang }
5093*572c4311Sfengbojiang buf[last_idx] = '\0';
5094*572c4311Sfengbojiang if (strlen(buf) > 0) to = buf;
5095*572c4311Sfengbojiang }
5096*572c4311Sfengbojiang int raise_err = 0;
5097*572c4311Sfengbojiang clusterManagerNode *target = clusterNodeForResharding(to, NULL, &raise_err);
5098*572c4311Sfengbojiang if (target == NULL) return 0;
5099*572c4311Sfengbojiang list *sources = listCreate();
5100*572c4311Sfengbojiang list *table = NULL;
5101*572c4311Sfengbojiang int all = 0, result = 1;
5102*572c4311Sfengbojiang if (from == NULL) {
5103*572c4311Sfengbojiang printf("Please enter all the source node IDs.\n");
5104*572c4311Sfengbojiang printf(" Type 'all' to use all the nodes as source nodes for "
5105*572c4311Sfengbojiang "the hash slots.\n");
5106*572c4311Sfengbojiang printf(" Type 'done' once you entered all the source nodes IDs.\n");
5107*572c4311Sfengbojiang while (1) {
5108*572c4311Sfengbojiang printf("Source node #%lu: ", listLength(sources) + 1);
5109*572c4311Sfengbojiang fflush(stdout);
5110*572c4311Sfengbojiang int nread = read(fileno(stdin),buf,255);
5111*572c4311Sfengbojiang if (nread <= 0) continue;
5112*572c4311Sfengbojiang int last_idx = nread - 1;
5113*572c4311Sfengbojiang if (buf[last_idx] != '\n') {
5114*572c4311Sfengbojiang int ch;
5115*572c4311Sfengbojiang while ((ch = getchar()) != '\n' && ch != EOF) {}
5116*572c4311Sfengbojiang }
5117*572c4311Sfengbojiang buf[last_idx] = '\0';
5118*572c4311Sfengbojiang if (!strcmp(buf, "done")) break;
5119*572c4311Sfengbojiang else if (!strcmp(buf, "all")) {
5120*572c4311Sfengbojiang all = 1;
5121*572c4311Sfengbojiang break;
5122*572c4311Sfengbojiang } else {
5123*572c4311Sfengbojiang clusterManagerNode *src =
5124*572c4311Sfengbojiang clusterNodeForResharding(buf, target, &raise_err);
5125*572c4311Sfengbojiang if (src != NULL) listAddNodeTail(sources, src);
5126*572c4311Sfengbojiang else if (raise_err) {
5127*572c4311Sfengbojiang result = 0;
5128*572c4311Sfengbojiang goto cleanup;
5129*572c4311Sfengbojiang }
5130*572c4311Sfengbojiang }
5131*572c4311Sfengbojiang }
5132*572c4311Sfengbojiang } else {
5133*572c4311Sfengbojiang char *p;
5134*572c4311Sfengbojiang while((p = strchr(from, ',')) != NULL) {
5135*572c4311Sfengbojiang *p = '\0';
5136*572c4311Sfengbojiang if (!strcmp(from, "all")) {
5137*572c4311Sfengbojiang all = 1;
5138*572c4311Sfengbojiang break;
5139*572c4311Sfengbojiang } else {
5140*572c4311Sfengbojiang clusterManagerNode *src =
5141*572c4311Sfengbojiang clusterNodeForResharding(from, target, &raise_err);
5142*572c4311Sfengbojiang if (src != NULL) listAddNodeTail(sources, src);
5143*572c4311Sfengbojiang else if (raise_err) {
5144*572c4311Sfengbojiang result = 0;
5145*572c4311Sfengbojiang goto cleanup;
5146*572c4311Sfengbojiang }
5147*572c4311Sfengbojiang }
5148*572c4311Sfengbojiang from = p + 1;
5149*572c4311Sfengbojiang }
5150*572c4311Sfengbojiang /* Check if there's still another source to process. */
5151*572c4311Sfengbojiang if (!all && strlen(from) > 0) {
5152*572c4311Sfengbojiang if (!strcmp(from, "all")) all = 1;
5153*572c4311Sfengbojiang if (!all) {
5154*572c4311Sfengbojiang clusterManagerNode *src =
5155*572c4311Sfengbojiang clusterNodeForResharding(from, target, &raise_err);
5156*572c4311Sfengbojiang if (src != NULL) listAddNodeTail(sources, src);
5157*572c4311Sfengbojiang else if (raise_err) {
5158*572c4311Sfengbojiang result = 0;
5159*572c4311Sfengbojiang goto cleanup;
5160*572c4311Sfengbojiang }
5161*572c4311Sfengbojiang }
5162*572c4311Sfengbojiang }
5163*572c4311Sfengbojiang }
5164*572c4311Sfengbojiang listIter li;
5165*572c4311Sfengbojiang listNode *ln;
5166*572c4311Sfengbojiang if (all) {
5167*572c4311Sfengbojiang listEmpty(sources);
5168*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
5169*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
5170*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
5171*572c4311Sfengbojiang if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE || n->replicate)
5172*572c4311Sfengbojiang continue;
5173*572c4311Sfengbojiang if (!sdscmp(n->name, target->name)) continue;
5174*572c4311Sfengbojiang listAddNodeTail(sources, n);
5175*572c4311Sfengbojiang }
5176*572c4311Sfengbojiang }
5177*572c4311Sfengbojiang if (listLength(sources) == 0) {
5178*572c4311Sfengbojiang fprintf(stderr, "*** No source nodes given, operation aborted.\n");
5179*572c4311Sfengbojiang result = 0;
5180*572c4311Sfengbojiang goto cleanup;
5181*572c4311Sfengbojiang }
5182*572c4311Sfengbojiang printf("\nReady to move %d slots.\n", slots);
5183*572c4311Sfengbojiang printf(" Source nodes:\n");
5184*572c4311Sfengbojiang listRewind(sources, &li);
5185*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
5186*572c4311Sfengbojiang clusterManagerNode *src = ln->value;
5187*572c4311Sfengbojiang sds info = clusterManagerNodeInfo(src, 4);
5188*572c4311Sfengbojiang printf("%s\n", info);
5189*572c4311Sfengbojiang sdsfree(info);
5190*572c4311Sfengbojiang }
5191*572c4311Sfengbojiang printf(" Destination node:\n");
5192*572c4311Sfengbojiang sds info = clusterManagerNodeInfo(target, 4);
5193*572c4311Sfengbojiang printf("%s\n", info);
5194*572c4311Sfengbojiang sdsfree(info);
5195*572c4311Sfengbojiang table = clusterManagerComputeReshardTable(sources, slots);
5196*572c4311Sfengbojiang printf(" Resharding plan:\n");
5197*572c4311Sfengbojiang clusterManagerShowReshardTable(table);
5198*572c4311Sfengbojiang if (!(config.cluster_manager_command.flags &
5199*572c4311Sfengbojiang CLUSTER_MANAGER_CMD_FLAG_YES))
5200*572c4311Sfengbojiang {
5201*572c4311Sfengbojiang printf("Do you want to proceed with the proposed "
5202*572c4311Sfengbojiang "reshard plan (yes/no)? ");
5203*572c4311Sfengbojiang fflush(stdout);
5204*572c4311Sfengbojiang char buf[4];
5205*572c4311Sfengbojiang int nread = read(fileno(stdin),buf,4);
5206*572c4311Sfengbojiang buf[3] = '\0';
5207*572c4311Sfengbojiang if (nread <= 0 || strcmp("yes", buf) != 0) {
5208*572c4311Sfengbojiang result = 0;
5209*572c4311Sfengbojiang goto cleanup;
5210*572c4311Sfengbojiang }
5211*572c4311Sfengbojiang }
5212*572c4311Sfengbojiang int opts = CLUSTER_MANAGER_OPT_VERBOSE;
5213*572c4311Sfengbojiang listRewind(table, &li);
5214*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
5215*572c4311Sfengbojiang clusterManagerReshardTableItem *item = ln->value;
5216*572c4311Sfengbojiang char *err = NULL;
5217*572c4311Sfengbojiang result = clusterManagerMoveSlot(item->source, target, item->slot,
5218*572c4311Sfengbojiang opts, &err);
5219*572c4311Sfengbojiang if (!result) {
5220*572c4311Sfengbojiang if (err != NULL) {
5221*572c4311Sfengbojiang //clusterManagerLogErr("\n%s\n", err);
5222*572c4311Sfengbojiang zfree(err);
5223*572c4311Sfengbojiang }
5224*572c4311Sfengbojiang goto cleanup;
5225*572c4311Sfengbojiang }
5226*572c4311Sfengbojiang }
5227*572c4311Sfengbojiang cleanup:
5228*572c4311Sfengbojiang listRelease(sources);
5229*572c4311Sfengbojiang clusterManagerReleaseReshardTable(table);
5230*572c4311Sfengbojiang return result;
5231*572c4311Sfengbojiang invalid_args:
5232*572c4311Sfengbojiang fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);
5233*572c4311Sfengbojiang return 0;
5234*572c4311Sfengbojiang }
5235*572c4311Sfengbojiang
clusterManagerCommandRebalance(int argc,char ** argv)5236*572c4311Sfengbojiang static int clusterManagerCommandRebalance(int argc, char **argv) {
5237*572c4311Sfengbojiang int port = 0;
5238*572c4311Sfengbojiang char *ip = NULL;
5239*572c4311Sfengbojiang clusterManagerNode **weightedNodes = NULL;
5240*572c4311Sfengbojiang list *involved = NULL;
5241*572c4311Sfengbojiang if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;
5242*572c4311Sfengbojiang clusterManagerNode *node = clusterManagerNewNode(ip, port);
5243*572c4311Sfengbojiang if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;
5244*572c4311Sfengbojiang int result = 1, i;
5245*572c4311Sfengbojiang if (config.cluster_manager_command.weight != NULL) {
5246*572c4311Sfengbojiang for (i = 0; i < config.cluster_manager_command.weight_argc; i++) {
5247*572c4311Sfengbojiang char *name = config.cluster_manager_command.weight[i];
5248*572c4311Sfengbojiang char *p = strchr(name, '=');
5249*572c4311Sfengbojiang if (p == NULL) {
5250*572c4311Sfengbojiang result = 0;
5251*572c4311Sfengbojiang goto cleanup;
5252*572c4311Sfengbojiang }
5253*572c4311Sfengbojiang *p = '\0';
5254*572c4311Sfengbojiang float w = atof(++p);
5255*572c4311Sfengbojiang clusterManagerNode *n = clusterManagerNodeByAbbreviatedName(name);
5256*572c4311Sfengbojiang if (n == NULL) {
5257*572c4311Sfengbojiang clusterManagerLogErr("*** No such master node %s\n", name);
5258*572c4311Sfengbojiang result = 0;
5259*572c4311Sfengbojiang goto cleanup;
5260*572c4311Sfengbojiang }
5261*572c4311Sfengbojiang n->weight = w;
5262*572c4311Sfengbojiang }
5263*572c4311Sfengbojiang }
5264*572c4311Sfengbojiang float total_weight = 0;
5265*572c4311Sfengbojiang int nodes_involved = 0;
5266*572c4311Sfengbojiang int use_empty = config.cluster_manager_command.flags &
5267*572c4311Sfengbojiang CLUSTER_MANAGER_CMD_FLAG_EMPTYMASTER;
5268*572c4311Sfengbojiang involved = listCreate();
5269*572c4311Sfengbojiang listIter li;
5270*572c4311Sfengbojiang listNode *ln;
5271*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
5272*572c4311Sfengbojiang /* Compute the total cluster weight. */
5273*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
5274*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
5275*572c4311Sfengbojiang if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE || n->replicate)
5276*572c4311Sfengbojiang continue;
5277*572c4311Sfengbojiang if (!use_empty && n->slots_count == 0) {
5278*572c4311Sfengbojiang n->weight = 0;
5279*572c4311Sfengbojiang continue;
5280*572c4311Sfengbojiang }
5281*572c4311Sfengbojiang total_weight += n->weight;
5282*572c4311Sfengbojiang nodes_involved++;
5283*572c4311Sfengbojiang listAddNodeTail(involved, n);
5284*572c4311Sfengbojiang }
5285*572c4311Sfengbojiang weightedNodes = zmalloc(nodes_involved * sizeof(clusterManagerNode *));
5286*572c4311Sfengbojiang if (weightedNodes == NULL) goto cleanup;
5287*572c4311Sfengbojiang /* Check cluster, only proceed if it looks sane. */
5288*572c4311Sfengbojiang clusterManagerCheckCluster(1);
5289*572c4311Sfengbojiang if (cluster_manager.errors && listLength(cluster_manager.errors) > 0) {
5290*572c4311Sfengbojiang clusterManagerLogErr("*** Please fix your cluster problems "
5291*572c4311Sfengbojiang "before rebalancing\n");
5292*572c4311Sfengbojiang result = 0;
5293*572c4311Sfengbojiang goto cleanup;
5294*572c4311Sfengbojiang }
5295*572c4311Sfengbojiang /* Calculate the slots balance for each node. It's the number of
5296*572c4311Sfengbojiang * slots the node should lose (if positive) or gain (if negative)
5297*572c4311Sfengbojiang * in order to be balanced. */
5298*572c4311Sfengbojiang int threshold_reached = 0, total_balance = 0;
5299*572c4311Sfengbojiang float threshold = config.cluster_manager_command.threshold;
5300*572c4311Sfengbojiang i = 0;
5301*572c4311Sfengbojiang listRewind(involved, &li);
5302*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
5303*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
5304*572c4311Sfengbojiang weightedNodes[i++] = n;
5305*572c4311Sfengbojiang int expected = (int) (((float)CLUSTER_MANAGER_SLOTS / total_weight) *
5306*572c4311Sfengbojiang n->weight);
5307*572c4311Sfengbojiang n->balance = n->slots_count - expected;
5308*572c4311Sfengbojiang total_balance += n->balance;
5309*572c4311Sfengbojiang /* Compute the percentage of difference between the
5310*572c4311Sfengbojiang * expected number of slots and the real one, to see
5311*572c4311Sfengbojiang * if it's over the threshold specified by the user. */
5312*572c4311Sfengbojiang int over_threshold = 0;
5313*572c4311Sfengbojiang if (threshold > 0) {
5314*572c4311Sfengbojiang if (n->slots_count > 0) {
5315*572c4311Sfengbojiang float err_perc = fabs((100-(100.0*expected/n->slots_count)));
5316*572c4311Sfengbojiang if (err_perc > threshold) over_threshold = 1;
5317*572c4311Sfengbojiang } else if (expected > 1) {
5318*572c4311Sfengbojiang over_threshold = 1;
5319*572c4311Sfengbojiang }
5320*572c4311Sfengbojiang }
5321*572c4311Sfengbojiang if (over_threshold) threshold_reached = 1;
5322*572c4311Sfengbojiang }
5323*572c4311Sfengbojiang if (!threshold_reached) {
5324*572c4311Sfengbojiang clusterManagerLogWarn("*** No rebalancing needed! "
5325*572c4311Sfengbojiang "All nodes are within the %.2f%% threshold.\n",
5326*572c4311Sfengbojiang config.cluster_manager_command.threshold);
5327*572c4311Sfengbojiang goto cleanup;
5328*572c4311Sfengbojiang }
5329*572c4311Sfengbojiang /* Because of rounding, it is possible that the balance of all nodes
5330*572c4311Sfengbojiang * summed does not give 0. Make sure that nodes that have to provide
5331*572c4311Sfengbojiang * slots are always matched by nodes receiving slots. */
5332*572c4311Sfengbojiang while (total_balance > 0) {
5333*572c4311Sfengbojiang listRewind(involved, &li);
5334*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
5335*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
5336*572c4311Sfengbojiang if (n->balance <= 0 && total_balance > 0) {
5337*572c4311Sfengbojiang n->balance--;
5338*572c4311Sfengbojiang total_balance--;
5339*572c4311Sfengbojiang }
5340*572c4311Sfengbojiang }
5341*572c4311Sfengbojiang }
5342*572c4311Sfengbojiang /* Sort nodes by their slots balance. */
5343*572c4311Sfengbojiang qsort(weightedNodes, nodes_involved, sizeof(clusterManagerNode *),
5344*572c4311Sfengbojiang clusterManagerCompareNodeBalance);
5345*572c4311Sfengbojiang clusterManagerLogInfo(">>> Rebalancing across %d nodes. "
5346*572c4311Sfengbojiang "Total weight = %.2f\n",
5347*572c4311Sfengbojiang nodes_involved, total_weight);
5348*572c4311Sfengbojiang if (config.verbose) {
5349*572c4311Sfengbojiang for (i = 0; i < nodes_involved; i++) {
5350*572c4311Sfengbojiang clusterManagerNode *n = weightedNodes[i];
5351*572c4311Sfengbojiang printf("%s:%d balance is %d slots\n", n->ip, n->port, n->balance);
5352*572c4311Sfengbojiang }
5353*572c4311Sfengbojiang }
5354*572c4311Sfengbojiang /* Now we have at the start of the 'sn' array nodes that should get
5355*572c4311Sfengbojiang * slots, at the end nodes that must give slots.
5356*572c4311Sfengbojiang * We take two indexes, one at the start, and one at the end,
5357*572c4311Sfengbojiang * incrementing or decrementing the indexes accordingly til we
5358*572c4311Sfengbojiang * find nodes that need to get/provide slots. */
5359*572c4311Sfengbojiang int dst_idx = 0;
5360*572c4311Sfengbojiang int src_idx = nodes_involved - 1;
5361*572c4311Sfengbojiang int simulate = config.cluster_manager_command.flags &
5362*572c4311Sfengbojiang CLUSTER_MANAGER_CMD_FLAG_SIMULATE;
5363*572c4311Sfengbojiang while (dst_idx < src_idx) {
5364*572c4311Sfengbojiang clusterManagerNode *dst = weightedNodes[dst_idx];
5365*572c4311Sfengbojiang clusterManagerNode *src = weightedNodes[src_idx];
5366*572c4311Sfengbojiang int db = abs(dst->balance);
5367*572c4311Sfengbojiang int sb = abs(src->balance);
5368*572c4311Sfengbojiang int numslots = (db < sb ? db : sb);
5369*572c4311Sfengbojiang if (numslots > 0) {
5370*572c4311Sfengbojiang printf("Moving %d slots from %s:%d to %s:%d\n", numslots,
5371*572c4311Sfengbojiang src->ip,
5372*572c4311Sfengbojiang src->port,
5373*572c4311Sfengbojiang dst->ip,
5374*572c4311Sfengbojiang dst->port);
5375*572c4311Sfengbojiang /* Actually move the slots. */
5376*572c4311Sfengbojiang list *lsrc = listCreate(), *table = NULL;
5377*572c4311Sfengbojiang listAddNodeTail(lsrc, src);
5378*572c4311Sfengbojiang table = clusterManagerComputeReshardTable(lsrc, numslots);
5379*572c4311Sfengbojiang listRelease(lsrc);
5380*572c4311Sfengbojiang int table_len = (int) listLength(table);
5381*572c4311Sfengbojiang if (!table || table_len != numslots) {
5382*572c4311Sfengbojiang clusterManagerLogErr("*** Assertion failed: Reshard table "
5383*572c4311Sfengbojiang "!= number of slots");
5384*572c4311Sfengbojiang result = 0;
5385*572c4311Sfengbojiang goto end_move;
5386*572c4311Sfengbojiang }
5387*572c4311Sfengbojiang if (simulate) {
5388*572c4311Sfengbojiang for (i = 0; i < table_len; i++) printf("#");
5389*572c4311Sfengbojiang } else {
5390*572c4311Sfengbojiang int opts = CLUSTER_MANAGER_OPT_QUIET |
5391*572c4311Sfengbojiang CLUSTER_MANAGER_OPT_UPDATE;
5392*572c4311Sfengbojiang listRewind(table, &li);
5393*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
5394*572c4311Sfengbojiang clusterManagerReshardTableItem *item = ln->value;
5395*572c4311Sfengbojiang result = clusterManagerMoveSlot(item->source,
5396*572c4311Sfengbojiang dst,
5397*572c4311Sfengbojiang item->slot,
5398*572c4311Sfengbojiang opts, NULL);
5399*572c4311Sfengbojiang if (!result) goto end_move;
5400*572c4311Sfengbojiang printf("#");
5401*572c4311Sfengbojiang fflush(stdout);
5402*572c4311Sfengbojiang }
5403*572c4311Sfengbojiang
5404*572c4311Sfengbojiang }
5405*572c4311Sfengbojiang printf("\n");
5406*572c4311Sfengbojiang end_move:
5407*572c4311Sfengbojiang clusterManagerReleaseReshardTable(table);
5408*572c4311Sfengbojiang if (!result) goto cleanup;
5409*572c4311Sfengbojiang }
5410*572c4311Sfengbojiang /* Update nodes balance. */
5411*572c4311Sfengbojiang dst->balance += numslots;
5412*572c4311Sfengbojiang src->balance -= numslots;
5413*572c4311Sfengbojiang if (dst->balance == 0) dst_idx++;
5414*572c4311Sfengbojiang if (src->balance == 0) src_idx --;
5415*572c4311Sfengbojiang }
5416*572c4311Sfengbojiang cleanup:
5417*572c4311Sfengbojiang if (involved != NULL) listRelease(involved);
5418*572c4311Sfengbojiang if (weightedNodes != NULL) zfree(weightedNodes);
5419*572c4311Sfengbojiang return result;
5420*572c4311Sfengbojiang invalid_args:
5421*572c4311Sfengbojiang fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);
5422*572c4311Sfengbojiang return 0;
5423*572c4311Sfengbojiang }
5424*572c4311Sfengbojiang
clusterManagerCommandSetTimeout(int argc,char ** argv)5425*572c4311Sfengbojiang static int clusterManagerCommandSetTimeout(int argc, char **argv) {
5426*572c4311Sfengbojiang UNUSED(argc);
5427*572c4311Sfengbojiang int port = 0;
5428*572c4311Sfengbojiang char *ip = NULL;
5429*572c4311Sfengbojiang if (!getClusterHostFromCmdArgs(1, argv, &ip, &port)) goto invalid_args;
5430*572c4311Sfengbojiang int timeout = atoi(argv[1]);
5431*572c4311Sfengbojiang if (timeout < 100) {
5432*572c4311Sfengbojiang fprintf(stderr, "Setting a node timeout of less than 100 "
5433*572c4311Sfengbojiang "milliseconds is a bad idea.\n");
5434*572c4311Sfengbojiang return 0;
5435*572c4311Sfengbojiang }
5436*572c4311Sfengbojiang // Load cluster information
5437*572c4311Sfengbojiang clusterManagerNode *node = clusterManagerNewNode(ip, port);
5438*572c4311Sfengbojiang if (!clusterManagerLoadInfoFromNode(node, 0)) return 0;
5439*572c4311Sfengbojiang int ok_count = 0, err_count = 0;
5440*572c4311Sfengbojiang
5441*572c4311Sfengbojiang clusterManagerLogInfo(">>> Reconfiguring node timeout in every "
5442*572c4311Sfengbojiang "cluster node...\n");
5443*572c4311Sfengbojiang listIter li;
5444*572c4311Sfengbojiang listNode *ln;
5445*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
5446*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
5447*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
5448*572c4311Sfengbojiang char *err = NULL;
5449*572c4311Sfengbojiang redisReply *reply = CLUSTER_MANAGER_COMMAND(n, "CONFIG %s %s %d",
5450*572c4311Sfengbojiang "SET",
5451*572c4311Sfengbojiang "cluster-node-timeout",
5452*572c4311Sfengbojiang timeout);
5453*572c4311Sfengbojiang if (reply == NULL) goto reply_err;
5454*572c4311Sfengbojiang int ok = clusterManagerCheckRedisReply(n, reply, &err);
5455*572c4311Sfengbojiang freeReplyObject(reply);
5456*572c4311Sfengbojiang if (!ok) goto reply_err;
5457*572c4311Sfengbojiang reply = CLUSTER_MANAGER_COMMAND(n, "CONFIG %s", "REWRITE");
5458*572c4311Sfengbojiang if (reply == NULL) goto reply_err;
5459*572c4311Sfengbojiang ok = clusterManagerCheckRedisReply(n, reply, &err);
5460*572c4311Sfengbojiang freeReplyObject(reply);
5461*572c4311Sfengbojiang if (!ok) goto reply_err;
5462*572c4311Sfengbojiang clusterManagerLogWarn("*** New timeout set for %s:%d\n", n->ip,
5463*572c4311Sfengbojiang n->port);
5464*572c4311Sfengbojiang ok_count++;
5465*572c4311Sfengbojiang continue;
5466*572c4311Sfengbojiang reply_err:;
5467*572c4311Sfengbojiang int need_free = 0;
5468*572c4311Sfengbojiang if (err == NULL) err = "";
5469*572c4311Sfengbojiang else need_free = 1;
5470*572c4311Sfengbojiang clusterManagerLogErr("ERR setting node-timeot for %s:%d: %s\n", n->ip,
5471*572c4311Sfengbojiang n->port, err);
5472*572c4311Sfengbojiang if (need_free) zfree(err);
5473*572c4311Sfengbojiang err_count++;
5474*572c4311Sfengbojiang }
5475*572c4311Sfengbojiang clusterManagerLogInfo(">>> New node timeout set. %d OK, %d ERR.\n",
5476*572c4311Sfengbojiang ok_count, err_count);
5477*572c4311Sfengbojiang return 1;
5478*572c4311Sfengbojiang invalid_args:
5479*572c4311Sfengbojiang fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);
5480*572c4311Sfengbojiang return 0;
5481*572c4311Sfengbojiang }
5482*572c4311Sfengbojiang
clusterManagerCommandImport(int argc,char ** argv)5483*572c4311Sfengbojiang static int clusterManagerCommandImport(int argc, char **argv) {
5484*572c4311Sfengbojiang int success = 1;
5485*572c4311Sfengbojiang int port = 0, src_port = 0;
5486*572c4311Sfengbojiang char *ip = NULL, *src_ip = NULL;
5487*572c4311Sfengbojiang char *invalid_args_msg = NULL;
5488*572c4311Sfengbojiang if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) {
5489*572c4311Sfengbojiang invalid_args_msg = CLUSTER_MANAGER_INVALID_HOST_ARG;
5490*572c4311Sfengbojiang goto invalid_args;
5491*572c4311Sfengbojiang }
5492*572c4311Sfengbojiang if (config.cluster_manager_command.from == NULL) {
5493*572c4311Sfengbojiang invalid_args_msg = "[ERR] Option '--cluster-from' is required for "
5494*572c4311Sfengbojiang "subcommand 'import'.\n";
5495*572c4311Sfengbojiang goto invalid_args;
5496*572c4311Sfengbojiang }
5497*572c4311Sfengbojiang char *src_host[] = {config.cluster_manager_command.from};
5498*572c4311Sfengbojiang if (!getClusterHostFromCmdArgs(1, src_host, &src_ip, &src_port)) {
5499*572c4311Sfengbojiang invalid_args_msg = "[ERR] Invalid --cluster-from host. You need to "
5500*572c4311Sfengbojiang "pass a valid address (ie. 120.0.0.1:7000).\n";
5501*572c4311Sfengbojiang goto invalid_args;
5502*572c4311Sfengbojiang }
5503*572c4311Sfengbojiang clusterManagerLogInfo(">>> Importing data from %s:%d to cluster %s:%d\n",
5504*572c4311Sfengbojiang src_ip, src_port, ip, port);
5505*572c4311Sfengbojiang
5506*572c4311Sfengbojiang clusterManagerNode *refnode = clusterManagerNewNode(ip, port);
5507*572c4311Sfengbojiang if (!clusterManagerLoadInfoFromNode(refnode, 0)) return 0;
5508*572c4311Sfengbojiang if (!clusterManagerCheckCluster(0)) return 0;
5509*572c4311Sfengbojiang char *reply_err = NULL;
5510*572c4311Sfengbojiang redisReply *src_reply = NULL;
5511*572c4311Sfengbojiang // Connect to the source node.
5512*572c4311Sfengbojiang redisContext *src_ctx = redisConnect(src_ip, src_port);
5513*572c4311Sfengbojiang if (src_ctx->err) {
5514*572c4311Sfengbojiang success = 0;
5515*572c4311Sfengbojiang fprintf(stderr,"Could not connect to Redis at %s:%d: %s.\n", src_ip,
5516*572c4311Sfengbojiang src_port, src_ctx->errstr);
5517*572c4311Sfengbojiang goto cleanup;
5518*572c4311Sfengbojiang }
5519*572c4311Sfengbojiang src_reply = reconnectingRedisCommand(src_ctx, "INFO");
5520*572c4311Sfengbojiang if (!src_reply || src_reply->type == REDIS_REPLY_ERROR) {
5521*572c4311Sfengbojiang if (src_reply && src_reply->str) reply_err = src_reply->str;
5522*572c4311Sfengbojiang success = 0;
5523*572c4311Sfengbojiang goto cleanup;
5524*572c4311Sfengbojiang }
5525*572c4311Sfengbojiang if (getLongInfoField(src_reply->str, "cluster_enabled")) {
5526*572c4311Sfengbojiang clusterManagerLogErr("[ERR] The source node should not be a "
5527*572c4311Sfengbojiang "cluster node.\n");
5528*572c4311Sfengbojiang success = 0;
5529*572c4311Sfengbojiang goto cleanup;
5530*572c4311Sfengbojiang }
5531*572c4311Sfengbojiang freeReplyObject(src_reply);
5532*572c4311Sfengbojiang src_reply = reconnectingRedisCommand(src_ctx, "DBSIZE");
5533*572c4311Sfengbojiang if (!src_reply || src_reply->type == REDIS_REPLY_ERROR) {
5534*572c4311Sfengbojiang if (src_reply && src_reply->str) reply_err = src_reply->str;
5535*572c4311Sfengbojiang success = 0;
5536*572c4311Sfengbojiang goto cleanup;
5537*572c4311Sfengbojiang }
5538*572c4311Sfengbojiang int size = src_reply->integer, i;
5539*572c4311Sfengbojiang clusterManagerLogWarn("*** Importing %d keys from DB 0\n", size);
5540*572c4311Sfengbojiang
5541*572c4311Sfengbojiang // Build a slot -> node map
5542*572c4311Sfengbojiang clusterManagerNode *slots_map[CLUSTER_MANAGER_SLOTS];
5543*572c4311Sfengbojiang memset(slots_map, 0, sizeof(slots_map));
5544*572c4311Sfengbojiang listIter li;
5545*572c4311Sfengbojiang listNode *ln;
5546*572c4311Sfengbojiang for (i = 0; i < CLUSTER_MANAGER_SLOTS; i++) {
5547*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
5548*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
5549*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
5550*572c4311Sfengbojiang if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;
5551*572c4311Sfengbojiang if (n->slots_count == 0) continue;
5552*572c4311Sfengbojiang if (n->slots[i]) {
5553*572c4311Sfengbojiang slots_map[i] = n;
5554*572c4311Sfengbojiang break;
5555*572c4311Sfengbojiang }
5556*572c4311Sfengbojiang }
5557*572c4311Sfengbojiang }
5558*572c4311Sfengbojiang
5559*572c4311Sfengbojiang char cmdfmt[50] = "MIGRATE %s %d %s %d %d";
5560*572c4311Sfengbojiang if (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_COPY)
5561*572c4311Sfengbojiang strcat(cmdfmt, " %s");
5562*572c4311Sfengbojiang if (config.cluster_manager_command.flags & CLUSTER_MANAGER_CMD_FLAG_REPLACE)
5563*572c4311Sfengbojiang strcat(cmdfmt, " %s");
5564*572c4311Sfengbojiang
5565*572c4311Sfengbojiang /* Use SCAN to iterate over the keys, migrating to the
5566*572c4311Sfengbojiang * right node as needed. */
5567*572c4311Sfengbojiang int cursor = -999, timeout = config.cluster_manager_command.timeout;
5568*572c4311Sfengbojiang while (cursor != 0) {
5569*572c4311Sfengbojiang if (cursor < 0) cursor = 0;
5570*572c4311Sfengbojiang freeReplyObject(src_reply);
5571*572c4311Sfengbojiang src_reply = reconnectingRedisCommand(src_ctx, "SCAN %d COUNT %d",
5572*572c4311Sfengbojiang cursor, 1000);
5573*572c4311Sfengbojiang if (!src_reply || src_reply->type == REDIS_REPLY_ERROR) {
5574*572c4311Sfengbojiang if (src_reply && src_reply->str) reply_err = src_reply->str;
5575*572c4311Sfengbojiang success = 0;
5576*572c4311Sfengbojiang goto cleanup;
5577*572c4311Sfengbojiang }
5578*572c4311Sfengbojiang assert(src_reply->type == REDIS_REPLY_ARRAY);
5579*572c4311Sfengbojiang assert(src_reply->elements >= 2);
5580*572c4311Sfengbojiang assert(src_reply->element[1]->type == REDIS_REPLY_ARRAY);
5581*572c4311Sfengbojiang if (src_reply->element[0]->type == REDIS_REPLY_STRING)
5582*572c4311Sfengbojiang cursor = atoi(src_reply->element[0]->str);
5583*572c4311Sfengbojiang else if (src_reply->element[0]->type == REDIS_REPLY_INTEGER)
5584*572c4311Sfengbojiang cursor = src_reply->element[0]->integer;
5585*572c4311Sfengbojiang int keycount = src_reply->element[1]->elements;
5586*572c4311Sfengbojiang for (i = 0; i < keycount; i++) {
5587*572c4311Sfengbojiang redisReply *kr = src_reply->element[1]->element[i];
5588*572c4311Sfengbojiang assert(kr->type == REDIS_REPLY_STRING);
5589*572c4311Sfengbojiang char *key = kr->str;
5590*572c4311Sfengbojiang uint16_t slot = clusterManagerKeyHashSlot(key, kr->len);
5591*572c4311Sfengbojiang clusterManagerNode *target = slots_map[slot];
5592*572c4311Sfengbojiang printf("Migrating %s to %s:%d: ", key, target->ip, target->port);
5593*572c4311Sfengbojiang redisReply *r = reconnectingRedisCommand(src_ctx, cmdfmt,
5594*572c4311Sfengbojiang target->ip, target->port,
5595*572c4311Sfengbojiang key, 0, timeout,
5596*572c4311Sfengbojiang "COPY", "REPLACE");
5597*572c4311Sfengbojiang if (!r || r->type == REDIS_REPLY_ERROR) {
5598*572c4311Sfengbojiang if (r && r->str) {
5599*572c4311Sfengbojiang clusterManagerLogErr("Source %s:%d replied with "
5600*572c4311Sfengbojiang "error:\n%s\n", src_ip, src_port,
5601*572c4311Sfengbojiang r->str);
5602*572c4311Sfengbojiang }
5603*572c4311Sfengbojiang success = 0;
5604*572c4311Sfengbojiang }
5605*572c4311Sfengbojiang freeReplyObject(r);
5606*572c4311Sfengbojiang if (!success) goto cleanup;
5607*572c4311Sfengbojiang clusterManagerLogOk("OK\n");
5608*572c4311Sfengbojiang }
5609*572c4311Sfengbojiang }
5610*572c4311Sfengbojiang cleanup:
5611*572c4311Sfengbojiang if (reply_err)
5612*572c4311Sfengbojiang clusterManagerLogErr("Source %s:%d replied with error:\n%s\n",
5613*572c4311Sfengbojiang src_ip, src_port, reply_err);
5614*572c4311Sfengbojiang if (src_ctx) redisFree(src_ctx);
5615*572c4311Sfengbojiang if (src_reply) freeReplyObject(src_reply);
5616*572c4311Sfengbojiang return success;
5617*572c4311Sfengbojiang invalid_args:
5618*572c4311Sfengbojiang fprintf(stderr, "%s", invalid_args_msg);
5619*572c4311Sfengbojiang return 0;
5620*572c4311Sfengbojiang }
5621*572c4311Sfengbojiang
clusterManagerCommandCall(int argc,char ** argv)5622*572c4311Sfengbojiang static int clusterManagerCommandCall(int argc, char **argv) {
5623*572c4311Sfengbojiang int port = 0, i;
5624*572c4311Sfengbojiang char *ip = NULL;
5625*572c4311Sfengbojiang if (!getClusterHostFromCmdArgs(1, argv, &ip, &port)) goto invalid_args;
5626*572c4311Sfengbojiang clusterManagerNode *refnode = clusterManagerNewNode(ip, port);
5627*572c4311Sfengbojiang if (!clusterManagerLoadInfoFromNode(refnode, 0)) return 0;
5628*572c4311Sfengbojiang argc--;
5629*572c4311Sfengbojiang argv++;
5630*572c4311Sfengbojiang size_t *argvlen = zmalloc(argc*sizeof(size_t));
5631*572c4311Sfengbojiang clusterManagerLogInfo(">>> Calling");
5632*572c4311Sfengbojiang for (i = 0; i < argc; i++) {
5633*572c4311Sfengbojiang argvlen[i] = strlen(argv[i]);
5634*572c4311Sfengbojiang printf(" %s", argv[i]);
5635*572c4311Sfengbojiang }
5636*572c4311Sfengbojiang printf("\n");
5637*572c4311Sfengbojiang listIter li;
5638*572c4311Sfengbojiang listNode *ln;
5639*572c4311Sfengbojiang listRewind(cluster_manager.nodes, &li);
5640*572c4311Sfengbojiang while ((ln = listNext(&li)) != NULL) {
5641*572c4311Sfengbojiang clusterManagerNode *n = ln->value;
5642*572c4311Sfengbojiang if (!n->context && !clusterManagerNodeConnect(n)) continue;
5643*572c4311Sfengbojiang redisReply *reply = NULL;
5644*572c4311Sfengbojiang redisAppendCommandArgv(n->context, argc, (const char **) argv, argvlen);
5645*572c4311Sfengbojiang int status = redisGetReply(n->context, (void **)(&reply));
5646*572c4311Sfengbojiang if (status != REDIS_OK || reply == NULL )
5647*572c4311Sfengbojiang printf("%s:%d: Failed!\n", n->ip, n->port);
5648*572c4311Sfengbojiang else {
5649*572c4311Sfengbojiang sds formatted_reply = cliFormatReplyRaw(reply);
5650*572c4311Sfengbojiang printf("%s:%d: %s\n", n->ip, n->port, (char *) formatted_reply);
5651*572c4311Sfengbojiang sdsfree(formatted_reply);
5652*572c4311Sfengbojiang }
5653*572c4311Sfengbojiang if (reply != NULL) freeReplyObject(reply);
5654*572c4311Sfengbojiang }
5655*572c4311Sfengbojiang zfree(argvlen);
5656*572c4311Sfengbojiang return 1;
5657*572c4311Sfengbojiang invalid_args:
5658*572c4311Sfengbojiang fprintf(stderr, CLUSTER_MANAGER_INVALID_HOST_ARG);
5659*572c4311Sfengbojiang return 0;
5660*572c4311Sfengbojiang }
5661*572c4311Sfengbojiang
clusterManagerCommandHelp(int argc,char ** argv)5662*572c4311Sfengbojiang static int clusterManagerCommandHelp(int argc, char **argv) {
5663*572c4311Sfengbojiang UNUSED(argc);
5664*572c4311Sfengbojiang UNUSED(argv);
5665*572c4311Sfengbojiang int commands_count = sizeof(clusterManagerCommands) /
5666*572c4311Sfengbojiang sizeof(clusterManagerCommandDef);
5667*572c4311Sfengbojiang int i = 0, j;
5668*572c4311Sfengbojiang fprintf(stderr, "Cluster Manager Commands:\n");
5669*572c4311Sfengbojiang int padding = 15;
5670*572c4311Sfengbojiang for (; i < commands_count; i++) {
5671*572c4311Sfengbojiang clusterManagerCommandDef *def = &(clusterManagerCommands[i]);
5672*572c4311Sfengbojiang int namelen = strlen(def->name), padlen = padding - namelen;
5673*572c4311Sfengbojiang fprintf(stderr, " %s", def->name);
5674*572c4311Sfengbojiang for (j = 0; j < padlen; j++) fprintf(stderr, " ");
5675*572c4311Sfengbojiang fprintf(stderr, "%s\n", (def->args ? def->args : ""));
5676*572c4311Sfengbojiang if (def->options != NULL) {
5677*572c4311Sfengbojiang int optslen = strlen(def->options);
5678*572c4311Sfengbojiang char *p = def->options, *eos = p + optslen;
5679*572c4311Sfengbojiang char *comma = NULL;
5680*572c4311Sfengbojiang while ((comma = strchr(p, ',')) != NULL) {
5681*572c4311Sfengbojiang int deflen = (int)(comma - p);
5682*572c4311Sfengbojiang char buf[255];
5683*572c4311Sfengbojiang memcpy(buf, p, deflen);
5684*572c4311Sfengbojiang buf[deflen] = '\0';
5685*572c4311Sfengbojiang for (j = 0; j < padding; j++) fprintf(stderr, " ");
5686*572c4311Sfengbojiang fprintf(stderr, " --cluster-%s\n", buf);
5687*572c4311Sfengbojiang p = comma + 1;
5688*572c4311Sfengbojiang if (p >= eos) break;
5689*572c4311Sfengbojiang }
5690*572c4311Sfengbojiang if (p < eos) {
5691*572c4311Sfengbojiang for (j = 0; j < padding; j++) fprintf(stderr, " ");
5692*572c4311Sfengbojiang fprintf(stderr, " --cluster-%s\n", p);
5693*572c4311Sfengbojiang }
5694*572c4311Sfengbojiang }
5695*572c4311Sfengbojiang }
5696*572c4311Sfengbojiang fprintf(stderr, "\nFor check, fix, reshard, del-node, set-timeout you "
5697*572c4311Sfengbojiang "can specify the host and port of any working node in "
5698*572c4311Sfengbojiang "the cluster.\n\n");
5699*572c4311Sfengbojiang return 0;
5700*572c4311Sfengbojiang }
5701*572c4311Sfengbojiang
5702*572c4311Sfengbojiang /*------------------------------------------------------------------------------
5703*572c4311Sfengbojiang * Latency and latency history modes
5704*572c4311Sfengbojiang *--------------------------------------------------------------------------- */
5705*572c4311Sfengbojiang
latencyModePrint(long long min,long long max,double avg,long long count)5706*572c4311Sfengbojiang static void latencyModePrint(long long min, long long max, double avg, long long count) {
5707*572c4311Sfengbojiang if (config.output == OUTPUT_STANDARD) {
5708*572c4311Sfengbojiang printf("min: %lld, max: %lld, avg: %.2f (%lld samples)",
5709*572c4311Sfengbojiang min, max, avg, count);
5710*572c4311Sfengbojiang fflush(stdout);
5711*572c4311Sfengbojiang } else if (config.output == OUTPUT_CSV) {
5712*572c4311Sfengbojiang printf("%lld,%lld,%.2f,%lld\n", min, max, avg, count);
5713*572c4311Sfengbojiang } else if (config.output == OUTPUT_RAW) {
5714*572c4311Sfengbojiang printf("%lld %lld %.2f %lld\n", min, max, avg, count);
5715*572c4311Sfengbojiang }
5716*572c4311Sfengbojiang }
5717*572c4311Sfengbojiang
5718*572c4311Sfengbojiang #define LATENCY_SAMPLE_RATE 10 /* milliseconds. */
5719*572c4311Sfengbojiang #define LATENCY_HISTORY_DEFAULT_INTERVAL 15000 /* milliseconds. */
latencyMode(void)5720*572c4311Sfengbojiang static void latencyMode(void) {
5721*572c4311Sfengbojiang redisReply *reply;
5722*572c4311Sfengbojiang long long start, latency, min = 0, max = 0, tot = 0, count = 0;
5723*572c4311Sfengbojiang long long history_interval =
5724*572c4311Sfengbojiang config.interval ? config.interval/1000 :
5725*572c4311Sfengbojiang LATENCY_HISTORY_DEFAULT_INTERVAL;
5726*572c4311Sfengbojiang double avg;
5727*572c4311Sfengbojiang long long history_start = mstime();
5728*572c4311Sfengbojiang
5729*572c4311Sfengbojiang /* Set a default for the interval in case of --latency option
5730*572c4311Sfengbojiang * with --raw, --csv or when it is redirected to non tty. */
5731*572c4311Sfengbojiang if (config.interval == 0) {
5732*572c4311Sfengbojiang config.interval = 1000;
5733*572c4311Sfengbojiang } else {
5734*572c4311Sfengbojiang config.interval /= 1000; /* We need to convert to milliseconds. */
5735*572c4311Sfengbojiang }
5736*572c4311Sfengbojiang
5737*572c4311Sfengbojiang if (!context) exit(1);
5738*572c4311Sfengbojiang while(1) {
5739*572c4311Sfengbojiang start = mstime();
5740*572c4311Sfengbojiang reply = reconnectingRedisCommand(context,"PING");
5741*572c4311Sfengbojiang if (reply == NULL) {
5742*572c4311Sfengbojiang fprintf(stderr,"\nI/O error\n");
5743*572c4311Sfengbojiang exit(1);
5744*572c4311Sfengbojiang }
5745*572c4311Sfengbojiang latency = mstime()-start;
5746*572c4311Sfengbojiang freeReplyObject(reply);
5747*572c4311Sfengbojiang count++;
5748*572c4311Sfengbojiang if (count == 1) {
5749*572c4311Sfengbojiang min = max = tot = latency;
5750*572c4311Sfengbojiang avg = (double) latency;
5751*572c4311Sfengbojiang } else {
5752*572c4311Sfengbojiang if (latency < min) min = latency;
5753*572c4311Sfengbojiang if (latency > max) max = latency;
5754*572c4311Sfengbojiang tot += latency;
5755*572c4311Sfengbojiang avg = (double) tot/count;
5756*572c4311Sfengbojiang }
5757*572c4311Sfengbojiang
5758*572c4311Sfengbojiang if (config.output == OUTPUT_STANDARD) {
5759*572c4311Sfengbojiang printf("\x1b[0G\x1b[2K"); /* Clear the line. */
5760*572c4311Sfengbojiang latencyModePrint(min,max,avg,count);
5761*572c4311Sfengbojiang } else {
5762*572c4311Sfengbojiang if (config.latency_history) {
5763*572c4311Sfengbojiang latencyModePrint(min,max,avg,count);
5764*572c4311Sfengbojiang } else if (mstime()-history_start > config.interval) {
5765*572c4311Sfengbojiang latencyModePrint(min,max,avg,count);
5766*572c4311Sfengbojiang exit(0);
5767*572c4311Sfengbojiang }
5768*572c4311Sfengbojiang }
5769*572c4311Sfengbojiang
5770*572c4311Sfengbojiang if (config.latency_history && mstime()-history_start > history_interval)
5771*572c4311Sfengbojiang {
5772*572c4311Sfengbojiang printf(" -- %.2f seconds range\n", (float)(mstime()-history_start)/1000);
5773*572c4311Sfengbojiang history_start = mstime();
5774*572c4311Sfengbojiang min = max = tot = count = 0;
5775*572c4311Sfengbojiang }
5776*572c4311Sfengbojiang usleep(LATENCY_SAMPLE_RATE * 1000);
5777*572c4311Sfengbojiang }
5778*572c4311Sfengbojiang }
5779*572c4311Sfengbojiang
5780*572c4311Sfengbojiang /*------------------------------------------------------------------------------
5781*572c4311Sfengbojiang * Latency distribution mode -- requires 256 colors xterm
5782*572c4311Sfengbojiang *--------------------------------------------------------------------------- */
5783*572c4311Sfengbojiang
5784*572c4311Sfengbojiang #define LATENCY_DIST_DEFAULT_INTERVAL 1000 /* milliseconds. */
5785*572c4311Sfengbojiang
5786*572c4311Sfengbojiang /* Structure to store samples distribution. */
5787*572c4311Sfengbojiang struct distsamples {
5788*572c4311Sfengbojiang long long max; /* Max latency to fit into this interval (usec). */
5789*572c4311Sfengbojiang long long count; /* Number of samples in this interval. */
5790*572c4311Sfengbojiang int character; /* Associated character in visualization. */
5791*572c4311Sfengbojiang };
5792*572c4311Sfengbojiang
5793*572c4311Sfengbojiang /* Helper function for latencyDistMode(). Performs the spectrum visualization
5794*572c4311Sfengbojiang * of the collected samples targeting an xterm 256 terminal.
5795*572c4311Sfengbojiang *
5796*572c4311Sfengbojiang * Takes an array of distsamples structures, ordered from smaller to bigger
5797*572c4311Sfengbojiang * 'max' value. Last sample max must be 0, to mean that it olds all the
5798*572c4311Sfengbojiang * samples greater than the previous one, and is also the stop sentinel.
5799*572c4311Sfengbojiang *
5800*572c4311Sfengbojiang * "tot' is the total number of samples in the different buckets, so it
5801*572c4311Sfengbojiang * is the SUM(samples[i].conut) for i to 0 up to the max sample.
5802*572c4311Sfengbojiang *
5803*572c4311Sfengbojiang * As a side effect the function sets all the buckets count to 0. */
showLatencyDistSamples(struct distsamples * samples,long long tot)5804*572c4311Sfengbojiang void showLatencyDistSamples(struct distsamples *samples, long long tot) {
5805*572c4311Sfengbojiang int j;
5806*572c4311Sfengbojiang
5807*572c4311Sfengbojiang /* We convert samples into a index inside the palette
5808*572c4311Sfengbojiang * proportional to the percentage a given bucket represents.
5809*572c4311Sfengbojiang * This way intensity of the different parts of the spectrum
5810*572c4311Sfengbojiang * don't change relative to the number of requests, which avoids to
5811*572c4311Sfengbojiang * pollute the visualization with non-latency related info. */
5812*572c4311Sfengbojiang printf("\033[38;5;0m"); /* Set foreground color to black. */
5813*572c4311Sfengbojiang for (j = 0; ; j++) {
5814*572c4311Sfengbojiang int coloridx =
5815*572c4311Sfengbojiang ceil((float) samples[j].count / tot * (spectrum_palette_size-1));
5816*572c4311Sfengbojiang int color = spectrum_palette[coloridx];
5817*572c4311Sfengbojiang printf("\033[48;5;%dm%c", (int)color, samples[j].character);
5818*572c4311Sfengbojiang samples[j].count = 0;
5819*572c4311Sfengbojiang if (samples[j].max == 0) break; /* Last sample. */
5820*572c4311Sfengbojiang }
5821*572c4311Sfengbojiang printf("\033[0m\n");
5822*572c4311Sfengbojiang fflush(stdout);
5823*572c4311Sfengbojiang }
5824*572c4311Sfengbojiang
5825*572c4311Sfengbojiang /* Show the legend: different buckets values and colors meaning, so
5826*572c4311Sfengbojiang * that the spectrum is more easily readable. */
showLatencyDistLegend(void)5827*572c4311Sfengbojiang void showLatencyDistLegend(void) {
5828*572c4311Sfengbojiang int j;
5829*572c4311Sfengbojiang
5830*572c4311Sfengbojiang printf("---------------------------------------------\n");
5831*572c4311Sfengbojiang printf(". - * # .01 .125 .25 .5 milliseconds\n");
5832*572c4311Sfengbojiang printf("1,2,3,...,9 from 1 to 9 milliseconds\n");
5833*572c4311Sfengbojiang printf("A,B,C,D,E 10,20,30,40,50 milliseconds\n");
5834*572c4311Sfengbojiang printf("F,G,H,I,J .1,.2,.3,.4,.5 seconds\n");
5835*572c4311Sfengbojiang printf("K,L,M,N,O,P,Q,? 1,2,4,8,16,30,60,>60 seconds\n");
5836*572c4311Sfengbojiang printf("From 0 to 100%%: ");
5837*572c4311Sfengbojiang for (j = 0; j < spectrum_palette_size; j++) {
5838*572c4311Sfengbojiang printf("\033[48;5;%dm ", spectrum_palette[j]);
5839*572c4311Sfengbojiang }
5840*572c4311Sfengbojiang printf("\033[0m\n");
5841*572c4311Sfengbojiang printf("---------------------------------------------\n");
5842*572c4311Sfengbojiang }
5843*572c4311Sfengbojiang
latencyDistMode(void)5844*572c4311Sfengbojiang static void latencyDistMode(void) {
5845*572c4311Sfengbojiang redisReply *reply;
5846*572c4311Sfengbojiang long long start, latency, count = 0;
5847*572c4311Sfengbojiang long long history_interval =
5848*572c4311Sfengbojiang config.interval ? config.interval/1000 :
5849*572c4311Sfengbojiang LATENCY_DIST_DEFAULT_INTERVAL;
5850*572c4311Sfengbojiang long long history_start = ustime();
5851*572c4311Sfengbojiang int j, outputs = 0;
5852*572c4311Sfengbojiang
5853*572c4311Sfengbojiang struct distsamples samples[] = {
5854*572c4311Sfengbojiang /* We use a mostly logarithmic scale, with certain linear intervals
5855*572c4311Sfengbojiang * which are more interesting than others, like 1-10 milliseconds
5856*572c4311Sfengbojiang * range. */
5857*572c4311Sfengbojiang {10,0,'.'}, /* 0.01 ms */
5858*572c4311Sfengbojiang {125,0,'-'}, /* 0.125 ms */
5859*572c4311Sfengbojiang {250,0,'*'}, /* 0.25 ms */
5860*572c4311Sfengbojiang {500,0,'#'}, /* 0.5 ms */
5861*572c4311Sfengbojiang {1000,0,'1'}, /* 1 ms */
5862*572c4311Sfengbojiang {2000,0,'2'}, /* 2 ms */
5863*572c4311Sfengbojiang {3000,0,'3'}, /* 3 ms */
5864*572c4311Sfengbojiang {4000,0,'4'}, /* 4 ms */
5865*572c4311Sfengbojiang {5000,0,'5'}, /* 5 ms */
5866*572c4311Sfengbojiang {6000,0,'6'}, /* 6 ms */
5867*572c4311Sfengbojiang {7000,0,'7'}, /* 7 ms */
5868*572c4311Sfengbojiang {8000,0,'8'}, /* 8 ms */
5869*572c4311Sfengbojiang {9000,0,'9'}, /* 9 ms */
5870*572c4311Sfengbojiang {10000,0,'A'}, /* 10 ms */
5871*572c4311Sfengbojiang {20000,0,'B'}, /* 20 ms */
5872*572c4311Sfengbojiang {30000,0,'C'}, /* 30 ms */
5873*572c4311Sfengbojiang {40000,0,'D'}, /* 40 ms */
5874*572c4311Sfengbojiang {50000,0,'E'}, /* 50 ms */
5875*572c4311Sfengbojiang {100000,0,'F'}, /* 0.1 s */
5876*572c4311Sfengbojiang {200000,0,'G'}, /* 0.2 s */
5877*572c4311Sfengbojiang {300000,0,'H'}, /* 0.3 s */
5878*572c4311Sfengbojiang {400000,0,'I'}, /* 0.4 s */
5879*572c4311Sfengbojiang {500000,0,'J'}, /* 0.5 s */
5880*572c4311Sfengbojiang {1000000,0,'K'}, /* 1 s */
5881*572c4311Sfengbojiang {2000000,0,'L'}, /* 2 s */
5882*572c4311Sfengbojiang {4000000,0,'M'}, /* 4 s */
5883*572c4311Sfengbojiang {8000000,0,'N'}, /* 8 s */
5884*572c4311Sfengbojiang {16000000,0,'O'}, /* 16 s */
5885*572c4311Sfengbojiang {30000000,0,'P'}, /* 30 s */
5886*572c4311Sfengbojiang {60000000,0,'Q'}, /* 1 minute */
5887*572c4311Sfengbojiang {0,0,'?'}, /* > 1 minute */
5888*572c4311Sfengbojiang };
5889*572c4311Sfengbojiang
5890*572c4311Sfengbojiang if (!context) exit(1);
5891*572c4311Sfengbojiang while(1) {
5892*572c4311Sfengbojiang start = ustime();
5893*572c4311Sfengbojiang reply = reconnectingRedisCommand(context,"PING");
5894*572c4311Sfengbojiang if (reply == NULL) {
5895*572c4311Sfengbojiang fprintf(stderr,"\nI/O error\n");
5896*572c4311Sfengbojiang exit(1);
5897*572c4311Sfengbojiang }
5898*572c4311Sfengbojiang latency = ustime()-start;
5899*572c4311Sfengbojiang freeReplyObject(reply);
5900*572c4311Sfengbojiang count++;
5901*572c4311Sfengbojiang
5902*572c4311Sfengbojiang /* Populate the relevant bucket. */
5903*572c4311Sfengbojiang for (j = 0; ; j++) {
5904*572c4311Sfengbojiang if (samples[j].max == 0 || latency <= samples[j].max) {
5905*572c4311Sfengbojiang samples[j].count++;
5906*572c4311Sfengbojiang break;
5907*572c4311Sfengbojiang }
5908*572c4311Sfengbojiang }
5909*572c4311Sfengbojiang
5910*572c4311Sfengbojiang /* From time to time show the spectrum. */
5911*572c4311Sfengbojiang if (count && (ustime()-history_start)/1000 > history_interval) {
5912*572c4311Sfengbojiang if ((outputs++ % 20) == 0)
5913*572c4311Sfengbojiang showLatencyDistLegend();
5914*572c4311Sfengbojiang showLatencyDistSamples(samples,count);
5915*572c4311Sfengbojiang history_start = ustime();
5916*572c4311Sfengbojiang count = 0;
5917*572c4311Sfengbojiang }
5918*572c4311Sfengbojiang usleep(LATENCY_SAMPLE_RATE * 1000);
5919*572c4311Sfengbojiang }
5920*572c4311Sfengbojiang }
5921*572c4311Sfengbojiang
5922*572c4311Sfengbojiang /*------------------------------------------------------------------------------
5923*572c4311Sfengbojiang * Slave mode
5924*572c4311Sfengbojiang *--------------------------------------------------------------------------- */
5925*572c4311Sfengbojiang
5926*572c4311Sfengbojiang /* Sends SYNC and reads the number of bytes in the payload. Used both by
5927*572c4311Sfengbojiang * slaveMode() and getRDB(). */
sendSync(int fd)5928*572c4311Sfengbojiang unsigned long long sendSync(int fd) {
5929*572c4311Sfengbojiang /* To start we need to send the SYNC command and return the payload.
5930*572c4311Sfengbojiang * The hiredis client lib does not understand this part of the protocol
5931*572c4311Sfengbojiang * and we don't want to mess with its buffers, so everything is performed
5932*572c4311Sfengbojiang * using direct low-level I/O. */
5933*572c4311Sfengbojiang char buf[4096], *p;
5934*572c4311Sfengbojiang ssize_t nread;
5935*572c4311Sfengbojiang
5936*572c4311Sfengbojiang /* Send the SYNC command. */
5937*572c4311Sfengbojiang if (write(fd,"SYNC\r\n",6) != 6) {
5938*572c4311Sfengbojiang fprintf(stderr,"Error writing to master\n");
5939*572c4311Sfengbojiang exit(1);
5940*572c4311Sfengbojiang }
5941*572c4311Sfengbojiang
5942*572c4311Sfengbojiang /* Read $<payload>\r\n, making sure to read just up to "\n" */
5943*572c4311Sfengbojiang p = buf;
5944*572c4311Sfengbojiang while(1) {
5945*572c4311Sfengbojiang nread = read(fd,p,1);
5946*572c4311Sfengbojiang if (nread <= 0) {
5947*572c4311Sfengbojiang fprintf(stderr,"Error reading bulk length while SYNCing\n");
5948*572c4311Sfengbojiang exit(1);
5949*572c4311Sfengbojiang }
5950*572c4311Sfengbojiang if (*p == '\n' && p != buf) break;
5951*572c4311Sfengbojiang if (*p != '\n') p++;
5952*572c4311Sfengbojiang }
5953*572c4311Sfengbojiang *p = '\0';
5954*572c4311Sfengbojiang if (buf[0] == '-') {
5955*572c4311Sfengbojiang printf("SYNC with master failed: %s\n", buf);
5956*572c4311Sfengbojiang exit(1);
5957*572c4311Sfengbojiang }
5958*572c4311Sfengbojiang return strtoull(buf+1,NULL,10);
5959*572c4311Sfengbojiang }
5960*572c4311Sfengbojiang
slaveMode(void)5961*572c4311Sfengbojiang static void slaveMode(void) {
5962*572c4311Sfengbojiang int fd = context->fd;
5963*572c4311Sfengbojiang unsigned long long payload = sendSync(fd);
5964*572c4311Sfengbojiang char buf[1024];
5965*572c4311Sfengbojiang int original_output = config.output;
5966*572c4311Sfengbojiang
5967*572c4311Sfengbojiang fprintf(stderr,"SYNC with master, discarding %llu "
5968*572c4311Sfengbojiang "bytes of bulk transfer...\n", payload);
5969*572c4311Sfengbojiang
5970*572c4311Sfengbojiang /* Discard the payload. */
5971*572c4311Sfengbojiang while(payload) {
5972*572c4311Sfengbojiang ssize_t nread;
5973*572c4311Sfengbojiang
5974*572c4311Sfengbojiang nread = read(fd,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
5975*572c4311Sfengbojiang if (nread <= 0) {
5976*572c4311Sfengbojiang fprintf(stderr,"Error reading RDB payload while SYNCing\n");
5977*572c4311Sfengbojiang exit(1);
5978*572c4311Sfengbojiang }
5979*572c4311Sfengbojiang payload -= nread;
5980*572c4311Sfengbojiang }
5981*572c4311Sfengbojiang fprintf(stderr,"SYNC done. Logging commands from master.\n");
5982*572c4311Sfengbojiang
5983*572c4311Sfengbojiang /* Now we can use hiredis to read the incoming protocol. */
5984*572c4311Sfengbojiang config.output = OUTPUT_CSV;
5985*572c4311Sfengbojiang while (cliReadReply(0) == REDIS_OK);
5986*572c4311Sfengbojiang config.output = original_output;
5987*572c4311Sfengbojiang }
5988*572c4311Sfengbojiang
5989*572c4311Sfengbojiang /*------------------------------------------------------------------------------
5990*572c4311Sfengbojiang * RDB transfer mode
5991*572c4311Sfengbojiang *--------------------------------------------------------------------------- */
5992*572c4311Sfengbojiang
5993*572c4311Sfengbojiang /* This function implements --rdb, so it uses the replication protocol in order
5994*572c4311Sfengbojiang * to fetch the RDB file from a remote server. */
getRDB(void)5995*572c4311Sfengbojiang static void getRDB(void) {
5996*572c4311Sfengbojiang int s = context->fd;
5997*572c4311Sfengbojiang int fd;
5998*572c4311Sfengbojiang unsigned long long payload = sendSync(s);
5999*572c4311Sfengbojiang char buf[4096];
6000*572c4311Sfengbojiang
6001*572c4311Sfengbojiang fprintf(stderr,"SYNC sent to master, writing %llu bytes to '%s'\n",
6002*572c4311Sfengbojiang payload, config.rdb_filename);
6003*572c4311Sfengbojiang
6004*572c4311Sfengbojiang /* Write to file. */
6005*572c4311Sfengbojiang if (!strcmp(config.rdb_filename,"-")) {
6006*572c4311Sfengbojiang fd = STDOUT_FILENO;
6007*572c4311Sfengbojiang } else {
6008*572c4311Sfengbojiang fd = open(config.rdb_filename, O_CREAT|O_WRONLY, 0644);
6009*572c4311Sfengbojiang if (fd == -1) {
6010*572c4311Sfengbojiang fprintf(stderr, "Error opening '%s': %s\n", config.rdb_filename,
6011*572c4311Sfengbojiang strerror(errno));
6012*572c4311Sfengbojiang exit(1);
6013*572c4311Sfengbojiang }
6014*572c4311Sfengbojiang }
6015*572c4311Sfengbojiang
6016*572c4311Sfengbojiang while(payload) {
6017*572c4311Sfengbojiang ssize_t nread, nwritten;
6018*572c4311Sfengbojiang
6019*572c4311Sfengbojiang nread = read(s,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
6020*572c4311Sfengbojiang if (nread <= 0) {
6021*572c4311Sfengbojiang fprintf(stderr,"I/O Error reading RDB payload from socket\n");
6022*572c4311Sfengbojiang exit(1);
6023*572c4311Sfengbojiang }
6024*572c4311Sfengbojiang nwritten = write(fd, buf, nread);
6025*572c4311Sfengbojiang if (nwritten != nread) {
6026*572c4311Sfengbojiang fprintf(stderr,"Error writing data to file: %s\n",
6027*572c4311Sfengbojiang (nwritten == -1) ? strerror(errno) : "short write");
6028*572c4311Sfengbojiang exit(1);
6029*572c4311Sfengbojiang }
6030*572c4311Sfengbojiang payload -= nread;
6031*572c4311Sfengbojiang }
6032*572c4311Sfengbojiang close(s); /* Close the file descriptor ASAP as fsync() may take time. */
6033*572c4311Sfengbojiang fsync(fd);
6034*572c4311Sfengbojiang close(fd);
6035*572c4311Sfengbojiang fprintf(stderr,"Transfer finished with success.\n");
6036*572c4311Sfengbojiang exit(0);
6037*572c4311Sfengbojiang }
6038*572c4311Sfengbojiang
6039*572c4311Sfengbojiang /*------------------------------------------------------------------------------
6040*572c4311Sfengbojiang * Bulk import (pipe) mode
6041*572c4311Sfengbojiang *--------------------------------------------------------------------------- */
6042*572c4311Sfengbojiang
6043*572c4311Sfengbojiang #define PIPEMODE_WRITE_LOOP_MAX_BYTES (128*1024)
pipeMode(void)6044*572c4311Sfengbojiang static void pipeMode(void) {
6045*572c4311Sfengbojiang int fd = context->fd;
6046*572c4311Sfengbojiang long long errors = 0, replies = 0, obuf_len = 0, obuf_pos = 0;
6047*572c4311Sfengbojiang char ibuf[1024*16], obuf[1024*16]; /* Input and output buffers */
6048*572c4311Sfengbojiang char aneterr[ANET_ERR_LEN];
6049*572c4311Sfengbojiang redisReader *reader = redisReaderCreate();
6050*572c4311Sfengbojiang redisReply *reply;
6051*572c4311Sfengbojiang int eof = 0; /* True once we consumed all the standard input. */
6052*572c4311Sfengbojiang int done = 0;
6053*572c4311Sfengbojiang char magic[20]; /* Special reply we recognize. */
6054*572c4311Sfengbojiang time_t last_read_time = time(NULL);
6055*572c4311Sfengbojiang
6056*572c4311Sfengbojiang srand(time(NULL));
6057*572c4311Sfengbojiang
6058*572c4311Sfengbojiang /* Use non blocking I/O. */
6059*572c4311Sfengbojiang if (anetNonBlock(aneterr,fd) == ANET_ERR) {
6060*572c4311Sfengbojiang fprintf(stderr, "Can't set the socket in non blocking mode: %s\n",
6061*572c4311Sfengbojiang aneterr);
6062*572c4311Sfengbojiang exit(1);
6063*572c4311Sfengbojiang }
6064*572c4311Sfengbojiang
6065*572c4311Sfengbojiang /* Transfer raw protocol and read replies from the server at the same
6066*572c4311Sfengbojiang * time. */
6067*572c4311Sfengbojiang while(!done) {
6068*572c4311Sfengbojiang int mask = AE_READABLE;
6069*572c4311Sfengbojiang
6070*572c4311Sfengbojiang if (!eof || obuf_len != 0) mask |= AE_WRITABLE;
6071*572c4311Sfengbojiang mask = aeWait(fd,mask,1000);
6072*572c4311Sfengbojiang
6073*572c4311Sfengbojiang /* Handle the readable state: we can read replies from the server. */
6074*572c4311Sfengbojiang if (mask & AE_READABLE) {
6075*572c4311Sfengbojiang ssize_t nread;
6076*572c4311Sfengbojiang
6077*572c4311Sfengbojiang /* Read from socket and feed the hiredis reader. */
6078*572c4311Sfengbojiang do {
6079*572c4311Sfengbojiang nread = read(fd,ibuf,sizeof(ibuf));
6080*572c4311Sfengbojiang if (nread == -1 && errno != EAGAIN && errno != EINTR) {
6081*572c4311Sfengbojiang fprintf(stderr, "Error reading from the server: %s\n",
6082*572c4311Sfengbojiang strerror(errno));
6083*572c4311Sfengbojiang exit(1);
6084*572c4311Sfengbojiang }
6085*572c4311Sfengbojiang if (nread > 0) {
6086*572c4311Sfengbojiang redisReaderFeed(reader,ibuf,nread);
6087*572c4311Sfengbojiang last_read_time = time(NULL);
6088*572c4311Sfengbojiang }
6089*572c4311Sfengbojiang } while(nread > 0);
6090*572c4311Sfengbojiang
6091*572c4311Sfengbojiang /* Consume replies. */
6092*572c4311Sfengbojiang do {
6093*572c4311Sfengbojiang if (redisReaderGetReply(reader,(void**)&reply) == REDIS_ERR) {
6094*572c4311Sfengbojiang fprintf(stderr, "Error reading replies from server\n");
6095*572c4311Sfengbojiang exit(1);
6096*572c4311Sfengbojiang }
6097*572c4311Sfengbojiang if (reply) {
6098*572c4311Sfengbojiang if (reply->type == REDIS_REPLY_ERROR) {
6099*572c4311Sfengbojiang fprintf(stderr,"%s\n", reply->str);
6100*572c4311Sfengbojiang errors++;
6101*572c4311Sfengbojiang } else if (eof && reply->type == REDIS_REPLY_STRING &&
6102*572c4311Sfengbojiang reply->len == 20) {
6103*572c4311Sfengbojiang /* Check if this is the reply to our final ECHO
6104*572c4311Sfengbojiang * command. If so everything was received
6105*572c4311Sfengbojiang * from the server. */
6106*572c4311Sfengbojiang if (memcmp(reply->str,magic,20) == 0) {
6107*572c4311Sfengbojiang printf("Last reply received from server.\n");
6108*572c4311Sfengbojiang done = 1;
6109*572c4311Sfengbojiang replies--;
6110*572c4311Sfengbojiang }
6111*572c4311Sfengbojiang }
6112*572c4311Sfengbojiang replies++;
6113*572c4311Sfengbojiang freeReplyObject(reply);
6114*572c4311Sfengbojiang }
6115*572c4311Sfengbojiang } while(reply);
6116*572c4311Sfengbojiang }
6117*572c4311Sfengbojiang
6118*572c4311Sfengbojiang /* Handle the writable state: we can send protocol to the server. */
6119*572c4311Sfengbojiang if (mask & AE_WRITABLE) {
6120*572c4311Sfengbojiang ssize_t loop_nwritten = 0;
6121*572c4311Sfengbojiang
6122*572c4311Sfengbojiang while(1) {
6123*572c4311Sfengbojiang /* Transfer current buffer to server. */
6124*572c4311Sfengbojiang if (obuf_len != 0) {
6125*572c4311Sfengbojiang ssize_t nwritten = write(fd,obuf+obuf_pos,obuf_len);
6126*572c4311Sfengbojiang
6127*572c4311Sfengbojiang if (nwritten == -1) {
6128*572c4311Sfengbojiang if (errno != EAGAIN && errno != EINTR) {
6129*572c4311Sfengbojiang fprintf(stderr, "Error writing to the server: %s\n",
6130*572c4311Sfengbojiang strerror(errno));
6131*572c4311Sfengbojiang exit(1);
6132*572c4311Sfengbojiang } else {
6133*572c4311Sfengbojiang nwritten = 0;
6134*572c4311Sfengbojiang }
6135*572c4311Sfengbojiang }
6136*572c4311Sfengbojiang obuf_len -= nwritten;
6137*572c4311Sfengbojiang obuf_pos += nwritten;
6138*572c4311Sfengbojiang loop_nwritten += nwritten;
6139*572c4311Sfengbojiang if (obuf_len != 0) break; /* Can't accept more data. */
6140*572c4311Sfengbojiang }
6141*572c4311Sfengbojiang /* If buffer is empty, load from stdin. */
6142*572c4311Sfengbojiang if (obuf_len == 0 && !eof) {
6143*572c4311Sfengbojiang ssize_t nread = read(STDIN_FILENO,obuf,sizeof(obuf));
6144*572c4311Sfengbojiang
6145*572c4311Sfengbojiang if (nread == 0) {
6146*572c4311Sfengbojiang /* The ECHO sequence starts with a "\r\n" so that if there
6147*572c4311Sfengbojiang * is garbage in the protocol we read from stdin, the ECHO
6148*572c4311Sfengbojiang * will likely still be properly formatted.
6149*572c4311Sfengbojiang * CRLF is ignored by Redis, so it has no effects. */
6150*572c4311Sfengbojiang char echo[] =
6151*572c4311Sfengbojiang "\r\n*2\r\n$4\r\nECHO\r\n$20\r\n01234567890123456789\r\n";
6152*572c4311Sfengbojiang int j;
6153*572c4311Sfengbojiang
6154*572c4311Sfengbojiang eof = 1;
6155*572c4311Sfengbojiang /* Everything transferred, so we queue a special
6156*572c4311Sfengbojiang * ECHO command that we can match in the replies
6157*572c4311Sfengbojiang * to make sure everything was read from the server. */
6158*572c4311Sfengbojiang for (j = 0; j < 20; j++)
6159*572c4311Sfengbojiang magic[j] = rand() & 0xff;
6160*572c4311Sfengbojiang memcpy(echo+21,magic,20);
6161*572c4311Sfengbojiang memcpy(obuf,echo,sizeof(echo)-1);
6162*572c4311Sfengbojiang obuf_len = sizeof(echo)-1;
6163*572c4311Sfengbojiang obuf_pos = 0;
6164*572c4311Sfengbojiang printf("All data transferred. Waiting for the last reply...\n");
6165*572c4311Sfengbojiang } else if (nread == -1) {
6166*572c4311Sfengbojiang fprintf(stderr, "Error reading from stdin: %s\n",
6167*572c4311Sfengbojiang strerror(errno));
6168*572c4311Sfengbojiang exit(1);
6169*572c4311Sfengbojiang } else {
6170*572c4311Sfengbojiang obuf_len = nread;
6171*572c4311Sfengbojiang obuf_pos = 0;
6172*572c4311Sfengbojiang }
6173*572c4311Sfengbojiang }
6174*572c4311Sfengbojiang if ((obuf_len == 0 && eof) ||
6175*572c4311Sfengbojiang loop_nwritten > PIPEMODE_WRITE_LOOP_MAX_BYTES) break;
6176*572c4311Sfengbojiang }
6177*572c4311Sfengbojiang }
6178*572c4311Sfengbojiang
6179*572c4311Sfengbojiang /* Handle timeout, that is, we reached EOF, and we are not getting
6180*572c4311Sfengbojiang * replies from the server for a few seconds, nor the final ECHO is
6181*572c4311Sfengbojiang * received. */
6182*572c4311Sfengbojiang if (eof && config.pipe_timeout > 0 &&
6183*572c4311Sfengbojiang time(NULL)-last_read_time > config.pipe_timeout)
6184*572c4311Sfengbojiang {
6185*572c4311Sfengbojiang fprintf(stderr,"No replies for %d seconds: exiting.\n",
6186*572c4311Sfengbojiang config.pipe_timeout);
6187*572c4311Sfengbojiang errors++;
6188*572c4311Sfengbojiang break;
6189*572c4311Sfengbojiang }
6190*572c4311Sfengbojiang }
6191*572c4311Sfengbojiang redisReaderFree(reader);
6192*572c4311Sfengbojiang printf("errors: %lld, replies: %lld\n", errors, replies);
6193*572c4311Sfengbojiang if (errors)
6194*572c4311Sfengbojiang exit(1);
6195*572c4311Sfengbojiang else
6196*572c4311Sfengbojiang exit(0);
6197*572c4311Sfengbojiang }
6198*572c4311Sfengbojiang
6199*572c4311Sfengbojiang /*------------------------------------------------------------------------------
6200*572c4311Sfengbojiang * Find big keys
6201*572c4311Sfengbojiang *--------------------------------------------------------------------------- */
6202*572c4311Sfengbojiang
sendScan(unsigned long long * it)6203*572c4311Sfengbojiang static redisReply *sendScan(unsigned long long *it) {
6204*572c4311Sfengbojiang redisReply *reply = redisCommand(context, "SCAN %llu", *it);
6205*572c4311Sfengbojiang
6206*572c4311Sfengbojiang /* Handle any error conditions */
6207*572c4311Sfengbojiang if(reply == NULL) {
6208*572c4311Sfengbojiang fprintf(stderr, "\nI/O error\n");
6209*572c4311Sfengbojiang exit(1);
6210*572c4311Sfengbojiang } else if(reply->type == REDIS_REPLY_ERROR) {
6211*572c4311Sfengbojiang fprintf(stderr, "SCAN error: %s\n", reply->str);
6212*572c4311Sfengbojiang exit(1);
6213*572c4311Sfengbojiang } else if(reply->type != REDIS_REPLY_ARRAY) {
6214*572c4311Sfengbojiang fprintf(stderr, "Non ARRAY response from SCAN!\n");
6215*572c4311Sfengbojiang exit(1);
6216*572c4311Sfengbojiang } else if(reply->elements != 2) {
6217*572c4311Sfengbojiang fprintf(stderr, "Invalid element count from SCAN!\n");
6218*572c4311Sfengbojiang exit(1);
6219*572c4311Sfengbojiang }
6220*572c4311Sfengbojiang
6221*572c4311Sfengbojiang /* Validate our types are correct */
6222*572c4311Sfengbojiang assert(reply->element[0]->type == REDIS_REPLY_STRING);
6223*572c4311Sfengbojiang assert(reply->element[1]->type == REDIS_REPLY_ARRAY);
6224*572c4311Sfengbojiang
6225*572c4311Sfengbojiang /* Update iterator */
6226*572c4311Sfengbojiang *it = strtoull(reply->element[0]->str, NULL, 10);
6227*572c4311Sfengbojiang
6228*572c4311Sfengbojiang return reply;
6229*572c4311Sfengbojiang }
6230*572c4311Sfengbojiang
getDbSize(void)6231*572c4311Sfengbojiang static int getDbSize(void) {
6232*572c4311Sfengbojiang redisReply *reply;
6233*572c4311Sfengbojiang int size;
6234*572c4311Sfengbojiang
6235*572c4311Sfengbojiang reply = redisCommand(context, "DBSIZE");
6236*572c4311Sfengbojiang
6237*572c4311Sfengbojiang if(reply == NULL || reply->type != REDIS_REPLY_INTEGER) {
6238*572c4311Sfengbojiang fprintf(stderr, "Couldn't determine DBSIZE!\n");
6239*572c4311Sfengbojiang exit(1);
6240*572c4311Sfengbojiang }
6241*572c4311Sfengbojiang
6242*572c4311Sfengbojiang /* Grab the number of keys and free our reply */
6243*572c4311Sfengbojiang size = reply->integer;
6244*572c4311Sfengbojiang freeReplyObject(reply);
6245*572c4311Sfengbojiang
6246*572c4311Sfengbojiang return size;
6247*572c4311Sfengbojiang }
6248*572c4311Sfengbojiang
6249*572c4311Sfengbojiang typedef struct {
6250*572c4311Sfengbojiang char *name;
6251*572c4311Sfengbojiang char *sizecmd;
6252*572c4311Sfengbojiang char *sizeunit;
6253*572c4311Sfengbojiang unsigned long long biggest;
6254*572c4311Sfengbojiang unsigned long long count;
6255*572c4311Sfengbojiang unsigned long long totalsize;
6256*572c4311Sfengbojiang sds biggest_key;
6257*572c4311Sfengbojiang } typeinfo;
6258*572c4311Sfengbojiang
6259*572c4311Sfengbojiang typeinfo type_string = { "string", "STRLEN", "bytes" };
6260*572c4311Sfengbojiang typeinfo type_list = { "list", "LLEN", "items" };
6261*572c4311Sfengbojiang typeinfo type_set = { "set", "SCARD", "members" };
6262*572c4311Sfengbojiang typeinfo type_hash = { "hash", "HLEN", "fields" };
6263*572c4311Sfengbojiang typeinfo type_zset = { "zset", "ZCARD", "members" };
6264*572c4311Sfengbojiang typeinfo type_stream = { "stream", "XLEN", "entries" };
6265*572c4311Sfengbojiang typeinfo type_other = { "other", NULL, "?" };
6266*572c4311Sfengbojiang
typeinfo_add(dict * types,char * name,typeinfo * type_template)6267*572c4311Sfengbojiang static typeinfo* typeinfo_add(dict *types, char* name, typeinfo* type_template) {
6268*572c4311Sfengbojiang typeinfo *info = zmalloc(sizeof(typeinfo));
6269*572c4311Sfengbojiang *info = *type_template;
6270*572c4311Sfengbojiang info->name = sdsnew(name);
6271*572c4311Sfengbojiang dictAdd(types, info->name, info);
6272*572c4311Sfengbojiang return info;
6273*572c4311Sfengbojiang }
6274*572c4311Sfengbojiang
type_free(void * priv_data,void * val)6275*572c4311Sfengbojiang void type_free(void* priv_data, void* val) {
6276*572c4311Sfengbojiang typeinfo *info = val;
6277*572c4311Sfengbojiang UNUSED(priv_data);
6278*572c4311Sfengbojiang if (info->biggest_key)
6279*572c4311Sfengbojiang sdsfree(info->biggest_key);
6280*572c4311Sfengbojiang sdsfree(info->name);
6281*572c4311Sfengbojiang zfree(info);
6282*572c4311Sfengbojiang }
6283*572c4311Sfengbojiang
6284*572c4311Sfengbojiang static dictType typeinfoDictType = {
6285*572c4311Sfengbojiang dictSdsHash, /* hash function */
6286*572c4311Sfengbojiang NULL, /* key dup */
6287*572c4311Sfengbojiang NULL, /* val dup */
6288*572c4311Sfengbojiang dictSdsKeyCompare, /* key compare */
6289*572c4311Sfengbojiang NULL, /* key destructor (owned by the value)*/
6290*572c4311Sfengbojiang type_free /* val destructor */
6291*572c4311Sfengbojiang };
6292*572c4311Sfengbojiang
getKeyTypes(dict * types_dict,redisReply * keys,typeinfo ** types)6293*572c4311Sfengbojiang static void getKeyTypes(dict *types_dict, redisReply *keys, typeinfo **types) {
6294*572c4311Sfengbojiang redisReply *reply;
6295*572c4311Sfengbojiang unsigned int i;
6296*572c4311Sfengbojiang
6297*572c4311Sfengbojiang /* Pipeline TYPE commands */
6298*572c4311Sfengbojiang for(i=0;i<keys->elements;i++) {
6299*572c4311Sfengbojiang redisAppendCommand(context, "TYPE %s", keys->element[i]->str);
6300*572c4311Sfengbojiang }
6301*572c4311Sfengbojiang
6302*572c4311Sfengbojiang /* Retrieve types */
6303*572c4311Sfengbojiang for(i=0;i<keys->elements;i++) {
6304*572c4311Sfengbojiang if(redisGetReply(context, (void**)&reply)!=REDIS_OK) {
6305*572c4311Sfengbojiang fprintf(stderr, "Error getting type for key '%s' (%d: %s)\n",
6306*572c4311Sfengbojiang keys->element[i]->str, context->err, context->errstr);
6307*572c4311Sfengbojiang exit(1);
6308*572c4311Sfengbojiang } else if(reply->type != REDIS_REPLY_STATUS) {
6309*572c4311Sfengbojiang if(reply->type == REDIS_REPLY_ERROR) {
6310*572c4311Sfengbojiang fprintf(stderr, "TYPE returned an error: %s\n", reply->str);
6311*572c4311Sfengbojiang } else {
6312*572c4311Sfengbojiang fprintf(stderr,
6313*572c4311Sfengbojiang "Invalid reply type (%d) for TYPE on key '%s'!\n",
6314*572c4311Sfengbojiang reply->type, keys->element[i]->str);
6315*572c4311Sfengbojiang }
6316*572c4311Sfengbojiang exit(1);
6317*572c4311Sfengbojiang }
6318*572c4311Sfengbojiang
6319*572c4311Sfengbojiang sds typereply = sdsnew(reply->str);
6320*572c4311Sfengbojiang dictEntry *de = dictFind(types_dict, typereply);
6321*572c4311Sfengbojiang sdsfree(typereply);
6322*572c4311Sfengbojiang typeinfo *type = NULL;
6323*572c4311Sfengbojiang if (de)
6324*572c4311Sfengbojiang type = dictGetVal(de);
6325*572c4311Sfengbojiang else if (strcmp(reply->str, "none")) /* create new types for modules, (but not for deleted keys) */
6326*572c4311Sfengbojiang type = typeinfo_add(types_dict, reply->str, &type_other);
6327*572c4311Sfengbojiang types[i] = type;
6328*572c4311Sfengbojiang freeReplyObject(reply);
6329*572c4311Sfengbojiang }
6330*572c4311Sfengbojiang }
6331*572c4311Sfengbojiang
getKeySizes(redisReply * keys,typeinfo ** types,unsigned long long * sizes,int memkeys,unsigned memkeys_samples)6332*572c4311Sfengbojiang static void getKeySizes(redisReply *keys, typeinfo **types,
6333*572c4311Sfengbojiang unsigned long long *sizes, int memkeys,
6334*572c4311Sfengbojiang unsigned memkeys_samples)
6335*572c4311Sfengbojiang {
6336*572c4311Sfengbojiang redisReply *reply;
6337*572c4311Sfengbojiang unsigned int i;
6338*572c4311Sfengbojiang
6339*572c4311Sfengbojiang /* Pipeline size commands */
6340*572c4311Sfengbojiang for(i=0;i<keys->elements;i++) {
6341*572c4311Sfengbojiang /* Skip keys that disappeared between SCAN and TYPE (or unknown types when not in memkeys mode) */
6342*572c4311Sfengbojiang if(!types[i] || (!types[i]->sizecmd && !memkeys))
6343*572c4311Sfengbojiang continue;
6344*572c4311Sfengbojiang
6345*572c4311Sfengbojiang if (!memkeys)
6346*572c4311Sfengbojiang redisAppendCommand(context, "%s %s",
6347*572c4311Sfengbojiang types[i]->sizecmd, keys->element[i]->str);
6348*572c4311Sfengbojiang else if (memkeys_samples==0)
6349*572c4311Sfengbojiang redisAppendCommand(context, "%s %s %s",
6350*572c4311Sfengbojiang "MEMORY", "USAGE", keys->element[i]->str);
6351*572c4311Sfengbojiang else
6352*572c4311Sfengbojiang redisAppendCommand(context, "%s %s %s SAMPLES %u",
6353*572c4311Sfengbojiang "MEMORY", "USAGE", keys->element[i]->str, memkeys_samples);
6354*572c4311Sfengbojiang }
6355*572c4311Sfengbojiang
6356*572c4311Sfengbojiang /* Retrieve sizes */
6357*572c4311Sfengbojiang for(i=0;i<keys->elements;i++) {
6358*572c4311Sfengbojiang /* Skip keys that disappeared between SCAN and TYPE (or unknown types when not in memkeys mode) */
6359*572c4311Sfengbojiang if(!types[i] || (!types[i]->sizecmd && !memkeys)) {
6360*572c4311Sfengbojiang sizes[i] = 0;
6361*572c4311Sfengbojiang continue;
6362*572c4311Sfengbojiang }
6363*572c4311Sfengbojiang
6364*572c4311Sfengbojiang /* Retrieve size */
6365*572c4311Sfengbojiang if(redisGetReply(context, (void**)&reply)!=REDIS_OK) {
6366*572c4311Sfengbojiang fprintf(stderr, "Error getting size for key '%s' (%d: %s)\n",
6367*572c4311Sfengbojiang keys->element[i]->str, context->err, context->errstr);
6368*572c4311Sfengbojiang exit(1);
6369*572c4311Sfengbojiang } else if(reply->type != REDIS_REPLY_INTEGER) {
6370*572c4311Sfengbojiang /* Theoretically the key could have been removed and
6371*572c4311Sfengbojiang * added as a different type between TYPE and SIZE */
6372*572c4311Sfengbojiang fprintf(stderr,
6373*572c4311Sfengbojiang "Warning: %s on '%s' failed (may have changed type)\n",
6374*572c4311Sfengbojiang !memkeys? types[i]->sizecmd: "MEMORY USAGE",
6375*572c4311Sfengbojiang keys->element[i]->str);
6376*572c4311Sfengbojiang sizes[i] = 0;
6377*572c4311Sfengbojiang } else {
6378*572c4311Sfengbojiang sizes[i] = reply->integer;
6379*572c4311Sfengbojiang }
6380*572c4311Sfengbojiang
6381*572c4311Sfengbojiang freeReplyObject(reply);
6382*572c4311Sfengbojiang }
6383*572c4311Sfengbojiang }
6384*572c4311Sfengbojiang
findBigKeys(int memkeys,unsigned memkeys_samples)6385*572c4311Sfengbojiang static void findBigKeys(int memkeys, unsigned memkeys_samples) {
6386*572c4311Sfengbojiang unsigned long long sampled = 0, total_keys, totlen=0, *sizes=NULL, it=0;
6387*572c4311Sfengbojiang redisReply *reply, *keys;
6388*572c4311Sfengbojiang unsigned int arrsize=0, i;
6389*572c4311Sfengbojiang dictIterator *di;
6390*572c4311Sfengbojiang dictEntry *de;
6391*572c4311Sfengbojiang typeinfo **types = NULL;
6392*572c4311Sfengbojiang double pct;
6393*572c4311Sfengbojiang
6394*572c4311Sfengbojiang dict *types_dict = dictCreate(&typeinfoDictType, NULL);
6395*572c4311Sfengbojiang typeinfo_add(types_dict, "string", &type_string);
6396*572c4311Sfengbojiang typeinfo_add(types_dict, "list", &type_list);
6397*572c4311Sfengbojiang typeinfo_add(types_dict, "set", &type_set);
6398*572c4311Sfengbojiang typeinfo_add(types_dict, "hash", &type_hash);
6399*572c4311Sfengbojiang typeinfo_add(types_dict, "zset", &type_zset);
6400*572c4311Sfengbojiang typeinfo_add(types_dict, "stream", &type_stream);
6401*572c4311Sfengbojiang
6402*572c4311Sfengbojiang /* Total keys pre scanning */
6403*572c4311Sfengbojiang total_keys = getDbSize();
6404*572c4311Sfengbojiang
6405*572c4311Sfengbojiang /* Status message */
6406*572c4311Sfengbojiang printf("\n# Scanning the entire keyspace to find biggest keys as well as\n");
6407*572c4311Sfengbojiang printf("# average sizes per key type. You can use -i 0.1 to sleep 0.1 sec\n");
6408*572c4311Sfengbojiang printf("# per 100 SCAN commands (not usually needed).\n\n");
6409*572c4311Sfengbojiang
6410*572c4311Sfengbojiang /* SCAN loop */
6411*572c4311Sfengbojiang do {
6412*572c4311Sfengbojiang /* Calculate approximate percentage completion */
6413*572c4311Sfengbojiang pct = 100 * (double)sampled/total_keys;
6414*572c4311Sfengbojiang
6415*572c4311Sfengbojiang /* Grab some keys and point to the keys array */
6416*572c4311Sfengbojiang reply = sendScan(&it);
6417*572c4311Sfengbojiang keys = reply->element[1];
6418*572c4311Sfengbojiang
6419*572c4311Sfengbojiang /* Reallocate our type and size array if we need to */
6420*572c4311Sfengbojiang if(keys->elements > arrsize) {
6421*572c4311Sfengbojiang types = zrealloc(types, sizeof(typeinfo*)*keys->elements);
6422*572c4311Sfengbojiang sizes = zrealloc(sizes, sizeof(unsigned long long)*keys->elements);
6423*572c4311Sfengbojiang
6424*572c4311Sfengbojiang if(!types || !sizes) {
6425*572c4311Sfengbojiang fprintf(stderr, "Failed to allocate storage for keys!\n");
6426*572c4311Sfengbojiang exit(1);
6427*572c4311Sfengbojiang }
6428*572c4311Sfengbojiang
6429*572c4311Sfengbojiang arrsize = keys->elements;
6430*572c4311Sfengbojiang }
6431*572c4311Sfengbojiang
6432*572c4311Sfengbojiang /* Retrieve types and then sizes */
6433*572c4311Sfengbojiang getKeyTypes(types_dict, keys, types);
6434*572c4311Sfengbojiang getKeySizes(keys, types, sizes, memkeys, memkeys_samples);
6435*572c4311Sfengbojiang
6436*572c4311Sfengbojiang /* Now update our stats */
6437*572c4311Sfengbojiang for(i=0;i<keys->elements;i++) {
6438*572c4311Sfengbojiang typeinfo *type = types[i];
6439*572c4311Sfengbojiang /* Skip keys that disappeared between SCAN and TYPE */
6440*572c4311Sfengbojiang if(!type)
6441*572c4311Sfengbojiang continue;
6442*572c4311Sfengbojiang
6443*572c4311Sfengbojiang type->totalsize += sizes[i];
6444*572c4311Sfengbojiang type->count++;
6445*572c4311Sfengbojiang totlen += keys->element[i]->len;
6446*572c4311Sfengbojiang sampled++;
6447*572c4311Sfengbojiang
6448*572c4311Sfengbojiang if(type->biggest<sizes[i]) {
6449*572c4311Sfengbojiang printf(
6450*572c4311Sfengbojiang "[%05.2f%%] Biggest %-6s found so far '%s' with %llu %s\n",
6451*572c4311Sfengbojiang pct, type->name, keys->element[i]->str, sizes[i],
6452*572c4311Sfengbojiang !memkeys? type->sizeunit: "bytes");
6453*572c4311Sfengbojiang
6454*572c4311Sfengbojiang /* Keep track of biggest key name for this type */
6455*572c4311Sfengbojiang if (type->biggest_key)
6456*572c4311Sfengbojiang sdsfree(type->biggest_key);
6457*572c4311Sfengbojiang type->biggest_key = sdsnew(keys->element[i]->str);
6458*572c4311Sfengbojiang if(!type->biggest_key) {
6459*572c4311Sfengbojiang fprintf(stderr, "Failed to allocate memory for key!\n");
6460*572c4311Sfengbojiang exit(1);
6461*572c4311Sfengbojiang }
6462*572c4311Sfengbojiang
6463*572c4311Sfengbojiang /* Keep track of the biggest size for this type */
6464*572c4311Sfengbojiang type->biggest = sizes[i];
6465*572c4311Sfengbojiang }
6466*572c4311Sfengbojiang
6467*572c4311Sfengbojiang /* Update overall progress */
6468*572c4311Sfengbojiang if(sampled % 1000000 == 0) {
6469*572c4311Sfengbojiang printf("[%05.2f%%] Sampled %llu keys so far\n", pct, sampled);
6470*572c4311Sfengbojiang }
6471*572c4311Sfengbojiang }
6472*572c4311Sfengbojiang
6473*572c4311Sfengbojiang /* Sleep if we've been directed to do so */
6474*572c4311Sfengbojiang if(sampled && (sampled %100) == 0 && config.interval) {
6475*572c4311Sfengbojiang usleep(config.interval);
6476*572c4311Sfengbojiang }
6477*572c4311Sfengbojiang
6478*572c4311Sfengbojiang freeReplyObject(reply);
6479*572c4311Sfengbojiang } while(it != 0);
6480*572c4311Sfengbojiang
6481*572c4311Sfengbojiang if(types) zfree(types);
6482*572c4311Sfengbojiang if(sizes) zfree(sizes);
6483*572c4311Sfengbojiang
6484*572c4311Sfengbojiang /* We're done */
6485*572c4311Sfengbojiang printf("\n-------- summary -------\n\n");
6486*572c4311Sfengbojiang
6487*572c4311Sfengbojiang printf("Sampled %llu keys in the keyspace!\n", sampled);
6488*572c4311Sfengbojiang printf("Total key length in bytes is %llu (avg len %.2f)\n\n",
6489*572c4311Sfengbojiang totlen, totlen ? (double)totlen/sampled : 0);
6490*572c4311Sfengbojiang
6491*572c4311Sfengbojiang /* Output the biggest keys we found, for types we did find */
6492*572c4311Sfengbojiang di = dictGetIterator(types_dict);
6493*572c4311Sfengbojiang while ((de = dictNext(di))) {
6494*572c4311Sfengbojiang typeinfo *type = dictGetVal(de);
6495*572c4311Sfengbojiang if(type->biggest_key) {
6496*572c4311Sfengbojiang printf("Biggest %6s found '%s' has %llu %s\n", type->name, type->biggest_key,
6497*572c4311Sfengbojiang type->biggest, !memkeys? type->sizeunit: "bytes");
6498*572c4311Sfengbojiang }
6499*572c4311Sfengbojiang }
6500*572c4311Sfengbojiang dictReleaseIterator(di);
6501*572c4311Sfengbojiang
6502*572c4311Sfengbojiang printf("\n");
6503*572c4311Sfengbojiang
6504*572c4311Sfengbojiang di = dictGetIterator(types_dict);
6505*572c4311Sfengbojiang while ((de = dictNext(di))) {
6506*572c4311Sfengbojiang typeinfo *type = dictGetVal(de);
6507*572c4311Sfengbojiang printf("%llu %ss with %llu %s (%05.2f%% of keys, avg size %.2f)\n",
6508*572c4311Sfengbojiang type->count, type->name, type->totalsize, !memkeys? type->sizeunit: "bytes",
6509*572c4311Sfengbojiang sampled ? 100 * (double)type->count/sampled : 0,
6510*572c4311Sfengbojiang type->count ? (double)type->totalsize/type->count : 0);
6511*572c4311Sfengbojiang }
6512*572c4311Sfengbojiang dictReleaseIterator(di);
6513*572c4311Sfengbojiang
6514*572c4311Sfengbojiang dictRelease(types_dict);
6515*572c4311Sfengbojiang
6516*572c4311Sfengbojiang /* Success! */
6517*572c4311Sfengbojiang exit(0);
6518*572c4311Sfengbojiang }
6519*572c4311Sfengbojiang
getKeyFreqs(redisReply * keys,unsigned long long * freqs)6520*572c4311Sfengbojiang static void getKeyFreqs(redisReply *keys, unsigned long long *freqs) {
6521*572c4311Sfengbojiang redisReply *reply;
6522*572c4311Sfengbojiang unsigned int i;
6523*572c4311Sfengbojiang
6524*572c4311Sfengbojiang /* Pipeline OBJECT freq commands */
6525*572c4311Sfengbojiang for(i=0;i<keys->elements;i++) {
6526*572c4311Sfengbojiang redisAppendCommand(context, "OBJECT freq %s", keys->element[i]->str);
6527*572c4311Sfengbojiang }
6528*572c4311Sfengbojiang
6529*572c4311Sfengbojiang /* Retrieve freqs */
6530*572c4311Sfengbojiang for(i=0;i<keys->elements;i++) {
6531*572c4311Sfengbojiang if(redisGetReply(context, (void**)&reply)!=REDIS_OK) {
6532*572c4311Sfengbojiang fprintf(stderr, "Error getting freq for key '%s' (%d: %s)\n",
6533*572c4311Sfengbojiang keys->element[i]->str, context->err, context->errstr);
6534*572c4311Sfengbojiang exit(1);
6535*572c4311Sfengbojiang } else if(reply->type != REDIS_REPLY_INTEGER) {
6536*572c4311Sfengbojiang if(reply->type == REDIS_REPLY_ERROR) {
6537*572c4311Sfengbojiang fprintf(stderr, "Error: %s\n", reply->str);
6538*572c4311Sfengbojiang exit(1);
6539*572c4311Sfengbojiang } else {
6540*572c4311Sfengbojiang fprintf(stderr, "Warning: OBJECT freq on '%s' failed (may have been deleted)\n", keys->element[i]->str);
6541*572c4311Sfengbojiang freqs[i] = 0;
6542*572c4311Sfengbojiang }
6543*572c4311Sfengbojiang } else {
6544*572c4311Sfengbojiang freqs[i] = reply->integer;
6545*572c4311Sfengbojiang }
6546*572c4311Sfengbojiang freeReplyObject(reply);
6547*572c4311Sfengbojiang }
6548*572c4311Sfengbojiang }
6549*572c4311Sfengbojiang
6550*572c4311Sfengbojiang #define HOTKEYS_SAMPLE 16
findHotKeys(void)6551*572c4311Sfengbojiang static void findHotKeys(void) {
6552*572c4311Sfengbojiang redisReply *keys, *reply;
6553*572c4311Sfengbojiang unsigned long long counters[HOTKEYS_SAMPLE] = {0};
6554*572c4311Sfengbojiang sds hotkeys[HOTKEYS_SAMPLE] = {NULL};
6555*572c4311Sfengbojiang unsigned long long sampled = 0, total_keys, *freqs = NULL, it = 0;
6556*572c4311Sfengbojiang unsigned int arrsize = 0, i, k;
6557*572c4311Sfengbojiang double pct;
6558*572c4311Sfengbojiang
6559*572c4311Sfengbojiang /* Total keys pre scanning */
6560*572c4311Sfengbojiang total_keys = getDbSize();
6561*572c4311Sfengbojiang
6562*572c4311Sfengbojiang /* Status message */
6563*572c4311Sfengbojiang printf("\n# Scanning the entire keyspace to find hot keys as well as\n");
6564*572c4311Sfengbojiang printf("# average sizes per key type. You can use -i 0.1 to sleep 0.1 sec\n");
6565*572c4311Sfengbojiang printf("# per 100 SCAN commands (not usually needed).\n\n");
6566*572c4311Sfengbojiang
6567*572c4311Sfengbojiang /* SCAN loop */
6568*572c4311Sfengbojiang do {
6569*572c4311Sfengbojiang /* Calculate approximate percentage completion */
6570*572c4311Sfengbojiang pct = 100 * (double)sampled/total_keys;
6571*572c4311Sfengbojiang
6572*572c4311Sfengbojiang /* Grab some keys and point to the keys array */
6573*572c4311Sfengbojiang reply = sendScan(&it);
6574*572c4311Sfengbojiang keys = reply->element[1];
6575*572c4311Sfengbojiang
6576*572c4311Sfengbojiang /* Reallocate our freqs array if we need to */
6577*572c4311Sfengbojiang if(keys->elements > arrsize) {
6578*572c4311Sfengbojiang freqs = zrealloc(freqs, sizeof(unsigned long long)*keys->elements);
6579*572c4311Sfengbojiang
6580*572c4311Sfengbojiang if(!freqs) {
6581*572c4311Sfengbojiang fprintf(stderr, "Failed to allocate storage for keys!\n");
6582*572c4311Sfengbojiang exit(1);
6583*572c4311Sfengbojiang }
6584*572c4311Sfengbojiang
6585*572c4311Sfengbojiang arrsize = keys->elements;
6586*572c4311Sfengbojiang }
6587*572c4311Sfengbojiang
6588*572c4311Sfengbojiang getKeyFreqs(keys, freqs);
6589*572c4311Sfengbojiang
6590*572c4311Sfengbojiang /* Now update our stats */
6591*572c4311Sfengbojiang for(i=0;i<keys->elements;i++) {
6592*572c4311Sfengbojiang sampled++;
6593*572c4311Sfengbojiang /* Update overall progress */
6594*572c4311Sfengbojiang if(sampled % 1000000 == 0) {
6595*572c4311Sfengbojiang printf("[%05.2f%%] Sampled %llu keys so far\n", pct, sampled);
6596*572c4311Sfengbojiang }
6597*572c4311Sfengbojiang
6598*572c4311Sfengbojiang /* Use eviction pool here */
6599*572c4311Sfengbojiang k = 0;
6600*572c4311Sfengbojiang while (k < HOTKEYS_SAMPLE && freqs[i] > counters[k]) k++;
6601*572c4311Sfengbojiang if (k == 0) continue;
6602*572c4311Sfengbojiang k--;
6603*572c4311Sfengbojiang if (k == 0 || counters[k] == 0) {
6604*572c4311Sfengbojiang sdsfree(hotkeys[k]);
6605*572c4311Sfengbojiang } else {
6606*572c4311Sfengbojiang sdsfree(hotkeys[0]);
6607*572c4311Sfengbojiang memmove(counters,counters+1,sizeof(counters[0])*k);
6608*572c4311Sfengbojiang memmove(hotkeys,hotkeys+1,sizeof(hotkeys[0])*k);
6609*572c4311Sfengbojiang }
6610*572c4311Sfengbojiang counters[k] = freqs[i];
6611*572c4311Sfengbojiang hotkeys[k] = sdsnew(keys->element[i]->str);
6612*572c4311Sfengbojiang printf(
6613*572c4311Sfengbojiang "[%05.2f%%] Hot key '%s' found so far with counter %llu\n",
6614*572c4311Sfengbojiang pct, keys->element[i]->str, freqs[i]);
6615*572c4311Sfengbojiang }
6616*572c4311Sfengbojiang
6617*572c4311Sfengbojiang /* Sleep if we've been directed to do so */
6618*572c4311Sfengbojiang if(sampled && (sampled %100) == 0 && config.interval) {
6619*572c4311Sfengbojiang usleep(config.interval);
6620*572c4311Sfengbojiang }
6621*572c4311Sfengbojiang
6622*572c4311Sfengbojiang freeReplyObject(reply);
6623*572c4311Sfengbojiang } while(it != 0);
6624*572c4311Sfengbojiang
6625*572c4311Sfengbojiang if (freqs) zfree(freqs);
6626*572c4311Sfengbojiang
6627*572c4311Sfengbojiang /* We're done */
6628*572c4311Sfengbojiang printf("\n-------- summary -------\n\n");
6629*572c4311Sfengbojiang
6630*572c4311Sfengbojiang printf("Sampled %llu keys in the keyspace!\n", sampled);
6631*572c4311Sfengbojiang
6632*572c4311Sfengbojiang for (i=1; i<= HOTKEYS_SAMPLE; i++) {
6633*572c4311Sfengbojiang k = HOTKEYS_SAMPLE - i;
6634*572c4311Sfengbojiang if(counters[k]>0) {
6635*572c4311Sfengbojiang printf("hot key found with counter: %llu\tkeyname: %s\n", counters[k], hotkeys[k]);
6636*572c4311Sfengbojiang sdsfree(hotkeys[k]);
6637*572c4311Sfengbojiang }
6638*572c4311Sfengbojiang }
6639*572c4311Sfengbojiang
6640*572c4311Sfengbojiang exit(0);
6641*572c4311Sfengbojiang }
6642*572c4311Sfengbojiang
6643*572c4311Sfengbojiang /*------------------------------------------------------------------------------
6644*572c4311Sfengbojiang * Stats mode
6645*572c4311Sfengbojiang *--------------------------------------------------------------------------- */
6646*572c4311Sfengbojiang
6647*572c4311Sfengbojiang /* Return the specified INFO field from the INFO command output "info".
6648*572c4311Sfengbojiang * A new buffer is allocated for the result, that needs to be free'd.
6649*572c4311Sfengbojiang * If the field is not found NULL is returned. */
getInfoField(char * info,char * field)6650*572c4311Sfengbojiang static char *getInfoField(char *info, char *field) {
6651*572c4311Sfengbojiang char *p = strstr(info,field);
6652*572c4311Sfengbojiang char *n1, *n2;
6653*572c4311Sfengbojiang char *result;
6654*572c4311Sfengbojiang
6655*572c4311Sfengbojiang if (!p) return NULL;
6656*572c4311Sfengbojiang p += strlen(field)+1;
6657*572c4311Sfengbojiang n1 = strchr(p,'\r');
6658*572c4311Sfengbojiang n2 = strchr(p,',');
6659*572c4311Sfengbojiang if (n2 && n2 < n1) n1 = n2;
6660*572c4311Sfengbojiang result = zmalloc(sizeof(char)*(n1-p)+1);
6661*572c4311Sfengbojiang memcpy(result,p,(n1-p));
6662*572c4311Sfengbojiang result[n1-p] = '\0';
6663*572c4311Sfengbojiang return result;
6664*572c4311Sfengbojiang }
6665*572c4311Sfengbojiang
6666*572c4311Sfengbojiang /* Like the above function but automatically convert the result into
6667*572c4311Sfengbojiang * a long. On error (missing field) LONG_MIN is returned. */
getLongInfoField(char * info,char * field)6668*572c4311Sfengbojiang static long getLongInfoField(char *info, char *field) {
6669*572c4311Sfengbojiang char *value = getInfoField(info,field);
6670*572c4311Sfengbojiang long l;
6671*572c4311Sfengbojiang
6672*572c4311Sfengbojiang if (!value) return LONG_MIN;
6673*572c4311Sfengbojiang l = strtol(value,NULL,10);
6674*572c4311Sfengbojiang zfree(value);
6675*572c4311Sfengbojiang return l;
6676*572c4311Sfengbojiang }
6677*572c4311Sfengbojiang
6678*572c4311Sfengbojiang /* Convert number of bytes into a human readable string of the form:
6679*572c4311Sfengbojiang * 100B, 2G, 100M, 4K, and so forth. */
bytesToHuman(char * s,long long n)6680*572c4311Sfengbojiang void bytesToHuman(char *s, long long n) {
6681*572c4311Sfengbojiang double d;
6682*572c4311Sfengbojiang
6683*572c4311Sfengbojiang if (n < 0) {
6684*572c4311Sfengbojiang *s = '-';
6685*572c4311Sfengbojiang s++;
6686*572c4311Sfengbojiang n = -n;
6687*572c4311Sfengbojiang }
6688*572c4311Sfengbojiang if (n < 1024) {
6689*572c4311Sfengbojiang /* Bytes */
6690*572c4311Sfengbojiang sprintf(s,"%lldB",n);
6691*572c4311Sfengbojiang return;
6692*572c4311Sfengbojiang } else if (n < (1024*1024)) {
6693*572c4311Sfengbojiang d = (double)n/(1024);
6694*572c4311Sfengbojiang sprintf(s,"%.2fK",d);
6695*572c4311Sfengbojiang } else if (n < (1024LL*1024*1024)) {
6696*572c4311Sfengbojiang d = (double)n/(1024*1024);
6697*572c4311Sfengbojiang sprintf(s,"%.2fM",d);
6698*572c4311Sfengbojiang } else if (n < (1024LL*1024*1024*1024)) {
6699*572c4311Sfengbojiang d = (double)n/(1024LL*1024*1024);
6700*572c4311Sfengbojiang sprintf(s,"%.2fG",d);
6701*572c4311Sfengbojiang }
6702*572c4311Sfengbojiang }
6703*572c4311Sfengbojiang
statMode(void)6704*572c4311Sfengbojiang static void statMode(void) {
6705*572c4311Sfengbojiang redisReply *reply;
6706*572c4311Sfengbojiang long aux, requests = 0;
6707*572c4311Sfengbojiang int i = 0;
6708*572c4311Sfengbojiang
6709*572c4311Sfengbojiang while(1) {
6710*572c4311Sfengbojiang char buf[64];
6711*572c4311Sfengbojiang int j;
6712*572c4311Sfengbojiang
6713*572c4311Sfengbojiang reply = reconnectingRedisCommand(context,"INFO");
6714*572c4311Sfengbojiang if (reply->type == REDIS_REPLY_ERROR) {
6715*572c4311Sfengbojiang printf("ERROR: %s\n", reply->str);
6716*572c4311Sfengbojiang exit(1);
6717*572c4311Sfengbojiang }
6718*572c4311Sfengbojiang
6719*572c4311Sfengbojiang if ((i++ % 20) == 0) {
6720*572c4311Sfengbojiang printf(
6721*572c4311Sfengbojiang "------- data ------ --------------------- load -------------------- - child -\n"
6722*572c4311Sfengbojiang "keys mem clients blocked requests connections \n");
6723*572c4311Sfengbojiang }
6724*572c4311Sfengbojiang
6725*572c4311Sfengbojiang /* Keys */
6726*572c4311Sfengbojiang aux = 0;
6727*572c4311Sfengbojiang for (j = 0; j < 20; j++) {
6728*572c4311Sfengbojiang long k;
6729*572c4311Sfengbojiang
6730*572c4311Sfengbojiang sprintf(buf,"db%d:keys",j);
6731*572c4311Sfengbojiang k = getLongInfoField(reply->str,buf);
6732*572c4311Sfengbojiang if (k == LONG_MIN) continue;
6733*572c4311Sfengbojiang aux += k;
6734*572c4311Sfengbojiang }
6735*572c4311Sfengbojiang sprintf(buf,"%ld",aux);
6736*572c4311Sfengbojiang printf("%-11s",buf);
6737*572c4311Sfengbojiang
6738*572c4311Sfengbojiang /* Used memory */
6739*572c4311Sfengbojiang aux = getLongInfoField(reply->str,"used_memory");
6740*572c4311Sfengbojiang bytesToHuman(buf,aux);
6741*572c4311Sfengbojiang printf("%-8s",buf);
6742*572c4311Sfengbojiang
6743*572c4311Sfengbojiang /* Clients */
6744*572c4311Sfengbojiang aux = getLongInfoField(reply->str,"connected_clients");
6745*572c4311Sfengbojiang sprintf(buf,"%ld",aux);
6746*572c4311Sfengbojiang printf(" %-8s",buf);
6747*572c4311Sfengbojiang
6748*572c4311Sfengbojiang /* Blocked (BLPOPPING) Clients */
6749*572c4311Sfengbojiang aux = getLongInfoField(reply->str,"blocked_clients");
6750*572c4311Sfengbojiang sprintf(buf,"%ld",aux);
6751*572c4311Sfengbojiang printf("%-8s",buf);
6752*572c4311Sfengbojiang
6753*572c4311Sfengbojiang /* Requests */
6754*572c4311Sfengbojiang aux = getLongInfoField(reply->str,"total_commands_processed");
6755*572c4311Sfengbojiang sprintf(buf,"%ld (+%ld)",aux,requests == 0 ? 0 : aux-requests);
6756*572c4311Sfengbojiang printf("%-19s",buf);
6757*572c4311Sfengbojiang requests = aux;
6758*572c4311Sfengbojiang
6759*572c4311Sfengbojiang /* Connections */
6760*572c4311Sfengbojiang aux = getLongInfoField(reply->str,"total_connections_received");
6761*572c4311Sfengbojiang sprintf(buf,"%ld",aux);
6762*572c4311Sfengbojiang printf(" %-12s",buf);
6763*572c4311Sfengbojiang
6764*572c4311Sfengbojiang /* Children */
6765*572c4311Sfengbojiang aux = getLongInfoField(reply->str,"bgsave_in_progress");
6766*572c4311Sfengbojiang aux |= getLongInfoField(reply->str,"aof_rewrite_in_progress") << 1;
6767*572c4311Sfengbojiang aux |= getLongInfoField(reply->str,"loading") << 2;
6768*572c4311Sfengbojiang switch(aux) {
6769*572c4311Sfengbojiang case 0: break;
6770*572c4311Sfengbojiang case 1:
6771*572c4311Sfengbojiang printf("SAVE");
6772*572c4311Sfengbojiang break;
6773*572c4311Sfengbojiang case 2:
6774*572c4311Sfengbojiang printf("AOF");
6775*572c4311Sfengbojiang break;
6776*572c4311Sfengbojiang case 3:
6777*572c4311Sfengbojiang printf("SAVE+AOF");
6778*572c4311Sfengbojiang break;
6779*572c4311Sfengbojiang case 4:
6780*572c4311Sfengbojiang printf("LOAD");
6781*572c4311Sfengbojiang break;
6782*572c4311Sfengbojiang }
6783*572c4311Sfengbojiang
6784*572c4311Sfengbojiang printf("\n");
6785*572c4311Sfengbojiang freeReplyObject(reply);
6786*572c4311Sfengbojiang usleep(config.interval);
6787*572c4311Sfengbojiang }
6788*572c4311Sfengbojiang }
6789*572c4311Sfengbojiang
6790*572c4311Sfengbojiang /*------------------------------------------------------------------------------
6791*572c4311Sfengbojiang * Scan mode
6792*572c4311Sfengbojiang *--------------------------------------------------------------------------- */
6793*572c4311Sfengbojiang
scanMode(void)6794*572c4311Sfengbojiang static void scanMode(void) {
6795*572c4311Sfengbojiang redisReply *reply;
6796*572c4311Sfengbojiang unsigned long long cur = 0;
6797*572c4311Sfengbojiang
6798*572c4311Sfengbojiang do {
6799*572c4311Sfengbojiang if (config.pattern)
6800*572c4311Sfengbojiang reply = redisCommand(context,"SCAN %llu MATCH %s",
6801*572c4311Sfengbojiang cur,config.pattern);
6802*572c4311Sfengbojiang else
6803*572c4311Sfengbojiang reply = redisCommand(context,"SCAN %llu",cur);
6804*572c4311Sfengbojiang if (reply == NULL) {
6805*572c4311Sfengbojiang printf("I/O error\n");
6806*572c4311Sfengbojiang exit(1);
6807*572c4311Sfengbojiang } else if (reply->type == REDIS_REPLY_ERROR) {
6808*572c4311Sfengbojiang printf("ERROR: %s\n", reply->str);
6809*572c4311Sfengbojiang exit(1);
6810*572c4311Sfengbojiang } else {
6811*572c4311Sfengbojiang unsigned int j;
6812*572c4311Sfengbojiang
6813*572c4311Sfengbojiang cur = strtoull(reply->element[0]->str,NULL,10);
6814*572c4311Sfengbojiang for (j = 0; j < reply->element[1]->elements; j++)
6815*572c4311Sfengbojiang printf("%s\n", reply->element[1]->element[j]->str);
6816*572c4311Sfengbojiang }
6817*572c4311Sfengbojiang freeReplyObject(reply);
6818*572c4311Sfengbojiang } while(cur != 0);
6819*572c4311Sfengbojiang
6820*572c4311Sfengbojiang exit(0);
6821*572c4311Sfengbojiang }
6822*572c4311Sfengbojiang
6823*572c4311Sfengbojiang /*------------------------------------------------------------------------------
6824*572c4311Sfengbojiang * LRU test mode
6825*572c4311Sfengbojiang *--------------------------------------------------------------------------- */
6826*572c4311Sfengbojiang
6827*572c4311Sfengbojiang /* Return an integer from min to max (both inclusive) using a power-law
6828*572c4311Sfengbojiang * distribution, depending on the value of alpha: the greater the alpha
6829*572c4311Sfengbojiang * the more bias towards lower values.
6830*572c4311Sfengbojiang *
6831*572c4311Sfengbojiang * With alpha = 6.2 the output follows the 80-20 rule where 20% of
6832*572c4311Sfengbojiang * the returned numbers will account for 80% of the frequency. */
powerLawRand(long long min,long long max,double alpha)6833*572c4311Sfengbojiang long long powerLawRand(long long min, long long max, double alpha) {
6834*572c4311Sfengbojiang double pl, r;
6835*572c4311Sfengbojiang
6836*572c4311Sfengbojiang max += 1;
6837*572c4311Sfengbojiang r = ((double)rand()) / RAND_MAX;
6838*572c4311Sfengbojiang pl = pow(
6839*572c4311Sfengbojiang ((pow(max,alpha+1) - pow(min,alpha+1))*r + pow(min,alpha+1)),
6840*572c4311Sfengbojiang (1.0/(alpha+1)));
6841*572c4311Sfengbojiang return (max-1-(long long)pl)+min;
6842*572c4311Sfengbojiang }
6843*572c4311Sfengbojiang
6844*572c4311Sfengbojiang /* Generates a key name among a set of lru_test_sample_size keys, using
6845*572c4311Sfengbojiang * an 80-20 distribution. */
LRUTestGenKey(char * buf,size_t buflen)6846*572c4311Sfengbojiang void LRUTestGenKey(char *buf, size_t buflen) {
6847*572c4311Sfengbojiang snprintf(buf, buflen, "lru:%lld",
6848*572c4311Sfengbojiang powerLawRand(1, config.lru_test_sample_size, 6.2));
6849*572c4311Sfengbojiang }
6850*572c4311Sfengbojiang
6851*572c4311Sfengbojiang #define LRU_CYCLE_PERIOD 1000 /* 1000 milliseconds. */
6852*572c4311Sfengbojiang #define LRU_CYCLE_PIPELINE_SIZE 250
LRUTestMode(void)6853*572c4311Sfengbojiang static void LRUTestMode(void) {
6854*572c4311Sfengbojiang redisReply *reply;
6855*572c4311Sfengbojiang char key[128];
6856*572c4311Sfengbojiang long long start_cycle;
6857*572c4311Sfengbojiang int j;
6858*572c4311Sfengbojiang
6859*572c4311Sfengbojiang srand(time(NULL)^getpid());
6860*572c4311Sfengbojiang while(1) {
6861*572c4311Sfengbojiang /* Perform cycles of 1 second with 50% writes and 50% reads.
6862*572c4311Sfengbojiang * We use pipelining batching writes / reads N times per cycle in order
6863*572c4311Sfengbojiang * to fill the target instance easily. */
6864*572c4311Sfengbojiang start_cycle = mstime();
6865*572c4311Sfengbojiang long long hits = 0, misses = 0;
6866*572c4311Sfengbojiang while(mstime() - start_cycle < 1000) {
6867*572c4311Sfengbojiang /* Write cycle. */
6868*572c4311Sfengbojiang for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {
6869*572c4311Sfengbojiang char val[6];
6870*572c4311Sfengbojiang val[5] = '\0';
6871*572c4311Sfengbojiang for (int i = 0; i < 5; i++) val[i] = 'A'+rand()%('z'-'A');
6872*572c4311Sfengbojiang LRUTestGenKey(key,sizeof(key));
6873*572c4311Sfengbojiang redisAppendCommand(context, "SET %s %s",key,val);
6874*572c4311Sfengbojiang }
6875*572c4311Sfengbojiang for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++)
6876*572c4311Sfengbojiang redisGetReply(context, (void**)&reply);
6877*572c4311Sfengbojiang
6878*572c4311Sfengbojiang /* Read cycle. */
6879*572c4311Sfengbojiang for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {
6880*572c4311Sfengbojiang LRUTestGenKey(key,sizeof(key));
6881*572c4311Sfengbojiang redisAppendCommand(context, "GET %s",key);
6882*572c4311Sfengbojiang }
6883*572c4311Sfengbojiang for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {
6884*572c4311Sfengbojiang if (redisGetReply(context, (void**)&reply) == REDIS_OK) {
6885*572c4311Sfengbojiang switch(reply->type) {
6886*572c4311Sfengbojiang case REDIS_REPLY_ERROR:
6887*572c4311Sfengbojiang printf("%s\n", reply->str);
6888*572c4311Sfengbojiang break;
6889*572c4311Sfengbojiang case REDIS_REPLY_NIL:
6890*572c4311Sfengbojiang misses++;
6891*572c4311Sfengbojiang break;
6892*572c4311Sfengbojiang default:
6893*572c4311Sfengbojiang hits++;
6894*572c4311Sfengbojiang break;
6895*572c4311Sfengbojiang }
6896*572c4311Sfengbojiang }
6897*572c4311Sfengbojiang }
6898*572c4311Sfengbojiang
6899*572c4311Sfengbojiang if (context->err) {
6900*572c4311Sfengbojiang fprintf(stderr,"I/O error during LRU test\n");
6901*572c4311Sfengbojiang exit(1);
6902*572c4311Sfengbojiang }
6903*572c4311Sfengbojiang }
6904*572c4311Sfengbojiang /* Print stats. */
6905*572c4311Sfengbojiang printf(
6906*572c4311Sfengbojiang "%lld Gets/sec | Hits: %lld (%.2f%%) | Misses: %lld (%.2f%%)\n",
6907*572c4311Sfengbojiang hits+misses,
6908*572c4311Sfengbojiang hits, (double)hits/(hits+misses)*100,
6909*572c4311Sfengbojiang misses, (double)misses/(hits+misses)*100);
6910*572c4311Sfengbojiang }
6911*572c4311Sfengbojiang exit(0);
6912*572c4311Sfengbojiang }
6913*572c4311Sfengbojiang
6914*572c4311Sfengbojiang /*------------------------------------------------------------------------------
6915*572c4311Sfengbojiang * Intrisic latency mode.
6916*572c4311Sfengbojiang *
6917*572c4311Sfengbojiang * Measure max latency of a running process that does not result from
6918*572c4311Sfengbojiang * syscalls. Basically this software should provide an hint about how much
6919*572c4311Sfengbojiang * time the kernel leaves the process without a chance to run.
6920*572c4311Sfengbojiang *--------------------------------------------------------------------------- */
6921*572c4311Sfengbojiang
6922*572c4311Sfengbojiang /* This is just some computation the compiler can't optimize out.
6923*572c4311Sfengbojiang * Should run in less than 100-200 microseconds even using very
6924*572c4311Sfengbojiang * slow hardware. Runs in less than 10 microseconds in modern HW. */
compute_something_fast(void)6925*572c4311Sfengbojiang unsigned long compute_something_fast(void) {
6926*572c4311Sfengbojiang unsigned char s[256], i, j, t;
6927*572c4311Sfengbojiang int count = 1000, k;
6928*572c4311Sfengbojiang unsigned long output = 0;
6929*572c4311Sfengbojiang
6930*572c4311Sfengbojiang for (k = 0; k < 256; k++) s[k] = k;
6931*572c4311Sfengbojiang
6932*572c4311Sfengbojiang i = 0;
6933*572c4311Sfengbojiang j = 0;
6934*572c4311Sfengbojiang while(count--) {
6935*572c4311Sfengbojiang i++;
6936*572c4311Sfengbojiang j = j + s[i];
6937*572c4311Sfengbojiang t = s[i];
6938*572c4311Sfengbojiang s[i] = s[j];
6939*572c4311Sfengbojiang s[j] = t;
6940*572c4311Sfengbojiang output += s[(s[i]+s[j])&255];
6941*572c4311Sfengbojiang }
6942*572c4311Sfengbojiang return output;
6943*572c4311Sfengbojiang }
6944*572c4311Sfengbojiang
intrinsicLatencyModeStop(int s)6945*572c4311Sfengbojiang static void intrinsicLatencyModeStop(int s) {
6946*572c4311Sfengbojiang UNUSED(s);
6947*572c4311Sfengbojiang force_cancel_loop = 1;
6948*572c4311Sfengbojiang }
6949*572c4311Sfengbojiang
intrinsicLatencyMode(void)6950*572c4311Sfengbojiang static void intrinsicLatencyMode(void) {
6951*572c4311Sfengbojiang long long test_end, run_time, max_latency = 0, runs = 0;
6952*572c4311Sfengbojiang
6953*572c4311Sfengbojiang run_time = config.intrinsic_latency_duration*1000000;
6954*572c4311Sfengbojiang test_end = ustime() + run_time;
6955*572c4311Sfengbojiang signal(SIGINT, intrinsicLatencyModeStop);
6956*572c4311Sfengbojiang
6957*572c4311Sfengbojiang while(1) {
6958*572c4311Sfengbojiang long long start, end, latency;
6959*572c4311Sfengbojiang
6960*572c4311Sfengbojiang start = ustime();
6961*572c4311Sfengbojiang compute_something_fast();
6962*572c4311Sfengbojiang end = ustime();
6963*572c4311Sfengbojiang latency = end-start;
6964*572c4311Sfengbojiang runs++;
6965*572c4311Sfengbojiang if (latency <= 0) continue;
6966*572c4311Sfengbojiang
6967*572c4311Sfengbojiang /* Reporting */
6968*572c4311Sfengbojiang if (latency > max_latency) {
6969*572c4311Sfengbojiang max_latency = latency;
6970*572c4311Sfengbojiang printf("Max latency so far: %lld microseconds.\n", max_latency);
6971*572c4311Sfengbojiang }
6972*572c4311Sfengbojiang
6973*572c4311Sfengbojiang double avg_us = (double)run_time/runs;
6974*572c4311Sfengbojiang double avg_ns = avg_us * 1e3;
6975*572c4311Sfengbojiang if (force_cancel_loop || end > test_end) {
6976*572c4311Sfengbojiang printf("\n%lld total runs "
6977*572c4311Sfengbojiang "(avg latency: "
6978*572c4311Sfengbojiang "%.4f microseconds / %.2f nanoseconds per run).\n",
6979*572c4311Sfengbojiang runs, avg_us, avg_ns);
6980*572c4311Sfengbojiang printf("Worst run took %.0fx longer than the average latency.\n",
6981*572c4311Sfengbojiang max_latency / avg_us);
6982*572c4311Sfengbojiang exit(0);
6983*572c4311Sfengbojiang }
6984*572c4311Sfengbojiang }
6985*572c4311Sfengbojiang }
6986*572c4311Sfengbojiang
6987*572c4311Sfengbojiang /*------------------------------------------------------------------------------
6988*572c4311Sfengbojiang * Program main()
6989*572c4311Sfengbojiang *--------------------------------------------------------------------------- */
6990*572c4311Sfengbojiang
main(int argc,char ** argv)6991*572c4311Sfengbojiang int main(int argc, char **argv) {
6992*572c4311Sfengbojiang int firstarg;
6993*572c4311Sfengbojiang
6994*572c4311Sfengbojiang config.hostip = sdsnew("127.0.0.1");
6995*572c4311Sfengbojiang config.hostport = 6379;
6996*572c4311Sfengbojiang config.hostsocket = NULL;
6997*572c4311Sfengbojiang config.repeat = 1;
6998*572c4311Sfengbojiang config.interval = 0;
6999*572c4311Sfengbojiang config.dbnum = 0;
7000*572c4311Sfengbojiang config.interactive = 0;
7001*572c4311Sfengbojiang config.shutdown = 0;
7002*572c4311Sfengbojiang config.monitor_mode = 0;
7003*572c4311Sfengbojiang config.pubsub_mode = 0;
7004*572c4311Sfengbojiang config.latency_mode = 0;
7005*572c4311Sfengbojiang config.latency_dist_mode = 0;
7006*572c4311Sfengbojiang config.latency_history = 0;
7007*572c4311Sfengbojiang config.lru_test_mode = 0;
7008*572c4311Sfengbojiang config.lru_test_sample_size = 0;
7009*572c4311Sfengbojiang config.cluster_mode = 0;
7010*572c4311Sfengbojiang config.slave_mode = 0;
7011*572c4311Sfengbojiang config.getrdb_mode = 0;
7012*572c4311Sfengbojiang config.stat_mode = 0;
7013*572c4311Sfengbojiang config.scan_mode = 0;
7014*572c4311Sfengbojiang config.intrinsic_latency_mode = 0;
7015*572c4311Sfengbojiang config.pattern = NULL;
7016*572c4311Sfengbojiang config.rdb_filename = NULL;
7017*572c4311Sfengbojiang config.pipe_mode = 0;
7018*572c4311Sfengbojiang config.pipe_timeout = REDIS_CLI_DEFAULT_PIPE_TIMEOUT;
7019*572c4311Sfengbojiang config.bigkeys = 0;
7020*572c4311Sfengbojiang config.hotkeys = 0;
7021*572c4311Sfengbojiang config.stdinarg = 0;
7022*572c4311Sfengbojiang config.auth = NULL;
7023*572c4311Sfengbojiang config.eval = NULL;
7024*572c4311Sfengbojiang config.eval_ldb = 0;
7025*572c4311Sfengbojiang config.eval_ldb_end = 0;
7026*572c4311Sfengbojiang config.eval_ldb_sync = 0;
7027*572c4311Sfengbojiang config.enable_ldb_on_eval = 0;
7028*572c4311Sfengbojiang config.last_cmd_type = -1;
7029*572c4311Sfengbojiang config.verbose = 0;
7030*572c4311Sfengbojiang config.no_auth_warning = 0;
7031*572c4311Sfengbojiang config.cluster_manager_command.name = NULL;
7032*572c4311Sfengbojiang config.cluster_manager_command.argc = 0;
7033*572c4311Sfengbojiang config.cluster_manager_command.argv = NULL;
7034*572c4311Sfengbojiang config.cluster_manager_command.flags = 0;
7035*572c4311Sfengbojiang config.cluster_manager_command.replicas = 0;
7036*572c4311Sfengbojiang config.cluster_manager_command.from = NULL;
7037*572c4311Sfengbojiang config.cluster_manager_command.to = NULL;
7038*572c4311Sfengbojiang config.cluster_manager_command.weight = NULL;
7039*572c4311Sfengbojiang config.cluster_manager_command.weight_argc = 0;
7040*572c4311Sfengbojiang config.cluster_manager_command.slots = 0;
7041*572c4311Sfengbojiang config.cluster_manager_command.timeout = CLUSTER_MANAGER_MIGRATE_TIMEOUT;
7042*572c4311Sfengbojiang config.cluster_manager_command.pipeline = CLUSTER_MANAGER_MIGRATE_PIPELINE;
7043*572c4311Sfengbojiang config.cluster_manager_command.threshold =
7044*572c4311Sfengbojiang CLUSTER_MANAGER_REBALANCE_THRESHOLD;
7045*572c4311Sfengbojiang pref.hints = 1;
7046*572c4311Sfengbojiang
7047*572c4311Sfengbojiang spectrum_palette = spectrum_palette_color;
7048*572c4311Sfengbojiang spectrum_palette_size = spectrum_palette_color_size;
7049*572c4311Sfengbojiang
7050*572c4311Sfengbojiang if (!isatty(fileno(stdout)) && (getenv("FAKETTY") == NULL))
7051*572c4311Sfengbojiang config.output = OUTPUT_RAW;
7052*572c4311Sfengbojiang else
7053*572c4311Sfengbojiang config.output = OUTPUT_STANDARD;
7054*572c4311Sfengbojiang config.mb_delim = sdsnew("\n");
7055*572c4311Sfengbojiang
7056*572c4311Sfengbojiang firstarg = parseOptions(argc,argv);
7057*572c4311Sfengbojiang argc -= firstarg;
7058*572c4311Sfengbojiang argv += firstarg;
7059*572c4311Sfengbojiang
7060*572c4311Sfengbojiang parseEnv();
7061*572c4311Sfengbojiang
7062*572c4311Sfengbojiang /* Cluster Manager mode */
7063*572c4311Sfengbojiang if (CLUSTER_MANAGER_MODE()) {
7064*572c4311Sfengbojiang clusterManagerCommandProc *proc = validateClusterManagerCommand();
7065*572c4311Sfengbojiang if (!proc) {
7066*572c4311Sfengbojiang sdsfree(config.hostip);
7067*572c4311Sfengbojiang sdsfree(config.mb_delim);
7068*572c4311Sfengbojiang exit(1);
7069*572c4311Sfengbojiang }
7070*572c4311Sfengbojiang clusterManagerMode(proc);
7071*572c4311Sfengbojiang }
7072*572c4311Sfengbojiang
7073*572c4311Sfengbojiang /* Latency mode */
7074*572c4311Sfengbojiang if (config.latency_mode) {
7075*572c4311Sfengbojiang if (cliConnect(0) == REDIS_ERR) exit(1);
7076*572c4311Sfengbojiang latencyMode();
7077*572c4311Sfengbojiang }
7078*572c4311Sfengbojiang
7079*572c4311Sfengbojiang /* Latency distribution mode */
7080*572c4311Sfengbojiang if (config.latency_dist_mode) {
7081*572c4311Sfengbojiang if (cliConnect(0) == REDIS_ERR) exit(1);
7082*572c4311Sfengbojiang latencyDistMode();
7083*572c4311Sfengbojiang }
7084*572c4311Sfengbojiang
7085*572c4311Sfengbojiang /* Slave mode */
7086*572c4311Sfengbojiang if (config.slave_mode) {
7087*572c4311Sfengbojiang if (cliConnect(0) == REDIS_ERR) exit(1);
7088*572c4311Sfengbojiang slaveMode();
7089*572c4311Sfengbojiang }
7090*572c4311Sfengbojiang
7091*572c4311Sfengbojiang /* Get RDB mode. */
7092*572c4311Sfengbojiang if (config.getrdb_mode) {
7093*572c4311Sfengbojiang if (cliConnect(0) == REDIS_ERR) exit(1);
7094*572c4311Sfengbojiang getRDB();
7095*572c4311Sfengbojiang }
7096*572c4311Sfengbojiang
7097*572c4311Sfengbojiang /* Pipe mode */
7098*572c4311Sfengbojiang if (config.pipe_mode) {
7099*572c4311Sfengbojiang if (cliConnect(0) == REDIS_ERR) exit(1);
7100*572c4311Sfengbojiang pipeMode();
7101*572c4311Sfengbojiang }
7102*572c4311Sfengbojiang
7103*572c4311Sfengbojiang /* Find big keys */
7104*572c4311Sfengbojiang if (config.bigkeys) {
7105*572c4311Sfengbojiang if (cliConnect(0) == REDIS_ERR) exit(1);
7106*572c4311Sfengbojiang findBigKeys(0, 0);
7107*572c4311Sfengbojiang }
7108*572c4311Sfengbojiang
7109*572c4311Sfengbojiang /* Find large keys */
7110*572c4311Sfengbojiang if (config.memkeys) {
7111*572c4311Sfengbojiang if (cliConnect(0) == REDIS_ERR) exit(1);
7112*572c4311Sfengbojiang findBigKeys(1, config.memkeys_samples);
7113*572c4311Sfengbojiang }
7114*572c4311Sfengbojiang
7115*572c4311Sfengbojiang /* Find hot keys */
7116*572c4311Sfengbojiang if (config.hotkeys) {
7117*572c4311Sfengbojiang if (cliConnect(0) == REDIS_ERR) exit(1);
7118*572c4311Sfengbojiang findHotKeys();
7119*572c4311Sfengbojiang }
7120*572c4311Sfengbojiang
7121*572c4311Sfengbojiang /* Stat mode */
7122*572c4311Sfengbojiang if (config.stat_mode) {
7123*572c4311Sfengbojiang if (cliConnect(0) == REDIS_ERR) exit(1);
7124*572c4311Sfengbojiang if (config.interval == 0) config.interval = 1000000;
7125*572c4311Sfengbojiang statMode();
7126*572c4311Sfengbojiang }
7127*572c4311Sfengbojiang
7128*572c4311Sfengbojiang /* Scan mode */
7129*572c4311Sfengbojiang if (config.scan_mode) {
7130*572c4311Sfengbojiang if (cliConnect(0) == REDIS_ERR) exit(1);
7131*572c4311Sfengbojiang scanMode();
7132*572c4311Sfengbojiang }
7133*572c4311Sfengbojiang
7134*572c4311Sfengbojiang /* LRU test mode */
7135*572c4311Sfengbojiang if (config.lru_test_mode) {
7136*572c4311Sfengbojiang if (cliConnect(0) == REDIS_ERR) exit(1);
7137*572c4311Sfengbojiang LRUTestMode();
7138*572c4311Sfengbojiang }
7139*572c4311Sfengbojiang
7140*572c4311Sfengbojiang /* Intrinsic latency mode */
7141*572c4311Sfengbojiang if (config.intrinsic_latency_mode) intrinsicLatencyMode();
7142*572c4311Sfengbojiang
7143*572c4311Sfengbojiang /* Start interactive mode when no command is provided */
7144*572c4311Sfengbojiang if (argc == 0 && !config.eval) {
7145*572c4311Sfengbojiang /* Ignore SIGPIPE in interactive mode to force a reconnect */
7146*572c4311Sfengbojiang signal(SIGPIPE, SIG_IGN);
7147*572c4311Sfengbojiang
7148*572c4311Sfengbojiang /* Note that in repl mode we don't abort on connection error.
7149*572c4311Sfengbojiang * A new attempt will be performed for every command send. */
7150*572c4311Sfengbojiang cliConnect(0);
7151*572c4311Sfengbojiang repl();
7152*572c4311Sfengbojiang }
7153*572c4311Sfengbojiang
7154*572c4311Sfengbojiang /* Otherwise, we have some arguments to execute */
7155*572c4311Sfengbojiang if (cliConnect(0) != REDIS_OK) exit(1);
7156*572c4311Sfengbojiang if (config.eval) {
7157*572c4311Sfengbojiang return evalMode(argc,argv);
7158*572c4311Sfengbojiang } else {
7159*572c4311Sfengbojiang return noninteractive(argc,convertToSds(argc,argv));
7160*572c4311Sfengbojiang }
7161*572c4311Sfengbojiang }
7162