How to Implement a Negative Cache with TTL in Python
This code provides a TTL mock cache that stores negative results (cache misses) for a short time to reduce repeated lookups of missing keys.
Python code
40 linesfrom time import time, sleep
class TTLMockCache:
def __init__(self, ttl_seconds=5):
self.ttl = ttl_seconds
self.store = {}
self.negative_cache = {}
def get(self, key):
now = time()
if key in self.store:
value, expires_at = self.store[key]
if expires_at > now:
return value
else:
del self.store[key]
if key in self.negative_cache:
neg_expires_at = self.negative_cache[key]
if neg_expires_at > now:
return None # negative cache hit
else:
del self.negative_cache[key]
# Not found: populate negative cache
self.negative_cache[key] = now + self.ttl
return None
def put(self, key, value, ttl=60):
self.store[key] = (value, time() + ttl)
self.negative_cache.pop(key, None)
if __name__ == "__main__":
cache = TTLMockCache(ttl_seconds=2)
print(cache.get("missing")) # None → negative cached
sleep(1)
print(cache.get("missing")) # None (negative hit)
cache.put("missing", "real-value") # overrides negative
print(cache.get("missing")) # "real-value"
sleep(3) # TTL expired, negative cleared
print(cache.get("expired-neg")) # None again, re-negative
Output
None
None
real-value
None
How it works
The TTLMockCache maintains two dictionaries: store for actual values with expiry timestamps, and negative_cache for keys that were not found, storing the expiry time of the negative entry. When get is called, it first checks the main store and removes expired entries. If the key is not in the main store, it checks the negative cache; if a valid negative entry exists, it returns None immediately. Otherwise, it records a negative cache entry with a short TTL and returns None. This mimics the behavior of caching systems that avoid frequent cache misses for missing keys by briefly caching negative results.
Common mistakes
- Not clearing negative cache when a value is put, leaving stale negative entries that block real data.
- Forgetting to check expiry timestamps for both positive and negative entries.
- Storing the same TTL for both positive and negative entries; negative TTL should typically be shorter.
Variations
- Use `time.monotonic()` instead of `time.time()` to avoid clock adjustments affecting TTL.
- Implement with a single cache and a separate flag to denote negative entries.
Real-world use cases
- Preventing repeated database queries for known-missing user IDs in a web app.
- Caching negative DNS or auth lookups to reduce external API calls under high traffic.
- Avoiding expensive file system checks for files that don't exist in short bursts.
Sponsored
More from Caching & Redis
- Cache Asides in Python with a Read-Through Loader easy
- Cache Data in Redis with Python easy
- Cache Penetration Null Object Mock in Python medium
- Cache Stampede Prevention with SingleFlight in Python medium
- Cache Warming with Python: Preload Hot Keys easy
- Coalescing duplicate in-flight requests: one shared result for concurrent callers hard
Keep learning
Related tutorials and quizzes for this topic.