Some endpoints are read often and change rarely. A vendor dashboard summary, a product catalogue, a config blob. Recomputing those on every request is wasted work. A Redis cache in front of them is one of the cheapest performance wins available.
Decide what is worth caching
Good cache candidates share three traits: they are expensive to build, they are read far more than they are written, and a few seconds of staleness causes no harm. If a value must always be exact to the millisecond, it does not belong in a cache.
Configure Redis as the cache backend
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": os.environ["REDIS_URL"],
"TIMEOUT": 300,
}
}
Cache keys you can reason about
Keys are an interface. Build them deliberately, and include a version segment so you can invalidate every key at once by bumping a number.
def vendor_summary_key(vendor_id):
return f"vendor:summary:v2:{vendor_id}"
The get-or-set pattern
Every cached read follows the same shape: look in the cache, build the value on a miss, store it, return it.
from django.core.cache import cache
def get_vendor_summary(vendor_id):
key = vendor_summary_key(vendor_id)
summary = cache.get(key)
if summary is None:
summary = build_vendor_summary(vendor_id)
cache.set(key, summary, timeout=600)
return summary
Invalidation is the hard part
There are two honest strategies. A short timeout accepts brief staleness and never needs explicit cleanup. Active invalidation deletes the key the moment the underlying data changes, usually from a model signal.
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=Order)
def drop_vendor_summary(sender, instance, **kwargs):
cache.delete(vendor_summary_key(instance.vendor_id))
Defend against the stampede
When a hot key expires, every request that arrives in the next moment misses the cache and rebuilds the value at once. Adding a small random offset to the timeout spreads those expiries out so they do not all land together.
import random
cache.set(key, summary, timeout=600 + random.randint(0, 120))
Cache the result, not the whole response
Caching at the function level, around the value itself, beats caching a full HTTP response. The same cached summary can then feed an API endpoint, a server-rendered page, and a background report without being computed three times.