Skip to main content

Production-ready idempotency library for async Python applications

Project description

idempotency-kit

Production-ready idempotency library for async Python applications.

PyPI Python License CI codecov Docs

Ensure operations execute exactly once, even when called multiple times with the same idempotency key. Built for production microservices with graceful degradation, collision handling, and observability.

Features

  • Clean Architecture — core domain separated from infrastructure
  • Protocol-Based — easy to swap storage backends (Redis, custom)
  • Type-Safe — full type hints with Pydantic validation
  • Async First — built for asyncio applications
  • Graceful Degradation — high availability over strict exactly-once
  • Collision Handling — automatic resolution of concurrent requests
  • Observability — built-in metrics (hits, misses, collisions, latency)
  • Bulk Operations — efficient get_many, save_many, delete_many
  • Redis Cluster Compatible — non-transactional pipelines
  • Decorator Pattern@async_idempotent for zero-boilerplate integration

Installation

pip install idempotency-kit

# With Redis support (recommended)
pip install idempotency-kit[redis]

# With Dishka DI
pip install idempotency-kit[dishka,redis]

Requirements: Python 3.11+, Redis 6+

Quick start

from idempotency_kit import AsyncIdempotencyCoordinator, PydanticResultAdapter, async_idempotent

class CreateOrderUseCase:
    def __init__(self, uow: AsyncUnitOfWork, coordinator: AsyncIdempotencyCoordinator):
        self._uow = uow
        self.coordinator = coordinator

    @async_idempotent(
        operation="order.create",
        adapter=PydanticResultAdapter(OrderDTO),
    )
    async def execute(
        self,
        dto: CreateOrderDTO,
        idempotency_key: str | None = None,
    ) -> OrderDTO:
        """Create order - idempotency handled automatically."""
        async with self._uow.transaction() as tx:
            # Your business logic - no idempotency code needed!
            order = await tx.orders.create(dto.items, dto.total)
            await tx.outbox.create(OrderCreatedEvent(order_id=order.id))
            
            return OrderDTO.from_entity(order)

Pass idempotency_key to downstream services for distributed idempotency:

# Orchestrate multiple services with same key
await identity_service.create_user(..., idempotency_key=idempotency_key)
await payment_service.charge(..., idempotency_key=idempotency_key)

How it works

1. Client provides key

POST /api/orders
Idempotency-Key: abc-123-def

2. Server checks cache

cached = await repo.get("order.create", "abc-123-def")
if cached:
    return cached.result  # Return immediately ✅

3. Server executes (if not cached)

order = await create_order(dto)
await repo.save(record)  # Cache result for future requests
return order

4. Concurrent requests handled

If two requests arrive simultaneously:

  • First request: cache miss → execute → save ✅
  • Second request: collision on save → fetch first result → return ✅

Both requests get the same result - idempotency guaranteed!

Use cases

  • HTTP APIs — ensure POST/PUT requests are idempotent
  • Background Jobs — prevent duplicate processing on retries
  • Event Consumers — handle duplicate events gracefully
  • Message Queues — at-most-once message processing

Documentation

📚 Full Documentation

Development

make install          # uv sync --group dev
make check            # ruff + mypy
make test-unit        # unit tests (no Docker)
make test-integration # integration tests (Docker required)
make test             # all tests with coverage
make docs-serve       # local docs preview

See CONTRIBUTING.md for the full guide.

License

Apache 2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

idempotency_kit-0.1.0.tar.gz (22.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

idempotency_kit-0.1.0-py3-none-any.whl (33.5 kB view details)

Uploaded Python 3

File details

Details for the file idempotency_kit-0.1.0.tar.gz.

File metadata

  • Download URL: idempotency_kit-0.1.0.tar.gz
  • Upload date:
  • Size: 22.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for idempotency_kit-0.1.0.tar.gz
Algorithm Hash digest
SHA256 d1056ec6af6645abcba19c532b7eecc6f5400253da54deea1dc5152633b39db2
MD5 f5b01f7cd54df5eb0e694e071927c4cb
BLAKE2b-256 acbfc97cfc86f442440094e639a3adb58df2fe04d14a311f57abea85702baecf

See more details on using hashes here.

File details

Details for the file idempotency_kit-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: idempotency_kit-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 33.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for idempotency_kit-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 38368c2cbcbd80d3dd35f169b7bb467712941bb39e59cfd82b428e7c41a24d6c
MD5 fd597620395e1613f7df2dbb4786e6a3
BLAKE2b-256 3c62374a7f64a9d1b9103cc74ccda2dc4cb096ee12121f5f333c8362e29b7fbb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page