API Reference¶
This document provides a detailed reference for all public classes and methods in idempotency-kit.
Core Layer¶
IdempotencyIdentifiers¶
Validation model for idempotency identifiers.
- Attributes:
operation(str): Name of the operation. Max 100 chars. Cannot contain ':'.idempotency_key(str): Unique key for this instance. Max 255 chars. Cannot contain ':'.
IdempotencyRecord¶
A frozen Pydantic model representing an idempotency result. Inherits from IdempotencyIdentifiers.
- Attributes:
operation(str): Name of the operation.idempotency_key(str): Unique key for this instance.result(JsonValue): The cached result — any JSON value (mapping, list, scalar);nullfor a void result.created_at(datetime): When the record was created.expires_at(datetime): When the record will expire. On a pending record this is the in-flight lease.status("pending" | "completed", default"completed"):"pending"while the action runs under an in-flight reservation,"completed"once the result is stored. A record written before the field existed reads as completed.fingerprint(str | None, defaultNone): Fingerprint of the request the record was made for. A hit with a different one is a key reuse;None— also how a record written before the field existed reads — never raises.- Methods:
create(operation: str, idempotency_key: str, result: JsonValue, ttl_seconds: float, fingerprint: str | None = None) -> IdempotencyRecord: Class method to create a new record.pending(operation: str, idempotency_key: str, lease_seconds: float, fingerprint: str | None = None) -> IdempotencyRecord: Class method to create the in-flight reservation;resultisnull.is_pending: Property returningTruefor an in-flight reservation.is_expired: Property returningTrueif current time is afterexpires_at.ttl_seconds: Property returning remaining TTL in seconds.
IdempotencyDomainService¶
Service for creating and validating records.
- Constructor (Parameters are keyword-only):
default_ttl_minutes(int, default: 60): Default TTL in minutes. Must be >= 1.min_ttl_seconds(int, default: 60): Minimum allowed TTL. Must be >= 1.max_ttl_seconds(int, default: 2592000): Maximum allowed TTL (30 days). Must be >=min_ttl_seconds.- Note: These three defaults live in
idempotency_kit.core.constantsand are also the field defaults ofBaseIdempotencySettings. - Note: Constructor validates that
default_ttl_minutes(converted to seconds) is within the[min_ttl_seconds, max_ttl_seconds]range. - Methods:
create_record(operation, idempotency_key, result, *, ttl_minutes=None, fingerprint=None): Creates a newIdempotencyRecordwith validation and TTL management.create_pending_record(operation, idempotency_key, *, lease_seconds, fingerprint=None): Creates the in-flight reservation. The lease is not held to the TTL bounds; it must be at least a second.validate_record(record): Validates that a record is still usable, raisingIdempotencyRecordExpiredErrorif not. The coordinator calls it on every record it reads.
AsyncIdempotencyCoordinator¶
The flow: reserve the key, run the action, store the result; replay a stored result; hand a second concurrent caller the first one's result.
- Constructor:
repository(AsyncIdempotencyRepository): Storage for records.domain_service(IdempotencyDomainService): Record factory and TTL bounds.operation_ttls(dict[str, int], optional): Per-operation TTLs in seconds; win over the decorator'sttl_seconds.metrics(IdempotencyMetricsProtocol, optional): Metrics collector.enabled(bool, default: True):Falseruns the action and nothing else.in_flight("wait" | "raise" | "run", default:"wait"): What a second caller gets while the first one's action is still running under the same key — the first caller's result once it lands,IdempotencyInProgressErrorat once, or a run of its own with the winner's result adopted on collision.in_flight_lease_seconds(int, default: 30): How long a reservation is held before it counts as abandoned, and the longest a waiting caller waits. Must be at least 1 and longer than the action can take.- Note: The constructor raises
TypeErrorwhen the repository has noreplaceandin_flightis not"run". - Methods:
coordinate(operation, idempotency_key, ttl_seconds, adapter, action, /, *args, idempotency_fingerprint=None, **kwargs): Runs the flow and returns the action's result type. Everything after the five positional-only arguments goes to the action exceptidempotency_fingerprint, the fingerprint of the request, which is stored with the record and compared on a hit. Never raises for storage or decode trouble; raisesIdempotencyInProgressErrorwhen the key is in flight andin_flightsays so, andIdempotencyKeyReuseErrorwhen the record's fingerprint differs from the caller's.
async_idempotent¶
The same flow as a decorator: it finds the key in the call's arguments and the coordinator in the call's arguments or on self, then delegates to coordinate().
- Parameters:
operation(str): Operation name.adapter(ResultAdapter): Encodes the result for storage and decodes it back.ttl_seconds(int, optional): TTL for the record; the coordinator'soperation_ttlswin over it.key_param(str, default:"idempotency_key"): Name of the parameter holding the key. Read from the keyword arguments, or from the positional ones when the parameter can be passed that way.infra_param(str, optional): Name of the argument or attribute holding the coordinator; without it the decorator searches by type.fingerprint_params(tuple[str, ...], optional): Names of the parameters whose bound values, defaults applied, identify the request. Their fingerprint is computed withfingerprint_ofand passed tocoordinate()asidempotency_fingerprint. A name the function does not have raisesTypeErrorat decoration time.
fingerprint_of¶
fingerprint_of(**values) -> str: the SHA-256 hex digest of the JSON form of the named values, keys sorted. Pydantic models, dataclasses, UUIDs, datetimes and Decimals are serialised the way Pydantic serialises them; a value with no JSON form raises pydantic_core.PydanticSerializationError. It is what the decorator computes from fingerprint_params, for a caller of coordinate() to use directly.
AsyncIdempotencyRepository (Protocol)¶
Interface for idempotency storage.
- Methods:
get(operation, idempotency_key): ReturnsIdempotencyRecordorNone.save(record): Saves a record with NX (not exists) guarantee.replace(record): Writes a record whether or not the key is there; how a reservation becomes a result.delete(operation, idempotency_key): Deletes a record. ReturnsTrueif deleted.get_many(operation, idempotency_keys): Returnsdict[str, IdempotencyRecord].save_many(records, *, rollback_on_error=False): Saves multiple records. NOT atomic unlessrollback_on_error=True.delete_many(operation, idempotency_keys): Deletes multiple records. Returns count.
IdempotencyMetricsProtocol¶
Interface for metrics collection.
- Methods:
record_hit(operation)record_miss(operation)record_collision(operation): Two callers on one key at the same time.record_error(operation, error_type)record_latency(operation, method, duration_seconds)record_bulk_hit(operation, count): For bulk operations.record_bulk_miss(operation, count): For bulk operations.- Who records what: the coordinator records hit, miss, collision and the latency of
get,reserveandsave; the repository records errors, the bulk hit and miss counts ofget_many, and the latency ofdeleteandget_many. One collector handed to both — which is what the Dishka providers do — therefore counts each operation once.
Infrastructure Layer¶
RedisAsyncIdempotencyRepository¶
Redis implementation of the repository protocol.
- Constructor:
redis(redis.asyncio.Redis | RedisCluster): Any async Redis client, single-node or cluster, including a subclass such as an instrumented or fake one.key_prefix(str, default: "idempotency:"): Prefix for all Redis keys. (keyword-only)metrics(IdempotencyMetricsProtocol, optional): Metrics collector. (keyword-only)- Storage:
SET key value EX ttl NXforsave, the same withoutNXforreplace,GETforget,MGETforget_many— one per hash slot, viamget_nonatomic, on aRedisCluster.
Exceptions¶
IdempotencyError(message): Base library exception.IdempotencyKeyCollisionError(operation, key): Raised when a key already exists.keycan be a singlestror alist[str]for bulk operations.IdempotencyRecordExpiredError(operation, key): Raised when record exists but is expired.IdempotencyInProgressError(operation, key): Raised bycoordinate()and the decorator when another call with the same key is still running its action.IdempotencyKeyReuseError(operation, key, stored_fingerprint, fingerprint): Raised bycoordinate()and the decorator when the record under the key was made for a request with a different fingerprint.IdempotencyStorageError(message, operation, original_error): Raised on storage failure.IdempotencyValidationError(message, errors=None): Raised for invalid input (e.g. empty key, too long string).errors(list, optional): Detailed Pydantic validation errors.IdempotencyInvalidTTLError(ttl_seconds, min_ttl, max_ttl): Raised for invalid TTL.