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

Code before:
}

static long long activeExpireCycleJob(enum activeExpiryType jobType, int cycleType, long long timelimit_us) {
    if (timelimit_us <= 0) return 0;

    unsigned long config_cycle_acceptable_stale = ACTIVE_EXPIRE_CYCLE_ACCEPTABLE_STALE - activeExpireEffort();
    unsigned long keys_per_loop =
        ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP + ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP / 4 * activeExpireEffort();

    /* This function has some global state in order to continue the work
     * incrementally across calls. */
    typedef struct {
        unsigned int current_db; /* Next DB to test. */
        bool timelimit_exit;     /* Time limit hit in previous call? */
    } expireState;
    static expireState _expire_state[ACTIVE_EXPIRY_TYPE_COUNT] = {0}; // [KEYS, FIELDS]
    expireState *state = &_expire_state[jobType];
    double *expired_stale_perc[ACTIVE_EXPIRY_TYPE_COUNT] = {
        &server.stat_expired_keys_stale_perc,
        &server.stat_expired_keys_with_vola_stale_perc,
    };

    int j, iteration = 0;
    int dbs_per_call = CRON_DBS_PER_CALL;
    int dbs_performed = 0;
    int time_check_rate; /* Check time limit every 1/Xth of the loop. */
    monotime start = getMonotonicUs();

    if (cycleType == ACTIVE_EXPIRE_CYCLE_FAST) {
        /* Don't start a fast cycle if the previous cycle did not exit
         * for time limit, unless the percentage of estimated stale keys is
         * too high. */
        if (!state->timelimit_exit && *expired_stale_perc[jobType] < config_cycle_acceptable_stale) return 0;
    }

    /* We usually should test CRON_DBS_PER_CALL per iteration, with
     * two exceptions:
     *
     * 1) Don't test more DBs than we have.
     * 2) If last time we hit the time limit, we want to scan all DBs
     * in this iteration, as there is work to do in some DB and we don't want
     * expired keys to use memory for too much time. */
    if (dbs_per_call > server.dbnum || state->timelimit_exit) dbs_per_call = server.dbnum;

    state->timelimit_exit = false;

    /* Accumulate some global stats as we expire keys, to have some idea
     * about the number of keys that are already logically expired, but still
     * existing inside the database. */
    long total_sampled = 0;
    long total_expired = 0;

Review comment (suggestion):
Looking at the code below seeing 0x0, I thought it will be a division by zero, but it's actually this comment being slightly off.

We use it as a mask. We check the time every X + 1 iteration, to be exact.
Should we call it mask instead?

```suggestion
    int time_check_mask; /* Check time limit when (i & mask) == 0, i.e. every (X+1)th of the loop. */
```

Code after:
}

static long long activeExpireCycleJob(enum activeExpiryType jobType, int cycleType, long long timelimit_us) {
    if (timelimit_us <= 0) return 0;

    unsigned long config_cycle_acceptable_stale = ACTIVE_EXPIRE_CYCLE_ACCEPTABLE_STALE - activeExpireEffort();
    unsigned long keys_per_loop =
        ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP + ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP / 4 * activeExpireEffort();

    /* This function has some global state in order to continue the work
     * incrementally across calls. */
    typedef struct {
        unsigned int current_db; /* Next DB to test. */
        bool timelimit_exit;     /* Time limit hit in previous call? */
    } expireState;
    static expireState _expire_state[ACTIVE_EXPIRY_TYPE_COUNT] = {0}; // [KEYS, FIELDS]
    expireState *state = &_expire_state[jobType];
    double *expired_stale_perc[ACTIVE_EXPIRY_TYPE_COUNT] = {
        &server.stat_expired_keys_stale_perc,
        &server.stat_expired_keys_with_vola_stale_perc,
    };

    int j, iteration = 0;
    int dbs_per_call = CRON_DBS_PER_CALL;
    int dbs_performed = 0;
    int time_check_mask; /* Check time limit when (i & mask) == 0, i.e. every (X+1)th of the loop. */
    monotime start = getMonotonicUs();

    if (cycleType == ACTIVE_EXPIRE_CYCLE_FAST) {
        /* Don't start a fast cycle if the previous cycle did not exit
         * for time limit, unless the percentage of estimated stale keys is
         * too high. */
        if (!state->timelimit_exit && *expired_stale_perc[jobType] < config_cycle_acceptable_stale) return 0;
    }

    /* We usually should test CRON_DBS_PER_CALL per iteration, with
     * two exceptions:
     *
     * 1) Don't test more DBs than we have.
     * 2) If last time we hit the time limit, we want to scan all DBs
     * in this iteration, as there is work to do in some DB and we don't want
     * expired keys to use memory for too much time. */
    if (dbs_per_call > server.dbnum || state->timelimit_exit) dbs_per_call = server.dbnum;

    state->timelimit_exit = false;

    /* Accumulate some global stats as we expire keys, to have some idea
     * about the number of keys that are already logically expired, but still
     * existing inside the database. */
    long total_sampled = 0;
    long total_expired = 0;