diff options
Diffstat (limited to 'src/t_list.c')
-rw-r--r-- | src/t_list.c | 451 |
1 files changed, 373 insertions, 78 deletions
diff --git a/src/t_list.c b/src/t_list.c index e0d324309..d7906d391 100644 --- a/src/t_list.c +++ b/src/t_list.c @@ -33,6 +33,131 @@ * List API *----------------------------------------------------------------------------*/ +/* Check the length and size of a number of objects that will be added to list to see + * if we need to convert a listpack to a quicklist. Note that we only check string + * encoded objects as their string length can be queried in constant time. + * + * If callback is given the function is called in order for caller to do some work + * before the list conversion. */ +static void listTypeTryConvertListpack(robj *o, robj **argv, int start, int end, + beforeConvertCB fn, void *data) +{ + serverAssert(o->encoding == OBJ_ENCODING_LISTPACK); + + size_t add_bytes = 0; + size_t add_length = 0; + + if (argv) { + for (int i = start; i <= end; i++) { + if (!sdsEncodedObject(argv[i])) + continue; + add_bytes += sdslen(argv[i]->ptr); + } + add_length = end - start + 1; + } + + if (quicklistNodeExceedsLimit(server.list_max_listpack_size, + lpBytes(o->ptr) + add_bytes, lpLength(o->ptr) + add_length)) + { + /* Invoke callback before conversion. */ + if (fn) fn(data); + + quicklist *ql = quicklistCreate(); + quicklistSetOptions(ql, server.list_max_listpack_size, server.list_compress_depth); + + /* Append listpack to quicklist if it's not empty, otherwise release it. */ + if (lpLength(o->ptr)) + quicklistAppendListpack(ql, o->ptr); + else + lpFree(o->ptr); + o->ptr = ql; + o->encoding = OBJ_ENCODING_QUICKLIST; + } +} + +/* Check the length and size of a quicklist to see if we need to convert it to listpack. + * + * 'shrinking' is 1 means that the conversion is due to a list shrinking, to avoid + * frequent conversions of quicklist and listpack due to frequent insertion and + * deletion, we don't convert quicklist to listpack until its length or size is + * below half of the limit. + * + * If callback is given the function is called in order for caller to do some work + * before the list conversion. */ +static void listTypeTryConvertQuicklist(robj *o, int shrinking, beforeConvertCB fn, void *data) { + serverAssert(o->encoding == OBJ_ENCODING_QUICKLIST); + + size_t sz_limit; + unsigned int count_limit; + quicklist *ql = o->ptr; + + /* A quicklist can be converted to listpack only if it has only one packed node. */ + if (ql->len != 1 || ql->head->container != QUICKLIST_NODE_CONTAINER_PACKED) + return; + + /* Check the length or size of the quicklist is below the limit. */ + quicklistNodeLimit(server.list_max_listpack_size, &sz_limit, &count_limit); + if (shrinking) { + sz_limit /= 2; + count_limit /= 2; + } + if (ql->head->sz > sz_limit || ql->count > count_limit) return; + + /* Invoke callback before conversion. */ + if (fn) fn(data); + + /* Extract the listpack from the unique quicklist node, + * then reset it and release the quicklist. */ + o->ptr = ql->head->entry; + ql->head->entry = NULL; + quicklistRelease(ql); + o->encoding = OBJ_ENCODING_LISTPACK; +} + +/* Check if the list needs to be converted to appropriate encoding due to + * growing, shrinking or other cases. + * + * 'lct' can be one of the following values: + * LIST_CONV_AUTO - Used after we built a new list, and we want to let the + * function decide on the best encoding for that list. + * LIST_CONV_GROWING - Used before or right after adding elements to the list, + * in which case we are likely to only consider converting + * from listpack to quicklist. + * 'argv' is only used in this case to calculate the size + * of a number of objects that will be added to list. + * LIST_CONV_SHRINKING - Used after removing an element from the list, in which case we + * wanna consider converting from quicklist to listpack. When we + * know we're shrinking, we use a lower (more strict) threshold in + * order to avoid repeated conversions on every list change. */ +static void listTypeTryConversionRaw(robj *o, list_conv_type lct, + robj **argv, int start, int end, + beforeConvertCB fn, void *data) +{ + if (o->encoding == OBJ_ENCODING_QUICKLIST) { + if (lct == LIST_CONV_GROWING) return; /* Growing has nothing to do with quicklist */ + listTypeTryConvertQuicklist(o, lct == LIST_CONV_SHRINKING, fn, data); + } else if (o->encoding == OBJ_ENCODING_LISTPACK) { + if (lct == LIST_CONV_SHRINKING) return; /* Shrinking has nothing to do with listpack */ + listTypeTryConvertListpack(o, argv, start, end, fn, data); + } else { + serverPanic("Unknown list encoding"); + } +} + +/* This is just a wrapper for listTypeTryConversionRaw() that is + * able to try conversion without passing 'argv'. */ +void listTypeTryConversion(robj *o, list_conv_type lct, beforeConvertCB fn, void *data) { + listTypeTryConversionRaw(o, lct, NULL, 0, 0, fn, data); +} + +/* This is just a wrapper for listTypeTryConversionRaw() that is + * able to try conversion before adding elements to the list. */ +void listTypeTryConversionAppend(robj *o, robj **argv, int start, int end, + beforeConvertCB fn, void *data) +{ + listTypeTryConversionRaw(o, LIST_CONV_GROWING, argv, start, end, fn, data); +} + /* The function pushes an element to the specified list object 'subject', * at head or tail position as specified by 'where'. * @@ -48,6 +173,16 @@ void listTypePush(robj *subject, robj *value, int where) { } else { quicklistPush(subject->ptr, value->ptr, sdslen(value->ptr), pos); } + } else if (subject->encoding == OBJ_ENCODING_LISTPACK) { + if (value->encoding == OBJ_ENCODING_INT) { + subject->ptr = (where == LIST_HEAD) ? + lpPrependInteger(subject->ptr, (long)value->ptr) : + lpAppendInteger(subject->ptr, (long)value->ptr); + } else { + subject->ptr = (where == LIST_HEAD) ? + lpPrepend(subject->ptr, value->ptr, sdslen(value->ptr)) : + lpAppend(subject->ptr, value->ptr, sdslen(value->ptr)); + } } else { serverPanic("Unknown list encoding"); } @@ -58,16 +193,28 @@ void *listPopSaver(unsigned char *data, size_t sz) { } robj *listTypePop(robj *subject, int where) { - long long vlong; robj *value = NULL; - int ql_where = where == LIST_HEAD ? QUICKLIST_HEAD : QUICKLIST_TAIL; if (subject->encoding == OBJ_ENCODING_QUICKLIST) { + long long vlong; + int ql_where = where == LIST_HEAD ? QUICKLIST_HEAD : QUICKLIST_TAIL; if (quicklistPopCustom(subject->ptr, ql_where, (unsigned char **)&value, NULL, &vlong, listPopSaver)) { if (!value) value = createStringObjectFromLongLong(vlong); } + } else if (subject->encoding == OBJ_ENCODING_LISTPACK) { + unsigned char *p; + unsigned char *vstr; + int64_t vlen; + unsigned char intbuf[LP_INTBUF_SIZE]; + + p = (where == LIST_HEAD) ? lpFirst(subject->ptr) : lpLast(subject->ptr); + if (p) { + vstr = lpGet(p, &vlen, intbuf); + value = createStringObject((char*)vstr, vlen); + subject->ptr = lpDelete(subject->ptr, p, NULL); + } } else { serverPanic("Unknown list encoding"); } @@ -77,6 +224,8 @@ robj *listTypePop(robj *subject, int where) { unsigned long listTypeLength(const robj *subject) { if (subject->encoding == OBJ_ENCODING_QUICKLIST) { return quicklistCount(subject->ptr); + } else if (subject->encoding == OBJ_ENCODING_LISTPACK) { + return lpLength(subject->ptr); } else { serverPanic("Unknown list encoding"); } @@ -92,11 +241,12 @@ listTypeIterator *listTypeInitIterator(robj *subject, long index, li->iter = NULL; /* LIST_HEAD means start at TAIL and move *towards* head. * LIST_TAIL means start at HEAD and move *towards tail. */ - int iter_direction = - direction == LIST_HEAD ? AL_START_TAIL : AL_START_HEAD; if (li->encoding == OBJ_ENCODING_QUICKLIST) { + int iter_direction = direction == LIST_HEAD ? AL_START_TAIL : AL_START_HEAD; li->iter = quicklistGetIteratorAtIdx(li->subject->ptr, iter_direction, index); + } else if (li->encoding == OBJ_ENCODING_LISTPACK) { + li->lpi = lpSeek(subject->ptr, index); } else { serverPanic("Unknown list encoding"); } @@ -104,15 +254,27 @@ listTypeIterator *listTypeInitIterator(robj *subject, long index, } /* Sets the direction of an iterator. */ -void listTypeSetIteratorDirection(listTypeIterator *li, unsigned char direction) { +void listTypeSetIteratorDirection(listTypeIterator *li, listTypeEntry *entry, unsigned char direction) { + if (li->direction == direction) return; + li->direction = direction; - int dir = direction == LIST_HEAD ? AL_START_TAIL : AL_START_HEAD; - quicklistSetDirection(li->iter, dir); + if (li->encoding == OBJ_ENCODING_QUICKLIST) { + int dir = direction == LIST_HEAD ? AL_START_TAIL : AL_START_HEAD; + quicklistSetDirection(li->iter, dir); + } else if (li->encoding == OBJ_ENCODING_LISTPACK) { + unsigned char *lp = li->subject->ptr; + /* Note that the iterator for listpack always points to the next of the current entry, + * so we need to update position of the iterator depending on the direction. */ + li->lpi = (direction == LIST_TAIL) ? lpNext(lp, entry->lpe) : lpPrev(lp, entry->lpe); + } else { + serverPanic("Unknown list encoding"); + } } /* Clean up the iterator. */ void listTypeReleaseIterator(listTypeIterator *li) { - quicklistReleaseIterator(li->iter); + if (li->encoding == OBJ_ENCODING_QUICKLIST) + quicklistReleaseIterator(li->iter); zfree(li); } @@ -126,62 +288,129 @@ int listTypeNext(listTypeIterator *li, listTypeEntry *entry) { entry->li = li; if (li->encoding == OBJ_ENCODING_QUICKLIST) { return quicklistNext(li->iter, &entry->entry); + } else if (li->encoding == OBJ_ENCODING_LISTPACK) { + entry->lpe = li->lpi; + if (entry->lpe != NULL) { + li->lpi = (li->direction == LIST_TAIL) ? + lpNext(li->subject->ptr,li->lpi) : lpPrev(li->subject->ptr,li->lpi); + return 1; + } } else { serverPanic("Unknown list encoding"); } return 0; } -/* Return entry or NULL at the current position of the iterator. */ -robj *listTypeGet(listTypeEntry *entry) { - robj *value = NULL; +/* Get entry value at the current position of the iterator. + * When the function returns NULL, it populates the integer value by + * reference in 'lval'. Otherwise a pointer to the string is returned, + * and 'vlen' is set to the length of the string. */ +unsigned char *listTypeGetValue(listTypeEntry *entry, size_t *vlen, long long *lval) { + unsigned char *vstr = NULL; if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) { if (entry->entry.value) { - value = createStringObject((char *)entry->entry.value, - entry->entry.sz); + vstr = entry->entry.value; + *vlen = entry->entry.sz; } else { - value = createStringObjectFromLongLong(entry->entry.longval); + *lval = entry->entry.longval; } + } else if (entry->li->encoding == OBJ_ENCODING_LISTPACK) { + unsigned int slen; + vstr = lpGetValue(entry->lpe, &slen, lval); + *vlen = slen; } else { serverPanic("Unknown list encoding"); } - return value; + return vstr; +} + +/* Return entry or NULL at the current position of the iterator. */ +robj *listTypeGet(listTypeEntry *entry) { + unsigned char *vstr; + size_t vlen; + long long lval; + + vstr = listTypeGetValue(entry, &vlen, &lval); + if (vstr) + return createStringObject((char *)vstr, vlen); + else + return createStringObjectFromLongLong(lval); } void listTypeInsert(listTypeEntry *entry, robj *value, int where) { + robj *subject = entry->li->subject; + value = getDecodedObject(value); + sds str = value->ptr; + size_t len = sdslen(str); + if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) { - value = getDecodedObject(value); - sds str = value->ptr; - size_t len = sdslen(str); if (where == LIST_TAIL) { quicklistInsertAfter(entry->li->iter, &entry->entry, str, len); } else if (where == LIST_HEAD) { quicklistInsertBefore(entry->li->iter, &entry->entry, str, len); } - decrRefCount(value); + } else if (entry->li->encoding == OBJ_ENCODING_LISTPACK) { + int lpw = (where == LIST_TAIL) ? LP_AFTER : LP_BEFORE; + subject->ptr = lpInsertString(subject->ptr, (unsigned char *)str, + len, entry->lpe, lpw, &entry->lpe); } else { serverPanic("Unknown list encoding"); } + decrRefCount(value); } /* Replaces entry at the current position of the iterator. */ void listTypeReplace(listTypeEntry *entry, robj *value) { + robj *subject = entry->li->subject; + value = getDecodedObject(value); + sds str = value->ptr; + size_t len = sdslen(str); + if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) { - value = getDecodedObject(value); - sds str = value->ptr; - size_t len = sdslen(str); quicklistReplaceEntry(entry->li->iter, &entry->entry, str, len); - decrRefCount(value); + } else if (entry->li->encoding == OBJ_ENCODING_LISTPACK) { + subject->ptr = lpReplace(subject->ptr, &entry->lpe, (unsigned char *)str, len); + } else { + serverPanic("Unknown list encoding"); + } + + decrRefCount(value); +} + +/* Replace entry at offset 'index' by 'value'. + * + * Returns 1 if replace happened. + * Returns 0 if replace failed and no changes happened. */ +int listTypeReplaceAtIndex(robj *o, int index, robj *value) { + value = getDecodedObject(value); + sds vstr = value->ptr; + size_t vlen = sdslen(vstr); + int replaced = 0; + + if (o->encoding == OBJ_ENCODING_QUICKLIST) { + quicklist *ql = o->ptr; + replaced = quicklistReplaceAtIndex(ql, index, vstr, vlen); + } else if (o->encoding == OBJ_ENCODING_LISTPACK) { + unsigned char *p = lpSeek(o->ptr,index); + if (p) { + o->ptr = lpReplace(o->ptr, &p, (unsigned char *)vstr, vlen); + replaced = 1; + } } else { serverPanic("Unknown list encoding"); } + + decrRefCount(value); + return replaced; } /* Compare the given object with the entry at the current position. */ int listTypeEqual(listTypeEntry *entry, robj *o) { + serverAssertWithInfo(NULL,o,sdsEncodedObject(o)); if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) { - serverAssertWithInfo(NULL,o,sdsEncodedObject(o)); return quicklistCompare(&entry->entry,o->ptr,sdslen(o->ptr)); + } else if (entry->li->encoding == OBJ_ENCODING_LISTPACK) { + return lpCompare(entry->lpe,o->ptr,sdslen(o->ptr)); } else { serverPanic("Unknown list encoding"); } @@ -191,6 +420,22 @@ int listTypeEqual(listTypeEntry *entry, robj *o) { void listTypeDelete(listTypeIterator *iter, listTypeEntry *entry) { if (entry->li->encoding == OBJ_ENCODING_QUICKLIST) { quicklistDelEntry(iter->iter, &entry->entry); + } else if (entry->li->encoding == OBJ_ENCODING_LISTPACK) { + unsigned char *p = entry->lpe; + iter->subject->ptr = lpDelete(iter->subject->ptr,p,&p); + + /* Update position of the iterator depending on the direction */ + if (iter->direction == LIST_TAIL) + iter->lpi = p; + else { + if (p) { + iter->lpi = lpPrev(iter->subject->ptr,p); + } else { + /* We deleted the last element, so we need to set the + * iterator to the last element. */ + iter->lpi = lpLast(iter->subject->ptr); + } + } } else { serverPanic("Unknown list encoding"); } @@ -207,21 +452,26 @@ robj *listTypeDup(robj *o) { serverAssert(o->type == OBJ_LIST); switch (o->encoding) { + case OBJ_ENCODING_LISTPACK: + lobj = createObject(OBJ_LIST, lpDup(o->ptr)); + break; case OBJ_ENCODING_QUICKLIST: lobj = createObject(OBJ_LIST, quicklistDup(o->ptr)); - lobj->encoding = o->encoding; break; default: serverPanic("Unknown list encoding"); break; } + lobj->encoding = o->encoding; return lobj; } /* Delete a range of elements from the list. */ -int listTypeDelRange(robj *subject, long start, long count) { +void listTypeDelRange(robj *subject, long start, long count) { if (subject->encoding == OBJ_ENCODING_QUICKLIST) { - return quicklistDelRange(subject->ptr, start, count); + quicklistDelRange(subject->ptr, start, count); + } else if (subject->encoding == OBJ_ENCODING_LISTPACK) { + subject->ptr = lpDeleteRange(subject->ptr, start, count); } else { serverPanic("Unknown list encoding"); } @@ -244,12 +494,11 @@ void pushGenericCommand(client *c, int where, int xx) { return; } - lobj = createQuicklistObject(); - quicklistSetOptions(lobj->ptr, server.list_max_listpack_size, - server.list_compress_depth); + lobj = createListListpackObject(); dbAdd(c->db,c->argv[1],lobj); } + listTypeTryConversionAppend(lobj,c->argv,2,c->argc-1,NULL,NULL); for (j = 2; j < c->argc; j++) { listTypePush(lobj,c->argv[j],where); server.dirty++; @@ -302,6 +551,13 @@ void linsertCommand(client *c) { if ((subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || checkType(c,subject,OBJ_LIST)) return; + /* We're not sure if this value can be inserted yet, but we cannot + * convert the list inside the iterator. We don't want to loop over + * the list twice (once to see if the value can be inserted and once + * to do the actual insert), so we assume this value can be inserted + * and convert the listpack to a regular list if necessary. */ + listTypeTryConversionAppend(subject,c->argv,4,4,NULL,NULL); + /* Seek pivot from head to tail */ iter = listTypeInitIterator(subject,0,LIST_TAIL); while (listTypeNext(iter,&entry)) { @@ -343,22 +599,24 @@ void lindexCommand(client *c) { if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != C_OK)) return; - if (o->encoding == OBJ_ENCODING_QUICKLIST) { - quicklistEntry entry; - quicklistIter *iter = quicklistGetIteratorEntryAtIdx(o->ptr, index, &entry); - if (iter) { - if (entry.value) { - addReplyBulkCBuffer(c, entry.value, entry.sz); - } else { - addReplyBulkLongLong(c, entry.longval); - } + listTypeIterator *iter = listTypeInitIterator(o,index,LIST_TAIL); + listTypeEntry entry; + unsigned char *vstr; + size_t vlen; + long long lval; + + if (listTypeNext(iter,&entry)) { + vstr = listTypeGetValue(&entry,&vlen,&lval); + if (vstr) { + addReplyBulkCBuffer(c, vstr, vlen); } else { - addReplyNull(c); + addReplyBulkLongLong(c, lval); } - quicklistReleaseIterator(iter); } else { - serverPanic("Unknown list encoding"); + addReplyNull(c); } + + listTypeReleaseIterator(iter); } /* LSET <key> <index> <element> */ @@ -371,20 +629,19 @@ void lsetCommand(client *c) { if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != C_OK)) return; - if (o->encoding == OBJ_ENCODING_QUICKLIST) { - quicklist *ql = o->ptr; - int replaced = quicklistReplaceAtIndex(ql, index, - value->ptr, sdslen(value->ptr)); - if (!replaced) { - addReplyErrorObject(c,shared.outofrangeerr); - } else { - addReply(c,shared.ok); - signalModifiedKey(c,c->db,c->argv[1]); - notifyKeyspaceEvent(NOTIFY_LIST,"lset",c->argv[1],c->db->id); - server.dirty++; - } + listTypeTryConversionAppend(o,c->argv,3,3,NULL,NULL); + if (listTypeReplaceAtIndex(o,index,value)) { + addReply(c,shared.ok); + signalModifiedKey(c,c->db,c->argv[1]); + notifyKeyspaceEvent(NOTIFY_LIST,"lset",c->argv[1],c->db->id); + server.dirty++; + + /* We might replace a big item with a small one or vice versa, but we've + * already handled the growing case in listTypeTryConversionAppend() + * above, so here we just need to try the conversion for shrinking. */ + listTypeTryConversion(o,LIST_CONV_SHRINKING,NULL,NULL); } else { - serverPanic("Unknown list encoding"); + addReplyErrorObject(c,shared.outofrangeerr); } } @@ -417,6 +674,51 @@ void listPopRangeAndReplyWithKey(client *c, robj *o, robj *key, int where, long listElementsRemoved(c, key, where, o, rangelen, signal, deleted); } +/* Extracted from `addListRangeReply()` to reply with a quicklist list. + * Note that the purpose is to make the methods small so that the + * code in the loop can be inlined better to improve performance. */ +void addListQuicklistRangeReply(client *c, robj *o, int from, int rangelen, int reverse) { + /* Return the result in form of a multi-bulk reply */ + addReplyArrayLen(c,rangelen); + + int direction = reverse ? AL_START_TAIL : AL_START_HEAD; + quicklistIter *iter = quicklistGetIteratorAtIdx(o->ptr, direction, from); + while(rangelen--) { + quicklistEntry qe; + serverAssert(quicklistNext(iter, &qe)); /* fail on corrupt data */ + if (qe.value) { + addReplyBulkCBuffer(c,qe.value,qe.sz); + } else { + addReplyBulkLongLong(c,qe.longval); + } + } + quicklistReleaseIterator(iter); +} + +/* Extracted from `addListRangeReply()` to reply with a listpack list. + * Note that the purpose is to make the methods small so that the + * code in the loop can be inlined better to improve performance. */ +void addListListpackRangeReply(client *c, robj *o, int from, int rangelen, int reverse) { + unsigned char *p = lpSeek(o->ptr, from); + unsigned char *vstr; + unsigned int vlen; + long long lval; + + /* Return the result in form of a multi-bulk reply */ + addReplyArrayLen(c,rangelen); + + while(rangelen--) { + serverAssert(p); /* fail on corrupt data */ + vstr = lpGetValue(p, &vlen, &lval); + if (vstr) { + addReplyBulkCBuffer(c,vstr,vlen); + } else { + addReplyBulkLongLong(c,lval); + } + p = reverse ? lpPrev(o->ptr,p) : lpNext(o->ptr,p); + } +} + /* A helper for replying with a list's range between the inclusive start and end * indexes as multi-bulk, with support for negative indexes. Note that start * must be less than end or an empty array is returned. When the reverse @@ -439,27 +741,13 @@ void addListRangeReply(client *c, robj *o, long start, long end, int reverse) { if (end >= llen) end = llen-1; rangelen = (end-start)+1; - /* Return the result in form of a multi-bulk reply */ - addReplyArrayLen(c,rangelen); - if (o->encoding == OBJ_ENCODING_QUICKLIST) { - int from = reverse ? end : start; - int direction = reverse ? LIST_HEAD : LIST_TAIL; - listTypeIterator *iter = listTypeInitIterator(o,from,direction); - - while(rangelen--) { - listTypeEntry entry; - serverAssert(listTypeNext(iter, &entry)); /* fail on corrupt data */ - quicklistEntry *qe = &entry.entry; - if (qe->value) { - addReplyBulkCBuffer(c,qe->value,qe->sz); - } else { - addReplyBulkLongLong(c,qe->longval); - } - } - listTypeReleaseIterator(iter); - } else { + int from = reverse ? end : start; + if (o->encoding == OBJ_ENCODING_QUICKLIST) + addListQuicklistRangeReply(c, o, from, rangelen, reverse); + else if (o->encoding == OBJ_ENCODING_LISTPACK) + addListListpackRangeReply(c, o, from, rangelen, reverse); + else serverPanic("Unknown list encoding"); - } } /* A housekeeping helper for list elements popping tasks. @@ -478,6 +766,7 @@ void listElementsRemoved(client *c, robj *key, int where, robj *o, long count, i dbDelete(c->db, key); notifyKeyspaceEvent(NOTIFY_GENERIC, "del", key, c->db->id); } else { + listTypeTryConversion(o, LIST_CONV_SHRINKING, NULL, NULL); if (deleted) *deleted = 0; } if (signal) signalModifiedKey(c, c->db, key); @@ -633,6 +922,9 @@ void ltrimCommand(client *c) { if (o->encoding == OBJ_ENCODING_QUICKLIST) { quicklistDelRange(o->ptr,0,ltrim); quicklistDelRange(o->ptr,-rtrim,rtrim); + } else if (o->encoding == OBJ_ENCODING_LISTPACK) { + o->ptr = lpDeleteRange(o->ptr,0,ltrim); + o->ptr = lpDeleteRange(o->ptr,-rtrim,rtrim); } else { serverPanic("Unknown list encoding"); } @@ -641,6 +933,8 @@ void ltrimCommand(client *c) { if (listTypeLength(o) == 0) { dbDelete(c->db,c->argv[1]); notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],c->db->id); + } else { + listTypeTryConversion(o,LIST_CONV_SHRINKING,NULL,NULL); } signalModifiedKey(c,c->db,c->argv[1]); server.dirty += (ltrim + rtrim); @@ -799,6 +1093,8 @@ void lremCommand(client *c) { if (listTypeLength(subject) == 0) { dbDelete(c->db,c->argv[1]); notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],c->db->id); + } else if (removed) { + listTypeTryConversion(subject,LIST_CONV_SHRINKING,NULL,NULL); } addReplyLongLong(c,removed); @@ -808,12 +1104,11 @@ void lmoveHandlePush(client *c, robj *dstkey, robj *dstobj, robj *value, int where) { /* Create the list if the key does not exist */ if (!dstobj) { - dstobj = createQuicklistObject(); - quicklistSetOptions(dstobj->ptr, server.list_max_listpack_size, - server.list_compress_depth); + dstobj = createListListpackObject(); dbAdd(c->db,dstkey,dstobj); } signalModifiedKey(c,c->db,dstkey); + listTypeTryConversionAppend(dstobj,&value,0,0,NULL,NULL); listTypePush(dstobj,value,where); notifyKeyspaceEvent(NOTIFY_LIST, where == LIST_HEAD ? "lpush" : "rpush", |