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.

Easy Python 3.9+ Aug 9, 2026 Caching & Redis 15 views 0 copies

Python code

38 lines
Python 3.9+
class 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

stdout
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

  1. Add a TTL to cache entries using `time.time()` to expire stale data
  2. Use `functools.lru_cache` for simple single-function caching
  3. 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

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.