mirror of
https://github.com/vinta/awesome-python.git
synced 2026-11-08 02:15:12 -06:00
docs: rewrite Caching category intro
The intro's lead and guide recommended dogpile.cache, which the list no longer carries, and never covered django-redis, which replaced it. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,23 +1,20 @@
|
||||
In memory, on disk, or on a cache server: the Python caching library you want is cachetools, DiskCache, or dogpile.cache. For HTTP responses, it's Hishel.
|
||||
Expensive function calls get cached in memory by one cachetools decorator. On disk, with no server, no Python caching library asks less of you than DiskCache.
|
||||
|
||||
How to choose:
|
||||
|
||||
- Function results in one process's memory, with a size limit or a time-to-live: cachetools
|
||||
- A cache on local disk that processes on one machine share, with no server to run: DiskCache
|
||||
- Memoizing a function in one process, with a size limit or an expiry time: cachetools
|
||||
- A persistent cache on local disk that several processes share: DiskCache
|
||||
- A Django cache backend on local disk: DiskCache
|
||||
- One cache on memcached or Redis for many processes or servers: dogpile.cache
|
||||
- HTTP responses in HTTPX or Requests: Hishel
|
||||
- `Cache-Control` headers and response caching in FastAPI or another ASGI app: Hishel
|
||||
- Django querysets, invalidated when a model changes: Cacheops
|
||||
- A Django cache and session store on Redis: django-redis
|
||||
- Caching HTTP responses in HTTPX or Requests, or setting cache headers in FastAPI: Hishel
|
||||
- Django ORM queries cached in Redis and invalidated when the data changes: Cacheops
|
||||
|
||||
cachetools offers [variants of the standard library's `@lru_cache`](https://cachetools.readthedocs.io/en/stable/) with more cache algorithms, including a `TTLCache` whose items [expire after a time-to-live](https://cachetools.readthedocs.io/en/stable/#cachetools.TTLCache). Wrap a function in [`@cached`](https://cachetools.readthedocs.io/en/stable/#cachetools.cached) and pass it the cache to use. The cache classes [aren't thread-safe](https://cachetools.readthedocs.io/en/stable/#cache-implementations), so when threads share a cache, give `@cached` a `threading.Lock`. The cache lives in your process's memory, so each worker process of a web app keeps its own copy.
|
||||
cachetools offers [variants of the standard library's `@lru_cache` decorator](https://cachetools.readthedocs.io/en/latest/). Its cache classes have a maximum size and drop items by different rules, like least recently used or a per-item time-to-live. Wrap a function in `@cached` with one of them: the README's `@cached(cache=TTLCache(maxsize=1024, ttl=600))` [keeps results for ten minutes](https://github.com/tkem/cachetools). To keep the `lru_cache` API instead, use the [`cachetools.func` decorators](https://cachetools.readthedocs.io/en/latest/#module-cachetools.func).
|
||||
|
||||
DiskCache is a [disk and file backed cache library](https://grantjenks.com/docs/diskcache/) in pure Python, built on SQLite, with no other process to run. Create a `Cache` with a directory path: [two `Cache` objects on the same directory](https://grantjenks.com/docs/diskcache/tutorial.html#cache) can live in separate processes, so the workers on one machine share one cache. Wrap a function in [`@cache.memoize()`](https://grantjenks.com/docs/diskcache/tutorial.html#fanoutcache), which takes arguments like `lru_cache`'s. In Django, set [`diskcache.DjangoCache`](https://grantjenks.com/docs/diskcache/tutorial.html#djangocache) as the cache `BACKEND`. Keep the directory on a local disk, since SQLite [isn't recommended on NFS mounts](https://grantjenks.com/docs/diskcache/tutorial.html#caveats).
|
||||
DiskCache keeps keys and values in [a SQLite database and a directory of files](https://grantjenks.com/docs/diskcache/), with no server process to run. Point a `Cache` at a directory, and [other threads and processes that open the same directory](https://grantjenks.com/docs/diskcache/tutorial.html#tutorial-cache) share its entries. Its `memoize()` decorator takes [arguments like `lru_cache`'s](https://grantjenks.com/docs/diskcache/tutorial.html#tutorial-fanoutcache), plus an expiry time. In Django, [set `diskcache.DjangoCache` as the `BACKEND`](https://grantjenks.com/docs/diskcache/tutorial.html#tutorial-djangocache) and a directory as the `LOCATION`.
|
||||
|
||||
dogpile.cache is [a caching API over backends of any variety](https://dogpilecache.sqlalchemy.org/en/latest/), memcached and Redis among them. You ask it for a value and hand it a function that [creates the value only when needed](https://dogpilecache.sqlalchemy.org/en/latest/usage.html#overview). When the value expires, one worker regenerates it instead of every worker at once. Create a region with `make_region()` at import time, decorate functions with `@region.cache_on_arguments()`, and [call `configure()` later](https://dogpilecache.sqlalchemy.org/en/latest/usage.html#rudimentary-usage) with the backend and expiration time from your config file. When several processes share one Redis or memcached server, turn on the backend's [`distributed_lock`](https://dogpilecache.sqlalchemy.org/en/latest/api.html#dogpile.cache.backends.redis.RedisBackend) so that lock covers them all.
|
||||
django-redis is a [full-featured Redis cache and session backend](https://github.com/jazzband/django-redis#introduction) for Django. [Set `django_redis.cache.RedisCache` as the `BACKEND`](https://github.com/jazzband/django-redis#configure-as-cache-backend) and a redis-py URL like `redis://127.0.0.1:6379/1` as the `LOCATION`. Sessions then need no extra backend: [point `SESSION_ENGINE` at Django's cache sessions](https://github.com/jazzband/django-redis#configure-as-session-backend), and they live in Redis too. Its clients, parsers, and serializers [are pluggable](https://github.com/jazzband/django-redis#why-use-django-redis).
|
||||
|
||||
Hishel caches HTTP responses by [RFC 9111](https://hishel.com/overview.html), the HTTP caching rules browsers follow, in both sync and async code. With HTTPX, [swap your client for Hishel's cache-enabled one](https://hishel.com/httpx.html#quick-start), or [give a client you already have its cache transport](https://hishel.com/httpx.html#cache-transports). With Requests, [mount its cache adapter](https://hishel.com/requests.html#quick-start) on a `Session`. In FastAPI, [a dependency sets `Cache-Control` headers](https://hishel.com/fastapi.html#quick-start) for browsers and CDNs, and its ASGI middleware also caches responses on your server.
|
||||
Hishel caches HTTP responses [by RFC 9111](https://hishel.com/overview.html), the rules browsers follow. It works in sync and async code, and comes with a backend that stores responses in SQLite. In HTTPX, [use `SyncCacheClient` or `AsyncCacheClient`](https://hishel.com/httpx.html#cache-clients), drop-in replacements for `Client` and `AsyncClient`. In Requests, [mount its `CacheAdapter`](https://hishel.com/requests.html#quick-start) on a `Session`. In FastAPI, [the `cache()` dependency](https://hishel.com/fastapi.html) sends Cache-Control headers to browsers and CDNs, and Hishel's ASGI middleware adds a cache on your server.
|
||||
|
||||
Cacheops caches Django querysets in Redis and [invalidates them](https://github.com/Suor/django-cacheops#user-content-invalidation) on `save()`, `delete()`, and many-to-many changes. [Add it to `INSTALLED_APPS`](https://github.com/Suor/django-cacheops#user-content-setup) and point it at its own Redis database, as its docs highly recommend. Turn caching on per app with `'app_name.*'`, since `'*.*'` can also cache tables you don't mean to, like migrations. Call `.cache()` on a queryset to cache it by hand, and wrap a function in [`@cached_as(Article)`](https://github.com/Suor/django-cacheops#user-content-usage) to drop its result whenever an `Article` changes.
|
||||
|
||||
Plan how stale entries leave the cache, too. cachetools' decorators add a [`cache_clear()`](https://cachetools.readthedocs.io/en/stable/#memoizing-decorators) function, DiskCache [evicts every key with a given tag](https://grantjenks.com/docs/diskcache/tutorial.html#cache), and a function decorated by dogpile.cache takes [`invalidate()`](https://dogpilecache.sqlalchemy.org/en/latest/api.html#dogpile.cache.region.CacheRegion.cache_on_arguments) with the same arguments you'd call it with.
|
||||
Cacheops caches Django ORM queries in Redis and [invalidates them on `save()`, `delete()`, and m2m changes](https://github.com/Suor/django-cacheops#invalidation), only where a change could affect the result. Add `cacheops` to `INSTALLED_APPS`, then set `CACHEOPS_REDIS` and [a `CACHEOPS` profile per app](https://github.com/Suor/django-cacheops#setup) like `'app_name.*'`. Profiles with `ops` cache those queries automatically. To cache by hand, [call `.cache()` on a queryset, or decorate a function with `@cached_as`](https://github.com/Suor/django-cacheops#usage), and it still invalidates with the model.
|
||||
|
||||
Reference in New Issue
Block a user