summaryrefslogtreecommitdiff
path: root/src/dict.c
diff options
context:
space:
mode:
authorOran Agra <oran@redislabs.com>2023-01-10 08:40:40 +0200
committerGitHub <noreply@github.com>2023-01-10 08:40:40 +0200
commit2bec254d89ff9266b2e688b15e34a3498da7c92c (patch)
treefdd239369e4e847693c9e43459a252e52b8d88e2 /src/dict.c
parenteef29b68a2cd94de1f03aa1b7891af75f5cabae2 (diff)
downloadredis-2bec254d89ff9266b2e688b15e34a3498da7c92c.tar.gz
Make sure that fork child doesn't do incremental rehashing (#11692)
Turns out that a fork child calling getExpire while persisting keys (and possibly also a result of some module fork tasks) could cause dictFind to do incremental rehashing in the child process, which is both a waste of time, and also causes COW harm.
Diffstat (limited to 'src/dict.c')
-rw-r--r--src/dict.c33
1 files changed, 18 insertions, 15 deletions
diff --git a/src/dict.c b/src/dict.c
index 7fecb2daa..90fd4f52e 100644
--- a/src/dict.c
+++ b/src/dict.c
@@ -47,15 +47,15 @@
#include "zmalloc.h"
#include "redisassert.h"
-/* Using dictEnableResize() / dictDisableResize() we make possible to
- * enable/disable resizing of the hash table as needed. This is very important
+/* Using dictEnableResize() / dictDisableResize() we make possible to disable
+ * resizing and rehashing of the hash table as needed. This is very important
* for Redis, as we use copy-on-write and don't want to move too much memory
* around when there is a child performing saving operations.
*
* Note that even when dict_can_resize is set to 0, not all resizes are
* prevented: a hash table is still allowed to grow if the ratio between
* the number of elements and the buckets > dict_force_resize_ratio. */
-static int dict_can_resize = 1;
+static dictResizeEnable dict_can_resize = DICT_RESIZE_ENABLE;
static unsigned int dict_force_resize_ratio = 5;
/* -------------------------- private prototypes ---------------------------- */
@@ -127,7 +127,7 @@ int dictResize(dict *d)
{
unsigned long minimal;
- if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;
+ if (dict_can_resize != DICT_RESIZE_ENABLE || dictIsRehashing(d)) return DICT_ERR;
minimal = d->ht_used[0];
if (minimal < DICT_HT_INITIAL_SIZE)
minimal = DICT_HT_INITIAL_SIZE;
@@ -210,7 +210,12 @@ int dictTryExpand(dict *d, unsigned long size) {
* work it does would be unbound and the function may block for a long time. */
int dictRehash(dict *d, int n) {
int empty_visits = n*10; /* Max number of empty buckets to visit. */
- if (!dictIsRehashing(d)) return 0;
+ if (dict_can_resize == DICT_RESIZE_FORBID || !dictIsRehashing(d)) return 0;
+ if (dict_can_resize == DICT_RESIZE_AVOID &&
+ (DICTHT_SIZE(d->ht_size_exp[1]) / DICTHT_SIZE(d->ht_size_exp[0]) < dict_force_resize_ratio))
+ {
+ return 0;
+ }
while(n-- && d->ht_used[0] != 0) {
dictEntry *de, *nextde;
@@ -1072,10 +1077,12 @@ static int _dictExpandIfNeeded(dict *d)
* table (global setting) or we should avoid it but the ratio between
* elements/buckets is over the "safe" threshold, we resize doubling
* the number of buckets. */
- if (d->ht_used[0] >= DICTHT_SIZE(d->ht_size_exp[0]) &&
- (dict_can_resize ||
- d->ht_used[0]/ DICTHT_SIZE(d->ht_size_exp[0]) > dict_force_resize_ratio) &&
- dictTypeExpandAllowed(d))
+ if (!dictTypeExpandAllowed(d))
+ return DICT_OK;
+ if ((dict_can_resize == DICT_RESIZE_ENABLE &&
+ d->ht_used[0] >= DICTHT_SIZE(d->ht_size_exp[0])) ||
+ (dict_can_resize != DICT_RESIZE_FORBID &&
+ d->ht_used[0] / DICTHT_SIZE(d->ht_size_exp[0]) > dict_force_resize_ratio))
{
return dictExpand(d, d->ht_used[0] + 1);
}
@@ -1135,12 +1142,8 @@ void dictEmpty(dict *d, void(callback)(dict*)) {
d->pauserehash = 0;
}
-void dictEnableResize(void) {
- dict_can_resize = 1;
-}
-
-void dictDisableResize(void) {
- dict_can_resize = 0;
+void dictSetResizeEnabled(dictResizeEnable enable) {
+ dict_can_resize = enable;
}
uint64_t dictGetHash(dict *d, const void *key) {