@@ -100,6 +100,36 @@ unsigned long long estimateObjectIdleTime(robj_roptr o) {
100100 }
101101}
102102
103+ unsigned long long getIdle (robj *obj, const expireEntry *e) {
104+ unsigned long long idle;
105+ /* Calculate the idle time according to the policy. This is called
106+ * idle just because the code initially handled LRU, but is in fact
107+ * just a score where an higher score means better candidate. */
108+ if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LRU) {
109+ idle = (obj != nullptr ) ? estimateObjectIdleTime (obj) : 0 ;
110+ } else if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU) {
111+ /* When we use an LRU policy, we sort the keys by idle time
112+ * so that we expire keys starting from greater idle time.
113+ * However when the policy is an LFU one, we have a frequency
114+ * estimation, and we want to evict keys with lower frequency
115+ * first. So inside the pool we put objects using the inverted
116+ * frequency subtracting the actual frequency to the maximum
117+ * frequency of 255. */
118+ idle = 255 -LFUDecrAndReturn (obj);
119+ } else if (g_pserver->maxmemory_policy == MAXMEMORY_VOLATILE_TTL) {
120+ /* In this case the sooner the expire the better. */
121+ if (e != nullptr )
122+ idle = ULLONG_MAX - e->when ();
123+ else
124+ idle = 0 ;
125+ } else if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) {
126+ idle = ULLONG_MAX;
127+ } else {
128+ serverPanic (" Unknown eviction policy in storage eviction" );
129+ }
130+ return idle;
131+ }
132+
103133/* LRU approximation algorithm
104134 *
105135 * Redis uses an approximation of the LRU algorithm that runs in constant
@@ -137,28 +167,7 @@ void evictionPoolAlloc(void) {
137167
138168void processEvictionCandidate (int dbid, sds key, robj *o, const expireEntry *e, struct evictionPoolEntry *pool)
139169{
140- unsigned long long idle;
141-
142- /* Calculate the idle time according to the policy. This is called
143- * idle just because the code initially handled LRU, but is in fact
144- * just a score where an higher score means better candidate. */
145- if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LRU) {
146- idle = (o != nullptr ) ? estimateObjectIdleTime (o) : 0 ;
147- } else if (g_pserver->maxmemory_policy & MAXMEMORY_FLAG_LFU) {
148- /* When we use an LRU policy, we sort the keys by idle time
149- * so that we expire keys starting from greater idle time.
150- * However when the policy is an LFU one, we have a frequency
151- * estimation, and we want to evict keys with lower frequency
152- * first. So inside the pool we put objects using the inverted
153- * frequency subtracting the actual frequency to the maximum
154- * frequency of 255. */
155- idle = 255 -LFUDecrAndReturn (o);
156- } else if (g_pserver->maxmemory_policy == MAXMEMORY_VOLATILE_TTL) {
157- /* In this case the sooner the expire the better. */
158- idle = ULLONG_MAX - e->when ();
159- } else {
160- serverPanic (" Unknown eviction policy in evictionPoolPopulate()" );
161- }
170+ unsigned long long idle = getIdle (o,e);
162171
163172 /* Insert the element inside the pool.
164173 * First, find the first empty bucket or the first populated
@@ -600,6 +609,31 @@ static unsigned long evictionTimeLimitUs() {
600609 return ULONG_MAX; /* No limit to eviction time */
601610}
602611
612+ void evict (redisDb *db, robj *keyobj) {
613+ mstime_t eviction_latency;
614+ propagateExpire (db,keyobj,g_pserver->lazyfree_lazy_eviction );
615+ /* We compute the amount of memory freed by db*Delete() alone.
616+ * It is possible that actually the memory needed to propagate
617+ * the DEL in AOF and replication link is greater than the one
618+ * we are freeing removing the key, but we can't account for
619+ * that otherwise we would never exit the loop.
620+ *
621+ * AOF and Output buffer memory will be freed eventually so
622+ * we only care about memory used by the key space. */
623+ latencyStartMonitor (eviction_latency);
624+ if (g_pserver->lazyfree_lazy_eviction )
625+ dbAsyncDelete (db,keyobj);
626+ else
627+ dbSyncDelete (db,keyobj);
628+ latencyEndMonitor (eviction_latency);
629+ latencyAddSampleIfNeeded (" eviction-del" ,eviction_latency);
630+
631+ signalModifiedKey (NULL ,db,keyobj);
632+ notifyKeyspaceEvent (NOTIFY_EVICTED, " evicted" ,
633+ keyobj, db->id );
634+ decrRefCount (keyobj);
635+ }
636+
603637static void updateSysAvailableMemory () {
604638 if (g_pserver->force_eviction_percent ) {
605639 g_pserver->cron_malloc_stats .sys_available = getMemAvailable ();
@@ -637,7 +671,7 @@ int performEvictions(bool fPreSnapshot) {
637671 int keys_freed = 0 ;
638672 size_t mem_reported, mem_tofree;
639673 long long mem_freed; /* May be negative */
640- mstime_t latency, eviction_latency ;
674+ mstime_t latency;
641675 long long delta;
642676 int slaves = listLength (g_pserver->slaves );
643677 const bool fEvictToStorage = !cserver.delete_on_evict && g_pserver->db [0 ]->FStorageProvider ();
@@ -662,6 +696,43 @@ int performEvictions(bool fPreSnapshot) {
662696 monotime evictionTimer;
663697 elapsedStart (&evictionTimer);
664698
699+ if (g_pserver->maxstorage && g_pserver->m_pstorageFactory != nullptr ) {
700+ while (g_pserver->m_pstorageFactory ->totalDiskspaceUsed () >= g_pserver->maxstorage && elapsedUs (evictionTimer) < eviction_time_limit_us) {
701+ redisDb *db;
702+ std::vector<std::string> evictionPool;
703+ robj *bestkey = nullptr ;
704+ redisDb *bestdb = nullptr ;
705+ unsigned long long bestidle = 0 ;
706+ for (int i = 0 ; i < cserver.dbnum ; i++) {
707+ db = g_pserver->db [i];
708+ evictionPool = db->getStorageCache ()->getEvictionCandidates (g_pserver->maxmemory_samples );
709+ for (std::string key : evictionPool) {
710+ robj *keyobj = createStringObject (key.c_str (), key.size ());
711+ robj *obj = db->find (szFromObj (keyobj));
712+ if (obj != nullptr ) {
713+ expireEntry *e = db->getExpire (keyobj);
714+ unsigned long long idle = getIdle (obj, e);
715+
716+ if (bestkey == nullptr || bestidle < idle) {
717+ if (bestkey != nullptr )
718+ decrRefCount (bestkey);
719+ incrRefCount (keyobj);
720+ bestkey = keyobj;
721+ bestidle = idle;
722+ bestdb = db;
723+ }
724+ }
725+ decrRefCount (keyobj);
726+ }
727+ }
728+ if (bestkey) {
729+ evict (bestdb, bestkey);
730+ } else {
731+ break ; // could not find a key to evict so stop now
732+ }
733+ }
734+ }
735+
665736 if (g_pserver->maxstorage && g_pserver->m_pstorageFactory != nullptr && g_pserver->m_pstorageFactory ->totalDiskspaceUsed () >= g_pserver->maxstorage )
666737 goto cant_free_storage;
667738
@@ -776,7 +847,7 @@ int performEvictions(bool fPreSnapshot) {
776847 if (db->removeCachedValue (bestkey, &deT)) {
777848 mem_freed += splazy->addEntry (db->dictUnsafeKeyOnly (), deT);
778849 ckeysFailed = 0 ;
779- g_pserver->stat_evictedkeys ++;
850+ g_pserver->stat_evictedkeys ++;
780851 }
781852 else {
782853 delta = 0 ;
@@ -788,30 +859,11 @@ int performEvictions(bool fPreSnapshot) {
788859 else
789860 {
790861 robj *keyobj = createStringObject (bestkey,sdslen (bestkey));
791- propagateExpire (db,keyobj,g_pserver->lazyfree_lazy_eviction );
792- /* We compute the amount of memory freed by db*Delete() alone.
793- * It is possible that actually the memory needed to propagate
794- * the DEL in AOF and replication link is greater than the one
795- * we are freeing removing the key, but we can't account for
796- * that otherwise we would never exit the loop.
797- *
798- * AOF and Output buffer memory will be freed eventually so
799- * we only care about memory used by the key space. */
800862 delta = (long long ) zmalloc_used_memory ();
801- latencyStartMonitor (eviction_latency);
802- if (g_pserver->lazyfree_lazy_eviction )
803- dbAsyncDelete (db,keyobj);
804- else
805- dbSyncDelete (db,keyobj);
806- latencyEndMonitor (eviction_latency);
807- latencyAddSampleIfNeeded (" eviction-del" ,eviction_latency);
863+ evict (db, keyobj);
808864 delta -= (long long ) zmalloc_used_memory ();
809865 mem_freed += delta;
810866 g_pserver->stat_evictedkeys ++;
811- signalModifiedKey (NULL ,db,keyobj);
812- notifyKeyspaceEvent (NOTIFY_EVICTED, " evicted" ,
813- keyobj, db->id );
814- decrRefCount (keyobj);
815867 }
816868 keys_freed++;
817869
0 commit comments