Response cache¶
Cache, FastAPICache, the @cache decorator, key builders and coders.
In fastapi_alcazaba.response_cache.
In the guide: Production setup, Configuration, Coders, Key builders. In an app: the example.
fastapi_alcazaba.response_cache.Cache
¶
Cache(backend: Backend, prefix: str = '', expire: int | None = None, coder: type[Coder] = JsonCoder, key_builder: KeyBuilder = default_key_builder, cache_status: str | None = 'Alcazaba', enable: bool = True, *, signing_key: Secrets | None = None, encryption_key: Secrets | None = None, vary_headers: Iterable[str] = DEFAULT_VARY_HEADERS, respect_cache_control: bool = False, single_flight: bool = True, backend_timeout: float | None = 1.0, circuit_breaker_failures: int | None = 5, circuit_breaker_reset: float = 30.0, max_value_size: int | None = None, distributed_lock: bool = False, lock_timeout: float = 10.0, lock_wait: float = 5.0, early_refresh: float | None = None, compression: Codec | None = None, compression_min_size: int = 1024, compression_level: int | None = None, metrics: bool = True, meter_provider: Any = None, tracing: bool = True, tracer_provider: Any = None)
A cache: a backend and its settings. Create one per app, or several (e.g. a public one and
an encrypted one for personal data), and decorate with cached(); or use the global
FastAPICache with @cache.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
backend
|
Backend
|
where entries are stored. |
required |
prefix
|
str
|
prefix of every key. |
''
|
expire
|
int | None
|
default time to live, in seconds. |
None
|
coder
|
type[Coder]
|
how values are stored (JsonCoder by default). |
JsonCoder
|
key_builder
|
KeyBuilder
|
builds keys from the function and its arguments. |
default_key_builder
|
cache_status
|
str | None
|
this cache's name in the responses' |
'Alcazaba'
|
enable
|
bool
|
False turns the cache off. |
True
|
signing_key
|
Secrets | None
|
secret (at least 32 bytes; text, bytes, or pydantic's SecretStr or SecretBytes) used to sign every entry with HMAC-SHA256; entries without a valid signature are ignored. Strongly recommended when the backend is shared or reachable by others, and needed for PickleCoder. A list of keys rotates them: the first one signs, all of them are accepted. |
None
|
encryption_key
|
Secrets | None
|
like |
None
|
vary_headers
|
Iterable[str]
|
request headers that give each value its own cache entry and make the
response |
DEFAULT_VARY_HEADERS
|
respect_cache_control
|
bool
|
let clients skip the cache with |
False
|
single_flight
|
bool
|
concurrent misses for the same key wait for the first one instead of all computing the value (cache stampede protection), within each process. |
True
|
backend_timeout
|
float | None
|
seconds each backend call may take before the cache is skipped for that request; None waits as long as the client does. |
1.0
|
circuit_breaker_failures
|
int | None
|
consecutive backend failures after which the backend is not
called for |
5
|
max_value_size
|
int | None
|
values larger than this many bytes (once encoded) aren't stored. |
None
|
distributed_lock
|
bool
|
on a miss, take a lock in the backend so that a single process
computes the value while the others wait for it (up to |
False
|
early_refresh
|
float | None
|
fraction of the lifetime at its end in which a hit may recompute the value (more likely as the expiry nears), so that popular entries are refreshed before they expire instead of all callers missing at once. E.g. 0.1. |
None
|
compression
|
Codec | None
|
"zstd" (Python 3.14, or the |
None
|
metrics
|
bool
|
record OpenTelemetry metrics (needs the |
True
|
meter_provider
|
Any
|
OpenTelemetry meter provider; the global one by default. |
None
|
tracing
|
bool
|
record OpenTelemetry spans of cached calls and backend calls ( |
True
|
tracer_provider
|
Any
|
OpenTelemetry tracer provider; the global one by default. |
None
|
cached
¶
cached(expire: int | None = None, coder: type[Coder] | None = None, key_builder: KeyBuilder | None = None, namespace: str = '', injected_dependency_namespace: str = '__fastapi_alcazaba', **options: Any) -> _CacheDecorator
Like fastapi_alcazaba.response_cache.cache, with this cache instead of the global
FastAPICache.
get_or_set
async
¶
get_or_set(key: str, compute: Callable[[], Awaitable[T]], *, expire: int | None = None, namespace: str = '', tags: Tags | None = None) -> T
The value cached under key, or else await compute(), stored. For caching inside an
endpoint what doesn't fit a decorator. Same pipeline as cached() (signing, encryption,
compression, single flight, locks); annotate compute's return type to get it back as
that type. The key is hashed: user input in it never reaches the backend in clear.
state_key
¶
state_key(kind: str, rest: str) -> str
Backend key of state other than cached responses: locks, rate-limit counters, revoked
tokens, one-time codes, idempotency records. It goes under {prefix}!, not {prefix}:,
so that clear() never frees a held lock, resets a rate limit or takes a revocation
back, and InMemoryBackend keeps it apart from the entries it evicts.
tag_key
¶
tag_key(tag: str) -> str
Backend key of a tag. The digest goes in {braces} (a cluster hash tag) so that the set and its temporary copy share a slot, whatever characters the tag has.
invalidate_tags
async
¶
invalidate_tags(*tags: str) -> int
Delete every entry stored with any of tags (see cached(tags=...)).
lock
¶
lock(name: str, *, ttl: float = 30.0, wait: float | None = 0.0, renew: bool = True) -> Lock
A lock shared by every process using this backend (fastapi_alcazaba.background.Lock):
async with cache.lock("nightly-report", ttl=60):
...
ttl, wait and renew are those of Lock.
clear
async
¶
clear(namespace: str | None = None, key: str | None = None) -> int
Delete the entry key (a full cache key), or every entry of namespace, or of the
whole prefix when neither is given. Cached responses only: locks, counters, revoked tokens
and the like are kept apart (state_key).
close
async
¶
close() -> None
Close the backend if it holds resources (TieredBackend's listener, DynamoDB client).
fastapi_alcazaba.response_cache.get_cache
¶
get_cache(request: HTTPConnection) -> Cache
FastAPI dependency: the cache installed on the app (Cache.install), else FastAPICache's.
Works in WebSocket routes too.
@app.post("/products/{product_id}")
async def update(product_id: int, cache: Annotated[Cache, Depends(get_cache)]) -> None:
await cache.invalidate_tags(f"product:{product_id}")
fastapi_alcazaba.response_cache.FastAPICache
¶
The global cache that @cache uses: a Cache configured once with init(). For several
caches, or one per app, create Cache instances instead.
init
classmethod
¶
init(backend: Backend, *args: Any, **kwargs: Any) -> None
Configure the global cache, with the arguments of Cache; later calls are ignored
until reset().
invalidate_tags
async
classmethod
¶
invalidate_tags(*tags: str) -> int
Delete every entry stored with any of tags (see @cache(tags=...)).
lock
classmethod
¶
lock(name: str, *, ttl: float = 30.0, wait: float | None = 0.0, renew: bool = True) -> Lock
A lock shared by every process (see Cache.lock).
clear
async
classmethod
¶
clear(namespace: str | None = None, key: str | None = None) -> int
Delete the entry key (a full cache key), or every entry of namespace, or of the
whole prefix when neither is given.
fastapi_alcazaba.response_cache.CacheOptions
dataclass
¶
CacheOptions(protector: EntryProtector | None = None, vary_headers: tuple[str, ...] = DEFAULT_VARY_HEADERS, respect_cache_control: bool = False, single_flight: bool = True, max_value_size: int | None = None, distributed_lock: bool = False, lock_timeout: float = 10.0, lock_wait: float = 5.0, early_refresh: float | None = None, compressor: Compressor | None = None)
How a cache protects, varies, locks, refreshes and compresses its entries.
fastapi_alcazaba.response_cache.cache
¶
cache(expire: int | None = None, coder: type[Coder] | None = None, key_builder: KeyBuilder | None = None, namespace: str = '', injected_dependency_namespace: str = '__fastapi_alcazaba', *, exclude_params: Collection[str] | None = None, vary_headers: Iterable[str] | None = None, respect_cache_control: bool | None = None, stale_while_revalidate: int | None = None, single_flight: bool | None = None, max_value_size: int | None = None, distributed_lock: bool | None = None, early_refresh: float | None = None, tags: Tags | None = None, instance: Cache | None = None) -> _CacheDecorator
Cache the result of an endpoint, or of any function, sync or async.
Settings left as None use the ones given to FastAPICache.init().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
expire
|
int | None
|
seconds to keep the entry. |
None
|
coder
|
type[Coder] | None
|
how the result is stored (JsonCoder by default). |
None
|
key_builder
|
KeyBuilder | None
|
builds the cache key from the function and its arguments. |
None
|
namespace
|
str
|
groups entries, to delete them together with FastAPICache.clear(). |
''
|
injected_dependency_namespace
|
str
|
prefix of the request and response parameters added to the endpoint when it doesn't declare them. |
'__fastapi_alcazaba'
|
exclude_params
|
Collection[str] | None
|
arguments left out of the cache key: calls that only differ in them share an entry (e.g. a nonce, or a database session whose repr changes with each call). |
None
|
vary_headers
|
Iterable[str] | None
|
request headers that give each value its own entry and make the
response |
None
|
respect_cache_control
|
bool | None
|
let clients skip the cache with Cache-Control no-cache or no-store. |
None
|
stale_while_revalidate
|
int | None
|
adds that directive to Cache-Control. |
None
|
single_flight
|
bool | None
|
concurrent misses for the same key wait for the first one. |
None
|
max_value_size
|
int | None
|
don't store values larger than this many bytes. |
None
|
distributed_lock
|
bool | None
|
one process computes a missing value while the others wait. |
None
|
early_refresh
|
float | None
|
refresh popular entries before they expire (a fraction of |
None
|
instance
|
Cache | None
|
the Cache to use instead of the global FastAPICache (what Cache.cached() passes). |
None
|
tags
|
Tags | None
|
names to invalidate the entry by, with FastAPICache.invalidate_tags(); a list,
or a function receiving the call's arguments, e.g. |
None
|
fastapi_alcazaba.response_cache.default_key_builder
¶
default_key_builder(func: Callable[..., Any], namespace: str = '', *, request: Request | None = None, response: Response | None = None, args: tuple[Any, ...], kwargs: dict[str, Any]) -> str
namespace plus the SHA-256 of the function's module, qualified name and the repr() of
its arguments.
SHA-256, not MD5, and __qualname__, not __name__, so that two methods with the same name
in one module don't share their entries. Request headers that change
the response, such as Authorization, are added by the decorator (vary_headers), whatever the
key builder.
fastapi_alcazaba.response_cache.Coder
¶
Turns values into bytes for the backend, and back.
decode_as_type
classmethod
¶
decode_as_type(value: bytes, *, type_: Any | None) -> Any
Decode value and validate it as type_ (the return annotation of the cached
function), so that a cache hit returns the same kind of object as a miss: a Pydantic
model or a dataclass, not the dict it was stored as. Raises pydantic.ValidationError when
the cached data doesn't fit the type (e.g. the model changed since it was cached).
fastapi_alcazaba.response_cache.JsonCoder
¶
Bases: Coder
JSON. Values without dates or Decimals outside Pydantic models (the usual case) go through pydantic-core, in Rust: some 14 times faster than jsonable_encoder() for a list of models, and hits with a return annotation are validated straight from the bytes.