void zremCommand(redisClient *c) { robj *zsetobj; zset *zs; dictEntry *de; double curscore; int deleted; if ((zsetobj = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || checkType(c,zsetobj,REDIS_ZSET)) return; zs = zsetobj->ptr; c->argv[2] = tryObjectEncoding(c->argv[2]); de = dictFind(zs->dict,c->argv[2]); if (de == NULL) { addReply(c,shared.czero); return; } /* Delete from the skiplist */ curscore = *(double*)dictGetEntryVal(de); deleted = zslDelete(zs->zsl,curscore,c->argv[2]); redisAssert(deleted != 0); /* Delete from the hash table */ dictDelete(zs->dict,c->argv[2]); if (htNeedsResize(zs->dict)) dictResize(zs->dict); if (dictSize(zs->dict) == 0) dbDelete(c->db,c->argv[1]); touchWatchedKey(c->db,c->argv[1]); server.dirty++; addReply(c,shared.cone); }
/* This generic command implements both ZADD and ZINCRBY. */ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double score, int incr) { robj *zsetobj; zset *zs; zskiplistNode *znode; zsetobj = lookupKeyWrite(c->db,key); if (zsetobj == NULL) { zsetobj = createZsetObject(); dbAdd(c->db,key,zsetobj); } else { if (zsetobj->type != REDIS_ZSET) { addReply(c,shared.wrongtypeerr); return; } } zs = zsetobj->ptr; /* Since both ZADD and ZINCRBY are implemented here, we need to increment * the score first by the current score if ZINCRBY is called. */ if (incr) { /* Read the old score. If the element was not present starts from 0 */ dictEntry *de = dictFind(zs->dict,ele); if (de != NULL) score += *(double*)dictGetEntryVal(de); if (isnan(score)) { addReplyError(c,"resulting score is not a number (NaN)"); /* 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 * there was already an element in the sorted set. */ return; } } /* We need to remove and re-insert the element when it was already present * in the dictionary, to update the skiplist. Note that we delay adding a * pointer to the score because we want to reference the score in the * skiplist node. */ if (dictAdd(zs->dict,ele,NULL) == DICT_OK) { dictEntry *de; /* New element */ incrRefCount(ele); /* added to hash */ znode = zslInsert(zs->zsl,score,ele); incrRefCount(ele); /* added to skiplist */ /* Update the score in the dict entry */ de = dictFind(zs->dict,ele); redisAssert(de != NULL); dictGetEntryVal(de) = &znode->score; touchWatchedKey(c->db,c->argv[1]); server.dirty++; if (incr) addReplyDouble(c,score); else addReply(c,shared.cone); } else { dictEntry *de; robj *curobj; double *curscore; int deleted; /* Update score */ de = dictFind(zs->dict,ele); redisAssert(de != NULL); curobj = dictGetEntryKey(de); curscore = dictGetEntryVal(de); /* When the score is updated, reuse the existing string object to * prevent extra alloc/dealloc of strings on ZINCRBY. */ if (score != *curscore) { deleted = zslDelete(zs->zsl,*curscore,curobj); redisAssert(deleted != 0); znode = zslInsert(zs->zsl,score,curobj); incrRefCount(curobj); /* Update the score in the current dict entry */ dictGetEntryVal(de) = &znode->score; touchWatchedKey(c->db,c->argv[1]); server.dirty++; } if (incr) addReplyDouble(c,score); else addReply(c,shared.czero); } }
int main(void) { int i; struct timeval start, end; int *key = malloc(N * sizeof(int)); if (key == NULL) { exit(-1); } zskiplist *zsl = zslCreate(); if (zsl == NULL) { exit(-1); } printf("Test start!\n"); printf("Add %d nodes...\n", N); /* Insert test */ srandom(time(NULL)); gettimeofday(&start, NULL); for (i = 0; i < N; i++) { key[i] = (int)random(); zslInsert(zsl, key[i]); } gettimeofday(&end, NULL); printf("time span: %ldms\n", (end.tv_sec - start.tv_sec)*1000 + (end.tv_usec - start.tv_usec)/1000); /* Search test 1 */ printf("Now search each node by key...\n"); gettimeofday(&start, NULL); for (i = 0; i < N; i++) { zrangespec range; range.min = range.max = key[i]; range.minex = range.maxex = 0; zskiplistNode *zn = zslFirstInRange(zsl, &range); if (zn != NULL) { #ifdef SKIPLIST_DEBUG printf("key:0x%08x\n", zn->score); #endif } else { printf("Not found:0x%08x\n", key[i]); } #ifdef SKIPLIST_DEBUG printf("key rank:%ld\n", zslGetRank(zsl, key[i])); #else zslGetRank(zsl, key[i]); #endif } gettimeofday(&end, NULL); printf("time span: %ldms\n", (end.tv_sec - start.tv_sec)*1000 + (end.tv_usec - start.tv_usec)/1000); /* Search test 2 */ printf("Now search each node by rank...\n"); gettimeofday(&start, NULL); for (i = 0; i < N; i++) { zskiplistNode* zn = zslGetElementByRank(zsl, i + 1); if (zn != NULL) { #ifdef SKIPLIST_DEBUG printf("key:0x%08x\n", zn->score); #endif } else { printf("Not found:%d\n", i + 1); } } gettimeofday(&end, NULL); printf("time span: %ldms\n", (end.tv_sec - start.tv_sec)*1000 + (end.tv_usec - start.tv_usec)/1000); /* Delete test */ printf("Now remove all nodes...\n"); gettimeofday(&start, NULL); for (i = 0; i < N; i++) { zslDelete(zsl, key[i]); } gettimeofday(&end, NULL); printf("time span: %ldms\n", (end.tv_sec - start.tv_sec)*1000 + (end.tv_usec - start.tv_usec)/1000); printf("End of Test.\n"); zslFree(zsl); return 0; }
void SlotToKeyDel(robj *key) { unsigned int hashslot = keyHashSlot(key->ptr,sdslen(key->ptr)); zslDelete(server.cluster.slots_to_keys,hashslot,key); }
/* This generic command implements both ZADD and ZINCRBY. * scoreval is the score if the operation is a ZADD (doincrement == 0) or * the increment if the operation is a ZINCRBY (doincrement == 1). */ void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double scoreval, int doincrement) { robj *zsetobj; zset *zs; double *score; if (isnan(scoreval)) { addReplySds(c,sdsnew("-ERR provide score is Not A Number (nan)\r\n")); return; } zsetobj = lookupKeyWrite(c->db,key); if (zsetobj == NULL) { zsetobj = createZsetObject(); dbAdd(c->db,key,zsetobj); } else { if (zsetobj->type != REDIS_ZSET) { addReply(c,shared.wrongtypeerr); return; } } zs = zsetobj->ptr; /* Ok now since we implement both ZADD and ZINCRBY here the code * needs to handle the two different conditions. It's all about setting * '*score', that is, the new score to set, to the right value. */ score = zmalloc(sizeof(double)); if (doincrement) { dictEntry *de; /* Read the old score. If the element was not present starts from 0 */ de = dictFind(zs->dict,ele); if (de) { double *oldscore = dictGetEntryVal(de); *score = *oldscore + scoreval; } else { *score = scoreval; } if (isnan(*score)) { addReplySds(c, sdsnew("-ERR resulting score is Not A Number (nan)\r\n")); zfree(score); /* 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 * there was already an element in the sorted set. */ return; } } else { *score = scoreval; } /* What follows is a simple remove and re-insert operation that is common * to both ZADD and ZINCRBY... */ if (dictAdd(zs->dict,ele,score) == DICT_OK) { /* case 1: New element */ incrRefCount(ele); /* added to hash */ zslInsert(zs->zsl,*score,ele); incrRefCount(ele); /* added to skiplist */ touchWatchedKey(c->db,c->argv[1]); server.dirty++; if (doincrement) addReplyDouble(c,*score); else addReply(c,shared.cone); } else { dictEntry *de; double *oldscore; /* case 2: Score update operation */ de = dictFind(zs->dict,ele); redisAssert(de != NULL); oldscore = dictGetEntryVal(de); if (*score != *oldscore) { int deleted; /* Remove and insert the element in the skip list with new score */ deleted = zslDelete(zs->zsl,*oldscore,ele); redisAssert(deleted != 0); zslInsert(zs->zsl,*score,ele); incrRefCount(ele); /* Update the score in the hash table */ dictReplace(zs->dict,ele,score); touchWatchedKey(c->db,c->argv[1]); server.dirty++; } else { zfree(score); } if (doincrement) addReplyDouble(c,*score); else addReply(c,shared.czero); } }