/* * * slotsdel slot1 [slot2 ...] * */ void slotsdelCommand(redisClient *c) { int slots_slot[HASH_SLOTS_SIZE]; int n = 0; if (c->argc <= 1) { addReplyErrorFormat(c, "wrong number of arguments for 'slotsdel' command"); return; } int i; for (i = 1; i < c->argc; i ++) { int slot; if (parse_slot(c, c->argv[i], &slot) != 0) { return; } slots_slot[n] = slot; n ++; } for (i = 0; i < n; i ++) { dict *d = c->db->hash_slots[slots_slot[i]]; int s = dictSize(d); if (s == 0) { continue; } list *l = listCreate(); listSetFreeMethod(l, decrRefCountVoid); unsigned long cursor = 0; do { cursor = dictScan(d, cursor, slotsScanSdsKeyCallback, l); } while (cursor != 0); while (1) { listNode *head = listFirst(l); if (head == NULL) { break; } robj *key = listNodeValue(head); robj *keys[] = {key}; slotsremove(c, keys, 1, 0); listDelNode(l, head); } listRelease(l); } addReplyMultiBulkLen(c, n); for (i = 0; i < n; i ++) { int n = slots_slot[i]; int s = dictSize(c->db->hash_slots[n]); addReplyMultiBulkLen(c, 2); addReplyLongLong(c, n); addReplyLongLong(c, s); } }
/* This command implements SCAN, HSCAN and SSCAN commands. * If object 'o' is passed, then it must be a Hash or Set object, otherwise * if 'o' is NULL the command will operate on the dictionary associated with * the current database. * * When 'o' is not NULL the function assumes that the first argument in * the client arguments vector is a key so it skips it before iterating * in order to parse options. * * In the case of a Hash object the function returns both the field and value * of every element on the Hash. */ void scanGenericCommand(client *c, robj *o, unsigned long cursor) { int i, j; list *keys = listCreate(); listNode *node, *nextnode; long count = 10; sds pat = NULL; int patlen = 0, use_pattern = 0; dict *ht; /* Object must be NULL (to iterate keys names), or the type of the object * must be Set, Sorted Set, or Hash. */ serverAssert(o == NULL || o->type == OBJ_SET || o->type == OBJ_HASH || o->type == OBJ_ZSET); /* Set i to the first option argument. The previous one is the cursor. */ i = (o == NULL) ? 2 : 3; /* Skip the key argument if needed. */ /* Step 1: Parse options. */ while (i < c->argc) { j = c->argc - i; if (!strcasecmp(c->argv[i]->ptr, "count") && j >= 2) { if (getLongFromObjectOrReply(c, c->argv[i+1], &count, NULL) != C_OK) { goto cleanup; } if (count < 1) { addReply(c,shared.syntaxerr); goto cleanup; } i += 2; } else if (!strcasecmp(c->argv[i]->ptr, "match") && j >= 2) { pat = c->argv[i+1]->ptr; patlen = sdslen(pat); /* The pattern always matches if it is exactly "*", so it is * equivalent to disabling it. */ use_pattern = !(pat[0] == '*' && patlen == 1); i += 2; } else { addReply(c,shared.syntaxerr); goto cleanup; } } /* Step 2: Iterate the collection. * * Note that if the object is encoded with a ziplist, intset, or any other * representation that is not a hash table, we are sure that it is also * composed of a small number of elements. So to avoid taking state we * just return everything inside the object in a single call, setting the * cursor to zero to signal the end of the iteration. */ /* Handle the case of a hash table. */ ht = NULL; if (o == NULL) { ht = c->db->dict; } else if (o->type == OBJ_SET && o->encoding == OBJ_ENCODING_HT) { ht = o->ptr; } else if (o->type == OBJ_HASH && o->encoding == OBJ_ENCODING_HT) { ht = o->ptr; count *= 2; /* We return key / value for this type. */ } else if (o->type == OBJ_ZSET && o->encoding == OBJ_ENCODING_SKIPLIST) { zset *zs = o->ptr; ht = zs->dict; count *= 2; /* We return key / value for this type. */ } if (ht) { void *privdata[2]; /* We set the max number of iterations to ten times the specified * COUNT, so if the hash table is in a pathological state (very * sparsely populated) we avoid to block too much time at the cost * of returning no or very few elements. */ long maxiterations = count*10; /* We pass two pointers to the callback: the list to which it will * add new elements, and the object containing the dictionary so that * it is possible to fetch more data in a type-dependent way. */ privdata[0] = keys; privdata[1] = o; do { cursor = dictScan(ht, cursor, scanCallback, privdata); } while (cursor && maxiterations-- && listLength(keys) < (unsigned long)count); } else if (o->type == OBJ_SET) { int pos = 0; int64_t ll; while(intsetGet(o->ptr,pos++,&ll)) listAddNodeTail(keys,createStringObjectFromLongLong(ll)); cursor = 0; } else if (o->type == OBJ_HASH || o->type == OBJ_ZSET) { unsigned char *p = ziplistIndex(o->ptr,0); unsigned char *vstr; unsigned int vlen; long long vll; while(p) { ziplistGet(p,&vstr,&vlen,&vll); listAddNodeTail(keys, (vstr != NULL) ? createStringObject((char*)vstr,vlen) : createStringObjectFromLongLong(vll)); p = ziplistNext(o->ptr,p); } cursor = 0; } else { serverPanic("Not handled encoding in SCAN."); } /* Step 3: Filter elements. */ node = listFirst(keys); while (node) { robj *kobj = listNodeValue(node); nextnode = listNextNode(node); int filter = 0; /* Filter element if it does not match the pattern. */ if (!filter && use_pattern) { if (sdsEncodedObject(kobj)) { if (!stringmatchlen(pat, patlen, kobj->ptr, sdslen(kobj->ptr), 0)) filter = 1; } else { char buf[LONG_STR_SIZE]; int len; serverAssert(kobj->encoding == OBJ_ENCODING_INT); len = ll2string(buf,sizeof(buf),(long)kobj->ptr); if (!stringmatchlen(pat, patlen, buf, len, 0)) filter = 1; } } /* Filter element if it is an expired key. */ if (!filter && o == NULL && expireIfNeeded(c->db, kobj)) filter = 1; /* Remove the element and its associted value if needed. */ if (filter) { decrRefCount(kobj); listDelNode(keys, node); } /* If this is a hash or a sorted set, we have a flat list of * key-value elements, so if this element was filtered, remove the * value, or skip it if it was not filtered: we only match keys. */ if (o && (o->type == OBJ_ZSET || o->type == OBJ_HASH)) { node = nextnode; nextnode = listNextNode(node); if (filter) { kobj = listNodeValue(node); decrRefCount(kobj); listDelNode(keys, node); } } node = nextnode; } /* Step 4: Reply to the client. */ addReplyMultiBulkLen(c, 2); addReplyBulkLongLong(c,cursor); addReplyMultiBulkLen(c, listLength(keys)); while ((node = listFirst(keys)) != NULL) { robj *kobj = listNodeValue(node); addReplyBulk(c, kobj); decrRefCount(kobj); listDelNode(keys, node); } cleanup: listSetFreeMethod(keys,decrRefCountVoid); listRelease(keys); }
/* * * do migrate mutli key-value(s) for {slotsmgrt/slotsmgrtone}with tag commands * return value: * -1 - error happens * >=0 - # of success migration * */ static int slotsmgrttag_command(redisClient *c, sds host, sds port, int timeout, robj *key) { int taglen; void *tag = slots_tag(key->ptr, &taglen); if (tag == NULL) { return slotsmgrtone_command(c, host, port, timeout, key); } int fd = slotsmgrt_get_socket(c, host, port, timeout); if (fd == -1) { return -1; } list *l = listCreate(); listSetFreeMethod(l, decrRefCountVoid); do { uint32_t crc; int slot = slots_num(key->ptr, &crc); dict *d = c->db->hash_slots[slot]; long long cursor = 0; void *args[] = {l, tag, &taglen, (void *)(long)crc}; do { cursor = dictScan(d, cursor, slotsScanSdsKeyTagCallback, args); } while (cursor != 0); } while (0); int max = listLength(l); if (max == 0) { listRelease(l); return 0; } robj **keys = zmalloc(sizeof(robj *) * max); robj **vals = zmalloc(sizeof(robj *) * max); int n = 0; for (int i = 0; i < max; i ++) { listNode *head = listFirst(l); robj *key = listNodeValue(head); robj *val = lookupKeyWrite(c->db, key); if (val != NULL) { keys[n] = key; vals[n] = val; n ++; incrRefCount(key); } listDelNode(l, head); } int ret = 0; if (n != 0) { if (slotsmgrt(c, host, port, fd, c->db->id, timeout, keys, vals, n) != 0) { slotsmgrt_close_socket(host, port); ret = -1; } else { slotsremove(c, keys, n, 1); ret = n; } } listRelease(l); for (int i = 0; i < n; i ++) { decrRefCount(keys[i]); } zfree(keys); zfree(vals); return ret; }
/* * * slotscheck * */ void slotscheckCommand(redisClient *c) { sds bug = NULL; int i; for (i = 0; i < HASH_SLOTS_SIZE && bug == NULL; i ++) { dict *d = c->db->hash_slots[i]; if (dictSize(d) == 0) { continue; } list *l = listCreate(); listSetFreeMethod(l, decrRefCountVoid); unsigned long cursor = 0; do { cursor = dictScan(d, cursor, slotsScanSdsKeyCallback, l); while (1) { listNode *head = listFirst(l); if (head == NULL) { break; } robj *key = listNodeValue(head); if (lookupKey(c->db, key) == NULL) { if (bug == NULL) { bug = sdsdup(key->ptr); } } listDelNode(l, head); } } while (cursor != 0 && bug == NULL); listRelease(l); } if (bug != NULL) { addReplyErrorFormat(c, "step 1, miss = '%s'", bug); sdsfree(bug); return; } do { dict *d = c->db->dict; if (dictSize(d) == 0) { break; } list *l = listCreate(); listSetFreeMethod(l, decrRefCountVoid); unsigned long cursor = 0; do { cursor = dictScan(d, cursor, slotsScanSdsKeyCallback, l); while (1) { listNode *head = listFirst(l); if (head == NULL) { break; } robj *key = listNodeValue(head); int slot = slots_num(key->ptr, NULL); if (dictFind(c->db->hash_slots[slot], key->ptr) == NULL) { if (bug == NULL) { bug = sdsdup(key->ptr); } } listDelNode(l, head); } } while (cursor != 0 && bug == NULL); listRelease(l); } while (0); if (bug != NULL) { addReplyErrorFormat(c, "step 2, miss = '%s'", bug); sdsfree(bug); return; } addReply(c, shared.ok); }
/* * main function. */ int main() { struct timeval tv; srand(time(NULL)^getpid()); gettimeofday(&tv,NULL); // rand seed or fixed seed. unsigned int mod = tv.tv_sec^tv.tv_usec^getpid(); printf("%u\n", mod); //dictSetHashFunctionSeed(mod); dictSetHashFunctionSeed(12391); // create <k,v> = <str, str>. dictType myType = { myHash, /* hash function */ NULL, /* key dup */ NULL, /* val dup */ myCompare, /* key compare */ myDestructor, /* key destructor */ NULL /* val destructor */ }; // step 1: create. dict* myDict = dictCreate(&myType, NULL); assert(myDict != NULL); printf("-------------------\n"); printState(myDict); char* key[10] = {"hello0", "hello1", "hello2", "hello3", "hello4", "hello5", "hello6", "hello7", "hello8", "hello9"}; char* val[10] = {"h0", "h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8", "h9"}; for(int i=0; i<10; i++) { unsigned int hash = myHash(key[i]); unsigned int idx = hash & 3; printf("real key: %u, real idx=%d\n", hash, idx); } // step 2: add printf("----------add first 5-----------------\n"); for(int i = 0; i<5; i++) { printf("add %d\n", i); int ret = dictAdd(myDict, key[i], val[i]); printState(myDict); assert(ret==0); } printf("----------start rehashing..------------\n"); for(int i=0; i<5; i++) { dictRehash(myDict, 1); printState(myDict); } printf("----------add last 5.-----------------\n"); for(int i = 5; i<10; i++) { printf("add %d\n", i); int ret = dictAdd(myDict, key[i], val[i]); printState(myDict); assert(ret==0); } // index. printf("------------index---------------\n"); for(int i = 0; i < 10; i++) { printf("i=%d\n", i); char* v = dictFetchValue(myDict, key[i]); int ret = strcmp(v, val[i]); assert(ret == 0); } char* v = dictFetchValue(myDict, "hello world2"); assert(v == NULL); // foreach dict. unsigned long cur = 0; while(1) { cur = dictScan(myDict, cur, dictScanCallback, NULL); if(cur == 0) { break; } } // release. dictRelease(myDict); return 0; }