mirror of
https://github.com/fluencelabs/redis
synced 2025-03-30 22:31:03 +00:00
Merge remote branch 'pietern/networking-perf'
This commit is contained in:
commit
89f9f83769
14
src/aof.c
14
src/aof.c
@ -189,6 +189,7 @@ struct redisClient *createFakeClient(void) {
|
|||||||
c->querybuf = sdsempty();
|
c->querybuf = sdsempty();
|
||||||
c->argc = 0;
|
c->argc = 0;
|
||||||
c->argv = NULL;
|
c->argv = NULL;
|
||||||
|
c->bufpos = 0;
|
||||||
c->flags = 0;
|
c->flags = 0;
|
||||||
/* We set the fake client as a slave waiting for the synchronization
|
/* We set the fake client as a slave waiting for the synchronization
|
||||||
* so that Redis will not try to send replies to this client. */
|
* so that Redis will not try to send replies to this client. */
|
||||||
@ -272,12 +273,14 @@ int loadAppendOnlyFile(char *filename) {
|
|||||||
fakeClient->argc = argc;
|
fakeClient->argc = argc;
|
||||||
fakeClient->argv = argv;
|
fakeClient->argv = argv;
|
||||||
cmd->proc(fakeClient);
|
cmd->proc(fakeClient);
|
||||||
/* Discard the reply objects list from the fake client */
|
|
||||||
while(listLength(fakeClient->reply))
|
/* The fake client should not have a reply */
|
||||||
listDelNode(fakeClient->reply,listFirst(fakeClient->reply));
|
redisAssert(fakeClient->bufpos == 0 && listLength(fakeClient->reply) == 0);
|
||||||
|
|
||||||
/* Clean up, ready for the next command */
|
/* Clean up, ready for the next command */
|
||||||
for (j = 0; j < argc; j++) decrRefCount(argv[j]);
|
for (j = 0; j < argc; j++) decrRefCount(argv[j]);
|
||||||
zfree(argv);
|
zfree(argv);
|
||||||
|
|
||||||
/* Handle swapping while loading big datasets when VM is on */
|
/* Handle swapping while loading big datasets when VM is on */
|
||||||
force_swapout = 0;
|
force_swapout = 0;
|
||||||
if ((zmalloc_used_memory() - server.vm_max_memory) > 1024*1024*32)
|
if ((zmalloc_used_memory() - server.vm_max_memory) > 1024*1024*32)
|
||||||
@ -629,12 +632,11 @@ int rewriteAppendOnlyFileBackground(void) {
|
|||||||
|
|
||||||
void bgrewriteaofCommand(redisClient *c) {
|
void bgrewriteaofCommand(redisClient *c) {
|
||||||
if (server.bgrewritechildpid != -1) {
|
if (server.bgrewritechildpid != -1) {
|
||||||
addReplySds(c,sdsnew("-ERR background append only file rewriting already in progress\r\n"));
|
addReplyError(c,"Background append only file rewriting already in progress");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (rewriteAppendOnlyFileBackground() == REDIS_OK) {
|
if (rewriteAppendOnlyFileBackground() == REDIS_OK) {
|
||||||
char *status = "+Background append only file rewriting started\r\n";
|
addReplyStatus(c,"Background append only file rewriting started");
|
||||||
addReplySds(c,sdsnew(status));
|
|
||||||
} else {
|
} else {
|
||||||
addReply(c,shared.err);
|
addReply(c,shared.err);
|
||||||
}
|
}
|
||||||
|
30
src/config.c
30
src/config.c
@ -270,8 +270,8 @@ void configSetCommand(redisClient *c) {
|
|||||||
stopAppendOnly();
|
stopAppendOnly();
|
||||||
} else {
|
} else {
|
||||||
if (startAppendOnly() == REDIS_ERR) {
|
if (startAppendOnly() == REDIS_ERR) {
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),
|
addReplyError(c,
|
||||||
"-ERR Unable to turn on AOF. Check server logs.\r\n"));
|
"Unable to turn on AOF. Check server logs.");
|
||||||
decrRefCount(o);
|
decrRefCount(o);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -312,9 +312,8 @@ void configSetCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
sdsfreesplitres(v,vlen);
|
sdsfreesplitres(v,vlen);
|
||||||
} else {
|
} else {
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),
|
addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
|
||||||
"-ERR not supported CONFIG parameter %s\r\n",
|
(char*)c->argv[2]->ptr);
|
||||||
(char*)c->argv[2]->ptr));
|
|
||||||
decrRefCount(o);
|
decrRefCount(o);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -323,22 +322,18 @@ void configSetCommand(redisClient *c) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
badfmt: /* Bad format errors */
|
badfmt: /* Bad format errors */
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),
|
addReplyErrorFormat(c,"Invalid argument '%s' for CONFIG SET '%s'",
|
||||||
"-ERR invalid argument '%s' for CONFIG SET '%s'\r\n",
|
|
||||||
(char*)o->ptr,
|
(char*)o->ptr,
|
||||||
(char*)c->argv[2]->ptr));
|
(char*)c->argv[2]->ptr);
|
||||||
decrRefCount(o);
|
decrRefCount(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
void configGetCommand(redisClient *c) {
|
void configGetCommand(redisClient *c) {
|
||||||
robj *o = getDecodedObject(c->argv[2]);
|
robj *o = getDecodedObject(c->argv[2]);
|
||||||
robj *lenobj = createObject(REDIS_STRING,NULL);
|
void *replylen = addDeferredMultiBulkLength(c);
|
||||||
char *pattern = o->ptr;
|
char *pattern = o->ptr;
|
||||||
int matches = 0;
|
int matches = 0;
|
||||||
|
|
||||||
addReply(c,lenobj);
|
|
||||||
decrRefCount(lenobj);
|
|
||||||
|
|
||||||
if (stringmatch(pattern,"dbfilename",0)) {
|
if (stringmatch(pattern,"dbfilename",0)) {
|
||||||
addReplyBulkCString(c,"dbfilename");
|
addReplyBulkCString(c,"dbfilename");
|
||||||
addReplyBulkCString(c,server.dbfilename);
|
addReplyBulkCString(c,server.dbfilename);
|
||||||
@ -410,7 +405,7 @@ void configGetCommand(redisClient *c) {
|
|||||||
matches++;
|
matches++;
|
||||||
}
|
}
|
||||||
decrRefCount(o);
|
decrRefCount(o);
|
||||||
lenobj->ptr = sdscatprintf(sdsempty(),"*%d\r\n",matches*2);
|
setDeferredMultiBulkLength(c,replylen,matches*2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void configCommand(redisClient *c) {
|
void configCommand(redisClient *c) {
|
||||||
@ -428,13 +423,12 @@ void configCommand(redisClient *c) {
|
|||||||
server.stat_starttime = time(NULL);
|
server.stat_starttime = time(NULL);
|
||||||
addReply(c,shared.ok);
|
addReply(c,shared.ok);
|
||||||
} else {
|
} else {
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),
|
addReplyError(c,
|
||||||
"-ERR CONFIG subcommand must be one of GET, SET, RESETSTAT\r\n"));
|
"CONFIG subcommand must be one of GET, SET, RESETSTAT");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
badarity:
|
badarity:
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),
|
addReplyErrorFormat(c,"Wrong number of arguments for CONFIG %s",
|
||||||
"-ERR Wrong number of arguments for CONFIG %s\r\n",
|
(char*) c->argv[1]->ptr);
|
||||||
(char*) c->argv[1]->ptr));
|
|
||||||
}
|
}
|
||||||
|
40
src/db.c
40
src/db.c
@ -204,7 +204,7 @@ void selectCommand(redisClient *c) {
|
|||||||
int id = atoi(c->argv[1]->ptr);
|
int id = atoi(c->argv[1]->ptr);
|
||||||
|
|
||||||
if (selectDb(c,id) == REDIS_ERR) {
|
if (selectDb(c,id) == REDIS_ERR) {
|
||||||
addReplySds(c,sdsnew("-ERR invalid DB index\r\n"));
|
addReplyError(c,"invalid DB index");
|
||||||
} else {
|
} else {
|
||||||
addReply(c,shared.ok);
|
addReply(c,shared.ok);
|
||||||
}
|
}
|
||||||
@ -228,11 +228,9 @@ void keysCommand(redisClient *c) {
|
|||||||
sds pattern = c->argv[1]->ptr;
|
sds pattern = c->argv[1]->ptr;
|
||||||
int plen = sdslen(pattern), allkeys;
|
int plen = sdslen(pattern), allkeys;
|
||||||
unsigned long numkeys = 0;
|
unsigned long numkeys = 0;
|
||||||
robj *lenobj = createObject(REDIS_STRING,NULL);
|
void *replylen = addDeferredMultiBulkLength(c);
|
||||||
|
|
||||||
di = dictGetIterator(c->db->dict);
|
di = dictGetIterator(c->db->dict);
|
||||||
addReply(c,lenobj);
|
|
||||||
decrRefCount(lenobj);
|
|
||||||
allkeys = (pattern[0] == '*' && pattern[1] == '\0');
|
allkeys = (pattern[0] == '*' && pattern[1] == '\0');
|
||||||
while((de = dictNext(di)) != NULL) {
|
while((de = dictNext(di)) != NULL) {
|
||||||
sds key = dictGetEntryKey(de);
|
sds key = dictGetEntryKey(de);
|
||||||
@ -248,17 +246,15 @@ void keysCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
dictReleaseIterator(di);
|
dictReleaseIterator(di);
|
||||||
lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",numkeys);
|
setDeferredMultiBulkLength(c,replylen,numkeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dbsizeCommand(redisClient *c) {
|
void dbsizeCommand(redisClient *c) {
|
||||||
addReplySds(c,
|
addReplyLongLong(c,dictSize(c->db->dict));
|
||||||
sdscatprintf(sdsempty(),":%lu\r\n",dictSize(c->db->dict)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void lastsaveCommand(redisClient *c) {
|
void lastsaveCommand(redisClient *c) {
|
||||||
addReplySds(c,
|
addReplyLongLong(c,server.lastsave);
|
||||||
sdscatprintf(sdsempty(),":%lu\r\n",server.lastsave));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void typeCommand(redisClient *c) {
|
void typeCommand(redisClient *c) {
|
||||||
@ -267,24 +263,23 @@ void typeCommand(redisClient *c) {
|
|||||||
|
|
||||||
o = lookupKeyRead(c->db,c->argv[1]);
|
o = lookupKeyRead(c->db,c->argv[1]);
|
||||||
if (o == NULL) {
|
if (o == NULL) {
|
||||||
type = "+none";
|
type = "none";
|
||||||
} else {
|
} else {
|
||||||
switch(o->type) {
|
switch(o->type) {
|
||||||
case REDIS_STRING: type = "+string"; break;
|
case REDIS_STRING: type = "string"; break;
|
||||||
case REDIS_LIST: type = "+list"; break;
|
case REDIS_LIST: type = "list"; break;
|
||||||
case REDIS_SET: type = "+set"; break;
|
case REDIS_SET: type = "set"; break;
|
||||||
case REDIS_ZSET: type = "+zset"; break;
|
case REDIS_ZSET: type = "zset"; break;
|
||||||
case REDIS_HASH: type = "+hash"; break;
|
case REDIS_HASH: type = "hash"; break;
|
||||||
default: type = "+unknown"; break;
|
default: type = "unknown"; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addReplySds(c,sdsnew(type));
|
addReplyStatus(c,type);
|
||||||
addReply(c,shared.crlf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveCommand(redisClient *c) {
|
void saveCommand(redisClient *c) {
|
||||||
if (server.bgsavechildpid != -1) {
|
if (server.bgsavechildpid != -1) {
|
||||||
addReplySds(c,sdsnew("-ERR background save in progress\r\n"));
|
addReplyError(c,"Background save already in progress");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (rdbSave(server.dbfilename) == REDIS_OK) {
|
if (rdbSave(server.dbfilename) == REDIS_OK) {
|
||||||
@ -296,12 +291,11 @@ void saveCommand(redisClient *c) {
|
|||||||
|
|
||||||
void bgsaveCommand(redisClient *c) {
|
void bgsaveCommand(redisClient *c) {
|
||||||
if (server.bgsavechildpid != -1) {
|
if (server.bgsavechildpid != -1) {
|
||||||
addReplySds(c,sdsnew("-ERR background save already in progress\r\n"));
|
addReplyError(c,"Background save already in progress");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (rdbSaveBackground(server.dbfilename) == REDIS_OK) {
|
if (rdbSaveBackground(server.dbfilename) == REDIS_OK) {
|
||||||
char *status = "+Background saving started\r\n";
|
addReplyStatus(c,"Background saving started");
|
||||||
addReplySds(c,sdsnew(status));
|
|
||||||
} else {
|
} else {
|
||||||
addReply(c,shared.err);
|
addReply(c,shared.err);
|
||||||
}
|
}
|
||||||
@ -310,7 +304,7 @@ void bgsaveCommand(redisClient *c) {
|
|||||||
void shutdownCommand(redisClient *c) {
|
void shutdownCommand(redisClient *c) {
|
||||||
if (prepareForShutdown() == REDIS_OK)
|
if (prepareForShutdown() == REDIS_OK)
|
||||||
exit(0);
|
exit(0);
|
||||||
addReplySds(c, sdsnew("-ERR Errors trying to SHUTDOWN. Check logs.\r\n"));
|
addReplyError(c,"Errors trying to SHUTDOWN. Check logs.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void renameGenericCommand(redisClient *c, int nx) {
|
void renameGenericCommand(redisClient *c, int nx) {
|
||||||
|
33
src/debug.c
33
src/debug.c
@ -211,18 +211,18 @@ void debugCommand(redisClient *c) {
|
|||||||
char *strenc;
|
char *strenc;
|
||||||
|
|
||||||
strenc = strEncoding(val->encoding);
|
strenc = strEncoding(val->encoding);
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),
|
addReplyStatusFormat(c,
|
||||||
"+Value at:%p refcount:%d "
|
"Value at:%p refcount:%d "
|
||||||
"encoding:%s serializedlength:%lld\r\n",
|
"encoding:%s serializedlength:%lld",
|
||||||
(void*)val, val->refcount,
|
(void*)val, val->refcount,
|
||||||
strenc, (long long) rdbSavedObjectLen(val,NULL)));
|
strenc, (long long) rdbSavedObjectLen(val,NULL));
|
||||||
} else {
|
} else {
|
||||||
vmpointer *vp = (vmpointer*) val;
|
vmpointer *vp = (vmpointer*) val;
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),
|
addReplyStatusFormat(c,
|
||||||
"+Value swapped at: page %llu "
|
"Value swapped at: page %llu "
|
||||||
"using %llu pages\r\n",
|
"using %llu pages",
|
||||||
(unsigned long long) vp->page,
|
(unsigned long long) vp->page,
|
||||||
(unsigned long long) vp->usedpages));
|
(unsigned long long) vp->usedpages);
|
||||||
}
|
}
|
||||||
} else if (!strcasecmp(c->argv[1]->ptr,"swapin") && c->argc == 3) {
|
} else if (!strcasecmp(c->argv[1]->ptr,"swapin") && c->argc == 3) {
|
||||||
lookupKeyRead(c->db,c->argv[2]);
|
lookupKeyRead(c->db,c->argv[2]);
|
||||||
@ -233,7 +233,7 @@ void debugCommand(redisClient *c) {
|
|||||||
vmpointer *vp;
|
vmpointer *vp;
|
||||||
|
|
||||||
if (!server.vm_enabled) {
|
if (!server.vm_enabled) {
|
||||||
addReplySds(c,sdsnew("-ERR Virtual Memory is disabled\r\n"));
|
addReplyError(c,"Virtual Memory is disabled");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!de) {
|
if (!de) {
|
||||||
@ -243,9 +243,9 @@ void debugCommand(redisClient *c) {
|
|||||||
val = dictGetEntryVal(de);
|
val = dictGetEntryVal(de);
|
||||||
/* Swap it */
|
/* Swap it */
|
||||||
if (val->storage != REDIS_VM_MEMORY) {
|
if (val->storage != REDIS_VM_MEMORY) {
|
||||||
addReplySds(c,sdsnew("-ERR This key is not in memory\r\n"));
|
addReplyError(c,"This key is not in memory");
|
||||||
} else if (val->refcount != 1) {
|
} else if (val->refcount != 1) {
|
||||||
addReplySds(c,sdsnew("-ERR Object is shared\r\n"));
|
addReplyError(c,"Object is shared");
|
||||||
} else if ((vp = vmSwapObjectBlocking(val)) != NULL) {
|
} else if ((vp = vmSwapObjectBlocking(val)) != NULL) {
|
||||||
dictGetEntryVal(de) = vp;
|
dictGetEntryVal(de) = vp;
|
||||||
addReply(c,shared.ok);
|
addReply(c,shared.ok);
|
||||||
@ -274,18 +274,17 @@ void debugCommand(redisClient *c) {
|
|||||||
addReply(c,shared.ok);
|
addReply(c,shared.ok);
|
||||||
} else if (!strcasecmp(c->argv[1]->ptr,"digest") && c->argc == 2) {
|
} else if (!strcasecmp(c->argv[1]->ptr,"digest") && c->argc == 2) {
|
||||||
unsigned char digest[20];
|
unsigned char digest[20];
|
||||||
sds d = sdsnew("+");
|
sds d = sdsempty();
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
computeDatasetDigest(digest);
|
computeDatasetDigest(digest);
|
||||||
for (j = 0; j < 20; j++)
|
for (j = 0; j < 20; j++)
|
||||||
d = sdscatprintf(d, "%02x",digest[j]);
|
d = sdscatprintf(d, "%02x",digest[j]);
|
||||||
|
addReplyStatus(c,d);
|
||||||
d = sdscatlen(d,"\r\n",2);
|
sdsfree(d);
|
||||||
addReplySds(c,d);
|
|
||||||
} else {
|
} else {
|
||||||
addReplySds(c,sdsnew(
|
addReplyError(c,
|
||||||
"-ERR Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|SWAPIN <key>|SWAPOUT <key>|RELOAD]\r\n"));
|
"Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|SWAPIN <key>|SWAPOUT <key>|RELOAD]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
src/multi.c
10
src/multi.c
@ -42,7 +42,7 @@ void queueMultiCommand(redisClient *c, struct redisCommand *cmd) {
|
|||||||
|
|
||||||
void multiCommand(redisClient *c) {
|
void multiCommand(redisClient *c) {
|
||||||
if (c->flags & REDIS_MULTI) {
|
if (c->flags & REDIS_MULTI) {
|
||||||
addReplySds(c,sdsnew("-ERR MULTI calls can not be nested\r\n"));
|
addReplyError(c,"MULTI calls can not be nested");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
c->flags |= REDIS_MULTI;
|
c->flags |= REDIS_MULTI;
|
||||||
@ -51,7 +51,7 @@ void multiCommand(redisClient *c) {
|
|||||||
|
|
||||||
void discardCommand(redisClient *c) {
|
void discardCommand(redisClient *c) {
|
||||||
if (!(c->flags & REDIS_MULTI)) {
|
if (!(c->flags & REDIS_MULTI)) {
|
||||||
addReplySds(c,sdsnew("-ERR DISCARD without MULTI\r\n"));
|
addReplyError(c,"DISCARD without MULTI");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ void execCommand(redisClient *c) {
|
|||||||
int orig_argc;
|
int orig_argc;
|
||||||
|
|
||||||
if (!(c->flags & REDIS_MULTI)) {
|
if (!(c->flags & REDIS_MULTI)) {
|
||||||
addReplySds(c,sdsnew("-ERR EXEC without MULTI\r\n"));
|
addReplyError(c,"EXEC without MULTI");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ void execCommand(redisClient *c) {
|
|||||||
unwatchAllKeys(c); /* Unwatch ASAP otherwise we'll waste CPU cycles */
|
unwatchAllKeys(c); /* Unwatch ASAP otherwise we'll waste CPU cycles */
|
||||||
orig_argv = c->argv;
|
orig_argv = c->argv;
|
||||||
orig_argc = c->argc;
|
orig_argc = c->argc;
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->mstate.count));
|
addReplyMultiBulkLen(c,c->mstate.count);
|
||||||
for (j = 0; j < c->mstate.count; j++) {
|
for (j = 0; j < c->mstate.count; j++) {
|
||||||
c->argc = c->mstate.commands[j].argc;
|
c->argc = c->mstate.commands[j].argc;
|
||||||
c->argv = c->mstate.commands[j].argv;
|
c->argv = c->mstate.commands[j].argv;
|
||||||
@ -251,7 +251,7 @@ void watchCommand(redisClient *c) {
|
|||||||
int j;
|
int j;
|
||||||
|
|
||||||
if (c->flags & REDIS_MULTI) {
|
if (c->flags & REDIS_MULTI) {
|
||||||
addReplySds(c,sdsnew("-ERR WATCH inside MULTI is not allowed\r\n"));
|
addReplyError(c,"WATCH inside MULTI is not allowed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (j = 1; j < c->argc; j++)
|
for (j = 1; j < c->argc; j++)
|
||||||
|
355
src/networking.c
355
src/networking.c
@ -1,5 +1,4 @@
|
|||||||
#include "redis.h"
|
#include "redis.h"
|
||||||
|
|
||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
|
|
||||||
void *dupClientReplyValue(void *o) {
|
void *dupClientReplyValue(void *o) {
|
||||||
@ -12,7 +11,12 @@ int listMatchObjects(void *a, void *b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
redisClient *createClient(int fd) {
|
redisClient *createClient(int fd) {
|
||||||
redisClient *c = zmalloc(sizeof(*c));
|
redisClient *c;
|
||||||
|
|
||||||
|
/* Allocate more space to hold a static write buffer. */
|
||||||
|
c = zmalloc(sizeof(redisClient)+REDIS_REPLY_CHUNK_BYTES);
|
||||||
|
c->buflen = REDIS_REPLY_CHUNK_BYTES;
|
||||||
|
c->bufpos = 0;
|
||||||
|
|
||||||
anetNonBlock(NULL,fd);
|
anetNonBlock(NULL,fd);
|
||||||
anetTcpNoDelay(NULL,fd);
|
anetTcpNoDelay(NULL,fd);
|
||||||
@ -56,70 +60,238 @@ redisClient *createClient(int fd) {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addReply(redisClient *c, robj *obj) {
|
int _ensureFileEvent(redisClient *c) {
|
||||||
if (listLength(c->reply) == 0 &&
|
if (c->fd <= 0) return REDIS_ERR;
|
||||||
|
if (c->bufpos == 0 && listLength(c->reply) == 0 &&
|
||||||
(c->replstate == REDIS_REPL_NONE ||
|
(c->replstate == REDIS_REPL_NONE ||
|
||||||
c->replstate == REDIS_REPL_ONLINE) &&
|
c->replstate == REDIS_REPL_ONLINE) &&
|
||||||
aeCreateFileEvent(server.el, c->fd, AE_WRITABLE,
|
aeCreateFileEvent(server.el, c->fd, AE_WRITABLE,
|
||||||
sendReplyToClient, c) == AE_ERR) return;
|
sendReplyToClient, c) == AE_ERR) return REDIS_ERR;
|
||||||
|
return REDIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
if (server.vm_enabled && obj->storage != REDIS_VM_MEMORY) {
|
/* Create a duplicate of the last object in the reply list when
|
||||||
obj = dupStringObject(obj);
|
* it is not exclusively owned by the reply list. */
|
||||||
obj->refcount = 0; /* getDecodedObject() will increment the refcount */
|
robj *dupLastObjectIfNeeded(list *reply) {
|
||||||
|
robj *new, *cur;
|
||||||
|
listNode *ln;
|
||||||
|
redisAssert(listLength(reply) > 0);
|
||||||
|
ln = listLast(reply);
|
||||||
|
cur = listNodeValue(ln);
|
||||||
|
if (cur->refcount > 1) {
|
||||||
|
new = dupStringObject(cur);
|
||||||
|
decrRefCount(cur);
|
||||||
|
listNodeValue(ln) = new;
|
||||||
}
|
}
|
||||||
listAddNodeTail(c->reply,getDecodedObject(obj));
|
return listNodeValue(ln);
|
||||||
|
}
|
||||||
|
|
||||||
|
int _addReplyToBuffer(redisClient *c, char *s, size_t len) {
|
||||||
|
size_t available = c->buflen-c->bufpos;
|
||||||
|
|
||||||
|
/* If there already are entries in the reply list, we cannot
|
||||||
|
* add anything more to the static buffer. */
|
||||||
|
if (listLength(c->reply) > 0) return REDIS_ERR;
|
||||||
|
|
||||||
|
/* Check that the buffer has enough space available for this string. */
|
||||||
|
if (len > available) return REDIS_ERR;
|
||||||
|
|
||||||
|
memcpy(c->buf+c->bufpos,s,len);
|
||||||
|
c->bufpos+=len;
|
||||||
|
return REDIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _addReplyObjectToList(redisClient *c, robj *o) {
|
||||||
|
robj *tail;
|
||||||
|
if (listLength(c->reply) == 0) {
|
||||||
|
incrRefCount(o);
|
||||||
|
listAddNodeTail(c->reply,o);
|
||||||
|
} else {
|
||||||
|
tail = listNodeValue(listLast(c->reply));
|
||||||
|
|
||||||
|
/* Append to this object when possible. */
|
||||||
|
if (tail->ptr != NULL &&
|
||||||
|
sdslen(tail->ptr)+sdslen(o->ptr) <= REDIS_REPLY_CHUNK_BYTES)
|
||||||
|
{
|
||||||
|
tail = dupLastObjectIfNeeded(c->reply);
|
||||||
|
tail->ptr = sdscatlen(tail->ptr,o->ptr,sdslen(o->ptr));
|
||||||
|
} else {
|
||||||
|
incrRefCount(o);
|
||||||
|
listAddNodeTail(c->reply,o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This method takes responsibility over the sds. When it is no longer
|
||||||
|
* needed it will be free'd, otherwise it ends up in a robj. */
|
||||||
|
void _addReplySdsToList(redisClient *c, sds s) {
|
||||||
|
robj *tail;
|
||||||
|
if (listLength(c->reply) == 0) {
|
||||||
|
listAddNodeTail(c->reply,createObject(REDIS_STRING,s));
|
||||||
|
} else {
|
||||||
|
tail = listNodeValue(listLast(c->reply));
|
||||||
|
|
||||||
|
/* Append to this object when possible. */
|
||||||
|
if (tail->ptr != NULL &&
|
||||||
|
sdslen(tail->ptr)+sdslen(s) <= REDIS_REPLY_CHUNK_BYTES)
|
||||||
|
{
|
||||||
|
tail = dupLastObjectIfNeeded(c->reply);
|
||||||
|
tail->ptr = sdscatlen(tail->ptr,s,sdslen(s));
|
||||||
|
sdsfree(s);
|
||||||
|
} else {
|
||||||
|
listAddNodeTail(c->reply,createObject(REDIS_STRING,s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _addReplyStringToList(redisClient *c, char *s, size_t len) {
|
||||||
|
robj *tail;
|
||||||
|
if (listLength(c->reply) == 0) {
|
||||||
|
listAddNodeTail(c->reply,createStringObject(s,len));
|
||||||
|
} else {
|
||||||
|
tail = listNodeValue(listLast(c->reply));
|
||||||
|
|
||||||
|
/* Append to this object when possible. */
|
||||||
|
if (tail->ptr != NULL &&
|
||||||
|
sdslen(tail->ptr)+len <= REDIS_REPLY_CHUNK_BYTES)
|
||||||
|
{
|
||||||
|
tail = dupLastObjectIfNeeded(c->reply);
|
||||||
|
tail->ptr = sdscatlen(tail->ptr,s,len);
|
||||||
|
} else {
|
||||||
|
listAddNodeTail(c->reply,createStringObject(s,len));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addReply(redisClient *c, robj *obj) {
|
||||||
|
if (_ensureFileEvent(c) != REDIS_OK) return;
|
||||||
|
if (server.vm_enabled && obj->storage != REDIS_VM_MEMORY) {
|
||||||
|
/* Returns a new object with refcount 1 */
|
||||||
|
obj = dupStringObject(obj);
|
||||||
|
} else {
|
||||||
|
/* This increments the refcount. */
|
||||||
|
obj = getDecodedObject(obj);
|
||||||
|
}
|
||||||
|
if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != REDIS_OK)
|
||||||
|
_addReplyObjectToList(c,obj);
|
||||||
|
decrRefCount(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addReplySds(redisClient *c, sds s) {
|
void addReplySds(redisClient *c, sds s) {
|
||||||
robj *o = createObject(REDIS_STRING,s);
|
if (_ensureFileEvent(c) != REDIS_OK) {
|
||||||
addReply(c,o);
|
/* The caller expects the sds to be free'd. */
|
||||||
decrRefCount(o);
|
sdsfree(s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_addReplyToBuffer(c,s,sdslen(s)) == REDIS_OK) {
|
||||||
|
sdsfree(s);
|
||||||
|
} else {
|
||||||
|
/* This method free's the sds when it is no longer needed. */
|
||||||
|
_addReplySdsToList(c,s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addReplyString(redisClient *c, char *s, size_t len) {
|
||||||
|
if (_ensureFileEvent(c) != REDIS_OK) return;
|
||||||
|
if (_addReplyToBuffer(c,s,len) != REDIS_OK)
|
||||||
|
_addReplyStringToList(c,s,len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _addReplyError(redisClient *c, char *s, size_t len) {
|
||||||
|
addReplyString(c,"-ERR ",5);
|
||||||
|
addReplyString(c,s,len);
|
||||||
|
addReplyString(c,"\r\n",2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addReplyError(redisClient *c, char *err) {
|
||||||
|
_addReplyError(c,err,strlen(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
void addReplyErrorFormat(redisClient *c, const char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap,fmt);
|
||||||
|
sds s = sdscatvprintf(sdsempty(),fmt,ap);
|
||||||
|
va_end(ap);
|
||||||
|
_addReplyError(c,s,sdslen(s));
|
||||||
|
sdsfree(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _addReplyStatus(redisClient *c, char *s, size_t len) {
|
||||||
|
addReplyString(c,"+",1);
|
||||||
|
addReplyString(c,s,len);
|
||||||
|
addReplyString(c,"\r\n",2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addReplyStatus(redisClient *c, char *status) {
|
||||||
|
_addReplyStatus(c,status,strlen(status));
|
||||||
|
}
|
||||||
|
|
||||||
|
void addReplyStatusFormat(redisClient *c, const char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap,fmt);
|
||||||
|
sds s = sdscatvprintf(sdsempty(),fmt,ap);
|
||||||
|
va_end(ap);
|
||||||
|
_addReplyStatus(c,s,sdslen(s));
|
||||||
|
sdsfree(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adds an empty object to the reply list that will contain the multi bulk
|
||||||
|
* length, which is not known when this function is called. */
|
||||||
|
void *addDeferredMultiBulkLength(redisClient *c) {
|
||||||
|
if (_ensureFileEvent(c) != REDIS_OK) return NULL;
|
||||||
|
listAddNodeTail(c->reply,createObject(REDIS_STRING,NULL));
|
||||||
|
return listLast(c->reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Populate the length object and try glueing it to the next chunk. */
|
||||||
|
void setDeferredMultiBulkLength(redisClient *c, void *node, long length) {
|
||||||
|
listNode *ln = (listNode*)node;
|
||||||
|
robj *len, *next;
|
||||||
|
|
||||||
|
/* Abort when *node is NULL (see addDeferredMultiBulkLength). */
|
||||||
|
if (node == NULL) return;
|
||||||
|
|
||||||
|
len = listNodeValue(ln);
|
||||||
|
len->ptr = sdscatprintf(sdsempty(),"*%ld\r\n",length);
|
||||||
|
if (ln->next != NULL) {
|
||||||
|
next = listNodeValue(ln->next);
|
||||||
|
|
||||||
|
/* Only glue when the next node is non-NULL (an sds in this case) */
|
||||||
|
if (next->ptr != NULL) {
|
||||||
|
len->ptr = sdscatlen(len->ptr,next->ptr,sdslen(next->ptr));
|
||||||
|
listDelNode(c->reply,ln->next);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void addReplyDouble(redisClient *c, double d) {
|
void addReplyDouble(redisClient *c, double d) {
|
||||||
char buf[128];
|
char dbuf[128], sbuf[128];
|
||||||
|
int dlen, slen;
|
||||||
snprintf(buf,sizeof(buf),"%.17g",d);
|
dlen = snprintf(dbuf,sizeof(dbuf),"%.17g",d);
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),"$%lu\r\n%s\r\n",
|
slen = snprintf(sbuf,sizeof(sbuf),"$%d\r\n%s\r\n",dlen,dbuf);
|
||||||
(unsigned long) strlen(buf),buf));
|
addReplyString(c,sbuf,slen);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addReplyLongLong(redisClient *c, long long ll) {
|
void _addReplyLongLong(redisClient *c, long long ll, char prefix) {
|
||||||
char buf[128];
|
char buf[128];
|
||||||
size_t len;
|
int len;
|
||||||
|
buf[0] = prefix;
|
||||||
if (ll == 0) {
|
|
||||||
addReply(c,shared.czero);
|
|
||||||
return;
|
|
||||||
} else if (ll == 1) {
|
|
||||||
addReply(c,shared.cone);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
buf[0] = ':';
|
|
||||||
len = ll2string(buf+1,sizeof(buf)-1,ll);
|
len = ll2string(buf+1,sizeof(buf)-1,ll);
|
||||||
buf[len+1] = '\r';
|
buf[len+1] = '\r';
|
||||||
buf[len+2] = '\n';
|
buf[len+2] = '\n';
|
||||||
addReplySds(c,sdsnewlen(buf,len+3));
|
addReplyString(c,buf,len+3);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addReplyUlong(redisClient *c, unsigned long ul) {
|
void addReplyLongLong(redisClient *c, long long ll) {
|
||||||
char buf[128];
|
_addReplyLongLong(c,ll,':');
|
||||||
size_t len;
|
}
|
||||||
|
|
||||||
if (ul == 0) {
|
void addReplyMultiBulkLen(redisClient *c, long length) {
|
||||||
addReply(c,shared.czero);
|
_addReplyLongLong(c,length,'*');
|
||||||
return;
|
|
||||||
} else if (ul == 1) {
|
|
||||||
addReply(c,shared.cone);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
len = snprintf(buf,sizeof(buf),":%lu\r\n",ul);
|
|
||||||
addReplySds(c,sdsnewlen(buf,len));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void addReplyBulkLen(redisClient *c, robj *obj) {
|
void addReplyBulkLen(redisClient *c, robj *obj) {
|
||||||
size_t len, intlen;
|
size_t len;
|
||||||
char buf[128];
|
|
||||||
|
|
||||||
if (obj->encoding == REDIS_ENCODING_RAW) {
|
if (obj->encoding == REDIS_ENCODING_RAW) {
|
||||||
len = sdslen(obj->ptr);
|
len = sdslen(obj->ptr);
|
||||||
@ -136,11 +308,7 @@ void addReplyBulkLen(redisClient *c, robj *obj) {
|
|||||||
len++;
|
len++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buf[0] = '$';
|
_addReplyLongLong(c,len,'$');
|
||||||
intlen = ll2string(buf+1,sizeof(buf)-1,(long long)len);
|
|
||||||
buf[intlen+1] = '\r';
|
|
||||||
buf[intlen+2] = '\n';
|
|
||||||
addReplySds(c,sdsnewlen(buf,intlen+3));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void addReplyBulk(redisClient *c, robj *obj) {
|
void addReplyBulk(redisClient *c, robj *obj) {
|
||||||
@ -290,34 +458,6 @@ void freeClient(redisClient *c) {
|
|||||||
zfree(c);
|
zfree(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define GLUEREPLY_UP_TO (1024)
|
|
||||||
static void glueReplyBuffersIfNeeded(redisClient *c) {
|
|
||||||
int copylen = 0;
|
|
||||||
char buf[GLUEREPLY_UP_TO];
|
|
||||||
listNode *ln;
|
|
||||||
listIter li;
|
|
||||||
robj *o;
|
|
||||||
|
|
||||||
listRewind(c->reply,&li);
|
|
||||||
while((ln = listNext(&li))) {
|
|
||||||
int objlen;
|
|
||||||
|
|
||||||
o = ln->value;
|
|
||||||
objlen = sdslen(o->ptr);
|
|
||||||
if (copylen + objlen <= GLUEREPLY_UP_TO) {
|
|
||||||
memcpy(buf+copylen,o->ptr,objlen);
|
|
||||||
copylen += objlen;
|
|
||||||
listDelNode(c->reply,ln);
|
|
||||||
} else {
|
|
||||||
if (copylen == 0) return;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Now the output buffer is empty, add the new single element */
|
|
||||||
o = createObject(REDIS_STRING,sdsnewlen(buf,copylen));
|
|
||||||
listAddNodeHead(c->reply,o);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
|
void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||||
redisClient *c = privdata;
|
redisClient *c = privdata;
|
||||||
int nwritten = 0, totwritten = 0, objlen;
|
int nwritten = 0, totwritten = 0, objlen;
|
||||||
@ -334,31 +474,48 @@ void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while(listLength(c->reply)) {
|
while(c->bufpos > 0 || listLength(c->reply)) {
|
||||||
if (server.glueoutputbuf && listLength(c->reply) > 1)
|
if (c->bufpos > 0) {
|
||||||
glueReplyBuffersIfNeeded(c);
|
if (c->flags & REDIS_MASTER) {
|
||||||
|
/* Don't reply to a master */
|
||||||
|
nwritten = c->bufpos - c->sentlen;
|
||||||
|
} else {
|
||||||
|
nwritten = write(fd,c->buf+c->sentlen,c->bufpos-c->sentlen);
|
||||||
|
if (nwritten <= 0) break;
|
||||||
|
}
|
||||||
|
c->sentlen += nwritten;
|
||||||
|
totwritten += nwritten;
|
||||||
|
|
||||||
o = listNodeValue(listFirst(c->reply));
|
/* If the buffer was sent, set bufpos to zero to continue with
|
||||||
objlen = sdslen(o->ptr);
|
* the remainder of the reply. */
|
||||||
|
if (c->sentlen == c->bufpos) {
|
||||||
if (objlen == 0) {
|
c->bufpos = 0;
|
||||||
listDelNode(c->reply,listFirst(c->reply));
|
c->sentlen = 0;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (c->flags & REDIS_MASTER) {
|
|
||||||
/* Don't reply to a master */
|
|
||||||
nwritten = objlen - c->sentlen;
|
|
||||||
} else {
|
} else {
|
||||||
nwritten = write(fd, ((char*)o->ptr)+c->sentlen, objlen - c->sentlen);
|
o = listNodeValue(listFirst(c->reply));
|
||||||
if (nwritten <= 0) break;
|
objlen = sdslen(o->ptr);
|
||||||
}
|
|
||||||
c->sentlen += nwritten;
|
if (objlen == 0) {
|
||||||
totwritten += nwritten;
|
listDelNode(c->reply,listFirst(c->reply));
|
||||||
/* If we fully sent the object on head go to the next one */
|
continue;
|
||||||
if (c->sentlen == objlen) {
|
}
|
||||||
listDelNode(c->reply,listFirst(c->reply));
|
|
||||||
c->sentlen = 0;
|
if (c->flags & REDIS_MASTER) {
|
||||||
|
/* Don't reply to a master */
|
||||||
|
nwritten = objlen - c->sentlen;
|
||||||
|
} else {
|
||||||
|
nwritten = write(fd, ((char*)o->ptr)+c->sentlen,objlen-c->sentlen);
|
||||||
|
if (nwritten <= 0) break;
|
||||||
|
}
|
||||||
|
c->sentlen += nwritten;
|
||||||
|
totwritten += nwritten;
|
||||||
|
|
||||||
|
/* If we fully sent the object on head go to the next one */
|
||||||
|
if (c->sentlen == objlen) {
|
||||||
|
listDelNode(c->reply,listFirst(c->reply));
|
||||||
|
c->sentlen = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* Note that we avoid to send more thank REDIS_MAX_WRITE_PER_EVENT
|
/* Note that we avoid to send more thank REDIS_MAX_WRITE_PER_EVENT
|
||||||
* bytes, in a single threaded server it's a good idea to serve
|
* bytes, in a single threaded server it's a good idea to serve
|
||||||
|
12
src/object.c
12
src/object.c
@ -354,9 +354,9 @@ int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const ch
|
|||||||
double value;
|
double value;
|
||||||
if (getDoubleFromObject(o, &value) != REDIS_OK) {
|
if (getDoubleFromObject(o, &value) != REDIS_OK) {
|
||||||
if (msg != NULL) {
|
if (msg != NULL) {
|
||||||
addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
|
addReplyError(c,(char*)msg);
|
||||||
} else {
|
} else {
|
||||||
addReplySds(c, sdsnew("-ERR value is not a double\r\n"));
|
addReplyError(c,"value is not a double");
|
||||||
}
|
}
|
||||||
return REDIS_ERR;
|
return REDIS_ERR;
|
||||||
}
|
}
|
||||||
@ -393,9 +393,9 @@ int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, con
|
|||||||
long long value;
|
long long value;
|
||||||
if (getLongLongFromObject(o, &value) != REDIS_OK) {
|
if (getLongLongFromObject(o, &value) != REDIS_OK) {
|
||||||
if (msg != NULL) {
|
if (msg != NULL) {
|
||||||
addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
|
addReplyError(c,(char*)msg);
|
||||||
} else {
|
} else {
|
||||||
addReplySds(c, sdsnew("-ERR value is not an integer or out of range\r\n"));
|
addReplyError(c,"value is not an integer or out of range");
|
||||||
}
|
}
|
||||||
return REDIS_ERR;
|
return REDIS_ERR;
|
||||||
}
|
}
|
||||||
@ -410,9 +410,9 @@ int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *
|
|||||||
if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;
|
if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;
|
||||||
if (value < LONG_MIN || value > LONG_MAX) {
|
if (value < LONG_MIN || value > LONG_MAX) {
|
||||||
if (msg != NULL) {
|
if (msg != NULL) {
|
||||||
addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
|
addReplyError(c,(char*)msg);
|
||||||
} else {
|
} else {
|
||||||
addReplySds(c, sdsnew("-ERR value is out of range\r\n"));
|
addReplyError(c,"value is out of range");
|
||||||
}
|
}
|
||||||
return REDIS_ERR;
|
return REDIS_ERR;
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,7 @@ static struct config {
|
|||||||
long long start;
|
long long start;
|
||||||
long long totlatency;
|
long long totlatency;
|
||||||
int *latency;
|
int *latency;
|
||||||
|
char *title;
|
||||||
list *clients;
|
list *clients;
|
||||||
int quiet;
|
int quiet;
|
||||||
int loop;
|
int loop;
|
||||||
@ -206,16 +207,27 @@ static void clientDone(client c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Read a length from the buffer pointed to by *p, store the length in *len,
|
||||||
|
* and return the number of bytes that the cursor advanced. */
|
||||||
|
static int readLen(char *p, int *len) {
|
||||||
|
char *tail = strstr(p,"\r\n");
|
||||||
|
if (tail == NULL)
|
||||||
|
return 0;
|
||||||
|
*tail = '\0';
|
||||||
|
*len = atoi(p+1);
|
||||||
|
return tail+2-p;
|
||||||
|
}
|
||||||
|
|
||||||
static void readHandler(aeEventLoop *el, int fd, void *privdata, int mask)
|
static void readHandler(aeEventLoop *el, int fd, void *privdata, int mask)
|
||||||
{
|
{
|
||||||
char buf[1024];
|
char buf[1024], *p;
|
||||||
int nread;
|
int nread, pos=0, len=0;
|
||||||
client c = privdata;
|
client c = privdata;
|
||||||
REDIS_NOTUSED(el);
|
REDIS_NOTUSED(el);
|
||||||
REDIS_NOTUSED(fd);
|
REDIS_NOTUSED(fd);
|
||||||
REDIS_NOTUSED(mask);
|
REDIS_NOTUSED(mask);
|
||||||
|
|
||||||
nread = read(c->fd, buf, 1024);
|
nread = read(c->fd,buf,sizeof(buf));
|
||||||
if (nread == -1) {
|
if (nread == -1) {
|
||||||
fprintf(stderr, "Reading from socket: %s\n", strerror(errno));
|
fprintf(stderr, "Reading from socket: %s\n", strerror(errno));
|
||||||
freeClient(c);
|
freeClient(c);
|
||||||
@ -228,82 +240,89 @@ static void readHandler(aeEventLoop *el, int fd, void *privdata, int mask)
|
|||||||
}
|
}
|
||||||
c->totreceived += nread;
|
c->totreceived += nread;
|
||||||
c->ibuf = sdscatlen(c->ibuf,buf,nread);
|
c->ibuf = sdscatlen(c->ibuf,buf,nread);
|
||||||
|
len = sdslen(c->ibuf);
|
||||||
|
|
||||||
processdata:
|
|
||||||
/* Are we waiting for the first line of the command of for sdf
|
|
||||||
* count in bulk or multi bulk operations? */
|
|
||||||
if (c->replytype == REPLY_INT ||
|
if (c->replytype == REPLY_INT ||
|
||||||
c->replytype == REPLY_RETCODE ||
|
c->replytype == REPLY_RETCODE)
|
||||||
(c->replytype == REPLY_BULK && c->readlen == -1) ||
|
|
||||||
(c->replytype == REPLY_MBULK && c->readlen == -1) ||
|
|
||||||
(c->replytype == REPLY_MBULK && c->mbulk == -1)) {
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
/* Check if the first line is complete. This is only true if
|
|
||||||
* there is a newline inside the buffer. */
|
|
||||||
if ((p = strchr(c->ibuf,'\n')) != NULL) {
|
|
||||||
if (c->replytype == REPLY_BULK ||
|
|
||||||
(c->replytype == REPLY_MBULK && c->mbulk != -1))
|
|
||||||
{
|
|
||||||
/* Read the count of a bulk reply (being it a single bulk or
|
|
||||||
* a multi bulk reply). "$<count>" for the protocol spec. */
|
|
||||||
*p = '\0';
|
|
||||||
*(p-1) = '\0';
|
|
||||||
c->readlen = atoi(c->ibuf+1)+2;
|
|
||||||
// printf("BULK ATOI: %s\n", c->ibuf+1);
|
|
||||||
/* Handle null bulk reply "$-1" */
|
|
||||||
if (c->readlen-2 == -1) {
|
|
||||||
clientDone(c);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* Leave all the rest in the input buffer */
|
|
||||||
c->ibuf = sdsrange(c->ibuf,(p-c->ibuf)+1,-1);
|
|
||||||
/* fall through to reach the point where the code will try
|
|
||||||
* to check if the bulk reply is complete. */
|
|
||||||
} else if (c->replytype == REPLY_MBULK && c->mbulk == -1) {
|
|
||||||
/* Read the count of a multi bulk reply. That is, how many
|
|
||||||
* bulk replies we have to read next. "*<count>" protocol. */
|
|
||||||
*p = '\0';
|
|
||||||
*(p-1) = '\0';
|
|
||||||
c->mbulk = atoi(c->ibuf+1);
|
|
||||||
/* Handle null bulk reply "*-1" */
|
|
||||||
if (c->mbulk == -1) {
|
|
||||||
clientDone(c);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// printf("%p) %d elements list\n", c, c->mbulk);
|
|
||||||
/* Leave all the rest in the input buffer */
|
|
||||||
c->ibuf = sdsrange(c->ibuf,(p-c->ibuf)+1,-1);
|
|
||||||
goto processdata;
|
|
||||||
} else {
|
|
||||||
c->ibuf = sdstrim(c->ibuf,"\r\n");
|
|
||||||
clientDone(c);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* bulk read, did we read everything? */
|
|
||||||
if (((c->replytype == REPLY_MBULK && c->mbulk != -1) ||
|
|
||||||
(c->replytype == REPLY_BULK)) && c->readlen != -1 &&
|
|
||||||
(unsigned)c->readlen <= sdslen(c->ibuf))
|
|
||||||
{
|
{
|
||||||
// printf("BULKSTATUS mbulk:%d readlen:%d sdslen:%d\n",
|
/* Check if the first line is complete. This is everything we need
|
||||||
// c->mbulk,c->readlen,sdslen(c->ibuf));
|
* when waiting for an integer or status code reply.*/
|
||||||
if (c->replytype == REPLY_BULK) {
|
if ((p = strstr(c->ibuf,"\r\n")) != NULL)
|
||||||
clientDone(c);
|
goto done;
|
||||||
} else if (c->replytype == REPLY_MBULK) {
|
} else if (c->replytype == REPLY_BULK) {
|
||||||
// printf("%p) %d (%d)) ",c, c->mbulk, c->readlen);
|
int advance = 0;
|
||||||
// fwrite(c->ibuf,c->readlen,1,stdout);
|
if (c->readlen < 0) {
|
||||||
// printf("\n");
|
advance = readLen(c->ibuf+pos,&c->readlen);
|
||||||
if (--c->mbulk == 0) {
|
if (advance) {
|
||||||
clientDone(c);
|
pos += advance;
|
||||||
|
if (c->readlen == -1) {
|
||||||
|
goto done;
|
||||||
|
} else {
|
||||||
|
/* include the trailing \r\n */
|
||||||
|
c->readlen += 2;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
c->ibuf = sdsrange(c->ibuf,c->readlen,-1);
|
goto skip;
|
||||||
c->readlen = -1;
|
|
||||||
goto processdata;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int canconsume;
|
||||||
|
if (c->readlen > 0) {
|
||||||
|
canconsume = c->readlen > (len-pos) ? (len-pos) : c->readlen;
|
||||||
|
c->readlen -= canconsume;
|
||||||
|
pos += canconsume;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->readlen == 0)
|
||||||
|
goto done;
|
||||||
|
} else if (c->replytype == REPLY_MBULK) {
|
||||||
|
int advance = 0;
|
||||||
|
if (c->mbulk == -1) {
|
||||||
|
advance = readLen(c->ibuf+pos,&c->mbulk);
|
||||||
|
if (advance) {
|
||||||
|
pos += advance;
|
||||||
|
if (c->mbulk == -1)
|
||||||
|
goto done;
|
||||||
|
} else {
|
||||||
|
goto skip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int canconsume;
|
||||||
|
while(c->mbulk > 0 && pos < len) {
|
||||||
|
if (c->readlen > 0) {
|
||||||
|
canconsume = c->readlen > (len-pos) ? (len-pos) : c->readlen;
|
||||||
|
c->readlen -= canconsume;
|
||||||
|
pos += canconsume;
|
||||||
|
if (c->readlen == 0)
|
||||||
|
c->mbulk--;
|
||||||
|
} else {
|
||||||
|
advance = readLen(c->ibuf+pos,&c->readlen);
|
||||||
|
if (advance) {
|
||||||
|
pos += advance;
|
||||||
|
if (c->readlen == -1) {
|
||||||
|
c->mbulk--;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
/* include the trailing \r\n */
|
||||||
|
c->readlen += 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
goto skip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->mbulk == 0)
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
skip:
|
||||||
|
c->ibuf = sdsrange(c->ibuf,pos,-1);
|
||||||
|
return;
|
||||||
|
done:
|
||||||
|
clientDone(c);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeHandler(aeEventLoop *el, int fd, void *privdata, int mask)
|
static void writeHandler(aeEventLoop *el, int fd, void *privdata, int mask)
|
||||||
@ -371,13 +390,13 @@ static void createMissingClients(client c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void showLatencyReport(char *title) {
|
static void showLatencyReport(void) {
|
||||||
int j, seen = 0;
|
int j, seen = 0;
|
||||||
float perc, reqpersec;
|
float perc, reqpersec;
|
||||||
|
|
||||||
reqpersec = (float)config.donerequests/((float)config.totlatency/1000);
|
reqpersec = (float)config.donerequests/((float)config.totlatency/1000);
|
||||||
if (!config.quiet) {
|
if (!config.quiet) {
|
||||||
printf("====== %s ======\n", title);
|
printf("====== %s ======\n", config.title);
|
||||||
printf(" %d requests completed in %.2f seconds\n", config.donerequests,
|
printf(" %d requests completed in %.2f seconds\n", config.donerequests,
|
||||||
(float)config.totlatency/1000);
|
(float)config.totlatency/1000);
|
||||||
printf(" %d parallel clients\n", config.numclients);
|
printf(" %d parallel clients\n", config.numclients);
|
||||||
@ -393,20 +412,20 @@ static void showLatencyReport(char *title) {
|
|||||||
}
|
}
|
||||||
printf("%.2f requests per second\n\n", reqpersec);
|
printf("%.2f requests per second\n\n", reqpersec);
|
||||||
} else {
|
} else {
|
||||||
printf("%s: %.2f requests per second\n", title, reqpersec);
|
printf("%s: %.2f requests per second\n", config.title, reqpersec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prepareForBenchmark(void)
|
static void prepareForBenchmark(char *title) {
|
||||||
{
|
|
||||||
memset(config.latency,0,sizeof(int)*(MAX_LATENCY+1));
|
memset(config.latency,0,sizeof(int)*(MAX_LATENCY+1));
|
||||||
|
config.title = title;
|
||||||
config.start = mstime();
|
config.start = mstime();
|
||||||
config.donerequests = 0;
|
config.donerequests = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void endBenchmark(char *title) {
|
static void endBenchmark(void) {
|
||||||
config.totlatency = mstime()-config.start;
|
config.totlatency = mstime()-config.start;
|
||||||
showLatencyReport(title);
|
showLatencyReport();
|
||||||
freeAllClients();
|
freeAllClients();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,6 +499,18 @@ void parseOptions(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int showThroughput(struct aeEventLoop *eventLoop, long long id, void *clientData) {
|
||||||
|
REDIS_NOTUSED(eventLoop);
|
||||||
|
REDIS_NOTUSED(id);
|
||||||
|
REDIS_NOTUSED(clientData);
|
||||||
|
|
||||||
|
float dt = (float)(mstime()-config.start)/1000.0;
|
||||||
|
float rps = (float)config.donerequests/dt;
|
||||||
|
printf("%s: %.2f\r", config.title, rps);
|
||||||
|
fflush(stdout);
|
||||||
|
return 250; /* every 250ms */
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
client c;
|
client c;
|
||||||
|
|
||||||
@ -491,6 +522,7 @@ int main(int argc, char **argv) {
|
|||||||
config.requests = 10000;
|
config.requests = 10000;
|
||||||
config.liveclients = 0;
|
config.liveclients = 0;
|
||||||
config.el = aeCreateEventLoop();
|
config.el = aeCreateEventLoop();
|
||||||
|
aeCreateTimeEvent(config.el,1,showThroughput,NULL,NULL);
|
||||||
config.keepalive = 1;
|
config.keepalive = 1;
|
||||||
config.donerequests = 0;
|
config.donerequests = 0;
|
||||||
config.datasize = 3;
|
config.datasize = 3;
|
||||||
@ -514,7 +546,7 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
if (config.idlemode) {
|
if (config.idlemode) {
|
||||||
printf("Creating %d idle connections and waiting forever (Ctrl+C when done)\n", config.numclients);
|
printf("Creating %d idle connections and waiting forever (Ctrl+C when done)\n", config.numclients);
|
||||||
prepareForBenchmark();
|
prepareForBenchmark("IDLE");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
if (!c) exit(1);
|
||||||
c->obuf = sdsempty();
|
c->obuf = sdsempty();
|
||||||
@ -525,25 +557,25 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
prepareForBenchmark();
|
prepareForBenchmark("PING");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
if (!c) exit(1);
|
||||||
c->obuf = sdscat(c->obuf,"PING\r\n");
|
c->obuf = sdscat(c->obuf,"PING\r\n");
|
||||||
prepareClientForReply(c,REPLY_RETCODE);
|
prepareClientForReply(c,REPLY_RETCODE);
|
||||||
createMissingClients(c);
|
createMissingClients(c);
|
||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
endBenchmark("PING");
|
endBenchmark();
|
||||||
|
|
||||||
prepareForBenchmark();
|
prepareForBenchmark("PING (multi bulk)");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
if (!c) exit(1);
|
||||||
c->obuf = sdscat(c->obuf,"*1\r\n$4\r\nPING\r\n");
|
c->obuf = sdscat(c->obuf,"*1\r\n$4\r\nPING\r\n");
|
||||||
prepareClientForReply(c,REPLY_RETCODE);
|
prepareClientForReply(c,REPLY_RETCODE);
|
||||||
createMissingClients(c);
|
createMissingClients(c);
|
||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
endBenchmark("PING (multi bulk)");
|
endBenchmark();
|
||||||
|
|
||||||
prepareForBenchmark();
|
prepareForBenchmark("SET");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
if (!c) exit(1);
|
||||||
c->obuf = sdscatprintf(c->obuf,"SET foo_rand000000000000 %d\r\n",config.datasize);
|
c->obuf = sdscatprintf(c->obuf,"SET foo_rand000000000000 %d\r\n",config.datasize);
|
||||||
@ -557,106 +589,106 @@ int main(int argc, char **argv) {
|
|||||||
prepareClientForReply(c,REPLY_RETCODE);
|
prepareClientForReply(c,REPLY_RETCODE);
|
||||||
createMissingClients(c);
|
createMissingClients(c);
|
||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
endBenchmark("SET");
|
endBenchmark();
|
||||||
|
|
||||||
prepareForBenchmark();
|
prepareForBenchmark("GET");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
if (!c) exit(1);
|
||||||
c->obuf = sdscat(c->obuf,"GET foo_rand000000000000\r\n");
|
c->obuf = sdscat(c->obuf,"GET foo_rand000000000000\r\n");
|
||||||
prepareClientForReply(c,REPLY_BULK);
|
prepareClientForReply(c,REPLY_BULK);
|
||||||
createMissingClients(c);
|
createMissingClients(c);
|
||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
endBenchmark("GET");
|
endBenchmark();
|
||||||
|
|
||||||
prepareForBenchmark();
|
prepareForBenchmark("INCR");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
if (!c) exit(1);
|
||||||
c->obuf = sdscat(c->obuf,"INCR counter_rand000000000000\r\n");
|
c->obuf = sdscat(c->obuf,"INCR counter_rand000000000000\r\n");
|
||||||
prepareClientForReply(c,REPLY_INT);
|
prepareClientForReply(c,REPLY_INT);
|
||||||
createMissingClients(c);
|
createMissingClients(c);
|
||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
endBenchmark("INCR");
|
endBenchmark();
|
||||||
|
|
||||||
prepareForBenchmark();
|
prepareForBenchmark("LPUSH");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
if (!c) exit(1);
|
||||||
c->obuf = sdscat(c->obuf,"LPUSH mylist 3\r\nbar\r\n");
|
c->obuf = sdscat(c->obuf,"LPUSH mylist 3\r\nbar\r\n");
|
||||||
prepareClientForReply(c,REPLY_INT);
|
prepareClientForReply(c,REPLY_INT);
|
||||||
createMissingClients(c);
|
createMissingClients(c);
|
||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
endBenchmark("LPUSH");
|
endBenchmark();
|
||||||
|
|
||||||
prepareForBenchmark();
|
prepareForBenchmark("LPOP");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
if (!c) exit(1);
|
||||||
c->obuf = sdscat(c->obuf,"LPOP mylist\r\n");
|
c->obuf = sdscat(c->obuf,"LPOP mylist\r\n");
|
||||||
prepareClientForReply(c,REPLY_BULK);
|
prepareClientForReply(c,REPLY_BULK);
|
||||||
createMissingClients(c);
|
createMissingClients(c);
|
||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
endBenchmark("LPOP");
|
endBenchmark();
|
||||||
|
|
||||||
prepareForBenchmark();
|
prepareForBenchmark("SADD");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
if (!c) exit(1);
|
||||||
c->obuf = sdscat(c->obuf,"SADD myset 24\r\ncounter_rand000000000000\r\n");
|
c->obuf = sdscat(c->obuf,"SADD myset 24\r\ncounter_rand000000000000\r\n");
|
||||||
prepareClientForReply(c,REPLY_RETCODE);
|
prepareClientForReply(c,REPLY_RETCODE);
|
||||||
createMissingClients(c);
|
createMissingClients(c);
|
||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
endBenchmark("SADD");
|
endBenchmark();
|
||||||
|
|
||||||
prepareForBenchmark();
|
prepareForBenchmark("SPOP");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
if (!c) exit(1);
|
||||||
c->obuf = sdscat(c->obuf,"SPOP myset\r\n");
|
c->obuf = sdscat(c->obuf,"SPOP myset\r\n");
|
||||||
prepareClientForReply(c,REPLY_BULK);
|
prepareClientForReply(c,REPLY_BULK);
|
||||||
createMissingClients(c);
|
createMissingClients(c);
|
||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
endBenchmark("SPOP");
|
endBenchmark();
|
||||||
|
|
||||||
prepareForBenchmark();
|
prepareForBenchmark("LPUSH (again, in order to bench LRANGE)");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
if (!c) exit(1);
|
||||||
c->obuf = sdscat(c->obuf,"LPUSH mylist 3\r\nbar\r\n");
|
c->obuf = sdscat(c->obuf,"LPUSH mylist 3\r\nbar\r\n");
|
||||||
prepareClientForReply(c,REPLY_RETCODE);
|
prepareClientForReply(c,REPLY_RETCODE);
|
||||||
createMissingClients(c);
|
createMissingClients(c);
|
||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
endBenchmark("LPUSH (again, in order to bench LRANGE)");
|
endBenchmark();
|
||||||
|
|
||||||
prepareForBenchmark();
|
prepareForBenchmark("LRANGE (first 100 elements)");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
if (!c) exit(1);
|
||||||
c->obuf = sdscat(c->obuf,"LRANGE mylist 0 99\r\n");
|
c->obuf = sdscat(c->obuf,"LRANGE mylist 0 99\r\n");
|
||||||
prepareClientForReply(c,REPLY_MBULK);
|
prepareClientForReply(c,REPLY_MBULK);
|
||||||
createMissingClients(c);
|
createMissingClients(c);
|
||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
endBenchmark("LRANGE (first 100 elements)");
|
endBenchmark();
|
||||||
|
|
||||||
prepareForBenchmark();
|
prepareForBenchmark("LRANGE (first 300 elements)");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
if (!c) exit(1);
|
||||||
c->obuf = sdscat(c->obuf,"LRANGE mylist 0 299\r\n");
|
c->obuf = sdscat(c->obuf,"LRANGE mylist 0 299\r\n");
|
||||||
prepareClientForReply(c,REPLY_MBULK);
|
prepareClientForReply(c,REPLY_MBULK);
|
||||||
createMissingClients(c);
|
createMissingClients(c);
|
||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
endBenchmark("LRANGE (first 300 elements)");
|
endBenchmark();
|
||||||
|
|
||||||
prepareForBenchmark();
|
prepareForBenchmark("LRANGE (first 450 elements)");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
if (!c) exit(1);
|
||||||
c->obuf = sdscat(c->obuf,"LRANGE mylist 0 449\r\n");
|
c->obuf = sdscat(c->obuf,"LRANGE mylist 0 449\r\n");
|
||||||
prepareClientForReply(c,REPLY_MBULK);
|
prepareClientForReply(c,REPLY_MBULK);
|
||||||
createMissingClients(c);
|
createMissingClients(c);
|
||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
endBenchmark("LRANGE (first 450 elements)");
|
endBenchmark();
|
||||||
|
|
||||||
prepareForBenchmark();
|
prepareForBenchmark("LRANGE (first 600 elements)");
|
||||||
c = createClient();
|
c = createClient();
|
||||||
if (!c) exit(1);
|
if (!c) exit(1);
|
||||||
c->obuf = sdscat(c->obuf,"LRANGE mylist 0 599\r\n");
|
c->obuf = sdscat(c->obuf,"LRANGE mylist 0 599\r\n");
|
||||||
prepareClientForReply(c,REPLY_MBULK);
|
prepareClientForReply(c,REPLY_MBULK);
|
||||||
createMissingClients(c);
|
createMissingClients(c);
|
||||||
aeMain(config.el);
|
aeMain(config.el);
|
||||||
endBenchmark("LRANGE (first 600 elements)");
|
endBenchmark();
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
} while(config.loop);
|
} while(config.loop);
|
||||||
|
25
src/redis.c
25
src/redis.c
@ -909,7 +909,7 @@ int processCommand(redisClient *c) {
|
|||||||
} else if (c->multibulk) {
|
} else if (c->multibulk) {
|
||||||
if (c->bulklen == -1) {
|
if (c->bulklen == -1) {
|
||||||
if (((char*)c->argv[0]->ptr)[0] != '$') {
|
if (((char*)c->argv[0]->ptr)[0] != '$') {
|
||||||
addReplySds(c,sdsnew("-ERR multi bulk protocol error\r\n"));
|
addReplyError(c,"multi bulk protocol error");
|
||||||
resetClient(c);
|
resetClient(c);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
@ -922,7 +922,7 @@ int processCommand(redisClient *c) {
|
|||||||
bulklen < 0 || bulklen > 1024*1024*1024)
|
bulklen < 0 || bulklen > 1024*1024*1024)
|
||||||
{
|
{
|
||||||
c->argc--;
|
c->argc--;
|
||||||
addReplySds(c,sdsnew("-ERR invalid bulk write count\r\n"));
|
addReplyError(c,"invalid bulk write count");
|
||||||
resetClient(c);
|
resetClient(c);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -975,17 +975,14 @@ int processCommand(redisClient *c) {
|
|||||||
* such wrong arity, bad command name and so forth. */
|
* such wrong arity, bad command name and so forth. */
|
||||||
cmd = lookupCommand(c->argv[0]->ptr);
|
cmd = lookupCommand(c->argv[0]->ptr);
|
||||||
if (!cmd) {
|
if (!cmd) {
|
||||||
addReplySds(c,
|
addReplyErrorFormat(c,"unknown command '%s'",
|
||||||
sdscatprintf(sdsempty(), "-ERR unknown command '%s'\r\n",
|
(char*)c->argv[0]->ptr);
|
||||||
(char*)c->argv[0]->ptr));
|
|
||||||
resetClient(c);
|
resetClient(c);
|
||||||
return 1;
|
return 1;
|
||||||
} else if ((cmd->arity > 0 && cmd->arity != c->argc) ||
|
} else if ((cmd->arity > 0 && cmd->arity != c->argc) ||
|
||||||
(c->argc < -cmd->arity)) {
|
(c->argc < -cmd->arity)) {
|
||||||
addReplySds(c,
|
addReplyErrorFormat(c,"wrong number of arguments for '%s' command",
|
||||||
sdscatprintf(sdsempty(),
|
cmd->name);
|
||||||
"-ERR wrong number of arguments for '%s' command\r\n",
|
|
||||||
cmd->name));
|
|
||||||
resetClient(c);
|
resetClient(c);
|
||||||
return 1;
|
return 1;
|
||||||
} else if (cmd->flags & REDIS_CMD_BULK && c->bulklen == -1) {
|
} else if (cmd->flags & REDIS_CMD_BULK && c->bulklen == -1) {
|
||||||
@ -999,7 +996,7 @@ int processCommand(redisClient *c) {
|
|||||||
bulklen < 0 || bulklen > 1024*1024*1024)
|
bulklen < 0 || bulklen > 1024*1024*1024)
|
||||||
{
|
{
|
||||||
c->argc--;
|
c->argc--;
|
||||||
addReplySds(c,sdsnew("-ERR invalid bulk write count\r\n"));
|
addReplyError(c,"invalid bulk write count");
|
||||||
resetClient(c);
|
resetClient(c);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -1026,7 +1023,7 @@ int processCommand(redisClient *c) {
|
|||||||
|
|
||||||
/* Check if the user is authenticated */
|
/* Check if the user is authenticated */
|
||||||
if (server.requirepass && !c->authenticated && cmd->proc != authCommand) {
|
if (server.requirepass && !c->authenticated && cmd->proc != authCommand) {
|
||||||
addReplySds(c,sdsnew("-ERR operation not permitted\r\n"));
|
addReplyError(c,"operation not permitted");
|
||||||
resetClient(c);
|
resetClient(c);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -1035,7 +1032,7 @@ int processCommand(redisClient *c) {
|
|||||||
if (server.maxmemory && (cmd->flags & REDIS_CMD_DENYOOM) &&
|
if (server.maxmemory && (cmd->flags & REDIS_CMD_DENYOOM) &&
|
||||||
zmalloc_used_memory() > server.maxmemory)
|
zmalloc_used_memory() > server.maxmemory)
|
||||||
{
|
{
|
||||||
addReplySds(c,sdsnew("-ERR command not allowed when used memory > 'maxmemory'\r\n"));
|
addReplyError(c,"command not allowed when used memory > 'maxmemory'");
|
||||||
resetClient(c);
|
resetClient(c);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -1045,7 +1042,7 @@ int processCommand(redisClient *c) {
|
|||||||
&&
|
&&
|
||||||
cmd->proc != subscribeCommand && cmd->proc != unsubscribeCommand &&
|
cmd->proc != subscribeCommand && cmd->proc != unsubscribeCommand &&
|
||||||
cmd->proc != psubscribeCommand && cmd->proc != punsubscribeCommand) {
|
cmd->proc != psubscribeCommand && cmd->proc != punsubscribeCommand) {
|
||||||
addReplySds(c,sdsnew("-ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context\r\n"));
|
addReplyError(c,"only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context");
|
||||||
resetClient(c);
|
resetClient(c);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -1109,7 +1106,7 @@ void authCommand(redisClient *c) {
|
|||||||
addReply(c,shared.ok);
|
addReply(c,shared.ok);
|
||||||
} else {
|
} else {
|
||||||
c->authenticated = 0;
|
c->authenticated = 0;
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),"-ERR invalid password\r\n"));
|
addReplyError(c,"invalid password");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
22
src/redis.h
22
src/redis.h
@ -47,6 +47,7 @@
|
|||||||
#define REDIS_MAX_WRITE_PER_EVENT (1024*64)
|
#define REDIS_MAX_WRITE_PER_EVENT (1024*64)
|
||||||
#define REDIS_REQUEST_MAX_SIZE (1024*1024*256) /* max bytes in inline command */
|
#define REDIS_REQUEST_MAX_SIZE (1024*1024*256) /* max bytes in inline command */
|
||||||
#define REDIS_SHARED_INTEGERS 10000
|
#define REDIS_SHARED_INTEGERS 10000
|
||||||
|
#define REDIS_REPLY_CHUNK_BYTES (5*1500) /* 5 TCP packets with default MTU */
|
||||||
|
|
||||||
/* If more then REDIS_WRITEV_THRESHOLD write packets are pending use writev */
|
/* If more then REDIS_WRITEV_THRESHOLD write packets are pending use writev */
|
||||||
#define REDIS_WRITEV_THRESHOLD 3
|
#define REDIS_WRITEV_THRESHOLD 3
|
||||||
@ -309,6 +310,11 @@ typedef struct redisClient {
|
|||||||
list *watched_keys; /* Keys WATCHED for MULTI/EXEC CAS */
|
list *watched_keys; /* Keys WATCHED for MULTI/EXEC CAS */
|
||||||
dict *pubsub_channels; /* channels a client is interested in (SUBSCRIBE) */
|
dict *pubsub_channels; /* channels a client is interested in (SUBSCRIBE) */
|
||||||
list *pubsub_patterns; /* patterns a client is interested in (SUBSCRIBE) */
|
list *pubsub_patterns; /* patterns a client is interested in (SUBSCRIBE) */
|
||||||
|
|
||||||
|
/* Response buffer */
|
||||||
|
int bufpos;
|
||||||
|
int buflen;
|
||||||
|
char buf[];
|
||||||
} redisClient;
|
} redisClient;
|
||||||
|
|
||||||
struct saveparam {
|
struct saveparam {
|
||||||
@ -588,6 +594,8 @@ void resetClient(redisClient *c);
|
|||||||
void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask);
|
void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask);
|
||||||
void sendReplyToClientWritev(aeEventLoop *el, int fd, void *privdata, int mask);
|
void sendReplyToClientWritev(aeEventLoop *el, int fd, void *privdata, int mask);
|
||||||
void addReply(redisClient *c, robj *obj);
|
void addReply(redisClient *c, robj *obj);
|
||||||
|
void *addDeferredMultiBulkLength(redisClient *c);
|
||||||
|
void setDeferredMultiBulkLength(redisClient *c, void *node, long length);
|
||||||
void addReplySds(redisClient *c, sds s);
|
void addReplySds(redisClient *c, sds s);
|
||||||
void processInputBuffer(redisClient *c);
|
void processInputBuffer(redisClient *c);
|
||||||
void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask);
|
void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask);
|
||||||
@ -597,11 +605,23 @@ void addReplyBulkCString(redisClient *c, char *s);
|
|||||||
void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask);
|
void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask);
|
||||||
void addReply(redisClient *c, robj *obj);
|
void addReply(redisClient *c, robj *obj);
|
||||||
void addReplySds(redisClient *c, sds s);
|
void addReplySds(redisClient *c, sds s);
|
||||||
|
void addReplyError(redisClient *c, char *err);
|
||||||
|
void addReplyStatus(redisClient *c, char *status);
|
||||||
void addReplyDouble(redisClient *c, double d);
|
void addReplyDouble(redisClient *c, double d);
|
||||||
void addReplyLongLong(redisClient *c, long long ll);
|
void addReplyLongLong(redisClient *c, long long ll);
|
||||||
void addReplyUlong(redisClient *c, unsigned long ul);
|
void addReplyMultiBulkLen(redisClient *c, long length);
|
||||||
void *dupClientReplyValue(void *o);
|
void *dupClientReplyValue(void *o);
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
void addReplyErrorFormat(redisClient *c, const char *fmt, ...)
|
||||||
|
__attribute__((format(printf, 2, 3)));
|
||||||
|
void addReplyStatusFormat(redisClient *c, const char *fmt, ...)
|
||||||
|
__attribute__((format(printf, 2, 3)));
|
||||||
|
#else
|
||||||
|
void addReplyErrorFormat(redisClient *c, const char *fmt, ...);
|
||||||
|
void addReplyStatusFormat(redisClient *c, const char *fmt, ...);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* List data type */
|
/* List data type */
|
||||||
void listTypeTryConversion(robj *subject, robj *value);
|
void listTypeTryConversion(robj *subject, robj *value);
|
||||||
void listTypePush(robj *subject, robj *value, int where);
|
void listTypePush(robj *subject, robj *value, int where);
|
||||||
|
@ -179,7 +179,7 @@ void syncCommand(redisClient *c) {
|
|||||||
/* Refuse SYNC requests if we are a slave but the link with our master
|
/* Refuse SYNC requests if we are a slave but the link with our master
|
||||||
* is not ok... */
|
* is not ok... */
|
||||||
if (server.masterhost && server.replstate != REDIS_REPL_CONNECTED) {
|
if (server.masterhost && server.replstate != REDIS_REPL_CONNECTED) {
|
||||||
addReplySds(c,sdsnew("-ERR Can't SYNC while not connected with my master\r\n"));
|
addReplyError(c,"Can't SYNC while not connected with my master");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +188,7 @@ void syncCommand(redisClient *c) {
|
|||||||
* buffer registering the differences between the BGSAVE and the current
|
* buffer registering the differences between the BGSAVE and the current
|
||||||
* dataset, so that we can copy to other slaves if needed. */
|
* dataset, so that we can copy to other slaves if needed. */
|
||||||
if (listLength(c->reply) != 0) {
|
if (listLength(c->reply) != 0) {
|
||||||
addReplySds(c,sdsnew("-ERR SYNC is invalid with pending input\r\n"));
|
addReplyError(c,"SYNC is invalid with pending input");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,7 +226,7 @@ void syncCommand(redisClient *c) {
|
|||||||
redisLog(REDIS_NOTICE,"Starting BGSAVE for SYNC");
|
redisLog(REDIS_NOTICE,"Starting BGSAVE for SYNC");
|
||||||
if (rdbSaveBackground(server.dbfilename) != REDIS_OK) {
|
if (rdbSaveBackground(server.dbfilename) != REDIS_OK) {
|
||||||
redisLog(REDIS_NOTICE,"Replication failed, can't BGSAVE");
|
redisLog(REDIS_NOTICE,"Replication failed, can't BGSAVE");
|
||||||
addReplySds(c,sdsnew("-ERR Unalbe to perform background save\r\n"));
|
addReplyError(c,"Unable to perform background save");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
c->replstate = REDIS_REPL_WAIT_BGSAVE_END;
|
c->replstate = REDIS_REPL_WAIT_BGSAVE_END;
|
||||||
|
19
src/sds.c
19
src/sds.c
@ -33,7 +33,6 @@
|
|||||||
#include "sds.h"
|
#include "sds.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdarg.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include "zmalloc.h"
|
#include "zmalloc.h"
|
||||||
@ -156,8 +155,8 @@ sds sdscpy(sds s, char *t) {
|
|||||||
return sdscpylen(s, t, strlen(t));
|
return sdscpylen(s, t, strlen(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
sds sdscatprintf(sds s, const char *fmt, ...) {
|
sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
|
||||||
va_list ap;
|
va_list cpy;
|
||||||
char *buf, *t;
|
char *buf, *t;
|
||||||
size_t buflen = 16;
|
size_t buflen = 16;
|
||||||
|
|
||||||
@ -169,9 +168,8 @@ sds sdscatprintf(sds s, const char *fmt, ...) {
|
|||||||
if (buf == NULL) return NULL;
|
if (buf == NULL) return NULL;
|
||||||
#endif
|
#endif
|
||||||
buf[buflen-2] = '\0';
|
buf[buflen-2] = '\0';
|
||||||
va_start(ap, fmt);
|
va_copy(cpy,ap);
|
||||||
vsnprintf(buf, buflen, fmt, ap);
|
vsnprintf(buf, buflen, fmt, cpy);
|
||||||
va_end(ap);
|
|
||||||
if (buf[buflen-2] != '\0') {
|
if (buf[buflen-2] != '\0') {
|
||||||
zfree(buf);
|
zfree(buf);
|
||||||
buflen *= 2;
|
buflen *= 2;
|
||||||
@ -184,6 +182,15 @@ sds sdscatprintf(sds s, const char *fmt, ...) {
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sds sdscatprintf(sds s, const char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
char *t;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
t = sdscatvprintf(s,fmt,ap);
|
||||||
|
va_end(ap);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
sds sdstrim(sds s, const char *cset) {
|
sds sdstrim(sds s, const char *cset) {
|
||||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||||
char *start, *end, *sp, *ep;
|
char *start, *end, *sp, *ep;
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#define __SDS_H
|
#define __SDS_H
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
typedef char *sds;
|
typedef char *sds;
|
||||||
|
|
||||||
@ -53,6 +54,7 @@ sds sdscat(sds s, char *t);
|
|||||||
sds sdscpylen(sds s, char *t, size_t len);
|
sds sdscpylen(sds s, char *t, size_t len);
|
||||||
sds sdscpy(sds s, char *t);
|
sds sdscpy(sds s, char *t);
|
||||||
|
|
||||||
|
sds sdscatvprintf(sds s, const char *fmt, va_list ap);
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
sds sdscatprintf(sds s, const char *fmt, ...)
|
sds sdscatprintf(sds s, const char *fmt, ...)
|
||||||
__attribute__((format(printf, 2, 3)));
|
__attribute__((format(printf, 2, 3)));
|
||||||
|
@ -307,7 +307,7 @@ void sortCommand(redisClient *c) {
|
|||||||
outputlen = getop ? getop*(end-start+1) : end-start+1;
|
outputlen = getop ? getop*(end-start+1) : end-start+1;
|
||||||
if (storekey == NULL) {
|
if (storekey == NULL) {
|
||||||
/* STORE option not specified, sent the sorting result to client */
|
/* STORE option not specified, sent the sorting result to client */
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",outputlen));
|
addReplyMultiBulkLen(c,outputlen);
|
||||||
for (j = start; j <= end; j++) {
|
for (j = start; j <= end; j++) {
|
||||||
listNode *ln;
|
listNode *ln;
|
||||||
listIter li;
|
listIter li;
|
||||||
@ -369,7 +369,7 @@ void sortCommand(redisClient *c) {
|
|||||||
* replaced. */
|
* replaced. */
|
||||||
server.dirty += 1+outputlen;
|
server.dirty += 1+outputlen;
|
||||||
touchWatchedKey(c->db,storekey);
|
touchWatchedKey(c->db,storekey);
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",outputlen));
|
addReplyLongLong(c,outputlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
|
17
src/t_hash.c
17
src/t_hash.c
@ -249,7 +249,7 @@ void hmsetCommand(redisClient *c) {
|
|||||||
robj *o;
|
robj *o;
|
||||||
|
|
||||||
if ((c->argc % 2) == 1) {
|
if ((c->argc % 2) == 1) {
|
||||||
addReplySds(c,sdsnew("-ERR wrong number of arguments for HMSET\r\n"));
|
addReplyError(c,"wrong number of arguments for HMSET");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,7 +315,7 @@ void hmgetCommand(redisClient *c) {
|
|||||||
/* Note the check for o != NULL happens inside the loop. This is
|
/* Note the check for o != NULL happens inside the loop. This is
|
||||||
* done because objects that cannot be found are considered to be
|
* done because objects that cannot be found are considered to be
|
||||||
* an empty hash. The reply should then be a series of NULLs. */
|
* an empty hash. The reply should then be a series of NULLs. */
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->argc-2));
|
addReplyMultiBulkLen(c,c->argc-2);
|
||||||
for (i = 2; i < c->argc; i++) {
|
for (i = 2; i < c->argc; i++) {
|
||||||
if (o != NULL && (value = hashTypeGet(o,c->argv[i])) != NULL) {
|
if (o != NULL && (value = hashTypeGet(o,c->argv[i])) != NULL) {
|
||||||
addReplyBulk(c,value);
|
addReplyBulk(c,value);
|
||||||
@ -346,21 +346,19 @@ void hlenCommand(redisClient *c) {
|
|||||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
||||||
checkType(c,o,REDIS_HASH)) return;
|
checkType(c,o,REDIS_HASH)) return;
|
||||||
|
|
||||||
addReplyUlong(c,hashTypeLength(o));
|
addReplyLongLong(c,hashTypeLength(o));
|
||||||
}
|
}
|
||||||
|
|
||||||
void genericHgetallCommand(redisClient *c, int flags) {
|
void genericHgetallCommand(redisClient *c, int flags) {
|
||||||
robj *o, *lenobj, *obj;
|
robj *o, *obj;
|
||||||
unsigned long count = 0;
|
unsigned long count = 0;
|
||||||
hashTypeIterator *hi;
|
hashTypeIterator *hi;
|
||||||
|
void *replylen = NULL;
|
||||||
|
|
||||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
|
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
|
||||||
|| checkType(c,o,REDIS_HASH)) return;
|
|| checkType(c,o,REDIS_HASH)) return;
|
||||||
|
|
||||||
lenobj = createObject(REDIS_STRING,NULL);
|
replylen = addDeferredMultiBulkLength(c);
|
||||||
addReply(c,lenobj);
|
|
||||||
decrRefCount(lenobj);
|
|
||||||
|
|
||||||
hi = hashTypeInitIterator(o);
|
hi = hashTypeInitIterator(o);
|
||||||
while (hashTypeNext(hi) != REDIS_ERR) {
|
while (hashTypeNext(hi) != REDIS_ERR) {
|
||||||
if (flags & REDIS_HASH_KEY) {
|
if (flags & REDIS_HASH_KEY) {
|
||||||
@ -377,8 +375,7 @@ void genericHgetallCommand(redisClient *c, int flags) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
hashTypeReleaseIterator(hi);
|
hashTypeReleaseIterator(hi);
|
||||||
|
setDeferredMultiBulkLength(c,replylen,count);
|
||||||
lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void hkeysCommand(redisClient *c) {
|
void hkeysCommand(redisClient *c) {
|
||||||
|
14
src/t_list.c
14
src/t_list.c
@ -342,7 +342,7 @@ void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) {
|
|||||||
server.dirty++;
|
server.dirty++;
|
||||||
}
|
}
|
||||||
|
|
||||||
addReplyUlong(c,listTypeLength(subject));
|
addReplyLongLong(c,listTypeLength(subject));
|
||||||
}
|
}
|
||||||
|
|
||||||
void lpushxCommand(redisClient *c) {
|
void lpushxCommand(redisClient *c) {
|
||||||
@ -366,7 +366,7 @@ void linsertCommand(redisClient *c) {
|
|||||||
void llenCommand(redisClient *c) {
|
void llenCommand(redisClient *c) {
|
||||||
robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.czero);
|
robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.czero);
|
||||||
if (o == NULL || checkType(c,o,REDIS_LIST)) return;
|
if (o == NULL || checkType(c,o,REDIS_LIST)) return;
|
||||||
addReplyUlong(c,listTypeLength(o));
|
addReplyLongLong(c,listTypeLength(o));
|
||||||
}
|
}
|
||||||
|
|
||||||
void lindexCommand(redisClient *c) {
|
void lindexCommand(redisClient *c) {
|
||||||
@ -494,7 +494,7 @@ void lrangeCommand(redisClient *c) {
|
|||||||
rangelen = (end-start)+1;
|
rangelen = (end-start)+1;
|
||||||
|
|
||||||
/* Return the result in form of a multi-bulk reply */
|
/* Return the result in form of a multi-bulk reply */
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",rangelen));
|
addReplyMultiBulkLen(c,rangelen);
|
||||||
listTypeIterator *li = listTypeInitIterator(o,start,REDIS_TAIL);
|
listTypeIterator *li = listTypeInitIterator(o,start,REDIS_TAIL);
|
||||||
for (j = 0; j < rangelen; j++) {
|
for (j = 0; j < rangelen; j++) {
|
||||||
redisAssert(listTypeNext(li,&entry));
|
redisAssert(listTypeNext(li,&entry));
|
||||||
@ -594,7 +594,7 @@ void lremCommand(redisClient *c) {
|
|||||||
decrRefCount(obj);
|
decrRefCount(obj);
|
||||||
|
|
||||||
if (listTypeLength(subject) == 0) dbDelete(c->db,c->argv[1]);
|
if (listTypeLength(subject) == 0) dbDelete(c->db,c->argv[1]);
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",removed));
|
addReplyLongLong(c,removed);
|
||||||
if (removed) touchWatchedKey(c->db,c->argv[1]);
|
if (removed) touchWatchedKey(c->db,c->argv[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,7 +772,7 @@ int handleClientsWaitingListPush(redisClient *c, robj *key, robj *ele) {
|
|||||||
redisAssert(ln != NULL);
|
redisAssert(ln != NULL);
|
||||||
receiver = ln->value;
|
receiver = ln->value;
|
||||||
|
|
||||||
addReplySds(receiver,sdsnew("*2\r\n"));
|
addReplyMultiBulkLen(receiver,2);
|
||||||
addReplyBulk(receiver,key);
|
addReplyBulk(receiver,key);
|
||||||
addReplyBulk(receiver,ele);
|
addReplyBulk(receiver,ele);
|
||||||
unblockClientWaitingData(receiver);
|
unblockClientWaitingData(receiver);
|
||||||
@ -792,7 +792,7 @@ void blockingPopGenericCommand(redisClient *c, int where) {
|
|||||||
|
|
||||||
/* Make sure the timeout is not negative */
|
/* Make sure the timeout is not negative */
|
||||||
if (lltimeout < 0) {
|
if (lltimeout < 0) {
|
||||||
addReplySds(c,sdsnew("-ERR timeout is negative\r\n"));
|
addReplyError(c,"timeout is negative");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -822,7 +822,7 @@ void blockingPopGenericCommand(redisClient *c, int where) {
|
|||||||
* "real" command will add the last element (the value)
|
* "real" command will add the last element (the value)
|
||||||
* for us. If this souds like an hack to you it's just
|
* for us. If this souds like an hack to you it's just
|
||||||
* because it is... */
|
* because it is... */
|
||||||
addReplySds(c,sdsnew("*2\r\n"));
|
addReplyMultiBulkLen(c,2);
|
||||||
addReplyBulk(c,argv[1]);
|
addReplyBulk(c,argv[1]);
|
||||||
popGenericCommand(c,where);
|
popGenericCommand(c,where);
|
||||||
|
|
||||||
|
13
src/t_set.c
13
src/t_set.c
@ -276,7 +276,7 @@ void scardCommand(redisClient *c) {
|
|||||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
||||||
checkType(c,o,REDIS_SET)) return;
|
checkType(c,o,REDIS_SET)) return;
|
||||||
|
|
||||||
addReplyUlong(c,setTypeSize(o));
|
addReplyLongLong(c,setTypeSize(o));
|
||||||
}
|
}
|
||||||
|
|
||||||
void spopCommand(redisClient *c) {
|
void spopCommand(redisClient *c) {
|
||||||
@ -320,7 +320,8 @@ int qsortCompareSetsByCardinality(const void *s1, const void *s2) {
|
|||||||
void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum, robj *dstkey) {
|
void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum, robj *dstkey) {
|
||||||
robj **sets = zmalloc(sizeof(robj*)*setnum);
|
robj **sets = zmalloc(sizeof(robj*)*setnum);
|
||||||
setTypeIterator *si;
|
setTypeIterator *si;
|
||||||
robj *ele, *lenobj = NULL, *dstset = NULL;
|
robj *ele, *dstset = NULL;
|
||||||
|
void *replylen = NULL;
|
||||||
unsigned long j, cardinality = 0;
|
unsigned long j, cardinality = 0;
|
||||||
|
|
||||||
for (j = 0; j < setnum; j++) {
|
for (j = 0; j < setnum; j++) {
|
||||||
@ -356,9 +357,7 @@ void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum,
|
|||||||
* to the output list and save the pointer to later modify it with the
|
* to the output list and save the pointer to later modify it with the
|
||||||
* right length */
|
* right length */
|
||||||
if (!dstkey) {
|
if (!dstkey) {
|
||||||
lenobj = createObject(REDIS_STRING,NULL);
|
replylen = addDeferredMultiBulkLength(c);
|
||||||
addReply(c,lenobj);
|
|
||||||
decrRefCount(lenobj);
|
|
||||||
} else {
|
} else {
|
||||||
/* If we have a target key where to store the resulting set
|
/* If we have a target key where to store the resulting set
|
||||||
* create this key with an empty set inside */
|
* create this key with an empty set inside */
|
||||||
@ -400,7 +399,7 @@ void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum,
|
|||||||
touchWatchedKey(c->db,dstkey);
|
touchWatchedKey(c->db,dstkey);
|
||||||
server.dirty++;
|
server.dirty++;
|
||||||
} else {
|
} else {
|
||||||
lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",cardinality);
|
setDeferredMultiBulkLength(c,replylen,cardinality);
|
||||||
}
|
}
|
||||||
zfree(sets);
|
zfree(sets);
|
||||||
}
|
}
|
||||||
@ -470,7 +469,7 @@ void sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj *
|
|||||||
|
|
||||||
/* Output the content of the resulting set, if not in STORE mode */
|
/* Output the content of the resulting set, if not in STORE mode */
|
||||||
if (!dstkey) {
|
if (!dstkey) {
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",cardinality));
|
addReplyMultiBulkLen(c,cardinality);
|
||||||
si = setTypeInitIterator(dstset);
|
si = setTypeInitIterator(dstset);
|
||||||
while((ele = setTypeNext(si)) != NULL) {
|
while((ele = setTypeNext(si)) != NULL) {
|
||||||
addReplyBulk(c,ele);
|
addReplyBulk(c,ele);
|
||||||
|
@ -12,7 +12,7 @@ void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expir
|
|||||||
if (getLongFromObjectOrReply(c, expire, &seconds, NULL) != REDIS_OK)
|
if (getLongFromObjectOrReply(c, expire, &seconds, NULL) != REDIS_OK)
|
||||||
return;
|
return;
|
||||||
if (seconds <= 0) {
|
if (seconds <= 0) {
|
||||||
addReplySds(c,sdsnew("-ERR invalid expire time in SETEX\r\n"));
|
addReplyError(c,"invalid expire time in SETEX");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,7 +79,7 @@ void getsetCommand(redisClient *c) {
|
|||||||
void mgetCommand(redisClient *c) {
|
void mgetCommand(redisClient *c) {
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->argc-1));
|
addReplyMultiBulkLen(c,c->argc-1);
|
||||||
for (j = 1; j < c->argc; j++) {
|
for (j = 1; j < c->argc; j++) {
|
||||||
robj *o = lookupKeyRead(c->db,c->argv[j]);
|
robj *o = lookupKeyRead(c->db,c->argv[j]);
|
||||||
if (o == NULL) {
|
if (o == NULL) {
|
||||||
@ -98,7 +98,7 @@ void msetGenericCommand(redisClient *c, int nx) {
|
|||||||
int j, busykeys = 0;
|
int j, busykeys = 0;
|
||||||
|
|
||||||
if ((c->argc % 2) == 0) {
|
if ((c->argc % 2) == 0) {
|
||||||
addReplySds(c,sdsnew("-ERR wrong number of arguments for MSET\r\n"));
|
addReplyError(c,"wrong number of arguments for MSET");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Handle the NX flag. The MSETNX semantic is to return zero and don't
|
/* Handle the NX flag. The MSETNX semantic is to return zero and don't
|
||||||
@ -211,7 +211,7 @@ void appendCommand(redisClient *c) {
|
|||||||
}
|
}
|
||||||
touchWatchedKey(c->db,c->argv[1]);
|
touchWatchedKey(c->db,c->argv[1]);
|
||||||
server.dirty++;
|
server.dirty++;
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),":%lu\r\n",(unsigned long)totlen));
|
addReplyLongLong(c,totlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
void substrCommand(redisClient *c) {
|
void substrCommand(redisClient *c) {
|
||||||
|
26
src/t_zset.c
26
src/t_zset.c
@ -355,8 +355,7 @@ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double scoreval, i
|
|||||||
*score = scoreval;
|
*score = scoreval;
|
||||||
}
|
}
|
||||||
if (isnan(*score)) {
|
if (isnan(*score)) {
|
||||||
addReplySds(c,
|
addReplyError(c,"resulting score is not a number (NaN)");
|
||||||
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
|
||||||
@ -561,7 +560,8 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
|
|||||||
/* 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);
|
||||||
if (setnum < 1) {
|
if (setnum < 1) {
|
||||||
addReplySds(c,sdsnew("-ERR at least 1 input key is needed for ZUNIONSTORE/ZINTERSTORE\r\n"));
|
addReplyError(c,
|
||||||
|
"at least 1 input key is needed for ZUNIONSTORE/ZINTERSTORE");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -782,8 +782,7 @@ void zrangeGenericCommand(redisClient *c, int reverse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Return the result in form of a multi-bulk reply */
|
/* Return the result in form of a multi-bulk reply */
|
||||||
addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",
|
addReplyMultiBulkLen(c,withscores ? (rangelen*2) : rangelen);
|
||||||
withscores ? (rangelen*2) : rangelen));
|
|
||||||
for (j = 0; j < rangelen; j++) {
|
for (j = 0; j < rangelen; j++) {
|
||||||
ele = ln->obj;
|
ele = ln->obj;
|
||||||
addReplyBulk(c,ele);
|
addReplyBulk(c,ele);
|
||||||
@ -840,8 +839,7 @@ void genericZrangebyscoreCommand(redisClient *c, int justcount) {
|
|||||||
if (c->argc != (4 + withscores) && c->argc != (7 + withscores))
|
if (c->argc != (4 + withscores) && c->argc != (7 + withscores))
|
||||||
badsyntax = 1;
|
badsyntax = 1;
|
||||||
if (badsyntax) {
|
if (badsyntax) {
|
||||||
addReplySds(c,
|
addReplyError(c,"wrong number of arguments for ZRANGEBYSCORE");
|
||||||
sdsnew("-ERR wrong number of arguments for ZRANGEBYSCORE\r\n"));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -866,7 +864,8 @@ void genericZrangebyscoreCommand(redisClient *c, int justcount) {
|
|||||||
zset *zsetobj = o->ptr;
|
zset *zsetobj = o->ptr;
|
||||||
zskiplist *zsl = zsetobj->zsl;
|
zskiplist *zsl = zsetobj->zsl;
|
||||||
zskiplistNode *ln;
|
zskiplistNode *ln;
|
||||||
robj *ele, *lenobj = NULL;
|
robj *ele;
|
||||||
|
void *replylen = NULL;
|
||||||
unsigned long rangelen = 0;
|
unsigned long rangelen = 0;
|
||||||
|
|
||||||
/* Get the first node with the score >= min, or with
|
/* Get the first node with the score >= min, or with
|
||||||
@ -884,11 +883,8 @@ void genericZrangebyscoreCommand(redisClient *c, int justcount) {
|
|||||||
* are in the list, so we push this object that will represent
|
* are in the list, so we push this object that will represent
|
||||||
* the multi-bulk length in the output buffer, and will "fix"
|
* the multi-bulk length in the output buffer, and will "fix"
|
||||||
* it later */
|
* it later */
|
||||||
if (!justcount) {
|
if (!justcount)
|
||||||
lenobj = createObject(REDIS_STRING,NULL);
|
replylen = addDeferredMultiBulkLength(c);
|
||||||
addReply(c,lenobj);
|
|
||||||
decrRefCount(lenobj);
|
|
||||||
}
|
|
||||||
|
|
||||||
while(ln && (maxex ? (ln->score < max) : (ln->score <= max))) {
|
while(ln && (maxex ? (ln->score < max) : (ln->score <= max))) {
|
||||||
if (offset) {
|
if (offset) {
|
||||||
@ -910,7 +906,7 @@ void genericZrangebyscoreCommand(redisClient *c, int justcount) {
|
|||||||
if (justcount) {
|
if (justcount) {
|
||||||
addReplyLongLong(c,(long)rangelen);
|
addReplyLongLong(c,(long)rangelen);
|
||||||
} else {
|
} else {
|
||||||
lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",
|
setDeferredMultiBulkLength(c,replylen,
|
||||||
withscores ? (rangelen*2) : rangelen);
|
withscores ? (rangelen*2) : rangelen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -933,7 +929,7 @@ void zcardCommand(redisClient *c) {
|
|||||||
checkType(c,o,REDIS_ZSET)) return;
|
checkType(c,o,REDIS_ZSET)) return;
|
||||||
|
|
||||||
zs = o->ptr;
|
zs = o->ptr;
|
||||||
addReplyUlong(c,zs->zsl->length);
|
addReplyLongLong(c,zs->zsl->length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void zscoreCommand(redisClient *c) {
|
void zscoreCommand(redisClient *c) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user