diff --git a/src/module.c b/src/module.c index 81982ba76..5d3451916 100644 --- a/src/module.c +++ b/src/module.c @@ -509,6 +509,17 @@ void RedisModuleCommandDispatcher(client *c) { cp->func(&ctx,(void**)c->argv,c->argc); moduleHandlePropagationAfterCommandCallback(&ctx); moduleFreeContext(&ctx); + + /* In some cases processMultibulkBuffer uses sdsMakeRoomFor to + * expand the querybuf, but later in some cases it uses that query + * buffer as is for an argv element (see "Optimization"), which means + * that the sds in argv may have a lot of wasted space, and then in case + * modules keep that argv RedisString inside their data structure, this + * space waste will remain for long (until restarted from rdb). */ + for (int i = 0; i < c->argc; i++) { + if (c->argv[i]->refcount > 1) + trimStringObjectIfNeeded(c->argv[i]); + } } /* This function returns the list of keys, with the same interface as the diff --git a/src/object.c b/src/object.c index ec0bd02ee..42c247b87 100644 --- a/src/object.c +++ b/src/object.c @@ -415,6 +415,17 @@ int isObjectRepresentableAsLongLong(robj *o, long long *llval) { } } +void trimStringObjectIfNeeded(robj *o) { + /* Optimize the SDS string inside the string object to require + * little space, in case there is more than 10% of free space + * at the end of the SDS string. */ + if (o->encoding == OBJ_ENCODING_RAW && + sdsavail(o->ptr) > sdslen(o->ptr)/10) + { + o->ptr = sdsRemoveFreeSpace(o->ptr); + } +} + /* Try to encode a string object in order to save space */ robj *tryObjectEncoding(robj *o) { long value; @@ -484,11 +495,7 @@ robj *tryObjectEncoding(robj *o) { * We do that only for relatively large strings as this branch * is only entered if the length of the string is greater than * OBJ_ENCODING_EMBSTR_SIZE_LIMIT. */ - if (o->encoding == OBJ_ENCODING_RAW && - sdsavail(s) > len/10) - { - o->ptr = sdsRemoveFreeSpace(o->ptr); - } + trimStringObjectIfNeeded(o); /* Return the original object. */ return o; diff --git a/src/sds.c b/src/sds.c index 330c955e8..cd60946bd 100644 --- a/src/sds.c +++ b/src/sds.c @@ -257,8 +257,12 @@ sds sdsRemoveFreeSpace(sds s) { char type, oldtype = s[-1] & SDS_TYPE_MASK; int hdrlen, oldhdrlen = sdsHdrSize(oldtype); size_t len = sdslen(s); + size_t avail = sdsavail(s); sh = (char*)s-oldhdrlen; + /* Return ASAP if there is no space left. */ + if (avail == 0) return s; + /* Check what would be the minimum SDS header that is just good enough to * fit this string. */ type = sdsReqType(len); diff --git a/src/server.h b/src/server.h index a396e1cf7..34b4cd8d5 100644 --- a/src/server.h +++ b/src/server.h @@ -1656,6 +1656,7 @@ int compareStringObjects(robj *a, robj *b); int collateStringObjects(robj *a, robj *b); int equalStringObjects(robj *a, robj *b); unsigned long long estimateObjectIdleTime(robj *o); +void trimStringObjectIfNeeded(robj *o); #define sdsEncodedObject(objptr) (objptr->encoding == OBJ_ENCODING_RAW || objptr->encoding == OBJ_ENCODING_EMBSTR) /* Synchronous I/O with timeout */