Pause dict auto-resize during multi-field deletion (#14783)

The idea comes directly from ValKey:
https://github.com/valkey-io/valkey/pull/3144

Deleting many fields from a hash/zset/set stored as a dict can trigger
repeated shrink/rehash work during the loop.

---------

Co-authored-by: Binbin <binloveplay1314@qq.com>
Co-authored-by: debing.sun <debing.sun@redis.com>
This commit is contained in:
Martin Dimitrov 2026-02-13 23:45:54 -07:00 committed by GitHub
parent a9838e6a4b
commit 98328ae2ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 29 additions and 1 deletions

View file

@ -1,6 +1,9 @@
/*
* Copyright (c) 2009-Present, Redis Ltd.
* All rights reserved.
*
* Copyright (c) 2024-present, Valkey contributors.
* All rights reserved.
*
* Licensed under your choice of (a) the Redis Source Available License 2.0
* (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the
@ -2926,6 +2929,8 @@ void hdelCommand(client *c) {
* field with expiration and removes it from global HFE DS. */
int isHFE = hashTypeIsFieldsWithExpire(o);
if (o->encoding == OBJ_ENCODING_HT)
dictPauseAutoResize((dict*)o->ptr);
for (j = 2; j < c->argc; j++) {
if (hashTypeDelete(o,c->argv[j]->ptr)) {
deleted++;
@ -2939,6 +2944,10 @@ void hdelCommand(client *c) {
}
}
}
if (!keyremoved && o->encoding == OBJ_ENCODING_HT) {
dictResumeAutoResize((dict*)o->ptr);
dictShrinkIfNeeded((dict*)o->ptr);
}
if (server.memory_tracking_enabled && !keyremoved)
updateSlotAllocSize(c->db, getKeySlot(c->argv[1]->ptr), o, oldsize, kvobjAllocSize(o));
if (deleted) {

View file

@ -657,6 +657,8 @@ void sremCommand(client *c) {
if (server.memory_tracking_enabled)
oldsize = kvobjAllocSize(set);
if (set->encoding == OBJ_ENCODING_HT)
dictPauseAutoResize((dict*)set->ptr);
for (j = 2; j < c->argc; j++) {
if (setTypeRemove(set,c->argv[j]->ptr)) {
deleted++;
@ -669,6 +671,10 @@ void sremCommand(client *c) {
}
}
}
if (!keyremoved && set->encoding == OBJ_ENCODING_HT) {
dictResumeAutoResize((dict*)set->ptr);
dictShrinkIfNeeded((dict*)set->ptr);
}
if (server.memory_tracking_enabled && !keyremoved)
updateSlotAllocSize(c->db, getKeySlot(c->argv[1]->ptr), set, oldsize, kvobjAllocSize(set));
if (deleted) {

View file

@ -2118,6 +2118,8 @@ void zremCommand(client *c) {
int64_t oldlen = (int64_t) zsetLength(zobj);
if (server.memory_tracking_enabled)
oldsize = kvobjAllocSize(zobj);
if (zobj->encoding == OBJ_ENCODING_SKIPLIST)
dictPauseAutoResize(((zset*)zobj->ptr)->dict);
for (j = 2; j < c->argc; j++) {
if (zsetDel(zobj, c->argv[j]->ptr)) deleted++;
if (zsetLength(zobj) == 0) {
@ -2129,6 +2131,10 @@ void zremCommand(client *c) {
break;
}
}
if (!keyremoved && zobj->encoding == OBJ_ENCODING_SKIPLIST) {
dictResumeAutoResize(((zset*)zobj->ptr)->dict);
dictShrinkIfNeeded(((zset*)zobj->ptr)->dict);
}
if (server.memory_tracking_enabled && !keyremoved)
updateSlotAllocSize(c->db, getKeySlot(key->ptr), zobj, oldsize, kvobjAllocSize(zobj));

View file

@ -1026,7 +1026,14 @@ foreach type {single multiple single_multiple} {
break
}
}
r srem $myset {*}$members
r deferred 1
foreach m $members {
r srem $myset $m
}
foreach m $members {
r read
}
r deferred 0
}
proc verify_rehashing_completed_key {myset table_size keys} {