diff --git a/src/aof.c b/src/aof.c index 64cd76d3d76abf9a965b532e135fd161b8dd0696..83633217f796354649929880f6ed509cad552f32 100644 --- a/src/aof.c +++ b/src/aof.c @@ -287,6 +287,7 @@ struct redisClient *createFakeClient(void) { selectDb(c,0); c->fd = -1; c->querybuf = sdsempty(); + c->querybuf_peak = 0; c->argc = 0; c->argv = NULL; c->bufpos = 0; diff --git a/src/networking.c b/src/networking.c index ae77d11b231cd1376e0e4b81df3ff48f04827b9a..375186d1e01b29ce35d38b4a5b3341955e018d47 100644 --- a/src/networking.c +++ b/src/networking.c @@ -43,6 +43,7 @@ redisClient *createClient(int fd) { c->fd = fd; c->bufpos = 0; c->querybuf = sdsempty(); + c->querybuf_peak = 0; c->reqtype = 0; c->argc = 0; c->argv = NULL; @@ -998,6 +999,7 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) { } qblen = sdslen(c->querybuf); + if (c->querybuf_peak < qblen) c->querybuf_peak = qblen; c->querybuf = sdsMakeRoomFor(c->querybuf, readlen); nread = read(fd, c->querybuf+qblen, readlen); if (nread == -1) { diff --git a/src/redis.c b/src/redis.c index 58b5f1c4bf2d973e8035abb39ede205dea14de95..dacf47125b4536b530ee62f67fbb08e61ed1ae42 100644 --- a/src/redis.c +++ b/src/redis.c @@ -629,7 +629,7 @@ long long getOperationsPerSecond(void) { } void clientsCronHandleTimeout(redisClient *c) { - time_t now = time(NULL); + time_t now = server.unixtime; if (server.maxidletime && !(c->flags & REDIS_SLAVE) && /* no timeout for slaves */ @@ -649,15 +649,40 @@ void clientsCronHandleTimeout(redisClient *c) { } } +/* The client query buffer is an sds.c string that can end with a lot of + * free space not used, this function reclaims space if needed. */ +void clientsCronResizeQueryBuffer(redisClient *c) { + size_t querybuf_size = sdsAllocSize(c->querybuf); + time_t idletime = server.unixtime - c->lastinteraction; + + /* There are two conditions to resize the query buffer: + * 1) Query buffer is > BIG_ARG and too big for latest peak. + * 2) Client is inactive and the buffer is bigger than 1k. */ + if (((querybuf_size > REDIS_MBULK_BIG_ARG) && + (querybuf_size/(c->querybuf_peak+1)) > 2) || + (querybuf_size > 1024 && idletime > 2)) + { + /* Only resize the query buffer if it is actually wasting space. */ + if (sdsavail(c->querybuf) > 1024) { + c->querybuf = sdsRemoveFreeSpace(c->querybuf); + } + } + /* Reset the peak again to capture the peak memory usage in the next + * cycle. */ + c->querybuf_peak = 0; +} + void clientsCron(void) { /* Make sure to process at least 1/100 of clients per call. * Since this function is called 10 times per second we are sure that * in the worst case we process all the clients in 10 seconds. * In normal conditions (a reasonable number of clients) we process * all the clients in a shorter time. */ - int iterations = listLength(server.clients)/100; - if (iterations < 50) iterations = 50; + int numclients = listLength(server.clients); + int iterations = numclients/100; + if (iterations < 50) + iterations = (numclients < 50) ? numclients : 50; while(listLength(server.clients) && iterations--) { redisClient *c; listNode *head; @@ -669,6 +694,7 @@ void clientsCron(void) { head = listFirst(server.clients); c = listNodeValue(head); clientsCronHandleTimeout(c); + clientsCronResizeQueryBuffer(c); } } diff --git a/src/redis.h b/src/redis.h index 28649f6d638b8482d79ea8ad50dd4792158de648..cbb2df791adf0ffca584cc5771c4b2c0912f4d25 100644 --- a/src/redis.h +++ b/src/redis.h @@ -323,6 +323,7 @@ typedef struct redisClient { redisDb *db; int dictid; sds querybuf; + size_t querybuf_peak; /* Recent (100ms or more) peak of querybuf size */ int argc; robj **argv; struct redisCommand *cmd, *lastcmd;