Skip to main content

Concurrency guard for Django using PostgreSQL advisory locks.

Project description

django-concurrency-safe

PyPI version Python versions

Concurrency guard for Django using PostgreSQL advisory locks.

Prevent race conditions in critical sections using simple, expressive decorators or context managers.


Why?

Race conditions are easy to introduce and hard to detect.

Example:

def withdraw(user, amount):
    if user.balance >= amount:
        user.balance -= amount
        user.save()

Two concurrent requests can both pass the balance check and withdraw twice.

This library prevents that.


Features

  • PostgreSQL advisory lock backend
  • Simple decorator API
  • Context manager support
  • Business-key locking (not limited to database rows)
  • Timeout and conflict handling

Installation

pip install django-concurrency-safe

Quickstart

Import:

from concurrency_safe import concurrency_safe, lock, LockAcquireTimeout

Using the decorator

@concurrency_safe(key="withdraw:user:{user_id}")
def withdraw(user_id, amount):
    ...

Only one execution per key runs at a time.

Using the context manager

with lock("stock:ABC"):
    process_order()

Conflict handling

When the lock cannot be acquired:

@concurrency_safe(key="stock:{sku}")

Raises LockAcquireTimeout by default.

Custom handler:

from django.http import JsonResponse

def busy(*args, **kwargs):
    return JsonResponse({"detail": "busy"}, status=409)

@concurrency_safe(
    key="stock:{sku}",
    on_conflict=busy,
)

Testing

django-concurrency-safe is tested against real PostgreSQL using advisory locks.

Integration tests require a running PostgreSQL instance.

Start PostgreSQL using docker compose:

docker compose up -d

Run tests:

export DATABASE_URL=postgres://postgres:postgres@localhost:5432/concurrency_safe
pytest

Tests will automatically use PostgreSQL via the DATABASE_URL environment variable.


Example project

example/ contains a runnable Django demo showcasing the race condition, row-level locks, and advisory locks (PostgreSQL).


Why advisory locks?

Unlike row-level locking, advisory locks:

  • Work without locking a specific database row
  • Support arbitrary business keys
  • Are fast and lightweight
  • Automatically release on connection close

Requirements

  • Python 3.10+
  • Django 4.2+
  • PostgreSQL

Roadmap

  • Redis backend
  • Async support
  • Metrics hooks

License

MIT

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

django_concurrency_safe-0.0.2.tar.gz (11.8 kB view details)

Uploaded Source

Built Distribution

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

django_concurrency_safe-0.0.2-py3-none-any.whl (10.1 kB view details)

Uploaded Python 3

File details

Details for the file django_concurrency_safe-0.0.2.tar.gz.

File metadata

  • Download URL: django_concurrency_safe-0.0.2.tar.gz
  • Upload date:
  • Size: 11.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for django_concurrency_safe-0.0.2.tar.gz
Algorithm Hash digest
SHA256 424ca2d8135632d0aaaa21131a95f8769bce39b500ec9fb01f0b8ece730cf78a
MD5 25fd2b87e98f2765d91aecdaa87be558
BLAKE2b-256 3ba15810b1b14501e3a91da581fcd32a8b7643bda409ab2f1a6d5ea3e81d337c

See more details on using hashes here.

File details

Details for the file django_concurrency_safe-0.0.2-py3-none-any.whl.

File metadata

File hashes

Hashes for django_concurrency_safe-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 01b60f0fb479b449d7e86310baf3a5c6d2f97f89f5c7a5073e5a1c45a89350ef
MD5 fd09f5e101b6b750a33f0e84d06c2719
BLAKE2b-256 fbd6d2a955dc4d7f72a574deafed6a9d906bd50668b60115d8488f15cf1e6cbc

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