void spopCommand(redisClient *c) { robj *set, *ele, *aux; int64_t llele; int encoding; if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL || checkType(c,set,REDIS_SET)) return; encoding = setTypeRandomElement(set,&ele,&llele); if (encoding == REDIS_ENCODING_INTSET) { ele = createStringObjectFromLongLong(llele); set->ptr = intsetRemove(set->ptr,llele,NULL); } else { incrRefCount(ele); setTypeRemove(set,ele); } notifyKeyspaceEvent(REDIS_NOTIFY_SET,"spop",c->argv[1],c->db->id); /* Replicate/AOF this command as an SREM operation */ aux = createStringObject("SREM",4); rewriteClientCommandVector(c,3,aux,c->argv[1],ele); decrRefCount(ele); decrRefCount(aux); addReplyBulk(c,ele); if (setTypeSize(set) == 0) { dbDelete(c->db,c->argv[1]); notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],c->db->id); } signalModifiedKey(c->db,c->argv[1]); server.dirty++; }
void spopCommand(client *c) { robj *set, *ele, *aux; sds sdsele; int64_t llele; int encoding; if (c->argc == 3) { spopWithCountCommand(c); return; } else if (c->argc > 3) { addReply(c,shared.syntaxerr); return; } /* Make sure a key with the name inputted exists, and that it's type is * indeed a set */ if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL || checkType(c,set,OBJ_SET)) return; /* Get a random element from the set */ encoding = setTypeRandomElement(set,&sdsele,&llele); /* Remove the element from the set */ if (encoding == OBJ_ENCODING_INTSET) { ele = createStringObjectFromLongLong(llele); set->ptr = intsetRemove(set->ptr,llele,NULL); } else { ele = createStringObject(sdsele,sdslen(sdsele)); setTypeRemove(set,ele->ptr); } notifyKeyspaceEvent(NOTIFY_SET,"spop",c->argv[1],c->db->id); /* Replicate/AOF this command as an SREM operation */ aux = createStringObject("SREM",4); rewriteClientCommandVector(c,3,aux,c->argv[1],ele); decrRefCount(aux); /* Add the element to the reply */ addReplyBulk(c,ele); decrRefCount(ele); /* Delete the set if it's empty */ if (setTypeSize(set) == 0) { dbDelete(c->db,c->argv[1]); notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],c->db->id); } /* Set has been modified */ signalModifiedKey(c->db,c->argv[1]); server.dirty++; }
int setTypeRemove(robj *setobj, robj *value) { long long llval; if (setobj->encoding == REDIS_ENCODING_HT) { if (dictDelete(setobj->ptr,value) == DICT_OK) { if (htNeedsResize(setobj->ptr)) dictResize(setobj->ptr); return 1; } } else if (setobj->encoding == REDIS_ENCODING_INTSET) { if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) { int success; setobj->ptr = intsetRemove(setobj->ptr,llval,&success); if (success) return 1; } } else { redisPanic("Unknown set encoding"); } return 0; }
void spopCommand(redisClient *c) { robj *set, *ele; int64_t llele; int encoding; if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL || checkType(c,set,REDIS_SET)) return; encoding = setTypeRandomElement(set,&ele,&llele); if (encoding == REDIS_ENCODING_INTSET) { addReplyBulkLongLong(c,llele); set->ptr = intsetRemove(set->ptr,llele,NULL); } else { addReplyBulk(c,ele); setTypeRemove(set,ele); } if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]); touchWatchedKey(c->db,c->argv[1]); server.dirty++; }
int main(int argc, char **argv) { uint8_t success; int i; intset *is; sranddev(); printf("Value encodings: "); { assert(_intsetValueEncoding(-32768) == INTSET_ENC_INT16); assert(_intsetValueEncoding(+32767) == INTSET_ENC_INT16); assert(_intsetValueEncoding(-32769) == INTSET_ENC_INT32); assert(_intsetValueEncoding(+32768) == INTSET_ENC_INT32); assert(_intsetValueEncoding(-2147483648) == INTSET_ENC_INT32); assert(_intsetValueEncoding(+2147483647) == INTSET_ENC_INT32); assert(_intsetValueEncoding(-2147483649) == INTSET_ENC_INT64); assert(_intsetValueEncoding(+2147483648) == INTSET_ENC_INT64); assert(_intsetValueEncoding(-9223372036854775808ull) == INTSET_ENC_INT64); assert(_intsetValueEncoding(+9223372036854775807ull) == INTSET_ENC_INT64); ok(); } printf("Basic adding: "); { is = intsetNew(); is = intsetAdd(is,5,&success); assert(success); is = intsetAdd(is,6,&success); assert(success); is = intsetAdd(is,4,&success); assert(success); is = intsetAdd(is,4,&success); assert(!success); ok(); } printf("Large number of random adds: "); { int inserts = 0; is = intsetNew(); for (i = 0; i < 1024; i++) { is = intsetAdd(is,rand()%0x800,&success); if (success) inserts++; } assert(is->length == inserts); checkConsistency(is); ok(); } printf("Upgrade from int16 to int32: "); { is = intsetNew(); is = intsetAdd(is,32,NULL); assert(is->encoding == INTSET_ENC_INT16); is = intsetAdd(is,65535,NULL); assert(is->encoding == INTSET_ENC_INT32); assert(intsetFind(is,32)); assert(intsetFind(is,65535)); checkConsistency(is); is = intsetNew(); is = intsetAdd(is,32,NULL); assert(is->encoding == INTSET_ENC_INT16); is = intsetAdd(is,-65535,NULL); assert(is->encoding == INTSET_ENC_INT32); assert(intsetFind(is,32)); assert(intsetFind(is,-65535)); checkConsistency(is); ok(); } printf("Upgrade from int16 to int64: "); { is = intsetNew(); is = intsetAdd(is,32,NULL); assert(is->encoding == INTSET_ENC_INT16); is = intsetAdd(is,4294967295,NULL); assert(is->encoding == INTSET_ENC_INT64); assert(intsetFind(is,32)); assert(intsetFind(is,4294967295)); checkConsistency(is); is = intsetNew(); is = intsetAdd(is,32,NULL); assert(is->encoding == INTSET_ENC_INT16); is = intsetAdd(is,-4294967295,NULL); assert(is->encoding == INTSET_ENC_INT64); assert(intsetFind(is,32)); assert(intsetFind(is,-4294967295)); checkConsistency(is); ok(); } printf("Upgrade from int32 to int64: "); { is = intsetNew(); is = intsetAdd(is,65535,NULL); assert(is->encoding == INTSET_ENC_INT32); is = intsetAdd(is,4294967295,NULL); assert(is->encoding == INTSET_ENC_INT64); assert(intsetFind(is,65535)); assert(intsetFind(is,4294967295)); checkConsistency(is); is = intsetNew(); is = intsetAdd(is,65535,NULL); assert(is->encoding == INTSET_ENC_INT32); is = intsetAdd(is,-4294967295,NULL); assert(is->encoding == INTSET_ENC_INT64); assert(intsetFind(is,65535)); assert(intsetFind(is,-4294967295)); checkConsistency(is); ok(); } printf("Stress lookups: "); { long num = 100000, size = 10000; int i, bits = 20; long long start; is = createSet(bits,size); checkConsistency(is); start = usec(); for (i = 0; i < num; i++) intsetSearch(is,rand() % ((1<<bits)-1),NULL); printf("%ld lookups, %ld element set, %lldusec\n",num,size,usec()-start); } printf("Stress add+delete: "); { int i, v1, v2; is = intsetNew(); for (i = 0; i < 0xffff; i++) { v1 = rand() % 0xfff; is = intsetAdd(is,v1,NULL); assert(intsetFind(is,v1)); v2 = rand() % 0xfff; is = intsetRemove(is,v2,NULL); assert(!intsetFind(is,v2)); } checkConsistency(is); ok(); } }
void spopWithCountCommand(client *c) { long l; unsigned long count, size; robj *set; /* Get the count argument */ if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != C_OK) return; if (l >= 0) { count = (unsigned) l; } else { addReply(c,shared.outofrangeerr); return; } /* Make sure a key with the name inputted exists, and that it's type is * indeed a set. Otherwise, return nil */ if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL || checkType(c,set,OBJ_SET)) return; /* If count is zero, serve an empty multibulk ASAP to avoid special * cases later. */ if (count == 0) { addReply(c,shared.emptymultibulk); return; } size = setTypeSize(set); /* Generate an SPOP keyspace notification */ notifyKeyspaceEvent(NOTIFY_SET,"spop",c->argv[1],c->db->id); server.dirty += count; /* CASE 1: * The number of requested elements is greater than or equal to * the number of elements inside the set: simply return the whole set. */ if (count >= size) { /* We just return the entire set */ sunionDiffGenericCommand(c,c->argv+1,1,NULL,SET_OP_UNION); /* Delete the set as it is now empty */ dbDelete(c->db,c->argv[1]); notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],c->db->id); /* Propagate this command as an DEL operation */ rewriteClientCommandVector(c,2,shared.del,c->argv[1]); signalModifiedKey(c->db,c->argv[1]); server.dirty++; return; } /* Case 2 and 3 require to replicate SPOP as a set of SREM commands. * Prepare our replication argument vector. Also send the array length * which is common to both the code paths. */ robj *propargv[3]; propargv[0] = createStringObject("SREM",4); propargv[1] = c->argv[1]; addReplyMultiBulkLen(c,count); /* Common iteration vars. */ sds sdsele; robj *objele; int encoding; int64_t llele; unsigned long remaining = size-count; /* Elements left after SPOP. */ /* If we are here, the number of requested elements is less than the * number of elements inside the set. Also we are sure that count < size. * Use two different strategies. * * CASE 2: The number of elements to return is small compared to the * set size. We can just extract random elements and return them to * the set. */ if (remaining*SPOP_MOVE_STRATEGY_MUL > count) { while(count--) { /* Emit and remove. */ encoding = setTypeRandomElement(set,&sdsele,&llele); if (encoding == OBJ_ENCODING_INTSET) { addReplyBulkLongLong(c,llele); objele = createStringObjectFromLongLong(llele); set->ptr = intsetRemove(set->ptr,llele,NULL); } else { addReplyBulkCBuffer(c,sdsele,sdslen(sdsele)); objele = createStringObject(sdsele,sdslen(sdsele)); setTypeRemove(set,sdsele); } /* Replicate/AOF this command as an SREM operation */ propargv[2] = objele; alsoPropagate(server.sremCommand,c->db->id,propargv,3, PROPAGATE_AOF|PROPAGATE_REPL); decrRefCount(objele); } } else { /* CASE 3: The number of elements to return is very big, approaching * the size of the set itself. After some time extracting random elements * from such a set becomes computationally expensive, so we use * a different strategy, we extract random elements that we don't * want to return (the elements that will remain part of the set), * creating a new set as we do this (that will be stored as the original * set). Then we return the elements left in the original set and * release it. */ robj *newset = NULL; /* Create a new set with just the remaining elements. */ while(remaining--) { encoding = setTypeRandomElement(set,&sdsele,&llele); if (encoding == OBJ_ENCODING_INTSET) { sdsele = sdsfromlonglong(llele); } else { sdsele = sdsdup(sdsele); } if (!newset) newset = setTypeCreate(sdsele); setTypeAdd(newset,sdsele); setTypeRemove(set,sdsele); sdsfree(sdsele); } /* Assign the new set as the key value. */ incrRefCount(set); /* Protect the old set value. */ dbOverwrite(c->db,c->argv[1],newset); /* Tranfer the old set to the client and release it. */ setTypeIterator *si; si = setTypeInitIterator(set); while((encoding = setTypeNext(si,&sdsele,&llele)) != -1) { if (encoding == OBJ_ENCODING_INTSET) { addReplyBulkLongLong(c,llele); objele = createStringObjectFromLongLong(llele); } else { addReplyBulkCBuffer(c,sdsele,sdslen(sdsele)); objele = createStringObject(sdsele,sdslen(sdsele)); } /* Replicate/AOF this command as an SREM operation */ propargv[2] = objele; alsoPropagate(server.sremCommand,c->db->id,propargv,3, PROPAGATE_AOF|PROPAGATE_REPL); decrRefCount(objele); } setTypeReleaseIterator(si); decrRefCount(set); } /* Don't propagate the command itself even if we incremented the * dirty counter. We don't want to propagate an SPOP command since * we propagated the command as a set of SREMs operations using * the alsoPropagate() API. */ decrRefCount(propargv[0]); preventCommandPropagation(c); signalModifiedKey(c->db,c->argv[1]); server.dirty++; }