Commit aaada3f9 authored by Pieter Noordhuis's avatar Pieter Noordhuis
Browse files

Merge branch 'master' into intset-split

Conflicts:
	src/Makefile
	src/t_set.c
parents 2767f1c0 cbce5171
...@@ -188,6 +188,7 @@ void saddCommand(redisClient *c) { ...@@ -188,6 +188,7 @@ void saddCommand(redisClient *c) {
} }
} }
if (setTypeAdd(set,c->argv[2])) { if (setTypeAdd(set,c->argv[2])) {
touchWatchedKey(c->db,c->argv[1]);
server.dirty++; server.dirty++;
addReply(c,shared.cone); addReply(c,shared.cone);
} else { } else {
...@@ -203,6 +204,7 @@ void sremCommand(redisClient *c) { ...@@ -203,6 +204,7 @@ void sremCommand(redisClient *c) {
if (setTypeRemove(set,c->argv[2])) { if (setTypeRemove(set,c->argv[2])) {
if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]); if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++; server.dirty++;
addReply(c,shared.cone); addReply(c,shared.cone);
} else { } else {
...@@ -241,6 +243,8 @@ void smoveCommand(redisClient *c) { ...@@ -241,6 +243,8 @@ void smoveCommand(redisClient *c) {
/* Remove the src set from the database when empty */ /* Remove the src set from the database when empty */
if (setTypeSize(srcset) == 0) dbDelete(c->db,c->argv[1]); if (setTypeSize(srcset) == 0) dbDelete(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[2]);
server.dirty++; server.dirty++;
/* Create the destination set when it doesn't exist */ /* Create the destination set when it doesn't exist */
...@@ -289,6 +293,7 @@ void spopCommand(redisClient *c) { ...@@ -289,6 +293,7 @@ void spopCommand(redisClient *c) {
addReplyBulk(c,ele); addReplyBulk(c,ele);
decrRefCount(ele); decrRefCount(ele);
if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]); if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++; server.dirty++;
} }
} }
...@@ -325,8 +330,10 @@ void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum, ...@@ -325,8 +330,10 @@ void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum,
if (!setobj) { if (!setobj) {
zfree(sets); zfree(sets);
if (dstkey) { if (dstkey) {
if (dbDelete(c->db,dstkey)) if (dbDelete(c->db,dstkey)) {
touchWatchedKey(c->db,dstkey);
server.dirty++; server.dirty++;
}
addReply(c,shared.czero); addReply(c,shared.czero);
} else { } else {
addReply(c,shared.emptymultibulk); addReply(c,shared.emptymultibulk);
...@@ -390,6 +397,7 @@ void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum, ...@@ -390,6 +397,7 @@ void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum,
decrRefCount(dstset); decrRefCount(dstset);
addReply(c,shared.czero); addReply(c,shared.czero);
} }
touchWatchedKey(c->db,dstkey);
server.dirty++; server.dirty++;
} else { } else {
lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",cardinality); lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",cardinality);
...@@ -481,6 +489,7 @@ void sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj * ...@@ -481,6 +489,7 @@ void sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj *
decrRefCount(dstset); decrRefCount(dstset);
addReply(c,shared.czero); addReply(c,shared.czero);
} }
touchWatchedKey(c->db,dstkey);
server.dirty++; server.dirty++;
} }
zfree(sets); zfree(sets);
......
...@@ -17,8 +17,6 @@ void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expir ...@@ -17,8 +17,6 @@ void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expir
} }
} }
touchWatchedKey(c->db,key);
if (nx) deleteIfVolatile(c->db,key);
retval = dbAdd(c->db,key,val); retval = dbAdd(c->db,key,val);
if (retval == REDIS_ERR) { if (retval == REDIS_ERR) {
if (!nx) { if (!nx) {
...@@ -31,6 +29,7 @@ void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expir ...@@ -31,6 +29,7 @@ void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expir
} else { } else {
incrRefCount(val); incrRefCount(val);
} }
touchWatchedKey(c->db,key);
server.dirty++; server.dirty++;
removeExpire(c->db,key); removeExpire(c->db,key);
if (expire) setExpire(c->db,key,time(NULL)+seconds); if (expire) setExpire(c->db,key,time(NULL)+seconds);
...@@ -72,6 +71,7 @@ void getsetCommand(redisClient *c) { ...@@ -72,6 +71,7 @@ void getsetCommand(redisClient *c) {
if (getGenericCommand(c) == REDIS_ERR) return; if (getGenericCommand(c) == REDIS_ERR) return;
dbReplace(c->db,c->argv[1],c->argv[2]); dbReplace(c->db,c->argv[1],c->argv[2]);
incrRefCount(c->argv[2]); incrRefCount(c->argv[2]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++; server.dirty++;
removeExpire(c->db,c->argv[1]); removeExpire(c->db,c->argv[1]);
} }
...@@ -120,6 +120,7 @@ void msetGenericCommand(redisClient *c, int nx) { ...@@ -120,6 +120,7 @@ void msetGenericCommand(redisClient *c, int nx) {
dbReplace(c->db,c->argv[j],c->argv[j+1]); dbReplace(c->db,c->argv[j],c->argv[j+1]);
incrRefCount(c->argv[j+1]); incrRefCount(c->argv[j+1]);
removeExpire(c->db,c->argv[j]); removeExpire(c->db,c->argv[j]);
touchWatchedKey(c->db,c->argv[j]);
} }
server.dirty += (c->argc-1)/2; server.dirty += (c->argc-1)/2;
addReply(c, nx ? shared.cone : shared.ok); addReply(c, nx ? shared.cone : shared.ok);
...@@ -144,6 +145,7 @@ void incrDecrCommand(redisClient *c, long long incr) { ...@@ -144,6 +145,7 @@ void incrDecrCommand(redisClient *c, long long incr) {
value += incr; value += incr;
o = createStringObjectFromLongLong(value); o = createStringObjectFromLongLong(value);
dbReplace(c->db,c->argv[1],o); dbReplace(c->db,c->argv[1],o);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++; server.dirty++;
addReply(c,shared.colon); addReply(c,shared.colon);
addReply(c,o); addReply(c,o);
...@@ -207,6 +209,7 @@ void appendCommand(redisClient *c) { ...@@ -207,6 +209,7 @@ void appendCommand(redisClient *c) {
} }
totlen = sdslen(o->ptr); totlen = sdslen(o->ptr);
} }
touchWatchedKey(c->db,c->argv[1]);
server.dirty++; server.dirty++;
addReplySds(c,sdscatprintf(sdsempty(),":%lu\r\n",(unsigned long)totlen)); addReplySds(c,sdscatprintf(sdsempty(),":%lu\r\n",(unsigned long)totlen));
} }
...@@ -248,4 +251,13 @@ void substrCommand(redisClient *c) { ...@@ -248,4 +251,13 @@ void substrCommand(redisClient *c) {
decrRefCount(o); decrRefCount(o);
} }
void strlenCommand(redisClient *c) {
robj *o;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,o,REDIS_STRING)) return;
o = getDecodedObject(o);
addReplyLongLong(c,sdslen(o->ptr));
decrRefCount(o);
}
...@@ -327,11 +327,6 @@ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double scoreval, i ...@@ -327,11 +327,6 @@ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double scoreval, i
zset *zs; zset *zs;
double *score; double *score;
if (isnan(scoreval)) {
addReplySds(c,sdsnew("-ERR provide score is Not A Number (nan)\r\n"));
return;
}
zsetobj = lookupKeyWrite(c->db,key); zsetobj = lookupKeyWrite(c->db,key);
if (zsetobj == NULL) { if (zsetobj == NULL) {
zsetobj = createZsetObject(); zsetobj = createZsetObject();
...@@ -361,7 +356,7 @@ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double scoreval, i ...@@ -361,7 +356,7 @@ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double scoreval, i
} }
if (isnan(*score)) { if (isnan(*score)) {
addReplySds(c, addReplySds(c,
sdsnew("-ERR resulting score is Not A Number (nan)\r\n")); sdsnew("-ERR resulting score is not a number (NaN)\r\n"));
zfree(score); zfree(score);
/* Note that we don't need to check if the zset may be empty and /* Note that we don't need to check if the zset may be empty and
* should be removed here, as we can only obtain Nan as score if * should be removed here, as we can only obtain Nan as score if
...@@ -379,6 +374,7 @@ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double scoreval, i ...@@ -379,6 +374,7 @@ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double scoreval, i
incrRefCount(ele); /* added to hash */ incrRefCount(ele); /* added to hash */
zslInsert(zs->zsl,*score,ele); zslInsert(zs->zsl,*score,ele);
incrRefCount(ele); /* added to skiplist */ incrRefCount(ele); /* added to skiplist */
touchWatchedKey(c->db,c->argv[1]);
server.dirty++; server.dirty++;
if (doincrement) if (doincrement)
addReplyDouble(c,*score); addReplyDouble(c,*score);
...@@ -402,6 +398,7 @@ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double scoreval, i ...@@ -402,6 +398,7 @@ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double scoreval, i
incrRefCount(ele); incrRefCount(ele);
/* Update the score in the hash table */ /* Update the score in the hash table */
dictReplace(zs->dict,ele,score); dictReplace(zs->dict,ele,score);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++; server.dirty++;
} else { } else {
zfree(score); zfree(score);
...@@ -415,15 +412,13 @@ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double scoreval, i ...@@ -415,15 +412,13 @@ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double scoreval, i
void zaddCommand(redisClient *c) { void zaddCommand(redisClient *c) {
double scoreval; double scoreval;
if (getDoubleFromObjectOrReply(c,c->argv[2],&scoreval,NULL) != REDIS_OK) return;
if (getDoubleFromObjectOrReply(c, c->argv[2], &scoreval, NULL) != REDIS_OK) return;
zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,0); zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,0);
} }
void zincrbyCommand(redisClient *c) { void zincrbyCommand(redisClient *c) {
double scoreval; double scoreval;
if (getDoubleFromObjectOrReply(c,c->argv[2],&scoreval,NULL) != REDIS_OK) return;
if (getDoubleFromObjectOrReply(c, c->argv[2], &scoreval, NULL) != REDIS_OK) return;
zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,1); zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,1);
} }
...@@ -452,6 +447,7 @@ void zremCommand(redisClient *c) { ...@@ -452,6 +447,7 @@ void zremCommand(redisClient *c) {
dictDelete(zs->dict,c->argv[2]); dictDelete(zs->dict,c->argv[2]);
if (htNeedsResize(zs->dict)) dictResize(zs->dict); if (htNeedsResize(zs->dict)) dictResize(zs->dict);
if (dictSize(zs->dict) == 0) dbDelete(c->db,c->argv[1]); if (dictSize(zs->dict) == 0) dbDelete(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++; server.dirty++;
addReply(c,shared.cone); addReply(c,shared.cone);
} }
...@@ -473,6 +469,7 @@ void zremrangebyscoreCommand(redisClient *c) { ...@@ -473,6 +469,7 @@ void zremrangebyscoreCommand(redisClient *c) {
deleted = zslDeleteRangeByScore(zs->zsl,min,max,zs->dict); deleted = zslDeleteRangeByScore(zs->zsl,min,max,zs->dict);
if (htNeedsResize(zs->dict)) dictResize(zs->dict); if (htNeedsResize(zs->dict)) dictResize(zs->dict);
if (dictSize(zs->dict) == 0) dbDelete(c->db,c->argv[1]); if (dictSize(zs->dict) == 0) dbDelete(c->db,c->argv[1]);
if (deleted) touchWatchedKey(c->db,c->argv[1]);
server.dirty += deleted; server.dirty += deleted;
addReplyLongLong(c,deleted); addReplyLongLong(c,deleted);
} }
...@@ -497,9 +494,9 @@ void zremrangebyrankCommand(redisClient *c) { ...@@ -497,9 +494,9 @@ void zremrangebyrankCommand(redisClient *c) {
if (start < 0) start = llen+start; if (start < 0) start = llen+start;
if (end < 0) end = llen+end; if (end < 0) end = llen+end;
if (start < 0) start = 0; if (start < 0) start = 0;
if (end < 0) end = 0;
/* indexes sanity checks */ /* Invariant: start >= 0, so this test will be true when end < 0.
* The range is empty when start > end or start >= length. */
if (start > end || start >= llen) { if (start > end || start >= llen) {
addReply(c,shared.czero); addReply(c,shared.czero);
return; return;
...@@ -511,6 +508,7 @@ void zremrangebyrankCommand(redisClient *c) { ...@@ -511,6 +508,7 @@ void zremrangebyrankCommand(redisClient *c) {
deleted = zslDeleteRangeByRank(zs->zsl,start+1,end+1,zs->dict); deleted = zslDeleteRangeByRank(zs->zsl,start+1,end+1,zs->dict);
if (htNeedsResize(zs->dict)) dictResize(zs->dict); if (htNeedsResize(zs->dict)) dictResize(zs->dict);
if (dictSize(zs->dict) == 0) dbDelete(c->db,c->argv[1]); if (dictSize(zs->dict) == 0) dbDelete(c->db,c->argv[1]);
if (deleted) touchWatchedKey(c->db,c->argv[1]);
server.dirty += deleted; server.dirty += deleted;
addReplyLongLong(c, deleted); addReplyLongLong(c, deleted);
} }
...@@ -536,6 +534,10 @@ int qsortCompareZsetopsrcByCardinality(const void *s1, const void *s2) { ...@@ -536,6 +534,10 @@ int qsortCompareZsetopsrcByCardinality(const void *s1, const void *s2) {
inline static void zunionInterAggregate(double *target, double val, int aggregate) { inline static void zunionInterAggregate(double *target, double val, int aggregate) {
if (aggregate == REDIS_AGGR_SUM) { if (aggregate == REDIS_AGGR_SUM) {
*target = *target + val; *target = *target + val;
/* The result of adding two doubles is NaN when one variable
* is +inf and the other is -inf. When these numbers are added,
* we maintain the convention of the result being 0.0. */
if (isnan(*target)) *target = 0.0;
} else if (aggregate == REDIS_AGGR_MIN) { } else if (aggregate == REDIS_AGGR_MIN) {
*target = val < *target ? val : *target; *target = val < *target ? val : *target;
} else if (aggregate == REDIS_AGGR_MAX) { } else if (aggregate == REDIS_AGGR_MAX) {
...@@ -554,6 +556,7 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) { ...@@ -554,6 +556,7 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
zset *dstzset; zset *dstzset;
dictIterator *di; dictIterator *di;
dictEntry *de; dictEntry *de;
int touched = 0;
/* expect setnum input keys to be given */ /* expect setnum input keys to be given */
setnum = atoi(c->argv[2]->ptr); setnum = atoi(c->argv[2]->ptr);
...@@ -598,8 +601,12 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) { ...@@ -598,8 +601,12 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
if (remaining >= (setnum + 1) && !strcasecmp(c->argv[j]->ptr,"weights")) { if (remaining >= (setnum + 1) && !strcasecmp(c->argv[j]->ptr,"weights")) {
j++; remaining--; j++; remaining--;
for (i = 0; i < setnum; i++, j++, remaining--) { for (i = 0; i < setnum; i++, j++, remaining--) {
if (getDoubleFromObjectOrReply(c, c->argv[j], &src[i].weight, NULL) != REDIS_OK) if (getDoubleFromObjectOrReply(c,c->argv[j],&src[i].weight,
"weight value is not a double") != REDIS_OK)
{
zfree(src);
return; return;
}
} }
} else if (remaining >= 2 && !strcasecmp(c->argv[j]->ptr,"aggregate")) { } else if (remaining >= 2 && !strcasecmp(c->argv[j]->ptr,"aggregate")) {
j++; remaining--; j++; remaining--;
...@@ -698,10 +705,15 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) { ...@@ -698,10 +705,15 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
redisAssert(op == REDIS_OP_INTER || op == REDIS_OP_UNION); redisAssert(op == REDIS_OP_INTER || op == REDIS_OP_UNION);
} }
dbDelete(c->db,dstkey); if (dbDelete(c->db,dstkey)) {
touchWatchedKey(c->db,dstkey);
touched = 1;
server.dirty++;
}
if (dstzset->zsl->length) { if (dstzset->zsl->length) {
dbAdd(c->db,dstkey,dstobj); dbAdd(c->db,dstkey,dstobj);
addReplyLongLong(c, dstzset->zsl->length); addReplyLongLong(c, dstzset->zsl->length);
if (!touched) touchWatchedKey(c->db,dstkey);
server.dirty++; server.dirty++;
} else { } else {
decrRefCount(dstobj); decrRefCount(dstobj);
...@@ -750,11 +762,10 @@ void zrangeGenericCommand(redisClient *c, int reverse) { ...@@ -750,11 +762,10 @@ void zrangeGenericCommand(redisClient *c, int reverse) {
if (start < 0) start = llen+start; if (start < 0) start = llen+start;
if (end < 0) end = llen+end; if (end < 0) end = llen+end;
if (start < 0) start = 0; if (start < 0) start = 0;
if (end < 0) end = 0;
/* indexes sanity checks */ /* Invariant: start >= 0, so this test will be true when end < 0.
* The range is empty when start > end or start >= length. */
if (start > end || start >= llen) { if (start > end || start >= llen) {
/* Out of range start or start > end result in empty list */
addReply(c,shared.emptymultibulk); addReply(c,shared.emptymultibulk);
return; return;
} }
......
...@@ -86,10 +86,9 @@ void vmInit(void) { ...@@ -86,10 +86,9 @@ void vmInit(void) {
} else { } else {
redisLog(REDIS_NOTICE,"Swap file allocated with success"); redisLog(REDIS_NOTICE,"Swap file allocated with success");
} }
server.vm_bitmap = zmalloc((server.vm_pages+7)/8); server.vm_bitmap = zcalloc((server.vm_pages+7)/8);
redisLog(REDIS_VERBOSE,"Allocated %lld bytes page table for %lld pages", redisLog(REDIS_VERBOSE,"Allocated %lld bytes page table for %lld pages",
(long long) (server.vm_pages+7)/8, server.vm_pages); (long long) (server.vm_pages+7)/8, server.vm_pages);
memset(server.vm_bitmap,0,(server.vm_pages+7)/8);
/* Initialize threaded I/O (used by Virtual Memory) */ /* Initialize threaded I/O (used by Virtual Memory) */
server.io_newjobs = listCreate(); server.io_newjobs = listCreate();
...@@ -1075,6 +1074,11 @@ int dontWaitForSwappedKey(redisClient *c, robj *key) { ...@@ -1075,6 +1074,11 @@ int dontWaitForSwappedKey(redisClient *c, robj *key) {
listIter li; listIter li;
struct dictEntry *de; struct dictEntry *de;
/* The key object might be destroyed when deleted from the c->io_keys
* list (and the "key" argument is physically the same object as the
* object inside the list), so we need to protect it. */
incrRefCount(key);
/* Remove the key from the list of keys this client is waiting for. */ /* Remove the key from the list of keys this client is waiting for. */
listRewind(c->io_keys,&li); listRewind(c->io_keys,&li);
while ((ln = listNext(&li)) != NULL) { while ((ln = listNext(&li)) != NULL) {
...@@ -1095,6 +1099,7 @@ int dontWaitForSwappedKey(redisClient *c, robj *key) { ...@@ -1095,6 +1099,7 @@ int dontWaitForSwappedKey(redisClient *c, robj *key) {
if (listLength(l) == 0) if (listLength(l) == 0)
dictDelete(c->db->io_keys,key); dictDelete(c->db->io_keys,key);
decrRefCount(key);
return listLength(c->io_keys) == 0; return listLength(c->io_keys) == 0;
} }
......
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
#include "zmalloc.h" #include "zmalloc.h"
#include "ziplist.h" #include "ziplist.h"
int ll2string(char *s, size_t len, long long value);
/* Important note: the ZIP_END value is used to depict the end of the /* Important note: the ZIP_END value is used to depict the end of the
* ziplist structure. When a pointer contains an entry, the first couple * ziplist structure. When a pointer contains an entry, the first couple
* of bytes contain the encoded length of the previous entry. This length * of bytes contain the encoded length of the previous entry. This length
...@@ -174,15 +176,27 @@ static int zipPrevLenByteDiff(unsigned char *p, unsigned int len) { ...@@ -174,15 +176,27 @@ static int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {
} }
/* Check if string pointed to by 'entry' can be encoded as an integer. /* Check if string pointed to by 'entry' can be encoded as an integer.
* Stores the integer value in 'v' and its encoding in 'encoding'. * Stores the integer value in 'v' and its encoding in 'encoding'. */
* Warning: this function requires a NULL-terminated string! */ static int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) {
static int zipTryEncoding(unsigned char *entry, long long *v, unsigned char *encoding) {
long long value; long long value;
char *eptr; char *eptr;
char buf[32];
if (entrylen >= 32 || entrylen == 0) return 0;
if (entry[0] == '-' || (entry[0] >= '0' && entry[0] <= '9')) { if (entry[0] == '-' || (entry[0] >= '0' && entry[0] <= '9')) {
value = strtoll((char*)entry,&eptr,10); int slen;
/* Perform a back-and-forth conversion to make sure that
* the string turned into an integer is not losing any info. */
memcpy(buf,entry,entrylen);
buf[entrylen] = '\0';
value = strtoll(buf,&eptr,10);
if (eptr[0] != '\0') return 0; if (eptr[0] != '\0') return 0;
slen = ll2string(buf,32,value);
if (entrylen != (unsigned)slen || memcmp(buf,entry,slen)) return 0;
/* Great, the string can be encoded. Check what's the smallest
* of our encoding types that can hold this value. */
if (value >= INT16_MIN && value <= INT16_MAX) { if (value >= INT16_MIN && value <= INT16_MAX) {
*encoding = ZIP_ENC_INT16; *encoding = ZIP_ENC_INT16;
} else if (value >= INT32_MIN && value <= INT32_MAX) { } else if (value >= INT32_MIN && value <= INT32_MAX) {
...@@ -329,7 +343,7 @@ static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsig ...@@ -329,7 +343,7 @@ static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsig
} }
/* See if the entry can be encoded */ /* See if the entry can be encoded */
if (zipTryEncoding(s,&value,&encoding)) { if (zipTryEncoding(s,slen,&value,&encoding)) {
reqlen = zipEncodingSize(encoding); reqlen = zipEncodingSize(encoding);
} else { } else {
reqlen = slen; reqlen = slen;
...@@ -505,7 +519,7 @@ unsigned int ziplistCompare(unsigned char *p, unsigned char *sstr, unsigned int ...@@ -505,7 +519,7 @@ unsigned int ziplistCompare(unsigned char *p, unsigned char *sstr, unsigned int
} }
} else { } else {
/* Try to compare encoded values */ /* Try to compare encoded values */
if (zipTryEncoding(sstr,&sval,&sencoding)) { if (zipTryEncoding(sstr,slen,&sval,&sencoding)) {
if (entry.encoding == sencoding) { if (entry.encoding == sencoding) {
zval = zipLoadInteger(p+entry.headersize,entry.encoding); zval = zipLoadInteger(p+entry.headersize,entry.encoding);
return zval == sval; return zval == sval;
......
...@@ -89,6 +89,20 @@ void *zmalloc(size_t size) { ...@@ -89,6 +89,20 @@ void *zmalloc(size_t size) {
#endif #endif
} }
void *zcalloc(size_t size) {
void *ptr = calloc(1, size+PREFIX_SIZE);
if (!ptr) zmalloc_oom(size);
#ifdef HAVE_MALLOC_SIZE
increment_used_memory(redis_malloc_size(ptr));
return ptr;
#else
*((size_t*)ptr) = size;
increment_used_memory(size+PREFIX_SIZE);
return (char*)ptr+PREFIX_SIZE;
#endif
}
void *zrealloc(void *ptr, size_t size) { void *zrealloc(void *ptr, size_t size) {
#ifndef HAVE_MALLOC_SIZE #ifndef HAVE_MALLOC_SIZE
void *realptr; void *realptr;
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#define _ZMALLOC_H #define _ZMALLOC_H
void *zmalloc(size_t size); void *zmalloc(size_t size);
void *zcalloc(size_t size);
void *zrealloc(void *ptr, size_t size); void *zrealloc(void *ptr, size_t size);
void zfree(void *ptr); void zfree(void *ptr);
char *zstrdup(const char *s); char *zstrdup(const char *s);
......
start_server {tags {"repl"}} {
start_server {} {
test {First server should have role slave after SLAVEOF} {
r -1 slaveof [srv 0 host] [srv 0 port]
after 1000
s -1 role
} {slave}
test {MASTER and SLAVE dataset should be identical after complex ops} {
createComplexDataset r 10000
after 500
if {[r debug digest] ne [r -1 debug digest]} {
set csv1 [csvdump r]
set csv2 [csvdump {r -1}]
set fd [open /tmp/repldump1.txt w]
puts -nonewline $fd $csv1
close $fd
set fd [open /tmp/repldump2.txt w]
puts -nonewline $fd $csv2
close $fd
puts "Master - Slave inconsistency"
puts "Run diff -u against /tmp/repldump*.txt for more info"
}
assert_equal [r debug digest] [r -1 debug digest]
}
test {MASTER and SLAVE consistency with expire} {
createComplexDataset r 50000 useexpire
after 4000 ;# Make sure everything expired before taking the digest
if {[r debug digest] ne [r -1 debug digest]} {
set csv1 [csvdump r]
set csv2 [csvdump {r -1}]
set fd [open /tmp/repldump1.txt w]
puts -nonewline $fd $csv1
close $fd
set fd [open /tmp/repldump2.txt w]
puts -nonewline $fd $csv2
close $fd
puts "Master - Slave inconsistency"
puts "Run diff -u against /tmp/repldump*.txt for more info"
}
assert_equal [r debug digest] [r -1 debug digest]
}
}
}
start_server {tags {"repl"}} { start_server {tags {"repl"}} {
r set mykey foo r set mykey foo
......
...@@ -33,9 +33,14 @@ proc assert_error {pattern code} { ...@@ -33,9 +33,14 @@ proc assert_error {pattern code} {
} }
proc assert_encoding {enc key} { proc assert_encoding {enc key} {
# swapped out value doesn't have encoding, so swap in first # Swapped out values don't have an encoding, so make sure that
r debug swapin $key # the value is swapped in before checking the encoding.
assert_match "* encoding:$enc *" [r debug object $key] set dbg [r debug object $key]
while {[string match "* swapped at:*" $dbg]} {
r debug swapin $key
set dbg [r debug object $key]
}
assert_match "* encoding:$enc *" $dbg
} }
proc assert_type {type key} { proc assert_type {type key} {
......
...@@ -44,7 +44,7 @@ proc warnings_from_file {filename} { ...@@ -44,7 +44,7 @@ proc warnings_from_file {filename} {
# Return value for INFO property # Return value for INFO property
proc status {r property} { proc status {r property} {
if {[regexp "\r\n$property:(.*?)\r\n" [$r info] _ value]} { if {[regexp "\r\n$property:(.*?)\r\n" [{*}$r info] _ value]} {
set _ $value set _ $value
} }
} }
...@@ -127,11 +127,32 @@ proc randomKey {} { ...@@ -127,11 +127,32 @@ proc randomKey {} {
} }
} }
proc createComplexDataset {r ops} { proc findKeyWithType {r type} {
for {set j 0} {$j < 20} {incr j} {
set k [{*}$r randomkey]
if {$k eq {}} {
return {}
}
if {[{*}$r type $k] eq $type} {
return $k
}
}
return {}
}
proc createComplexDataset {r ops {opt {}}} {
for {set j 0} {$j < $ops} {incr j} { for {set j 0} {$j < $ops} {incr j} {
set k [randomKey] set k [randomKey]
set k2 [randomKey]
set f [randomValue] set f [randomValue]
set v [randomValue] set v [randomValue]
if {[lsearch -exact $opt useexpire] != -1} {
if {rand() < 0.1} {
{*}$r expire [randomKey] [randomInt 2]
}
}
randpath { randpath {
set d [expr {rand()}] set d [expr {rand()}]
} { } {
...@@ -145,21 +166,23 @@ proc createComplexDataset {r ops} { ...@@ -145,21 +166,23 @@ proc createComplexDataset {r ops} {
} { } {
randpath {set d +inf} {set d -inf} randpath {set d +inf} {set d -inf}
} }
set t [$r type $k] set t [{*}$r type $k]
if {$t eq {none}} { if {$t eq {none}} {
randpath { randpath {
$r set $k $v {*}$r set $k $v
} { } {
$r lpush $k $v {*}$r lpush $k $v
} { } {
$r sadd $k $v {*}$r sadd $k $v
} { } {
$r zadd $k $d $v {*}$r zadd $k $d $v
} { } {
$r hset $k $f $v {*}$r hset $k $f $v
} {
{*}$r del $k
} }
set t [$r type $k] set t [{*}$r type $k]
} }
switch $t { switch $t {
...@@ -167,23 +190,45 @@ proc createComplexDataset {r ops} { ...@@ -167,23 +190,45 @@ proc createComplexDataset {r ops} {
# Nothing to do # Nothing to do
} }
{list} { {list} {
randpath {$r lpush $k $v} \ randpath {{*}$r lpush $k $v} \
{$r rpush $k $v} \ {{*}$r rpush $k $v} \
{$r lrem $k 0 $v} \ {{*}$r lrem $k 0 $v} \
{$r rpop $k} \ {{*}$r rpop $k} \
{$r lpop $k} {{*}$r lpop $k}
} }
{set} { {set} {
randpath {$r sadd $k $v} \ randpath {{*}$r sadd $k $v} \
{$r srem $k $v} {{*}$r srem $k $v} \
{
set otherset [findKeyWithType r set]
if {$otherset ne {}} {
randpath {
{*}$r sunionstore $k2 $k $otherset
} {
{*}$r sinterstore $k2 $k $otherset
} {
{*}$r sdiffstore $k2 $k $otherset
}
}
}
} }
{zset} { {zset} {
randpath {$r zadd $k $d $v} \ randpath {{*}$r zadd $k $d $v} \
{$r zrem $k $v} {{*}$r zrem $k $v} \
{
set otherzset [findKeyWithType r zset]
if {$otherzset ne {}} {
randpath {
{*}$r zunionstore $k2 2 $k $otherzset
} {
{*}$r zinterstore $k2 2 $k $otherzset
}
}
}
} }
{hash} { {hash} {
randpath {$r hset $k $f $v} \ randpath {{*}$r hset $k $f $v} \
{$r hdel $k $f} {{*}$r hdel $k $f}
} }
} }
} }
...@@ -196,3 +241,52 @@ proc formatCommand {args} { ...@@ -196,3 +241,52 @@ proc formatCommand {args} {
} }
set _ $cmd set _ $cmd
} }
proc csvdump r {
set o {}
foreach k [lsort [{*}$r keys *]] {
set type [{*}$r type $k]
append o [csvstring $k] , [csvstring $type] ,
switch $type {
string {
append o [csvstring [{*}$r get $k]] "\n"
}
list {
foreach e [{*}$r lrange $k 0 -1] {
append o [csvstring $e] ,
}
append o "\n"
}
set {
foreach e [lsort [{*}$r smembers $k]] {
append o [csvstring $e] ,
}
append o "\n"
}
zset {
foreach e [{*}$r zrange $k 0 -1 withscores] {
append o [csvstring $e] ,
}
append o "\n"
}
hash {
set fields [{*}$r hgetall $k]
set newfields {}
foreach {k v} $fields {
lappend newfields [list $k $v]
}
set fields [lsort -index 0 $newfields]
foreach kv $fields {
append o [csvstring [lindex $kv 0]] ,
append o [csvstring [lindex $kv 1]] ,
}
append o "\n"
}
}
}
return $o
}
proc csvstring s {
return "\"$s\""
}
...@@ -102,13 +102,13 @@ proc main {} { ...@@ -102,13 +102,13 @@ proc main {} {
execute_tests "unit/expire" execute_tests "unit/expire"
execute_tests "unit/other" execute_tests "unit/other"
execute_tests "unit/cas" execute_tests "unit/cas"
cleanup
puts "\n[expr $::passed+$::failed] tests, $::passed passed, $::failed failed" puts "\n[expr $::passed+$::failed] tests, $::passed passed, $::failed failed"
if {$::failed > 0} { if {$::failed > 0} {
puts "\n*** WARNING!!! $::failed FAILED TESTS ***\n" puts "\n*** WARNING!!! $::failed FAILED TESTS ***\n"
exit 1
} }
cleanup
} }
# parse arguments # parse arguments
......
...@@ -148,12 +148,11 @@ start_server {tags {"basic"}} { ...@@ -148,12 +148,11 @@ start_server {tags {"basic"}} {
r get novar2 r get novar2
} {foobared} } {foobared}
test {SETNX will overwrite EXPIREing key} { test {SETNX against volatile key} {
r set x 10 r set x 10
r expire x 10000 r expire x 10000
r setnx x 20 list [r setnx x 20] [r get x]
r get x } {0 10}
} {20}
test {EXISTS} { test {EXISTS} {
set res {} set res {}
...@@ -362,10 +361,17 @@ start_server {tags {"basic"}} { ...@@ -362,10 +361,17 @@ start_server {tags {"basic"}} {
list [r msetnx x1 xxx y2 yyy] [r get x1] [r get y2] list [r msetnx x1 xxx y2 yyy] [r get x1] [r get y2]
} {1 xxx yyy} } {1 xxx yyy}
test {MSETNX should remove all the volatile keys even on failure} { test {STRLEN against non existing key} {
r mset x 1 y 2 z 3 r strlen notakey
r expire y 10000 } {0}
r expire z 10000
list [r msetnx x A y B z C] [r mget x y z] test {STRLEN against integer} {
} {0 {1 {} {}}} r set myinteger -555
r strlen myinteger
} {4}
test {STRLEN against plain string} {
r set mystring "foozzz0123456789 baz"
r strlen mystring
}
} }
...@@ -111,4 +111,25 @@ start_server {tags {"cas"}} { ...@@ -111,4 +111,25 @@ start_server {tags {"cas"}} {
r ping r ping
r exec r exec
} {PONG} } {PONG}
test {WATCH will consider touched keys target of EXPIRE} {
r del x
r set x foo
r watch x
r expire x 10
r multi
r ping
r exec
} {}
test {WATCH will not consider touched expired keys} {
r del x
r set x foo
r expire x 2
r watch x
after 3000
r multi
r ping
r exec
} {PONG}
} }
start_server {tags {"expire"}} { start_server {tags {"expire"}} {
test {EXPIRE - don't set timeouts multiple times} { test {EXPIRE - set timeouts multiple times} {
r set x foobar r set x foobar
set v1 [r expire x 5] set v1 [r expire x 5]
set v2 [r ttl x] set v2 [r ttl x]
set v3 [r expire x 10] set v3 [r expire x 10]
set v4 [r ttl x] set v4 [r ttl x]
r expire x 4
list $v1 $v2 $v3 $v4 list $v1 $v2 $v3 $v4
} {1 5 0 5} } {1 5 1 10}
test {EXPIRE - It should be still possible to read 'x'} { test {EXPIRE - It should be still possible to read 'x'} {
r get x r get x
...@@ -19,13 +20,13 @@ start_server {tags {"expire"}} { ...@@ -19,13 +20,13 @@ start_server {tags {"expire"}} {
} {{} 0} } {{} 0}
} }
test {EXPIRE - Delete on write policy} { test {EXPIRE - write on expire should work} {
r del x r del x
r lpush x foo r lpush x foo
r expire x 1000 r expire x 1000
r lpush x bar r lpush x bar
r lrange x 0 -1 r lrange x 0 -1
} {bar} } {bar foo}
test {EXPIREAT - Check for EXPIRE alike behavior} { test {EXPIREAT - Check for EXPIRE alike behavior} {
r del x r del x
...@@ -59,4 +60,15 @@ start_server {tags {"expire"}} { ...@@ -59,4 +60,15 @@ start_server {tags {"expire"}} {
catch {r setex z -10 foo} e catch {r setex z -10 foo} e
set _ $e set _ $e
} {*invalid expire*} } {*invalid expire*}
test {PERSIST can undo an EXPIRE} {
r set x foo
r expire x 50
list [r ttl x] [r persist x] [r ttl x] [r get x]
} {50 1 -1 foo}
test {PERSIST returns 0 against non existing or non volatile keys} {
r set x foo
list [r persist foo] [r persist nokeyatall]
} {0 0}
} }
...@@ -46,23 +46,56 @@ start_server {} { ...@@ -46,23 +46,56 @@ start_server {} {
set _ $err set _ $err
} {*invalid*} } {*invalid*}
if {![catch {package require sha1}]} { tags {consistency} {
test {Check consistency of different data types after a reload} { if {![catch {package require sha1}]} {
r flushdb test {Check consistency of different data types after a reload} {
createComplexDataset r 10000 r flushdb
set sha1 [r debug digest] createComplexDataset r 10000
r debug reload set dump [csvdump r]
set sha1_after [r debug digest] set sha1 [r debug digest]
expr {$sha1 eq $sha1_after} r debug reload
} {1} set sha1_after [r debug digest]
if {$sha1 eq $sha1_after} {
test {Same dataset digest if saving/reloading as AOF?} { set _ 1
r bgrewriteaof } else {
waitForBgrewriteaof r set newdump [csvdump r]
r debug loadaof puts "Consistency test failed!"
set sha1_after [r debug digest] puts "You can inspect the two dumps in /tmp/repldump*.txt"
expr {$sha1 eq $sha1_after}
} {1} set fd [open /tmp/repldump1.txt w]
puts $fd $dump
close $fd
set fd [open /tmp/repldump2.txt w]
puts $fd $newdump
close $fd
set _ 0
}
} {1}
test {Same dataset digest if saving/reloading as AOF?} {
r bgrewriteaof
waitForBgrewriteaof r
r debug loadaof
set sha1_after [r debug digest]
if {$sha1 eq $sha1_after} {
set _ 1
} else {
set newdump [csvdump r]
puts "Consistency test failed!"
puts "You can inspect the two dumps in /tmp/aofdump*.txt"
set fd [open /tmp/aofdump1.txt w]
puts $fd $dump
close $fd
set fd [open /tmp/aofdump2.txt w]
puts $fd $newdump
close $fd
set _ 0
}
} {1}
}
} }
test {EXPIRES after a reload (snapshot + append only file)} { test {EXPIRES after a reload (snapshot + append only file)} {
......
...@@ -5,6 +5,12 @@ start_server { ...@@ -5,6 +5,12 @@ start_server {
"list-max-ziplist-entries" 256 "list-max-ziplist-entries" 256
} }
} { } {
# We need a value larger than list-max-ziplist-value to make sure
# the list has the right encoding when it is swapped in again.
array set largevalue {}
set largevalue(ziplist) "hello"
set largevalue(linkedlist) [string repeat "hello" 4]
test {LPUSH, RPUSH, LLENGTH, LINDEX - ziplist} { test {LPUSH, RPUSH, LLENGTH, LINDEX - ziplist} {
# first lpush then rpush # first lpush then rpush
assert_equal 1 [r lpush myziplist1 a] assert_equal 1 [r lpush myziplist1 a]
...@@ -28,28 +34,25 @@ start_server { ...@@ -28,28 +34,25 @@ start_server {
} }
test {LPUSH, RPUSH, LLENGTH, LINDEX - regular list} { test {LPUSH, RPUSH, LLENGTH, LINDEX - regular list} {
# use a string of length 17 to ensure a regular list is used
set large_value "aaaaaaaaaaaaaaaaa"
# first lpush then rpush # first lpush then rpush
assert_equal 1 [r lpush mylist1 $large_value] assert_equal 1 [r lpush mylist1 $largevalue(linkedlist)]
assert_encoding linkedlist mylist1 assert_encoding linkedlist mylist1
assert_equal 2 [r rpush mylist1 b] assert_equal 2 [r rpush mylist1 b]
assert_equal 3 [r rpush mylist1 c] assert_equal 3 [r rpush mylist1 c]
assert_equal 3 [r llen mylist1] assert_equal 3 [r llen mylist1]
assert_equal $large_value [r lindex mylist1 0] assert_equal $largevalue(linkedlist) [r lindex mylist1 0]
assert_equal b [r lindex mylist1 1] assert_equal b [r lindex mylist1 1]
assert_equal c [r lindex mylist1 2] assert_equal c [r lindex mylist1 2]
# first rpush then lpush # first rpush then lpush
assert_equal 1 [r rpush mylist2 $large_value] assert_equal 1 [r rpush mylist2 $largevalue(linkedlist)]
assert_encoding linkedlist mylist2 assert_encoding linkedlist mylist2
assert_equal 2 [r lpush mylist2 b] assert_equal 2 [r lpush mylist2 b]
assert_equal 3 [r lpush mylist2 c] assert_equal 3 [r lpush mylist2 c]
assert_equal 3 [r llen mylist2] assert_equal 3 [r llen mylist2]
assert_equal c [r lindex mylist2 0] assert_equal c [r lindex mylist2 0]
assert_equal b [r lindex mylist2 1] assert_equal b [r lindex mylist2 1]
assert_equal $large_value [r lindex mylist2 2] assert_equal $largevalue(linkedlist) [r lindex mylist2 2]
} }
test {DEL a list - ziplist} { test {DEL a list - ziplist} {
...@@ -72,16 +75,14 @@ start_server { ...@@ -72,16 +75,14 @@ start_server {
proc create_linkedlist {key entries} { proc create_linkedlist {key entries} {
r del $key r del $key
r rpush $key "aaaaaaaaaaaaaaaaa"
foreach entry $entries { r rpush $key $entry } foreach entry $entries { r rpush $key $entry }
assert_equal "aaaaaaaaaaaaaaaaa" [r lpop $key]
assert_encoding linkedlist $key assert_encoding linkedlist $key
} }
foreach type {ziplist linkedlist} { foreach {type large} [array get largevalue] {
test "BLPOP, BRPOP: single existing list - $type" { test "BLPOP, BRPOP: single existing list - $type" {
set rd [redis_deferring_client] set rd [redis_deferring_client]
create_$type blist {a b c d} create_$type blist "a b $large c d"
$rd blpop blist 1 $rd blpop blist 1
assert_equal {blist a} [$rd read] assert_equal {blist a} [$rd read]
...@@ -96,8 +97,8 @@ start_server { ...@@ -96,8 +97,8 @@ start_server {
test "BLPOP, BRPOP: multiple existing lists - $type" { test "BLPOP, BRPOP: multiple existing lists - $type" {
set rd [redis_deferring_client] set rd [redis_deferring_client]
create_$type blist1 {a b c} create_$type blist1 "a $large c"
create_$type blist2 {d e f} create_$type blist2 "d $large f"
$rd blpop blist1 blist2 1 $rd blpop blist1 blist2 1
assert_equal {blist1 a} [$rd read] assert_equal {blist1 a} [$rd read]
...@@ -117,7 +118,7 @@ start_server { ...@@ -117,7 +118,7 @@ start_server {
test "BLPOP, BRPOP: second list has an entry - $type" { test "BLPOP, BRPOP: second list has an entry - $type" {
set rd [redis_deferring_client] set rd [redis_deferring_client]
r del blist1 r del blist1
create_$type blist2 {d e f} create_$type blist2 "d $large f"
$rd blpop blist1 blist2 1 $rd blpop blist1 blist2 1
assert_equal {blist2 d} [$rd read] assert_equal {blist2 d} [$rd read]
...@@ -179,26 +180,26 @@ start_server { ...@@ -179,26 +180,26 @@ start_server {
assert_equal 0 [r llen xlist] assert_equal 0 [r llen xlist]
} }
foreach type {ziplist linkedlist} { foreach {type large} [array get largevalue] {
test "LPUSHX, RPUSHX - $type" { test "LPUSHX, RPUSHX - $type" {
create_$type xlist {b c} create_$type xlist "$large c"
assert_equal 3 [r rpushx xlist d] assert_equal 3 [r rpushx xlist d]
assert_equal 4 [r lpushx xlist a] assert_equal 4 [r lpushx xlist a]
assert_equal {a b c d} [r lrange xlist 0 -1] assert_equal "a $large c d" [r lrange xlist 0 -1]
} }
test "LINSERT - $type" { test "LINSERT - $type" {
create_$type xlist {a b c d} create_$type xlist "a $large c d"
assert_equal 5 [r linsert xlist before c zz] assert_equal 5 [r linsert xlist before c zz]
assert_equal {a b zz c d} [r lrange xlist 0 10] assert_equal "a $large zz c d" [r lrange xlist 0 10]
assert_equal 6 [r linsert xlist after c yy] assert_equal 6 [r linsert xlist after c yy]
assert_equal {a b zz c yy d} [r lrange xlist 0 10] assert_equal "a $large zz c yy d" [r lrange xlist 0 10]
assert_equal 7 [r linsert xlist after d dd] assert_equal 7 [r linsert xlist after d dd]
assert_equal -1 [r linsert xlist after bad ddd] assert_equal -1 [r linsert xlist after bad ddd]
assert_equal {a b zz c yy d dd} [r lrange xlist 0 10] assert_equal "a $large zz c yy d dd" [r lrange xlist 0 10]
assert_equal 8 [r linsert xlist before a aa] assert_equal 8 [r linsert xlist before a aa]
assert_equal -1 [r linsert xlist before bad aaa] assert_equal -1 [r linsert xlist before bad aaa]
assert_equal {aa a b zz c yy d dd} [r lrange xlist 0 10] assert_equal "aa a $large zz c yy d dd" [r lrange xlist 0 10]
# check inserting integer encoded value # check inserting integer encoded value
assert_equal 9 [r linsert xlist before aa 42] assert_equal 9 [r linsert xlist before aa 42]
...@@ -207,14 +208,14 @@ start_server { ...@@ -207,14 +208,14 @@ start_server {
} }
test {LPUSHX, RPUSHX convert from ziplist to list} { test {LPUSHX, RPUSHX convert from ziplist to list} {
set large_value "aaaaaaaaaaaaaaaaa" set large $largevalue(linkedlist)
# convert when a large value is pushed # convert when a large value is pushed
create_ziplist xlist a create_ziplist xlist a
assert_equal 2 [r rpushx xlist $large_value] assert_equal 2 [r rpushx xlist $large]
assert_encoding linkedlist xlist assert_encoding linkedlist xlist
create_ziplist xlist a create_ziplist xlist a
assert_equal 2 [r lpushx xlist $large_value] assert_equal 2 [r lpushx xlist $large]
assert_encoding linkedlist xlist assert_encoding linkedlist xlist
# convert when the length threshold is exceeded # convert when the length threshold is exceeded
...@@ -227,14 +228,14 @@ start_server { ...@@ -227,14 +228,14 @@ start_server {
} }
test {LINSERT convert from ziplist to list} { test {LINSERT convert from ziplist to list} {
set large_value "aaaaaaaaaaaaaaaaa" set large $largevalue(linkedlist)
# convert when a large value is inserted # convert when a large value is inserted
create_ziplist xlist a create_ziplist xlist a
assert_equal 2 [r linsert xlist before a $large_value] assert_equal 2 [r linsert xlist before a $large]
assert_encoding linkedlist xlist assert_encoding linkedlist xlist
create_ziplist xlist a create_ziplist xlist a
assert_equal 2 [r linsert xlist after a $large_value] assert_equal 2 [r linsert xlist after a $large]
assert_encoding linkedlist xlist assert_encoding linkedlist xlist
# convert when the length threshold is exceeded # convert when the length threshold is exceeded
...@@ -320,32 +321,38 @@ start_server { ...@@ -320,32 +321,38 @@ start_server {
assert_error ERR* {r rpush mylist 0} assert_error ERR* {r rpush mylist 0}
} }
foreach type {ziplist linkedlist} { foreach {type large} [array get largevalue] {
test "RPOPLPUSH base case - $type" { test "RPOPLPUSH base case - $type" {
r del mylist1 mylist2 r del mylist1 mylist2
create_$type mylist1 {a b c d} create_$type mylist1 "a $large c d"
assert_equal d [r rpoplpush mylist1 mylist2] assert_equal d [r rpoplpush mylist1 mylist2]
assert_equal c [r rpoplpush mylist1 mylist2] assert_equal c [r rpoplpush mylist1 mylist2]
assert_equal {a b} [r lrange mylist1 0 -1] assert_equal "a $large" [r lrange mylist1 0 -1]
assert_equal {c d} [r lrange mylist2 0 -1] assert_equal "c d" [r lrange mylist2 0 -1]
assert_encoding ziplist mylist2 assert_encoding ziplist mylist2
} }
test "RPOPLPUSH with the same list as src and dst - $type" { test "RPOPLPUSH with the same list as src and dst - $type" {
create_$type mylist {a b c} create_$type mylist "a $large c"
assert_equal {a b c} [r lrange mylist 0 -1] assert_equal "a $large c" [r lrange mylist 0 -1]
assert_equal c [r rpoplpush mylist mylist] assert_equal c [r rpoplpush mylist mylist]
assert_equal {c a b} [r lrange mylist 0 -1] assert_equal "c a $large" [r lrange mylist 0 -1]
} }
foreach othertype {ziplist linkedlist} { foreach {othertype otherlarge} [array get largevalue] {
test "RPOPLPUSH with $type source and existing target $othertype" { test "RPOPLPUSH with $type source and existing target $othertype" {
create_$type srclist {a b c d} create_$type srclist "a b c $large"
create_$othertype dstlist {x} create_$othertype dstlist "$otherlarge"
assert_equal d [r rpoplpush srclist dstlist] assert_equal $large [r rpoplpush srclist dstlist]
assert_equal c [r rpoplpush srclist dstlist] assert_equal c [r rpoplpush srclist dstlist]
assert_equal {a b} [r lrange srclist 0 -1] assert_equal "a b" [r lrange srclist 0 -1]
assert_equal {c d x} [r lrange dstlist 0 -1] assert_equal "c $large $otherlarge" [r lrange dstlist 0 -1]
# When we rpoplpush'ed a large value, dstlist should be
# converted to the same encoding as srclist.
if {$type eq "linkedlist"} {
assert_encoding linkedlist dstlist
}
} }
} }
} }
...@@ -378,10 +385,10 @@ start_server { ...@@ -378,10 +385,10 @@ start_server {
assert_equal {} [r rpoplpush srclist dstlist] assert_equal {} [r rpoplpush srclist dstlist]
} {} } {}
foreach type {ziplist linkedlist} { foreach {type large} [array get largevalue] {
test "Basic LPOP/RPOP - $type" { test "Basic LPOP/RPOP - $type" {
create_$type mylist {0 1 2} create_$type mylist "$large 1 2"
assert_equal 0 [r lpop mylist] assert_equal $large [r lpop mylist]
assert_equal 2 [r rpop mylist] assert_equal 2 [r rpop mylist]
assert_equal 1 [r lpop mylist] assert_equal 1 [r lpop mylist]
assert_equal 0 [r llen mylist] assert_equal 0 [r llen mylist]
...@@ -416,28 +423,28 @@ start_server { ...@@ -416,28 +423,28 @@ start_server {
} }
} }
foreach type {ziplist linkedlist} { foreach {type large} [array get largevalue] {
test "LRANGE basics - $type" { test "LRANGE basics - $type" {
create_$type mylist {0 1 2 3 4 5 6 7 8 9} create_$type mylist "$large 1 2 3 4 5 6 7 8 9"
assert_equal {1 2 3 4 5 6 7 8} [r lrange mylist 1 -2] assert_equal {1 2 3 4 5 6 7 8} [r lrange mylist 1 -2]
assert_equal {7 8 9} [r lrange mylist -3 -1] assert_equal {7 8 9} [r lrange mylist -3 -1]
assert_equal {4} [r lrange mylist 4 4] assert_equal {4} [r lrange mylist 4 4]
} }
test "LRANGE inverted indexes - $type" { test "LRANGE inverted indexes - $type" {
create_$type mylist {0 1 2 3 4 5 6 7 8 9} create_$type mylist "$large 1 2 3 4 5 6 7 8 9"
assert_equal {} [r lrange mylist 6 2] assert_equal {} [r lrange mylist 6 2]
} }
test "LRANGE out of range indexes including the full list - $type" { test "LRANGE out of range indexes including the full list - $type" {
create_$type mylist {1 2 3} create_$type mylist "$large 1 2 3"
assert_equal {1 2 3} [r lrange mylist -1000 1000] assert_equal "$large 1 2 3" [r lrange mylist -1000 1000]
} }
test "LRANGE out of range negative end index - $type" { test "LRANGE out of range negative end index - $type" {
create_$type mylist {1 2 3} create_$type mylist "$large 1 2 3"
assert_equal {1} [r lrange mylist 0 -3] assert_equal $large [r lrange mylist 0 -4]
assert_equal {} [r lrange mylist 0 -4] assert_equal {} [r lrange mylist 0 -5]
} }
} }
...@@ -445,27 +452,28 @@ start_server { ...@@ -445,27 +452,28 @@ start_server {
assert_equal {} [r lrange nosuchkey 0 1] assert_equal {} [r lrange nosuchkey 0 1]
} }
foreach type {ziplist linkedlist} { foreach {type large} [array get largevalue] {
proc trim_list {type min max} { proc trim_list {type min max} {
upvar 1 large large
r del mylist r del mylist
create_$type mylist {1 2 3 4 5} create_$type mylist "1 2 3 4 $large"
r ltrim mylist $min $max r ltrim mylist $min $max
r lrange mylist 0 -1 r lrange mylist 0 -1
} }
test "LTRIM basics - $type" { test "LTRIM basics - $type" {
assert_equal {1} [trim_list $type 0 0] assert_equal "1" [trim_list $type 0 0]
assert_equal {1 2} [trim_list $type 0 1] assert_equal "1 2" [trim_list $type 0 1]
assert_equal {1 2 3} [trim_list $type 0 2] assert_equal "1 2 3" [trim_list $type 0 2]
assert_equal {2 3} [trim_list $type 1 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 $large" [trim_list $type 1 -1]
assert_equal {2 3 4} [trim_list $type 1 -2] assert_equal "2 3 4" [trim_list $type 1 -2]
assert_equal {4 5} [trim_list $type -2 -1] assert_equal "4 $large" [trim_list $type -2 -1]
assert_equal {5} [trim_list $type -1 -1] assert_equal "$large" [trim_list $type -1 -1]
assert_equal {1 2 3 4 5} [trim_list $type -5 -1] assert_equal "1 2 3 4 $large" [trim_list $type -5 -1]
assert_equal {1 2 3 4 5} [trim_list $type -10 10] assert_equal "1 2 3 4 $large" [trim_list $type -10 10]
assert_equal {1 2 3 4 5} [trim_list $type 0 5] assert_equal "1 2 3 4 $large" [trim_list $type 0 5]
assert_equal {1 2 3 4 5} [trim_list $type 0 10] assert_equal "1 2 3 4 $large" [trim_list $type 0 10]
} }
test "LTRIM out of range negative end index - $type" { test "LTRIM out of range negative end index - $type" {
...@@ -478,20 +486,19 @@ start_server { ...@@ -478,20 +486,19 @@ start_server {
set mylist {} set mylist {}
set startlen 32 set startlen 32
r del mylist r del mylist
# Start with the large value to ensure the
# right encoding is used.
r rpush mylist $large
lappend mylist $large
for {set i 0} {$i < $startlen} {incr i} { for {set i 0} {$i < $startlen} {incr i} {
set str [randomInt 9223372036854775807] set str [randomInt 9223372036854775807]
r rpush mylist $str r rpush mylist $str
lappend mylist $str lappend mylist $str
} }
# do a push/pop of a large value to convert to a real list for {set i 0} {$i < 1000} {incr i} {
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 min [expr {int(rand()*$startlen)}]
set max [expr {$min+int(rand()*$startlen)}] set max [expr {$min+int(rand()*$startlen)}]
set mylist [lrange $mylist $min $max] set mylist [lrange $mylist $min $max]
...@@ -508,12 +515,12 @@ start_server { ...@@ -508,12 +515,12 @@ start_server {
} }
} }
foreach type {ziplist linkedlist} { foreach {type large} [array get largevalue] {
test "LSET - $type" { test "LSET - $type" {
create_$type mylist {99 98 97 96 95} create_$type mylist "99 98 $large 96 95"
r lset mylist 1 foo r lset mylist 1 foo
r lset mylist -1 bar r lset mylist -1 bar
assert_equal {99 foo 97 96 bar} [r lrange mylist 0 -1] assert_equal "99 foo $large 96 bar" [r lrange mylist 0 -1]
} }
test "LSET out of range index - $type" { test "LSET out of range index - $type" {
...@@ -530,38 +537,38 @@ start_server { ...@@ -530,38 +537,38 @@ start_server {
assert_error ERR*value* {r lset nolist 0 foo} assert_error ERR*value* {r lset nolist 0 foo}
} }
foreach type {ziplist linkedlist} { foreach {type e} [array get largevalue] {
test "LREM remove all the occurrences - $type" { test "LREM remove all the occurrences - $type" {
create_$type mylist {foo bar foobar foobared zap bar test foo} create_$type mylist "$e foo bar foobar foobared zap bar test foo"
assert_equal 2 [r lrem mylist 0 bar] assert_equal 2 [r lrem mylist 0 bar]
assert_equal {foo foobar foobared zap test foo} [r lrange mylist 0 -1] assert_equal "$e foo foobar foobared zap test foo" [r lrange mylist 0 -1]
} }
test "LREM remove the first occurrence - $type" { test "LREM remove the first occurrence - $type" {
assert_equal 1 [r lrem mylist 1 foo] assert_equal 1 [r lrem mylist 1 foo]
assert_equal {foobar foobared zap test foo} [r lrange mylist 0 -1] assert_equal "$e foobar foobared zap test foo" [r lrange mylist 0 -1]
} }
test "LREM remove non existing element - $type" { test "LREM remove non existing element - $type" {
assert_equal 0 [r lrem mylist 1 nosuchelement] assert_equal 0 [r lrem mylist 1 nosuchelement]
assert_equal {foobar foobared zap test foo} [r lrange mylist 0 -1] assert_equal "$e foobar foobared zap test foo" [r lrange mylist 0 -1]
} }
test "LREM starting from tail with negative count - $type" { test "LREM starting from tail with negative count - $type" {
create_$type mylist {foo bar foobar foobared zap bar test foo foo} create_$type mylist "$e foo bar foobar foobared zap bar test foo foo"
assert_equal 1 [r lrem mylist -1 bar] assert_equal 1 [r lrem mylist -1 bar]
assert_equal {foo bar foobar foobared zap test foo foo} [r lrange mylist 0 -1] assert_equal "$e foo bar foobar foobared zap test foo foo" [r lrange mylist 0 -1]
} }
test "LREM starting from tail with negative count (2) - $type" { test "LREM starting from tail with negative count (2) - $type" {
assert_equal 2 [r lrem mylist -2 foo] assert_equal 2 [r lrem mylist -2 foo]
assert_equal {foo bar foobar foobared zap test} [r lrange mylist 0 -1] assert_equal "$e foo bar foobar foobared zap test" [r lrange mylist 0 -1]
} }
test "LREM deleting objects that may be int encoded - $type" { test "LREM deleting objects that may be int encoded - $type" {
create_$type myotherlist {1 2 3} create_$type myotherlist "$e 1 2 3"
assert_equal 1 [r lrem myotherlist 1 2] assert_equal 1 [r lrem myotherlist 1 2]
assert_equal 2 [r llen myotherlist] assert_equal 3 [r llen myotherlist]
} }
} }
} }
...@@ -433,6 +433,42 @@ start_server {tags {"zset"}} { ...@@ -433,6 +433,42 @@ start_server {tags {"zset"}} {
list [r zinterstore zsetc 2 zseta zsetb aggregate max] [r zrange zsetc 0 -1 withscores] list [r zinterstore zsetc 2 zseta zsetb aggregate max] [r zrange zsetc 0 -1 withscores]
} {2 {b 2 c 3}} } {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"} { tags {"slow"} {
test {ZSETs skiplist implementation backlink consistency test} { test {ZSETs skiplist implementation backlink consistency test} {
set diff 0 set diff 0
...@@ -477,22 +513,16 @@ start_server {tags {"zset"}} { ...@@ -477,22 +513,16 @@ start_server {tags {"zset"}} {
} {} } {}
} }
test {ZSET element can't be set to nan with ZADD} { test {ZSET element can't be set to NaN with ZADD} {
set e {} assert_error "*not a double*" {r zadd myzset nan abc}
catch {r zadd myzset nan abc} e }
set _ $e
} {*Not A Number*}
test {ZSET element can't be set to nan with ZINCRBY} { test {ZSET element can't be set to NaN with ZINCRBY} {
set e {} assert_error "*not a double*" {r zadd myzset nan abc}
catch {r zincrby myzset nan abc} e }
set _ $e
} {*Not A Number*}
test {ZINCRBY calls leading to Nan are refused} { test {ZINCRBY calls leading to NaN result in error} {
set e {}
r zincrby myzset +inf abc r zincrby myzset +inf abc
catch {r zincrby myzset -inf abc} e assert_error "*NaN*" {r zincrby myzset -inf abc}
set _ $e }
} {*Not A Number*}
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment