diff --git a/redis.c b/redis.c index 3fe9af1c..f6a765da 100644 --- a/redis.c +++ b/redis.c @@ -129,11 +129,11 @@ #define REDIS_ENCODING_INT 1 /* Encoded as integer */ #define REDIS_ENCODING_HT 2 /* Encoded as hash table */ #define REDIS_ENCODING_ZIPMAP 3 /* Encoded as zipmap */ -#define REDIS_ENCODING_LIST 4 /* Encoded as zipmap */ +#define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */ #define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */ static char* strencoding[] = { - "raw", "int", "hashtable", "zipmap", "list", "ziplist" + "raw", "int", "hashtable", "zipmap", "linkedlist", "ziplist" }; /* Object types only used for dumping to disk */ @@ -3059,7 +3059,7 @@ static robj *createListObject(void) { list *l = listCreate(); robj *o = createObject(REDIS_LIST,l); listSetFreeMethod(l,decrRefCount); - o->encoding = REDIS_ENCODING_LIST; + o->encoding = REDIS_ENCODING_LINKEDLIST; return o; } @@ -3101,7 +3101,7 @@ static void freeStringObject(robj *o) { static void freeListObject(robj *o) { switch (o->encoding) { - case REDIS_ENCODING_LIST: + case REDIS_ENCODING_LINKEDLIST: listRelease((list*) o->ptr); break; case REDIS_ENCODING_ZIPLIST: @@ -3777,7 +3777,7 @@ static int rdbSaveObject(FILE *fp, robj *o) { } p = ziplistNext(o->ptr,p); } - } else if (o->encoding == REDIS_ENCODING_LIST) { + } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) { list *list = o->ptr; listIter li; listNode *ln; @@ -4187,7 +4187,7 @@ static robj *rdbLoadObject(int type, FILE *fp) { if (o->encoding == REDIS_ENCODING_ZIPLIST && ele->encoding == REDIS_ENCODING_RAW && sdslen(ele->ptr) > server.list_max_ziplist_value) - listTypeConvert(o,REDIS_ENCODING_LIST); + listTypeConvert(o,REDIS_ENCODING_LINKEDLIST); if (o->encoding == REDIS_ENCODING_ZIPLIST) { dec = getDecodedObject(ele); @@ -4927,7 +4927,7 @@ static void listTypeTryConversion(robj *subject, robj *value) { if (subject->encoding != REDIS_ENCODING_ZIPLIST) return; if (value->encoding == REDIS_ENCODING_RAW && sdslen(value->ptr) > server.list_max_ziplist_value) - listTypeConvert(subject,REDIS_ENCODING_LIST); + listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST); } static void listTypePush(robj *subject, robj *value, int where) { @@ -4935,14 +4935,14 @@ static void listTypePush(robj *subject, robj *value, int where) { listTypeTryConversion(subject,value); if (subject->encoding == REDIS_ENCODING_ZIPLIST && ziplistLen(subject->ptr) >= server.list_max_ziplist_entries) - listTypeConvert(subject,REDIS_ENCODING_LIST); + listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST); if (subject->encoding == REDIS_ENCODING_ZIPLIST) { int pos = (where == REDIS_HEAD) ? ZIPLIST_HEAD : ZIPLIST_TAIL; value = getDecodedObject(value); subject->ptr = ziplistPush(subject->ptr,value->ptr,sdslen(value->ptr),pos); decrRefCount(value); - } else if (subject->encoding == REDIS_ENCODING_LIST) { + } else if (subject->encoding == REDIS_ENCODING_LINKEDLIST) { if (where == REDIS_HEAD) { listAddNodeHead(subject->ptr,value); } else { @@ -4972,7 +4972,7 @@ static robj *listTypePop(robj *subject, int where) { /* We only need to delete an element when it exists */ subject->ptr = ziplistDelete(subject->ptr,&p); } - } else if (subject->encoding == REDIS_ENCODING_LIST) { + } else if (subject->encoding == REDIS_ENCODING_LINKEDLIST) { list *list = subject->ptr; listNode *ln; if (where == REDIS_HEAD) { @@ -4994,7 +4994,7 @@ static robj *listTypePop(robj *subject, int where) { static unsigned long listTypeLength(robj *subject) { if (subject->encoding == REDIS_ENCODING_ZIPLIST) { return ziplistLen(subject->ptr); - } else if (subject->encoding == REDIS_ENCODING_LIST) { + } else if (subject->encoding == REDIS_ENCODING_LINKEDLIST) { return listLength((list*)subject->ptr); } else { redisPanic("Unknown list encoding"); @@ -5025,7 +5025,7 @@ static listTypeIterator *listTypeInitIterator(robj *subject, int index, unsigned li->direction = direction; if (li->encoding == REDIS_ENCODING_ZIPLIST) { li->zi = ziplistIndex(subject->ptr,index); - } else if (li->encoding == REDIS_ENCODING_LIST) { + } else if (li->encoding == REDIS_ENCODING_LINKEDLIST) { li->ln = listIndex(subject->ptr,index); } else { redisPanic("Unknown list encoding"); @@ -5055,7 +5055,7 @@ static int listTypeNext(listTypeIterator *li, listTypeEntry *entry) { li->zi = ziplistPrev(li->subject->ptr,li->zi); return 1; } - } else if (li->encoding == REDIS_ENCODING_LIST) { + } else if (li->encoding == REDIS_ENCODING_LINKEDLIST) { entry->ln = li->ln; if (entry->ln != NULL) { if (li->direction == REDIS_TAIL) @@ -5086,7 +5086,7 @@ static robj *listTypeGet(listTypeEntry *entry) { value = createStringObjectFromLongLong(vlong); } } - } else if (li->encoding == REDIS_ENCODING_LIST) { + } else if (li->encoding == REDIS_ENCODING_LINKEDLIST) { redisAssert(entry->ln != NULL); value = listNodeValue(entry->ln); incrRefCount(value); @@ -5114,7 +5114,7 @@ static void listTypeInsert(listTypeEntry *entry, robj *value, int where) { subject->ptr = ziplistInsert(subject->ptr,entry->zi,value->ptr,sdslen(value->ptr)); } decrRefCount(value); - } else if (entry->li->encoding == REDIS_ENCODING_LIST) { + } else if (entry->li->encoding == REDIS_ENCODING_LINKEDLIST) { if (where == REDIS_TAIL) { listInsertNode(subject->ptr,entry->ln,value,AL_START_TAIL); } else { @@ -5132,7 +5132,7 @@ static int listTypeEqual(listTypeEntry *entry, robj *o) { if (li->encoding == REDIS_ENCODING_ZIPLIST) { redisAssert(o->encoding == REDIS_ENCODING_RAW); return ziplistCompare(entry->zi,o->ptr,sdslen(o->ptr)); - } else if (li->encoding == REDIS_ENCODING_LIST) { + } else if (li->encoding == REDIS_ENCODING_LINKEDLIST) { return equalStringObjects(o,listNodeValue(entry->ln)); } else { redisPanic("Unknown list encoding"); @@ -5151,7 +5151,7 @@ static void listTypeDelete(listTypeEntry *entry) { li->zi = p; else li->zi = ziplistPrev(li->subject->ptr,p); - } else if (entry->li->encoding == REDIS_ENCODING_LIST) { + } else if (entry->li->encoding == REDIS_ENCODING_LINKEDLIST) { listNode *next; if (li->direction == REDIS_TAIL) next = entry->ln->next; @@ -5169,7 +5169,7 @@ static void listTypeConvert(robj *subject, int enc) { listTypeEntry entry; redisAssert(subject->type == REDIS_LIST); - if (enc == REDIS_ENCODING_LIST) { + if (enc == REDIS_ENCODING_LINKEDLIST) { list *l = listCreate(); listSetFreeMethod(l,decrRefCount); @@ -5178,7 +5178,7 @@ static void listTypeConvert(robj *subject, int enc) { while (listTypeNext(li,&entry)) listAddNodeTail(l,listTypeGet(&entry)); listTypeReleaseIterator(li); - subject->encoding = REDIS_ENCODING_LIST; + subject->encoding = REDIS_ENCODING_LINKEDLIST; zfree(subject->ptr); subject->ptr = l; } else { @@ -5254,7 +5254,7 @@ static void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int whe /* Check if the length exceeds the ziplist length threshold. */ if (subject->encoding == REDIS_ENCODING_ZIPLIST && ziplistLen(subject->ptr) > server.list_max_ziplist_entries) - listTypeConvert(subject,REDIS_ENCODING_LIST); + listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST); server.dirty++; } else { /* Notify client of a failed insert */ @@ -5316,7 +5316,7 @@ static void lindexCommand(redisClient *c) { } else { addReply(c,shared.nullbulk); } - } else if (o->encoding == REDIS_ENCODING_LIST) { + } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) { listNode *ln = listIndex(o->ptr,index); if (ln != NULL) { value = listNodeValue(ln); @@ -5349,7 +5349,7 @@ static void lsetCommand(redisClient *c) { addReply(c,shared.ok); server.dirty++; } - } else if (o->encoding == REDIS_ENCODING_LIST) { + } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) { listNode *ln = listIndex(o->ptr,index); if (ln == NULL) { addReply(c,shared.outofrangeerr); @@ -5461,7 +5461,7 @@ static void ltrimCommand(redisClient *c) { if (o->encoding == REDIS_ENCODING_ZIPLIST) { o->ptr = ziplistDeleteRange(o->ptr,0,ltrim); o->ptr = ziplistDeleteRange(o->ptr,-rtrim,rtrim); - } else if (o->encoding == REDIS_ENCODING_LIST) { + } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) { list = o->ptr; for (j = 0; j < ltrim; j++) { ln = listFirst(list); @@ -9151,7 +9151,7 @@ static int rewriteAppendOnlyFile(char *filename) { } p = ziplistNext(zl,p); } - } else if (o->encoding == REDIS_ENCODING_LIST) { + } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) { list *list = o->ptr; listNode *ln; listIter li; diff --git a/tests/support/test.tcl b/tests/support/test.tcl index 4caa6ca7..988189bf 100644 --- a/tests/support/test.tcl +++ b/tests/support/test.tcl @@ -26,6 +26,8 @@ proc assert_error {pattern code} { } proc assert_encoding {enc key} { + # swapped out value doesn't have encoding, so swap in first + r debug swapin $key assert_match "* encoding:$enc *" [r debug object $key] } diff --git a/tests/unit/type/list.tcl b/tests/unit/type/list.tcl index 1d69a88f..ecae5d22 100644 --- a/tests/unit/type/list.tcl +++ b/tests/unit/type/list.tcl @@ -33,7 +33,7 @@ start_server { # first lpush then rpush assert_equal 1 [r lpush mylist1 $large_value] - assert_encoding list mylist1 + assert_encoding linkedlist mylist1 assert_equal 2 [r rpush mylist1 b] assert_equal 3 [r rpush mylist1 c] assert_equal 3 [r llen mylist1] @@ -43,7 +43,7 @@ start_server { # first rpush then lpush assert_equal 1 [r rpush mylist2 $large_value] - assert_encoding list mylist2 + assert_encoding linkedlist mylist2 assert_equal 2 [r lpush mylist2 b] assert_equal 3 [r lpush mylist2 c] assert_equal 3 [r llen mylist2] @@ -70,12 +70,12 @@ start_server { assert_encoding ziplist $key } - proc create_list {key entries} { + proc create_linkedlist {key entries} { r del $key r rpush $key "aaaaaaaaaaaaaaaaa" foreach entry $entries { r rpush $key $entry } assert_equal "aaaaaaaaaaaaaaaaa" [r lpop $key] - assert_encoding list $key + assert_encoding linkedlist $key } test {LPUSHX, RPUSHX - generic} { @@ -86,7 +86,7 @@ start_server { assert_equal 0 [r llen xlist] } - foreach type {ziplist list} { + foreach type {ziplist linkedlist} { test "LPUSHX, RPUSHX - $type" { create_$type xlist {b c} assert_equal 3 [r rpushx xlist d] @@ -119,18 +119,18 @@ start_server { # convert when a large value is pushed create_ziplist xlist a assert_equal 2 [r rpushx xlist $large_value] - assert_encoding list xlist + assert_encoding linkedlist xlist create_ziplist xlist a assert_equal 2 [r lpushx xlist $large_value] - assert_encoding list xlist + assert_encoding linkedlist xlist # convert when the length threshold is exceeded create_ziplist xlist [lrepeat 256 a] assert_equal 257 [r rpushx xlist b] - assert_encoding list xlist + assert_encoding linkedlist xlist create_ziplist xlist [lrepeat 256 a] assert_equal 257 [r lpushx xlist b] - assert_encoding list xlist + assert_encoding linkedlist xlist } test {LINSERT convert from ziplist to list} { @@ -139,18 +139,18 @@ start_server { # convert when a large value is inserted create_ziplist xlist a assert_equal 2 [r linsert xlist before a $large_value] - assert_encoding list xlist + assert_encoding linkedlist xlist create_ziplist xlist a assert_equal 2 [r linsert xlist after a $large_value] - assert_encoding list xlist + assert_encoding linkedlist xlist # convert when the length threshold is exceeded create_ziplist xlist [lrepeat 256 a] assert_equal 257 [r linsert xlist before a a] - assert_encoding list xlist + assert_encoding linkedlist xlist create_ziplist xlist [lrepeat 256 a] assert_equal 257 [r linsert xlist after a a] - assert_encoding list xlist + assert_encoding linkedlist xlist # don't convert when the value could not be inserted create_ziplist xlist [lrepeat 256 a] @@ -161,7 +161,7 @@ start_server { assert_encoding ziplist xlist } - foreach {type num} {ziplist 250 list 500} { + foreach {type num} {ziplist 250 linkedlist 500} { proc check_numbered_list_consistency {key} { set len [r llen $key] for {set i 0} {$i < $len} {incr i} { @@ -227,7 +227,7 @@ start_server { assert_error ERR* {r rpush mylist 0} } - foreach type {ziplist list} { + foreach type {ziplist linkedlist} { test "RPOPLPUSH base case - $type" { r del mylist1 mylist2 create_$type mylist1 {a b c d} @@ -245,7 +245,7 @@ start_server { assert_equal {c a b} [r lrange mylist 0 -1] } - foreach othertype {ziplist list} { + foreach othertype {ziplist linkedlist} { test "RPOPLPUSH with $type source and existing target $othertype" { create_$type srclist {a b c d} create_$othertype dstlist {x} @@ -285,7 +285,7 @@ start_server { assert_equal {} [r rpoplpush srclist dstlist] } {} - foreach type {ziplist list} { + foreach type {ziplist linkedlist} { test "Basic LPOP/RPOP - $type" { create_$type mylist {0 1 2} assert_equal 0 [r lpop mylist] @@ -305,7 +305,7 @@ start_server { assert_error ERR*kind* {r rpop notalist} } - foreach {type num} {ziplist 250 list 500} { + foreach {type num} {ziplist 250 linkedlist 500} { test "Mass RPOP/LPOP - $type" { r del mylist set sum1 0 @@ -323,7 +323,7 @@ start_server { } } - foreach type {ziplist list} { + foreach type {ziplist linkedlist} { test "LRANGE basics - $type" { create_$type mylist {0 1 2 3 4 5 6 7 8 9} assert_equal {1 2 3 4 5 6 7 8} [r lrange mylist 1 -2] @@ -346,36 +346,65 @@ start_server { assert_equal {} [r lrange nosuchkey 0 1] } - foreach type {ziplist list} { - test "LTRIM basics - $type" { - create_$type mylist "foo" - for {set i 0} {$i < 100} {incr i} { - r lpush mylist $i - r ltrim mylist 0 4 - } + foreach type {ziplist linkedlist} { + proc trim_list {type min max} { + r del mylist + create_$type mylist {1 2 3 4 5} + r ltrim mylist $min $max r lrange mylist 0 -1 - } {99 98 97 96 95} - - test "LTRIM stress testing - $type" { - set mylist {} - for {set i 0} {$i < 20} {incr i} { - lappend mylist $i - } - - for {set j 0} {$j < 100} {incr j} { - create_$type mylist $mylist - - # Trim at random - set a [randomInt 20] - set b [randomInt 20] - r ltrim mylist $a $b - assert_equal [lrange $mylist $a $b] [r lrange mylist 0 -1] - } } + test "LTRIM basics - $type" { + assert_equal {1} [trim_list $type 0 0] + assert_equal {1 2} [trim_list $type 0 1] + assert_equal {1 2 3} [trim_list $type 0 2] + assert_equal {2 3} [trim_list $type 1 2] + assert_equal {2 3 4 5} [trim_list $type 1 -1] + assert_equal {2 3 4} [trim_list $type 1 -2] + assert_equal {4 5} [trim_list $type -2 -1] + assert_equal {5} [trim_list $type -1 -1] + assert_equal {1 2 3 4 5} [trim_list $type -5 -1] + assert_equal {1 2 3 4 5} [trim_list $type -10 10] + assert_equal {1 2 3 4 5} [trim_list $type 0 5] + assert_equal {1 2 3 4 5} [trim_list $type 0 10] + } + + tags {"slow"} { + test "LTRIM stress testing - $type" { + set mylist {} + set startlen 32 + r del mylist + for {set i 0} {$i < $startlen} {incr i} { + set str [randomInt 9223372036854775807] + r rpush mylist $str + lappend mylist $str + } + + # do a push/pop of a large value to convert to a real list + if {$type eq "list"} { + r rpush mylist "aaaaaaaaaaaaaaaaa" + r rpop mylist + assert_encoding linkedlist mylist + } + + for {set i 0} {$i < 10000} {incr i} { + set min [expr {int(rand()*$startlen)}] + set max [expr {$min+int(rand()*$startlen)}] + set mylist [lrange $mylist $min $max] + r ltrim mylist $min $max + assert_equal $mylist [r lrange mylist 0 -1] + + for {set j [r llen mylist]} {$j < $startlen} {incr j} { + set str [randomInt 9223372036854775807] + r rpush mylist $str + lappend mylist $str + } + } + } + } } - foreach type {ziplist list} { + foreach type {ziplist linkedlist} { test "LSET - $type" { create_$type mylist {99 98 97 96 95} r lset mylist 1 foo @@ -397,7 +426,7 @@ start_server { assert_error ERR*value* {r lset nolist 0 foo} } - foreach type {ziplist list} { + foreach type {ziplist linkedlist} { test "LREM remove all the occurrences - $type" { create_$type mylist {foo bar foobar foobared zap bar test foo} assert_equal 2 [r lrem mylist 0 bar]