Caching Overview¶
The caching subsystem is a declarative layer on top of Django's cache framework. It models three things separately: how a cache key is built, when a cached value is invalidated, and where invalidation work runs.
Cache backend¶
The caching layer plugs into Django's cache framework and works with any configured backend.
Cache backend recommendation
A small set of features only works on a redis-compatible backend:
delete_by_prefix(), invalidate_all(), and any
InvalidationRule that needs to wipe a partition rather than a
single key. Without a redis-compatible backend, those calls
raise; the rest of the caching API still works on Django's
local-memory or database cache.
# settings.py
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
},
},
}
Caching¶
A cached function in Restflow is a CachedWrapper built by the
@cache_result decorator. The wrapper has three pieces:
- A cache key. Built by a
KeyConstructor, which is a declarative class whose attributes are key fields. Each field contributes a deterministic string slice to the final key. Some fields are marked as part of the key prefix (a partition); others contribute to the suffix. - A TTL. A timeout in seconds.
Nonemeans no expiration. - A list of invalidation rules. Each rule is an
InvalidationRulethat hooks into a Django model'spost_saveandpost_deletesignals. When the rule fires, the wrapper drops or refreshes the relevant cache entries.
from restflow.caching import (
KeyConstructor, ArgsKeyField, ConstantKeyField,
cache_result, InvalidationRule,
)
class UserPayloadKey(KeyConstructor):
user = ArgsKeyField("user_id", partition=True)
version = ConstantKeyField("v", "1")
class Meta:
namespace = "users"
@cache_result(
key_constructor=UserPayloadKey,
ttl=300,
invalidates_on=[
InvalidationRule(
model=User,
field_mapping={"user_id": "id"},
),
],
)
def get_user_payload(user_id: int):
return expensive_lookup(user_id)
Picking a dispatcher¶
Each InvalidationRule decides where its work runs through the
dispatcher attribute. The default is inline, which runs the work
synchronously inside transaction.on_commit.
| Dispatcher | Use when |
|---|---|
inline (default) |
Invalidation is fast and extra moving parts are not wanted. |
threadpool |
Invalidation is slower than the request can afford, but a task broker is not wanted. Loses work on process exit. |
asyncio |
Async views and async signal handlers. Falls back to inline when no event loop is running. |
celery |
The project already runs celery; cache work needs retries, dead-lettering, or fan-out. |
django_rq |
Redis-backed queue without celery's broker abstraction. |
django_q |
Django-native queue with built-in scheduling. |
dramatiq |
The project already runs dramatiq, or its actor-based middleware is preferred. |
See the Dispatchers guide for the per-broker setup.
Caching API¶
| Topic | Page |
|---|---|
| Building cache keys declaratively | Key Constructors |
The @cache_result decorator and CachedWrapper methods |
cache_result |
| Connecting model signals to cache invalidation | Invalidation Rules |
| Choosing where invalidation runs | Dispatchers |
| Reporting cache status to the client | Response Headers |
| Tuning defaults globally | Settings |
Where to next¶
- Caching API reference for the generated reference of every public symbol.