Commit c45570f4 authored by Vitaly Arbuzov's avatar Vitaly Arbuzov
Browse files

Make random dict selection fair

parent 2a9c77a2
...@@ -387,17 +387,26 @@ robj *dbRandomKey(redisDb *db) { ...@@ -387,17 +387,26 @@ robj *dbRandomKey(redisDb *db) {
/* Return random non-empty dictionary from this DB. */ /* Return random non-empty dictionary from this DB. */
dict *getRandomDict(redisDb *db) { dict *getRandomDict(redisDb *db) {
if (db->dict_count == 1) return db->dict[0]; if (db->dict_count == 1) return db->dict[0];
unsigned long long target = rand();
int i = 0, r = 0; /* If number of keys is greater than max target value, then we should call target once more and get more entropy. */
for (int j = 0; j < CLUSTER_SLOTS; j++) { if (db->key_count > RAND_MAX) {
/* Skip empty dicts or if we want only rehashing dicts and the dict isn't rehashing. */ target <<= 32;
if (dictSize(db->dict[j]) == 0) continue; target |= rand();
if (i == 0 || (rand() % (i + 1)) == 0) { }
r = j; /* Select K-th non-empty bucket with 1/K probability, this keeps balanced probabilities for all non-empty buckets. */ unsigned long long int key_count = dbSize(db);
target = key_count ? target % key_count : 0; /* Random key index in range [0..KEY_COUNT), or 0 if db is empty. */
/* In linearly enumerated key space, find a slot that contains target key. */
dbIterator dbit;
dbInitIterator(&dbit, db);
dict *d = NULL;
while ((d = dbNextDict(&dbit))) {
unsigned long long int ds = dictSize(d);
if (target < ds) { /* Found dict that contains target key. */
break;
} }
i++; target -= ds;
} }
return db->dict[r]; return d;
} }
/* Helper for sync and async delete. */ /* Helper for sync and async delete. */
...@@ -427,6 +436,7 @@ int dbGenericDelete(redisDb *db, robj *key, int async, int flags) { ...@@ -427,6 +436,7 @@ int dbGenericDelete(redisDb *db, robj *key, int async, int flags) {
* the key, because it is shared with the main dictionary. */ * the key, because it is shared with the main dictionary. */
if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr); if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);
dictTwoPhaseUnlinkFree(d,de,plink,table); dictTwoPhaseUnlinkFree(d,de,plink,table);
db->key_count--;
return 1; return 1;
} else { } else {
return 0; return 0;
......
...@@ -960,6 +960,7 @@ typedef struct redisDb { ...@@ -960,6 +960,7 @@ typedef struct redisDb {
list *rehashing; /* List of dictionaries in this DB that are currently rehashing. */ list *rehashing; /* List of dictionaries in this DB that are currently rehashing. */
int dict_count; /* Indicates total number of dictionaires owned by this DB, 1 dict per slot in cluster mode. */ int dict_count; /* Indicates total number of dictionaires owned by this DB, 1 dict per slot in cluster mode. */
int command_slot; /* Slot that is used by the current command that uses DB, -1 if cluster mode is disabled, or if command uses multiple slots. */ int command_slot; /* Slot that is used by the current command that uses DB, -1 if cluster mode is disabled, or if command uses multiple slots. */
unsigned long long key_count; /* Total number of keys in this DB. */
} redisDb; } redisDb;
/* forward declaration for functions ctx */ /* forward declaration for functions ctx */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment