Repository: valkey-io/valkey
File: src/hashtable.c (C)

Code before:
    }
    return resize(ht, min_capacity, NULL);
}

/* Shrinking is done automatically on deletion, but less eagerly if resize
 * policy is set to AVOID and not at all if set to FORBID.
 * Returns true if shrinking, false if not shrinking. */
bool hashtableShrinkIfNeeded(hashtable *ht) {
    /* Don't shrink if rehashing is already in progress. */
    if (hashtableIsRehashing(ht) || ht->pause_auto_shrink) {
        return false;
    }
    size_t current_capacity = numBuckets(ht->bucket_exp[0]) * ENTRIES_PER_BUCKET;
    unsigned min_fill_percent = resize_policy == HASHTABLE_RESIZE_ALLOW ? MIN_FILL_PERCENT_SOFT : MIN_FILL_PERCENT_HARD;
    if (ht->used[0] * 100 > current_capacity * min_fill_percent) {
        return false;
    }
    return resize(ht, ht->used[0], NULL);
}

/* Check if ht[1] is overloaded during shrink and abort if necessary.
 * Returns true if shrink was aborted, false otherwise.
 *
 * This function should be called before inserting new entries during shrink
 * rehashing to prevent ht[1] from becoming severely overloaded. */
static bool abortHashtableShrinkIfNeeded(hashtable *ht) {
    /* Not allow to abort. */
    if (!hashtable_can_abort_shrink) {
        return false;
    }
    /* Only check during shrink rehashing. */
    if (!hashtableIsRehashing(ht) || ht->bucket_exp[1] >= ht->bucket_exp[0]) {
        return false;
    }
    /* It is not safe to abort while safe iterators are active. */
    if (ht->safe_iterators) {
        return false;
    }

    /* Check ht[1] fill percent. */
    size_t min_capacity = ht->used[1] + 1;
    size_t num_buckets = numBuckets(ht->bucket_exp[1]);
    size_t current_capacity = num_buckets * ENTRIES_PER_BUCKET;
    unsigned max_fill_percent = MAX_FILL_PERCENT_HARD;
    if (min_capacity * 100 <= current_capacity * max_fill_percent) {
        return false;
    }

    /* ht[1] is overloaded, swap the tables to abort the shrink. */
    bucket *table_0 = ht->tables[0];
    size_t used_0 = ht->used[0];

Review comment (suggestion):
We don't need to include "hashtable" in the name of local functions inside hashtable.c. It's implied.

We can put this code in `hashtableExpandIfNeeded` because

* It needs to be called in exactly the same places: `insert` and `hashtableFindPositionForInsert`. No need to call one more function in those places.
* Aborting shink and switching to expand mode is somehow expand-if-needed.

Code after:
 * policy is set to AVOID and not at all if set to FORBID.
 * Returns true if expanding, false if not expanding. */
bool hashtableExpandIfNeeded(hashtable *ht) {
    if (hashtableIsRehashing(ht)) {
        if (ht->bucket_exp[1] >= ht->bucket_exp[0]) {
            /* Expand already in progress. */
            return false;
        } else {
            /* Shrink in progress, check the fill percent of ht[1] to see if
             * we need to abort the shrink. */
            return abortShrinkIfNeeded(ht);
        }
    }

    /* There's no rehashing on going now, check the fill percent of ht[0] to
     * see if we need to expand. */
    size_t min_capacity = ht->used[0] + 1;
    size_t num_buckets = numBuckets(ht->bucket_exp[0]);
    size_t current_capacity = num_buckets * ENTRIES_PER_BUCKET;
    unsigned max_fill_percent = resize_policy == HASHTABLE_RESIZE_ALLOW ? MAX_FILL_PERCENT_SOFT : MAX_FILL_PERCENT_HARD;
    if (min_capacity * 100 <= current_capacity * max_fill_percent) {
        return false;
    }
    return resize(ht, min_capacity, NULL);
}

/* Shrinking is done automatically on deletion, but less eagerly if resize
 * policy is set to AVOID and not at all if set to FORBID.
 * Returns true if shrinking, false if not shrinking. */
bool hashtableShrinkIfNeeded(hashtable *ht) {
    /* Don't shrink if rehashing is already in progress. */
    if (hashtableIsRehashing(ht) || ht->pause_auto_shrink) {
        return false;
    }
    size_t current_capacity = numBuckets(ht->bucket_exp[0]) * ENTRIES_PER_BUCKET;
    unsigned min_fill_percent = resize_policy == HASHTABLE_RESIZE_ALLOW ? MIN_FILL_PERCENT_SOFT : MIN_FILL_PERCENT_HARD;
    if (ht->used[0] * 100 > current_capacity * min_fill_percent) {
        return false;
    }
    return resize(ht, ht->used[0], NULL);
}

/* Check if ht[1] will overload during the shrink and abort if necessary.
 *
 * This function should be called before inserting new entries during
 * shrink rehashing to prevent ht[1] from becoming severely overloaded.
 * Currently, it is called in hashtableExpandIfNeeded because we call
 * it every time an insertion occurs, aborting shrink and switching it
 * to expand is somehow expand-if-needed.
 *
 * Returns true if shrink was aborted, false otherwise. */