Skip to content

Commit 05cb8af

Browse files
enjoy-binbinoranagrazuiderkwast
authored andcommitted
Fix zuiFind crash / RM_ScanKey hang on SET object listpack encoding (redis#11581)
In redis#11290, we added listpack encoding for SET object. But forgot to support it in zuiFind, causes ZINTER, ZINTERSTORE, ZINTERCARD, ZIDFF, ZDIFFSTORE to crash. And forgot to support it in RM_ScanKey, causes it hang. This PR add support SET listpack in zuiFind, and in RM_ScanKey. And add tests for related commands to cover this case. Other changes: - There is no reason for zuiFind to go into the internals of the SET. It can simply use setTypeIsMember and don't care about encoding. - Remove the `#include "intset.h"` from server.h reduce the chance of accidental intset API use. - Move setTypeAddAux, setTypeRemoveAux and setTypeIsMemberAux interfaces to the header. - In scanGenericCommand, use setTypeInitIterator and setTypeNext to handle OBJ_SET scan. - In RM_ScanKey, improve hash scan mode, use lpGetValue like zset, they can share code and better performance. The zuiFind part fixes redis#11578 Co-authored-by: Oran Agra <[email protected]> Co-authored-by: Viktor Söderqvist <[email protected]>
1 parent 99cab76 commit 05cb8af

File tree

10 files changed

+134
-65
lines changed

10 files changed

+134
-65
lines changed

src/db.c

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -946,14 +946,21 @@ void scanGenericCommand(client *c, robj *o, unsigned long cursor) {
946946
} while (cursor &&
947947
maxiterations-- &&
948948
listLength(keys) < (unsigned long)count);
949-
} else if (o->type == OBJ_SET && o->encoding == OBJ_ENCODING_INTSET) {
950-
int pos = 0;
951-
int64_t ll;
952-
953-
while(intsetGet(o->ptr,pos++,&ll))
954-
listAddNodeTail(keys,createStringObjectFromLongLong(ll));
949+
} else if (o->type == OBJ_SET) {
950+
char *str;
951+
size_t len;
952+
int64_t llele;
953+
setTypeIterator *si = setTypeInitIterator(o);
954+
while (setTypeNext(si, &str, &len, &llele) != -1) {
955+
if (str == NULL) {
956+
listAddNodeTail(keys, createStringObjectFromLongLong(llele));
957+
} else {
958+
listAddNodeTail(keys, createStringObject(str, len));
959+
}
960+
}
961+
setTypeReleaseIterator(si);
955962
cursor = 0;
956-
} else if ((o->type == OBJ_HASH || o->type == OBJ_ZSET || o->type == OBJ_SET) &&
963+
} else if ((o->type == OBJ_HASH || o->type == OBJ_ZSET) &&
957964
o->encoding == OBJ_ENCODING_LISTPACK)
958965
{
959966
unsigned char *p = lpFirst(o->ptr);

src/module.c

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10425,18 +10425,19 @@ int RM_ScanKey(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleSc
1042510425
cursor->done = 1;
1042610426
ret = 0;
1042710427
}
10428-
} else if (o->type == OBJ_SET && o->encoding == OBJ_ENCODING_INTSET) {
10429-
int pos = 0;
10430-
int64_t ll;
10431-
while(intsetGet(o->ptr,pos++,&ll)) {
10432-
robj *field = createObject(OBJ_STRING,sdsfromlonglong(ll));
10428+
} else if (o->type == OBJ_SET) {
10429+
setTypeIterator *si = setTypeInitIterator(o);
10430+
sds sdsele;
10431+
while ((sdsele = setTypeNextObject(si)) != NULL) {
10432+
robj *field = createObject(OBJ_STRING, sdsele);
1043310433
fn(key, field, NULL, privdata);
1043410434
decrRefCount(field);
1043510435
}
10436+
setTypeReleaseIterator(si);
1043610437
cursor->cursor = 1;
1043710438
cursor->done = 1;
1043810439
ret = 0;
10439-
} else if (o->type == OBJ_ZSET) {
10440+
} else if (o->type == OBJ_ZSET || o->type == OBJ_HASH) {
1044010441
unsigned char *p = lpSeek(o->ptr,0);
1044110442
unsigned char *vstr;
1044210443
unsigned int vlen;
@@ -10459,25 +10460,6 @@ int RM_ScanKey(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleSc
1045910460
cursor->cursor = 1;
1046010461
cursor->done = 1;
1046110462
ret = 0;
10462-
} else if (o->type == OBJ_HASH) {
10463-
unsigned char *p = lpFirst(o->ptr);
10464-
unsigned char *vstr;
10465-
int64_t vlen;
10466-
unsigned char intbuf[LP_INTBUF_SIZE];
10467-
while(p) {
10468-
vstr = lpGet(p,&vlen,intbuf);
10469-
robj *field = createStringObject((char*)vstr,vlen);
10470-
p = lpNext(o->ptr,p);
10471-
vstr = lpGet(p,&vlen,intbuf);
10472-
robj *value = createStringObject((char*)vstr,vlen);
10473-
fn(key, field, value, privdata);
10474-
p = lpNext(o->ptr,p);
10475-
decrRefCount(field);
10476-
decrRefCount(value);
10477-
}
10478-
cursor->cursor = 1;
10479-
cursor->done = 1;
10480-
ret = 0;
1048110463
}
1048210464
errno = 0;
1048310465
return ret;

src/object.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
#include "server.h"
3232
#include "functions.h"
33+
#include "intset.h" /* Compact integer set structure */
3334
#include <math.h>
3435
#include <ctype.h>
3536

src/rdb.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "fpconv_dtoa.h"
3535
#include "stream.h"
3636
#include "functions.h"
37+
#include "intset.h" /* Compact integer set structure */
3738

3839
#include <math.h>
3940
#include <fcntl.h>

src/server.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6856,6 +6856,7 @@ int iAmMaster(void) {
68566856

68576857
#ifdef REDIS_TEST
68586858
#include "testhelp.h"
6859+
#include "intset.h" /* Compact integer set structure */
68596860

68606861
int __failed_tests = 0;
68616862
int __test_num = 0;

src/server.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ typedef long long ustime_t; /* microsecond time type. */
7171
#include "adlist.h" /* Linked lists */
7272
#include "zmalloc.h" /* total memory usage aware version of malloc/free */
7373
#include "anet.h" /* Networking the easy way */
74-
#include "intset.h" /* Compact integer set structure */
7574
#include "version.h" /* Version macro */
7675
#include "util.h" /* Misc functions useful in many places */
7776
#include "latency.h" /* Latency monitor API */
@@ -3012,8 +3011,11 @@ int restartServer(int flags, mstime_t delay);
30123011
/* Set data type */
30133012
robj *setTypeCreate(sds value);
30143013
int setTypeAdd(robj *subject, sds value);
3014+
int setTypeAddAux(robj *set, char *str, size_t len, int64_t llval, int str_is_sds);
30153015
int setTypeRemove(robj *subject, sds value);
3016+
int setTypeRemoveAux(robj *set, char *str, size_t len, int64_t llval, int str_is_sds);
30163017
int setTypeIsMember(robj *subject, sds value);
3018+
int setTypeIsMemberAux(robj *set, char *str, size_t len, int64_t llval, int str_is_sds);
30173019
setTypeIterator *setTypeInitIterator(robj *subject);
30183020
void setTypeReleaseIterator(setTypeIterator *si);
30193021
int setTypeNext(setTypeIterator *si, char **str, size_t *len, int64_t *llele);

src/t_set.c

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,7 @@
2828
*/
2929

3030
#include "server.h"
31-
32-
/* Internal prototypes */
33-
34-
int setTypeAddAux(robj *set, char *str, size_t len, int64_t llval, int str_is_sds);
35-
int setTypeRemoveAux(robj *set, char *str, size_t len, int64_t llval, int str_is_sds);
36-
int setTypeIsMemberAux(robj *set, char *str, size_t len, int64_t llval, int str_is_sds);
31+
#include "intset.h" /* Compact integer set structure */
3732

3833
/*-----------------------------------------------------------------------------
3934
* Set Commands

src/t_zset.c

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
* from tail to head, useful for ZREVRANGE. */
5858

5959
#include "server.h"
60+
#include "intset.h" /* Compact integer set structure */
6061
#include <math.h>
6162

6263
/*-----------------------------------------------------------------------------
@@ -2257,26 +2258,13 @@ int zuiFind(zsetopsrc *op, zsetopval *val, double *score) {
22572258
return 0;
22582259

22592260
if (op->type == OBJ_SET) {
2260-
if (op->encoding == OBJ_ENCODING_INTSET) {
2261-
if (zuiLongLongFromValue(val) &&
2262-
intsetFind(op->subject->ptr,val->ell))
2263-
{
2264-
*score = 1.0;
2265-
return 1;
2266-
} else {
2267-
return 0;
2268-
}
2269-
} else if (op->encoding == OBJ_ENCODING_HT) {
2270-
dict *ht = op->subject->ptr;
2271-
zuiSdsFromValue(val);
2272-
if (dictFind(ht,val->ele) != NULL) {
2273-
*score = 1.0;
2274-
return 1;
2275-
} else {
2276-
return 0;
2277-
}
2261+
char *str = val->ele ? val->ele : (char *)val->estr;
2262+
size_t len = val->ele ? sdslen(val->ele) : val->elen;
2263+
if (setTypeIsMemberAux(op->subject, str, len, val->ell, val->ele != NULL)) {
2264+
*score = 1.0;
2265+
return 1;
22782266
} else {
2279-
serverPanic("Unknown set encoding");
2267+
return 0;
22802268
}
22812269
} else if (op->type == OBJ_ZSET) {
22822270
zuiSdsFromValue(val);

tests/unit/moduleapi/scan.tcl

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,45 +12,58 @@ start_server {tags {"modules"}} {
1212
lsort [r scan.scan_strings]
1313
} {{x 1} {y 2} {z 3}}
1414

15-
test {Module scan hash ziplist} {
15+
test {Module scan hash listpack} {
1616
r hmset hh f1 v1 f2 v2
17+
assert_encoding listpack hh
1718
lsort [r scan.scan_key hh]
1819
} {{f1 v1} {f2 v2}}
19-
20-
test {Module scan hash dict with int value} {
21-
r hmset hh1 f1 1
20+
21+
test {Module scan hash listpack with int value} {
22+
r hmset hh1 f1 1
23+
assert_encoding listpack hh1
2224
lsort [r scan.scan_key hh1]
2325
} {{f1 1}}
2426

2527
test {Module scan hash dict} {
2628
r config set hash-max-ziplist-entries 2
2729
r hmset hh f3 v3
30+
assert_encoding hashtable hh
2831
lsort [r scan.scan_key hh]
2932
} {{f1 v1} {f2 v2} {f3 v3}}
3033

31-
test {Module scan zset ziplist} {
34+
test {Module scan zset listpack} {
3235
r zadd zz 1 f1 2 f2
36+
assert_encoding listpack zz
3337
lsort [r scan.scan_key zz]
3438
} {{f1 1} {f2 2}}
3539

36-
test {Module scan zset dict} {
40+
test {Module scan zset skiplist} {
3741
r config set zset-max-ziplist-entries 2
3842
r zadd zz 3 f3
43+
assert_encoding skiplist zz
3944
lsort [r scan.scan_key zz]
4045
} {{f1 1} {f2 2} {f3 3}}
4146

4247
test {Module scan set intset} {
4348
r sadd ss 1 2
49+
assert_encoding intset ss
4450
lsort [r scan.scan_key ss]
4551
} {{1 {}} {2 {}}}
4652

4753
test {Module scan set dict} {
4854
r config set set-max-intset-entries 2
4955
r sadd ss 3
56+
assert_encoding hashtable ss
5057
lsort [r scan.scan_key ss]
5158
} {{1 {}} {2 {}} {3 {}}}
5259

60+
test {Module scan set listpack} {
61+
r sadd ss1 a b c
62+
assert_encoding listpack ss1
63+
lsort [r scan.scan_key ss1]
64+
} {{a {}} {b {}} {c {}}}
65+
5366
test "Unload the module - scan" {
5467
assert_equal {OK} [r module unload scan]
5568
}
56-
}
69+
}

tests/unit/type/zset.tcl

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2480,4 +2480,83 @@ start_server {tags {"zset"}} {
24802480
r zscore zz dblmax
24812481
} {1.7976931348623157e+308}
24822482

2483+
test {zunionInterDiffGenericCommand acts on SET and ZSET} {
2484+
r del set_small{t} set_big{t} zset_small{t} zset_big{t} zset_dest{t}
2485+
2486+
foreach set_type {intset listpack hashtable} {
2487+
# Restore all default configurations before each round of testing.
2488+
r config set set-max-intset-entries 512
2489+
r config set set-max-listpack-entries 128
2490+
r config set zset-max-listpack-entries 128
2491+
2492+
r del set_small{t} set_big{t}
2493+
2494+
if {$set_type == "intset"} {
2495+
r sadd set_small{t} 1 2 3
2496+
r sadd set_big{t} 1 2 3 4 5
2497+
assert_encoding intset set_small{t}
2498+
assert_encoding intset set_big{t}
2499+
} elseif {$set_type == "listpack"} {
2500+
# Add an "a" and then remove it, make sure the set is listpack encoding.
2501+
r sadd set_small{t} a 1 2 3
2502+
r sadd set_big{t} a 1 2 3 4 5
2503+
r srem set_small{t} a
2504+
r srem set_big{t} a
2505+
assert_encoding listpack set_small{t}
2506+
assert_encoding listpack set_big{t}
2507+
} elseif {$set_type == "hashtable"} {
2508+
r config set set-max-intset-entries 0
2509+
r config set set-max-listpack-entries 0
2510+
r sadd set_small{t} 1 2 3
2511+
r sadd set_big{t} 1 2 3 4 5
2512+
assert_encoding hashtable set_small{t}
2513+
assert_encoding hashtable set_big{t}
2514+
}
2515+
2516+
foreach zset_type {listpack skiplist} {
2517+
r del zset_small{t} zset_big{t}
2518+
2519+
if {$zset_type == "listpack"} {
2520+
r zadd zset_small{t} 1 1 2 2 3 3
2521+
r zadd zset_big{t} 1 1 2 2 3 3 4 4 5 5
2522+
assert_encoding listpack zset_small{t}
2523+
assert_encoding listpack zset_big{t}
2524+
} elseif {$zset_type == "skiplist"} {
2525+
r config set zset-max-listpack-entries 0
2526+
r zadd zset_small{t} 1 1 2 2 3 3
2527+
r zadd zset_big{t} 1 1 2 2 3 3 4 4 5 5
2528+
assert_encoding skiplist zset_small{t}
2529+
assert_encoding skiplist zset_big{t}
2530+
}
2531+
2532+
# Test one key is big and one key is small separately.
2533+
# The reason for this is because we will sort the sets from smallest to largest.
2534+
# So set one big key and one small key, then the test can cover more code paths.
2535+
foreach {small_or_big set_key zset_key} {
2536+
small set_small{t} zset_big{t}
2537+
big set_big{t} zset_small{t}
2538+
} {
2539+
# The result of these commands are not related to the order of the keys.
2540+
assert_equal {1 2 3 4 5} [lsort [r zunion 2 $set_key $zset_key]]
2541+
assert_equal {5} [r zunionstore zset_dest{t} 2 $set_key $zset_key]
2542+
assert_equal {1 2 3} [lsort [r zinter 2 $set_key $zset_key]]
2543+
assert_equal {3} [r zinterstore zset_dest{t} 2 $set_key $zset_key]
2544+
assert_equal {3} [r zintercard 2 $set_key $zset_key]
2545+
2546+
# The result of sdiff is related to the order of the keys.
2547+
if {$small_or_big == "small"} {
2548+
assert_equal {} [r zdiff 2 $set_key $zset_key]
2549+
assert_equal {0} [r zdiffstore zset_dest{t} 2 $set_key $zset_key]
2550+
} else {
2551+
assert_equal {4 5} [lsort [r zdiff 2 $set_key $zset_key]]
2552+
assert_equal {2} [r zdiffstore zset_dest{t} 2 $set_key $zset_key]
2553+
}
2554+
}
2555+
}
2556+
}
2557+
2558+
r config set set-max-intset-entries 512
2559+
r config set set-max-listpack-entries 128
2560+
r config set zset-max-listpack-entries 128
2561+
}
24832562
}

0 commit comments

Comments
 (0)