Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
4409877e
Commit
4409877e
authored
Dec 29, 2009
by
antirez
Browse files
not yet working BLPOP implementation
parent
436b423e
Changes
4
Hide whitespace changes
Inline
Side-by-side
redis-cli.c
View file @
4409877e
...
@@ -71,6 +71,8 @@ static struct redisCommand cmdTable[] = {
...
@@ -71,6 +71,8 @@ static struct redisCommand cmdTable[] = {
{
"lpush"
,
3
,
REDIS_CMD_BULK
},
{
"lpush"
,
3
,
REDIS_CMD_BULK
},
{
"rpop"
,
2
,
REDIS_CMD_INLINE
},
{
"rpop"
,
2
,
REDIS_CMD_INLINE
},
{
"lpop"
,
2
,
REDIS_CMD_INLINE
},
{
"lpop"
,
2
,
REDIS_CMD_INLINE
},
{
"brpop"
,
3
,
REDIS_CMD_INLINE
},
{
"blpop"
,
3
,
REDIS_CMD_INLINE
},
{
"llen"
,
2
,
REDIS_CMD_INLINE
},
{
"llen"
,
2
,
REDIS_CMD_INLINE
},
{
"lindex"
,
3
,
REDIS_CMD_INLINE
},
{
"lindex"
,
3
,
REDIS_CMD_INLINE
},
{
"lset"
,
4
,
REDIS_CMD_BULK
},
{
"lset"
,
4
,
REDIS_CMD_BULK
},
...
...
redis.c
View file @
4409877e
...
@@ -158,6 +158,7 @@
...
@@ -158,6 +158,7 @@
#define REDIS_MASTER 4 /* This client is a master server */
#define REDIS_MASTER 4 /* This client is a master server */
#define REDIS_MONITOR 8 /* This client is a slave monitor, see MONITOR */
#define REDIS_MONITOR 8 /* This client is a slave monitor, see MONITOR */
#define REDIS_MULTI 16 /* This client is in a MULTI context */
#define REDIS_MULTI 16 /* This client is in a MULTI context */
#define REDIS_BLOCKED 32 /* The client is waiting in a blocking operation */
/* Slave replication state - slave side */
/* Slave replication state - slave side */
#define REDIS_REPL_NONE 0 /* No active replication */
#define REDIS_REPL_NONE 0 /* No active replication */
...
@@ -226,8 +227,9 @@ typedef struct redisObject {
...
@@ -226,8 +227,9 @@ typedef struct redisObject {
} while(0);
} while(0);
typedef struct redisDb {
typedef struct redisDb {
dict *dict;
dict *dict; /* The keyspace for this DB */
dict *expires;
dict *expires; /* Timeout of keys with a timeout set */
dict *blockingkeys; /* Keys with clients waiting for data (BLPOP) */
int id;
int id;
} redisDb;
} redisDb;
...
@@ -266,6 +268,10 @@ typedef struct redisClient {
...
@@ -266,6 +268,10 @@ typedef struct redisClient {
long repldboff; /* replication DB file offset */
long repldboff; /* replication DB file offset */
off_t repldbsize; /* replication DB file size */
off_t repldbsize; /* replication DB file size */
multiState mstate; /* MULTI/EXEC state */
multiState mstate; /* MULTI/EXEC state */
robj *blockingkey; /* The key we waiting to terminate a blocking
* operation such as BLPOP. Otherwise NULL. */
time_t blockingto; /* Blocking operation timeout. If UNIX current time
* is >= blockingto then the operation timed out. */
} redisClient;
} redisClient;
struct saveparam {
struct saveparam {
...
@@ -278,7 +284,7 @@ struct redisServer {
...
@@ -278,7 +284,7 @@ struct redisServer {
int port;
int port;
int fd;
int fd;
redisDb *db;
redisDb *db;
dict *sharingpool;
dict *sharingpool;
/* Poll used for object sharing */
unsigned int sharingpoolsize;
unsigned int sharingpoolsize;
long long dirty; /* changes to DB from the last save */
long long dirty; /* changes to DB from the last save */
list *clients;
list *clients;
...
@@ -437,6 +443,8 @@ static void sendReplyToClientWritev(aeEventLoop *el, int fd, void *privdata, int
...
@@ -437,6 +443,8 @@ static void sendReplyToClientWritev(aeEventLoop *el, int fd, void *privdata, int
static void initClientMultiState(redisClient *c);
static void initClientMultiState(redisClient *c);
static void freeClientMultiState(redisClient *c);
static void freeClientMultiState(redisClient *c);
static void queueMultiCommand(redisClient *c, struct redisCommand *cmd);
static void queueMultiCommand(redisClient *c, struct redisCommand *cmd);
static void unblockClient(redisClient *c);
static int handleClientsWaitingListPush(redisClient *c, robj *key, robj *ele);
static void authCommand(redisClient *c);
static void authCommand(redisClient *c);
static void pingCommand(redisClient *c);
static void pingCommand(redisClient *c);
...
@@ -513,6 +521,8 @@ static void zscoreCommand(redisClient *c);
...
@@ -513,6 +521,8 @@ static void zscoreCommand(redisClient *c);
static void zremrangebyscoreCommand(redisClient *c);
static void zremrangebyscoreCommand(redisClient *c);
static void multiCommand(redisClient *c);
static void multiCommand(redisClient *c);
static void execCommand(redisClient *c);
static void execCommand(redisClient *c);
static void blpopCommand(redisClient *c);
static void brpopCommand(redisClient *c);
/*================================= Globals ================================= */
/*================================= Globals ================================= */
...
@@ -531,6 +541,8 @@ static struct redisCommand cmdTable[] = {
...
@@ -531,6 +541,8 @@ static struct redisCommand cmdTable[] = {
{"lpush",lpushCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
{"lpush",lpushCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
{"rpop",rpopCommand,2,REDIS_CMD_INLINE},
{"rpop",rpopCommand,2,REDIS_CMD_INLINE},
{"lpop",lpopCommand,2,REDIS_CMD_INLINE},
{"lpop",lpopCommand,2,REDIS_CMD_INLINE},
{"brpop",brpopCommand,3,REDIS_CMD_INLINE},
{"blpop",blpopCommand,3,REDIS_CMD_INLINE},
{"llen",llenCommand,2,REDIS_CMD_INLINE},
{"llen",llenCommand,2,REDIS_CMD_INLINE},
{"lindex",lindexCommand,3,REDIS_CMD_INLINE},
{"lindex",lindexCommand,3,REDIS_CMD_INLINE},
{"lset",lsetCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
{"lset",lsetCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM},
...
@@ -760,6 +772,12 @@ static void dictVanillaFree(void *privdata, void *val)
...
@@ -760,6 +772,12 @@ static void dictVanillaFree(void *privdata, void *val)
zfree(val);
zfree(val);
}
}
static void dictListDestructor(void *privdata, void *val)
{
DICT_NOTUSED(privdata);
listRelease((list*)val);
}
static int sdsDictKeyCompare(void *privdata, const void *key1,
static int sdsDictKeyCompare(void *privdata, const void *key1,
const void *key2)
const void *key2)
{
{
...
@@ -841,6 +859,17 @@ static dictType hashDictType = {
...
@@ -841,6 +859,17 @@ static dictType hashDictType = {
dictRedisObjectDestructor /* val destructor */
dictRedisObjectDestructor /* val destructor */
};
};
/* Keylist hash table type has unencoded redis objects as keys and
* lists as values. It's used for blocking operations (BLPOP) */
static dictType keylistDictType = {
dictObjHash, /* hash function */
NULL, /* key dup */
NULL, /* val dup */
dictObjKeyCompare, /* key compare */
dictRedisObjectDestructor, /* key destructor */
dictListDestructor /* val destructor */
};
/* ========================= Random utility functions ======================= */
/* ========================= Random utility functions ======================= */
/* Redis generally does not try to recover from out of memory conditions
/* Redis generally does not try to recover from out of memory conditions
...
@@ -1211,6 +1240,7 @@ static void initServer() {
...
@@ -1211,6 +1240,7 @@ static void initServer() {
for (j = 0; j < server.dbnum; j++) {
for (j = 0; j < server.dbnum; j++) {
server.db[j].dict = dictCreate(&hashDictType,NULL);
server.db[j].dict = dictCreate(&hashDictType,NULL);
server.db[j].expires = dictCreate(&setDictType,NULL);
server.db[j].expires = dictCreate(&setDictType,NULL);
server.db[j].blockingkeys = dictCreate(&keylistDictType,NULL);
server.db[j].id = j;
server.db[j].id = j;
}
}
server.cronloops = 0;
server.cronloops = 0;
...
@@ -1432,9 +1462,18 @@ static void freeClientArgv(redisClient *c) {
...
@@ -1432,9 +1462,18 @@ static void freeClientArgv(redisClient *c) {
static void freeClient(redisClient *c) {
static void freeClient(redisClient *c) {
listNode *ln;
listNode *ln;
/* Note that if the client we are freeing is blocked into a blocking
* call, we have to set querybuf to NULL *before* to call unblockClient()
* to avoid processInputBuffer() will get called. Also it is important
* to remove the file events after this, because this call adds
* the READABLE event. */
sdsfree(c->querybuf);
c->querybuf = NULL;
if (c->flags & REDIS_BLOCKED)
unblockClient(c);
aeDeleteFileEvent(server.el,c->fd,AE_READABLE);
aeDeleteFileEvent(server.el,c->fd,AE_READABLE);
aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);
sdsfree(c->querybuf);
listRelease(c->reply);
listRelease(c->reply);
freeClientArgv(c);
freeClientArgv(c);
close(c->fd);
close(c->fd);
...
@@ -1898,6 +1937,13 @@ static void replicationFeedSlaves(list *slaves, struct redisCommand *cmd, int di
...
@@ -1898,6 +1937,13 @@ static void replicationFeedSlaves(list *slaves, struct redisCommand *cmd, int di
static void processInputBuffer(redisClient *c) {
static void processInputBuffer(redisClient *c) {
again:
again:
/* Before to process the input buffer, make sure the client is not
* waitig for a blocking operation such as BLPOP. Note that the first
* iteration the client is never blocked, otherwise the processInputBuffer
* would not be called at all, but after the execution of the first commands
* in the input buffer the client may be blocked, and the "goto again"
* will try to reiterate. The following line will make it return asap. */
if (c->flags & REDIS_BLOCKED) return;
if (c->bulklen == -1) {
if (c->bulklen == -1) {
/* Read the first line of the query */
/* Read the first line of the query */
char *p = strchr(c->querybuf,'\n');
char *p = strchr(c->querybuf,'\n');
...
@@ -2034,6 +2080,7 @@ static redisClient *createClient(int fd) {
...
@@ -2034,6 +2080,7 @@ static redisClient *createClient(int fd) {
c->authenticated = 0;
c->authenticated = 0;
c->replstate = REDIS_REPL_NONE;
c->replstate = REDIS_REPL_NONE;
c->reply = listCreate();
c->reply = listCreate();
c->blockingkey = NULL;
listSetFreeMethod(c->reply,decrRefCount);
listSetFreeMethod(c->reply,decrRefCount);
listSetDupMethod(c->reply,dupClientReplyValue);
listSetDupMethod(c->reply,dupClientReplyValue);
if (aeCreateFileEvent(server.el, c->fd, AE_READABLE,
if (aeCreateFileEvent(server.el, c->fd, AE_READABLE,
...
@@ -3477,6 +3524,7 @@ static void pushGenericCommand(redisClient *c, int where) {
...
@@ -3477,6 +3524,7 @@ static void pushGenericCommand(redisClient *c, int where) {
lobj = lookupKeyWrite(c->db,c->argv[1]);
lobj = lookupKeyWrite(c->db,c->argv[1]);
if (lobj == NULL) {
if (lobj == NULL) {
if (handleClientsWaitingListPush(c,c->argv[1],c->argv[2])) return;
lobj = createListObject();
lobj = createListObject();
list = lobj->ptr;
list = lobj->ptr;
if (where == REDIS_HEAD) {
if (where == REDIS_HEAD) {
...
@@ -3492,6 +3540,7 @@ static void pushGenericCommand(redisClient *c, int where) {
...
@@ -3492,6 +3540,7 @@ static void pushGenericCommand(redisClient *c, int where) {
addReply(c,shared.wrongtypeerr);
addReply(c,shared.wrongtypeerr);
return;
return;
}
}
if (handleClientsWaitingListPush(c,c->argv[1],c->argv[2])) return;
list = lobj->ptr;
list = lobj->ptr;
if (where == REDIS_HEAD) {
if (where == REDIS_HEAD) {
listAddNodeHead(list,c->argv[2]);
listAddNodeHead(list,c->argv[2]);
...
@@ -5393,6 +5442,160 @@ static void execCommand(redisClient *c) {
...
@@ -5393,6 +5442,160 @@ static void execCommand(redisClient *c) {
c->flags &= (~REDIS_MULTI);
c->flags &= (~REDIS_MULTI);
}
}
/* =========================== Blocking Operations ========================= */
/* Currently Redis blocking operations support is limited to list POP ops,
* so the current implementation is not fully generic, but it is also not
* completely specific so it will not require a rewrite to support new
* kind of blocking operations in the future.
*
* Still it's important to note that list blocking operations can be already
* used as a notification mechanism in order to implement other blocking
* operations at application level, so there must be a very strong evidence
* of usefulness and generality before new blocking operations are implemented.
*
* This is how the current blocking POP works, we use BLPOP as example:
* - If the user calls BLPOP and the key exists and contains a non empty list
* then LPOP is called instead. So BLPOP is semantically the same as LPOP
* if there is not to block.
* - If instead BLPOP is called and the key does not exists or the list is
* empty we need to block. In order to do so we remove the notification for
* new data to read in the client socket (so that we'll not serve new
* requests if the blocking request is not served). Also we put the client
* in a dictionary (server.blockingkeys) mapping keys to a list of clients
* blocking for this keys.
* - If a PUSH operation against a key with blocked clients waiting is
* performed, we serve the first in the list: basically instead to push
* the new element inside the list we return it to the (first / oldest)
* blocking client, unblock the client, and remove it form the list.
*
* The above comment and the source code should be enough in order to understand
* the implementation and modify / fix it later.
*/
/* Set a client in blocking mode for the specified key, with the specified
* timeout */
static void blockForKey(redisClient *c, robj *key, time_t timeout) {
dictEntry *de;
list *l;
c->blockingkey = key;
incrRefCount(key);
c->blockingto = timeout;
de = dictFind(c->db->blockingkeys,key);
if (de == NULL) {
int retval;
l = listCreate();
retval = dictAdd(c->db->blockingkeys,c,l);
assert(retval == DICT_OK);
} else {
l = dictGetEntryVal(de);
}
listAddNodeTail(l,c);
c->flags |= REDIS_BLOCKED;
aeDeleteFileEvent(server.el,c->fd,AE_READABLE);
}
/* Unblock a client that's waiting in a blocking operation such as BLPOP */
static void unblockClient(redisClient *c) {
dictEntry *de;
list *l;
/* Remove this client from the list of clients waiting for this key. */
assert(c->blockingkey != NULL);
de = dictFind(c->db->blockingkeys,c->blockingkey);
assert(de != NULL);
l = dictGetEntryVal(de);
listDelNode(l,listSearchKey(l,c));
/* If the list is empty we need to remove it to avoid wasting memory */
if (listLength(l) == 0)
dictDelete(c->db->blockingkeys,c->blockingkey);
/* Finally set the right flags in the client structure */
decrRefCount(c->blockingkey);
c->blockingkey = NULL;
c->flags &= (~REDIS_BLOCKED);
/* Ok now we are ready to get read events from socket, note that we
* can't trap errors here as it's possible that unblockClients() is
* called from freeClient() itself, and the only thing we can do
* if we failed to register the READABLE event is to kill the client.
* Still the following function should never fail in the real world as
* we are sure the file descriptor is sane, and we exit on out of mem. */
aeCreateFileEvent(server.el, c->fd, AE_READABLE, readQueryFromClient, c);
/* As a final step we want to process data if there is some command waiting
* in the input buffer. Note that this is safe even if unblockClient()
* gets called from freeClient() because freeClient() will be smart
* enough to call this function *after* c->querybuf was set to NULL. */
if (c->querybuf && sdslen(c->querybuf) > 0) processInputBuffer(c);
}
/* This should be called from any function PUSHing into lists.
* 'c' is the "pushing client", 'key' is the key it is pushing data against,
* 'ele' is the element pushed.
*
* If the function returns 0 there was no client waiting for a list push
* against this key.
*
* If the function returns 1 there was a client waiting for a list push
* against this key, the element was passed to this client thus it's not
* needed to actually add it to the list and the caller should return asap. */
static int handleClientsWaitingListPush(redisClient *c, robj *key, robj *ele) {
struct dictEntry *de;
redisClient *receiver;
list *l;
listNode *ln;
de = dictFind(c->db->blockingkeys,key);
if (de == NULL) return 0;
l = dictGetEntryVal(de);
ln = listFirst(l);
assert(ln != NULL);
receiver = ln->value;
listDelNode(l,ln);
if (listLength(l) == 0)
dictDelete(c->db->blockingkeys,key);
addReplyBulkLen(receiver,ele);
addReply(receiver,ele);
addReply(receiver,shared.crlf);
unblockClient(receiver);
return 1;
}
/* Blocking RPOP/LPOP */
static void blockingPopGenericCommand(redisClient *c, int where) {
robj *o;
time_t timeout;
o = lookupKeyWrite(c->db,c->argv[1]);
if (o != NULL) {
if (o->type != REDIS_LIST) {
popGenericCommand(c,where);
return;
} else {
list *list = o->ptr;
if (listLength(list) != 0) {
/* If the list contains elements fall back to the usual
* non-blocking POP operation */
popGenericCommand(c,where);
return;
}
}
}
/* If the list is empty or the key does not exists we must block */
timeout = strtol(c->argv[2]->ptr,NULL,10);
if (timeout > 0) timeout += time(NULL);
blockForKey(c,c->argv[1],timeout);
}
static void blpopCommand(redisClient *c) {
blockingPopGenericCommand(c,REDIS_HEAD);
}
static void brpopCommand(redisClient *c) {
blockingPopGenericCommand(c,REDIS_TAIL);
}
/* =============================== Replication ============================= */
/* =============================== Replication ============================= */
static int syncWrite(int fd, char *ptr, ssize_t size, int timeout) {
static int syncWrite(int fd, char *ptr, ssize_t size, int timeout) {
...
...
staticsymbols.h
View file @
4409877e
...
@@ -10,6 +10,11 @@ static struct redisFunctionSym symsTable[] = {
...
@@ -10,6 +10,11 @@ static struct redisFunctionSym symsTable[] = {
{
"authCommand"
,(
unsigned
long
)
authCommand
},
{
"authCommand"
,(
unsigned
long
)
authCommand
},
{
"bgrewriteaofCommand"
,(
unsigned
long
)
bgrewriteaofCommand
},
{
"bgrewriteaofCommand"
,(
unsigned
long
)
bgrewriteaofCommand
},
{
"bgsaveCommand"
,(
unsigned
long
)
bgsaveCommand
},
{
"bgsaveCommand"
,(
unsigned
long
)
bgsaveCommand
},
{
"blockForKey"
,(
unsigned
long
)
blockForKey
},
{
"blockingPopGenericCommand"
,(
unsigned
long
)
blockingPopGenericCommand
},
{
"blpopCommand"
,(
unsigned
long
)
blpopCommand
},
{
"brpopCommand"
,(
unsigned
long
)
brpopCommand
},
{
"call"
,(
unsigned
long
)
call
},
{
"closeTimedoutClients"
,(
unsigned
long
)
closeTimedoutClients
},
{
"closeTimedoutClients"
,(
unsigned
long
)
closeTimedoutClients
},
{
"compareStringObjects"
,(
unsigned
long
)
compareStringObjects
},
{
"compareStringObjects"
,(
unsigned
long
)
compareStringObjects
},
{
"createClient"
,(
unsigned
long
)
createClient
},
{
"createClient"
,(
unsigned
long
)
createClient
},
...
@@ -30,11 +35,13 @@ static struct redisFunctionSym symsTable[] = {
...
@@ -30,11 +35,13 @@ static struct redisFunctionSym symsTable[] = {
{
"deleteIfVolatile"
,(
unsigned
long
)
deleteIfVolatile
},
{
"deleteIfVolatile"
,(
unsigned
long
)
deleteIfVolatile
},
{
"deleteKey"
,(
unsigned
long
)
deleteKey
},
{
"deleteKey"
,(
unsigned
long
)
deleteKey
},
{
"dictEncObjKeyCompare"
,(
unsigned
long
)
dictEncObjKeyCompare
},
{
"dictEncObjKeyCompare"
,(
unsigned
long
)
dictEncObjKeyCompare
},
{
"dictListDestructor"
,(
unsigned
long
)
dictListDestructor
},
{
"dictObjKeyCompare"
,(
unsigned
long
)
dictObjKeyCompare
},
{
"dictObjKeyCompare"
,(
unsigned
long
)
dictObjKeyCompare
},
{
"dictRedisObjectDestructor"
,(
unsigned
long
)
dictRedisObjectDestructor
},
{
"dictRedisObjectDestructor"
,(
unsigned
long
)
dictRedisObjectDestructor
},
{
"dictVanillaFree"
,(
unsigned
long
)
dictVanillaFree
},
{
"dictVanillaFree"
,(
unsigned
long
)
dictVanillaFree
},
{
"dupClientReplyValue"
,(
unsigned
long
)
dupClientReplyValue
},
{
"dupClientReplyValue"
,(
unsigned
long
)
dupClientReplyValue
},
{
"echoCommand"
,(
unsigned
long
)
echoCommand
},
{
"echoCommand"
,(
unsigned
long
)
echoCommand
},
{
"execCommand"
,(
unsigned
long
)
execCommand
},
{
"existsCommand"
,(
unsigned
long
)
existsCommand
},
{
"existsCommand"
,(
unsigned
long
)
existsCommand
},
{
"expireCommand"
,(
unsigned
long
)
expireCommand
},
{
"expireCommand"
,(
unsigned
long
)
expireCommand
},
{
"expireGenericCommand"
,(
unsigned
long
)
expireGenericCommand
},
{
"expireGenericCommand"
,(
unsigned
long
)
expireGenericCommand
},
...
@@ -46,6 +53,7 @@ static struct redisFunctionSym symsTable[] = {
...
@@ -46,6 +53,7 @@ static struct redisFunctionSym symsTable[] = {
{
"flushdbCommand"
,(
unsigned
long
)
flushdbCommand
},
{
"flushdbCommand"
,(
unsigned
long
)
flushdbCommand
},
{
"freeClient"
,(
unsigned
long
)
freeClient
},
{
"freeClient"
,(
unsigned
long
)
freeClient
},
{
"freeClientArgv"
,(
unsigned
long
)
freeClientArgv
},
{
"freeClientArgv"
,(
unsigned
long
)
freeClientArgv
},
{
"freeClientMultiState"
,(
unsigned
long
)
freeClientMultiState
},
{
"freeFakeClient"
,(
unsigned
long
)
freeFakeClient
},
{
"freeFakeClient"
,(
unsigned
long
)
freeFakeClient
},
{
"freeHashObject"
,(
unsigned
long
)
freeHashObject
},
{
"freeHashObject"
,(
unsigned
long
)
freeHashObject
},
{
"freeListObject"
,(
unsigned
long
)
freeListObject
},
{
"freeListObject"
,(
unsigned
long
)
freeListObject
},
...
@@ -60,15 +68,18 @@ static struct redisFunctionSym symsTable[] = {
...
@@ -60,15 +68,18 @@ static struct redisFunctionSym symsTable[] = {
{
"getCommand"
,(
unsigned
long
)
getCommand
},
{
"getCommand"
,(
unsigned
long
)
getCommand
},
{
"getDecodedObject"
,(
unsigned
long
)
getDecodedObject
},
{
"getDecodedObject"
,(
unsigned
long
)
getDecodedObject
},
{
"getExpire"
,(
unsigned
long
)
getExpire
},
{
"getExpire"
,(
unsigned
long
)
getExpire
},
{
"getGenericCommand"
,(
unsigned
long
)
getGenericCommand
},
{
"getMcontextEip"
,(
unsigned
long
)
getMcontextEip
},
{
"getMcontextEip"
,(
unsigned
long
)
getMcontextEip
},
{
"getsetCommand"
,(
unsigned
long
)
getsetCommand
},
{
"getsetCommand"
,(
unsigned
long
)
getsetCommand
},
{
"glueReplyBuffersIfNeeded"
,(
unsigned
long
)
glueReplyBuffersIfNeeded
},
{
"glueReplyBuffersIfNeeded"
,(
unsigned
long
)
glueReplyBuffersIfNeeded
},
{
"handleClientsWaitingListPush"
,(
unsigned
long
)
handleClientsWaitingListPush
},
{
"htNeedsResize"
,(
unsigned
long
)
htNeedsResize
},
{
"htNeedsResize"
,(
unsigned
long
)
htNeedsResize
},
{
"incrCommand"
,(
unsigned
long
)
incrCommand
},
{
"incrCommand"
,(
unsigned
long
)
incrCommand
},
{
"incrDecrCommand"
,(
unsigned
long
)
incrDecrCommand
},
{
"incrDecrCommand"
,(
unsigned
long
)
incrDecrCommand
},
{
"incrRefCount"
,(
unsigned
long
)
incrRefCount
},
{
"incrRefCount"
,(
unsigned
long
)
incrRefCount
},
{
"incrbyCommand"
,(
unsigned
long
)
incrbyCommand
},
{
"incrbyCommand"
,(
unsigned
long
)
incrbyCommand
},
{
"infoCommand"
,(
unsigned
long
)
infoCommand
},
{
"infoCommand"
,(
unsigned
long
)
infoCommand
},
{
"initClientMultiState"
,(
unsigned
long
)
initClientMultiState
},
{
"initServer"
,(
unsigned
long
)
initServer
},
{
"initServer"
,(
unsigned
long
)
initServer
},
{
"initServerConfig"
,(
unsigned
long
)
initServerConfig
},
{
"initServerConfig"
,(
unsigned
long
)
initServerConfig
},
{
"isStringRepresentableAsLong"
,(
unsigned
long
)
isStringRepresentableAsLong
},
{
"isStringRepresentableAsLong"
,(
unsigned
long
)
isStringRepresentableAsLong
},
...
@@ -93,6 +104,7 @@ static struct redisFunctionSym symsTable[] = {
...
@@ -93,6 +104,7 @@ static struct redisFunctionSym symsTable[] = {
{
"msetCommand"
,(
unsigned
long
)
msetCommand
},
{
"msetCommand"
,(
unsigned
long
)
msetCommand
},
{
"msetGenericCommand"
,(
unsigned
long
)
msetGenericCommand
},
{
"msetGenericCommand"
,(
unsigned
long
)
msetGenericCommand
},
{
"msetnxCommand"
,(
unsigned
long
)
msetnxCommand
},
{
"msetnxCommand"
,(
unsigned
long
)
msetnxCommand
},
{
"multiCommand"
,(
unsigned
long
)
multiCommand
},
{
"oom"
,(
unsigned
long
)
oom
},
{
"oom"
,(
unsigned
long
)
oom
},
{
"pingCommand"
,(
unsigned
long
)
pingCommand
},
{
"pingCommand"
,(
unsigned
long
)
pingCommand
},
{
"popGenericCommand"
,(
unsigned
long
)
popGenericCommand
},
{
"popGenericCommand"
,(
unsigned
long
)
popGenericCommand
},
...
@@ -100,6 +112,7 @@ static struct redisFunctionSym symsTable[] = {
...
@@ -100,6 +112,7 @@ static struct redisFunctionSym symsTable[] = {
{
"processInputBuffer"
,(
unsigned
long
)
processInputBuffer
},
{
"processInputBuffer"
,(
unsigned
long
)
processInputBuffer
},
{
"pushGenericCommand"
,(
unsigned
long
)
pushGenericCommand
},
{
"pushGenericCommand"
,(
unsigned
long
)
pushGenericCommand
},
{
"qsortCompareSetsByCardinality"
,(
unsigned
long
)
qsortCompareSetsByCardinality
},
{
"qsortCompareSetsByCardinality"
,(
unsigned
long
)
qsortCompareSetsByCardinality
},
{
"queueMultiCommand"
,(
unsigned
long
)
queueMultiCommand
},
{
"randomkeyCommand"
,(
unsigned
long
)
randomkeyCommand
},
{
"randomkeyCommand"
,(
unsigned
long
)
randomkeyCommand
},
{
"rdbLoad"
,(
unsigned
long
)
rdbLoad
},
{
"rdbLoad"
,(
unsigned
long
)
rdbLoad
},
{
"rdbLoadDoubleValue"
,(
unsigned
long
)
rdbLoadDoubleValue
},
{
"rdbLoadDoubleValue"
,(
unsigned
long
)
rdbLoadDoubleValue
},
...
@@ -178,6 +191,7 @@ static struct redisFunctionSym symsTable[] = {
...
@@ -178,6 +191,7 @@ static struct redisFunctionSym symsTable[] = {
{
"tryResizeHashTables"
,(
unsigned
long
)
tryResizeHashTables
},
{
"tryResizeHashTables"
,(
unsigned
long
)
tryResizeHashTables
},
{
"ttlCommand"
,(
unsigned
long
)
ttlCommand
},
{
"ttlCommand"
,(
unsigned
long
)
ttlCommand
},
{
"typeCommand"
,(
unsigned
long
)
typeCommand
},
{
"typeCommand"
,(
unsigned
long
)
typeCommand
},
{
"unblockClient"
,(
unsigned
long
)
unblockClient
},
{
"updateSlavesWaitingBgsave"
,(
unsigned
long
)
updateSlavesWaitingBgsave
},
{
"updateSlavesWaitingBgsave"
,(
unsigned
long
)
updateSlavesWaitingBgsave
},
{
"yesnotoi"
,(
unsigned
long
)
yesnotoi
},
{
"yesnotoi"
,(
unsigned
long
)
yesnotoi
},
{
"zaddCommand"
,(
unsigned
long
)
zaddCommand
},
{
"zaddCommand"
,(
unsigned
long
)
zaddCommand
},
...
...
test-redis.tcl
View file @
4409877e
...
@@ -1562,6 +1562,18 @@ proc main {server port} {
...
@@ -1562,6 +1562,18 @@ proc main {server port} {
set _ 1
set _ 1
}
{
1
}
}
{
1
}
test
{
MUTLI / EXEC basics
}
{
$r del mylist
$r rpush mylist a
$r rpush mylist b
$r rpush mylist c
$r multi
set v1
[
$r
lrange mylist 0 -1
]
set v2
[
$r
ping
]
set v3
[
$r
exec
]
list $v1 $v2 $v3
}
{
QUEUED QUEUED
{{
a b c
}
PONG
}}
# Leave the user with a clean DB before to exit
# Leave the user with a clean DB before to exit
test
{
FLUSHDB
}
{
test
{
FLUSHDB
}
{
set aux
{}
set aux
{}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment