Skip to content

Idempotency

grelmicro.idempotency

Idempotency.

Stripe-style idempotency keys for safe retries.

Idempotency("name", ttl=...) stores a response under a caller-supplied key. A repeated key within ttl replays the stored response without running the operation again. Use the explicit block form for the most control, or the @idempotent decorator for a function-level shortcut.

Storage rides the cache layer. Pass an explicit cache= or leave it unset to resolve the active app's Cache component backend.

Idempotency

Idempotency(
    name: str,
    *,
    ttl: float | None = None,
    fingerprint: str | None = None,
    cache: TTLCache[T] | None = None,
    serializer: CacheSerializer[T] | None = None,
    env_prefix: str | None = None,
    env_load: bool | None = None,
)

Bases: Reconfigurable[IdempotencyConfig], Generic[T]

Idempotency keys for safe retries of an operation.

Each named Idempotency stores a response under a caller-supplied key for ttl seconds. A repeated key within that window replays the stored response without running the operation again. A duplicate arriving while the first execution is in flight waits and receives the stored response, across replicas when a lock backend is configured and in-process otherwise. An exception in the block stores nothing and a later retry with the same key executes fresh.

Storage rides the cache layer. Pass an explicit cache to bind a TTLCache, or leave it unset to resolve the active app's Cache component. Without either, OutOfContextError is raised on first use.

Supports live reconfiguration via reconfigure(new_config). A swap takes effect on the next call. In-flight calls keep the config they started with.

The type parameter T represents the stored response type. Defaults to Any when unspecified.

Initialize the idempotency namespace.

PARAMETER DESCRIPTION
name

The idempotency namespace.

Keys are scoped under this name, so the same key used by two different Idempotency instances does not collide.

TYPE: str

ttl

Lifetime in seconds of a stored response.

Default: 86400. When unset and env reads are enabled (see env_load and GREL_ENV_LOAD), resolves from the environment variable GREL_IDEMPOTENCY_TTL for the default instance (GREL_IDEMPOTENCY_{NAME_UPPER}_TTL for a named one) if present, otherwise falls back to the IdempotencyConfig default.

TYPE: float | None DEFAULT: None

fingerprint

Default payload fingerprint applied to every call.

A per-call fingerprint= on __call__ overrides this. A replay with a fingerprint different from the stored one raises IdempotencyConflictError. When None, no check.

TYPE: str | None DEFAULT: None

cache

The TTLCache used to store responses.

By default, a TTLCache is composed internally and resolves the active app's Cache component backend.

TYPE: TTLCache[T] | None DEFAULT: None

serializer

Serialization strategy for stored responses.

Ignored when an explicit cache is given. Otherwise passed to the internally composed TTLCache. Defaults to JsonSerializer.

TYPE: CacheSerializer[T] | None DEFAULT: None

env_prefix

Override the auto-derived environment variable prefix.

Default: GREL_IDEMPOTENCY_ for the default instance, GREL_IDEMPOTENCY_{NAME_UPPER}_ for a named one.

TYPE: str | None DEFAULT: None

env_load

Whether to read environment variables.

When None (the default), follow the process-wide GREL_ENV_LOAD flag. Pass True or False to override the flag for this construction.

TYPE: bool | None DEFAULT: None

name property

name: str

Return the idempotency namespace.

from_config classmethod

from_config(
    name: str,
    config: IdempotencyConfig,
    *,
    fingerprint: str | None = None,
    cache: TTLCache[T] | None = None,
    serializer: CacheSerializer[T] | None = None,
) -> Self

Construct an Idempotency from a name and a pre-built config.

PARAMETER DESCRIPTION
name

The idempotency namespace. Acts as the instance identity.

TYPE: str

config

The pre-built idempotency configuration.

Use this path when the configuration is assembled at startup from a settings tree. The environment path is bypassed and the config is used as-is.

TYPE: IdempotencyConfig

fingerprint

Default payload fingerprint applied to every call.

TYPE: str | None DEFAULT: None

cache

The TTLCache used to store responses.

TYPE: TTLCache[T] | None DEFAULT: None

serializer

Serialization strategy for stored responses.

TYPE: CacheSerializer[T] | None DEFAULT: None

run async

run(
    key: str,
    factory: Callable[[], T] | Callable[[], Awaitable[T]],
    *,
    fingerprint: str | None = None,
) -> T

Run an operation once for key, then replay its response.

On a first execution the factory runs, its result is stored, and the result is returned. A later call with the same key within ttl replays the stored response without running the factory again. A failing factory stores nothing, so a later retry runs fresh.

PARAMETER DESCRIPTION
key

The idempotency key derived from the request.

TYPE: str

factory

Sync or async callable that produces the response on a first execution. Awaited when it returns a coroutine.

TYPE: Callable[[], T] | Callable[[], Awaitable[T]]

fingerprint

Payload fingerprint for this call.

Overrides the instance-level fingerprint. A replay with a different fingerprint raises IdempotencyConflictError. When None, the instance default applies.

TYPE: str | None DEFAULT: None

IdempotencyConfig

Bases: BaseModel

Frozen snapshot of the Idempotency declarative settings.

Carries the settings that round-trip in serialized form. Runtime dependencies (cache, serializer, fingerprint) stay as constructor kwargs on Idempotency since they are object references or callables, not values.

ttl class-attribute instance-attribute

ttl: PositiveFloat = 86400

Lifetime in seconds of a stored response. A repeated key within this window replays the stored response. After it elapses, the key executes fresh.

IdempotencyConflictError

IdempotencyConflictError(*, name: str, key: str)

Bases: IdempotencyError

Raised when a key is replayed with a different payload fingerprint.

The same idempotency key arrived twice with different payloads. The stored fingerprint from the first execution does not match the fingerprint supplied on the replay, so the second call is rejected instead of returning a response computed for a different request.

Initialize the error.

name instance-attribute

name = name

key instance-attribute

key = key

IdempotencyError

Bases: GrelmicroError

Base idempotency error.

Operation

Operation(*, replayed: bool, response: T | None)

Bases: Generic[T]

Handle for a single idempotent operation, yielded by Idempotency.__call__.

On a replay, replayed is True and response carries the stored value. On a first execution, replayed is False and the body runs the work and calls store(response) to persist it.

Initialize the operation handle.

replayed property

replayed: bool

Whether the key was already stored and the response replayed.

response property

response: T | None

The stored response when replayed is True, else None.

store

store(response: T) -> None

Record the response to persist for this key.

Called once during a first execution. The value is written when the block exits without an exception. Calling it on a replay is a no-op.

PARAMETER DESCRIPTION
response

The response to store and replay on later calls.

TYPE: T

idempotent

idempotent(
    idempotency: Idempotency[Any],
    *,
    key: Callable[..., str],
    fingerprint: Callable[..., str] | None = None,
) -> Callable[[Callable[P, R]], Callable[P, R]]

Make an async function idempotent on a per-call key.

On a first call for a key, the function runs and its return value is stored. A later call with the same key within the configured ttl replays the stored value without running the function again. A failing call stores nothing, so a later retry executes fresh.

The decorated function must be a coroutine function.

PARAMETER DESCRIPTION
idempotency

The Idempotency instance that stores and replays responses.

TYPE: Idempotency[Any]

key

Derive the idempotency key from the call arguments. Receives the same positional and keyword arguments as the decorated function and returns the key string.

TYPE: Callable[..., str]

fingerprint

Optional payload fingerprint derived from the call arguments. Receives the same arguments as the decorated function. A replay with a different fingerprint raises IdempotencyConflictError. When None, the instance default applies.

TYPE: Callable[..., str] | None DEFAULT: None

RETURNS DESCRIPTION
Callable[[Callable[P, R]], Callable[P, R]]

A decorator that makes the function idempotent.