Cache
- Start here: Cache guide
- Common recipes:
@cached,TTLCache - Configuration: Backend setup, Redis backend configuration
grelmicro.cache
Cache.
CacheBackend
Bases: Protocol
Protocol for cache storage backends.
All methods are async because backends typically perform I/O.
Backends are pure key-value stores: TTL, eviction, and statistics
are managed by TTLCache.
Implementations capture the running event loop on __aenter__
in a _loop attribute so the sync @cached wrapper can
dispatch coroutines back into it.
get
async
get(*, key: str) -> bytes | None
Get raw bytes by key.
Returns None if the key is missing or expired.
| PARAMETER | DESCRIPTION |
|---|---|
key
|
Fully qualified cache key, already namespaced by
TYPE:
|
set
async
set(
*,
key: str,
value: bytes,
ttl: float,
tags: Sequence[str] = (),
) -> None
Store raw bytes with a TTL in seconds and optional tags.
| PARAMETER | DESCRIPTION |
|---|---|
key
|
Fully qualified cache key, already namespaced by
TYPE:
|
value
|
Serialized payload to store. Opaque to the backend.
TYPE:
|
ttl
|
Time-to-live in seconds. The backend must drop the entry once this many seconds have elapsed since the write.
TYPE:
|
tags
|
Tags to associate with the key. The backend writes the value and the tag membership in one atomic operation.
TYPE:
|
get_many
async
get_many(*, keys: Sequence[str]) -> dict[str, bytes]
Get raw bytes for many keys at once.
Returns a dict holding only the keys that were found. Missing or expired keys are absent from the result.
| PARAMETER | DESCRIPTION |
|---|---|
keys
|
Fully qualified cache keys to read.
TYPE:
|
set_many
async
set_many(
*,
items: Mapping[str, bytes],
ttl: float,
tags: Sequence[str] = (),
) -> None
Store many keys with one TTL and optional tags.
| PARAMETER | DESCRIPTION |
|---|---|
items
|
Fully qualified key to serialized payload.
TYPE:
|
ttl
|
Time-to-live in seconds applied to every written key.
TYPE:
|
tags
|
Tags to associate with every written key.
TYPE:
|
delete
async
delete(*, key: str) -> None
Delete a key and clean its tag membership (no-op if absent).
| PARAMETER | DESCRIPTION |
|---|---|
key
|
Fully qualified cache key. No-op if absent.
TYPE:
|
delete_many
async
delete_many(*, keys: Sequence[str]) -> None
Delete many keys and clean their tag membership.
| PARAMETER | DESCRIPTION |
|---|---|
keys
|
Fully qualified cache keys to delete.
TYPE:
|
delete_tags
async
delete_tags(*, tags: Sequence[str]) -> None
Delete every key associated with any of the given tags.
| PARAMETER | DESCRIPTION |
|---|---|
tags
|
Tags whose every member key should be deleted.
TYPE:
|
clear
async
clear() -> None
Remove all entries managed by this backend.
CacheError
Bases: GrelmicroError
Base cache error.
CacheInfo
dataclass
CacheInfo(
hits: int,
misses: int,
maxsize: int,
currsize: int,
evictions: int,
)
Cache statistics snapshot.
| ATTRIBUTE | DESCRIPTION |
|---|---|
hits |
Number of cache hits.
TYPE:
|
misses |
Number of cache misses.
TYPE:
|
maxsize |
Maximum number of entries (
TYPE:
|
currsize |
Current number of tracked entries.
TYPE:
|
evictions |
Number of entries evicted to make room.
TYPE:
|
hits
instance-attribute
hits: int
misses
instance-attribute
misses: int
maxsize
instance-attribute
maxsize: int
currsize
instance-attribute
currsize: int
evictions
instance-attribute
evictions: int
CacheSerializer
Bases: Protocol[T]
Protocol for cache serialization strategies.
Any object implementing dumps and loads can be used
as a TTLCache serializer.
dumps
dumps(value: T) -> bytes
Serialize a value to bytes.
loads
loads(data: bytes) -> T
Deserialize bytes to a value.
CacheSettingsValidationError
CacheSettingsValidationError(error: ValidationError | str)
JsonSerializer
Serialize values as JSON bytes.
Uses orjson when available (roughly 7x faster than stdlib),
otherwise falls back to the standard library json module.
Suitable for dicts, lists, and other JSON-native types.
datetime objects are serialized to ISO 8601 strings but
deserialized back as strings (not datetime).
dumps
dumps(value: JSONEncodable) -> bytes
Serialize a value to JSON bytes.
loads
loads(data: bytes) -> JSONDecodable
Deserialize JSON bytes to a value.
PickleSerializer
PickleSerializer(*, protocol: int = HIGHEST_PROTOCOL)
Bases: Generic[T]
Serialize values using Python pickle.
Supports any picklable Python object. Fast and transparent, but produces opaque binary data.
Danger
Deserialization can execute arbitrary code. A compromised cache
backend can run code inside the application process. Use this
serializer only when the backend is fully trusted (in-process,
single-tenant). For shared backends (Redis, Memcached, any
multi-tenant store), use JsonSerializer or
PydanticSerializer instead.
| PARAMETER | DESCRIPTION |
|---|---|
protocol
|
Serialization protocol version. Defaults to the highest available protocol.
TYPE:
|
Initialize the pickle serializer.
dumps
dumps(value: T) -> bytes
Serialize a value to bytes.
loads
loads(data: bytes) -> T
Deserialize bytes to a value.
PydanticSerializer
PydanticSerializer(model: type[T])
Bases: Generic[T]
Serialize values using Pydantic's TypeAdapter.
Uses Pydantic's Rust-based serializer for fast, type-safe
roundtrips. Works with BaseModel, dataclass,
TypedDict, and any type supported by TypeAdapter.
This is the fastest serialization option (benchmarked at roughly 2x faster than pickle for Pydantic models).
| PARAMETER | DESCRIPTION |
|---|---|
model
|
The type to serialize/deserialize. Can be any type
supported by
TYPE:
|
Initialize the Pydantic serializer.
dumps
dumps(value: T) -> bytes
Serialize a value to JSON bytes via TypeAdapter.
loads
loads(data: bytes) -> T
Deserialize JSON bytes to a typed value via TypeAdapter.
TTLCache
TTLCache(
maxsize: int = 0,
ttl: float = 60,
*,
backend: CacheBackend | None = None,
serializer: CacheSerializer[T] | None = None,
)
Bases: Generic[T]
Cache with per-entry TTL and optional LRU eviction.
Delegates storage to a CacheBackend (in-memory, Redis, etc.).
TTLCache handles maxsize enforcement, LRU eviction, serialization,
and statistics on top of the backend.
When no backend is provided, the registered default is used
(see MemoryCacheAdapter or RedisCacheAdapter).
The type parameter T represents the cached value type.
Defaults to Any when unspecified (TTLCache()).
Use TTLCache[User](serializer=PydanticSerializer(User))
for typed caching.
| RAISES | DESCRIPTION |
|---|---|
ValidationError
|
If |
Initialize the cache.
| PARAMETER | DESCRIPTION |
|---|---|
maxsize
|
Maximum number of entries.
TYPE:
|
ttl
|
Default TTL in seconds for all entries.
TYPE:
|
backend
|
The cache storage backend. By default, the registered cache backend is used.
TYPE:
|
serializer
|
Serialization strategy for cached values. Any object implementing the Built-in options:
TYPE:
|
config
property
config: TTLCacheConfig
Return the frozen config snapshot.
get
async
get(key: str, default: T | None = None) -> T | None
Get a value by key.
Returns the default if the key is missing or expired. A hit promotes the key in LRU order.
| PARAMETER | DESCRIPTION |
|---|---|
key
|
The cache key.
TYPE:
|
default
|
Value to return if the key is missing or expired.
TYPE:
|
set
async
set(
key: str,
value: T,
ttl: float | None = None,
*,
tags: Sequence[str] = (),
stale_ttl: float | None = None,
) -> None
Set a value with an optional per-entry TTL override and tags.
If the cache is full (maxsize > 0), evicts the least recently used entry before storing.
| PARAMETER | DESCRIPTION |
|---|---|
key
|
The cache key.
TYPE:
|
value
|
The value to store. Must be bytes or serializable.
TYPE:
|
ttl
|
Per-entry TTL override in seconds. Uses the default TTL if None.
TYPE:
|
tags
|
Tags to associate with the entry. Invalidate every entry sharing a tag at once with
TYPE:
|
stale_ttl
|
Extra seconds to keep a fallback copy of the value past its TTL. When set,
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If ttl or stale_ttl is not positive. |
TypeError
|
If value is not bytes and no serializer is set. |
get_or_set
async
get_or_set(
key: str,
factory: Callable[[], T] | Callable[[], Awaitable[T]],
*,
ttl: float | None = None,
tags: Sequence[str] = (),
stale_ttl: float | None = None,
) -> T
Return the cached value, or compute, store, and return it.
On a hit the cached value is returned and a hit is recorded. On a
miss the factory runs once under stampede protection, the
result is stored with the given ttl and tags, and a miss
is recorded by the initial read. Concurrent misses on the same
key fold into a single computation, across replicas when a lock
backend is configured.
With stale_ttl set, a factory that raises on a miss serves
the most recent value (kept for stale_ttl seconds past its TTL)
instead of propagating the error.
| PARAMETER | DESCRIPTION |
|---|---|
key
|
The cache key.
TYPE:
|
factory
|
Sync or async callable that produces the value on a miss. Awaited when it returns a coroutine.
TYPE:
|
ttl
|
Per-entry TTL override in seconds. Uses the default if None.
TYPE:
|
tags
|
Tags to associate with the entry when it is computed.
TYPE:
|
stale_ttl
|
Extra seconds to keep a fallback copy past the TTL. When set and the
TYPE:
|
get_many
async
get_many(keys: Sequence[str]) -> dict[str, T]
Return a dict of found values for the given keys.
Missing or expired keys are absent from the result. Records one hit per found key and one miss per absent key.
| PARAMETER | DESCRIPTION |
|---|---|
keys
|
The cache keys to read.
TYPE:
|
set_many
async
set_many(
mapping: Mapping[str, T],
*,
ttl: float | None = None,
tags: Sequence[str] = (),
) -> None
Store many key to value pairs with one TTL and optional tags.
| PARAMETER | DESCRIPTION |
|---|---|
mapping
|
Key to value pairs to store.
TYPE:
|
ttl
|
Per-entry TTL override in seconds. Uses the default if None.
TYPE:
|
tags
|
Tags to associate with every stored entry.
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If ttl is not positive. |
TypeError
|
If a value is not bytes and no serializer is set. |
delete
async
delete(key: str) -> None
Delete a key from the cache.
Also drops the stale-reserve copy, so an explicit delete is never undone by a later stale serve. No-op if the key does not exist.
| PARAMETER | DESCRIPTION |
|---|---|
key
|
The cache key to delete.
TYPE:
|
delete_many
async
delete_many(keys: Sequence[str]) -> None
Delete many keys from the cache.
Keys that do not exist are ignored.
| PARAMETER | DESCRIPTION |
|---|---|
keys
|
The cache keys to delete.
TYPE:
|
delete_tags
async
delete_tags(*tags: str) -> None
Delete every entry associated with any of the given tags.
Clears the local LRU bookkeeping, since the deleted keys are not known in advance.
| PARAMETER | DESCRIPTION |
|---|---|
*tags
|
Tags whose every entry should be deleted.
TYPE:
|
clear
async
clear() -> None
Remove all entries from the cache.
cached
Cached Decorator.
P
module-attribute
P = ParamSpec('P')
R
module-attribute
R = TypeVar('R')
cached
cached(
cache: TTLCache | None = None,
*,
ttl: float | None = None,
maxsize: int = 0,
key: str | None = None,
key_maker: Callable[
[
Callable[..., Any],
tuple[Any, ...],
dict[str, Any],
],
str,
]
| None = None,
skip: Callable[[Any], bool] | None = None,
typed: bool = False,
lock: bool | Literal["local"] = "local",
early: float | None = None,
stale_ttl: float | None = None,
tags: Sequence[str] = (),
) -> Callable[[Callable[P, R]], Callable[P, R]]
Cache decorator for sync and async functions.
Automatically detects whether the decorated function is sync or async and wraps it accordingly.
The decorated function exposes cache_info() and
cache_clear() helpers (matching functools.lru_cache).
cache_clear() is always a coroutine (must be awaited).
Pass a TTLCache as cache to share one store across
functions, invalidate by tag, or override it in tests. For the
plain memoize case, omit cache and pass ttl= instead. The
decorator then builds a private process-local cache for this
function alone.
| PARAMETER | DESCRIPTION |
|---|---|
cache
|
The TTLCache instance to store results in. Omit it and pass
TYPE:
|
ttl
|
TTL in seconds for the private process-local cache. Only valid when
TYPE:
|
maxsize
|
Maximum number of entries in the private process-local
cache.
TYPE:
|
key
|
Cache key template rendered from the call's arguments, so
TYPE:
|
key_maker
|
Optional custom key generation function. Receives
TYPE:
|
skip
|
Optional predicate receiving the function result. When
it returns
TYPE:
|
typed
|
If
TYPE:
|
lock
|
Protect against duplicate work when many callers miss the same key at once (the "dog-pile" effect).
TYPE:
|
early
|
Probabilistic early refresh (XFetch). A float in Costs one extra recompute per refresh. Leave
TYPE:
|
stale_ttl
|
Serve-stale-on-error budget in seconds. When set, each cached
result is also kept as a fallback copy for Composes with
TYPE:
|
tags
|
Tags to associate with each cached result. Each tag is a
template rendered from the call's arguments, so
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
TypeError
|
If both |
ValueError
|
If |
| RETURNS | DESCRIPTION |
|---|---|
Callable[[Callable[P, R]], Callable[P, R]]
|
A decorator that caches function results. |
grelmicro.cache.memory
Memory Cache Adapter.
MemoryCacheAdapter
MemoryCacheAdapter()
Bases: CacheBackend
In-memory cache backend.
Stores entries in a Python dict with lazy TTL expiry. Suitable for testing and single-process applications.
Tag membership is tracked with two dicts kept in sync: a forward map from tag to its set of keys, and a reverse map from key to its set of tags. The reverse map lets a delete or a lazy expiry remove the key from every tag it belonged to, so no tag ever points at a key that is gone.
Initialize the memory cache backend.
get
async
get(*, key: str) -> bytes | None
Get raw bytes by key.
Returns None if the key is missing or expired. Expired entries are removed lazily on access.
set
async
set(
*,
key: str,
value: bytes,
ttl: float,
tags: Sequence[str] = (),
) -> None
Store raw bytes with a TTL in seconds and optional tags.
get_many
async
get_many(*, keys: Sequence[str]) -> dict[str, bytes]
Get raw bytes for many keys, returning only found entries.
set_many
async
set_many(
*,
items: Mapping[str, bytes],
ttl: float,
tags: Sequence[str] = (),
) -> None
Store many keys with one TTL and optional tags.
delete
async
delete(*, key: str) -> None
Delete a key and clean its tag membership (no-op if absent).
delete_many
async
delete_many(*, keys: Sequence[str]) -> None
Delete many keys and clean their tag membership.
delete_tags
async
delete_tags(*, tags: Sequence[str]) -> None
Delete every key associated with any of the given tags.
clear
async
clear() -> None
Remove all entries.
grelmicro.cache.redis
Redis Cache Adapter.
RedisCacheAdapter
RedisCacheAdapter(
*,
provider: RedisProvider | None = None,
env_prefix: str = "REDIS_",
prefix: str = "",
)
Bases: CacheBackend
Redis cache storage backend.
Wraps a RedisProvider and implements the cache protocol:
get, set (with per-entry TTL via SET ... PX), delete,
batch operations, tag-based invalidation, and a prefix-scoped
clear. Pass an explicit provider= to share a pool with other
components, or rely on the default env_prefix= to build one from
environment variables.
Each tag holds the fully qualified keys tagged with it. A reverse
record next to each value lists the value's tags and self-expires
with the value, so an expired key never leaves a stale tag entry
behind. Both live under the cache prefix, so clear sweeps them too.
Initialize the Redis cache backend.
| PARAMETER | DESCRIPTION |
|---|---|
provider
|
A pre-built
TYPE:
|
env_prefix
|
Environment variable prefix used by the implicit
TYPE:
|
prefix
|
Prefix prepended to every Redis key (cache namespace).
TYPE:
|
provider
property
provider: RedisProvider
The bound RedisProvider.
get
async
get(*, key: str) -> bytes | None
Get raw bytes by key.
Returns None if the key is missing or expired.
set
async
set(
*,
key: str,
value: bytes,
ttl: float,
tags: Sequence[str] = (),
) -> None
Store raw bytes with a TTL in seconds and optional tags.
get_many
async
get_many(*, keys: Sequence[str]) -> dict[str, bytes]
Get raw bytes for many keys, returning only found entries.
set_many
async
set_many(
*,
items: Mapping[str, bytes],
ttl: float,
tags: Sequence[str] = (),
) -> None
Store many keys with one TTL and optional tags.
delete
async
delete(*, key: str) -> None
Delete a key and clean its tag membership (no-op if absent).
delete_many
async
delete_many(*, keys: Sequence[str]) -> None
Delete many keys and clean their tag membership.
delete_tags
async
delete_tags(*, tags: Sequence[str]) -> None
Delete every key associated with any of the given tags.
clear
async
clear() -> None
Remove all entries matching the configured prefix.
Uses SCAN to iterate keys without blocking Redis, then deletes in batches. Tag and reverse-tag sets live under the same prefix, so this sweeps them too.
grelmicro.cache.postgres
Postgres Cache Adapter.
PostgresCacheAdapter
PostgresCacheAdapter(
*,
provider: PostgresProvider | None = None,
env_prefix: str = "POSTGRES_",
prefix: str = "",
table_name: str = "grelmicro_cache",
auto_migrate: bool = True,
cleanup_interval: float | None = None,
)
Bases: CacheBackend
Postgres cache storage backend.
Wraps a PostgresProvider and implements the cache protocol:
get, set (with per-entry TTL via expires_at), delete,
batch operations, tag-based invalidation, and a prefix-scoped
clear. Entries live in a single table keyed on key with
value BYTEA and expires_at TIMESTAMPTZ.
Tags live in a companion table that maps each key to its tags. A
foreign key with ON DELETE CASCADE removes a key's tag rows
whenever the key row goes away, so deleting by tag, by key, or by
janitor never leaves an orphan tag row behind.
Pass an explicit provider= to share a pool with other
components, or rely on the default env_prefix= to build one
from environment variables.
Set cleanup_interval= to enable a background janitor that
deletes expired rows. Off by default. Lazy expiry on get
keeps reads correct, the janitor only reclaims storage.
Example:
from grelmicro.cache import Cache
from grelmicro.providers.postgres import PostgresProvider
postgres = PostgresProvider("postgresql://localhost:5432/app")
cache = Cache(postgres)
Read more in the Cache docs.
Initialize the Postgres cache backend.
| PARAMETER | DESCRIPTION |
|---|---|
provider
|
A pre-built
TYPE:
|
env_prefix
|
Environment variable prefix used by the implicit
TYPE:
|
prefix
|
Prefix prepended to every key (cache namespace).
TYPE:
|
table_name
|
Table that stores cache entries. Auto-created on first
connect (set
TYPE:
|
auto_migrate
|
When True (the default), the adapter creates the table
on
TYPE:
|
cleanup_interval
|
Period in seconds between janitor runs that delete
rows expired for more than one hour. Default
TYPE:
|
provider
property
provider: PostgresProvider
The bound PostgresProvider.
get
async
get(*, key: str) -> bytes | None
Get raw bytes by key.
Returns None if the key is missing or expired.
set
async
set(
*,
key: str,
value: bytes,
ttl: float,
tags: Sequence[str] = (),
) -> None
Store raw bytes with a TTL in seconds and optional tags.
The value upsert and the tag rows commit in one transaction.
get_many
async
get_many(*, keys: Sequence[str]) -> dict[str, bytes]
Get raw bytes for many keys, returning only found entries.
set_many
async
set_many(
*,
items: Mapping[str, bytes],
ttl: float,
tags: Sequence[str] = (),
) -> None
Store many keys with one TTL and optional tags.
Every value upsert and its tag rows commit in one transaction.
delete
async
delete(*, key: str) -> None
Delete a key (no-op if absent).
The cascade removes the key's tag rows.
delete_many
async
delete_many(*, keys: Sequence[str]) -> None
Delete many keys. The cascade removes their tag rows.
delete_tags
async
delete_tags(*, tags: Sequence[str]) -> None
Delete every key associated with any of the given tags.
One statement deletes the matching value rows, and the cascade cleans their tag rows.
clear
async
clear() -> None
Remove all entries matching the configured prefix.
Falls back to a full table delete when no prefix is set. The cascade cleans the matching tag rows.