Skip to main content

Distributed semaphore utility based on NATS JetStream key-value stores.

Project description

nats-semaphore

A distributed semaphore implementation for Python using NATS JetStream KeyValue stores. This library allows you to coordinate access to shared resources across multiple processes or services with ease.

Features

  • Distributed Locking: Leverages NATS JetStream KeyValue stores for reliable distributed coordination.
  • Configurable Concurrency: Define the number of slots (concurrency limit) for each semaphore.
  • Asyncio Support: Built from the ground up for Python's asyncio.
  • Context Manager: Easy-to-use async with syntax for automatic lock acquisition and release.
  • Timeout Handling: Support for acquisition timeouts.
  • Automatic Renewal: Locks are renewed periodically while held, so the default 10 second KV TTL does not expire during longer work.

Installation

pip install nats-semaphore

Usage

Here is a simple example of how to use nats-semaphore:

import asyncio
import nats
from nats_semaphore import NatsSemaphoreContext, SemaphoreBucketConfig

async def main():
    # 1. Connect to NATS
    nc = await nats.connect("nats://localhost:4222")

    # 2. Explicitly opt into lazy create-or-bind provisioning.
    semaphore_context = NatsSemaphoreContext(nc, bucket=SemaphoreBucketConfig())

    # To bind only to infrastructure provisioned elsewhere, pass its bucket name.
    # This raises BucketNotFoundError on first use if the bucket does not exist.
    # semaphore_context = NatsSemaphoreContext(nc, bucket="SEMAPHORES")

    # 3. Define a semaphore
    # 'name' identifies the resource.
    # 'slot_count' is the maximum number of concurrent locks allowed.
    semaphore = semaphore_context.semaphore(name="my-shared-resource", slot_count=3)

    # 4. Acquire a lock using a context manager
    try:
        # Try to acquire a lock, waiting up to 5 seconds.
        # By default, the lock is renewed every 5 seconds.
        async with semaphore.lock(timeout=5.0) as lock:
            print("Lock acquired! Doing work...")
            await asyncio.sleep(1)
            print("Work done.")
    except asyncio.TimeoutError:
        print("Failed to acquire lock within timeout.")

    # Alternative: Manual acquire/release
    try:
        lock = await semaphore.acquire(timeout=5.0)
        print("Manually acquired lock.")
        # ... do work ...
    finally:
        await lock.release()
        print("Manually released lock.")

    # Disable automatic renewal by setting renew_interval to 0.
    lock = await semaphore.acquire(timeout=5.0, renew_interval=0)
    await lock.release()

    await nc.close()

if __name__ == "__main__":
    asyncio.run(main())

SemaphoreBucketConfig() provisions the SEMAPHORES bucket lazily with a 10 second TTL, a 1 MiB limit, memory storage, and one replica. Acquired locks are renewed every 5 seconds unless renew_interval=0 is passed to acquire() or lock().

Custom provisioning remains semaphore-specific:

from nats.js.api import StorageType
from nats_semaphore import NatsSemaphoreContext, SemaphoreBucketConfig

context = NatsSemaphoreContext(
    nc,
    bucket=SemaphoreBucketConfig(
        bucket="MY_SEMAPHORES",
        ttl=30,
        max_bytes=2 * 1024 * 1024,
        storage=StorageType.FILE,
        replicas=3,
    ),
)

Migrating To 0.0.3

The context API intentionally no longer accepts kv= or native NATS KeyValueConfig values. Replace create-or-bind calls with bucket=SemaphoreBucketConfig(...). Replace kv="NAME" with bucket="NAME" only when the bucket is provisioned externally and should be bind-only.

Requirements

Compatibility

Compatible with nats-server versions:

  • 2.7
  • 2.8
  • 2.9
  • 2.10
  • 2.11
  • 2.12
  • 2.14

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

nats_semaphore-0.0.3.tar.gz (11.7 kB view details)

Uploaded Source

Built Distribution

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

nats_semaphore-0.0.3-py3-none-any.whl (6.3 kB view details)

Uploaded Python 3

File details

Details for the file nats_semaphore-0.0.3.tar.gz.

File metadata

  • Download URL: nats_semaphore-0.0.3.tar.gz
  • Upload date:
  • Size: 11.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for nats_semaphore-0.0.3.tar.gz
Algorithm Hash digest
SHA256 ddddefbda4afd39d53734d4028d9883576864b1e28b98cb6cf3d41c0b5f3b44e
MD5 94ffc63a25e09cb5a1ea78259caf70b8
BLAKE2b-256 ffd83d2f889fb47dc35066d525921324e87ae4de419df6959eb9018e7556fa4d

See more details on using hashes here.

Provenance

The following attestation bundles were made for nats_semaphore-0.0.3.tar.gz:

Publisher: publish.yml on m3nowak/nats-semaphore

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

File details

Details for the file nats_semaphore-0.0.3-py3-none-any.whl.

File metadata

  • Download URL: nats_semaphore-0.0.3-py3-none-any.whl
  • Upload date:
  • Size: 6.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for nats_semaphore-0.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 09b5cfc3b4ab29ce3db3b75b08cfd5eb0217a33ce62f55e7609a45305125c301
MD5 1516203b64365f401d4a798f371146cb
BLAKE2b-256 63185b5139472c3bc3053204b57d409cac0aa2ade558cc1d691d50f873c390f6

See more details on using hashes here.

Provenance

The following attestation bundles were made for nats_semaphore-0.0.3-py3-none-any.whl:

Publisher: publish.yml on m3nowak/nats-semaphore

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