Metadata-Version: 2.4
Name: django-concurrency-safe
Version: 0.0.1
Summary: Concurrency guard for Django using PostgreSQL advisory locks.
Author: Aslı Kök
License: MIT
Project-URL: Homepage, https://github.com/imgeaslikok/django-concurrency-safe
Project-URL: Repository, https://github.com/imgeaslikok/django-concurrency-safe
Project-URL: Issues, https://github.com/imgeaslikok/django-concurrency-safe/issues
Keywords: django,postgres,advisory-lock,locking,concurrency,race-condition
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.0
Classifier: Framework :: Django :: 5.1
Classifier: Framework :: Django :: 5.2
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Django>=4.2
Provides-Extra: test
Requires-Dist: pytest>=8; extra == "test"
Provides-Extra: dev
Requires-Dist: ruff>=0.6; extra == "dev"
Requires-Dist: mypy>=1.10; extra == "dev"
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: twine>=5.1; extra == "dev"
Dynamic: license-file

# django-concurrency-safe

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:

```python
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
```bash
pip install django-concurrency-safe
```
---

## Quickstart
Import:
```python
from concurrency_safe import concurrency_safe, lock, LockAcquireTimeout
```
### Using the decorator
```python
@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
```python
with lock("stock:ABC"):
    process_order()
```
### Conflict handling
When the lock cannot be acquired:
```python
@concurrency_safe(key="stock:{sku}")
```
Raises `LockAcquireTimeout` by default.

Custom handler:
```python
from django.http import JsonResponse

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

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

---

## 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
