Commit 994ed2bc authored by antirez's avatar antirez
Browse files

unstable merge conflicts resolved

parents 45ec3243 d5b36c51
...@@ -366,12 +366,12 @@ void sortCommand(redisClient *c) { ...@@ -366,12 +366,12 @@ void sortCommand(redisClient *c) {
} }
} }
} }
dbReplace(c->db,storekey,sobj); setKey(c->db,storekey,sobj);
decrRefCount(sobj);
/* Note: we add 1 because the DB is dirty anyway since even if the /* Note: we add 1 because the DB is dirty anyway since even if the
* SORT result is empty a new key is set and maybe the old content * SORT result is empty a new key is set and maybe the old content
* replaced. */ * replaced. */
server.dirty += 1+outputlen; server.dirty += 1+outputlen;
signalModifiedKey(c->db,storekey);
addReplyLongLong(c,outputlen); addReplyLongLong(c,outputlen);
} }
......
...@@ -640,7 +640,9 @@ void lremCommand(redisClient *c) { ...@@ -640,7 +640,9 @@ void lremCommand(redisClient *c) {
* as well. This command was originally proposed by Ezra Zygmuntowicz. * as well. This command was originally proposed by Ezra Zygmuntowicz.
*/ */
void rpoplpushHandlePush(redisClient *c, robj *dstkey, robj *dstobj, robj *value) { void rpoplpushHandlePush(redisClient *origclient, redisClient *c, robj *dstkey, robj *dstobj, robj *value) {
robj *aux;
if (!handleClientsWaitingListPush(c,dstkey,value)) { if (!handleClientsWaitingListPush(c,dstkey,value)) {
/* Create the list if the key does not exist */ /* Create the list if the key does not exist */
if (!dstobj) { if (!dstobj) {
...@@ -648,9 +650,25 @@ void rpoplpushHandlePush(redisClient *c, robj *dstkey, robj *dstobj, robj *value ...@@ -648,9 +650,25 @@ void rpoplpushHandlePush(redisClient *c, robj *dstkey, robj *dstobj, robj *value
dbAdd(c->db,dstkey,dstobj); dbAdd(c->db,dstkey,dstobj);
} else { } else {
signalModifiedKey(c->db,dstkey); signalModifiedKey(c->db,dstkey);
server.dirty++;
} }
listTypePush(dstobj,value,REDIS_HEAD); listTypePush(dstobj,value,REDIS_HEAD);
/* If we are pushing as a result of LPUSH against a key
* watched by BLPOPLPUSH, we need to rewrite the command vector.
* But if this is called directly by RPOPLPUSH (either directly
* or via a BRPOPLPUSH where the popped list exists)
* we should replicate the BRPOPLPUSH command itself. */
if (c != origclient) {
aux = createStringObject("LPUSH",5);
rewriteClientCommandVector(origclient,3,aux,dstkey,value);
decrRefCount(aux);
} else {
/* Make sure to always use RPOPLPUSH in the replication / AOF,
* even if the original command was BRPOPLPUSH. */
aux = createStringObject("RPOPLPUSH",9);
rewriteClientCommandVector(origclient,3,aux,c->argv[1],c->argv[2]);
decrRefCount(aux);
}
server.dirty++;
} }
/* Always send the pushed value to the client. */ /* Always send the pushed value to the client. */
...@@ -666,16 +684,22 @@ void rpoplpushCommand(redisClient *c) { ...@@ -666,16 +684,22 @@ void rpoplpushCommand(redisClient *c) {
addReply(c,shared.nullbulk); addReply(c,shared.nullbulk);
} else { } else {
robj *dobj = lookupKeyWrite(c->db,c->argv[2]); robj *dobj = lookupKeyWrite(c->db,c->argv[2]);
robj *touchedkey = c->argv[1];
if (dobj && checkType(c,dobj,REDIS_LIST)) return; if (dobj && checkType(c,dobj,REDIS_LIST)) return;
value = listTypePop(sobj,REDIS_TAIL); value = listTypePop(sobj,REDIS_TAIL);
rpoplpushHandlePush(c,c->argv[2],dobj,value); /* We saved touched key, and protect it, since rpoplpushHandlePush
* may change the client command argument vector. */
incrRefCount(touchedkey);
rpoplpushHandlePush(c,c,c->argv[2],dobj,value);
/* listTypePop returns an object with its refcount incremented */ /* listTypePop returns an object with its refcount incremented */
decrRefCount(value); decrRefCount(value);
/* Delete the source list when it is empty */ /* Delete the source list when it is empty */
if (listTypeLength(sobj) == 0) dbDelete(c->db,c->argv[1]); if (listTypeLength(sobj) == 0) dbDelete(c->db,touchedkey);
signalModifiedKey(c->db,c->argv[1]); signalModifiedKey(c->db,touchedkey);
decrRefCount(touchedkey);
server.dirty++; server.dirty++;
} }
} }
...@@ -777,6 +801,7 @@ void unblockClientWaitingData(redisClient *c) { ...@@ -777,6 +801,7 @@ void unblockClientWaitingData(redisClient *c) {
/* Cleanup the client structure */ /* Cleanup the client structure */
zfree(c->bpop.keys); zfree(c->bpop.keys);
c->bpop.keys = NULL; c->bpop.keys = NULL;
if (c->bpop.target) decrRefCount(c->bpop.target);
c->bpop.target = NULL; c->bpop.target = NULL;
c->flags &= ~REDIS_BLOCKED; c->flags &= ~REDIS_BLOCKED;
c->flags |= REDIS_UNBLOCKED; c->flags |= REDIS_UNBLOCKED;
...@@ -820,6 +845,10 @@ int handleClientsWaitingListPush(redisClient *c, robj *key, robj *ele) { ...@@ -820,6 +845,10 @@ int handleClientsWaitingListPush(redisClient *c, robj *key, robj *ele) {
receiver = ln->value; receiver = ln->value;
dstkey = receiver->bpop.target; dstkey = receiver->bpop.target;
/* Protect receiver->bpop.target, that will be freed by
* the next unblockClientWaitingData() call. */
if (dstkey) incrRefCount(dstkey);
/* This should remove the first element of the "clients" list. */ /* This should remove the first element of the "clients" list. */
unblockClientWaitingData(receiver); unblockClientWaitingData(receiver);
...@@ -828,17 +857,16 @@ int handleClientsWaitingListPush(redisClient *c, robj *key, robj *ele) { ...@@ -828,17 +857,16 @@ int handleClientsWaitingListPush(redisClient *c, robj *key, robj *ele) {
addReplyMultiBulkLen(receiver,2); addReplyMultiBulkLen(receiver,2);
addReplyBulk(receiver,key); addReplyBulk(receiver,key);
addReplyBulk(receiver,ele); addReplyBulk(receiver,ele);
return 1; return 1; /* Serve just the first client as in B[RL]POP semantics */
} else { } else {
/* BRPOPLPUSH, note that receiver->db is always equal to c->db. */ /* BRPOPLPUSH, note that receiver->db is always equal to c->db. */
dstobj = lookupKeyWrite(receiver->db,dstkey); dstobj = lookupKeyWrite(receiver->db,dstkey);
if (dstobj && checkType(receiver,dstobj,REDIS_LIST)) { if (!(dstobj && checkType(receiver,dstobj,REDIS_LIST))) {
decrRefCount(dstkey); rpoplpushHandlePush(c,receiver,dstkey,dstobj,ele);
} else {
rpoplpushHandlePush(receiver,dstkey,dstobj,ele);
decrRefCount(dstkey); decrRefCount(dstkey);
return 1; return 1;
} }
decrRefCount(dstkey);
} }
} }
......
...@@ -332,7 +332,7 @@ void scardCommand(redisClient *c) { ...@@ -332,7 +332,7 @@ void scardCommand(redisClient *c) {
} }
void spopCommand(redisClient *c) { void spopCommand(redisClient *c) {
robj *set, *ele; robj *set, *ele, *aux;
int64_t llele; int64_t llele;
int encoding; int encoding;
...@@ -348,16 +348,11 @@ void spopCommand(redisClient *c) { ...@@ -348,16 +348,11 @@ void spopCommand(redisClient *c) {
setTypeRemove(set,ele); setTypeRemove(set,ele);
} }
/* Change argv to replicate as SREM */ /* Replicate/AOF this command as an SREM operation */
c->argc = 3; aux = createStringObject("SREM",4);
c->argv = zrealloc(c->argv,sizeof(robj*)*(c->argc)); rewriteClientCommandVector(c,3,aux,c->argv[1],ele);
decrRefCount(ele);
/* Overwrite SREM with SPOP (same length) */ decrRefCount(aux);
redisAssert(sdslen(c->argv[0]->ptr) == 4);
memcpy(c->argv[0]->ptr, "SREM", 4);
/* Popped element already has incremented refcount */
c->argv[2] = ele;
addReplyBulk(c,ele); addReplyBulk(c,ele);
if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]); if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]);
......
...@@ -13,7 +13,6 @@ static int checkStringLength(redisClient *c, long long size) { ...@@ -13,7 +13,6 @@ static int checkStringLength(redisClient *c, long long size) {
} }
void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire) { void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire) {
int retval;
long seconds = 0; /* initialized to avoid an harmness warning */ long seconds = 0; /* initialized to avoid an harmness warning */
if (expire) { if (expire) {
...@@ -25,21 +24,12 @@ void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expir ...@@ -25,21 +24,12 @@ void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expir
} }
} }
retval = dbAdd(c->db,key,val); if (lookupKeyWrite(c->db,key) != NULL && nx) {
if (retval == REDIS_ERR) { addReply(c,shared.czero);
if (!nx) { return;
dbReplace(c->db,key,val);
incrRefCount(val);
} else {
addReply(c,shared.czero);
return;
}
} else {
incrRefCount(val);
} }
signalModifiedKey(c->db,key); setKey(c->db,key,val);
server.dirty++; server.dirty++;
removeExpire(c->db,key);
if (expire) setExpire(c->db,key,time(NULL)+seconds); if (expire) setExpire(c->db,key,time(NULL)+seconds);
addReply(c, nx ? shared.cone : shared.ok); addReply(c, nx ? shared.cone : shared.ok);
} }
...@@ -81,11 +71,8 @@ void getCommand(redisClient *c) { ...@@ -81,11 +71,8 @@ void getCommand(redisClient *c) {
void getsetCommand(redisClient *c) { void getsetCommand(redisClient *c) {
if (getGenericCommand(c) == REDIS_ERR) return; if (getGenericCommand(c) == REDIS_ERR) return;
c->argv[2] = tryObjectEncoding(c->argv[2]); c->argv[2] = tryObjectEncoding(c->argv[2]);
dbReplace(c->db,c->argv[1],c->argv[2]); setKey(c->db,c->argv[1],c->argv[2]);
incrRefCount(c->argv[2]);
signalModifiedKey(c->db,c->argv[1]);
server.dirty++; server.dirty++;
removeExpire(c->db,c->argv[1]);
} }
static int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) { static int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) {
...@@ -138,7 +125,7 @@ void setbitCommand(redisClient *c) { ...@@ -138,7 +125,7 @@ void setbitCommand(redisClient *c) {
robj *decoded = getDecodedObject(o); robj *decoded = getDecodedObject(o);
o = createStringObject(decoded->ptr, sdslen(decoded->ptr)); o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
decrRefCount(decoded); decrRefCount(decoded);
dbReplace(c->db,c->argv[1],o); dbOverwrite(c->db,c->argv[1],o);
} }
} }
...@@ -236,7 +223,7 @@ void setrangeCommand(redisClient *c) { ...@@ -236,7 +223,7 @@ void setrangeCommand(redisClient *c) {
robj *decoded = getDecodedObject(o); robj *decoded = getDecodedObject(o);
o = createStringObject(decoded->ptr, sdslen(decoded->ptr)); o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
decrRefCount(decoded); decrRefCount(decoded);
dbReplace(c->db,c->argv[1],o); dbOverwrite(c->db,c->argv[1],o);
} }
} }
...@@ -319,18 +306,15 @@ void msetGenericCommand(redisClient *c, int nx) { ...@@ -319,18 +306,15 @@ void msetGenericCommand(redisClient *c, int nx) {
busykeys++; busykeys++;
} }
} }
} if (busykeys) {
if (busykeys) { addReply(c, shared.czero);
addReply(c, shared.czero); return;
return; }
} }
for (j = 1; j < c->argc; j += 2) { for (j = 1; j < c->argc; j += 2) {
c->argv[j+1] = tryObjectEncoding(c->argv[j+1]); c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
dbReplace(c->db,c->argv[j],c->argv[j+1]); setKey(c->db,c->argv[j],c->argv[j+1]);
incrRefCount(c->argv[j+1]);
removeExpire(c->db,c->argv[j]);
signalModifiedKey(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);
...@@ -346,7 +330,7 @@ void msetnxCommand(redisClient *c) { ...@@ -346,7 +330,7 @@ void msetnxCommand(redisClient *c) {
void incrDecrCommand(redisClient *c, long long incr) { void incrDecrCommand(redisClient *c, long long incr) {
long long value, oldvalue; long long value, oldvalue;
robj *o; robj *o, *new;
o = lookupKeyWrite(c->db,c->argv[1]); o = lookupKeyWrite(c->db,c->argv[1]);
if (o != NULL && checkType(c,o,REDIS_STRING)) return; if (o != NULL && checkType(c,o,REDIS_STRING)) return;
...@@ -358,12 +342,15 @@ void incrDecrCommand(redisClient *c, long long incr) { ...@@ -358,12 +342,15 @@ void incrDecrCommand(redisClient *c, long long incr) {
addReplyError(c,"increment or decrement would overflow"); addReplyError(c,"increment or decrement would overflow");
return; return;
} }
o = createStringObjectFromLongLong(value); new = createStringObjectFromLongLong(value);
dbReplace(c->db,c->argv[1],o); if (o)
dbOverwrite(c->db,c->argv[1],new);
else
dbAdd(c->db,c->argv[1],new);
signalModifiedKey(c->db,c->argv[1]); signalModifiedKey(c->db,c->argv[1]);
server.dirty++; server.dirty++;
addReply(c,shared.colon); addReply(c,shared.colon);
addReply(c,o); addReply(c,new);
addReply(c,shared.crlf); addReply(c,shared.crlf);
} }
...@@ -416,7 +403,7 @@ void appendCommand(redisClient *c) { ...@@ -416,7 +403,7 @@ void appendCommand(redisClient *c) {
robj *decoded = getDecodedObject(o); robj *decoded = getDecodedObject(o);
o = createStringObject(decoded->ptr, sdslen(decoded->ptr)); o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
decrRefCount(decoded); decrRefCount(decoded);
dbReplace(c->db,c->argv[1],o); dbOverwrite(c->db,c->argv[1],o);
} }
/* Append the value */ /* Append the value */
......
...@@ -51,6 +51,11 @@ ...@@ -51,6 +51,11 @@
#define calloc(count,size) tc_calloc(count,size) #define calloc(count,size) tc_calloc(count,size)
#define realloc(ptr,size) tc_realloc(ptr,size) #define realloc(ptr,size) tc_realloc(ptr,size)
#define free(ptr) tc_free(ptr) #define free(ptr) tc_free(ptr)
#elif defined(USE_JEMALLOC)
#define malloc(size) je_malloc(size)
#define calloc(count,size) je_calloc(count,size)
#define realloc(ptr,size) je_realloc(ptr,size)
#define free(ptr) je_free(ptr)
#endif #endif
#define update_zmalloc_stat_alloc(__n,__size) do { \ #define update_zmalloc_stat_alloc(__n,__size) do { \
...@@ -98,7 +103,7 @@ void *zmalloc(size_t size) { ...@@ -98,7 +103,7 @@ void *zmalloc(size_t size) {
if (!ptr) zmalloc_oom(size); if (!ptr) zmalloc_oom(size);
#ifdef HAVE_MALLOC_SIZE #ifdef HAVE_MALLOC_SIZE
update_zmalloc_stat_alloc(redis_malloc_size(ptr),size); update_zmalloc_stat_alloc(zmalloc_size(ptr),size);
return ptr; return ptr;
#else #else
*((size_t*)ptr) = size; *((size_t*)ptr) = size;
...@@ -112,7 +117,7 @@ void *zcalloc(size_t size) { ...@@ -112,7 +117,7 @@ void *zcalloc(size_t size) {
if (!ptr) zmalloc_oom(size); if (!ptr) zmalloc_oom(size);
#ifdef HAVE_MALLOC_SIZE #ifdef HAVE_MALLOC_SIZE
update_zmalloc_stat_alloc(redis_malloc_size(ptr),size); update_zmalloc_stat_alloc(zmalloc_size(ptr),size);
return ptr; return ptr;
#else #else
*((size_t*)ptr) = size; *((size_t*)ptr) = size;
...@@ -130,12 +135,12 @@ void *zrealloc(void *ptr, size_t size) { ...@@ -130,12 +135,12 @@ void *zrealloc(void *ptr, size_t size) {
if (ptr == NULL) return zmalloc(size); if (ptr == NULL) return zmalloc(size);
#ifdef HAVE_MALLOC_SIZE #ifdef HAVE_MALLOC_SIZE
oldsize = redis_malloc_size(ptr); oldsize = zmalloc_size(ptr);
newptr = realloc(ptr,size); newptr = realloc(ptr,size);
if (!newptr) zmalloc_oom(size); if (!newptr) zmalloc_oom(size);
update_zmalloc_stat_free(oldsize); update_zmalloc_stat_free(oldsize);
update_zmalloc_stat_alloc(redis_malloc_size(newptr),size); update_zmalloc_stat_alloc(zmalloc_size(newptr),size);
return newptr; return newptr;
#else #else
realptr = (char*)ptr-PREFIX_SIZE; realptr = (char*)ptr-PREFIX_SIZE;
...@@ -158,7 +163,7 @@ void zfree(void *ptr) { ...@@ -158,7 +163,7 @@ void zfree(void *ptr) {
if (ptr == NULL) return; if (ptr == NULL) return;
#ifdef HAVE_MALLOC_SIZE #ifdef HAVE_MALLOC_SIZE
update_zmalloc_stat_free(redis_malloc_size(ptr)); update_zmalloc_stat_free(zmalloc_size(ptr));
free(ptr); free(ptr);
#else #else
realptr = (char*)ptr-PREFIX_SIZE; realptr = (char*)ptr-PREFIX_SIZE;
......
...@@ -28,8 +28,43 @@ ...@@ -28,8 +28,43 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef _ZMALLOC_H #ifndef __ZMALLOC_H
#define _ZMALLOC_H #define __ZMALLOC_H
/* Double expansion needed for stringification of macro values. */
#define __xstr(s) __str(s)
#define __str(s) #s
#if defined(USE_TCMALLOC)
#define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR))
#include <google/tcmalloc.h>
#if TC_VERSION_MAJOR >= 1 && TC_VERSION_MINOR >= 6
#define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) tc_malloc_size(p)
#else
#error "Newer version of tcmalloc required"
#endif
#elif defined(USE_JEMALLOC)
#define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX))
#define JEMALLOC_MANGLE
#include <jemalloc/jemalloc.h>
#if JEMALLOC_VERSION_MAJOR >= 2 && JEMALLOC_VERSION_MINOR >= 1
#define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) JEMALLOC_P(malloc_usable_size)(p)
#else
#error "Newer version of jemalloc required"
#endif
#elif defined(__APPLE__)
#include <malloc/malloc.h>
#define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) malloc_size(p)
#endif
#ifndef ZMALLOC_LIB
#define ZMALLOC_LIB "libc"
#endif
void *zmalloc(size_t size); void *zmalloc(size_t size);
void *zcalloc(size_t size); void *zcalloc(size_t size);
...@@ -44,4 +79,4 @@ size_t zmalloc_allocations_for_size(size_t size); ...@@ -44,4 +79,4 @@ size_t zmalloc_allocations_for_size(size_t size);
#define ZMALLOC_MAX_ALLOC_STAT 256 #define ZMALLOC_MAX_ALLOC_STAT 256
#endif /* _ZMALLOC_H */ #endif /* __ZMALLOC_H */
...@@ -291,32 +291,6 @@ appendfsync everysec ...@@ -291,32 +291,6 @@ appendfsync everysec
# "no" that is the safest pick from the point of view of durability. # "no" that is the safest pick from the point of view of durability.
no-appendfsync-on-rewrite no no-appendfsync-on-rewrite no
#################################### DISK STORE ###############################
# When disk store is active Redis works as an on-disk database, where memory
# is only used as a object cache.
#
# This mode is good for datasets that are bigger than memory, and in general
# when you want to trade speed for:
#
# - less memory used
# - immediate server restart
# - per key durability, without need for backgrond savig
#
# On the other hand, with disk store enabled MULTI/EXEC are no longer
# transactional from the point of view of the persistence on disk, that is,
# Redis transactions will still guarantee that commands are either processed
# all or nothing, but there is no guarantee that all the keys are flushed
# on disk in an atomic way.
#
# Of course with disk store enabled Redis is not as fast as it is when
# working with just the memory back end.
diskstore-enabled no
diskstore-path redis.ds
cache-max-memory 0
cache-flush-delay 0
############################### ADVANCED CONFIG ############################### ############################### ADVANCED CONFIG ###############################
# Hashes are encoded in a special way (much more memory efficient) when they # Hashes are encoded in a special way (much more memory efficient) when they
......
...@@ -6,6 +6,24 @@ start_server {tags {"repl"}} { ...@@ -6,6 +6,24 @@ start_server {tags {"repl"}} {
s -1 role s -1 role
} {slave} } {slave}
test {BRPOPLPUSH replication, when blocking against empty list} {
set rd [redis_deferring_client]
$rd brpoplpush a b 5
r lpush a foo
after 1000
assert_equal [r debug digest] [r -1 debug digest]
}
test {BRPOPLPUSH replication, list exists} {
set rd [redis_deferring_client]
r lpush c 1
r lpush c 2
r lpush c 3
$rd brpoplpush c d 5
after 1000
assert_equal [r debug digest] [r -1 debug digest]
}
test {MASTER and SLAVE dataset should be identical after complex ops} { test {MASTER and SLAVE dataset should be identical after complex ops} {
createComplexDataset r 10000 createComplexDataset r 10000
after 500 after 500
......
...@@ -138,21 +138,48 @@ start_server {tags {"basic"}} { ...@@ -138,21 +138,48 @@ start_server {tags {"basic"}} {
r decrby novar 17179869185 r decrby novar 17179869185
} {-1} } {-1}
test {SETNX target key missing} { test "SETNX target key missing" {
r setnx novar2 foobared r del novar
r get novar2 assert_equal 1 [r setnx novar foobared]
} {foobared} assert_equal "foobared" [r get novar]
}
test {SETNX target key exists} { test "SETNX target key exists" {
r setnx novar2 blabla r set novar foobared
r get novar2 assert_equal 0 [r setnx novar blabla]
} {foobared} assert_equal "foobared" [r get novar]
}
test {SETNX against volatile key} { test "SETNX against not-expired volatile key" {
r set x 10 r set x 10
r expire x 10000 r expire x 10000
list [r setnx x 20] [r get x] assert_equal 0 [r setnx x 20]
} {0 10} assert_equal 10 [r get x]
}
test "SETNX against expired volatile key" {
# Make it very unlikely for the key this test uses to be expired by the
# active expiry cycle. This is tightly coupled to the implementation of
# active expiry and dbAdd() but currently the only way to test that
# SETNX expires a key when it should have been.
for {set x 0} {$x < 9999} {incr x} {
r setex key-$x 3600 value
}
# This will be one of 10000 expiring keys. A cycle is executed every
# 100ms, sampling 10 keys for being expired or not. This key will be
# expired for at most 1s when we wait 2s, resulting in a total sample
# of 100 keys. The probability of the success of this test being a
# false positive is therefore approx. 1%.
r set x 10
r expire x 1
# Wait for the key to expire
after 2000
assert_equal 1 [r setnx x 20]
assert_equal 20 [r get x]
}
test {EXISTS} { test {EXISTS} {
set res {} set res {}
......
...@@ -278,6 +278,14 @@ start_server { ...@@ -278,6 +278,14 @@ start_server {
r exec r exec
} {foo bar {} {} {bar foo}} } {foo bar {} {} {bar foo}}
test {BRPOPLPUSH timeout} {
set rd [redis_deferring_client]
$rd brpoplpush foo_list bar_list 1
after 2000
$rd read
} {}
foreach {pop} {BLPOP BRPOP} { foreach {pop} {BLPOP BRPOP} {
test "$pop: with single empty list argument" { test "$pop: with single empty list argument" {
set rd [redis_deferring_client] set rd [redis_deferring_client]
......
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