Cache Asides in Python with a Read-Through Loader
Implements a cache-aside pattern with a read-through loader that fetches missing keys from a backing data store and caches them.
Python code
38 linesclass DataStore:
"""Mock database with a few records."""
def __init__(self):
self.data = {1: "Alice", 2: "Bob", 3: "Charlie"}
def get(self, key):
print(f"Loading key {key} from database")
return self.data.get(key)
class CacheAsideLoader:
"""Cache-aside pattern with a read-through loader."""
def __init__(self, data_store):
self.cache = {}
self.data_store = data_store
def get(self, key):
if key not in self.cache:
value = self.data_store.get(key)
self.cache[key] = value
return self.cache[key]
if __name__ == "__main__":
store = DataStore()
cache = CacheAsideLoader(store)
# First load reads from database and caches
print(cache.get(1))
print(cache.get(2))
# Subsequent reads hit cache only
print(cache.get(1))
print(cache.get(2))
# Missing keys return None
print(cache.get(99))
Output
First load reads from database and caches
Loading key 1 from database
Alice
Loading key 2 from database
Bob
Subsequent reads hit cache only
Alice
Bob
Missing keys return None
Loading key 99 from database
None
How it works
The CacheAsideLoader checks the local cache dict first; a key miss triggers a fetch from the DataStore and stores the result. The in operator tests membership without raising errors for absent keys. The pattern separates the cache logic from the data store, making it easy to swap in Redis or other caches. This approach minimizes database load by serving repeated reads from fast in-memory storage.
Common mistakes
- Caching `None` for missing keys and repeatedly hitting the database for the same absent key
- Using `self.cache.get(key)` without checking `None` and conflating a cached `None` with a miss
- Forgetting to invalidate or evict cache entries when the underlying data changes
- Using an unbounded dictionary as a cache without a size limit or TTL
Variations
- Add a TTL to cache entries using `time.time()` to expire stale data
- Use `functools.lru_cache` for simple single-function caching
- Switch the backing store to Redis with `redis-py` for distributed caching
Real-world use cases
- E-commerce product pages caching product details to avoid repeated database queries.
- User profile services reading session or profile data from a fast cache before hitting SQL.
- API gateways caching slow upstream responses like weather or exchange-rate lookups.
Sponsored
More from Caching & Redis
- 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
- Consistent Hashing Cache Shard in Python medium
Keep learning
Related tutorials and quizzes for this topic.