Skip to content
intermediatePhase 46 · Caching

Cache Eviction

Remove old entries when cache is full: LRU, LFU, FIFO, TTL.

45m
0 problems
Topic Progress0%

LRU (Least Recently Used)

LRU - Least Recently Used

LRU evicts the item that hasn't been accessed for the longest time.

How LRU Works

LRU Cache (Capacity: 3):

Access: A, B, C, D (evicts A), B (hit), E (evicts C)

State after each access:
[A] → [B,A] → [C,B,A] → [D,B,A] → [D,B,A] → [E,D,B]
                                              ↑
                                           Evicts C (LRU)

Implementation

class LRUCache:
    def __init__(self, capacity):
        self.capacity = capacity
        self.cache = {}  # key -> value
        self.order = []  # Track access order
    
    def get(self, key):
        if key in self.cache:
            # Move to most recently used
            self.order.remove(key)
            self.order.append(key)
            return self.cache[key]
        return None
    
    def put(self, key, value):
        if key in self.cache:
            # Update existing
            self.cache[key] = value
            self.order.remove(key)
            self.order.append(key)
        else:
            # Evict if at capacity
            if len(self.cache) >= self.capacity:
                lru_key = self.order.pop(0)
                del self.cache[lru_key]
            
            # Add new entry
            self.cache[key] = value
            self.order.append(key)

# O(1) implementation with OrderedDict
from collections import OrderedDict

class LRUCacheOptimized:
    def __init__(self, capacity):
        self.capacity = capacity
        self.cache = OrderedDict()
    
    def get(self, key):
        if key in self.cache:
            self.cache.move_to_end(key)
            return self.cache[key]
        return None
    
    def put(self, key, value):
        if key in self.cache:
            self.cache.move_to_end(key)
        self.cache[key] = value
        if len(self.cache) > self.capacity:
            self.cache.popitem(last=False)

Characteristics

  • Time complexity: O(1) for get/put
  • Space complexity: O(capacity)
  • Best for: General purpose caching
  • Redis implementation: Uses LRU by default

LFU (Least Frequently Used)

LFU - Least Frequently Used

LFU evicts the item with the lowest access count.

How LFU Works

LFU Cache (Capacity: 3):

Access: A, A, A, B, B, C, D (evicts B)

Counts: A=3, B=2, C=1, D=1
Evict B (lowest frequency, but A has highest)

Implementation

import heapq
from collections import defaultdict

class LFUCache:
    def __init__(self, capacity):
        self.capacity = capacity
        self.cache = {}  # key -> value
        self.freq = {}   # key -> frequency
        self.freq_keys = defaultdict(list)  # freq -> [keys]
        self.min_freq = 0
        self.size = 0
    
    def get(self, key):
        if key not in self.cache:
            return None
        
        # Update frequency
        freq = self.freq[key]
        self.freq[key] = freq + 1
        
        # Move to new frequency bucket
        self.freq_keys[freq].remove(key)
        if not self.freq_keys[freq]:
            del self.freq_keys[freq]
            if self.min_freq == freq:
                self.min_freq = freq + 1
        
        self.freq_keys[freq + 1].append(key)
        return self.cache[key]
    
    def put(self, key, value):
        if key in self.cache:
            self.cache[key] = value
            self.get(key)  # Update frequency
            return
        
        if self.size >= self.capacity:
            # Evict least frequent
            evict_key = self.freq_keys[self.min_freq].pop(0)
            if not self.freq_keys[self.min_freq]:
                del self.freq_keys[self.min_freq]
            del self.cache[evict_key]
            del self.freq[evict_key]
            self.size -= 1
        
        # Add new entry
        self.cache[key] = value
        self.freq[key] = 1
        self.freq_keys[1].append(key)
        self.min_freq = 1
        self.size += 1

Characteristics

  • Best for: Workloads with skewed access patterns
  • Adaptive: Automatically favors frequently accessed items
  • More complex: Requires frequency tracking
  • Redis: Does not natively support LFU (can be simulated)

FIFO and Random

FIFO and Random Eviction

FIFO - First In First Out

from collections import deque

class FIFOCache:
    def __init__(self, capacity):
        self.capacity = capacity
        self.cache = {}
        self.queue = deque()
    
    def get(self, key):
        return self.cache.get(key)
    
    def put(self, key, value):
        if key in self.cache:
            self.cache[key] = value
            return
        
        if len(self.cache) >= self.capacity:
            oldest = self.queue.popleft()
            del self.cache[oldest]
        
        self.cache[key] = value
        self.queue.append(key)

Random Eviction

import random

class RandomCache:
    def __init__(self, capacity):
        self.capacity = capacity
        self.cache = {}
    
    def get(self, key):
        return self.cache.get(key)
    
    def put(self, key, value):
        if len(self.cache) >= self.capacity:
            # Evict random key
            random_key = random.choice(list(self.cache.keys()))
            del self.cache[random_key]
        
        self.cache[key] = value

Comparison

Algorithm Complexity Hit Rate Use Case
LRU O(1) High General purpose
LFU O(log n) Highest Skewed access patterns
FIFO O(1) Medium Simple, predictable
Random O(1) Low Uniform access patterns

When to Use Each

  • LRU: Default choice for most workloads
  • LFU: When access frequency varies significantly
  • FIFO: When order matters more than frequency
  • Random: Simple workloads, uniform distribution

When to Evict

When to Evict

Eviction Triggers

Eviction occurs when:

1. Memory Limit Reached:
   - Cache at maximum capacity
   - New item needs space
   - Eviction policy selects victim

2. TTL Expiration:
   - Item's TTL has passed
   - Background cleanup process
   - Immediate on access

3. Manual Invalidation:
   - Explicit delete operation
   - Pattern-based deletion
   - Administrative action

Eviction Monitoring

# Track eviction metrics
metrics = {
    'evictions_total': 0,
    'evictions_by_policy': defaultdict(int),
    'memory_usage': 0,
    'hit_ratio': 0.0
}

def on_eviction(key, policy):
    metrics['evictions_total'] += 1
    metrics['evictions_by_policy'][policy] += 1
    
    # Alert if eviction rate too high
    if metrics['evictions_total'] > 1000:
        alert('High eviction rate detected')

Tuning Eviction

# Redis eviction configuration
# redis.conf
maxmemory 1gb
maxmemory-policy allkeys-lru

# Policies:
# noeviction: Return errors when memory limit reached
# allkeys-lru: LRU across all keys
# volatile-lru: LRU only for keys with TTL
# allkeys-lfu: LFU across all keys (Redis 4.0+)
# volatile-ttl: Evict shortest TTL first
# allkeys-random: Random eviction

Best Practices

  1. Monitor eviction rates - high rates indicate undersized cache
  2. Choose policy based on workload - LRU for most cases
  3. Set appropriate maxmemory - based on available RAM
  4. Use volatile policies - when only some keys should be evictable
  5. Test with production patterns - validate policy choice

Practice Problems

0/3solved
Design Cache Eviction System

Design a scalable Cache Eviction system. Cover high-level architecture, data model, and API design.

Solution
// Complete system design:
// - Functional + Non-functional requirements
// - Capacity estimation
// - Data model (SQL/NoSQL choice)
// - API endpoints
// - Component architecture
// - Scaling strategy
// - Monitoring & reliability
Cache Eviction Scaling

How would you scale Cache Eviction to handle 10x the current load? Identify bottlenecks and solutions.

Solution
// Scaling approach:
// 1. Load balancing
// 2. Database sharding/replication
// 3. Cache layer (Redis)
// 4. CDN for static assets
// 5. Async processing (queues)
// 6. Microservices decomposition
Cache Eviction Failure Modes

Analyze potential failure modes for Cache Eviction and design mitigation strategies.

Solution
// Failure mitigation:
// 1. Redundancy (multi-AZ)
// 2. Circuit breakers
// 3. Retry with backoff
// 4. Dead letter queues
// 5. Health checks
// 6. Graceful degradation

Quiz

1. Which eviction policy evicts the item accessed least recently?

Question 1 options

2. What is the time complexity of LRU get/put operations?

Question 2 options

3. When is LFU better than LRU?

Question 3 options

4. Which eviction policy is simplest to implement?

Question 4 options

5. What does high eviction rate indicate?

Question 5 options

Flashcards

Question

What does LRU stand for and how does it work?

Answer

Least Recently Used - evicts the item that hasn't been accessed for the longest time

Question

How to implement LRU in O(1)?

Answer

Use HashMap + Doubly Linked List: HashMap for O(1) lookup, Linked List for O(1) reordering

Question

LRU vs LFU: When use which?

Answer

LRU: general purpose, recency matters. LFU: skewed access patterns, frequency matters more.

Question

What are the 4 main eviction policies?

Answer

1) LRU (Least Recently Used), 2) LFU (Least Frequently Used), 3) FIFO (First In First Out), 4) Random

Question

What does high eviction rate indicate?

Answer

Cache is undersized for the workload - the working set doesn't fit in cache, causing frequent evictions

Revision Notes

Key Takeaways

  • 1.LRU is the most common and generally best eviction policy
  • 2.LFU is better for skewed access patterns but more complex
  • 3.FIFO is simplest but has lower hit rates
  • 4.High eviction rate indicates undersized cache
  • 5.Choose policy based on workload characteristics

Interview Tips

  • Know how to implement LRU in O(1) with HashMap + Linked List
  • Explain trade-offs between LRU and LFU
  • Discuss Redis eviction policies and configuration
  • Mention monitoring eviction rates as operational concern

Cheat Sheet

Cheat Sheet: Cache Eviction

Eviction Policies

  1. LRU: Evict least recently used - O(1), general purpose
  2. LFU: Evict least frequently used - O(log n), skewed patterns
  3. FIFO: Evict oldest - O(1), simple
  4. Random: Evict random - O(1), uniform patterns

Implementation

  • LRU: HashMap + Doubly Linked List
  • LFU: Frequency buckets + HashMap
  • FIFO: Queue
  • Random: Random selection

Redis Configuration

maxmemory 1gb
maxmemory-policy allkeys-lru

Monitoring

  • Track eviction rate
  • Monitor memory usage
  • Alert on high eviction rates