Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shrink dict when deleting dictEntry #12850

Merged
merged 14 commits into from
Jan 15, 2024
Merged
22 changes: 22 additions & 0 deletions src/dict.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
* between the number of elements and the buckets > dict_force_resize_ratio. */
static dictResizeEnable dict_can_resize = DICT_RESIZE_ENABLE;
static unsigned int dict_force_resize_ratio = 5;
static unsigned int dict_force_shrink_ratio = 2;
oranagra marked this conversation as resolved.
Show resolved Hide resolved

/* -------------------------- types ----------------------------------------- */
struct dictEntry {
Expand All @@ -79,6 +80,7 @@ typedef struct {
/* -------------------------- private prototypes ---------------------------- */

static int _dictExpandIfNeeded(dict *d);
static int _dictShrinkIfNeeded(dict *d);
static signed char _dictNextExp(unsigned long size);
static int _dictInit(dict *d, dictType *type);
static dictEntry *dictGetNext(const dictEntry *de);
Expand Down Expand Up @@ -578,6 +580,8 @@ static dictEntry *dictGenericDelete(dict *d, const void *key, int nofree) {
dictFreeUnlinkedEntry(d, he);
}
d->ht_used[table]--;
if (_dictShrinkIfNeeded(d) == DICT_ERR)
enjoy-binbin marked this conversation as resolved.
Show resolved Hide resolved
return NULL;
return he;
}
prevHe = he;
Expand Down Expand Up @@ -742,6 +746,7 @@ void dictTwoPhaseUnlinkFree(dict *d, dictEntry *he, dictEntry **plink, int table
dictFreeKey(d, he);
dictFreeVal(d, he);
if (!entryIsKey(he)) zfree(decodeMaskedPtr(he));
_dictShrinkIfNeeded(d);
dictResumeRehashing(d);
}

Expand Down Expand Up @@ -1423,6 +1428,23 @@ static int _dictExpandIfNeeded(dict *d)
return DICT_OK;
}

static int _dictShrinkIfNeeded(dict *d) {
/* Incremental rehashing already in progress. Return. */
if (dictIsRehashing(d)) return DICT_OK;

if (DICTHT_SIZE(d->ht_size_exp[0]) > DICT_HT_INITIAL_SIZE &&
oranagra marked this conversation as resolved.
Show resolved Hide resolved
((dict_can_resize == DICT_RESIZE_ENABLE &&
oranagra marked this conversation as resolved.
Show resolved Hide resolved
d->ht_used[0] * 100 / DICTHT_SIZE(d->ht_size_exp[0]) < HASHTABLE_MIN_FILL) ||
(dict_can_resize != DICT_RESIZE_FORBID &&
d->ht_used[0] * 100 / DICTHT_SIZE(d->ht_size_exp[0]) < dict_force_shrink_ratio)))
{
if (!dictTypeExpandAllowed(d))
oranagra marked this conversation as resolved.
Show resolved Hide resolved
return DICT_OK;
return dictExpand(d, d->ht_used[0]);
oranagra marked this conversation as resolved.
Show resolved Hide resolved
}
return DICT_OK;
}

/* Our hash table capability is a power of two */
static signed char _dictNextExp(unsigned long size)
{
Expand Down
3 changes: 3 additions & 0 deletions src/dict.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
#define DICT_OK 0
#define DICT_ERR 1

/* Hash table parameters */
#define HASHTABLE_MIN_FILL 10 /* Minimal hash table fill 10% */

typedef struct dictEntry dictEntry; /* opaque */
typedef struct dict dict;

Expand Down
1 change: 0 additions & 1 deletion src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,6 @@ struct hdr_histogram;
extern int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT];

/* Hash table parameters */
#define HASHTABLE_MIN_FILL 10 /* Minimal hash table fill 10% */
#define HASHTABLE_MAX_LOAD_FACTOR 1.618 /* Maximum hash table load factor. */

/* Command flags. Please check the definition of struct redisCommand in this file
Expand Down
3 changes: 0 additions & 3 deletions src/t_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -292,9 +292,6 @@ int hashTypeDelete(robj *o, sds field) {
} else if (o->encoding == OBJ_ENCODING_HT) {
if (dictDelete((dict*)o->ptr, field) == C_OK) {
deleted = 1;

/* Always check if the dictionary needs a resize after a delete. */
if (htNeedsResize(o->ptr)) dictResize(o->ptr);
}

} else {
Expand Down
1 change: 0 additions & 1 deletion src/t_set.c
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,6 @@ int setTypeRemoveAux(robj *setobj, char *str, size_t len, int64_t llval, int str
if (setobj->encoding == OBJ_ENCODING_HT) {
sds sdsval = str_is_sds ? (sds)str : sdsnewlen(str, len);
int deleted = (dictDelete(setobj->ptr, sdsval) == DICT_OK);
if (deleted && htNeedsResize(setobj->ptr)) dictResize(setobj->ptr);
enjoy-binbin marked this conversation as resolved.
Show resolved Hide resolved
if (sdsval != str) sdsfree(sdsval); /* free temp copy */
return deleted;
} else if (setobj->encoding == OBJ_ENCODING_LISTPACK) {
Expand Down
5 changes: 0 additions & 5 deletions src/t_zset.c
Original file line number Diff line number Diff line change
Expand Up @@ -1596,7 +1596,6 @@ int zsetDel(robj *zobj, sds ele) {
} else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {
zset *zs = zobj->ptr;
if (zsetRemoveFromSkiplist(zs, ele)) {
if (htNeedsResize(zs->dict)) dictResize(zs->dict);
return 1;
}
} else {
Expand Down Expand Up @@ -2023,7 +2022,6 @@ void zremrangeGenericCommand(client *c, zrange_type rangetype) {
deleted = zslDeleteRangeByLex(zs->zsl,&lexrange,zs->dict);
break;
}
if (htNeedsResize(zs->dict)) dictResize(zs->dict);
oranagra marked this conversation as resolved.
Show resolved Hide resolved
if (dictSize(zs->dict) == 0) {
dbDelete(c->db,key);
keyremoved = 1;
Expand Down Expand Up @@ -2550,9 +2548,6 @@ static void zdiffAlgorithm2(zsetopsrc *src, long setnum, zset *dstzset, size_t *
if (cardinality == 0) break;
}

/* Resize dict if needed after removing multiple elements */
if (htNeedsResize(dstzset->dict)) dictResize(dstzset->dict);

oranagra marked this conversation as resolved.
Show resolved Hide resolved
/* Using this algorithm, we can't calculate the max element as we go,
* we have to iterate through all elements to find the max one after. */
*maxelelen = zsetDictGetMaxElementLength(dstzset->dict, totelelen);
Expand Down