diff --git a/src/redis.c b/src/redis.c index 7a563b16..1ddf2eef 100644 --- a/src/redis.c +++ b/src/redis.c @@ -648,19 +648,31 @@ void updateDictResizePolicy(void) { * No more than REDIS_DBCRON_DBS_PER_CALL databases are tested at every * iteration. */ void activeExpireCycle(void) { - static unsigned int current_db = 0; + /* This function has some global state in order to continue the work + * incrementally across calls. */ + static unsigned int current_db = 0; /* Last DB tested. */ + static int timelimit_exit = 0; /* Time limit hit in previous call? */ + unsigned int j, iteration = 0; unsigned int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL; long long start = ustime(), timelimit; - /* Don't test more DBs than we have. */ - if (dbs_per_call > server.dbnum) dbs_per_call = server.dbnum; + /* We usually should test REDIS_DBCRON_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 || timelimit_exit) + dbs_per_call = server.dbnum; /* We can use at max REDIS_EXPIRELOOKUPS_TIME_PERC percentage of CPU time * per iteration. Since this function gets called with a frequency of * server.hz times per second, the following is the max amount of * microseconds we can spend in this function. */ timelimit = 1000000*REDIS_EXPIRELOOKUPS_TIME_PERC/server.hz/100; + timelimit_exit = 0; if (timelimit <= 0) timelimit = 1; for (j = 0; j < dbs_per_call; j++) { @@ -718,7 +730,11 @@ void activeExpireCycle(void) { * caller waiting for the other active expire cycle. */ iteration++; if ((iteration & 0xf) == 0 && /* check once every 16 iterations. */ - (ustime()-start) > timelimit) return; + (ustime()-start) > timelimit) + { + timelimit_exit = 1; + return; + } } while (expired > REDIS_EXPIRELOOKUPS_PER_CRON/4); } }