if (de) {
robj *val = dictGetEntryVal(de);
+ /* Update the access time for the aging algorithm. */
+ val->lru = server.lruclock;
+
if (server.vm_enabled) {
if (val->storage == REDIS_VM_MEMORY ||
val->storage == REDIS_VM_SWAPPING)
/* If we were swapping the object out, cancel the operation */
if (val->storage == REDIS_VM_SWAPPING)
vmCancelThreadedIOJob(val);
- /* Update the access time for the aging algorithm. */
- val->lru = server.lruclock;
} else {
int notify = (val->storage == REDIS_VM_LOADING);
strenc = strEncoding(val->encoding);
addReplyStatusFormat(c,
"Value at:%p refcount:%d "
- "encoding:%s serializedlength:%lld",
+ "encoding:%s serializedlength:%lld "
+ "lru :%d lru_seconds_idle:%lu",
(void*)val, val->refcount,
- strenc, (long long) rdbSavedObjectLen(val,NULL));
+ strenc, (long long) rdbSavedObjectLen(val,NULL),
+ val->lru, estimateObjectIdleTime(val));
} else {
vmpointer *vp = (vmpointer*) val;
addReplyStatusFormat(c,
o->encoding = REDIS_ENCODING_RAW;
o->ptr = ptr;
o->refcount = 1;
- if (server.vm_enabled) {
- /* Note that this code may run in the context of an I/O thread
- * and accessing server.lruclock in theory is an error
- * (no locks). But in practice this is safe, and even if we read
- * garbage Redis will not fail. */
- o->lru = server.lruclock;
- o->storage = REDIS_VM_MEMORY;
- }
+ /* Set the LRU to the current lruclock (minutes resolution).
+ * We do this regardless of the fact VM is active as LRU is also
+ * used for the maxmemory directive when Redis is used as cache.
+ *
+ * Note that this code may run in the context of an I/O thread
+ * and accessing server.lruclock in theory is an error
+ * (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;
}
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;
+ }
+}
* in objects at every object access, and accuracy is not needed.
* To access a global var is faster than calling 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.
+ * 2^22 minutes are more than 7 years.
*
- * When we need to select what object to swap, we compute the minimum
- * time distance between the current lruclock and the object last access
- * lruclock info. Even if clocks will wrap on overflow, there is
- * 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.
+ * Note that even if this will wrap after 7 years it's not a problem,
+ * everything will still work but just some object will appear younger
+ * to Redis :)
*/
- 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
* not ok doing so inside the signal handler. */
"process_id:%ld\r\n"
"uptime_in_seconds:%ld\r\n"
"uptime_in_days:%ld\r\n"
+ "lru_clock:%ld\r\n"
"used_cpu_sys:%.2f\r\n"
"used_cpu_user:%.2f\r\n"
"used_cpu_sys_childrens:%.2f\r\n"
(long) getpid(),
uptime,
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_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,
/* A redis object, that is a type able to hold a string / list / set */
/* The actual Redis Object */
+#define REDIS_LRU_CLOCK_MAX ((1<<21)-1) /* Max value of obj->lru */
typedef struct redisObject {
unsigned type:4;
unsigned storage:2; /* REDIS_VM_MEMORY or REDIS_VM_SWAPPING */
char *strEncoding(int encoding);
int compareStringObjects(robj *a, robj *b);
int equalStringObjects(robj *a, robj *b);
+unsigned long estimateObjectIdleTime(robj *o);
/* Replication */
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc);
double computeObjectSwappability(robj *o) {
/* actual age can be >= minage, but not < minage. As we use wrapping
* 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;
robj *ele;
list *l;