diff --git a/src/aof.c b/src/aof.c index 9a0be96fe..5e69399fc 100644 --- a/src/aof.c +++ b/src/aof.c @@ -507,6 +507,7 @@ struct redisClient *createFakeClient(void) { c->reply_bytes = 0; c->obuf_soft_limit_reached_time = 0; c->watched_keys = listCreate(); + c->peerid = NULL; listSetFreeMethod(c->reply,decrRefCountVoid); listSetDupMethod(c->reply,dupClientReplyValue); initClientMultiState(c); diff --git a/src/debug.c b/src/debug.c index a9c7cc5e7..1869f7553 100644 --- a/src/debug.c +++ b/src/debug.c @@ -701,7 +701,7 @@ void logCurrentClient(void) { int j; redisLog(REDIS_WARNING, "--- CURRENT CLIENT INFO"); - client = getClientInfoString(cc); + client = catClientInfoString(sdsempty(),cc); redisLog(REDIS_WARNING,"client: %s", client); sdsfree(client); for (j = 0; j < cc->argc; j++) { diff --git a/src/networking.c b/src/networking.c index 6e4687f22..06677d571 100644 --- a/src/networking.c +++ b/src/networking.c @@ -118,6 +118,7 @@ redisClient *createClient(int fd) { c->watched_keys = listCreate(); c->pubsub_channels = dictCreate(&setDictType,NULL); c->pubsub_patterns = listCreate(); + c->peerid = NULL; listSetFreeMethod(c->pubsub_patterns,decrRefCountVoid); listSetMatchMethod(c->pubsub_patterns,listMatchObjects); if (fd != -1) listAddNodeTail(server.clients,c); @@ -763,6 +764,7 @@ void freeClient(redisClient *c) { if (c->name) decrRefCount(c->name); zfree(c->argv); freeClientMultiState(c); + sdsfree(c->peerid); zfree(c); } @@ -946,7 +948,7 @@ int processInlineBuffer(redisClient *c) { * multi bulk requests idempotent. */ static void setProtocolError(redisClient *c, int pos) { if (server.verbosity >= REDIS_VERBOSE) { - sds client = getClientInfoString(c); + sds client = catClientInfoString(sdsempty(),c); redisLog(REDIS_VERBOSE, "Protocol error from client: %s", client); sdsfree(client); @@ -1184,7 +1186,7 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) { return; } if (sdslen(c->querybuf) > server.client_max_querybuf_len) { - sds ci = getClientInfoString(c), bytes = sdsempty(); + sds ci = catClientInfoString(sdsempty(),c), bytes = sdsempty(); bytes = sdscatrepr(bytes,c->querybuf,64); redisLog(REDIS_WARNING,"Closing client that reached max query buffer length: %s (qbuf initial bytes: %s)", ci, bytes); @@ -1215,7 +1217,7 @@ void getClientsMaxBuffers(unsigned long *longest_output_list, *biggest_input_buffer = bib; } -/* This is a helper function for getClientPeerId(). +/* This is a helper function for genClientPeerId(). * It writes the specified ip/port to "peerid" as a null termiated string * in the form ip:port if ip does not contain ":" itself, otherwise * [ip]:port format is used (for IPv6 addresses basically). */ @@ -1239,7 +1241,7 @@ void formatPeerId(char *peerid, size_t peerid_len, char *ip, int port) { * On failure the function still populates 'peerid' with the "?:0" string * in case you want to relax error checking or need to display something * anyway (see anetPeerToString implementation for more info). */ -int getClientPeerId(redisClient *client, char *peerid, size_t peerid_len) { +int genClientPeerId(redisClient *client, char *peerid, size_t peerid_len) { char ip[REDIS_IP_STR_LEN]; int port; @@ -1255,12 +1257,26 @@ int getClientPeerId(redisClient *client, char *peerid, size_t peerid_len) { } } -/* Turn a Redis client into an sds string representing its state. */ -sds getClientInfoString(redisClient *client) { - char peerid[REDIS_PEER_ID_LEN], flags[16], events[3], *p; +/* This function returns the client peer id, by creating and caching it + * if client->perrid is NULL, otherwise returning the cached value. + * The Peer ID never changes during the life of the client, however it + * is expensive to compute. */ +char *getClientPeerId(redisClient *c) { + char peerid[REDIS_PEER_ID_LEN]; + + if (c->peerid == NULL) { + genClientPeerId(c,peerid,sizeof(peerid)); + c->peerid = sdsnew(peerid); + } + return c->peerid; +} + +/* Concatenate a string representing the state of a client in an human + * readable format, into the sds string 's'. */ +sds catClientInfoString(sds s, redisClient *client) { + char flags[16], events[3], *p; int emask; - getClientPeerId(client,peerid,sizeof(peerid)); p = flags; if (client->flags & REDIS_SLAVE) { if (client->flags & REDIS_MONITOR) @@ -1285,9 +1301,9 @@ sds getClientInfoString(redisClient *client) { if (emask & AE_READABLE) *p++ = 'r'; if (emask & AE_WRITABLE) *p++ = 'w'; *p = '\0'; - return sdscatfmt(sdsempty(), + return sdscatfmt(s, "addr=%s fd=%i name=%s age=%I idle=%I flags=%s db=%i sub=%i psub=%i multi=%i qbuf=%U qbuf-free=%U obl=%U oll=%U omem=%U events=%s cmd=%s", - peerid, + getClientPeerId(client), client->fd, client->name ? (char*)client->name->ptr : "", (long long)(server.unixtime - client->ctime), @@ -1312,14 +1328,11 @@ sds getAllClientsInfoString(void) { redisClient *client; sds o = sdsempty(); + o = sdsMakeRoomFor(o,200*listLength(server.clients)); listRewind(server.clients,&li); while ((ln = listNext(&li)) != NULL) { - sds cs; - client = listNodeValue(ln); - cs = getClientInfoString(client); - o = sdscatsds(o,cs); - sdsfree(cs); + o = catClientInfoString(o,client); o = sdscatlen(o,"\n",1); } return o; @@ -1337,11 +1350,10 @@ void clientCommand(redisClient *c) { } else if (!strcasecmp(c->argv[1]->ptr,"kill") && c->argc == 3) { listRewind(server.clients,&li); while ((ln = listNext(&li)) != NULL) { - char peerid[REDIS_PEER_ID_LEN]; + char *peerid; client = listNodeValue(ln); - if (getClientPeerId(client,peerid,sizeof(peerid)) == REDIS_ERR) - continue; + peerid = getClientPeerId(client); if (strcmp(peerid,c->argv[2]->ptr) == 0) { addReply(c,shared.ok); if (c == client) { @@ -1547,7 +1559,7 @@ void asyncCloseClientOnOutputBufferLimitReached(redisClient *c) { redisAssert(c->reply_bytes < ULONG_MAX-(1024*64)); if (c->reply_bytes == 0 || c->flags & REDIS_CLOSE_ASAP) return; if (checkClientOutputBufferLimits(c)) { - sds client = getClientInfoString(c); + sds client = catClientInfoString(sdsempty(),c); freeClientAsync(c); redisLog(REDIS_WARNING,"Client %s scheduled to be closed ASAP for overcoming of output buffer limits.", client); diff --git a/src/redis.h b/src/redis.h index ecdb4bc9d..b234185b5 100644 --- a/src/redis.h +++ b/src/redis.h @@ -530,6 +530,7 @@ typedef struct redisClient { list *watched_keys; /* Keys WATCHED for MULTI/EXEC CAS */ dict *pubsub_channels; /* channels a client is interested in (SUBSCRIBE) */ list *pubsub_patterns; /* patterns a client is interested in (SUBSCRIBE) */ + sds peerid; /* Cached peer ID. */ /* Response buffer */ int bufpos; @@ -991,8 +992,8 @@ void *dupClientReplyValue(void *o); void getClientsMaxBuffers(unsigned long *longest_output_list, unsigned long *biggest_input_buffer); void formatPeerId(char *peerid, size_t peerid_len, char *ip, int port); -int getClientPeerId(redisClient *client, char *peerid, size_t peerid_len); -sds getClientInfoString(redisClient *client); +char *getClientPeerId(redisClient *client); +sds catClientInfoString(sds s, redisClient *client); sds getAllClientsInfoString(void); void rewriteClientCommandVector(redisClient *c, int argc, ...); void rewriteClientCommandArgument(redisClient *c, int i, robj *newval); diff --git a/src/replication.c b/src/replication.c index 9c1e1f01a..94aacea5f 100644 --- a/src/replication.c +++ b/src/replication.c @@ -239,7 +239,6 @@ void replicationFeedMonitors(redisClient *c, list *monitors, int dictid, robj ** int j; sds cmdrepr = sdsnew("+"); robj *cmdobj; - char peerid[REDIS_PEER_ID_LEN]; struct timeval tv; gettimeofday(&tv,NULL); @@ -249,8 +248,7 @@ void replicationFeedMonitors(redisClient *c, list *monitors, int dictid, robj ** } else if (c->flags & REDIS_UNIX_SOCKET) { cmdrepr = sdscatprintf(cmdrepr,"[%d unix:%s] ",dictid,server.unixsocket); } else { - getClientPeerId(c,peerid,sizeof(peerid)); - cmdrepr = sdscatprintf(cmdrepr,"[%d %s] ",dictid,peerid); + cmdrepr = sdscatprintf(cmdrepr,"[%d %s] ",dictid,getClientPeerId(c)); } for (j = 0; j < argc; j++) { @@ -1387,6 +1385,12 @@ void replicationCacheMaster(redisClient *c) { /* Set fd to -1 so that we can safely call freeClient(c) later. */ c->fd = -1; + /* Invalidate the Peer ID cache. */ + if (c->peerid) { + sdsfree(c->peerid); + c->peerid = NULL; + } + /* Caching the master happens instead of the actual freeClient() call, * so make sure to adjust the replication state. This function will * also set server.master to NULL. */