Skip to main content

fencekit

CI PyPI Python License: MIT

Redis-backed idempotency and fenced distributed locks for background jobs.

Celery with acks_late redelivers work after a worker crash. Redis gives you primitives (SET NX, Lua). fencekit wires them into three tested pieces:

Piece Problem it solves
IdempotencyGuard Double-start / redelivery of the same logical job
DistributedLock + fencing token Two workers on the same resource at once
FenceGate / fenced_update Stale lock holder overwriting newer state after TTL expiry

Extracted from ChessMate (Celery + Redis + Postgres). See DESIGN.md for guarantees, non-guarantees, and crash semantics.

Install

pip install fencekit
pip install "fencekit[django]"   # optional Django QuerySet helper

Requires Redis 6+ (tested with Redis 7) and Python 3.10+.

Quick example

from datetime import timedelta
from redis import Redis

from fencekit import (
    DistributedLock,
    FenceGate,
    IdempotencyGuard,
    IdempotencyResultMissing,
    fenced_update,
    idempotency_key,
)

r = Redis.from_url("redis://localhost:6379/0", decode_responses=True)
guard = IdempotencyGuard(r)
lock = DistributedLock(r)
fence = FenceGate(r)

def analyze_batch(job) -> dict | None:
    key = idempotency_key(
        {"game_ids": ["abc", "def"], "engine": "sf16"},
        namespace="analysis",
    )
    if not guard.try_begin(key, ttl=timedelta(hours=24)):
        try:
            return guard.get_result(key)  # prior outcome on redelivery
        except IdempotencyResultMissing:
            return None  # still pending or done without a memo

    handle = lock.acquire(f"analysis:{job.pk}", ttl=timedelta(minutes=5))
    try:
        fence.set_if_fresh(handle.token, f"analysis:{job.pk}:status", "running")
        fenced_update(
            type(job).objects.filter(pk=job.pk),
            handle.token,
            updates={"progress": 50, "status": "running"},
        )
        result = {"report_id": job.pk, "status": "done"}
        guard.mark_done(key, result=result, ttl=timedelta(hours=24))
        return result
    finally:
        lock.release(handle)

API overview

  • idempotency_key(payload, namespace=...) — deterministic key from JSON-canonicalized payload
  • IdempotencyGuard.try_begin / mark_done / get_result — at-most-once start; optional JSON memo on completion
  • DistributedLock.acquire / release / extend — lease + monotonic fencing token (Lua)
  • FenceGate.set_if_fresh — atomic fenced Redis string writes
  • fenced_update(queryset, token, updates=...) — fenced Django/Postgres UPDATE in one statement

Typed public API (py.typed). No Celery adapter yet; wire the guard in your task body for now.

Guarantees (honest)

Claim Status
At-most-once start within idempotency TTL Yes (SET NX)
Memoized result after mark_done(..., result=...) Yes (within TTL)
Mutual exclusion while lock TTL held (single Redis primary) Best-effort lease
Stale holder blocked via FenceGate / fenced_update Yes (when used)
Exactly-once delivery No
Safety under Redis failover / split brain No
Writes that skip the fencing token No

fencekit does not implement Redlock. Fencing only works when the storage layer checks the token in the same operation as the write.

Development

REM Windows (CMD)
python -m pip install -e ".[dev]"
python -m ruff check src tests
python -m mypy src
python -m pytest -m "not integration" -q

Full suite (Redis on localhost:6379):

set FENCEKIT_REDIS_URL=redis://localhost:6379/15
python -m pytest -q
# Linux/macOS with uv
uv sync --extra dev
uv run pytest

CI runs lint, type-check, and tests on Python 3.10–3.13 with Redis. Releases publish to PyPI via Trusted Publishing (OIDC, no long-lived token).

License

MIT

Download files

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

Source Distribution

fencekit-0.3.2.tar.gz (24.3 kB view details)

Uploaded Source

Built Distribution

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

fencekit-0.3.2-py3-none-any.whl (17.6 kB view details)

Uploaded Python 3

File details

Details for the file fencekit-0.3.2.tar.gz.

File metadata

  • Download URL: fencekit-0.3.2.tar.gz
  • Upload date:
  • Size: 24.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for fencekit-0.3.2.tar.gz
Algorithm Hash digest
SHA256 c9e1ad2a0b5b201cd071269cf987f1f5fd9f179aaa77508e3238c337fa92d349
MD5 ccaef10a7808c3f19e27428408da7f74
BLAKE2b-256 7796b8ef0c9b6273f2d3c25acaae497d3ac6ef1cafe8628c632d7c53aa442d42

See more details on using hashes here.

Provenance

The following attestation bundles were made for fencekit-0.3.2.tar.gz:

Publisher: release.yml on ahmed5145/fencekit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fencekit-0.3.2-py3-none-any.whl.

File metadata

  • Download URL: fencekit-0.3.2-py3-none-any.whl
  • Upload date:
  • Size: 17.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for fencekit-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 9d1576b184f75ae2f925485fd2f5173195797761ef81916248311c05ea42ddb0
MD5 c863783acb5faf315632f3a9a3bf0727
BLAKE2b-256 d4b6a6df2236fca47566fe039fa7620ca365480cbf0f70f048d300082b366d0b

See more details on using hashes here.

Provenance

The following attestation bundles were made for fencekit-0.3.2-py3-none-any.whl:

Publisher: release.yml on ahmed5145/fencekit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.6.1

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

This release

0.3.2 This release

2 files

0.3.1

2 files

0.3.0

2 files

0.2.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page