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

Code before:
                     * We just use the current estimate with a weight of 2%
                     * and the previous estimate with a weight of 98%. */
                    if (db->expiry[jobType].avg_ttl == 0) {
                        db->expiry[jobType].avg_ttl = avg_ttl;
                    } else {
                        /* The origin code is as follow.
                         * for (int i = 0; i < update_avg_ttl_times; i++) {
                         *   db->avg_ttl = (db->avg_ttl/50)*49 + (avg_ttl/50);
                         * }
                         * We can convert the loop into a sum of a geometric progression.
                         * db->avg_ttl = db->avg_ttl * pow(0.98, update_avg_ttl_times) +
                         *                  avg_ttl / 50 * (pow(0.98, update_avg_ttl_times - 1) + ... + 1)
                         *             = db->avg_ttl * pow(0.98, update_avg_ttl_times) +
                         *                  avg_ttl * (1 - pow(0.98, update_avg_ttl_times))
                         *             = avg_ttl +  (db->avg_ttl - avg_ttl) * pow(0.98, update_avg_ttl_times)
                         * Notice that update_avg_ttl_times is between 1 and 16, we use a constant table
                         * to accelerate the calculation of pow(0.98, update_avg_ttl_times).*/
                        db->expiry[jobType].avg_ttl = avg_ttl + (db->expiry[jobType].avg_ttl - avg_ttl) * avg_ttl_factor[update_avg_ttl_times - 1];
                    }
                    update_avg_ttl_times = 0;
                    data.ttl_sum = 0;
                    data.ttl_samples = 0;
                }
            }
            /* check time limit for every FIELDS job iteration or every 16 iterations for KEYS. */
            if ((iteration & time_check_rate) == 0) {
                if (elapsedUs(start) > (uint64_t)timelimit_us) {
                    state->timelimit_exit = 1;
                    server.stat_expired_time_cap_reached_count++;
                    break;
                }
            }
        } while (repeat);
    }

    long long elapsed = (long long)elapsedUs(start);
    if (jobType == KEYS) {
        latencyTraceIfNeeded(db, expire_cycle_keys, elapsed);
    } else if (jobType == FIELDS) {
        latencyTraceIfNeeded(db, expire_cycle_fields, elapsed);
    }

    /* Update our estimate of keys existing but yet to be expired.
     * Running average with this sample accounting for 5%. */
    double current_perc;
    if (total_sampled) {
        current_perc = (double)total_expired / total_sampled;
    } else
        current_perc = 0;
    *expired_stale_perc[jobType] = (current_perc * 0.05) + (*expired_stale_perc[jobType] * 0.95);


Review comment (suggestion):
```suggestion
            if ((iteration & time_check_mask) == 0) {
```

Code after:
                     * We just use the current estimate with a weight of 2%
                     * and the previous estimate with a weight of 98%. */
                    if (db->expiry[jobType].avg_ttl == 0) {
                        db->expiry[jobType].avg_ttl = avg_ttl;
                    } else {
                        /* The origin code is as follow.
                         * for (int i = 0; i < update_avg_ttl_times; i++) {
                         *   db->avg_ttl = (db->avg_ttl/50)*49 + (avg_ttl/50);
                         * }
                         * We can convert the loop into a sum of a geometric progression.
                         * db->avg_ttl = db->avg_ttl * pow(0.98, update_avg_ttl_times) +
                         *                  avg_ttl / 50 * (pow(0.98, update_avg_ttl_times - 1) + ... + 1)
                         *             = db->avg_ttl * pow(0.98, update_avg_ttl_times) +
                         *                  avg_ttl * (1 - pow(0.98, update_avg_ttl_times))
                         *             = avg_ttl +  (db->avg_ttl - avg_ttl) * pow(0.98, update_avg_ttl_times)
                         * Notice that update_avg_ttl_times is between 1 and 16, we use a constant table
                         * to accelerate the calculation of pow(0.98, update_avg_ttl_times).*/
                        db->expiry[jobType].avg_ttl = avg_ttl + (db->expiry[jobType].avg_ttl - avg_ttl) * avg_ttl_factor[update_avg_ttl_times - 1];
                    }
                    update_avg_ttl_times = 0;
                    data.ttl_sum = 0;
                    data.ttl_samples = 0;
                }
            }
            /* check time limit for every FIELDS job iteration or every 16 iterations for KEYS. */
            if ((iteration & time_check_mask) == 0) {
                if (elapsedUs(start) > (uint64_t)timelimit_us) {
                    state->timelimit_exit = 1;
                    server.stat_expired_time_cap_reached_count++;
                    break;
                }
            }
        } while (repeat);
    }

    long long elapsed = (long long)elapsedUs(start);
    if (jobType == KEYS) {
        latencyTraceIfNeeded(db, expire_cycle_keys, elapsed);
    } else if (jobType == FIELDS) {
        latencyTraceIfNeeded(db, expire_cycle_fields, elapsed);
    }

    /* Update our estimate of keys existing but yet to be expired.
     * Running average with this sample accounting for 5%. */
    double current_perc;
    if (total_sampled) {
        current_perc = (double)total_expired / total_sampled;
    } else
        current_perc = 0;
    *expired_stale_perc[jobType] = (current_perc * 0.05) + (*expired_stale_perc[jobType] * 0.95);