mirror of
https://github.com/fluencelabs/redis
synced 2025-03-28 05:11:03 +00:00
Merge remote branch 'pietern/unstable-zset' into unstable
This commit is contained in:
commit
7c0e1b53c4
@ -340,6 +340,12 @@ list-max-ziplist-value 64
|
||||
# set in order to use this special memory saving encoding.
|
||||
set-max-intset-entries 512
|
||||
|
||||
# Similarly to hashes and lists, sorted sets are also specially encoded in
|
||||
# order to save a lot of space. This encoding is only used when the length and
|
||||
# elements of a sorted set are below the following limits:
|
||||
zset-max-ziplist-entries 128
|
||||
zset-max-ziplist-value 64
|
||||
|
||||
# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in
|
||||
# order to help rehashing the main Redis hash table (the one mapping top-level
|
||||
# keys to values). The hash table implementation redis uses (see dict.c)
|
||||
|
36
src/aof.c
36
src/aof.c
@ -429,12 +429,43 @@ int rewriteAppendOnlyFile(char *filename) {
|
||||
}
|
||||
} else if (o->type == REDIS_ZSET) {
|
||||
/* Emit the ZADDs needed to rebuild the sorted set */
|
||||
char cmd[]="*4\r\n$4\r\nZADD\r\n";
|
||||
|
||||
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
|
||||
unsigned char *zl = o->ptr;
|
||||
unsigned char *eptr, *sptr;
|
||||
unsigned char *vstr;
|
||||
unsigned int vlen;
|
||||
long long vll;
|
||||
double score;
|
||||
|
||||
eptr = ziplistIndex(zl,0);
|
||||
redisAssert(eptr != NULL);
|
||||
sptr = ziplistNext(zl,eptr);
|
||||
redisAssert(sptr != NULL);
|
||||
|
||||
while (eptr != NULL) {
|
||||
redisAssert(ziplistGet(eptr,&vstr,&vlen,&vll));
|
||||
score = zzlGetScore(sptr);
|
||||
|
||||
if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr;
|
||||
if (fwriteBulkObject(fp,&key) == 0) goto werr;
|
||||
if (fwriteBulkDouble(fp,score) == 0) goto werr;
|
||||
if (vstr != NULL) {
|
||||
if (fwriteBulkString(fp,(char*)vstr,vlen) == 0)
|
||||
goto werr;
|
||||
} else {
|
||||
if (fwriteBulkLongLong(fp,vll) == 0)
|
||||
goto werr;
|
||||
}
|
||||
zzlNext(zl,&eptr,&sptr);
|
||||
}
|
||||
} else if (o->encoding == REDIS_ENCODING_SKIPLIST) {
|
||||
zset *zs = o->ptr;
|
||||
dictIterator *di = dictGetIterator(zs->dict);
|
||||
dictEntry *de;
|
||||
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
char cmd[]="*4\r\n$4\r\nZADD\r\n";
|
||||
robj *eleobj = dictGetEntryKey(de);
|
||||
double *score = dictGetEntryVal(de);
|
||||
|
||||
@ -444,6 +475,9 @@ int rewriteAppendOnlyFile(char *filename) {
|
||||
if (fwriteBulkObject(fp,eleobj) == 0) goto werr;
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
} else {
|
||||
redisPanic("Unknown sorted set encoding");
|
||||
}
|
||||
} else if (o->type == REDIS_HASH) {
|
||||
char cmd[]="*4\r\n$4\r\nHSET\r\n";
|
||||
|
||||
|
20
src/config.c
20
src/config.c
@ -261,6 +261,10 @@ void loadServerConfig(char *filename) {
|
||||
server.list_max_ziplist_value = memtoll(argv[1], NULL);
|
||||
} else if (!strcasecmp(argv[0],"set-max-intset-entries") && argc == 2) {
|
||||
server.set_max_intset_entries = memtoll(argv[1], NULL);
|
||||
} else if (!strcasecmp(argv[0],"zset-max-ziplist-entries") && argc == 2) {
|
||||
server.zset_max_ziplist_entries = memtoll(argv[1], NULL);
|
||||
} else if (!strcasecmp(argv[0],"zset-max-ziplist-value") && argc == 2) {
|
||||
server.zset_max_ziplist_value = memtoll(argv[1], NULL);
|
||||
} else if (!strcasecmp(argv[0],"rename-command") && argc == 3) {
|
||||
struct redisCommand *cmd = lookupCommand(argv[1]);
|
||||
int retval;
|
||||
@ -450,6 +454,12 @@ void configSetCommand(redisClient *c) {
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"set-max-intset-entries")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
|
||||
server.set_max_intset_entries = ll;
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-entries")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
|
||||
server.zset_max_ziplist_entries = ll;
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"zset-max-ziplist-value")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
|
||||
server.zset_max_ziplist_value = ll;
|
||||
} else {
|
||||
addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
|
||||
(char*)c->argv[2]->ptr);
|
||||
@ -601,6 +611,16 @@ void configGetCommand(redisClient *c) {
|
||||
addReplyBulkLongLong(c,server.set_max_intset_entries);
|
||||
matches++;
|
||||
}
|
||||
if (stringmatch(pattern,"zset-max-ziplist-entries",0)) {
|
||||
addReplyBulkCString(c,"zset-max-ziplist-entries");
|
||||
addReplyBulkLongLong(c,server.zset_max_ziplist_entries);
|
||||
matches++;
|
||||
}
|
||||
if (stringmatch(pattern,"zset-max-ziplist-value",0)) {
|
||||
addReplyBulkCString(c,"zset-max-ziplist-value");
|
||||
addReplyBulkLongLong(c,server.zset_max_ziplist_value);
|
||||
matches++;
|
||||
}
|
||||
setDeferredMultiBulkLength(c,replylen,matches*2);
|
||||
}
|
||||
|
||||
|
37
src/debug.c
37
src/debug.c
@ -127,6 +127,39 @@ void computeDatasetDigest(unsigned char *final) {
|
||||
}
|
||||
setTypeReleaseIterator(si);
|
||||
} else if (o->type == REDIS_ZSET) {
|
||||
unsigned char eledigest[20];
|
||||
|
||||
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
|
||||
unsigned char *zl = o->ptr;
|
||||
unsigned char *eptr, *sptr;
|
||||
unsigned char *vstr;
|
||||
unsigned int vlen;
|
||||
long long vll;
|
||||
double score;
|
||||
|
||||
eptr = ziplistIndex(zl,0);
|
||||
redisAssert(eptr != NULL);
|
||||
sptr = ziplistNext(zl,eptr);
|
||||
redisAssert(sptr != NULL);
|
||||
|
||||
while (eptr != NULL) {
|
||||
redisAssert(ziplistGet(eptr,&vstr,&vlen,&vll));
|
||||
score = zzlGetScore(sptr);
|
||||
|
||||
memset(eledigest,0,20);
|
||||
if (vstr != NULL) {
|
||||
mixDigest(eledigest,vstr,vlen);
|
||||
} else {
|
||||
ll2string(buf,sizeof(buf),vll);
|
||||
mixDigest(eledigest,buf,strlen(buf));
|
||||
}
|
||||
|
||||
snprintf(buf,sizeof(buf),"%.17g",score);
|
||||
mixDigest(eledigest,buf,strlen(buf));
|
||||
xorDigest(digest,eledigest,20);
|
||||
zzlNext(zl,&eptr,&sptr);
|
||||
}
|
||||
} else if (o->encoding == REDIS_ENCODING_SKIPLIST) {
|
||||
zset *zs = o->ptr;
|
||||
dictIterator *di = dictGetIterator(zs->dict);
|
||||
dictEntry *de;
|
||||
@ -134,7 +167,6 @@ void computeDatasetDigest(unsigned char *final) {
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
robj *eleobj = dictGetEntryKey(de);
|
||||
double *score = dictGetEntryVal(de);
|
||||
unsigned char eledigest[20];
|
||||
|
||||
snprintf(buf,sizeof(buf),"%.17g",*score);
|
||||
memset(eledigest,0,20);
|
||||
@ -143,6 +175,9 @@ void computeDatasetDigest(unsigned char *final) {
|
||||
xorDigest(digest,eledigest,20);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
} else {
|
||||
redisPanic("Unknown sorted set encoding");
|
||||
}
|
||||
} else if (o->type == REDIS_HASH) {
|
||||
hashTypeIterator *hi;
|
||||
robj *obj;
|
||||
|
20
src/object.c
20
src/object.c
@ -102,6 +102,13 @@ robj *createZsetObject(void) {
|
||||
return o;
|
||||
}
|
||||
|
||||
robj *createZsetZiplistObject(void) {
|
||||
unsigned char *zl = ziplistNew();
|
||||
robj *o = createObject(REDIS_ZSET,zl);
|
||||
o->encoding = REDIS_ENCODING_ZIPLIST;
|
||||
return o;
|
||||
}
|
||||
|
||||
void freeStringObject(robj *o) {
|
||||
if (o->encoding == REDIS_ENCODING_RAW) {
|
||||
sdsfree(o->ptr);
|
||||
@ -135,11 +142,20 @@ void freeSetObject(robj *o) {
|
||||
}
|
||||
|
||||
void freeZsetObject(robj *o) {
|
||||
zset *zs = o->ptr;
|
||||
|
||||
zset *zs;
|
||||
switch (o->encoding) {
|
||||
case REDIS_ENCODING_SKIPLIST:
|
||||
zs = o->ptr;
|
||||
dictRelease(zs->dict);
|
||||
zslFree(zs->zsl);
|
||||
zfree(zs);
|
||||
break;
|
||||
case REDIS_ENCODING_ZIPLIST:
|
||||
zfree(o->ptr);
|
||||
break;
|
||||
default:
|
||||
redisPanic("Unknown sorted set encoding");
|
||||
}
|
||||
}
|
||||
|
||||
void freeHashObject(robj *o) {
|
||||
|
37
src/rdb.c
37
src/rdb.c
@ -302,7 +302,13 @@ int rdbSaveObject(FILE *fp, robj *o) {
|
||||
redisPanic("Unknown set encoding");
|
||||
}
|
||||
} else if (o->type == REDIS_ZSET) {
|
||||
/* Save a set value */
|
||||
/* Save a sorted set value */
|
||||
if (o->encoding == REDIS_ENCODING_ZIPLIST) {
|
||||
size_t l = ziplistBlobLen((unsigned char*)o->ptr);
|
||||
|
||||
if ((n = rdbSaveRawString(fp,o->ptr,l)) == -1) return -1;
|
||||
nwritten += n;
|
||||
} else if (o->encoding == REDIS_ENCODING_SKIPLIST) {
|
||||
zset *zs = o->ptr;
|
||||
dictIterator *di = dictGetIterator(zs->dict);
|
||||
dictEntry *de;
|
||||
@ -320,6 +326,9 @@ int rdbSaveObject(FILE *fp, robj *o) {
|
||||
nwritten += n;
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
} else {
|
||||
redisPanic("Unknown sorted set encoding");
|
||||
}
|
||||
} else if (o->type == REDIS_HASH) {
|
||||
/* Save a hash value */
|
||||
if (o->encoding == REDIS_ENCODING_ZIPMAP) {
|
||||
@ -386,6 +395,8 @@ int rdbSaveKeyValuePair(FILE *fp, robj *key, robj *val,
|
||||
vtype = REDIS_LIST_ZIPLIST;
|
||||
else if (vtype == REDIS_SET && val->encoding == REDIS_ENCODING_INTSET)
|
||||
vtype = REDIS_SET_INTSET;
|
||||
else if (vtype == REDIS_ZSET && val->encoding == REDIS_ENCODING_ZIPLIST)
|
||||
vtype = REDIS_ZSET_ZIPLIST;
|
||||
/* Save type, key, value */
|
||||
if (rdbSaveType(fp,vtype) == -1) return -1;
|
||||
if (rdbSaveStringObject(fp,key) == -1) return -1;
|
||||
@ -745,11 +756,13 @@ robj *rdbLoadObject(int type, FILE *fp) {
|
||||
} else if (type == REDIS_ZSET) {
|
||||
/* Read list/set value */
|
||||
size_t zsetlen;
|
||||
size_t maxelelen = 0;
|
||||
zset *zs;
|
||||
|
||||
if ((zsetlen = rdbLoadLen(fp,NULL)) == REDIS_RDB_LENERR) return NULL;
|
||||
o = createZsetObject();
|
||||
zs = o->ptr;
|
||||
|
||||
/* Load every single element of the list/set */
|
||||
while(zsetlen--) {
|
||||
robj *ele;
|
||||
@ -759,10 +772,21 @@ robj *rdbLoadObject(int type, FILE *fp) {
|
||||
if ((ele = rdbLoadEncodedStringObject(fp)) == NULL) return NULL;
|
||||
ele = tryObjectEncoding(ele);
|
||||
if (rdbLoadDoubleValue(fp,&score) == -1) return NULL;
|
||||
|
||||
/* Don't care about integer-encoded strings. */
|
||||
if (ele->encoding == REDIS_ENCODING_RAW &&
|
||||
sdslen(ele->ptr) > maxelelen)
|
||||
maxelelen = sdslen(ele->ptr);
|
||||
|
||||
znode = zslInsert(zs->zsl,score,ele);
|
||||
dictAdd(zs->dict,ele,&znode->score);
|
||||
incrRefCount(ele); /* added to skiplist */
|
||||
}
|
||||
|
||||
/* Convert *after* loading, since sorted sets are not stored ordered. */
|
||||
if (zsetLength(o) <= server.zset_max_ziplist_entries &&
|
||||
maxelelen <= server.zset_max_ziplist_value)
|
||||
zsetConvert(o,REDIS_ENCODING_ZIPLIST);
|
||||
} else if (type == REDIS_HASH) {
|
||||
size_t hashlen;
|
||||
|
||||
@ -811,7 +835,8 @@ robj *rdbLoadObject(int type, FILE *fp) {
|
||||
}
|
||||
} else if (type == REDIS_HASH_ZIPMAP ||
|
||||
type == REDIS_LIST_ZIPLIST ||
|
||||
type == REDIS_SET_INTSET)
|
||||
type == REDIS_SET_INTSET ||
|
||||
type == REDIS_ZSET_ZIPLIST)
|
||||
{
|
||||
robj *aux = rdbLoadStringObject(fp);
|
||||
|
||||
@ -846,8 +871,14 @@ robj *rdbLoadObject(int type, FILE *fp) {
|
||||
if (intsetLen(o->ptr) > server.set_max_intset_entries)
|
||||
setTypeConvert(o,REDIS_ENCODING_HT);
|
||||
break;
|
||||
case REDIS_ZSET_ZIPLIST:
|
||||
o->type = REDIS_ZSET;
|
||||
o->encoding = REDIS_ENCODING_ZIPLIST;
|
||||
if (zsetLength(o) > server.zset_max_ziplist_entries)
|
||||
zsetConvert(o,REDIS_ENCODING_SKIPLIST);
|
||||
break;
|
||||
default:
|
||||
redisPanic("Unknown enoding");
|
||||
redisPanic("Unknown encoding");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
@ -846,6 +846,8 @@ void initServerConfig() {
|
||||
server.list_max_ziplist_entries = REDIS_LIST_MAX_ZIPLIST_ENTRIES;
|
||||
server.list_max_ziplist_value = REDIS_LIST_MAX_ZIPLIST_VALUE;
|
||||
server.set_max_intset_entries = REDIS_SET_MAX_INTSET_ENTRIES;
|
||||
server.zset_max_ziplist_entries = REDIS_ZSET_MAX_ZIPLIST_ENTRIES;
|
||||
server.zset_max_ziplist_value = REDIS_ZSET_MAX_ZIPLIST_VALUE;
|
||||
server.shutdown_asap = 0;
|
||||
server.cache_flush_delay = 0;
|
||||
server.cluster_enabled = 0;
|
||||
|
15
src/redis.h
15
src/redis.h
@ -71,10 +71,12 @@
|
||||
#define REDIS_ZSET 3
|
||||
#define REDIS_HASH 4
|
||||
#define REDIS_VMPOINTER 8
|
||||
|
||||
/* Object types only used for persistence in .rdb files */
|
||||
#define REDIS_HASH_ZIPMAP 9
|
||||
#define REDIS_LIST_ZIPLIST 10
|
||||
#define REDIS_SET_INTSET 11
|
||||
#define REDIS_ZSET_ZIPLIST 12
|
||||
|
||||
/* Objects encoding. Some kind of objects like Strings and Hashes can be
|
||||
* internally represented in multiple ways. The 'encoding' field of the object
|
||||
@ -198,6 +200,8 @@
|
||||
#define REDIS_LIST_MAX_ZIPLIST_ENTRIES 512
|
||||
#define REDIS_LIST_MAX_ZIPLIST_VALUE 64
|
||||
#define REDIS_SET_MAX_INTSET_ENTRIES 512
|
||||
#define REDIS_ZSET_MAX_ZIPLIST_ENTRIES 128
|
||||
#define REDIS_ZSET_MAX_ZIPLIST_VALUE 64
|
||||
|
||||
/* Sets operations codes */
|
||||
#define REDIS_OP_UNION 0
|
||||
@ -589,6 +593,8 @@ struct redisServer {
|
||||
size_t list_max_ziplist_entries;
|
||||
size_t list_max_ziplist_value;
|
||||
size_t set_max_intset_entries;
|
||||
size_t zset_max_ziplist_entries;
|
||||
size_t zset_max_ziplist_value;
|
||||
time_t unixtime; /* Unix time sampled every second. */
|
||||
/* Virtual memory I/O threads stuff */
|
||||
/* An I/O thread process an element taken from the io_jobs queue and
|
||||
@ -853,6 +859,7 @@ robj *createSetObject(void);
|
||||
robj *createIntsetObject(void);
|
||||
robj *createHashObject(void);
|
||||
robj *createZsetObject(void);
|
||||
robj *createZsetZiplistObject(void);
|
||||
int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg);
|
||||
int checkType(redisClient *c, robj *o, int type);
|
||||
int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg);
|
||||
@ -916,6 +923,12 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal);
|
||||
zskiplist *zslCreate(void);
|
||||
void zslFree(zskiplist *zsl);
|
||||
zskiplistNode *zslInsert(zskiplist *zsl, double score, robj *obj);
|
||||
unsigned char *zzlInsert(unsigned char *zl, robj *ele, double score);
|
||||
double zzlGetScore(unsigned char *sptr);
|
||||
void zzlNext(unsigned char *zl, unsigned char **eptr, unsigned char **sptr);
|
||||
void zzlPrev(unsigned char *zl, unsigned char **eptr, unsigned char **sptr);
|
||||
unsigned int zsetLength(robj *zobj);
|
||||
void zsetConvert(robj *zobj, int encoding);
|
||||
|
||||
/* Core functions */
|
||||
void freeMemoryIfNeeded(void);
|
||||
@ -1009,6 +1022,8 @@ int stringmatchlen(const char *pattern, int patternLen,
|
||||
int stringmatch(const char *pattern, const char *string, int nocase);
|
||||
long long memtoll(const char *p, int *err);
|
||||
int ll2string(char *s, size_t len, long long value);
|
||||
int string2ll(char *s, size_t len, long long *value);
|
||||
int d2string(char *s, size_t len, double value);
|
||||
int isStringRepresentableAsLong(sds s, long *longval);
|
||||
int isStringRepresentableAsLongLong(sds s, long long *longval);
|
||||
int isObjectRepresentableAsLongLong(robj *o, long long *llongval);
|
||||
|
@ -199,6 +199,9 @@ void sortCommand(redisClient *c) {
|
||||
j++;
|
||||
}
|
||||
|
||||
/* Destructively convert encoded sorted sets for SORT. */
|
||||
if (sortval->type == REDIS_ZSET) zsetConvert(sortval, REDIS_ENCODING_SKIPLIST);
|
||||
|
||||
/* Load the sorting vector with all the objects to sort */
|
||||
switch(sortval->type) {
|
||||
case REDIS_LIST: vectorlen = listTypeLength(sortval); break;
|
||||
|
1601
src/t_zset.c
1601
src/t_zset.c
File diff suppressed because it is too large
Load Diff
97
src/util.c
97
src/util.c
@ -1,6 +1,7 @@
|
||||
#include "redis.h"
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
/* Glob-style pattern matching. */
|
||||
int stringmatchlen(const char *pattern, int patternLen,
|
||||
@ -200,6 +201,102 @@ int ll2string(char *s, size_t len, long long value) {
|
||||
return l;
|
||||
}
|
||||
|
||||
/* Convert a string into a long long. Returns 1 if the string could be parsed
|
||||
* into a (non-overflowing) long long, 0 otherwise. The value will be set to
|
||||
* the parsed value when appropriate. */
|
||||
int string2ll(char *s, size_t slen, long long *value) {
|
||||
char *p = s;
|
||||
size_t plen = 0;
|
||||
int negative = 0;
|
||||
unsigned long long v;
|
||||
|
||||
if (plen == slen)
|
||||
return 0;
|
||||
|
||||
if (p[0] == '-') {
|
||||
negative = 1;
|
||||
p++; plen++;
|
||||
|
||||
/* Abort on only a negative sign. */
|
||||
if (plen == slen)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* First digit should be 1-9. */
|
||||
if (p[0] >= '1' && p[0] <= '9') {
|
||||
v = p[0]-'0';
|
||||
p++; plen++;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (plen < slen && p[0] >= '0' && p[0] <= '9') {
|
||||
if (v > (ULLONG_MAX / 10)) /* Overflow. */
|
||||
return 0;
|
||||
v *= 10;
|
||||
|
||||
if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */
|
||||
return 0;
|
||||
v += p[0]-'0';
|
||||
|
||||
p++; plen++;
|
||||
}
|
||||
|
||||
/* Return if not all bytes were used. */
|
||||
if (plen < slen)
|
||||
return 0;
|
||||
|
||||
if (negative) {
|
||||
if (v > (-(unsigned long long)LLONG_MIN)) /* Overflow. */
|
||||
return 0;
|
||||
if (value != NULL) *value = -v;
|
||||
} else {
|
||||
if (v > LLONG_MAX) /* Overflow. */
|
||||
return 0;
|
||||
if (value != NULL) *value = v;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Convert a double to a string representation. Returns the number of bytes
|
||||
* required. The representation should always be parsable by stdtod(3). */
|
||||
int d2string(char *buf, size_t len, double value) {
|
||||
if (isnan(value)) {
|
||||
len = snprintf(buf,len,"nan");
|
||||
} else if (isinf(value)) {
|
||||
if (value < 0)
|
||||
len = snprintf(buf,len,"-inf");
|
||||
else
|
||||
len = snprintf(buf,len,"inf");
|
||||
} else if (value == 0) {
|
||||
/* See: http://en.wikipedia.org/wiki/Signed_zero, "Comparisons". */
|
||||
if (1.0/value < 0)
|
||||
len = snprintf(buf,len,"-0");
|
||||
else
|
||||
len = snprintf(buf,len,"0");
|
||||
} else {
|
||||
#if (DBL_MANT_DIG >= 52) && (LLONG_MAX == 0x7fffffffffffffffLL)
|
||||
/* Check if the float is in a safe range to be casted into a
|
||||
* long long. We are assuming that long long is 64 bit here.
|
||||
* Also we are assuming that there are no implementations around where
|
||||
* double has precision < 52 bit.
|
||||
*
|
||||
* Under this assumptions we test if a double is inside an interval
|
||||
* where casting to long long is safe. Then using two castings we
|
||||
* make sure the decimal part is zero. If all this is true we use
|
||||
* integer printing function that is much faster. */
|
||||
double min = -4503599627370495; /* (2^52)-1 */
|
||||
double max = 4503599627370496; /* -(2^52) */
|
||||
if (val > min && val < max && value == ((double)((long long)value)))
|
||||
len = ll2string(buf,len,(long long)value);
|
||||
else
|
||||
#endif
|
||||
len = snprintf(buf,len,"%.17g",value);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Check if the sds string 's' can be represented by a long long
|
||||
* (that is, is a number that fits into long without any other space or
|
||||
* character before or after the digits, so that converting this number
|
||||
|
@ -385,8 +385,8 @@ static unsigned char *ziplistResize(unsigned char *zl, unsigned int len) {
|
||||
* The pointer "p" points to the first entry that does NOT need to be
|
||||
* updated, i.e. consecutive fields MAY need an update. */
|
||||
static unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) {
|
||||
unsigned int curlen = ZIPLIST_BYTES(zl), rawlen, rawlensize;
|
||||
unsigned int offset, noffset, extra;
|
||||
size_t curlen = ZIPLIST_BYTES(zl), rawlen, rawlensize;
|
||||
size_t offset, noffset, extra;
|
||||
unsigned char *np;
|
||||
zlentry cur, next;
|
||||
|
||||
@ -441,7 +441,8 @@ static unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p
|
||||
/* Delete "num" entries, starting at "p". Returns pointer to the ziplist. */
|
||||
static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) {
|
||||
unsigned int i, totlen, deleted = 0;
|
||||
int offset, nextdiff = 0;
|
||||
size_t offset;
|
||||
int nextdiff = 0;
|
||||
zlentry first, tail;
|
||||
|
||||
first = zipEntry(p);
|
||||
@ -493,8 +494,9 @@ static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsig
|
||||
|
||||
/* Insert item at "p". */
|
||||
static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
|
||||
unsigned int curlen = ZIPLIST_BYTES(zl), reqlen, prevlen = 0;
|
||||
unsigned int offset, nextdiff = 0;
|
||||
size_t curlen = ZIPLIST_BYTES(zl), reqlen, prevlen = 0;
|
||||
size_t offset;
|
||||
int nextdiff = 0;
|
||||
unsigned char encoding = 0;
|
||||
long long value;
|
||||
zlentry entry, tail;
|
||||
@ -678,7 +680,7 @@ unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char
|
||||
* Also update *p in place, to be able to iterate over the
|
||||
* ziplist, while deleting entries. */
|
||||
unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p) {
|
||||
unsigned int offset = *p-zl;
|
||||
size_t offset = *p-zl;
|
||||
zl = __ziplistDelete(zl,*p,1);
|
||||
|
||||
/* Store pointer to current element in p, because ziplistDelete will
|
||||
|
@ -6,25 +6,66 @@ start_server {tags {"zset"}} {
|
||||
}
|
||||
}
|
||||
|
||||
test {ZSET basic ZADD and score update} {
|
||||
proc basics {encoding} {
|
||||
if {$encoding == "ziplist"} {
|
||||
r config set zset-max-ziplist-entries 128
|
||||
r config set zset-max-ziplist-value 64
|
||||
} elseif {$encoding == "skiplist"} {
|
||||
r config set zset-max-ziplist-entries 0
|
||||
r config set zset-max-ziplist-value 0
|
||||
} else {
|
||||
puts "Unknown sorted set encoding"
|
||||
exit
|
||||
}
|
||||
|
||||
test "Check encoding - $encoding" {
|
||||
r del ztmp
|
||||
r zadd ztmp 10 x
|
||||
assert_encoding $encoding ztmp
|
||||
}
|
||||
|
||||
test "ZSET basic ZADD and score update - $encoding" {
|
||||
r del ztmp
|
||||
r zadd ztmp 10 x
|
||||
r zadd ztmp 20 y
|
||||
r zadd ztmp 30 z
|
||||
set aux1 [r zrange ztmp 0 -1]
|
||||
assert_equal {x y z} [r zrange ztmp 0 -1]
|
||||
|
||||
r zadd ztmp 1 y
|
||||
set aux2 [r zrange ztmp 0 -1]
|
||||
list $aux1 $aux2
|
||||
} {{x y z} {y x z}}
|
||||
assert_equal {y x z} [r zrange ztmp 0 -1]
|
||||
}
|
||||
|
||||
test {ZCARD basics} {
|
||||
r zcard ztmp
|
||||
} {3}
|
||||
test "ZSET element can't be set to NaN with ZADD - $encoding" {
|
||||
assert_error "*not a double*" {r zadd myzset nan abc}
|
||||
}
|
||||
|
||||
test {ZCARD non existing key} {
|
||||
r zcard ztmp-blabla
|
||||
} {0}
|
||||
test "ZSET element can't be set to NaN with ZINCRBY" {
|
||||
assert_error "*not a double*" {r zadd myzset nan abc}
|
||||
}
|
||||
|
||||
test "ZRANGE basics" {
|
||||
test "ZINCRBY calls leading to NaN result in error" {
|
||||
r zincrby myzset +inf abc
|
||||
assert_error "*NaN*" {r zincrby myzset -inf abc}
|
||||
}
|
||||
|
||||
test "ZCARD basics - $encoding" {
|
||||
assert_equal 3 [r zcard ztmp]
|
||||
assert_equal 0 [r zcard zdoesntexist]
|
||||
}
|
||||
|
||||
test "ZREM removes key after last element is removed" {
|
||||
r del ztmp
|
||||
r zadd ztmp 10 x
|
||||
r zadd ztmp 20 y
|
||||
|
||||
assert_equal 1 [r exists ztmp]
|
||||
assert_equal 0 [r zrem ztmp z]
|
||||
assert_equal 1 [r zrem ztmp y]
|
||||
assert_equal 1 [r zrem ztmp x]
|
||||
assert_equal 0 [r exists ztmp]
|
||||
}
|
||||
|
||||
test "ZRANGE basics - $encoding" {
|
||||
r del ztmp
|
||||
r zadd ztmp 1 a
|
||||
r zadd ztmp 2 b
|
||||
@ -54,7 +95,7 @@ start_server {tags {"zset"}} {
|
||||
assert_equal {a 1 b 2 c 3 d 4} [r zrange ztmp 0 -1 withscores]
|
||||
}
|
||||
|
||||
test "ZREVRANGE basics" {
|
||||
test "ZREVRANGE basics - $encoding" {
|
||||
r del ztmp
|
||||
r zadd ztmp 1 a
|
||||
r zadd ztmp 2 b
|
||||
@ -84,120 +125,47 @@ start_server {tags {"zset"}} {
|
||||
assert_equal {d 4 c 3 b 2 a 1} [r zrevrange ztmp 0 -1 withscores]
|
||||
}
|
||||
|
||||
test {ZRANK basics} {
|
||||
test "ZRANK/ZREVRANK basics - $encoding" {
|
||||
r del zranktmp
|
||||
r zadd zranktmp 10 x
|
||||
r zadd zranktmp 20 y
|
||||
r zadd zranktmp 30 z
|
||||
list [r zrank zranktmp x] [r zrank zranktmp y] [r zrank zranktmp z]
|
||||
} {0 1 2}
|
||||
assert_equal 0 [r zrank zranktmp x]
|
||||
assert_equal 1 [r zrank zranktmp y]
|
||||
assert_equal 2 [r zrank zranktmp z]
|
||||
assert_equal "" [r zrank zranktmp foo]
|
||||
assert_equal 2 [r zrevrank zranktmp x]
|
||||
assert_equal 1 [r zrevrank zranktmp y]
|
||||
assert_equal 0 [r zrevrank zranktmp z]
|
||||
assert_equal "" [r zrevrank zranktmp foo]
|
||||
}
|
||||
|
||||
test {ZREVRANK basics} {
|
||||
list [r zrevrank zranktmp x] [r zrevrank zranktmp y] [r zrevrank zranktmp z]
|
||||
} {2 1 0}
|
||||
|
||||
test {ZRANK - after deletion} {
|
||||
test "ZRANK - after deletion - $encoding" {
|
||||
r zrem zranktmp y
|
||||
list [r zrank zranktmp x] [r zrank zranktmp z]
|
||||
} {0 1}
|
||||
assert_equal 0 [r zrank zranktmp x]
|
||||
assert_equal 1 [r zrank zranktmp z]
|
||||
}
|
||||
|
||||
test {ZSCORE} {
|
||||
set aux {}
|
||||
set err {}
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
set score [expr rand()]
|
||||
lappend aux $score
|
||||
r zadd zscoretest $score $i
|
||||
}
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
if {[r zscore zscoretest $i] != [lindex $aux $i]} {
|
||||
set err "Expected score was [lindex $aux $i] but got [r zscore zscoretest $i] for element $i"
|
||||
break
|
||||
}
|
||||
}
|
||||
set _ $err
|
||||
} {}
|
||||
|
||||
test {ZSCORE after a DEBUG RELOAD} {
|
||||
set aux {}
|
||||
set err {}
|
||||
r del zscoretest
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
set score [expr rand()]
|
||||
lappend aux $score
|
||||
r zadd zscoretest $score $i
|
||||
}
|
||||
r debug reload
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
if {[r zscore zscoretest $i] != [lindex $aux $i]} {
|
||||
set err "Expected score was [lindex $aux $i] but got [r zscore zscoretest $i] for element $i"
|
||||
break
|
||||
}
|
||||
}
|
||||
set _ $err
|
||||
} {}
|
||||
|
||||
test {ZSETs stress tester - sorting is working well?} {
|
||||
set delta 0
|
||||
for {set test 0} {$test < 2} {incr test} {
|
||||
unset -nocomplain auxarray
|
||||
array set auxarray {}
|
||||
set auxlist {}
|
||||
r del myzset
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
if {$test == 0} {
|
||||
set score [expr rand()]
|
||||
} else {
|
||||
set score [expr int(rand()*10)]
|
||||
}
|
||||
set auxarray($i) $score
|
||||
r zadd myzset $score $i
|
||||
# Random update
|
||||
if {[expr rand()] < .2} {
|
||||
set j [expr int(rand()*1000)]
|
||||
if {$test == 0} {
|
||||
set score [expr rand()]
|
||||
} else {
|
||||
set score [expr int(rand()*10)]
|
||||
}
|
||||
set auxarray($j) $score
|
||||
r zadd myzset $score $j
|
||||
}
|
||||
}
|
||||
foreach {item score} [array get auxarray] {
|
||||
lappend auxlist [list $score $item]
|
||||
}
|
||||
set sorted [lsort -command zlistAlikeSort $auxlist]
|
||||
set auxlist {}
|
||||
foreach x $sorted {
|
||||
lappend auxlist [lindex $x 1]
|
||||
}
|
||||
set fromredis [r zrange myzset 0 -1]
|
||||
set delta 0
|
||||
for {set i 0} {$i < [llength $fromredis]} {incr i} {
|
||||
if {[lindex $fromredis $i] != [lindex $auxlist $i]} {
|
||||
incr delta
|
||||
}
|
||||
}
|
||||
}
|
||||
format $delta
|
||||
} {0}
|
||||
|
||||
test {ZINCRBY - can create a new sorted set} {
|
||||
test "ZINCRBY - can create a new sorted set - $encoding" {
|
||||
r del zset
|
||||
r zincrby zset 1 foo
|
||||
list [r zrange zset 0 -1] [r zscore zset foo]
|
||||
} {foo 1}
|
||||
assert_equal {foo} [r zrange zset 0 -1]
|
||||
assert_equal 1 [r zscore zset foo]
|
||||
}
|
||||
|
||||
test {ZINCRBY - increment and decrement} {
|
||||
test "ZINCRBY - increment and decrement - $encoding" {
|
||||
r zincrby zset 2 foo
|
||||
r zincrby zset 1 bar
|
||||
set v1 [r zrange zset 0 -1]
|
||||
assert_equal {bar foo} [r zrange zset 0 -1]
|
||||
|
||||
r zincrby zset 10 bar
|
||||
r zincrby zset -5 foo
|
||||
r zincrby zset -5 bar
|
||||
set v2 [r zrange zset 0 -1]
|
||||
list $v1 $v2 [r zscore zset foo] [r zscore zset bar]
|
||||
} {{bar foo} {foo bar} -2 6}
|
||||
assert_equal {foo bar} [r zrange zset 0 -1]
|
||||
|
||||
assert_equal -2 [r zscore zset foo]
|
||||
assert_equal 6 [r zscore zset bar]
|
||||
}
|
||||
|
||||
proc create_default_zset {} {
|
||||
create_zset zset {-inf a 1 b 2 c 3 d 4 e 5 f +inf g}
|
||||
@ -227,6 +195,32 @@ start_server {tags {"zset"}} {
|
||||
assert_equal {f e} [r zrevrangebyscore zset (6 (3]
|
||||
assert_equal {f} [r zrevrangebyscore zset (+inf (4]
|
||||
assert_equal 2 [r zcount zset (0 (3]
|
||||
|
||||
# test empty ranges
|
||||
r zrem zset a
|
||||
r zrem zset g
|
||||
|
||||
# inclusive
|
||||
assert_equal {} [r zrangebyscore zset 4 2]
|
||||
assert_equal {} [r zrangebyscore zset 6 +inf]
|
||||
assert_equal {} [r zrangebyscore zset -inf -6]
|
||||
assert_equal {} [r zrevrangebyscore zset +inf 6]
|
||||
assert_equal {} [r zrevrangebyscore zset -6 -inf]
|
||||
|
||||
# exclusive
|
||||
assert_equal {} [r zrangebyscore zset (4 (2]
|
||||
assert_equal {} [r zrangebyscore zset 2 (2]
|
||||
assert_equal {} [r zrangebyscore zset (2 2]
|
||||
assert_equal {} [r zrangebyscore zset (6 (+inf]
|
||||
assert_equal {} [r zrangebyscore zset (-inf (-6]
|
||||
assert_equal {} [r zrevrangebyscore zset (+inf (6]
|
||||
assert_equal {} [r zrevrangebyscore zset (-6 (-inf]
|
||||
|
||||
# empty inner range
|
||||
assert_equal {} [r zrangebyscore zset 2.4 2.6]
|
||||
assert_equal {} [r zrangebyscore zset (2.4 2.6]
|
||||
assert_equal {} [r zrangebyscore zset 2.4 (2.6]
|
||||
assert_equal {} [r zrangebyscore zset (2.4 (2.6]
|
||||
}
|
||||
|
||||
test "ZRANGEBYSCORE with WITHSCORES" {
|
||||
@ -259,13 +253,315 @@ start_server {tags {"zset"}} {
|
||||
assert_error "*not a double*" {r zrangebyscore fooz 1 NaN}
|
||||
}
|
||||
|
||||
tags {"slow"} {
|
||||
test {ZRANGEBYSCORE fuzzy test, 100 ranges in 1000 elements sorted set} {
|
||||
test "ZREMRANGEBYSCORE basics" {
|
||||
proc remrangebyscore {min max} {
|
||||
create_zset zset {1 a 2 b 3 c 4 d 5 e}
|
||||
assert_equal 1 [r exists zset]
|
||||
r zremrangebyscore zset $min $max
|
||||
}
|
||||
|
||||
# inner range
|
||||
assert_equal 3 [remrangebyscore 2 4]
|
||||
assert_equal {a e} [r zrange zset 0 -1]
|
||||
|
||||
# start underflow
|
||||
assert_equal 1 [remrangebyscore -10 1]
|
||||
assert_equal {b c d e} [r zrange zset 0 -1]
|
||||
|
||||
# end overflow
|
||||
assert_equal 1 [remrangebyscore 5 10]
|
||||
assert_equal {a b c d} [r zrange zset 0 -1]
|
||||
|
||||
# switch min and max
|
||||
assert_equal 0 [remrangebyscore 4 2]
|
||||
assert_equal {a b c d e} [r zrange zset 0 -1]
|
||||
|
||||
# -inf to mid
|
||||
assert_equal 3 [remrangebyscore -inf 3]
|
||||
assert_equal {d e} [r zrange zset 0 -1]
|
||||
|
||||
# mid to +inf
|
||||
assert_equal 3 [remrangebyscore 3 +inf]
|
||||
assert_equal {a b} [r zrange zset 0 -1]
|
||||
|
||||
# -inf to +inf
|
||||
assert_equal 5 [remrangebyscore -inf +inf]
|
||||
assert_equal {} [r zrange zset 0 -1]
|
||||
|
||||
# exclusive min
|
||||
assert_equal 4 [remrangebyscore (1 5]
|
||||
assert_equal {a} [r zrange zset 0 -1]
|
||||
assert_equal 3 [remrangebyscore (2 5]
|
||||
assert_equal {a b} [r zrange zset 0 -1]
|
||||
|
||||
# exclusive max
|
||||
assert_equal 4 [remrangebyscore 1 (5]
|
||||
assert_equal {e} [r zrange zset 0 -1]
|
||||
assert_equal 3 [remrangebyscore 1 (4]
|
||||
assert_equal {d e} [r zrange zset 0 -1]
|
||||
|
||||
# exclusive min and max
|
||||
assert_equal 3 [remrangebyscore (1 (5]
|
||||
assert_equal {a e} [r zrange zset 0 -1]
|
||||
|
||||
# destroy when empty
|
||||
assert_equal 5 [remrangebyscore 1 5]
|
||||
assert_equal 0 [r exists zset]
|
||||
}
|
||||
|
||||
test "ZREMRANGEBYSCORE with non-value min or max" {
|
||||
assert_error "*not a double*" {r zremrangebyscore fooz str 1}
|
||||
assert_error "*not a double*" {r zremrangebyscore fooz 1 str}
|
||||
assert_error "*not a double*" {r zremrangebyscore fooz 1 NaN}
|
||||
}
|
||||
|
||||
test "ZREMRANGEBYRANK basics" {
|
||||
proc remrangebyrank {min max} {
|
||||
create_zset zset {1 a 2 b 3 c 4 d 5 e}
|
||||
assert_equal 1 [r exists zset]
|
||||
r zremrangebyrank zset $min $max
|
||||
}
|
||||
|
||||
# inner range
|
||||
assert_equal 3 [remrangebyrank 1 3]
|
||||
assert_equal {a e} [r zrange zset 0 -1]
|
||||
|
||||
# start underflow
|
||||
assert_equal 1 [remrangebyrank -10 0]
|
||||
assert_equal {b c d e} [r zrange zset 0 -1]
|
||||
|
||||
# start overflow
|
||||
assert_equal 0 [remrangebyrank 10 -1]
|
||||
assert_equal {a b c d e} [r zrange zset 0 -1]
|
||||
|
||||
# end underflow
|
||||
assert_equal 0 [remrangebyrank 0 -10]
|
||||
assert_equal {a b c d e} [r zrange zset 0 -1]
|
||||
|
||||
# end overflow
|
||||
assert_equal 5 [remrangebyrank 0 10]
|
||||
assert_equal {} [r zrange zset 0 -1]
|
||||
|
||||
# destroy when empty
|
||||
assert_equal 5 [remrangebyrank 0 4]
|
||||
assert_equal 0 [r exists zset]
|
||||
}
|
||||
|
||||
test "ZUNIONSTORE against non-existing key doesn't set destination - $encoding" {
|
||||
r del zseta
|
||||
assert_equal 0 [r zunionstore dst_key 1 zseta]
|
||||
assert_equal 0 [r exists dst_key]
|
||||
}
|
||||
|
||||
test "ZUNIONSTORE basics - $encoding" {
|
||||
r del zseta zsetb zsetc
|
||||
r zadd zseta 1 a
|
||||
r zadd zseta 2 b
|
||||
r zadd zseta 3 c
|
||||
r zadd zsetb 1 b
|
||||
r zadd zsetb 2 c
|
||||
r zadd zsetb 3 d
|
||||
|
||||
assert_equal 4 [r zunionstore zsetc 2 zseta zsetb]
|
||||
assert_equal {a 1 b 3 d 3 c 5} [r zrange zsetc 0 -1 withscores]
|
||||
}
|
||||
|
||||
test "ZUNIONSTORE with weights - $encoding" {
|
||||
assert_equal 4 [r zunionstore zsetc 2 zseta zsetb weights 2 3]
|
||||
assert_equal {a 2 b 7 d 9 c 12} [r zrange zsetc 0 -1 withscores]
|
||||
}
|
||||
|
||||
test "ZUNIONSTORE with a regular set and weights - $encoding" {
|
||||
r del seta
|
||||
r sadd seta a
|
||||
r sadd seta b
|
||||
r sadd seta c
|
||||
|
||||
assert_equal 4 [r zunionstore zsetc 2 seta zsetb weights 2 3]
|
||||
assert_equal {a 2 b 5 c 8 d 9} [r zrange zsetc 0 -1 withscores]
|
||||
}
|
||||
|
||||
test "ZUNIONSTORE with AGGREGATE MIN - $encoding" {
|
||||
assert_equal 4 [r zunionstore zsetc 2 zseta zsetb aggregate min]
|
||||
assert_equal {a 1 b 1 c 2 d 3} [r zrange zsetc 0 -1 withscores]
|
||||
}
|
||||
|
||||
test "ZUNIONSTORE with AGGREGATE MAX - $encoding" {
|
||||
assert_equal 4 [r zunionstore zsetc 2 zseta zsetb aggregate max]
|
||||
assert_equal {a 1 b 2 c 3 d 3} [r zrange zsetc 0 -1 withscores]
|
||||
}
|
||||
|
||||
test "ZINTERSTORE basics - $encoding" {
|
||||
assert_equal 2 [r zinterstore zsetc 2 zseta zsetb]
|
||||
assert_equal {b 3 c 5} [r zrange zsetc 0 -1 withscores]
|
||||
}
|
||||
|
||||
test "ZINTERSTORE with weights - $encoding" {
|
||||
assert_equal 2 [r zinterstore zsetc 2 zseta zsetb weights 2 3]
|
||||
assert_equal {b 7 c 12} [r zrange zsetc 0 -1 withscores]
|
||||
}
|
||||
|
||||
test "ZINTERSTORE with a regular set and weights - $encoding" {
|
||||
r del seta
|
||||
r sadd seta a
|
||||
r sadd seta b
|
||||
r sadd seta c
|
||||
assert_equal 2 [r zinterstore zsetc 2 seta zsetb weights 2 3]
|
||||
assert_equal {b 5 c 8} [r zrange zsetc 0 -1 withscores]
|
||||
}
|
||||
|
||||
test "ZINTERSTORE with AGGREGATE MIN - $encoding" {
|
||||
assert_equal 2 [r zinterstore zsetc 2 zseta zsetb aggregate min]
|
||||
assert_equal {b 1 c 2} [r zrange zsetc 0 -1 withscores]
|
||||
}
|
||||
|
||||
test "ZINTERSTORE with AGGREGATE MAX - $encoding" {
|
||||
assert_equal 2 [r zinterstore zsetc 2 zseta zsetb aggregate max]
|
||||
assert_equal {b 2 c 3} [r zrange zsetc 0 -1 withscores]
|
||||
}
|
||||
|
||||
foreach cmd {ZUNIONSTORE ZINTERSTORE} {
|
||||
test "$cmd with +inf/-inf scores - $encoding" {
|
||||
r del zsetinf1 zsetinf2
|
||||
|
||||
r zadd zsetinf1 +inf key
|
||||
r zadd zsetinf2 +inf key
|
||||
r $cmd zsetinf3 2 zsetinf1 zsetinf2
|
||||
assert_equal inf [r zscore zsetinf3 key]
|
||||
|
||||
r zadd zsetinf1 -inf key
|
||||
r zadd zsetinf2 +inf key
|
||||
r $cmd zsetinf3 2 zsetinf1 zsetinf2
|
||||
assert_equal 0 [r zscore zsetinf3 key]
|
||||
|
||||
r zadd zsetinf1 +inf key
|
||||
r zadd zsetinf2 -inf key
|
||||
r $cmd zsetinf3 2 zsetinf1 zsetinf2
|
||||
assert_equal 0 [r zscore zsetinf3 key]
|
||||
|
||||
r zadd zsetinf1 -inf key
|
||||
r zadd zsetinf2 -inf key
|
||||
r $cmd zsetinf3 2 zsetinf1 zsetinf2
|
||||
assert_equal -inf [r zscore zsetinf3 key]
|
||||
}
|
||||
|
||||
test "$cmd with NaN weights $encoding" {
|
||||
r del zsetinf1 zsetinf2
|
||||
|
||||
r zadd zsetinf1 1.0 key
|
||||
r zadd zsetinf2 1.0 key
|
||||
assert_error "*weight value is not a double*" {
|
||||
r $cmd zsetinf3 2 zsetinf1 zsetinf2 weights nan nan
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
basics ziplist
|
||||
basics skiplist
|
||||
|
||||
proc stressers {encoding} {
|
||||
if {$encoding == "ziplist"} {
|
||||
# Little extra to allow proper fuzzing in the sorting stresser
|
||||
r config set zset-max-ziplist-entries 256
|
||||
r config set zset-max-ziplist-value 64
|
||||
set elements 128
|
||||
} elseif {$encoding == "skiplist"} {
|
||||
r config set zset-max-ziplist-entries 0
|
||||
r config set zset-max-ziplist-value 0
|
||||
set elements 1000
|
||||
} else {
|
||||
puts "Unknown sorted set encoding"
|
||||
exit
|
||||
}
|
||||
|
||||
test "ZSCORE - $encoding" {
|
||||
r del zscoretest
|
||||
set aux {}
|
||||
for {set i 0} {$i < $elements} {incr i} {
|
||||
set score [expr rand()]
|
||||
lappend aux $score
|
||||
r zadd zscoretest $score $i
|
||||
}
|
||||
|
||||
assert_encoding $encoding zscoretest
|
||||
for {set i 0} {$i < $elements} {incr i} {
|
||||
assert_equal [lindex $aux $i] [r zscore zscoretest $i]
|
||||
}
|
||||
}
|
||||
|
||||
test "ZSCORE after a DEBUG RELOAD - $encoding" {
|
||||
r del zscoretest
|
||||
set aux {}
|
||||
for {set i 0} {$i < $elements} {incr i} {
|
||||
set score [expr rand()]
|
||||
lappend aux $score
|
||||
r zadd zscoretest $score $i
|
||||
}
|
||||
|
||||
r debug reload
|
||||
assert_encoding $encoding zscoretest
|
||||
for {set i 0} {$i < $elements} {incr i} {
|
||||
assert_equal [lindex $aux $i] [r zscore zscoretest $i]
|
||||
}
|
||||
}
|
||||
|
||||
test "ZSET sorting stresser - $encoding" {
|
||||
set delta 0
|
||||
for {set test 0} {$test < 2} {incr test} {
|
||||
unset -nocomplain auxarray
|
||||
array set auxarray {}
|
||||
set auxlist {}
|
||||
r del myzset
|
||||
for {set i 0} {$i < $elements} {incr i} {
|
||||
if {$test == 0} {
|
||||
set score [expr rand()]
|
||||
} else {
|
||||
set score [expr int(rand()*10)]
|
||||
}
|
||||
set auxarray($i) $score
|
||||
r zadd myzset $score $i
|
||||
# Random update
|
||||
if {[expr rand()] < .2} {
|
||||
set j [expr int(rand()*1000)]
|
||||
if {$test == 0} {
|
||||
set score [expr rand()]
|
||||
} else {
|
||||
set score [expr int(rand()*10)]
|
||||
}
|
||||
set auxarray($j) $score
|
||||
r zadd myzset $score $j
|
||||
}
|
||||
}
|
||||
foreach {item score} [array get auxarray] {
|
||||
lappend auxlist [list $score $item]
|
||||
}
|
||||
set sorted [lsort -command zlistAlikeSort $auxlist]
|
||||
set auxlist {}
|
||||
foreach x $sorted {
|
||||
lappend auxlist [lindex $x 1]
|
||||
}
|
||||
|
||||
assert_encoding $encoding myzset
|
||||
set fromredis [r zrange myzset 0 -1]
|
||||
set delta 0
|
||||
for {set i 0} {$i < [llength $fromredis]} {incr i} {
|
||||
if {[lindex $fromredis $i] != [lindex $auxlist $i]} {
|
||||
incr delta
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_equal 0 $delta
|
||||
}
|
||||
|
||||
test "ZRANGEBYSCORE fuzzy test, 100 ranges in $elements element sorted set - $encoding" {
|
||||
set err {}
|
||||
r del zset
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
for {set i 0} {$i < $elements} {incr i} {
|
||||
r zadd zset [expr rand()] $i
|
||||
}
|
||||
|
||||
assert_encoding $encoding zset
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
set min [expr rand()]
|
||||
set max [expr rand()]
|
||||
@ -337,198 +633,17 @@ start_server {tags {"zset"}} {
|
||||
}
|
||||
}
|
||||
}
|
||||
set _ $err
|
||||
} {}
|
||||
assert_equal {} $err
|
||||
}
|
||||
|
||||
test "ZREMRANGEBYSCORE basics" {
|
||||
proc remrangebyscore {min max} {
|
||||
create_zset zset {1 a 2 b 3 c 4 d 5 e}
|
||||
r zremrangebyscore zset $min $max
|
||||
}
|
||||
|
||||
# inner range
|
||||
assert_equal 3 [remrangebyscore 2 4]
|
||||
assert_equal {a e} [r zrange zset 0 -1]
|
||||
|
||||
# start underflow
|
||||
assert_equal 1 [remrangebyscore -10 1]
|
||||
assert_equal {b c d e} [r zrange zset 0 -1]
|
||||
|
||||
# end overflow
|
||||
assert_equal 1 [remrangebyscore 5 10]
|
||||
assert_equal {a b c d} [r zrange zset 0 -1]
|
||||
|
||||
# switch min and max
|
||||
assert_equal 0 [remrangebyscore 4 2]
|
||||
assert_equal {a b c d e} [r zrange zset 0 -1]
|
||||
|
||||
# -inf to mid
|
||||
assert_equal 3 [remrangebyscore -inf 3]
|
||||
assert_equal {d e} [r zrange zset 0 -1]
|
||||
|
||||
# mid to +inf
|
||||
assert_equal 3 [remrangebyscore 3 +inf]
|
||||
assert_equal {a b} [r zrange zset 0 -1]
|
||||
|
||||
# -inf to +inf
|
||||
assert_equal 5 [remrangebyscore -inf +inf]
|
||||
assert_equal {} [r zrange zset 0 -1]
|
||||
|
||||
# exclusive min
|
||||
assert_equal 4 [remrangebyscore (1 5]
|
||||
assert_equal {a} [r zrange zset 0 -1]
|
||||
assert_equal 3 [remrangebyscore (2 5]
|
||||
assert_equal {a b} [r zrange zset 0 -1]
|
||||
|
||||
# exclusive max
|
||||
assert_equal 4 [remrangebyscore 1 (5]
|
||||
assert_equal {e} [r zrange zset 0 -1]
|
||||
assert_equal 3 [remrangebyscore 1 (4]
|
||||
assert_equal {d e} [r zrange zset 0 -1]
|
||||
|
||||
# exclusive min and max
|
||||
assert_equal 3 [remrangebyscore (1 (5]
|
||||
assert_equal {a e} [r zrange zset 0 -1]
|
||||
}
|
||||
|
||||
test "ZREMRANGEBYSCORE with non-value min or max" {
|
||||
assert_error "*not a double*" {r zremrangebyscore fooz str 1}
|
||||
assert_error "*not a double*" {r zremrangebyscore fooz 1 str}
|
||||
assert_error "*not a double*" {r zremrangebyscore fooz 1 NaN}
|
||||
}
|
||||
|
||||
test "ZREMRANGEBYRANK basics" {
|
||||
proc remrangebyrank {min max} {
|
||||
create_zset zset {1 a 2 b 3 c 4 d 5 e}
|
||||
r zremrangebyrank zset $min $max
|
||||
}
|
||||
|
||||
# inner range
|
||||
assert_equal 3 [remrangebyrank 1 3]
|
||||
assert_equal {a e} [r zrange zset 0 -1]
|
||||
|
||||
# start underflow
|
||||
assert_equal 1 [remrangebyrank -10 0]
|
||||
assert_equal {b c d e} [r zrange zset 0 -1]
|
||||
|
||||
# start overflow
|
||||
assert_equal 0 [remrangebyrank 10 -1]
|
||||
assert_equal {a b c d e} [r zrange zset 0 -1]
|
||||
|
||||
# end underflow
|
||||
assert_equal 0 [remrangebyrank 0 -10]
|
||||
assert_equal {a b c d e} [r zrange zset 0 -1]
|
||||
|
||||
# end overflow
|
||||
assert_equal 5 [remrangebyrank 0 10]
|
||||
assert_equal {} [r zrange zset 0 -1]
|
||||
}
|
||||
|
||||
test {ZUNIONSTORE against non-existing key doesn't set destination} {
|
||||
r del zseta
|
||||
list [r zunionstore dst_key 1 zseta] [r exists dst_key]
|
||||
} {0 0}
|
||||
|
||||
test {ZUNIONSTORE basics} {
|
||||
r del zseta zsetb zsetc
|
||||
r zadd zseta 1 a
|
||||
r zadd zseta 2 b
|
||||
r zadd zseta 3 c
|
||||
r zadd zsetb 1 b
|
||||
r zadd zsetb 2 c
|
||||
r zadd zsetb 3 d
|
||||
list [r zunionstore zsetc 2 zseta zsetb] [r zrange zsetc 0 -1 withscores]
|
||||
} {4 {a 1 b 3 d 3 c 5}}
|
||||
|
||||
test {ZUNIONSTORE with weights} {
|
||||
list [r zunionstore zsetc 2 zseta zsetb weights 2 3] [r zrange zsetc 0 -1 withscores]
|
||||
} {4 {a 2 b 7 d 9 c 12}}
|
||||
|
||||
test {ZUNIONSTORE with a regular set and weights} {
|
||||
r del seta
|
||||
r sadd seta a
|
||||
r sadd seta b
|
||||
r sadd seta c
|
||||
list [r zunionstore zsetc 2 seta zsetb weights 2 3] [r zrange zsetc 0 -1 withscores]
|
||||
} {4 {a 2 b 5 c 8 d 9}}
|
||||
|
||||
test {ZUNIONSTORE with AGGREGATE MIN} {
|
||||
list [r zunionstore zsetc 2 zseta zsetb aggregate min] [r zrange zsetc 0 -1 withscores]
|
||||
} {4 {a 1 b 1 c 2 d 3}}
|
||||
|
||||
test {ZUNIONSTORE with AGGREGATE MAX} {
|
||||
list [r zunionstore zsetc 2 zseta zsetb aggregate max] [r zrange zsetc 0 -1 withscores]
|
||||
} {4 {a 1 b 2 c 3 d 3}}
|
||||
|
||||
test {ZINTERSTORE basics} {
|
||||
list [r zinterstore zsetc 2 zseta zsetb] [r zrange zsetc 0 -1 withscores]
|
||||
} {2 {b 3 c 5}}
|
||||
|
||||
test {ZINTERSTORE with weights} {
|
||||
list [r zinterstore zsetc 2 zseta zsetb weights 2 3] [r zrange zsetc 0 -1 withscores]
|
||||
} {2 {b 7 c 12}}
|
||||
|
||||
test {ZINTERSTORE with a regular set and weights} {
|
||||
r del seta
|
||||
r sadd seta a
|
||||
r sadd seta b
|
||||
r sadd seta c
|
||||
list [r zinterstore zsetc 2 seta zsetb weights 2 3] [r zrange zsetc 0 -1 withscores]
|
||||
} {2 {b 5 c 8}}
|
||||
|
||||
test {ZINTERSTORE with AGGREGATE MIN} {
|
||||
list [r zinterstore zsetc 2 zseta zsetb aggregate min] [r zrange zsetc 0 -1 withscores]
|
||||
} {2 {b 1 c 2}}
|
||||
|
||||
test {ZINTERSTORE with AGGREGATE MAX} {
|
||||
list [r zinterstore zsetc 2 zseta zsetb aggregate max] [r zrange zsetc 0 -1 withscores]
|
||||
} {2 {b 2 c 3}}
|
||||
|
||||
foreach cmd {ZUNIONSTORE ZINTERSTORE} {
|
||||
test "$cmd with +inf/-inf scores" {
|
||||
r del zsetinf1 zsetinf2
|
||||
|
||||
r zadd zsetinf1 +inf key
|
||||
r zadd zsetinf2 +inf key
|
||||
r $cmd zsetinf3 2 zsetinf1 zsetinf2
|
||||
assert_equal inf [r zscore zsetinf3 key]
|
||||
|
||||
r zadd zsetinf1 -inf key
|
||||
r zadd zsetinf2 +inf key
|
||||
r $cmd zsetinf3 2 zsetinf1 zsetinf2
|
||||
assert_equal 0 [r zscore zsetinf3 key]
|
||||
|
||||
r zadd zsetinf1 +inf key
|
||||
r zadd zsetinf2 -inf key
|
||||
r $cmd zsetinf3 2 zsetinf1 zsetinf2
|
||||
assert_equal 0 [r zscore zsetinf3 key]
|
||||
|
||||
r zadd zsetinf1 -inf key
|
||||
r zadd zsetinf2 -inf key
|
||||
r $cmd zsetinf3 2 zsetinf1 zsetinf2
|
||||
assert_equal -inf [r zscore zsetinf3 key]
|
||||
}
|
||||
|
||||
test "$cmd with NaN weights" {
|
||||
r del zsetinf1 zsetinf2
|
||||
|
||||
r zadd zsetinf1 1.0 key
|
||||
r zadd zsetinf2 1.0 key
|
||||
assert_error "*weight value is not a double*" {
|
||||
r $cmd zsetinf3 2 zsetinf1 zsetinf2 weights nan nan
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tags {"slow"} {
|
||||
test {ZSETs skiplist implementation backlink consistency test} {
|
||||
test "ZSETs skiplist implementation backlink consistency test - $encoding" {
|
||||
set diff 0
|
||||
set elements 10000
|
||||
for {set j 0} {$j < $elements} {incr j} {
|
||||
r zadd myzset [expr rand()] "Element-$j"
|
||||
r zrem myzset "Element-[expr int(rand()*$elements)]"
|
||||
}
|
||||
|
||||
assert_encoding $encoding myzset
|
||||
set l1 [r zrange myzset 0 -1]
|
||||
set l2 [r zrevrange myzset 0 -1]
|
||||
for {set j 0} {$j < [llength $l1]} {incr j} {
|
||||
@ -536,20 +651,22 @@ start_server {tags {"zset"}} {
|
||||
incr diff
|
||||
}
|
||||
}
|
||||
format $diff
|
||||
} {0}
|
||||
assert_equal 0 $diff
|
||||
}
|
||||
|
||||
test {ZSETs ZRANK augmented skip list stress testing} {
|
||||
test "ZSETs ZRANK augmented skip list stress testing - $encoding" {
|
||||
set err {}
|
||||
r del myzset
|
||||
for {set k 0} {$k < 10000} {incr k} {
|
||||
set i [expr {$k%1000}]
|
||||
for {set k 0} {$k < 2000} {incr k} {
|
||||
set i [expr {$k % $elements}]
|
||||
if {[expr rand()] < .2} {
|
||||
r zrem myzset $i
|
||||
} else {
|
||||
set score [expr rand()]
|
||||
r zadd myzset $score $i
|
||||
assert_encoding $encoding myzset
|
||||
}
|
||||
|
||||
set card [r zcard myzset]
|
||||
if {$card > 0} {
|
||||
set index [randomInt $card]
|
||||
@ -561,20 +678,12 @@ start_server {tags {"zset"}} {
|
||||
}
|
||||
}
|
||||
}
|
||||
set _ $err
|
||||
} {}
|
||||
assert_equal {} $err
|
||||
}
|
||||
}
|
||||
|
||||
test {ZSET element can't be set to NaN with ZADD} {
|
||||
assert_error "*not a double*" {r zadd myzset nan abc}
|
||||
}
|
||||
|
||||
test {ZSET element can't be set to NaN with ZINCRBY} {
|
||||
assert_error "*not a double*" {r zadd myzset nan abc}
|
||||
}
|
||||
|
||||
test {ZINCRBY calls leading to NaN result in error} {
|
||||
r zincrby myzset +inf abc
|
||||
assert_error "*NaN*" {r zincrby myzset -inf abc}
|
||||
tags {"slow"} {
|
||||
stressers ziplist
|
||||
stressers skiplist
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user