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.

Medium Python 3.9+ Aug 9, 2026 Caching & Redis 12 views 0 copies

Python code

40 lines
Python 3.9+
from 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

stdout
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

  1. Use `time.monotonic()` instead of `time.time()` to avoid clock adjustments affecting TTL.
  2. 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

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Caching & Redis

Related tutorials and quizzes for this topic.