Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/seankerr/py-postgresql-lock
Lock mechanism implemented with PostgreSQL advisory locks.
https://github.com/seankerr/py-postgresql-lock
distributed-lock lock postgres postgresql python
Last synced: 2 months ago
JSON representation
Lock mechanism implemented with PostgreSQL advisory locks.
- Host: GitHub
- URL: https://github.com/seankerr/py-postgresql-lock
- Owner: seankerr
- License: bsd-3-clause
- Created: 2023-05-30T00:46:27.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-06-19T01:45:10.000Z (7 months ago)
- Last Synced: 2024-09-19T23:42:06.858Z (4 months ago)
- Topics: distributed-lock, lock, postgres, postgresql, python
- Language: Python
- Homepage: https://pypi.org/project/postgresql-lock/
- Size: 61.5 KB
- Stars: 11
- Watchers: 1
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
## postgresql-lock
Lock mechanism implemented with PostgreSQL advisory locks.
Easily implement distributed database locking.
### Install
```sh
pip install postgresql-lock
```### Supported database interfaces
- **asyncpg**
- asynchronous
- **psycopg2**
- synchronous
- **psycopg3**
- asynchronous
- synchronous
- **sqlalchemy** (supports version 1 & 2; can use any underlying database interface)
- asynchronous
- synchronous### Why would I use this?
- PostgreSQL table locks aren't sufficient for your use-case
- PostgreSQL row locks don't work on `INSERT`
- You want to prevent race conditions between `INSERT` and `UPDATE` on the same primary key
- None of the aforementioned details fit your use-case, but you have PostgreSQL installed and need to prevent race conditions in a distributed system### Default operation
By default `postgresql-lock` will use `session` lock scope in `blocking` mode with
`rollback_on_error` enabled. The `session` lock scope means only a single database connection can
acquire the lock at a time.### Usage
All work revolves around the `Lock` class.
The easiest way to use `Lock` is with `with` or `async with` statements. The lock will be
released automatically. If `rollback_on_error` is enabled (default), rollbacks are automatically
handled prior to release._Using `with` and `async with` implies blocking mode._
```python
from postgresql_lock import Lock# setup connection
conn = ...# create and use lock
with Lock(conn, "shared-identifier"):
print("Acquired lock!")# do something here
```Now compare the above example to the equivalent try/finally example below:
```python
from postgresql_lock import Lock# setup connection
conn = ...# create lock
lock = Lock(conn, "shared-identifier")try:
# acquire lock
lock.acquire()print("Acquired lock!")
try:
# do something here
passexcept Exception as exc:
# handle_error() will rollback the transaction by default
lock.handle_error(exc)raise exc
finally:
# release lock (this is safe to run even if the lock has not been acquired)
lock.release()
```### Asynchronous usage (without `async with`)
```python
from postgresql_lock import Lock# setup connection
conn = ...# create lock
lock = Lock(conn, "shared-identifier")try:
# acquire lock
await lock.acquire_async()print("Acquired lock!")
try:
# do something here
passexcept Exception as exc:
# handle_error_async() will rollback the transaction by default
await lock.handle_error_async(exc)raise exc
finally:
# release lock (this is safe to run even if the lock has not been acquired)
await lock.release_async()
```### Non-blocking mode (supports async as well)
```python
from postgresql_lock import Lock# setup connection
conn = ...# create lock
lock = Lock(conn, "shared-identifier")# acquire lock
if lock.acquire(block=False):
# do something here
passelse:
# could not acquire lock
pass# release lock (this is safe to run even if the lock has not been acquired)
lock.release()
```### Specify the database interface manually
```python
from postgresql_lock import Lock# setup connection
conn = ...# create and use lock
lock = Lock(conn, "shared-identifier", interface="asyncpg")# do things with the lock
```### Handle rollbacks manually
```python
from postgresql_lock import Lock# setup connection
conn = ...# create and use lock
lock = Lock(conn, "shared-identifier", rollback_on_error=False)# do things with the lock
```### Changelog
- **0.1.8**
- Add logger() function
- Use "postgresql_lock" logger name
- **0.1.7**
- Add logging statements
- **0.1.6**
- Use int.from_bytes() to convert lock key into integer
- Fix: psycopg3 close() not being awaited bug
- **0.1.5**
- Rename package from postgres-lock to postgresql-lock
- **0.1.4**
- Add py.typed for mypy
- **0.1.3**
- Key can be any object
- **0.1.2**
- Add Lock.rollback_on_error (default true)
- Add Lock.handle_error() & Lock.handle_error_async()
- **0.1.1**
- Key can be str or int