Cache
- Start here: Cache guide
- Common recipes:
@cached,TTLCache - Configuration: Backend setup, Redis backend configuration
grelmicro.cache
Cache.
cached, CachedFunction and CachedStream load with the package. Each
other name loads its submodule on first access.
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: timedelta,
tags: Sequence[str] = (),
) -> None
Store raw bytes with a TTL 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. The backend must keep the entry for at least this long, rounded up to its own resolution, and drop it once that has 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: timedelta,
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 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.
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.
CachedFunction
A function wrapped by @cached.
Calling it reads through the cache. The helpers alongside it act on
the whole TTLCache backing the function, not on one entry.
refresh
refresh(*args: args, **kwargs: kwargs) -> R
Recompute for these arguments, store the result, and return it.
Skips the read, so a live entry is replaced rather than served.
Use it for a caller-driven refresh, such as an endpoint honouring
Cache-Control: no-cache.
Each caller recomputes, so a refresh never returns a value
computed before the call started. Under the default lock,
refreshes for one key also serialize, within the process and,
with lock="host" or lock="cluster", through the app's lock
backend. An error
propagates, even under stale_ttl, because a caller asking for
fresh data is not served the value it asked to bypass.
A result the skip predicate rejects is not stored, so the
previous entry stays and keeps being served.
Accessed on an instance, call it as Class.method.refresh(obj,
...). Attribute access on a bound method does not bind self
for the helper.
Returns a coroutine for an async function and the value itself for a sync one, matching the call it mirrors.
cache_clear
cache_clear() -> Awaitable[None]
Remove every entry from the cache backing this function.
Always a coroutine, even for a sync function, so it must be awaited.
CachedStream
An async generator producer wrapped by @cached.
Iterating it streams the items. On a miss they arrive live from the producer and the assembled list is stored once the producer runs to completion. On a hit the stored list is replayed. A caller that stops reading early stores nothing, so a truncated stream never becomes the entry every later caller reads.
collect reads the same entry as a whole, so a streaming endpoint
and a buffered one share one producer, one key, and one execution.
collect
collect(
*args: args, **kwargs: kwargs
) -> Coroutine[Any, Any, list[T]]
Return the whole sequence, producing it on a miss.
The buffered read of the entry __call__ streams. Both share the
key, so whichever runs first populates it and the other serves it.
refresh
refresh(
*args: args, **kwargs: kwargs
) -> Coroutine[Any, Any, list[T]]
Produce the sequence again, store it, and return it whole.
Skips the read, so a live entry is replaced rather than served.
cache_clear
cache_clear() -> Awaitable[None]
Remove every entry from the cache backing this producer.
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: int | timedelta = 60,
*,
name: str = "default",
backend: CacheBackend | None = None,
serializer: CacheSerializer[T]
| type[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()).
TTLCache[User]() serializes with PydanticSerializer(User)
unless a serializer is given. TTLCache(), TTLCache[bytes](),
and any type parameter Pydantic cannot build a schema for store
bytes raw and other values as JSON: None, bool, int,
float, str, and list, tuple, or dict with str
keys holding those. They read back as JSON gives them, so a tuple
comes back as a list. Any other value raises TypeError.
| RAISES | DESCRIPTION |
|---|---|
SettingsValidationError
|
If |
Initialize the cache.
| PARAMETER | DESCRIPTION |
|---|---|
maxsize
|
Maximum number of entries.
TYPE:
|
ttl
|
Default TTL for all entries, in whole seconds or as a
TYPE:
|
name
|
The cache name, carried by every metric this cache emits. Name each cache an app builds, so a hit rate can be read per cache rather than for the process as a whole.
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:
|
name
property
name: str
Return the cache name carried by its metrics.
config
property
config: TTLCacheConfig
Return the frozen config snapshot.
from_config
classmethod
from_config(
config: TTLCacheConfig,
*,
name: str = "default",
backend: CacheBackend | None = None,
serializer: CacheSerializer[T]
| type[T]
| None = None,
) -> Self
Construct a TTLCache from a pre-built TTLCacheConfig.
TTLCache reads no environment variable, so this differs from the
constructor only in taking the settings as one object.
| PARAMETER | DESCRIPTION |
|---|---|
config
|
The pre-built cache configuration.
TYPE:
|
name
|
The cache name, carried by every metric this cache emits.
TYPE:
|
backend
|
The cache storage backend.
TYPE:
|
serializer
|
Serialization strategy for cached values.
TYPE:
|
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: int | timedelta | None = None,
*,
tags: Sequence[str] = (),
stale_ttl: int | timedelta | 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. With no serializer, bytes or a JSON value.
TYPE:
|
ttl
|
Per-entry TTL override, in whole seconds or as a
TYPE:
|
tags
|
Tags to associate with the entry. Invalidate every entry sharing a tag at once with
TYPE:
|
stale_ttl
|
Extra time to keep a fallback copy of the value past its TTL, in whole seconds or as a
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If ttl or stale_ttl is a float, not positive, or over 100 years. |
TypeError
|
If the value is neither bytes nor JSON and no serializer is set. |
get_or_set
async
get_or_set(
key: str,
factory: Callable[[], T]
| Callable[[], Awaitable[T]],
*,
ttl: int | timedelta | None = None,
tags: Sequence[str] = (),
stale_ttl: int | timedelta | None = None,
lock: BackendScope | None = "process",
) -> 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, as far as lock says.
With stale_ttl set, a factory that raises on a miss serves
the most recent value (kept for stale_ttl 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 whole seconds or as a
TYPE:
|
tags
|
Tags to associate with the entry when it is computed.
TYPE:
|
stale_ttl
|
Extra time to keep a fallback copy past the TTL, in whole seconds or as a
TYPE:
|
lock
|
How far concurrent misses on
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If ttl or stale_ttl is a float, not positive, or over 100 years. |
SettingsValidationError
|
If |
OutOfContextError
|
If |
BackendScopeError
|
If the app runs in |
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: int | timedelta | 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 whole seconds or as a
TYPE:
|
tags
|
Tags to associate with every stored entry.
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If ttl is a float, not positive, or over 100 years. |
TypeError
|
If a value is neither bytes nor JSON 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')
T
module-attribute
T = TypeVar('T')
logger
module-attribute
logger = logging.getLogger(__name__)
CachedFunction
A function wrapped by @cached.
Calling it reads through the cache. The helpers alongside it act on
the whole TTLCache backing the function, not on one entry.
refresh
refresh(*args: args, **kwargs: kwargs) -> R
Recompute for these arguments, store the result, and return it.
Skips the read, so a live entry is replaced rather than served.
Use it for a caller-driven refresh, such as an endpoint honouring
Cache-Control: no-cache.
Each caller recomputes, so a refresh never returns a value
computed before the call started. Under the default lock,
refreshes for one key also serialize, within the process and,
with lock="host" or lock="cluster", through the app's lock
backend. An error
propagates, even under stale_ttl, because a caller asking for
fresh data is not served the value it asked to bypass.
A result the skip predicate rejects is not stored, so the
previous entry stays and keeps being served.
Accessed on an instance, call it as Class.method.refresh(obj,
...). Attribute access on a bound method does not bind self
for the helper.
Returns a coroutine for an async function and the value itself for a sync one, matching the call it mirrors.
cache_clear
cache_clear() -> Awaitable[None]
Remove every entry from the cache backing this function.
Always a coroutine, even for a sync function, so it must be awaited.
CachedStream
An async generator producer wrapped by @cached.
Iterating it streams the items. On a miss they arrive live from the producer and the assembled list is stored once the producer runs to completion. On a hit the stored list is replayed. A caller that stops reading early stores nothing, so a truncated stream never becomes the entry every later caller reads.
collect reads the same entry as a whole, so a streaming endpoint
and a buffered one share one producer, one key, and one execution.
collect
collect(
*args: args, **kwargs: kwargs
) -> Coroutine[Any, Any, list[T]]
Return the whole sequence, producing it on a miss.
The buffered read of the entry __call__ streams. Both share the
key, so whichever runs first populates it and the other serves it.
refresh
refresh(
*args: args, **kwargs: kwargs
) -> Coroutine[Any, Any, list[T]]
Produce the sequence again, store it, and return it whole.
Skips the read, so a live entry is replaced rather than served.
cache_clear
cache_clear() -> Awaitable[None]
Remove every entry from the cache backing this producer.
cached
cached(
cache: TTLCache | None = None,
*,
ttl: int | timedelta | None = None,
maxsize: int = 0,
key: KeyFunction | None = None,
key_template: str | None = None,
skip: Callable[[Any], bool] | None = None,
typed: bool = False,
lock: BackendScope | None = "process",
early: float | None = None,
stale_ttl: int | timedelta | None = None,
tags: Sequence[str] = (),
) -> _CachedDecorator
Cache decorator for async functions, and for sync functions with a cache.
Automatically detects whether the decorated function is sync or
async and wraps it accordingly. The ttl= form supports async
functions only and raises TypeError on a def, because the
private cache it builds has no event loop for a sync wrapper. Pass a
TTLCache to cache a sync function.
An async generator producer is wrapped as a
CachedStream: iterating it streams
the items and stores the assembled list once the producer completes,
and collect() reads that same entry whole. A sync generator is not
supported and raises.
The decorated function exposes refresh(), which recomputes for
the given arguments and overwrites the stored entry, plus
cache_info() and cache_clear(). The last two act on the whole
TTLCache, so on a cache shared between functions they report and
clear every function's entries, not only this one's.
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 for the private process-local cache, in whole seconds or
as a Only valid when
TYPE:
|
maxsize
|
Maximum number of entries in the private process-local
cache.
TYPE:
|
key
|
Derive the cache key from the call. Receives
TYPE:
|
key_template
|
Cache key template rendered from the call's arguments, so
TYPE:
|
skip
|
Optional predicate receiving the function result. When
it returns
TYPE:
|
typed
|
If
TYPE:
|
lock
|
How far concurrent misses on one key share a single call of the function.
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 whole seconds or as a
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 |
SettingsValidationError
|
If |
| RETURNS | DESCRIPTION |
|---|---|
_CachedDecorator
|
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, timed on the monotonic clock in whole nanoseconds. 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.
scope
class-attribute
scope: BackendScope = 'process'
State lives in this process and is not shared beyond it.
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: timedelta,
tags: Sequence[str] = (),
) -> None
Store raw bytes with a TTL 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: timedelta,
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:
|
scope
class-attribute
scope: BackendScope = 'cluster'
State is shared by every process that connects to it.
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: timedelta,
tags: Sequence[str] = (),
) -> None
Store raw bytes with a TTL and optional tags.
The TTL is rounded up to the millisecond.
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: timedelta,
tags: Sequence[str] = (),
) -> None
Store many keys with one TTL and optional tags.
The TTL is rounded up to the millisecond.
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:
|
scope
class-attribute
scope: BackendScope = 'cluster'
State is shared by every process that connects to it.
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: timedelta,
tags: Sequence[str] = (),
) -> None
Store raw bytes with a TTL and optional tags.
The TTL is kept to the microsecond. 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: timedelta,
tags: Sequence[str] = (),
) -> None
Store many keys with one TTL and optional tags.
The TTL is kept to the microsecond. 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.