X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/9ebed7cf066267c0fe91f100f50c028fc4cec23f..69d95c3e1cb1e6e306261957fba361cc45290a6d:/redis.c diff --git a/redis.c b/redis.c index d34b2abe..1abfd96b 100644 --- a/redis.c +++ b/redis.c @@ -27,7 +27,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#define REDIS_VERSION "1.3.3" +#define REDIS_VERSION "1.3.4" #include "fmacros.h" #include "config.h" @@ -38,6 +38,7 @@ #include #include #define __USE_POSIX199309 +#define __USE_UNIX98 #include #ifdef HAVE_BACKTRACE @@ -455,6 +456,7 @@ typedef struct _redisSortOperation { typedef struct zskiplistNode { struct zskiplistNode **forward; struct zskiplistNode *backward; + unsigned long *span; double score; robj *obj; } zskiplistNode; @@ -653,9 +655,11 @@ static void zscoreCommand(redisClient *c); static void zremrangebyscoreCommand(redisClient *c); static void multiCommand(redisClient *c); static void execCommand(redisClient *c); +static void discardCommand(redisClient *c); static void blpopCommand(redisClient *c); static void brpopCommand(redisClient *c); static void appendCommand(redisClient *c); +static void zrankCommand(redisClient *c); /*================================= Globals ================================= */ @@ -708,6 +712,7 @@ static struct redisCommand cmdTable[] = { {"zrevrange",zrevrangeCommand,-4,REDIS_CMD_INLINE,1,1,1}, {"zcard",zcardCommand,2,REDIS_CMD_INLINE,1,1,1}, {"zscore",zscoreCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,1,1,1}, + {"zrank",zrankCommand,3,REDIS_CMD_INLINE,1,1,1}, {"incrby",incrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,1,1,1}, {"decrby",decrbyCommand,3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,1,1,1}, {"getset",getsetCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,1,1,1}, @@ -733,6 +738,7 @@ static struct redisCommand cmdTable[] = { {"type",typeCommand,2,REDIS_CMD_INLINE,1,1,1}, {"multi",multiCommand,1,REDIS_CMD_INLINE,0,0,0}, {"exec",execCommand,1,REDIS_CMD_INLINE,0,0,0}, + {"discard",discardCommand,1,REDIS_CMD_INLINE,0,0,0}, {"sync",syncCommand,1,REDIS_CMD_INLINE,0,0,0}, {"flushdb",flushdbCommand,1,REDIS_CMD_INLINE,0,0,0}, {"flushall",flushallCommand,1,REDIS_CMD_INLINE,0,0,0}, @@ -2141,7 +2147,7 @@ static int processCommand(redisClient *c) { } /* Exec the command */ - if (c->flags & REDIS_MULTI && cmd->proc != execCommand) { + if (c->flags & REDIS_MULTI && cmd->proc != execCommand && cmd->proc != discardCommand) { queueMultiCommand(c,cmd); addReply(c,shared.queued); } else { @@ -2334,7 +2340,8 @@ static void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mas } else { return; } - processInputBuffer(c); + if (!(c->flags & REDIS_BLOCKED)) + processInputBuffer(c); } static int selectDb(redisClient *c, int id) { @@ -3817,7 +3824,7 @@ static void keysCommand(redisClient *c) { dictEntry *de; sds pattern = c->argv[1]->ptr; int plen = sdslen(pattern); - unsigned long numkeys = 0, keyslen = 0; + unsigned long numkeys = 0; robj *lenobj = createObject(REDIS_STRING,NULL); di = dictGetIterator(c->db->dict); @@ -3830,17 +3837,15 @@ static void keysCommand(redisClient *c) { if ((pattern[0] == '*' && pattern[1] == '\0') || stringmatchlen(pattern,plen,key,sdslen(key),0)) { if (expireIfNeeded(c->db,keyobj) == 0) { - if (numkeys != 0) - addReply(c,shared.space); + addReplyBulkLen(c,keyobj); addReply(c,keyobj); + addReply(c,shared.crlf); numkeys++; - keyslen += sdslen(key); } } } dictReleaseIterator(di); - lenobj->ptr = sdscatprintf(sdsempty(),"$%lu\r\n",keyslen+(numkeys ? (numkeys-1) : 0)); - addReply(c,shared.crlf); + lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",numkeys); } static void dbsizeCommand(redisClient *c) { @@ -4793,6 +4798,7 @@ static zskiplistNode *zslCreateNode(int level, double score, robj *obj) { zskiplistNode *zn = zmalloc(sizeof(*zn)); zn->forward = zmalloc(sizeof(zskiplistNode*) * level); + zn->span = zmalloc(sizeof(unsigned long) * level); zn->score = score; zn->obj = obj; return zn; @@ -4806,8 +4812,10 @@ static zskiplist *zslCreate(void) { zsl->level = 1; zsl->length = 0; zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL,0,NULL); - for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) + for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) { zsl->header->forward[j] = NULL; + zsl->header->span[j] = 0; + } zsl->header->backward = NULL; zsl->tail = NULL; return zsl; @@ -4816,6 +4824,7 @@ static zskiplist *zslCreate(void) { static void zslFreeNode(zskiplistNode *node) { decrRefCount(node->obj); zfree(node->forward); + zfree(node->span); zfree(node); } @@ -4823,6 +4832,7 @@ static void zslFree(zskiplist *zsl) { zskiplistNode *node = zsl->header->forward[0], *next; zfree(zsl->header->forward); + zfree(zsl->header->span); zfree(zsl->header); while(node) { next = node->forward[0]; @@ -4841,15 +4851,21 @@ static int zslRandomLevel(void) { static void zslInsert(zskiplist *zsl, double score, robj *obj) { zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x; + unsigned long span[ZSKIPLIST_MAXLEVEL]; int i, level; x = zsl->header; for (i = zsl->level-1; i >= 0; i--) { + /* store span that is crossed to reach the insert position */ + span[i] = i == (zsl->level-1) ? 0 : span[i+1]; + while (x->forward[i] && (x->forward[i]->score < score || (x->forward[i]->score == score && - compareStringObjects(x->forward[i]->obj,obj) < 0))) + compareStringObjects(x->forward[i]->obj,obj) < 0))) { + span[i] += x->span[i]; x = x->forward[i]; + } update[i] = x; } /* we assume the key is not already inside, since we allow duplicated @@ -4858,15 +4874,28 @@ static void zslInsert(zskiplist *zsl, double score, robj *obj) { * if the element is already inside or not. */ level = zslRandomLevel(); if (level > zsl->level) { - for (i = zsl->level; i < level; i++) + for (i = zsl->level; i < level; i++) { + span[i] = 0; update[i] = zsl->header; + update[i]->span[i] = zsl->length; + } zsl->level = level; } x = zslCreateNode(level,score,obj); for (i = 0; i < level; i++) { x->forward[i] = update[i]->forward[i]; update[i]->forward[i] = x; + + /* update span covered by update[i] as x is inserted here */ + x->span[i] = update[i]->span[i] - (span[0] - span[i]); + update[i]->span[i] = (span[0] - span[i]) + 1; } + + /* increment span for untouched levels */ + for (i = level; i < zsl->level; i++) { + update[i]->span[i]++; + } + x->backward = (update[0] == zsl->header) ? NULL : update[0]; if (x->forward[0]) x->forward[0]->backward = x; @@ -4894,8 +4923,12 @@ static int zslDelete(zskiplist *zsl, double score, robj *obj) { x = x->forward[0]; if (x && score == x->score && compareStringObjects(x->obj,obj) == 0) { for (i = 0; i < zsl->level; i++) { - if (update[i]->forward[i] != x) break; - update[i]->forward[i] = x->forward[i]; + if (update[i]->forward[i] == x) { + update[i]->span[i] += x->span[i] - 1; + update[i]->forward[i] = x->forward[i]; + } else { + update[i]->span[i] -= 1; + } } if (x->forward[0]) { x->forward[0]->backward = (x->backward == zsl->header) ? @@ -4936,8 +4969,12 @@ static unsigned long zslDeleteRange(zskiplist *zsl, double min, double max, dict zskiplistNode *next; for (i = 0; i < zsl->level; i++) { - if (update[i]->forward[i] != x) break; - update[i]->forward[i] = x->forward[i]; + if (update[i]->forward[i] == x) { + update[i]->span[i] += x->span[i] - 1; + update[i]->forward[i] = x->forward[i]; + } else { + update[i]->span[i] -= 1; + } } if (x->forward[0]) { x->forward[0]->backward = (x->backward == zsl->header) ? @@ -5377,6 +5414,50 @@ static void zscoreCommand(redisClient *c) { } } +static void zrankCommand(redisClient *c) { + robj *o; + o = lookupKeyRead(c->db,c->argv[1]); + if (o == NULL) { + addReply(c,shared.nullbulk); + return; + } + if (o->type != REDIS_ZSET) { + addReply(c,shared.wrongtypeerr); + return; + } + + zset *zs = o->ptr; + zskiplist *zsl = zs->zsl; + dictEntry *de = dictFind(zs->dict,c->argv[2]); + if (!de) { + addReply(c,shared.nullbulk); + return; + } + + double *score = dictGetEntryVal(de); + zskiplistNode *x; + unsigned long rank = 0; + int i; + + x = zsl->header; + for (i = zsl->level-1; i >= 0; i--) { + while (x->forward[i] && + (x->forward[i]->score < *score || + (x->forward[i]->score == *score && + compareStringObjects(x->forward[i]->obj,c->argv[2]) < 0))) { + rank += x->span[i]; + x = x->forward[i]; + } + + if (x->forward[i] && compareStringObjects(x->forward[i]->obj,c->argv[2]) == 0) { + addReplyLong(c, rank); + return; + } + } + + addReply(c,shared.nullbulk); +} + /* ========================= Non type-specific commands ==================== */ static void flushdbCommand(redisClient *c) { @@ -6050,6 +6131,18 @@ static void multiCommand(redisClient *c) { addReply(c,shared.ok); } +static void discardCommand(redisClient *c) { + if (!(c->flags & REDIS_MULTI)) { + addReplySds(c,sdsnew("-ERR DISCARD without MULTI\r\n")); + return; + } + + freeClientMultiState(c); + initClientMultiState(c); + c->flags &= (~REDIS_MULTI); + addReply(c,shared.ok); +} + static void execCommand(redisClient *c) { int j; robj **orig_argv; @@ -6138,7 +6231,6 @@ static void blockForKeys(redisClient *c, robj **keys, int numkeys, time_t timeou } /* Mark the client as a blocked client */ c->flags |= REDIS_BLOCKED; - aeDeleteFileEvent(server.el,c->fd,AE_READABLE); server.blpop_blocked_clients++; } @@ -6166,14 +6258,7 @@ static void unblockClientWaitingData(redisClient *c) { c->blockingkeys = NULL; c->flags &= (~REDIS_BLOCKED); server.blpop_blocked_clients--; - /* Ok now we are ready to get read events from socket, note that we - * can't trap errors here as it's possible that unblockClientWaitingDatas() 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 + /* We want to process data if there is some command waiting * in the input buffer. Note that this is safe even if * unblockClientWaitingData() gets called from freeClient() because * freeClient() will be smart enough to call this function @@ -7411,6 +7496,7 @@ static int vmWriteObjectOnSwap(robj *o, off_t page) { return REDIS_ERR; } rdbSaveObject(server.vm_fp,o); + fflush(server.vm_fp); if (server.vm_enabled) pthread_mutex_unlock(&server.io_swapfile_mutex); return REDIS_OK; } @@ -7438,7 +7524,6 @@ static int vmSwapObjectBlocking(robj *key, robj *val) { (unsigned long long) page, (unsigned long long) pages); server.vm_stats_swapped_objects++; server.vm_stats_swapouts++; - fflush(server.vm_fp); return REDIS_OK; } @@ -7567,7 +7652,7 @@ static double computeObjectSwappability(robj *o) { } break; } - return (double)asize*log(1+asize); + return (double)age*log(1+asize); } /* Try to swap an object that's a good candidate for swapping. @@ -8403,7 +8488,7 @@ static void *getMcontextEip(ucontext_t *uc) { #else return (void*) uc->uc_mcontext->__ss.__eip; #endif -#elif defined(__i386__) || defined(__X86_64__) || defined(__x86_64__) +#elif defined(__i386__) || defined(__X86_64__) || defined(__x86_64__) return (void*) uc->uc_mcontext.gregs[REG_EIP]; /* Linux 32/64 bit */ #elif defined(__ia64__) /* Linux IA64 */ return (void*) uc->uc_mcontext.sc_ip;