Skip to main content

Redis-based distributed synchronization primitives for Python

Project description

redsync

Python 3.10+ License: MIT

Redis-based distributed synchronization primitives for Python. Async API using redis.asyncio.

Features

  • Blocking, no polling – Uses Redis BLPOP: the connection blocks on the server until a permit is available. No busy-waiting, no lock + pub/sub overhead.
  • Async-first – Built on redis.asyncio; use with async/await and context managers.
  • Configurable init – LUA (atomic, default) or SETNX strategy for creating the permit pool.
  • N permits – Semaphore count from 1 to 4096 for limiting concurrency across processes.
  • Python 3.10+ – Modern Python support.

TODO

  • Semaphore delete / lifecycle
    • Option A: set expire time on the list key (simple; semaphore disappears when unused).
    • Option B: async background task that extends TTL while at least one semaphore instance exists (keeps it alive as long as someone uses it).
    • Consider other algorithms (e.g. refcount in metadata, lease-based cleanup).
  • Creator-only count – Only the creator sets count; other callers wait until the semaphore exists and then read metadata (count, etc.) instead of passing count.
  • Maybe List vs sorted set – Evaluate whether Redis sorted sets are a better fit than a list (e.g. per-permit TTL, ordering, or different blocking semantics).
  • Other sync primitives – Add more primitives (e.g. event).

Installation

pip install redsync

Or with uv:

uv add redsync

Requirements: Redis server, redis>=5.0.0 (async support).

Semaphore

Usage

import asyncio
from redis.asyncio import Redis
from redsync import RedisSemaphore, RedisSemaphoreTimeoutError

async def main():
    r = Redis()
    sem = await RedisSemaphore.create(r, "my_resource", count=1)

    # acquire() raises RedisSemaphoreTimeoutError on timeout
    try:
        await sem.acquire(timeout=10)
        try:
            # do work
            pass
        finally:
            await sem.release()
    except RedisSemaphoreTimeoutError:
        pass  # handle timeout

    # or use context manager (raises on timeout)
    async with sem:
        # do work
        pass

asyncio.run(main())

N permits

Use count > 1 to allow N concurrent holders. count must be between 1 and 4096.

from redsync import SemaphoreInitStrategy

sem = await RedisSemaphore.create(r, "pool", count=5, semaphore_init_strategy=SemaphoreInitStrategy.LUA)
await sem.acquire()
# ...
await sem.release()

Init strategies

The semaphore uses a Redis list as a permit pool. The list must be created and filled with count elements before anyone can BLPOP. Two strategies are supported:

Lua SETNX
Idea Run a script that atomically ensures the list has N elements (if LLEN == 0 then RPUSH N times). Use a separate init key; the first process that wins SET NX creates the list and pushes N elements, then deletes the init key.
Pros Single atomic op; no extra key; no crash race during init; idempotent. No Lua; only basic commands; easy to debug in Redis.
Cons Requires Lua (standard in Redis). Extra key; two round-trips for the initializer (SETNX then RPUSH).

Default is SemaphoreInitStrategy.LUA. Use SemaphoreInitStrategy.SETNX to avoid Lua.

Exceptions

  • RedisSemaphoreError - Base exception
  • RedisSemaphoreTimeoutErroracquire() timed out
  • RedisSemaphoreNotAcquiredErrorrelease() called without acquiring
  • RedisSemaphoreCountErrorcount not in 1–4096

API Reference

RedisSemaphore

class RedisSemaphore:
    @classmethod
    async def create(cls, redis_client, name: str, *, count: int = 1,
                    semaphore_init_strategy: SemaphoreInitStrategy = SemaphoreInitStrategy.LUA,
                    key_prefix: str = "redis_semaphore") -> RedisSemaphore

    async def acquire(self, timeout: float | None = None) -> None  # None = block until available
    async def release(self) -> None
    async def __aenter__(self) -> RedisSemaphore
    async def __aexit__(...) -> None
  • name – Semaphore identifier (shared across processes).
  • count – Number of permits (1–4096).
  • timeout – For acquire(): seconds to wait; None blocks indefinitely. Raises RedisSemaphoreTimeoutError on timeout.

Running tests

pytest
# or
uv run pytest

Set REDIS_URL if Redis is not on localhost:6379.

License

MIT License – see LICENSE.

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

redsync-0.0.1.tar.gz (60.0 kB view details)

Uploaded Source

Built Distribution

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

redsync-0.0.1-py3-none-any.whl (6.4 kB view details)

Uploaded Python 3

File details

Details for the file redsync-0.0.1.tar.gz.

File metadata

  • Download URL: redsync-0.0.1.tar.gz
  • Upload date:
  • Size: 60.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for redsync-0.0.1.tar.gz
Algorithm Hash digest
SHA256 aac930c75d1d730f2664a0434a52f290ae27e001f1b35497034659fbf724755b
MD5 0a0d984e2e217f6521b49633d5a3af7b
BLAKE2b-256 56627980b26304aa69d2caa2bfe947a4463f26e4e13756f427a97c18b9231b50

See more details on using hashes here.

Provenance

The following attestation bundles were made for redsync-0.0.1.tar.gz:

Publisher: release.yml on martinmkhitaryan/redsync

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

File details

Details for the file redsync-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: redsync-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 6.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for redsync-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 05f6d7e5a54c5f07517b13ce7f1f874289ef77473830f66163736fefdddb7455
MD5 f0c022feec42eba00025f84cf492c54a
BLAKE2b-256 bf15010d68465a3fedfbbd97ff1d45768cf380a22bd44be90d82a54134af916e

See more details on using hashes here.

Provenance

The following attestation bundles were made for redsync-0.0.1-py3-none-any.whl:

Publisher: release.yml on martinmkhitaryan/redsync

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

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