Implement a Key-Value Cache with Per-Entry Expiration
NetflixSoftware Engineer · Technical ScreenLast updated
This problem tests the ability to design an in-memory storage system that combines fixed-capacity constraints with time-based invalidation logic.
Problem Statement
Design a thread-safe ExpiringCache that stores key-value pairs with a maximum capacity. Each entry in the cache has a specific Time-To-Live (TTL). Once an entry expires, it should no longer be accessible. If the cache reaches its capacity, it must evict the oldest entry currently in the cache (Least Recently Added policy).
Your implementation must support the following interface:
class ExpiringCache:
def __init__(self, capacity: int):
# Initialize the cache with a fixed max capacity
def put(self, key: str, value: any, ttl_seconds: int) -> None:
# Store value with a TTL; evict oldest if at capacity
def get(self, key: str) -> any:
# Return value if key exists and not expired, else return None
Requirements
- Lazy Expiration: If
get()is called on an expired key, returnNoneand remove the item. - Eviction: When adding a new item exceeds
capacity, remove the entry that was added the earliest (regardless of its expiration status). - Concurrency: Ensure that multiple threads can safely read and write to the cache.
Examples
cache = ExpiringCache(capacity=2)
cache.put("a", 1, 10) # Cache: {"a": 1}
cache.put("b", 2, 5) # Cache: {"a": 1, "b": 2}
# After 6 seconds:
cache.get("b") # Returns None (expired)
cache.put("c", 3, 10) # Cache: {"a": 1, "c": 3} ("b" was evicted by capacity logic)
Edge Cases
- Accessing a key exactly at its expiration time.
- Repeatedly calling
putwith the same key. - Inserting into a cache with
capacity=0or1.
Hints
Hint 1
Use a Hash Map for O(1) access and a Doubly Linked List to keep track of the insertion order for O(1) eviction.
Hint 2
Store the absolute expiration timestamp (current time + TTL) alongside the value to check for validity during retrieval.
Hint 3
Use a ReentrantLock (or similar concurrency primitive) to wrap internal operations and ensure thread safety.
Follow-up questions
- How would you implement a proactive cleanup mechanism (e.g., a background thread) to remove expired items before they are accessed?
- If the cache needed to be distributed across multiple servers, what trade-offs would you face regarding consistency and latency?
- How would you change the eviction policy if you wanted to evict the Least Recently Used (LRU) entry instead of the oldest insertion?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.