Some activeExpireCycle() refactoring.

This commit is contained in:
antirez 2013-08-06 12:55:49 +02:00
parent d398f38879
commit 6500fabfb8
2 changed files with 32 additions and 21 deletions

View File

@ -691,33 +691,37 @@ int activeExpireCycleTryExpire(redisDb *db, struct dictEntry *de, long long now)
* No more than REDIS_DBCRON_DBS_PER_CALL databases are tested at every * No more than REDIS_DBCRON_DBS_PER_CALL databases are tested at every
* iteration. * iteration.
* *
* If fast is non-zero the function will try to run a "fast" expire cycle that
* takes no longer than EXPIRE_FAST_CYCLE_DURATION microseconds, and is not
* repeated again before the same amount of time.
*
* This kind of call is used when Redis detects that timelimit_exit is * This kind of call is used when Redis detects that timelimit_exit is
* true, so there is more work to do, and we do it more incrementally from * true, so there is more work to do, and we do it more incrementally from
* the beforeSleep() function of the event loop. */ * the beforeSleep() function of the event loop.
*
* Expire cycle type:
*
* If type is ACTIVE_EXPIRE_CYCLE_FAST the function will try to run a
* "fast" expire cycle that takes no longer than EXPIRE_FAST_CYCLE_DURATION
* microseconds, and is not repeated again before the same amount of time.
*
* If type is ACTIVE_EXPIRE_CYCLE_SLOW, that normal expire cycle is
* executed, where the time limit is a percentage of the REDIS_HZ period
* as specified by the REDIS_EXPIRELOOKUPS_TIME_PERC define. */
#define EXPIRE_FAST_CYCLE_DURATION 1000 void activeExpireCycle(int type) {
void activeExpireCycle(int fast) {
/* This function has some global state in order to continue the work /* This function has some global state in order to continue the work
* incrementally across calls. */ * incrementally across calls. */
static unsigned int current_db = 0; /* Last DB tested. */ static unsigned int current_db = 0; /* Last DB tested. */
static int timelimit_exit = 0; /* Time limit hit in previous call? */ static int timelimit_exit = 0; /* Time limit hit in previous call? */
static long long last_fast_cycle = 0; /* When last fast cycle ran. */
unsigned int j, iteration = 0; unsigned int j, iteration = 0;
unsigned int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL; unsigned int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL;
long long start = ustime(), timelimit; long long start = ustime(), timelimit;
static long long last_fast_cycle = 0;
if (fast) { if (type == ACTIVE_EXPIRE_CYCLE_FAST) {
/* Don't start a fast cycle if the previous cycle did not exited /* Don't start a fast cycle if the previous cycle did not exited
* for time limt. Also don't repeat a fast cycle for the same period * for time limt. Also don't repeat a fast cycle for the same period
* as the fast cycle total duration itself. */ * as the fast cycle total duration itself. */
if (!timelimit_exit) return; if (!timelimit_exit) return;
if (start < last_fast_cycle + EXPIRE_FAST_CYCLE_DURATION) return; if (start < last_fast_cycle + ACTIVE_EXPIRE_CYCLE_FAST_DURATION) return;
last_fast_cycle = start; last_fast_cycle = start;
} }
@ -731,15 +735,16 @@ void activeExpireCycle(int fast) {
if (dbs_per_call > server.dbnum || timelimit_exit) if (dbs_per_call > server.dbnum || timelimit_exit)
dbs_per_call = server.dbnum; dbs_per_call = server.dbnum;
/* We can use at max REDIS_EXPIRELOOKUPS_TIME_PERC percentage of CPU time /* We can use at max ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC percentage of CPU time
* per iteration. Since this function gets called with a frequency of * per iteration. Since this function gets called with a frequency of
* server.hz times per second, the following is the max amount of * server.hz times per second, the following is the max amount of
* microseconds we can spend in this function. */ * microseconds we can spend in this function. */
timelimit = 1000000*REDIS_EXPIRELOOKUPS_TIME_PERC/server.hz/100; timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/server.hz/100;
timelimit_exit = 0; timelimit_exit = 0;
if (timelimit <= 0) timelimit = 1; if (timelimit <= 0) timelimit = 1;
if (fast) timelimit = EXPIRE_FAST_CYCLE_DURATION; /* in microseconds. */ if (type == ACTIVE_EXPIRE_CYCLE_FAST)
timelimit = ACTIVE_EXPIRE_CYCLE_FAST_DURATION; /* in microseconds. */
for (j = 0; j < dbs_per_call; j++) { for (j = 0; j < dbs_per_call; j++) {
int expired; int expired;
@ -770,8 +775,8 @@ void activeExpireCycle(int fast) {
/* The main collection cycle. Sample random keys among keys /* The main collection cycle. Sample random keys among keys
* with an expire set, checking for expired ones. */ * with an expire set, checking for expired ones. */
expired = 0; expired = 0;
if (num > REDIS_EXPIRELOOKUPS_PER_CRON) if (num > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)
num = REDIS_EXPIRELOOKUPS_PER_CRON; num = ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP;
while (num--) { while (num--) {
dictEntry *de; dictEntry *de;
@ -788,7 +793,9 @@ void activeExpireCycle(int fast) {
timelimit_exit = 1; timelimit_exit = 1;
} }
if (timelimit_exit) return; if (timelimit_exit) return;
} while (expired > REDIS_EXPIRELOOKUPS_PER_CRON/4); /* We don't repeat the cycle if there are less than 25% of keys
* found expired in the current DB. */
} while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4);
} }
} }
@ -908,7 +915,7 @@ void databasesCron(void) {
/* Expire keys by random sampling. Not required for slaves /* Expire keys by random sampling. Not required for slaves
* as master will synthesize DELs for us. */ * as master will synthesize DELs for us. */
if (server.active_expire_enabled && server.masterhost == NULL) if (server.active_expire_enabled && server.masterhost == NULL)
activeExpireCycle(0); activeExpireCycle(ACTIVE_EXPIRE_CYCLE_SLOW);
/* Perform hash tables rehashing if needed, but only if there are no /* Perform hash tables rehashing if needed, but only if there are no
* other processes saving the DB on disk. Otherwise rehashing is bad * other processes saving the DB on disk. Otherwise rehashing is bad
@ -1151,7 +1158,7 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
redisClient *c; redisClient *c;
/* Run a fast expire cycle. */ /* Run a fast expire cycle. */
activeExpireCycle(1); activeExpireCycle(ACTIVE_EXPIRE_CYCLE_FAST);
/* Try to process pending commands for clients that were just unblocked. */ /* Try to process pending commands for clients that were just unblocked. */
while (listLength(server.unblocked_clients)) { while (listLength(server.unblocked_clients)) {

View File

@ -74,8 +74,6 @@
#define REDIS_MAXIDLETIME 0 /* default client timeout: infinite */ #define REDIS_MAXIDLETIME 0 /* default client timeout: infinite */
#define REDIS_DEFAULT_DBNUM 16 #define REDIS_DEFAULT_DBNUM 16
#define REDIS_CONFIGLINE_MAX 1024 #define REDIS_CONFIGLINE_MAX 1024
#define REDIS_EXPIRELOOKUPS_PER_CRON 20 /* lookup 20 expires per loop */
#define REDIS_EXPIRELOOKUPS_TIME_PERC 25 /* CPU max % for keys collection */
#define REDIS_DBCRON_DBS_PER_CALL 16 #define REDIS_DBCRON_DBS_PER_CALL 16
#define REDIS_MAX_WRITE_PER_EVENT (1024*64) #define REDIS_MAX_WRITE_PER_EVENT (1024*64)
#define REDIS_SHARED_SELECT_CMDS 10 #define REDIS_SHARED_SELECT_CMDS 10
@ -124,6 +122,12 @@
#define REDIS_PEER_ID_LEN (REDIS_IP_STR_LEN+32) /* Must be enough for ip:port */ #define REDIS_PEER_ID_LEN (REDIS_IP_STR_LEN+32) /* Must be enough for ip:port */
#define REDIS_BINDADDR_MAX 16 #define REDIS_BINDADDR_MAX 16
#define ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 20 /* Loopkups per loop. */
#define ACTIVE_EXPIRE_CYCLE_FAST_DURATION 1000 /* Microseconds */
#define ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 25 /* CPU max % for keys collection */
#define ACTIVE_EXPIRE_CYCLE_SLOW 0
#define ACTIVE_EXPIRE_CYCLE_FAST 1
/* Protocol and I/O related defines */ /* Protocol and I/O related defines */
#define REDIS_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max query buffer. */ #define REDIS_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max query buffer. */
#define REDIS_IOBUF_LEN (1024*16) /* Generic I/O buffer size */ #define REDIS_IOBUF_LEN (1024*16) /* Generic I/O buffer size */