From 2d5eb1f1a03b9109137e2fbfe8df488a09e4cd6c Mon Sep 17 00:00:00 2001 From: antirez Date: Wed, 20 Jul 2016 19:53:27 +0200 Subject: [PATCH] Volatile-ttl eviction policy implemented in terms of the pool. Precision of the eviction improved sensibly. Also this allows us to have a single code path for most eviction types. --- src/evict.c | 65 ++++++++++++++++------------------------------------ src/server.h | 8 ++++--- 2 files changed, 25 insertions(+), 48 deletions(-) diff --git a/src/evict.c b/src/evict.c index 4a4ba2ea..802997ce 100644 --- a/src/evict.c +++ b/src/evict.c @@ -159,14 +159,21 @@ void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evic de = samples[j]; key = dictGetKey(de); + /* If the dictionary we are sampling from is not the main * dictionary (but the expires one) we need to lookup the key * again in the key dictionary to obtain the value object. */ - if (sampledict != keydict) de = dictFind(keydict, key); - o = dictGetVal(de); + if (server.maxmemory_policy != MAXMEMORY_VOLATILE_TTL) { + if (sampledict != keydict) de = dictFind(keydict, key); + o = dictGetVal(de); + } + + /* Calculate the idle time according to the policy. This is called + * idle just because the code initially handled LRU, but is in fact + * just a score where an higher score means better candidate. */ if (server.maxmemory_policy & MAXMEMORY_FLAG_LRU) { idle = estimateObjectIdleTime(o); - } else { + } else if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) { /* When we use an LRU policy, we sort the keys by idle time * so that we expire keys starting from greater idle time. * However when the policy is an LFU one, we have a frequency @@ -175,6 +182,11 @@ void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evic * frequency subtracting the actual frequency to the maximum * frequency of 255. */ idle = 255-LFUDecrAndReturn(o); + } else if (server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL) { + /* In this case the sooner the expire the better. */ + idle = ULLONG_MAX - (long)dictGetVal(de); + } else { + serverPanic("Unknown eviction policy in evictionPoolPopulate()"); } /* Insert the element inside the pool. @@ -377,7 +389,9 @@ int freeMemoryIfNeeded(void) { dict *dict; dictEntry *de; - if (server.maxmemory_policy & (MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU)) { + if (server.maxmemory_policy & (MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU) || + server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL) + { struct evictionPoolEntry *pool = EvictionPoolLRU; while(bestkey == NULL) { @@ -388,8 +402,7 @@ int freeMemoryIfNeeded(void) { * every DB. */ for (i = 0; i < server.dbnum; i++) { db = server.db+i; - dict = (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU || - server.maxmemory_policy == MAXMEMORY_ALLKEYS_LFU) ? + dict = (server.maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) ? db->dict : db->expires; if ((keys = dictSize(dict)) != 0) { evictionPoolPopulate(i, dict, db->dict, pool); @@ -403,9 +416,7 @@ int freeMemoryIfNeeded(void) { if (pool[k].key == NULL) continue; bestdbid = pool[k].dbid; - if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU || - server.maxmemory_policy == MAXMEMORY_ALLKEYS_LFU) - { + if (server.maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) { de = dictFind(server.db[pool[k].dbid].dict, pool[k].key); } else { @@ -452,42 +463,6 @@ int freeMemoryIfNeeded(void) { } } - /* volatile-ttl */ - else if (server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL) { - long bestttl = 0; /* Initialized to avoid warning. */ - - /* In this policy we scan a single DB per iteration (visiting - * a different DB per call), expiring the key with the smallest - * TTL among the few sampled. - * - * Note that this algorithm makes local-DB choices, and should - * use a pool and code more similr to the one used in the - * LRU eviction policies in the future. */ - for (i = 0; i < server.dbnum; i++) { - j = (++next_db) % server.dbnum; - db = server.db+j; - dict = db->expires; - if (dictSize(dict) != 0) { - for (k = 0; k < server.maxmemory_samples; k++) { - sds thiskey; - long thisttl; - - de = dictGetRandomKey(dict); - thiskey = dictGetKey(de); - thisttl = (long) dictGetVal(de); - - /* Keys expiring sooner (smaller unix timestamp) are - * better candidates for deletion */ - if (bestkey == NULL || thisttl < bestttl) { - bestkey = thiskey; - bestttl = thisttl; - bestdbid = j; - } - } - } - } - } - /* Finally remove the selected key. */ if (bestkey) { db = server.db+bestdbid; diff --git a/src/server.h b/src/server.h index 266c5336..4e34453e 100644 --- a/src/server.h +++ b/src/server.h @@ -348,15 +348,17 @@ typedef long long mstime_t; /* millisecond time type. */ * properties common to multiple policies is faster. */ #define MAXMEMORY_FLAG_LRU (1<<0) #define MAXMEMORY_FLAG_LFU (1<<1) +#define MAXMEMORY_FLAG_ALLKEYS (1<<2) #define MAXMEMORY_FLAG_NO_SHARED_INTEGERS \ (MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU) + #define MAXMEMORY_VOLATILE_LRU ((0<<8)|MAXMEMORY_FLAG_LRU) #define MAXMEMORY_VOLATILE_LFU ((1<<8)|MAXMEMORY_FLAG_LFU) #define MAXMEMORY_VOLATILE_TTL (2<<8) #define MAXMEMORY_VOLATILE_RANDOM (3<<8) -#define MAXMEMORY_ALLKEYS_LRU ((4<<8)|MAXMEMORY_FLAG_LRU) -#define MAXMEMORY_ALLKEYS_LFU ((5<<8)|MAXMEMORY_FLAG_LFU) -#define MAXMEMORY_ALLKEYS_RANDOM (6<<8) +#define MAXMEMORY_ALLKEYS_LRU ((4<<8)|MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_ALLKEYS) +#define MAXMEMORY_ALLKEYS_LFU ((5<<8)|MAXMEMORY_FLAG_LFU|MAXMEMORY_FLAG_ALLKEYS) +#define MAXMEMORY_ALLKEYS_RANDOM ((6<<8)|MAXMEMORY_FLAG_ALLKEYS) #define MAXMEMORY_NO_EVICTION (7<<8) #define CONFIG_DEFAULT_MAXMEMORY_POLICY MAXMEMORY_NO_EVICTION