mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 16:18:28 -05:00
Object approximated LRU algorithm enhanced / fixed / refactored. This is used for the VM currently but will soon be used for maxmemory expiring.
This commit is contained in:
parent
b4f2e412d0
commit
ef59a8bc9e
5
src/db.c
5
src/db.c
@ -11,6 +11,9 @@ robj *lookupKey(redisDb *db, robj *key) {
|
|||||||
if (de) {
|
if (de) {
|
||||||
robj *val = dictGetEntryVal(de);
|
robj *val = dictGetEntryVal(de);
|
||||||
|
|
||||||
|
/* Update the access time for the aging algorithm. */
|
||||||
|
val->lru = server.lruclock;
|
||||||
|
|
||||||
if (server.vm_enabled) {
|
if (server.vm_enabled) {
|
||||||
if (val->storage == REDIS_VM_MEMORY ||
|
if (val->storage == REDIS_VM_MEMORY ||
|
||||||
val->storage == REDIS_VM_SWAPPING)
|
val->storage == REDIS_VM_SWAPPING)
|
||||||
@ -18,8 +21,6 @@ robj *lookupKey(redisDb *db, robj *key) {
|
|||||||
/* If we were swapping the object out, cancel the operation */
|
/* If we were swapping the object out, cancel the operation */
|
||||||
if (val->storage == REDIS_VM_SWAPPING)
|
if (val->storage == REDIS_VM_SWAPPING)
|
||||||
vmCancelThreadedIOJob(val);
|
vmCancelThreadedIOJob(val);
|
||||||
/* Update the access time for the aging algorithm. */
|
|
||||||
val->lru = server.lruclock;
|
|
||||||
} else {
|
} else {
|
||||||
int notify = (val->storage == REDIS_VM_LOADING);
|
int notify = (val->storage == REDIS_VM_LOADING);
|
||||||
|
|
||||||
|
@ -213,9 +213,11 @@ void debugCommand(redisClient *c) {
|
|||||||
strenc = strEncoding(val->encoding);
|
strenc = strEncoding(val->encoding);
|
||||||
addReplyStatusFormat(c,
|
addReplyStatusFormat(c,
|
||||||
"Value at:%p refcount:%d "
|
"Value at:%p refcount:%d "
|
||||||
"encoding:%s serializedlength:%lld",
|
"encoding:%s serializedlength:%lld "
|
||||||
|
"lru :%d lru_seconds_idle:%lu",
|
||||||
(void*)val, val->refcount,
|
(void*)val, val->refcount,
|
||||||
strenc, (long long) rdbSavedObjectLen(val,NULL));
|
strenc, (long long) rdbSavedObjectLen(val,NULL),
|
||||||
|
val->lru, estimateObjectIdleTime(val));
|
||||||
} else {
|
} else {
|
||||||
vmpointer *vp = (vmpointer*) val;
|
vmpointer *vp = (vmpointer*) val;
|
||||||
addReplyStatusFormat(c,
|
addReplyStatusFormat(c,
|
||||||
|
31
src/object.c
31
src/object.c
@ -19,14 +19,19 @@ robj *createObject(int type, void *ptr) {
|
|||||||
o->encoding = REDIS_ENCODING_RAW;
|
o->encoding = REDIS_ENCODING_RAW;
|
||||||
o->ptr = ptr;
|
o->ptr = ptr;
|
||||||
o->refcount = 1;
|
o->refcount = 1;
|
||||||
if (server.vm_enabled) {
|
/* Set the LRU to the current lruclock (minutes resolution).
|
||||||
/* Note that this code may run in the context of an I/O thread
|
* We do this regardless of the fact VM is active as LRU is also
|
||||||
* and accessing server.lruclock in theory is an error
|
* used for the maxmemory directive when Redis is used as cache.
|
||||||
* (no locks). But in practice this is safe, and even if we read
|
*
|
||||||
* garbage Redis will not fail. */
|
* Note that this code may run in the context of an I/O thread
|
||||||
o->lru = server.lruclock;
|
* and accessing server.lruclock in theory is an error
|
||||||
o->storage = REDIS_VM_MEMORY;
|
* (no locks). But in practice this is safe, and even if we read
|
||||||
}
|
* garbage Redis will not fail. */
|
||||||
|
o->lru = server.lruclock;
|
||||||
|
/* The following is only needed if VM is active, but since the conditional
|
||||||
|
* is probably more costly than initializing the field it's better to
|
||||||
|
* have every field properly initialized anyway. */
|
||||||
|
o->storage = REDIS_VM_MEMORY;
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,3 +438,13 @@ char *strEncoding(int encoding) {
|
|||||||
default: return "unknown";
|
default: return "unknown";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Given an object returns the min number of seconds the object was never
|
||||||
|
* requested, using an approximated LRU algorithm. */
|
||||||
|
unsigned long estimateObjectIdleTime(robj *o) {
|
||||||
|
if (server.lruclock >= o->lru) {
|
||||||
|
return (server.lruclock - o->lru) * 60;
|
||||||
|
} else {
|
||||||
|
return ((REDIS_LRU_CLOCK_MAX - o->lru) + server.lruclock) * 60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
18
src/redis.c
18
src/redis.c
@ -490,19 +490,15 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
|
|||||||
* in objects at every object access, and accuracy is not needed.
|
* in objects at every object access, and accuracy is not needed.
|
||||||
* To access a global var is faster than calling time(NULL) */
|
* To access a global var is faster than calling time(NULL) */
|
||||||
server.unixtime = time(NULL);
|
server.unixtime = time(NULL);
|
||||||
/* We have just 21 bits per object for LRU information.
|
/* We have just 22 bits per object for LRU information.
|
||||||
* So we use an (eventually wrapping) LRU clock with minutes resolution.
|
* So we use an (eventually wrapping) LRU clock with minutes resolution.
|
||||||
|
* 2^22 minutes are more than 7 years.
|
||||||
*
|
*
|
||||||
* When we need to select what object to swap, we compute the minimum
|
* Note that even if this will wrap after 7 years it's not a problem,
|
||||||
* time distance between the current lruclock and the object last access
|
* everything will still work but just some object will appear younger
|
||||||
* lruclock info. Even if clocks will wrap on overflow, there is
|
* to Redis :)
|
||||||
* the interesting property that we are sure that at least
|
|
||||||
* ABS(A-B) minutes passed between current time and timestamp B.
|
|
||||||
*
|
|
||||||
* This is not precise but we don't need at all precision, but just
|
|
||||||
* something statistically reasonable.
|
|
||||||
*/
|
*/
|
||||||
server.lruclock = (time(NULL)/60)&((1<<21)-1);
|
server.lruclock = (time(NULL)/60) & REDIS_LRU_CLOCK_MAX;
|
||||||
|
|
||||||
/* We received a SIGTERM, shutting down here in a safe way, as it is
|
/* We received a SIGTERM, shutting down here in a safe way, as it is
|
||||||
* not ok doing so inside the signal handler. */
|
* not ok doing so inside the signal handler. */
|
||||||
@ -1165,6 +1161,7 @@ sds genRedisInfoString(void) {
|
|||||||
"process_id:%ld\r\n"
|
"process_id:%ld\r\n"
|
||||||
"uptime_in_seconds:%ld\r\n"
|
"uptime_in_seconds:%ld\r\n"
|
||||||
"uptime_in_days:%ld\r\n"
|
"uptime_in_days:%ld\r\n"
|
||||||
|
"lru_clock:%ld\r\n"
|
||||||
"used_cpu_sys:%.2f\r\n"
|
"used_cpu_sys:%.2f\r\n"
|
||||||
"used_cpu_user:%.2f\r\n"
|
"used_cpu_user:%.2f\r\n"
|
||||||
"used_cpu_sys_childrens:%.2f\r\n"
|
"used_cpu_sys_childrens:%.2f\r\n"
|
||||||
@ -1196,6 +1193,7 @@ sds genRedisInfoString(void) {
|
|||||||
(long) getpid(),
|
(long) getpid(),
|
||||||
uptime,
|
uptime,
|
||||||
uptime/(3600*24),
|
uptime/(3600*24),
|
||||||
|
(unsigned long) server.lruclock,
|
||||||
(float)self_ru.ru_utime.tv_sec+(float)self_ru.ru_utime.tv_usec/1000000,
|
(float)self_ru.ru_utime.tv_sec+(float)self_ru.ru_utime.tv_usec/1000000,
|
||||||
(float)self_ru.ru_stime.tv_sec+(float)self_ru.ru_stime.tv_usec/1000000,
|
(float)self_ru.ru_stime.tv_sec+(float)self_ru.ru_stime.tv_usec/1000000,
|
||||||
(float)c_ru.ru_utime.tv_sec+(float)c_ru.ru_utime.tv_usec/1000000,
|
(float)c_ru.ru_utime.tv_sec+(float)c_ru.ru_utime.tv_usec/1000000,
|
||||||
|
@ -211,6 +211,7 @@ void _redisPanic(char *msg, char *file, int line);
|
|||||||
/* A redis object, that is a type able to hold a string / list / set */
|
/* A redis object, that is a type able to hold a string / list / set */
|
||||||
|
|
||||||
/* The actual Redis Object */
|
/* The actual Redis Object */
|
||||||
|
#define REDIS_LRU_CLOCK_MAX ((1<<21)-1) /* Max value of obj->lru */
|
||||||
typedef struct redisObject {
|
typedef struct redisObject {
|
||||||
unsigned type:4;
|
unsigned type:4;
|
||||||
unsigned storage:2; /* REDIS_VM_MEMORY or REDIS_VM_SWAPPING */
|
unsigned storage:2; /* REDIS_VM_MEMORY or REDIS_VM_SWAPPING */
|
||||||
@ -678,6 +679,7 @@ int getLongLongFromObject(robj *o, long long *target);
|
|||||||
char *strEncoding(int encoding);
|
char *strEncoding(int encoding);
|
||||||
int compareStringObjects(robj *a, robj *b);
|
int compareStringObjects(robj *a, robj *b);
|
||||||
int equalStringObjects(robj *a, robj *b);
|
int equalStringObjects(robj *a, robj *b);
|
||||||
|
unsigned long estimateObjectIdleTime(robj *o);
|
||||||
|
|
||||||
/* Replication */
|
/* Replication */
|
||||||
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc);
|
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc);
|
||||||
|
2
src/vm.c
2
src/vm.c
@ -362,7 +362,7 @@ robj *vmPreviewObject(robj *o) {
|
|||||||
double computeObjectSwappability(robj *o) {
|
double computeObjectSwappability(robj *o) {
|
||||||
/* actual age can be >= minage, but not < minage. As we use wrapping
|
/* actual age can be >= minage, but not < minage. As we use wrapping
|
||||||
* 21 bit clocks with minutes resolution for the LRU. */
|
* 21 bit clocks with minutes resolution for the LRU. */
|
||||||
time_t minage = abs(server.lruclock - o->lru);
|
time_t minage = estimateObjectIdleTime(o);
|
||||||
long asize = 0, elesize;
|
long asize = 0, elesize;
|
||||||
robj *ele;
|
robj *ele;
|
||||||
list *l;
|
list *l;
|
||||||
|
Loading…
Reference in New Issue
Block a user