When protecting the cache accidentally destroys the database.
Caches have limited memory. When the cache gets full, it has to delete (evict) old data to make room for new data. But if a bad actor writes a script to request millions of random, unique items, the cache quickly fills up and evicts all the popular items. Suddenly, every normal user request results in a cache miss, sending a tsunami of traffic straight to the fragile database.
Most caches use Least Recently Used (LRU) eviction. If a scraper requests 10,000 obscure profiles, those profiles become the "most recently used". The actually popular profiles are pushed out the back of the queue.
# The vulnerability of LRU
cache = LRUCache(max_size=3)
# 1. Normal state: Popular items are cached
cache.put("popular_A", data_A)
cache.put("popular_B", data_B)
cache.put("popular_C", data_C)
# 2. A bot scans 3 random user profiles
cache.get_or_fetch_from_db("obscure_user_1") # Evicts A
cache.get_or_fetch_from_db("obscure_user_2") # Evicts B
cache.get_or_fetch_from_db("obscure_user_3") # Evicts C
# 3. Now 1,000 real users request "popular_A" simultaneously.
# The cache is empty of A. All 1,000 requests hit the database at once!
An eviction storm causes massive latency spikes and often a complete database outage. The database is usually sized to handle only 5-10% of total read traffic (relying on the cache for the other 90%).